diff options
Diffstat (limited to 'mbbsd/mail.c')
-rw-r--r-- | mbbsd/mail.c | 1675 |
1 files changed, 1675 insertions, 0 deletions
diff --git a/mbbsd/mail.c b/mbbsd/mail.c new file mode 100644 index 00000000..e480abb2 --- /dev/null +++ b/mbbsd/mail.c @@ -0,0 +1,1675 @@ +/* $Id: mail.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <string.h> +#include "config.h" +#include "pttstruct.h" +#include "common.h" +#include "perm.h" +#include "modes.h" +#include "proto.h" + +extern int TagNum; +extern int b_lines; /* Screen bottom line number: t_lines-1 */ +extern char save_title[]; /* used by editor when inserting */ +extern int curredit; +extern char *err_uid; +extern char *msg_cancel; +extern char *msg_uid; +extern char *fn_overrides; +extern char quote_file[80]; +extern char quote_user[80]; +extern char *fn_notes; +extern char *msg_mailer; +extern char *msg_sure_ny; +extern char *BBSName; +extern char currtitle[44]; +extern unsigned char currfmode; /* current file mode */ +extern char *msg_del_ny; +extern char currfile[FNLEN]; +extern int currmode; +extern char currboard[]; /* name of currently selected board */ +extern char *str_space; +extern char *str_author1; +extern char *str_author2; +extern userinfo_t *currutmp; +extern unsigned int currstat; +extern pid_t currpid; +extern int usernum; +extern char *str_mail_address; +extern userec_t cuser; + +char currmaildir[32]; +static char msg_cc[] = "\033[32m[群組名單]\033[m\n"; +static char listfile[] = "list.0"; +static int mailkeep = 0, mailsum = 0; +static int mailsumlimit = 0,mailmaxkeep = 0; + +int setforward() { + 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(); + return 0; + } + } + move(b_lines,0); + clrtoeol(); + outs("取消自動轉信!"); + unlink(buf); + refresh(); + return 0; +} + +int built_mail_index() { + char genbuf[128]; + + getdata(b_lines, 0, + "重建信箱?(警告:請確定信箱有問題時才使用)(y/N)", genbuf, 3, + LCECHO); + if(genbuf[0] != 'y') return 0; + + sprintf(genbuf, BBSHOME "/bin/buildir " BBSHOME "/home/%c/%s", + cuser.userid[0], cuser.userid); + move(22,0); + prints("\033[1;31m已經處理完畢!! 諸多不便 敬請原諒~\033[m");pressanykey(); + system(genbuf); + return 0; +} + +int mailalert(char *userid) +{ + userinfo_t *uentp=NULL; + int n,tuid,i; + + if((tuid=searchuser(userid))==0) return -1; + + n=count_logins(tuid, 0); + for(i=1;i<=n;i++) + if((uentp = (userinfo_t *)search_ulistn(tuid, i))) + uentp->mailalert=1; + return 0; +} + +int mail_muser(userec_t muser, char *title, char *filename) { + return mail_id(muser.userid, title, filename, cuser.userid); +} + +/* Heat: 用id來寄信,內容則link準備好的檔案 */ +int mail_id(char* id, char *title, char *filename, char *owner) { + fileheader_t mhdr; + char genbuf[128]; + sethomepath(genbuf, id); + if(stampfile(genbuf, &mhdr)) + return 0; + strcpy(mhdr.owner, owner); + strncpy(mhdr.title, title, TTLEN); + mhdr.savemode = 0; + mhdr.filemode = 0; + Link(filename, genbuf); + sethomedir(genbuf,id); + append_record(genbuf, &mhdr, sizeof(mhdr)); + mailalert(id); + return 0; +} + +int invalidaddr(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; +} + +void m_init() { + sethomedir(currmaildir, cuser.userid); +} + +int chkmailbox() { + if(!HAVE_PERM(PERM_SYSOP) && !HAVE_PERM(PERM_MAILLIMIT)) { + int max_keepmail = MAX_KEEPMAIL; + if ( HAS_PERM(PERM_SYSSUBOP) || HAS_PERM(PERM_SMG) || + HAS_PERM(PERM_PRG) || HAS_PERM(PERM_ACTION) || HAS_PERM(PERM_PAINT)) + { + mailsumlimit = 700; + max_keepmail = 500; + } + else if(HAS_PERM(PERM_BM)) + { + mailsumlimit = 500; + max_keepmail = 300; + } + else if(HAS_PERM(PERM_LOGINOK)) + mailsumlimit = 200; + else + mailsumlimit = 50; + mailsumlimit += cuser.exmailbox * 10; + mailmaxkeep = max_keepmail + cuser.exmailbox; + m_init(); + if((mailkeep = get_num_records(currmaildir, sizeof(fileheader_t))) > + mailmaxkeep) { + move(b_lines, 0); + clrtoeol(); + bell(); + prints("您保存信件數目 %d 超出上限 %d, 請整理", + mailkeep, mailmaxkeep); + bell(); + refresh(); + igetch(); + return mailkeep; + } + if((mailsum = get_sum_records(currmaildir, sizeof(fileheader_t))) > + mailsumlimit) { + move(b_lines, 0); + clrtoeol(); + bell(); + prints("您保存信件容量 %d(k)超出上限 %d(k), 請整理", + mailsum, mailsumlimit); + bell(); + refresh(); + igetch(); + return mailkeep; + } + } + return 0; +} + +static void do_hold_mail(char *fpath, char *receiver, char *holder) { + char buf[80], title[128]; + + fileheader_t 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); + + unlink(buf); + Link(fpath, buf); + /* Ptt: append_record->do_append */ + do_append(title, &mymail, sizeof(mymail)); +} + +extern userec_t xuser; + +void hold_mail(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); +} + +int do_send(char *userid, char *title) { + fileheader_t mhdr; + char fpath[STRLEN]; + char receiver[IDLEN]; + char genbuf[200]; + int internet_mail, i; + + if(strchr(userid, '@')) + internet_mail = 1; + else { + internet_mail = 0; + if(!getuser(userid)) + return -1; + if(!(xuser.userlevel & PERM_READMAIL)) + return -3; + + if(!title) + getdata(2, 0, "主題:", save_title, TTLEN, DOECHO); + curredit |= EDIT_MAIL; + curredit &= ~EDIT_ITEM; + } + + setutmpmode(SMAIL); + + fpath[0] = '\0'; + + if(internet_mail) { + int res, ch; + + if(vedit(fpath, NA, NULL) == -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 = +#ifndef USE_BSMTP + bbs_sendmail(fpath, title, userid); +#else + bsmtp(fpath, title, userid,0); +#endif + hold_mail(fpath, userid); + } + unlink(fpath); + return res; + } else { + strcpy(receiver, userid); + sethomepath(genbuf, userid); + stampfile(genbuf, &mhdr); + strcpy(mhdr.owner, cuser.userid); + strncpy(mhdr.title, save_title, TTLEN); + mhdr.savemode = '\0'; + if(vedit(genbuf, YEA, NULL) == -1) { + unlink(genbuf); + clear(); + return -2; + } + clear(); + sethomefile(fpath, userid, FN_OVERRIDES); + i=belong(fpath, cuser.userid); + sethomefile(fpath, userid, FN_REJECT); + + if(i || !belong(fpath, cuser.userid)) //Ptt:用belong有點討厭 + { + sethomedir(fpath, userid); + if(append_record(fpath, &mhdr, sizeof(mhdr)) == -1) + return -1; + mailalert(userid); + } + hold_mail(genbuf, userid); + return 0; + } +} + +void my_send(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); + showplans(uident); + if(uident[0]) + my_send(uident); + return 0; +} + +/* 群組寄信、回信 : multi_send, multi_reply */ +extern struct word_t *toplev; + +static void multi_list(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; + default: + return; + } + } +} + +static void multi_send(char *title) { + FILE *fp; + struct word_t *p; + fileheader_t 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, NULL) == -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); + mailalert(p->word); + } + hold_mail(fpath, NULL); + unlink(fpath); + curredit = 0; + } else + outs(msg_cancel); + pressanykey(); +} + +static int multi_reply(int ent, fileheader_t *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; +} + +int mail_all() { + FILE *fp; + fileheader_t mymail; + char fpath[TTLEN]; + char genbuf[200]; + extern struct uhash_t *uhash; + int i, unum; + char *userid; + + stand_title("給所有使用者的系統通告"); + setutmpmode(SMAIL); + getdata(2, 0, "主題:", fpath, 64, DOECHO); + sprintf(save_title, "[系統通告]\033[1;32m %s\033[m", fpath); + + setuserfile(fpath, fn_notes); + + if((fp = fopen(fpath, "w"))) { + fprintf(fp, "※ [\033[1m系統通告\033[m] 這是封給所有使用者的信\n"); + fprintf(fp, "-----------------------------------------------------" + "----------------------\n"); + fclose(fp); + } + + *quote_file = 0; + + curredit |= EDIT_MAIL; + curredit &= ~EDIT_ITEM; + if(vedit(fpath, YEA, NULL) == -1) { + curredit = 0; + unlink(fpath); + outs(msg_cancel); + pressanykey(); + return 0; + } + 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; + + sethomedir(genbuf, cuser.userid); + if(append_record(genbuf, &mymail, sizeof(mymail)) == -1) + outs(err_uid); + + for(unum = uhash->number, i = 0; i < unum; i++) { + if(bad_user_id(uhash->userid[i])) + continue; /* Ptt */ + + userid = uhash->userid[i]; + if(strcmp(userid,STR_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 0; +} + +int mail_mbox() { + char cmd[100]; + fileheader_t fhdr; + + sprintf(cmd, "/tmp/%s.uu", cuser.userid); + sprintf(fhdr.title, "%s 私人資料", cuser.userid); + doforward(cmd, &fhdr, 'Z'); + return 0; +} + +static int m_forward(int ent, fileheader_t *fhdr, char *direct) { + char uid[STRLEN]; + + 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; +} + +static int delmsgs[128]; +static int delcnt; +static int mrd; + +static int read_new_mail(fileheader_t *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; + curredit &= ~EDIT_ITEM; + if(apply_record(currmaildir, read_new_mail, sizeof(fileheader_t)) == -1) { + outs("沒有新信件了"); + pressanykey(); + return -1; + } + curredit = 0; + if(delcnt) { + while(delcnt--) + delete_record(currmaildir, sizeof(fileheader_t), delmsgs[delcnt]); + } + outs(mrd ? "信已閱\畢" : "沒有新信件了"); + pressanykey(); + return -1; +} + +static void mailtitle() { + char buf[256] = ""; + + showtitle("\0郵件選單", BBSName); + sprintf(buf,"[←]離開[↑↓]選擇[→]閱\讀信件 [R]回信 [x]轉達 " + "[y]群組回信 [O]站外信:%s [h]求助\n\033[7m" + "編號 日 期 作 者 信 件 標 題 \033[32m", + HAS_PERM(PERM_NOOUTMAIL)? "\033[31m關\033[m":"開"); + outs(buf); + buf[0]=0; + if(mailsumlimit) { + sprintf(buf,"(容量:%d/%dk %d/%d篇)", mailsum, mailsumlimit, + mailkeep, mailmaxkeep); + } + sprintf(buf, "%s%*s\033[m", buf, 29 - (int) strlen(buf), ""); + outs(buf); +} + +static void maildoent(int num, fileheader_t *ent) { + char *title, *mark, color, type = "+ Mm"[ent->filemode]; + + if (TagNum && !Tagger(atoi(ent->filename + 2), 0, TAG_NIN)) + 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.14s\033[1;3%cm%s %.46s\033[0m\n", num, type, + ent->date, ent->owner, color, mark, title); +} + +#ifdef POSTBUG +extern int bug_possible; +#endif + + +static int m_idle(int ent, fileheader_t *fhdr, char *direct) { + t_idle(); + return FULLUPDATE; +} + +static int mail_del(int ent, fileheader_t *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') { + 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_t)); + delete_file(genbuf, sizeof(fileheader_t), now, cmpfilename); + } + return DIRCHANGED; + } + } + return FULLUPDATE; +} + +static int mail_read(int ent, fileheader_t *fhdr, char *direct) { + char buf[64]; + char done, delete_it, replied; + + clear(); + setdirpath(buf, direct, fhdr->filename); + strncpy(currtitle, subject(fhdr->title), 40); + done = delete_it = replied = NA; + while(!done) { + int more_result = more(buf, YEA); + + if(more_result != -1) { + fhdr->filemode |= FILE_READ; + if((currmode & MODE_SELECT)) { + int now; + + now = getindex(currmaildir, fhdr->filename, + sizeof(fileheader_t)); + substitute_record(currmaildir, fhdr, sizeof(*fhdr), now); + substitute_record(direct, fhdr, sizeof(*fhdr), ent); + } + else + 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 + if((currmode & MODE_SELECT)) { + int now; + + now = getindex(currmaildir, fhdr->filename, sizeof(fileheader_t)); + substitute_record(currmaildir, fhdr, sizeof(*fhdr), now); + substitute_record(direct, fhdr, sizeof(*fhdr), ent); + } else + substitute_record(currmaildir, fhdr, sizeof(*fhdr), ent); +#ifdef POSTBUG + bug_possible = NA; +#endif + } + return FULLUPDATE; +} + +/* in boards/mail 回信給原作者,轉信站亦可 */ +int mail_reply(int ent, fileheader_t *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; +} + +static int mail_edit(int ent, fileheader_t *fhdr, char *direct) { + char genbuf[200]; + + if(!HAS_PERM(PERM_SYSOP) && + strcmp(cuser.userid, fhdr->owner) && + strcmp("[備.忘.錄]", fhdr->owner)) + return DONOTHING; + + setdirpath(genbuf, direct, fhdr->filename); + vedit(genbuf, NA, NULL); + return FULLUPDATE; +} + +static int mail_nooutmail(int ent, fileheader_t *fhdr, char *direct) +{ + cuser.userlevel ^= PERM_NOOUTMAIL; + passwd_update(usernum, &cuser); + return FULLUPDATE; + +} + +static int mail_mark(int ent, fileheader_t *fhdr, char *direct) { + fhdr->filemode ^= FILE_MARKED; + + if((currmode & MODE_SELECT)) { + int now; + + now = getindex(currmaildir, fhdr->filename, sizeof(fileheader_t)); + substitute_record(currmaildir, fhdr, sizeof(*fhdr), now); + substitute_record(direct, fhdr, sizeof(*fhdr), ent); + } else + substitute_record(currmaildir, fhdr, sizeof(*fhdr), ent); + return PART_REDRAW; +} + +/* help for mail reading */ +static char *mail_help[] = { + "\0電子信箱操作說明", + "\01基本命令", + "(p)(↑) 前一篇文章", + "(n)(↓) 下一篇文章", + "(P)(PgUp) 前一頁", + "(N)(PgDn) 下一頁", + "(##)(cr) 跳到第 ## 筆", + "($) 跳到最後一筆", + "\01進階命令", + "(r)(→)/(R)讀信 / 回信", + "(O) 關閉/開啟 站外信件轉入", + "(c/z) 收入此信件進入私人信件夾/進入私人信件夾", + "(x/X) 轉達信件/轉錄文章到其他看板", + "(y) 群組回信", + "(F) 將信傳送回您的電子信箱 (u)水球整理寄回信箱", + "(d) 殺掉此信", + "(D) 殺掉指定範圍的信", + "(m) 將信標記,以防被清除", + "(^G) 立即重建信箱 (信箱毀損時用)", + "(t) 標記欲刪除信件", + "(^D) 刪除已標記信件", + NULL +}; + +static int m_help() { + show_help(mail_help); + return FULLUPDATE; +} + +static int mail_cross_post(int ent, fileheader_t *fhdr, char *direct) { + char xboard[20], fname[80], xfpath[80], xtitle[80], inputbuf[10]; + fileheader_t 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,0); + fclose(xptr); + } else { + unlink(xfpath); + Link(fname, xfpath); + } + + setbdir(fname, xboard); + append_record(fname, &xfile, sizeof(xfile)); + setbtotal(getbnum(xboard)); + if(!xfile.filemode) + outgo_post(&xfile, xboard); + cuser.numposts++; + passwd_update(usernum, &cuser); + outs("文章轉錄完成"); + pressanykey(); + currmode = currmode0; + } + return FULLUPDATE; +} + +int 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, 1); + currutmp->mode = mode0; + currstat = stat0; + return FULLUPDATE; + } + return DONOTHING; +} + +static int mail_cite(int ent, fileheader_t *fhdr, char *direct) { + char fpath[256]; + char title[TTLEN + 1]; + static char xboard[20]; + char buf[20]; + boardheader_t *bp; + + setuserfile(fpath, fhdr->filename); + strcpy(title, "◇ "); + strncpy(title+3, fhdr->title, TTLEN-3); + title[TTLEN] = '\0'; + a_copyitem(fpath, title, 0, 1); + + 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(getbnum(xboard)))) { + setapath(fpath, xboard); + setutmpmode(ANNOUNCE); + a_menu(xboard, fpath, HAS_PERM(PERM_ALLBOARD) ? 2 : + is_BM(bp->BM) ? 1 : 0); + } else { + mail_man(); + } + return FULLUPDATE; + } else { + mail_man(); + return FULLUPDATE; + } +} + +static int mail_save(int ent, fileheader_t *fhdr, char *direct) { + char fpath[256]; + char title[TTLEN+1]; + + 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, 1); + sethomeman(fpath, cuser.userid); + a_menu(cuser.userid, fpath, 1); + return FULLUPDATE; + } + return DONOTHING; +} + +#ifdef OUTJOBSPOOL +static int mail_waterball(int ent, fileheader_t *fhdr, char *direct) +{ + static char address[60], cmode = 1; + char fname[500], genbuf[200]; + FILE *fp; + int now; + + if(!address[0]) + strcpy(address, cuser.email); + if(address[0]) { + sprintf(genbuf, "寄給 [%s] 嗎(Y/N/Q)?[Y] ", address); + getdata(b_lines - 2, 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 - 2, 0, "請輸入郵件地址:", fname, 60, DOECHO); + if(fname[0] && strchr(fname, '.')) { + strcpy(address, fname); + } else { + outmsg("取消處理"); + return 1; + } + } + if(invalidaddr(address)) + return -2; + + // sprintf(fname, "%d\n", cmode); + getdata(b_lines - 1, 0, "使用模式(0/1)? [1]", fname, 3, LCECHO); + cmode = (fname[0] != '0' && fname[0] != '1') ? 1 : fname[0] - '0'; + + now = time(NULL); + sprintf(fname, BBSHOME "/jobspool/water.src.%s-%d", + cuser.userid, now); + sprintf(genbuf, "cp " BBSHOME "/home/%c/%s/%s %s", + cuser.userid[0], cuser.userid, fhdr->filename, fname); + system(genbuf); + /* dirty code ;x */ + sprintf(fname, BBSHOME "/jobspool/water.des.%s-%d", + cuser.userid, now); + fp = fopen(fname, "wt"); + fprintf(fp, "%s\n%s\n%d\n", cuser.userid, address, cmode); + fclose(fp); + return FULLUPDATE; +} +#endif +static struct onekey_t 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}, + {'O', mail_nooutmail}, + {'T', edit_title}, + {'x', m_forward}, + {'X', mail_cross_post}, + {Ctrl('G'), built_mail_index}, /* 修信箱 */ + {'y', multi_reply}, + {Ctrl('I'), m_idle}, + {'h', m_help}, +#ifdef OUTJOBSPOOL + {'u', mail_waterball}, +#endif + {'\0', NULL} +}; + +int m_read() { + if(get_num_records(currmaildir, sizeof(fileheader_t))) { + curredit = EDIT_MAIL; + curredit &= ~EDIT_ITEM; + i_read(RMAIL, currmaildir, mailtitle, maildoent, mail_comms, -1); + curredit = 0; + currutmp->mailalert = load_mailalert(cuser.userid); + return 0; + } else { + outs("您沒有來信"); + return XEASY; + } +} + +/* 寄站內信 */ +static int send_inner_mail(char *fpath, char *title, char *receiver) { + char genbuf[256]; + fileheader_t mymail; + + if(!searchuser(receiver)) + return -2; + sethomepath(genbuf, receiver); + stampfile(genbuf, &mymail); + if(!strcmp(receiver, cuser.userid)) { + strcpy(mymail.owner, "[" BBSNAME "]"); + mymail.filemode = FILE_READ; + } else + strcpy(mymail.owner, cuser.userid); + strncpy(mymail.title, title, TTLEN); + unlink(genbuf); + Link(fpath, genbuf); + sethomedir(genbuf, receiver); + return do_append(genbuf, &mymail, sizeof(mymail)); +} + +#include <netdb.h> +#include <pwd.h> +#include <time.h> + +#ifndef USE_BSMTP +static int bbs_sendmail(char *fpath, char *title, char *receiver) { + static int configured = 0; + static char myhostname[STRLEN]; + static char myusername[20]; + struct hostent *hbuf; + struct passwd *pbuf; + char *ptr; + char genbuf[256]; + FILE *fin, *fout; + + /* 中途攔截 */ + if((ptr = strchr(receiver, ';'))) { + struct tm *ptime; + time_t now; + + *ptr = '\0'; + } + + if((ptr = strstr(receiver, str_mail_address)) || !strchr(receiver,'@')) { + char hacker[20]; + int len; + + if(strchr(receiver,'@')) { + len = ptr - receiver; + memcpy(hacker, receiver, len); + hacker[len] = '\0'; + } else + strcpy(hacker,receiver); + return send_inner_mail(fpath, title, hacker); + } + + /* setup the hostname and username */ + if(!configured) { + /* get host name */ + hbuf = gethostbyname("localhost"); + 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 */ + 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); + fprintf(fout, "To: %s\nSubject: %s\n", receiver, title); + fprintf(fout, "X-Disclaimer: " BBSNAME "對本信內容恕不負責。\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; +} +#else /* USE_BSMTP */ + +int bsmtp(char *fpath, char *title, char *rcpt, int method) { + char buf[80], *ptr; + time_t chrono; + MailQueue mqueue; + + /* check if the mail is a inner mail */ + if((ptr = strstr(rcpt, str_mail_address)) || !strchr(rcpt, '@')) { + char hacker[20]; + int len; + + if(strchr(rcpt,'@')) { + len = ptr - rcpt; + memcpy(hacker, rcpt, len); + hacker[len] = '\0'; + } else + strcpy(hacker, rcpt); + return send_inner_mail(fpath, title, hacker); + } + + chrono = time(NULL); + if(method != MQ_JUSTIFY) { /* 認證信 */ + /* stamp the queue file */ + strcpy(buf, "out/"); + for(;;) { + sprintf(buf + 4,"M.%ld.A", ++chrono); + if(!dashf(buf)) { + Link(fpath, buf); + break; + } + } + + fpath = buf; + + strcpy(mqueue.filepath, fpath); + strcpy(mqueue.subject, title); + } + /* setup mail queue */ + mqueue.mailtime = chrono; + mqueue.method = method; + strcpy(mqueue.sender, cuser.userid); + strcpy(mqueue.username, cuser.username); + strcpy(mqueue.rcpt, rcpt); + if(do_append("out/.DIR", (fileheader_t *)&mqueue, sizeof(mqueue)) < 0) + return 0; + return chrono; +} +#endif /* USE_BSMTP */ + +int doforward(char *direct, fileheader_t *fh, int mode) { + static char address[60]; + char fname[500]; + 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]) { + do{ + 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; + } + }while(mode=='Z' && strstr(address, MYHOSTNAME)); + } + if(invalidaddr(address)) + return -2; + + sprintf(fname, "正轉寄給 %s, 請稍候...", address); + outmsg(fname); + move(b_lines - 1, 0); + refresh(); + + /* 追蹤使用者 */ + if(HAS_PERM(PERM_LOGUSER)) { + time_t now = time(NULL); + char msg[200]; + + sprintf(msg, "%s mailforward to %s at %s", + cuser.userid, address, Cdate(&now)); + log_user(msg); + } + + if(mode == 'Z') { + sprintf(fname, TAR_PATH " cfz - home/%c/%s | " + "/usr/bin/uuencode %s.tgz > %s", + cuser.userid[0], cuser.userid, cuser.userid, direct); + system(fname); + strcpy(fname, direct); + } else if(mode == 'U') { + 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); + } else if (mode == 'F'){ + char tmp_buf[128]; + + sprintf(fname, "/tmp/bbs.f%05d", currpid); + sprintf(tmp_buf, "cp %s/%s %s",direct,fh->filename,fname); + system(tmp_buf); + } else + return -1; + + return_no = +#ifndef USE_BSMTP + bbs_sendmail(fname, fh->title, address); +#else + bsmtp(fname, fh->title, address,mode); +#endif + unlink(fname); + return (return_no); +} + +int load_mailalert(char *userid) { + struct stat st; + char maildir[256]; + int fd; + register int numfiles; + fileheader_t my_mail; + + sethomedir(maildir, userid); + if(!HAS_PERM(PERM_BASIC)) + return 0; + if(stat(maildir, &st) < 0) + return 0; + numfiles = st.st_size / sizeof(fileheader_t); + if(numfiles <= 0) + return 0; + + /* 看看有沒有信件還沒讀過?從檔尾回頭檢查,效率較高 */ + if((fd = open(maildir, O_RDONLY)) > 0) { + lseek(fd, st.st_size - sizeof(fileheader_t), SEEK_SET); + while(numfiles--) { + read(fd, &my_mail, sizeof(fileheader_t)); + if(!(my_mail.filemode & FILE_READ)) { + close(fd); + return 1; + } + lseek(fd, -(off_t)2 * sizeof(fileheader_t), SEEK_CUR); + } + close(fd); + } + return 0; +} + +#ifdef EMAIL_JUSTIFY +static void mail_justify(userec_t muser) { + fileheader_t 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; + + if(valid_ident(muser.email) && !invalidaddr(muser.email)) { + char title[80], *ptr; + unsigned short checksum; /* 16-bit is enough */ + char ch; + + checksum = searchuser(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( +#ifndef USE_BSMTP + bbs_sendmail(NULL, title, muser.email) +#else + bsmtp(NULL, title, muser.email, MQ_JUSTIFY); +#endif + < 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 */ |