summaryrefslogblamecommitdiffstats
path: root/mbbsd/mail.c
blob: 6a635f39825f6cd765e123bfe7d2d96c08b41f4e (plain) (tree)
1
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
                                                     




























                                                                            
                                 































                                                                      
                                        
                               
                                                                          




















































































                                                                              
                                                                   
                                                         
                                                              




























































































                                                                               
                                      





















                                                    
                                                                   























































































































































































































                                                                               
                                                                  





































































































                                                                    
                                                          

































































































































































































































































                                                                              
                                        















































                                                                             
                                                    

















































































































































































































































                                                                              
                                                            






































































































































































































































































































































































































































                                                                             






                                                                               













































































































                                                                           
/* $Id: mail.c,v 1.5 2002/04/30 11:15:29 ptt 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[TTLEN + 1];
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, sizeof(ip), DOECHO);
    if(ip[0] && ip[0] != ' ') {
    getdata(b_lines, 0, "確定開啟自動轉信功\能?(Y/n)", yn, sizeof(yn),
        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, sizeof(receiver), DOECHO);
    if(strchr(receiver, '@') && !invalidaddr(receiver) &&
       getdata(21, 0, "主  題:", save_title, STRLEN, 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, sizeof(buf), 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, STRLEN-20, 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, sizeof(fpath), 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, sizeof(fpath), 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, TTLEN))
    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), TTLEN);
    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, sizeof(genbuf2), 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 /tmp/home.%s.tgz home/%c/%s; "
        MUTT_PATH" -a /tmp/home.%s.tgz -s 'home.%s.tgz' %s </dev/null;"
        "rm /tmp/home.%s.tgz",
        cuser.userid, cuser.userid[0], cuser.userid,
        cuser.userid, cuser.userid, address, cuser.userid);
    system(fname);
    return 0;
    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 */