精華區beta SetupBBS 關於我們 聯絡資訊
/*-------------------------------------------------------*/ /* mail.c ( NTHU CS MapleBBS Ver 2.36 ) */ /*-------------------------------------------------------*/ /* target : local/internet mail routines */ /* create : 95/03/29 */ /* update : 95/12/15 */ /*-------------------------------------------------------*/ #include "bbs.h" extern int del_range(); extern int cmpfmode(); char currmaildir[32]; static char msg_cc[] = "[群組名單]\n"; static char listfile[] = "list.0"; int setforward() /* Ptt */ { char buf[80],ip[50]="",yn[4]; FILE *fp; sethomepath(buf, cuser.userid); strcat(buf,"/.forward"); if(fp = fopen(buf,"r")) { fscanf(fp,"%s",ip); fclose(fp); } getdata_buf(b_lines-1,0,"請輸入信箱自動轉寄的email地址:",ip,41, DOECHO); if(ip[0] && ip[0] != ' ') { getdata(b_lines,0,"確定開啟自動轉信功\能?(Y/n)",yn,3, LCECHO); if(yn[0] != 'n' && (fp=fopen(buf,"w"))) { move(b_lines,0); clrtoeol(); fprintf(fp,"%s",ip); fclose(fp); outs("設定完成!"); refresh(); sleep(1); return 0; } } move(b_lines,0); clrtoeol(); outs("取消自動轉信!"); unlink(buf); refresh(); sleep(1); return 0; } #ifdef INTERNET_PRIVATE_EMAIL int invalidaddr(addr) char *addr; { if (*addr == '\0') return 1; /* blank */ while (*addr) { if (not_alnum(*addr) && !strchr("[].%!@:-_;", *addr)) return 1; addr++; } return 0; } int m_internet() { char receiver[60]; getdata(20, 0, "收信人:", receiver, 60, DOECHO); if (strchr(receiver, '@') && !invalidaddr(receiver) && getdata(21, 0, "主 題:", save_title, TTLEN, DOECHO)) { do_send(receiver, save_title); } else { move(22, 0); outs("收信人或主題不正確, 請重新選取指令"); pressanykey(); } return 0; } #endif void m_init() { sethomedir(currmaildir, cuser.userid); } int chkmailbox(void) { if (!HAVE_PERM(PERM_SYSOP) && !HAVE_PERM(PERM_MAILLIMIT)) { int keep, sum; int sumlimit; if (HAS_PERM(PERM_BM)) sumlimit = 300; else if (HAS_PERM(PERM_LOGINOK)) sumlimit = 150; else sumlimit = 100; m_init(); if ((keep = get_num_records(currmaildir, sizeof(fileheader))) > MAXKEEPMAIL) { move(b_lines, 0); clrtoeol(); bell(); prints("您保存信件數目 %d 超出上限 %d, 請整理", keep, MAXKEEPMAIL); bell(); refresh(); igetch(); return keep; } if ((sum = get_sum_records(currmaildir, sizeof(fileheader))) > sumlimit) { move(b_lines, 0); clrtoeol(); bell(); prints("您保存信件容量 %d(k)超出上限 %d(k), 請整理", sum, sumlimit); bell(); refresh(); igetch(); return keep; } } return 0; } static void do_hold_mail(fpath, receiver, holder) char *fpath; char *receiver; char *holder; { char buf[80], title[128]; fileheader mymail; sethomepath(buf, holder); stampfile(buf, &mymail); mymail.savemode = 'H'; /* hold-mail flag */ mymail.filemode = FILE_READ; strcpy(mymail.owner, "[備.忘.錄]"); if (receiver) { sprintf(title, "(%s) %s", receiver, save_title); strncpy(mymail.title, title, TTLEN); } else strcpy(mymail.title, save_title); sethomedir(title, holder); if (append_record(title, &mymail, sizeof(mymail)) != -1) { unlink(buf); Link(fpath, buf); } } void hold_mail(fpath, receiver) char *fpath; char *receiver; { char buf[4]; getdata(b_lines - 1, 0, "已順利寄出,是否自存底稿(Y/N)?[N] ", buf, 4, LCECHO); if (buf[0] == 'y') do_hold_mail(fpath, receiver, cuser.userid); if (is_watched(cuser.userid)) do_hold_mail(fpath, receiver, "SYSOP"); } int do_send(userid, title) char *userid, *title; { fileheader mhdr; struct stat st; char fpath[STRLEN], buf[80], *ip; char receiver[IDLEN]; int fp; char genbuf[200]; #ifdef INTERNET_PRIVATE_EMAIL int internet_mail; if (strchr(userid, '@')) { internet_mail = 1; } else { internet_mail = 0; #endif if (!getuser(userid)) return -1; if (!(xuser.userlevel & PERM_READMAIL)) return -3; if (!title) getdata(2, 0, "主題:", save_title, TTLEN, DOECHO); curredit |= EDIT_MAIL; #ifdef INTERNET_PRIVATE_EMAIL } #endif setutmpmode(SMAIL); fpath[0] = '\0'; #ifdef INTERNET_PRIVATE_EMAIL if (internet_mail) { int res, ch; if (vedit(fpath, NA) == -1) { unlink(fpath); clear(); return -2; } clear(); prints("信件即將寄給 %s\n標題為:%s\n確定要寄出嗎? (Y/N) [Y]", userid, title); ch = igetch(); switch (ch) { case 'N': case 'n': outs("N\n信件已取消"); res = -2; break; default: outs("Y\n請稍候, 信件傳遞中...\n"); res = bbs_sendmail(fpath, title, userid); hold_mail(fpath, userid); } unlink(fpath); return res; } else { #endif strcpy(receiver, userid); if (vedit(fpath, YEA) == -1) { unlink(fpath); clear(); return -2; } clear(); strcpy(userid, receiver); sethomepath(genbuf, userid); stampfile(genbuf, &mhdr); Rename(fpath, genbuf); strcpy(mhdr.owner, cuser.userid); strncpy(mhdr.title, save_title, TTLEN); mhdr.savemode = '\0'; sethomedir(fpath, userid); if (append_record(fpath, &mhdr, sizeof(mhdr)) == -1) return -1; hold_mail(genbuf, userid); return 0; #ifdef INTERNET_PRIVATE_EMAIL } #endif } void my_send(uident) char *uident; { switch (do_send(uident, NULL)) { case -1: outs(err_uid); break; case -2: outs(msg_cancel); break; case -3: prints("使用者 [%s] 無法收信", uident); break; } pressanykey(); } int m_send() { char uident[40]; stand_title("且聽風的話"); usercomplete(msg_uid, uident); if (uident[0]) my_send(uident); return 0; } /* ------------------------------------------------------------ */ /* 群組寄信、回信 : multi_send, multi_reply */ /* ------------------------------------------------------------ */ extern struct word *toplev; static void multi_list(reciper) int *reciper; { char uid[16]; char genbuf[200]; while (1) { stand_title("群組寄信名單"); ShowNameList(3, 0, msg_cc); getdata(1, 0, "(I)引入好友 (O)引入上線通知 (N)引入新文章通知 (0-9)引入其他特別名單\n" "(A)增加 (D)刪除 (M)確認寄信名單 (Q)取消 ?[M]", genbuf, 4, LCECHO); switch (genbuf[0]) { case 'a': while (1) { move(1, 0); usercomplete("請輸入要增加的代號(只按 ENTER 結束新增): ", uid); if (uid[0] == '\0') break; move(2, 0); clrtoeol(); if (!searchuser(uid)) outs(err_uid); else if (!InNameList(uid)) { AddNameList(uid); (*reciper)++; } ShowNameList(3, 0, msg_cc); } break; case 'd': while (*reciper) { move(1, 0); namecomplete("請輸入要刪除的代號(只按 ENTER 結束刪除): ", uid); if (uid[0] == '\0') break; if (RemoveNameList(uid)) { (*reciper)--; } ShowNameList(3, 0, msg_cc); } break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': listfile[5] = genbuf[0]; genbuf[0] = '1'; case 'i': setuserfile(genbuf, genbuf[0] == '1' ? listfile : fn_overrides); ToggleNameList(reciper, genbuf, msg_cc); break; case 'o': setuserfile(genbuf, "alohaed"); ToggleNameList(reciper, genbuf, msg_cc); break; case 'n': setuserfile(genbuf, "postlist"); ToggleNameList(reciper, genbuf, msg_cc); break; case 'q': *reciper = 0; return; break; default: return; } } } static int multi_send(title) char *title; { FILE *fp; struct word *p; fileheader mymail; char fpath[TTLEN], *ptr; int reciper, listing; char genbuf[256]; CreateNameList(); listing = reciper = 0; if (*quote_file) { AddNameList(quote_user); reciper = 1; fp = fopen(quote_file, "r"); while (fgets(genbuf, 256, fp)) { if (strncmp(genbuf, "※ ", 3)) { if (listing) break; } else { if (listing) { strtok(ptr = genbuf + 3, " \n\r"); do { if (searchuser(ptr) && !InNameList(ptr) && strcmp(cuser.userid, ptr)) { AddNameList(ptr); reciper++; } } while (ptr = (char *) strtok(NULL, " \n\r")); } else if (!strncmp(genbuf + 3, "[通告]", 6)) listing = 1; } } ShowNameList(3, 0, msg_cc); } multi_list(&reciper); move(1, 0); clrtobot(); if (reciper) { setutmpmode(SMAIL); if (title) { do_reply_title(2, title); } else { getdata(2, 0, "主題:", fpath, 64, DOECHO); sprintf(save_title, "[通告] %s", fpath); } setuserfile(fpath, fn_notes); if (fp = fopen(fpath, "w")) { fprintf(fp, "※ [通告] 共 %d 人收件", reciper); listing = 80; for (p = toplev; p; p = p->next) { reciper = strlen(p->word) + 1; if (listing + reciper > 75) { listing = reciper; fprintf(fp, "\n※"); } else listing += reciper; fprintf(fp, " %s", p->word); } memset(genbuf, '-', 75); genbuf[75] = '\0'; fprintf(fp, "\n%s\n\n", genbuf); fclose(fp); } curredit |= EDIT_LIST; if (vedit(fpath, YEA) == -1) { unlink(fpath); curredit = 0; outs(msg_cancel); pressanykey(); return; } stand_title("寄信中..."); refresh(); listing = 80; for (p = toplev; p; p = p->next) { reciper = strlen(p->word) + 1; if (listing + reciper > 75) { listing = reciper; outc('\n'); } else { listing += reciper; outc(' '); } outs(p->word); if (searchuser(p->word) && strcmp(STR_GUEST, p->word) ) sethomepath(genbuf, p->word); else continue; stampfile(genbuf, &mymail); unlink(genbuf); Link(fpath, genbuf); strcpy(mymail.owner, cuser.userid); strcpy(mymail.title, save_title); mymail.savemode = 'M'; /* multi-send flag */ sethomedir(genbuf, p->word); if (append_record(genbuf, &mymail, sizeof(mymail)) == -1) outs(err_uid); } hold_mail(fpath, NULL); unlink(fpath); curredit = 0; } else { outs(msg_cancel); } pressanykey(); } static int multi_reply(ent, fhdr, direct) int ent; fileheader *fhdr; char *direct; { if (fhdr->savemode != 'M') return mail_reply(ent, fhdr, direct); stand_title("群組回信"); strcpy(quote_user, fhdr->owner); setuserfile(quote_file, fhdr->filename); multi_send(fhdr->title); return 0; } int mail_list() { stand_title("群組作業"); multi_send(NULL); return 0; } extern int bad_user_id(char userid[]); int mail_all() { FILE *fp; fileheader mymail; char fpath[TTLEN]; char genbuf[200]; extern struct UCACHE *uidshm; int i, unum; char* userid; stand_title("給所有使用者的系統通告"); setutmpmode(SMAIL); getdata(2, 0, "主題:", fpath, 64, DOECHO); sprintf(save_title, "[系統通告] %s", fpath); setuserfile(fpath, fn_notes); if (fp = fopen(fpath, "w")) { fprintf(fp, "※ [系統通告] 這是封給所有使用者的信\n"); fprintf(fp, "---------------------------------------------------------------------------\n"); fclose(fp); } *quote_file = 0; curredit |= EDIT_MAIL; if (vedit(fpath, YEA) == -1) { curredit = 0; unlink(fpath); outs(msg_cancel); pressanykey(); return; } curredit = 0; setutmpmode(MAILALL); stand_title("寄信中..."); sethomepath(genbuf, cuser.userid); stampfile(genbuf, &mymail); unlink(genbuf); Link(fpath, genbuf); unlink(fpath); strcpy(fpath, genbuf); strcpy(mymail.owner, cuser.userid); /*站長 ID*/ strcpy(mymail.title, save_title); mymail.savemode = 0; /* mymail.filemode |= FILE_TAGED; Ptt 公告改成不會mark */ sethomedir(genbuf, cuser.userid); if (append_record(genbuf, &mymail, sizeof(mymail)) == -1) outs(err_uid); for (unum = uidshm->number, i = 0; i < unum; i++) { if(bad_user_id(uidshm->userid[i])) continue; /* Ptt */ userid = uidshm->userid[i]; if (strcmp(userid, "guest") && strcmp(userid, "new") && strcmp(userid, cuser.userid)) { sethomepath(genbuf, userid); stampfile(genbuf, &mymail); unlink(genbuf); Link(fpath, genbuf); strcpy(mymail.owner, cuser.userid); strcpy(mymail.title, save_title); mymail.savemode = 0; /* mymail.filemode |= FILE_MARKED; Ptt 公告改成不會mark */ sethomedir(genbuf, userid); if (append_record(genbuf, &mymail, sizeof(mymail)) == -1) outs(err_uid); sprintf(genbuf, "%*s %5d / %5d", IDLEN + 1, userid, i + 1, unum); outmsg(genbuf); refresh(); } } return; } mail_mbox() { char cmd[100]; fileheader fhdr; sprintf(cmd, "/tmp/%s.tgz", cuser.userid); sprintf(fhdr.title, "%s 私人資料", cuser.userid); doforward(cmd, &fhdr, 'Z'); } /* Ptt 拿掉 int m_list() { int aborted; char genbuf[4]; getdata(b_lines - 1, 0, "名單檔 (E)編輯 (D)刪除 (Q)取消?[Q] ", genbuf, 4, LCECHO); if (genbuf[0] == 'd') aborted = 1; else if (genbuf[0] == 'e') aborted = 2; else aborted = 0; if (aborted) { char listpath[64], ans[4]; getdata(b_lines - 1, 0, "請選擇名單 (0-9)?[0] ", ans, 3, DOECHO); if (ans[0] == '\0') ans[0] = '0'; if (ans[0] >= '0' && ans[0] <= '9') { listfile[5] = ans[0]; setuserfile(listpath, listfile); if (aborted == 1) { if (more(listpath, NA) != -1) getdata(b_lines - 1, 0, "確定刪除嗎(Y/N)?[N]", ans, 3, LCECHO); if (*ans == 'y') { unlink(listpath); outmsg(msg_del_ok); } } else { int reciper; stand_title("編輯名單"); reciper = 0; CreateNameList(); LoadNameList(&reciper, listpath, msg_cc); multi_list(&reciper); SaveNameList(listpath); } } } return FULLUPDATE; } */ static int m_forward(ent, fhdr, direct) int ent; fileheader *fhdr; char *direct; { char uid[STRLEN]; char *t; FILE *fp; stand_title("轉達信件"); usercomplete(msg_uid, uid); if (uid[0] == '\0') { return FULLUPDATE; } strcpy(quote_user, fhdr->owner); setuserfile(quote_file, fhdr->filename); sprintf(save_title, "%.64s (fwd)", fhdr->title); move(1, 0); clrtobot(); prints("轉信給: %s\n標 題: %s\n", uid, save_title); switch (do_send(uid, save_title)) { case -1: outs(err_uid); break; case -2: outs(msg_cancel); break; case -3: prints("使用者 [%s] 無法收信", uid); break; } pressanykey(); return FULLUPDATE; } /* JhLin: At most 128 mail */ int delmsgs[128]; int delcnt; int mrd; static int read_new_mail(fptr) fileheader *fptr; { static int idc; char done = NA, delete_it; char fname[256]; char genbuf[4]; if (fptr == NULL) { delcnt = 0; idc = 0; return 0; } idc++; if (fptr->filemode) return 0; clear(); move(10, 0); prints("您要讀來自[%s]的訊息(%s)嗎?", fptr->owner, fptr->title); getdata(11, 0, "請您確定(Y/N/Q)?[Y] ", genbuf, 3, DOECHO); if (genbuf[0] == 'q') return QUIT; if (genbuf[0] == 'n') return 0; setuserfile(fname, fptr->filename); fptr->filemode |= FILE_READ; if (substitute_record(currmaildir, fptr, sizeof(*fptr), idc)) return -1; mrd = 1; delete_it = NA; while (!done) { int more_result = more(fname, YEA); switch (more_result) { case 1: return READ_PREV; case 2: return RELATE_PREV; case 3: return READ_NEXT; case 4: return RELATE_NEXT; case 5: return RELATE_FIRST; case 6: return 0; case 7: mail_reply(idc, fptr, currmaildir); return FULLUPDATE; case 8: multi_reply(idc, fptr, currmaildir); return FULLUPDATE; } move(b_lines, 0); clrtoeol(); outs(msg_mailer); refresh(); switch (egetch()) { case 'r': case 'R': mail_reply(idc, fptr, currmaildir); break; case 'x': m_forward(idc, fptr, currmaildir); break; case 'y': multi_reply(idc, fptr, currmaildir); break; case 'd': case 'D': delete_it = YEA; default: done = YEA; } } if (delete_it) { clear(); prints("刪除信件《%s》", fptr->title); getdata(1, 0, msg_sure_ny, genbuf, 2, LCECHO); if (genbuf[0] == 'y') { unlink(fname); delmsgs[delcnt++] = idc; } } clear(); return 0; } int m_new() { clear(); mrd = 0; setutmpmode(RMAIL); read_new_mail(NULL); clear(); curredit |= EDIT_MAIL; if (apply_record(currmaildir, read_new_mail, sizeof(fileheader)) == -1) { outs("沒有新信件了"); pressanykey(); return -1; } curredit = 0; if (delcnt) { while (delcnt--) delete_record(currmaildir, sizeof(fileheader), delmsgs[delcnt]); } outs(mrd ? "信已閱\畢" : "沒有新信件了"); pressanykey(); return -1; } static void mailtitle() { showtitle("\0郵件選單", BoardName); outs("\ [←]離開 [↑,↓]選擇 [→,r]閱\讀信件 [R]回信 [x]轉達 [y]群組回信 求助[h]\n\ 編號 日 期 作 者 信 件 標 題 "); } static void maildoent(num, ent) fileheader *ent; { char *title, *mark, color, type = "+ Mm"[ent->filemode]; /* woju taged for delete */ if (ent->filemode & FILE_TAGED) type = 'D'; title = subject(mark = ent->title); if (title == mark) { color = '1'; mark = "◇"; } else { color = '3'; mark = "R:"; } if (strncmp(currtitle, title, 40)) prints("%5d %c %-7s%-15.14s%s %.46s\n", num, type, ent->date, ent->owner, mark, title); else prints("%5d %c %-7s%-15.14sm%s %.46s\n", num, type, ent->date, ent->owner, color, mark, title); } #ifdef POSTBUG extern int bug_possible; #endif /* woju */ static int mail_del_tag(ent, fhdr, direct) int ent; fileheader *fhdr; char *direct; { char genbuf[3]; getdata(1, 0, "確定刪除標記信件(Y/N)? [Y]", genbuf, 3, LCECHO); if (genbuf[0] != 'n') { char buf[STRLEN]; currfmode = FILE_TAGED; if (delete_files(direct, cmpfmode)) return DIRCHANGED; } return FULLUPDATE; } m_idle(ent, fhdr, direct) int ent; fileheader *fhdr; char *direct; { t_idle(); return FULLUPDATE; } static int mail_del(ent, fhdr, direct) int ent; fileheader *fhdr; char *direct; { char genbuf[200]; if (fhdr->filemode & FILE_MARKED) return DONOTHING; getdata(1, 0, msg_del_ny, genbuf, 3, LCECHO); if (genbuf[0] == 'y') { char buf[STRLEN]; char *t; extern int cmpfilename(); strcpy(currfile, fhdr->filename); if (!delete_file(direct, sizeof(*fhdr), ent, cmpfilename)) { setdirpath(genbuf, direct, fhdr->filename); unlink(genbuf); if( currmode & MODE_SELECT ){ int now; sethomedir(genbuf,cuser.userid); now=getindex(genbuf,fhdr->filename,sizeof(fileheader)); delete_file (genbuf, sizeof(fileheader),now,cmpfilename); } return DIRCHANGED; } } return FULLUPDATE; } /* woju static int */ mail_read(ent, fhdr, direct) int ent; fileheader *fhdr; char *direct; { char buf[64]; char *t; char done, delete_it, replied; clear(); setdirpath(buf, direct, fhdr->filename); strncpy(currtitle, subject(fhdr->title), 40); done = delete_it = replied = NA; while (!done) { /* woju */ int more_result = more(buf, YEA); if (more_result != -1) { fhdr->filemode |= FILE_READ; if (!strncmp("[新]", fhdr->title, 4) && !(fhdr->filemode & FILE_MARKED)) fhdr->filemode |= FILE_TAGED; substitute_record(currmaildir, fhdr, sizeof(*fhdr), ent); } switch (more_result) { case 1: return READ_PREV; case 2: return RELATE_PREV; case 3: return READ_NEXT; case 4: return RELATE_NEXT; case 5: return RELATE_FIRST; case 6: return FULLUPDATE; case 7: mail_reply(ent, fhdr, direct); return FULLUPDATE; case 8: multi_reply(ent, fhdr, direct); return FULLUPDATE; } move(b_lines, 0); clrtoeol(); refresh(); outs(msg_mailer); switch (egetch()) { case 'r': case 'R': replied = YEA; mail_reply(ent, fhdr, direct); break; case 'x': m_forward(ent, fhdr, direct); break; case 'y': multi_reply(ent, fhdr, direct); break; case 'd': delete_it = YEA; default: done = YEA; } } if (delete_it) mail_del(ent, fhdr, direct); else { fhdr->filemode |= FILE_READ; #ifdef POSTBUG if (replied) bug_possible = YEA; #endif substitute_record(currmaildir, fhdr, sizeof(*fhdr), ent); #ifdef POSTBUG bug_possible = NA; #endif } return FULLUPDATE; } /* ---------------------------------------------- */ /* in boards/mail 回信給原作者,轉信站亦可 */ /* ---------------------------------------------- */ int mail_reply(ent, fhdr, direct) int ent; fileheader *fhdr; char *direct; { char uid[STRLEN]; char *t; FILE *fp; char genbuf[512]; stand_title("回 信"); /* 判斷是 boards 或 mail */ if (curredit & EDIT_MAIL) setuserfile(quote_file, fhdr->filename); else setbfile(quote_file, currboard, fhdr->filename); /* find the author */ strcpy(quote_user, fhdr->owner); if (strchr(quote_user, '.')) { genbuf[0] = '\0'; if (fp = fopen(quote_file, "r")) { fgets(genbuf, 512, fp); fclose(fp); } t = strtok(genbuf, str_space); if (!strcmp(t, str_author1) || !strcmp(t, str_author2)) { strcpy(uid, strtok(NULL, str_space)); } else { outs("錯誤: 找不到作者。"); pressanykey(); return FULLUPDATE; } } else strcpy(uid, quote_user); /* make the title */ do_reply_title(3, fhdr->title); prints("\n收信人: %s\n標 題: %s\n", uid, save_title); /* edit, then send the mail */ ent = curredit; switch (do_send(uid, save_title)) { case -1: outs(err_uid); break; case -2: outs(msg_cancel); break; case -3: prints("使用者 [%s] 無法收信", uid); break; } curredit = ent; pressanykey(); return FULLUPDATE; } int mail_edit(ent, fhdr, direct) int ent; fileheader *fhdr; char *direct; { char uid[STRLEN]; char *t; FILE *fp; char genbuf[200]; setdirpath(genbuf, direct, fhdr->filename); vedit(genbuf, belong("etc/bbsadms", cuser.userid) ? 0 : 2); return FULLUPDATE; } static int mail_mark(ent, fhdr, direct) int ent; fileheader *fhdr; char *direct; { fhdr->filemode ^= FILE_MARKED; fhdr->filemode &= ~FILE_TAGED; substitute_record(currmaildir, fhdr, sizeof(*fhdr), ent); return (PART_REDRAW); } /* woju */ static int mail_tag(ent, fhdr, direct) int ent; fileheader *fhdr; char *direct; { fhdr->filemode ^= FILE_TAGED; substitute_record(currmaildir, fhdr, sizeof(*fhdr), ent); return POS_NEXT; } /* help for mail reading */ static char *mail_help[] = { "\0電子信箱操作說明", "\01基本命令", "(p)(↑) 前一篇文章", "(n)(↓) 下一篇文章", "(P)(PgUp) 前一頁", "(N)(PgDn) 下一頁", "(##)(cr) 跳到第 ## 筆", "($) 跳到最後一筆", "\01進階命令", "(r)(→) 讀信", "(R) 回信", "(c/z) 收入此信件進入私人信件夾/進入私人信件夾", "(x) 轉達信件", "(X) 轉錄文章到其他看板", "(y) 群組回信", #ifdef INTERNET_EMAIL "(F) 將信傳送回您的電子信箱", #endif "(d) 殺掉此信", "(D) 殺掉指定範圍的信", "(m) 將信標記,以防被清除", "(t) 標記欲刪除信件", "(^D) 刪除已標記信件", NULL }; static int m_help() { show_help(mail_help); return FULLUPDATE; } static int mail_cross_post(ent, fhdr, direct) int ent; fileheader *fhdr; char *direct; { char xboard[20], fname[80], xfpath[80], xtitle[80], inputbuf[10]; fileheader xfile; FILE *xptr; int author = 0; char genbuf[200]; char genbuf2[4]; make_blist(); move(2, 0); clrtoeol(); move(3, 0); clrtoeol(); move(1, 0); namecomplete("轉錄本文章於看板:", xboard); if (*xboard == '\0' || !haspostperm(xboard)) return FULLUPDATE; ent = 1; if (HAS_PERM(PERM_SYSOP) || !strcmp(fhdr->owner, cuser.userid)) { getdata(2, 0, "(1)原文轉載 (2)舊轉錄格式?[1] ", genbuf, 3, DOECHO); if (genbuf[0] != '2') { ent = 0; getdata(2, 0, "保留原作者名稱嗎?[Y] ", inputbuf, 3, DOECHO); if (inputbuf[0] != 'n' && inputbuf[0] != 'N') author = 1; } } if (ent) sprintf(xtitle, "[轉錄]%.66s", fhdr->title); else strcpy(xtitle, fhdr->title); sprintf(genbuf, "採用原標題《%.60s》嗎?[Y] ", xtitle); getdata(2, 0, genbuf, genbuf2, 4, LCECHO); if (*genbuf2 == 'n') { if (getdata(2, 0, "標題:", genbuf, TTLEN, DOECHO)) strcpy(xtitle, genbuf); } getdata(2, 0, "(S)存檔 (L)站內 (Q)取消?[Q] ", genbuf, 3, LCECHO); if (genbuf[0] == 'l' || genbuf[0] == 's') { int currmode0 = currmode; currmode = 0; setbpath(xfpath, xboard); stampfile(xfpath, &xfile); if (author) strcpy(xfile.owner, fhdr->owner); else strcpy(xfile.owner, cuser.userid); strcpy(xfile.title, xtitle); if (genbuf[0] == 'l') { xfile.savemode = 'L'; xfile.filemode = FILE_LOCAL; } else xfile.savemode = 'S'; setuserfile(fname, fhdr->filename); if (ent) { xptr = fopen(xfpath, "w"); strcpy(save_title, xfile.title); strcpy(xfpath, currboard); strcpy(currboard, xboard); write_header(xptr); strcpy(currboard, xfpath); fprintf(xptr, "※ [本文轉錄自 %s 信箱]\n\n", cuser.userid); b_suckinfile(xptr, fname); addsignature(xptr); fclose(xptr); } else { unlink(xfpath); Link(fname, xfpath); } setbdir(fname, xboard); append_record(fname, (char *) &xfile, sizeof(xfile)); if (!xfile.filemode) outgo_post(&xfile, xboard); cuser.numposts++; substitute_record(fn_passwd, &cuser, sizeof(userec), usernum); outs("文章轉錄完成"); pressanykey(); currmode = currmode0; } return FULLUPDATE; } mail_cite(int ent, fileheader* fhdr, char* direct) { char fpath[256]; char title[TTLEN+1]; static char xboard[20]; char buf[20]; boardheader *bp; boardheader *getbcache(); setuserfile(fpath, fhdr->filename); strcpy(title, "◇ "); strncpy(title+3, fhdr->title, TTLEN-3); title[TTLEN] = '\0'; a_copyitem(fpath, title, 0); if (cuser.userlevel >= PERM_BM) { move(2, 0); clrtoeol(); move(3, 0); clrtoeol(); move(1, 0); make_blist(); namecomplete("輸入看版名稱 \ (直接Enter進入私人信件夾):", buf); if (*buf) strcpy(xboard, buf); if (*xboard && (bp = getbcache(xboard))) { setapath(fpath, xboard); a_menu(xboard, fpath, HAS_PERM(PERM_ALLBOARD) ? 2 : is_BM(bp->BM) ? 1 : 0); } else { mail_man(); /* Ptt */ fhdr->filemode |= FILE_TAGED; } return FULLUPDATE; } else { mail_man(); /* Ptt */ fhdr->filemode |= FILE_TAGED; return FULLUPDATE; } } int edit_title(); mail_man() { char buf[64],buf1[64]; if (HAS_PERM(PERM_MAILLIMIT)) { int mode0 = currutmp->mode; int stat0 = currstat; sethomeman(buf, cuser.userid); sprintf(buf1,"%s 的信件夾",cuser.userid); a_menu(buf1, buf, belong("etc/bbsadms", cuser.userid) ? 2 : 1); currutmp->mode = mode0; currstat = stat0; return FULLUPDATE; } return DONOTHING; } mail_save(int ent, fileheader* fhdr, char* direct) { char fpath[256]; char title[TTLEN+1]; static char xboard[20]; char buf[20]; if (HAS_PERM(PERM_MAILLIMIT)) { setuserfile(fpath, fhdr->filename); strcpy(title, "◇ "); strncpy(title+3, fhdr->title, TTLEN-3); title[TTLEN] = '\0'; a_copyitem(fpath, title, fhdr->owner); sethomeman(fpath, cuser.userid); a_menu(cuser.userid, fpath, belong("etc/bbsadms", cuser.userid) ? 2 : 1); return FULLUPDATE; } return DONOTHING; } static struct one_key mail_comms[] = { 'z', mail_man, 'c', mail_cite, 's', mail_save, 'd', mail_del, 'D', del_range, 'r', mail_read, 'R', mail_reply, 'E', mail_edit, 'm', mail_mark, 't', mail_tag, 'T', edit_title, 'x', m_forward, 'X', mail_cross_post, Ctrl('D'), mail_del_tag, 'y', multi_reply, Ctrl('I'), m_idle, 'h', m_help, '\0', NULL }; int m_read() { if (get_num_records(currmaildir, sizeof(fileheader))) { curredit = EDIT_MAIL; i_read(RMAIL, currmaildir, mailtitle, maildoent, mail_comms); currfmode = FILE_TAGED; if (search_rec(currmaildir, cmpfmode)) mail_del_tag(0, 0, currmaildir); curredit = 0; return 0; } else { outs("您沒有來信"); return XEASY; } } #ifdef INTERNET_EMAIL #include <netdb.h> #include <pwd.h> #include <time.h> /* 寄信交給 sendmail,下列可以省略 */ #if 0 #define BBSMAILDIR "/usr/spool/mqueue" static void spacestozeros(s) char *s; { while (*s) { if (*s == ' ') *s = '0'; s++; } } static int getqsuffix(s) char *s; { struct stat stbuf; char qbuf[STRLEN], dbuf[STRLEN]; char c1 = 'A', c2 = 'A'; int pos = strlen(BBSMAILDIR) + 3; sprintf(dbuf, "%s/dfAA%5d", BBSMAILDIR, currpid); sprintf(qbuf, "%s/qfAA%5d", BBSMAILDIR, currpid); spacestozeros(dbuf); spacestozeros(qbuf); while (1) { if (stat(dbuf, &stbuf) && stat(qbuf, &stbuf)) break; if (c2 == 'Z') { c2 = 'A'; if (c1 == 'Z') return -1; else c1++; dbuf[pos] = c1; qbuf[pos] = c1; } else c2++; dbuf[pos + 1] = c2; qbuf[pos + 1] = c2; } strcpy(s, &(qbuf[pos])); return 0; } static void convert_tz(local, gmt, buf) int gmt, local; char *buf; { local -= gmt; if (local < -11) local += 24; else if (local > 12) local -= 24; sprintf(buf, " %4d", abs(local * 100)); spacestozeros(buf); if (local < 0) buf[0] = '-'; else if (local > 0) buf[0] = '+'; else buf[0] = '\0'; } static int createqf(title, qsuffix) char *title, *qsuffix; { static int configured = 0; static char myhostname[STRLEN]; static char myusername[20]; char mytime[STRLEN]; char idtime[STRLEN]; char qfname[STRLEN]; char t_offset[6]; FILE *qfp; time_t timenow; int savehour; struct tm *gtime, *ltime; struct hostent *hbuf; struct passwd *pbuf; if (!configured) { /* get host name */ gethostname(myhostname, STRLEN); hbuf = gethostbyname(myhostname); if (hbuf) strncpy(myhostname, hbuf->h_name, STRLEN); /* get bbs uident */ pbuf = getpwuid(getuid()); if (pbuf) strncpy(myusername, pbuf->pw_name, 20); if (hbuf && pbuf) configured = 1; else return -1; } /* get file name */ sprintf(qfname, "%s/qf%s", BBSMAILDIR, qsuffix); if ((qfp = fopen(qfname, "w")) == NULL) return -1; /* get time */ time(&timenow); ltime = localtime(&timenow); #ifdef SYSV ascftime(mytime, "%a, %d %b %Y %T ", ltime); #else strftime(mytime, sizeof(mytime), "%a, %d %b %Y %T ", ltime); #endif savehour = ltime->tm_hour; gtime = gmtime(&timenow); strftime(idtime, sizeof(idtime), "%Y%m%d%y%H%M", gtime); convert_tz(savehour, gtime->tm_hour, t_offset); strcat(mytime, t_offset); fprintf(qfp, "P1000\nT%lu\nDdf%s\nS%s\nR%s\n", timenow, qsuffix, myusername, cuser.email); /* do those headers! */ fprintf(qfp, "HReceived: by %s (" MYVERSION ")\n\tid %s; %s\n", myhostname, qsuffix, mytime); fprintf(qfp, "HReturn-Path: <%s@%s>\n", myusername, myhostname); fprintf(qfp, "HDate: %s\n", mytime); fprintf(qfp, "HMessage-Id: <%s.%s@%s>\n", idtime, qsuffix, myhostname); fprintf(qfp, "HFrom: %s@%s (%s in NTHU CS BBS)\n", myusername, myhostname, cuser.userid); fprintf(qfp, "HSubject: %s (fwd)\n", title); fprintf(qfp, "HTo: %s\n", cuser.email); fprintf(qfp, "HX-Forwarded-By: %s (%s)\n", cuser.userid, #ifdef REALNAME cuser.realname); #else cuser.username); #endif fprintf(qfp, "HX-Disclaimer: %s 對本信內容恕不負責。\n", BoardName); fclose(qfp); return 0; } #endif int bbs_sendmail(fpath, title, receiver) char *fpath, *title, *receiver; { static int configured = 0; static char myhostname[STRLEN]; static char myusername[20]; struct hostent *hbuf; struct passwd *pbuf; char *ptr; char genbuf[200]; FILE *fin, *fout; /* 中途攔截 */ if (ptr = strchr(receiver, ';')) { struct tm *ptime; time_t now; /* if (fout = fopen("hackerlog", "a+")) { time(&now); ptime = localtime(&now); fprintf(fout, "[%d/%d %d:%02d] %s from %s\n\t\t寄出 %s\n", ptime->tm_mon + 1, ptime->tm_mday, ptime->tm_hour, ptime->tm_min, cuser.userid, getenv("RFC931"), receiver); fclose(fout); } */ *ptr = '\0'; } if ((ptr = strstr(receiver, str_mail_address)) || !strchr(receiver,'@')) { fileheader mymail; char hacker[20]; int len; if(strchr(receiver,'@') ) { len = ptr - receiver; memcpy(hacker, receiver, len); hacker[len] = '\0'; } else { strcpy(hacker,receiver); } if (!searchuser(hacker)) { return -2; } sethomepath(genbuf, hacker); stampfile(genbuf, &mymail); if (!strcmp(hacker, cuser.userid)) { strcpy(mymail.owner, "[批踢踢實業坊]"); mymail.filemode = FILE_READ; } else strcpy(mymail.owner, cuser.userid); strncpy(mymail.title, title, TTLEN); unlink(genbuf); Link(fpath, genbuf); sethomedir(genbuf, hacker); return do_append(genbuf, &mymail, sizeof(mymail)); } /* setup the hostname and username */ if (!configured) { /* get host name */ gethostname(myhostname, STRLEN); hbuf = gethostbyname(myhostname); if (hbuf) strncpy(myhostname, hbuf->h_name, STRLEN); /* get bbs uident */ pbuf = getpwuid(getuid()); if (pbuf) strncpy(myusername, pbuf->pw_name, 20); if (hbuf && pbuf) configured = 1; else return -1; } /* Running the sendmail */ #ifdef INTERNET_PRIVATE_EMAIL if (fpath == NULL) { sprintf(genbuf, "/usr/sbin/sendmail %s > /dev/null", receiver); fin = fopen("etc/confirm", "r"); } else { sprintf(genbuf, "/usr/sbin/sendmail -f %s%s %s > /dev/null" , cuser.userid, str_mail_address, receiver); fin = fopen(fpath, "r"); } fout = popen(genbuf, "w"); if (fin == NULL || fout == NULL) return -1; if (fpath) fprintf(fout, "Reply-To: %s%s\nFrom: %s%s\n", cuser.userid, str_mail_address, cuser.userid, str_mail_address); #else sprintf(genbuf, "/usr/sbin/sendmail %s > /dev/null", receiver); fout = popen(genbuf, "w"); fin = fopen(fpath ? fpath : "etc/confirm", "r"); if (fin == NULL || fout == NULL) return -1; if (fpath) fprintf(fout, "From: %s@%s (%s)\n", myusername, myhostname, BBSNAME); #endif fprintf(fout, "To: %s\nSubject: %s\n", receiver, title); fprintf(fout, "X-Disclaimer: 批踢踢實業坊" BOARDNAME "對本信內容恕不負責。\n\n"); while (fgets(genbuf, 255, fin)) { if (genbuf[0] == '.' && genbuf[1] == '\n') fputs(". \n", fout); else fputs(genbuf, fout); } fclose(fin); fprintf(fout, ".\n"); pclose(fout); return 0; } int doforward(direct, fh, mode) char *direct; fileheader *fh; int mode; /* 是否 uuencode */ { static char address[60]; char fname[STRLEN]; int return_no; char genbuf[200]; if (!address[0]) strcpy(address, cuser.email); if (address[0]) { sprintf(genbuf, "確定轉寄給 [%s] 嗎(Y/N/Q)?[Y] ", address); getdata(b_lines - 1, 0, genbuf, fname, 3, LCECHO); if (fname[0] == 'q') { outmsg("取消轉寄"); return 1; } if (fname[0] == 'n') address[0] = '\0'; } if (!address[0]) { getdata(b_lines - 1, 0, "請輸入轉寄地址:", fname, 60, DOECHO); if (fname[0]) { if (strchr(fname, '.')) strcpy(address, fname); else sprintf(address, "%s.bbs@%s", fname, MYHOSTNAME); } else { outmsg("取消轉寄"); return 1; } } if (invalidaddr(address)) return -2; sprintf(fname, "正轉寄給 %s, 請稍候...", address); outmsg(fname); move(b_lines - 1, 0); refresh(); if (mode) { char tmp_buf[128]; sprintf(fname, "/tmp/bbs.uu%05d", currpid); sprintf(tmp_buf, "/usr/bin/uuencode %s/%s uu.%05d > %s", direct, fh->filename, currpid, fname); system(tmp_buf); sleep(1); } else sprintf(fname, "%s/%s", direct, fh->filename); return_no = bbs_sendmail(fname, fh->title, address); if (mode) unlink(fname); return (return_no); } #endif int chkmail(rechk) int rechk; { static time_t lasttime = 0; static int ismail = 0; struct stat st; fileheader fh; int fd; register numfiles; unsigned char ch; if (!HAS_PERM(PERM_BASIC)) return 0; if (stat(currmaildir, &st) < 0) return (ismail = 0); if ((lasttime >= st.st_mtime) && !rechk) return ismail; lasttime = st.st_mtime; numfiles = st.st_size / sizeof(fileheader); if (numfiles <= 0) return (ismail = 0); /* ------------------------------------------------ */ /* 看看有沒有信件還沒讀過?從檔尾回頭檢查,效率較高 */ /* ------------------------------------------------ */ if ((fd = open(currmaildir, O_RDONLY)) > 0) { lseek(fd, st.st_size - 1, SEEK_SET); while (numfiles--) { read(fd, &ch, 1); if (!(ch & FILE_READ)) { close(fd); return (ismail = 1); } lseek(fd, (off_t) -sizeof(fileheader) - 1, SEEK_CUR); } close(fd); } return (ismail = 0); } #ifdef EMAIL_JUSTIFY void mail_justify(userec muser) { fileheader mhdr; char title[128], buf1[80]; FILE* fp; sethomepath(buf1, muser.userid); stampfile(buf1, &mhdr); unlink(buf1); strcpy(mhdr.owner, cuser.userid); strncpy(mhdr.title, "[審核通過]", TTLEN); mhdr.savemode = 0; mhdr.filemode = 0; /* woju more("etc/justify", NA); */ if (valid_ident(muser.email) && !invalidaddr(muser.email)) { char title[80], *ptr; ushort checksum; /* 16-bit is enough */ char ch; checksum = getuser(muser.userid); ptr = muser.email; while (ch = *ptr++) { if (ch <= ' ') break; if (ch >= 'A' && ch <= 'Z') ch |= 0x20; checksum = (checksum << 1) ^ ch; } sprintf(title, "[PTT BBS]To %s(%d:%d) [User Justify]", muser.userid, getuser(muser.userid) + MAGIC_KEY, checksum); if (bbs_sendmail(NULL, title, muser.email) < 0) Link("etc/bademail", buf1); else Link("etc/replyemail", buf1); } else Link("etc/bademail", buf1); sethomedir(title, muser.userid); append_record(title, &mhdr, sizeof(mhdr)); } #endif /* EMAIL_JUSTIFY */