diff options
author | piaip <piaip@63ad8ddf-47c3-0310-b6dd-a9e9d9715204> | 2010-11-07 02:15:37 +0800 |
---|---|---|
committer | piaip <piaip@63ad8ddf-47c3-0310-b6dd-a9e9d9715204> | 2010-11-07 02:15:37 +0800 |
commit | 1e225bea45722d52335eca19d4fdc36594be3f00 (patch) | |
tree | ce142ecb1675df7aceba9b0efcc48c4d9f20040c | |
parent | fac0f8f0c23b5cf8448970970adbba33ffdab6e4 (diff) | |
download | pttbbs-1e225bea45722d52335eca19d4fdc36594be3f00.tar pttbbs-1e225bea45722d52335eca19d4fdc36594be3f00.tar.gz pttbbs-1e225bea45722d52335eca19d4fdc36594be3f00.tar.bz2 pttbbs-1e225bea45722d52335eca19d4fdc36594be3f00.tar.lz pttbbs-1e225bea45722d52335eca19d4fdc36594be3f00.tar.xz pttbbs-1e225bea45722d52335eca19d4fdc36594be3f00.tar.zst pttbbs-1e225bea45722d52335eca19d4fdc36594be3f00.zip |
improvements to 'delete' system:
- refine del_range to distinguish from mail, board, and man
- add D (del_range) to timcap
git-svn-id: http://opensvn.csie.org/pttbbs/trunk@5215 63ad8ddf-47c3-0310-b6dd-a9e9d9715204
-rw-r--r-- | pttbbs/include/proto.h | 12 | ||||
-rw-r--r-- | pttbbs/mbbsd/announce.c | 84 | ||||
-rw-r--r-- | pttbbs/mbbsd/bbs.c | 387 | ||||
-rw-r--r-- | pttbbs/mbbsd/edit.c | 2 | ||||
-rw-r--r-- | pttbbs/mbbsd/mail.c | 65 | ||||
-rw-r--r-- | pttbbs/mbbsd/ordersong.c | 4 | ||||
-rw-r--r-- | pttbbs/mbbsd/record.c | 103 |
7 files changed, 404 insertions, 253 deletions
diff --git a/pttbbs/include/proto.h b/pttbbs/include/proto.h index d334b8ab..e22addd8 100644 --- a/pttbbs/include/proto.h +++ b/pttbbs/include/proto.h @@ -47,7 +47,8 @@ void pressanykey_or_callangel(void); #endif /* announce */ -int a_menu(const char *maintitle, const char *path, int lastlevel, int lastbid, char *trans_buffer); +int a_menu(const char *maintitle, const char *path, int lastlevel, int lastbid, + char *trans_buffer, const char *backup_dir); void a_copyitem(const char* fpath, const char* title, const char* owner, int mode); int Announce(void); @@ -65,7 +66,7 @@ int assign_badpost(const char *userid, fileheader_t *fhdr, const char *newpath, /* bbs */ int is_file_owner(const fileheader_t *, const userec_t*); void delete_allpost(const char *userid); -int del_range(int ent, const fileheader_t *fhdr, const char *direct); +int del_range(int ent, const fileheader_t *fhdr, const char *direct, const char *backup_direct); int cmpfowner(fileheader_t *fhdr); int b_note_edit_bname(int bid); int Read(void); @@ -498,6 +499,13 @@ int rotate_text_logfile(const char *filename, off_t max_size, float keep_ratio); int rotate_bin_logfile(const char *filename, off_t record_size, off_t max_size, float keep_ratio); +int delete_file_content(const char *direct, const fileheader_t *fh, + const char *backup_direct, + char *newpath, size_t sznewpath); +#define DELETE_FILE_CONTENT_SUCCESS (0) +#define DELETE_FILE_CONTENT_FAILED (-1) +#define DELETE_FILE_CONTENT_BACKUP_FAILED (1) +#define IS_DELETE_FILE_CONTENT_OK(x) ((x) != DELETE_FILE_CONTENT_FAILED) /* register */ int u_register(void); diff --git a/pttbbs/mbbsd/announce.c b/pttbbs/mbbsd/announce.c index 654afa1d..6b0aec0b 100644 --- a/pttbbs/mbbsd/announce.c +++ b/pttbbs/mbbsd/announce.c @@ -756,21 +756,25 @@ a_moveitem(menu_t * pm) } static void -a_delrange(menu_t * pm) +a_delrange(menu_t * pm, const char *backup_dir) { char fname[PATHLEN]; snprintf(fname, sizeof(fname), "%s/" FN_DIR, pm->path); - del_range(0, NULL, fname); + del_range(0, NULL, fname, backup_dir); pm->num = get_num_records(fname, FHSZ); } static void -a_delete(menu_t * pm) +a_delete(menu_t * pm, const char *backup_dir) { char fpath[PATHLEN], buf[PATHLEN], cmd[PATHLEN]; char ans[4]; fileheader_t backup, *fhdr = &(pm->header[pm->now - pm->page]); + const char *msg_errsync = "刪除檔案失敗,請退回上層目錄後再重試一次", + *msg_errsync2 = "檔案可能已被它人刪除,請退回上層目錄再重進確認", + *msg_errbackup = "檔案已刪除但無法備份。請至 " BN_BUGREPORT + "報告您試圖刪除檔案的位置。"; snprintf(fpath, sizeof(fpath), "%s/%s", pm->path, fhdr->filename); @@ -781,42 +785,42 @@ a_delete(menu_t * pm) ans, sizeof(ans), LCECHO); if (ans[0] != 'y') return; - if (delete_record(buf, FHSZ, pm->now + 1) == -1) + if (delete_fileheader(buf, fhdr, pm->now + 1) == -1) { + vmsg(msg_errsync); return; + } } else if (dashl(fpath)) { getdata(b_lines - 1, 1, "您確定要刪除此 symbolic link 嗎(Y/N)?[N] ", ans, sizeof(ans), LCECHO); if (ans[0] != 'y') return; - if (delete_record(buf, FHSZ, pm->now + 1) == -1) + if (delete_fileheader(buf, fhdr, pm->now + 1) == -1) { + vmsg(msg_errsync); return; + } unlink(fpath); } else if (dashf(fpath)) { - // XXX we also check PERM_MAILLIMIT here because RMAIL - // may be not trusted... - const char *save_bn = ( HasUserPerm(PERM_MAILLIMIT) && (currstat & RMAIL) ) ? - BN_JUNK : BN_DELETED; - getdata(b_lines - 1, 1, "您確定要刪除此檔案嗎(Y/N)?[N] ", ans, sizeof(ans), LCECHO); if (ans[0] != 'y') return; - if (delete_record(buf, FHSZ, pm->now + 1) == -1) - return; - - setbpath(buf, save_bn); - stampfile(buf, &backup); - strlcpy(backup.owner, cuser.userid, sizeof(backup.owner)); - strlcpy(backup.title, fhdr->title + 2, sizeof(backup.title)); - - snprintf(cmd, sizeof(cmd), - "mv -f %s %s", fpath, buf); - system(cmd); - setbdir(buf, save_bn); - append_record(buf, &backup, sizeof(backup)); - setbtotal(getbnum(save_bn)); + if (delete_fileheader(buf, fhdr, pm->now + 1) == -1) { + vmsg(msg_errsync); + return; + } + + switch(delete_file_content(buf, fhdr, backup_dir, NULL, 0)) { + case DELETE_FILE_CONTENT_BACKUP_FAILED: + vmsg(msg_errbackup); + break; + case DELETE_FILE_CONTENT_FAILED: + vmsg(msg_errsync2); + break; + default: + break; + } } else if (dashd(fpath)) { @@ -829,8 +833,10 @@ a_delete(menu_t * pm) sizeof(ans), LCECHO); if (ans[0] != 'y') return; - if (delete_record(buf, FHSZ, pm->now + 1) == -1) + if (delete_fileheader(buf, fhdr, pm->now + 1) == -1) { + vmsg(msg_errsync); return; + } setapath(buf, save_bn); // XXX because this directory will hold folders from entire site, @@ -863,7 +869,7 @@ a_delete(menu_t * pm) ans, sizeof(ans), LCECHO); if (ans[0] != 'y') return; - if (delete_record(buf, FHSZ, pm->now + 1) == -1) + if (delete_fileheader(buf, fhdr, pm->now + 1) == -1) return; } pm->num--; @@ -873,14 +879,16 @@ static void a_newtitle(const menu_t * pm) { char buf[PATHLEN]; - fileheader_t item; + fileheader_t item, *fhdr; - memcpy(&item, &pm->header[pm->now - pm->page], FHSZ); + fhdr = &pm->header[pm->now - pm->page]; + memcpy(&item, fhdr, FHSZ); strlcpy(buf, item.title + 3, sizeof(buf)); if (getdata_buf(b_lines - 1, 0, " 新標題: ", buf, 60, DOECHO)) { strlcpy(item.title + 3, buf, sizeof(item.title) - 3); setadir(buf, pm->path); - substitute_record(buf, &item, FHSZ, pm->now + 1); + if (substitute_fileheader(buf, fhdr, &item, pm->now + 1) != 0) + vmsg("無法變更名稱,可能目錄有其它板主正在修改。請退出本層目錄後再重試。"); } } static void @@ -896,7 +904,9 @@ a_hideitem(const menu_t * pm) else item->filemode |= FILE_HIDE; setadir(buf, pm->path); - substitute_record(buf, item, FHSZ, pm->now + 1); + if (substitute_fileheader(buf, item, item, pm->now + 1) != 0) { + vmsg("無法變更,可能目錄有其它板主正在修改。請退出本層目錄後再重試。"); + } } static void a_editsign(const menu_t * pm) @@ -911,7 +921,9 @@ a_editsign(const menu_t * pm) item.title[1] = buf[1] ? buf[1] : ' '; item.title[2] = ' '; setadir(buf, pm->path); - substitute_record(buf, &item, FHSZ, pm->now + 1); + if (substitute_fileheader(buf, &item, &item, pm->now + 1) != 0) { + vmsg("無法變更,可能目錄有其它板主正在修改。請退出本層目錄後再重試。"); + } } } @@ -1046,6 +1058,7 @@ isvisible_man(const menu_t * me) typedef struct { char bReturnToRoot; // 用來跳出 recursion int z_indexes [STRLEN/2]; // each index code takes minimal 2 characters + const char *backup_dir; // 砍文章時要存到哪 } a_menu_session_t; // look up current location @@ -1624,11 +1637,11 @@ a_menu_rec(const char *maintitle, const char *path, case 'D': /* Ptt me.page = -1; */ - a_delrange(&me); + a_delrange(&me, sess->backup_dir); me.page = A_INVALID_PAGE; break; case 'd': - a_delete(&me); + a_delete(&me, sess->backup_dir); me.page = A_INVALID_PAGE; break; case 'H': @@ -1662,9 +1675,10 @@ a_menu_rec(const char *maintitle, const char *path, int a_menu(const char *maintitle, const char *path, int lastlevel, int lastbid, - char *trans_buffer) + char *trans_buffer, const char *backup_dir) { a_menu_session_t sess = {0}; + sess.backup_dir = backup_dir; return a_menu_rec(maintitle, path, lastlevel, lastbid, trans_buffer, &sess, NULL, NULL, NULL); @@ -1677,7 +1691,7 @@ Announce(void) a_menu(BBSNAME "佈告欄", "man", ((HasUserPerm(PERM_SYSOP) ) ? SYSOP : NOBODY), 0, - NULL); + NULL, NULL); return 0; } diff --git a/pttbbs/mbbsd/bbs.c b/pttbbs/mbbsd/bbs.c index de201cce..e81fd638 100644 --- a/pttbbs/mbbsd/bbs.c +++ b/pttbbs/mbbsd/bbs.c @@ -242,8 +242,8 @@ save_violatelaw(void) reload_money(); if (cuser.money < (int)cuser.vl_count * 1000) { snprintf(buf, sizeof(buf), - ANSI_COLOR(1;31) "這是你第 %d 次違反本站法規" - "必須繳出 %d " MONEYNAME "幣;但你目前只有 %d ,數量不足!!" + ANSI_COLOR(1;31) "這是你第 %d 次違反本站法規" + "必須繳出 %d " MONEYNAME "幣;但你目前只有 %d ,數量不足!!" ANSI_RESET, (int)cuser.vl_count, (int)cuser.vl_count * 1000, cuser.money); mvouts(22, 0, buf); @@ -798,8 +798,6 @@ outgo_post(const fileheader_t *fh, const char *board, const char *userid, const } } -#ifdef USE_TIME_CAPSULE - static void innd_cancel_post(const fileheader_t *fh, const char *fpath, const char *userid) { @@ -829,93 +827,37 @@ innd_cancel_post(const fileheader_t *fh, const char *fpath, const char *userid) } static int -cancelpost2(const fileheader_t *fh, char *newpath, size_t sznewpath) { - char fpath[PATHLEN]; +cancelpost(const char *direct, const fileheader_t *fh, + int not_owned, char *newpath, size_t sznewpath) { int ret = 0; + char bakdir[PATHLEN]; - if(!fh->filename[0]) - return -1; - - setbfile(fpath, currboard, fh->filename); - // if (!dashf(fpath)) return -1; +#ifdef USE_TIME_CAPSULE + setbdir(bakdir, currboard); +#else + const char *brd = not_owned ? BN_DELETED : BN_JUNK; + setbdir(bakdir, brd); +#endif - // TODO touch modify time to now? save the name who deleted it? - if (strncmp(fh->owner, RECYCLE_BIN_OWNER, strlen(RECYCLE_BIN_OWNER)) != 0) { - log_filef(fpath, LOG_CREAT, "\n※ Deleted by: %s (%s) %s", - cuser.userid, fromhost, Cdatelite(&now)); + ret = delete_file_content(direct, fh, bakdir, newpath, sznewpath); + if (!IS_DELETE_FILE_CONTENT_OK(ret)) + return ret; - if (!timecapsule_archive_new_revision( - fpath, fh, sizeof(*fh), newpath, sznewpath)) - ret = -1; - } +#ifdef USE_TIME_CAPSULE + // do nothing for capsule +#else + setbtotal(getbnum(brd)); +#endif - // the file should be already in time capsule - if (unlink(fpath) != 0) - ret = -1; - // should we use cuser.userid, or userid in post? // I don't know, simply following the old way in cancelpost... - if (!(currbrdattr & BRD_NOTRAN)) - innd_cancel_post(fh, fpath, cuser.userid); - - return ret; -} - -#else - -static int -cancelpost(const fileheader_t *fh, int by_BM, char *newpath) -{ - FILE *fin, *fout; - char *ptr, *brd; - fileheader_t postfile; - char genbuf[200]; - char nick[STRLEN], fn1[PATHLEN]; - int len = 42-strlen(currboard); - int ret = -1; - - if(!fh->filename[0]) return ret; - setbfile(fn1, currboard, fh->filename); - if ((fin = fopen(fn1, "r"))) { - brd = by_BM ? BN_DELETED : BN_JUNK; - - memcpy(&postfile, fh, sizeof(fileheader_t)); - setbpath(newpath, brd); - stampfile_u(newpath, &postfile); - - nick[0] = '\0'; - while (fgets(genbuf, sizeof(genbuf), fin)) { - if (!strncmp(genbuf, str_author1, LEN_AUTHOR1) || - !strncmp(genbuf, str_author2, LEN_AUTHOR2)) { - if ((ptr = strrchr(genbuf, ')'))) - *ptr = '\0'; - if ((ptr = (char *)strchr(genbuf, '('))) - strlcpy(nick, ptr + 1, sizeof(nick)); - break; - } - } - if(!strncasecmp(postfile.title, str_reply, 3)) - len=len+4; - sprintf(postfile.title, "%-*.*s.%s板", len, len, fh->title, currboard); - - if ((fout = fopen("innd/cancel.bntp", "a"))) { - fprintf(fout, "%s\t%s\t%s\t%s\t%s\n", currboard, fh->filename, - cuser.userid, nick, fh->title); - fclose(fout); - } - fclose(fin); - log_filef(fn1, LOG_CREAT, "\n※ Deleted by: %s (%s) %s", - cuser.userid, fromhost, Cdatelite(&now)); - ret = Rename(fn1, newpath); - setbdir(genbuf, brd); - append_record(genbuf, &postfile, sizeof(postfile)); - setbtotal(getbnum(brd)); + if (!(currbrdattr & BRD_NOTRAN)) { + innd_cancel_post(fh, newpath, cuser.userid); } + return ret; } -#endif - static void do_deleteCrossPost(const fileheader_t *fh, char bname[]) { @@ -943,10 +885,10 @@ do_deleteCrossPost(const fileheader_t *fh, char bname[]) #ifdef SAFE_ARTICLE_DELETE if(bp && !(currmode & MODE_DIGEST) && bp->nuser >= SAFE_ARTICLE_DELETE_NUSER) - safe_article_delete(i, &newfh, bdir, NULL); + safe_article_delete(i, &newfh, bdir, NULL); else #endif - delete_record(bdir, sizeof(fileheader_t), i); + delete_fileheader(bdir, &newfh, i); setbtotal(bid); unlink(file); } @@ -2363,20 +2305,29 @@ do_limitedit(int ent, fileheader_t * fhdr, const char *direct) static int b_man(void) { - char buf[PATHLEN]; + char apath[PATHLEN], backup_path[PATHLEN]; + +#ifdef USE_TIME_CAPSULE + setbdir(backup_path, currboard); +#else + boardheader_t *bp = getbcache(currbid); + setbdir(backup_path, (bp->brdattr & BRD_HIDE) ? BN_JUNK : BN_DELETED); +#endif + setapath(apath, currboard); - setapath(buf, currboard); if ((currmode & MODE_BOARD) || HasUserPerm(PERM_SYSOP)) { - char genbuf[128]; - int fd; - snprintf(genbuf, sizeof(genbuf), "%s/.rebuild", buf); - if ((fd = OpenCreate(genbuf, O_RDWR)) > 0) + char rebuild_path[PATHLEN]; + int fd; + + snprintf(rebuild_path, sizeof(rebuild_path), "%s/.rebuild", apath); + if ((fd = OpenCreate(rebuild_path, O_RDWR)) > 0) close(fd); } - return a_menu(currboard, buf, HasUserPerm(PERM_ALLBOARD) ? 2 : + + return a_menu(currboard, apath,HasUserPerm(PERM_ALLBOARD) ? 2 : (currmode & MODE_BOARD ? 1 : 0), currbid, // getbnum(currboard)? - NULL); + NULL, backup_path); } #ifndef NO_GAMBLE @@ -3137,75 +3088,145 @@ mark_post(int ent, fileheader_t * fhdr, const char *direct) } int -del_range(int ent, const fileheader_t *fhdr, const char *direct) +del_range(int ent, const fileheader_t *fhdr, const char *direct, + const char *backup_direct) { - char num1[8], num2[8]; - int inum1, inum2; - boardheader_t *bp = NULL; + char numstr[8]; + int num1, num2, num, cdeleted = 0; + fileheader_t *recs = NULL; + int ret = 0; + int use_safe_delete = 0; /* 有三種情況會進這裡, 信件, 看板, 精華區 */ - - if( direct[0] != 'h' && currbid) /* 信件不用 check */ - { - // 很不幸的是有一種是信件->mail_cite->精華區 - bp = getbcache(currbid); - if (is_readonly_board(bp->brdname)) - return DONOTHING; - } - + /* rocker.011018: 串接模式下還是不允許刪除比較好 */ if (currmode & MODE_SELECT) { vmsg("請先回到正常模式後再進行刪除..."); return FULLUPDATE; } - if ((currstat != READING) || (currmode & MODE_BOARD)) { - getdata(1, 0, "[設定刪除範圍] 起點:", num1, 6, DOECHO); - inum1 = atoi(num1); - if (inum1 <= 0) { - vmsg("起點有誤"); - return FULLUPDATE; - } - getdata(1, 28, "終點:", num2, 6, DOECHO); - inum2 = atoi(num2); - if (inum2 < inum1) { - vmsg("終點有誤"); - return FULLUPDATE; - } - getdata(1, 48, msg_sure_ny, num1, 3, LCECHO); - if (*num1 == 'y') { - int ret = 0; - outmsg("處理中,請稍後..."); - refresh(); + // let's do a full screen delete. + clear(); + vs_hdr("刪除範圍"); + + getdata(2, 0, "起點: ", numstr, 6, DOECHO); + num1 = atoi(numstr); + if (num1 <= 0) { + vmsg("起點有誤"); + return FULLUPDATE; + } + getdata(3, 0, "終點: ",numstr, 6, DOECHO); + num2 = atoi(numstr); + if (num2 < num1) { + vmsg("終點有誤"); + return FULLUPDATE; + } + num = num2 - num1 + 1; + if (num > 1000) { + vmsg("請勿一次刪除超過 1000 篇。"); + return FULLUPDATE; + } + + // verify the results + // TODO kcwu suggested to check only first/end and compare + // timestamp. that's a good idea and more efficient. + recs = (fileheader_t*) malloc ( num * sizeof(fileheader_t)); + if (!recs || + get_records(direct, recs, sizeof(fileheader_t), num1, num) != num) { + free(recs); + vmsg("無法取得指定範圍的資訊,請退出後稍候再試"); + return FULLUPDATE; + } + mvprints(5, 0, "#%06d %-*s %s\n" + " . . .\n" + "#%06d %-*s %s\n", + num1, IDLEN, recs[0].owner, recs[0].title, + num2, IDLEN, recs[num-1].owner, recs[num-1].title); + + getdata(10, 0, msg_sure_yn, numstr, 3, LCECHO); + if (*numstr != 'y') { + free(recs); + return FULLUPDATE; + } + + // ready to start. + outmsg("處理中,請稍後..."); + refresh(); + ret = 0; + #ifdef SAFE_ARTICLE_DELETE - if(bp && !(currmode & MODE_DIGEST) && - bp->nuser >= SAFE_ARTICLE_DELETE_NUSER) - ret = safe_article_delete_range(direct, inum1, inum2); - else + if (*direct == 'b') { + boardheader_t *bp = getbcache(currbid); + if(!(currmode & MODE_DIGEST) && + bp->nuser >= SAFE_ARTICLE_DELETE_NUSER) + use_safe_delete = 1; + } #endif - ret = delete_range(direct, inum1, inum2); - if (ret < 0) - { - clear(); - vs_hdr("刪除失敗"); - outs("\n\n無法刪除檔案。可能是同時有其它人也在進行刪除。\n\n" - "若此錯誤持續發生,請等約一小時後再重試。\n\n" - "若到時仍無法刪除,請到 " BN_SYSOP " 看板報告。\n"); - vmsg("無法刪除。可能有其它人正在同時刪除。"); - return FULLUPDATE; - } else - fixkeep(direct, inum1); - if ((curredit & EDIT_MAIL)==0 && (currmode & MODE_BOARD)) // Ptt:update cache - setbtotal(currbid); - else if(currstat == RMAIL) - setupmailusage(); + do { + int id = num1, i; + for (i = 0; ret == 0 && i < num; i++) { + // TODO now we can read file header and check MARK. +#ifdef SAFE_ARTICLE_DELETE + if (use_safe_delete && + safe_article_delete(id, recs+i, direct, NULL) == 0) { + id++; + } + else +#endif + if (delete_fileheader(direct, recs+i, id) == 0) { + // no need to add id + } else { + ret = -1; + break; + } + // delete file + if (!IS_DELETE_FILE_CONTENT_OK( + delete_file_content(direct, recs+i, + backup_direct, NULL, 0))) { + ret = -1; + break; + } + cdeleted++; + } + } while (0); - return DIRCHANGED; - } - return FULLUPDATE; + // clean up + free(recs); + fixkeep(direct, num1); + + if (ret < 0) { + clear(); + vs_hdr("部份刪除失敗"); + prints("\n\n已刪除了 %d 個檔案,但無法刪除其它檔案。\n", cdeleted); + outs( "可能是同時有其它人也在進行刪除。請退出此目錄後再重試。\n\n" + "若此錯誤持續發生,請等約一小時後再重試。\n\n" + "若到時仍無法刪除,請到 " BN_BUGREPORT " 看板報告。\n"); + vmsg("無法刪除。可能有其它人正在同時刪除。"); } - return DONOTHING; + + return (ret < 0 || cdeleted > 0) ? DIRCHANGED : FULLUPDATE; +} + +static int +del_range_post(int ent, fileheader_t * fhdr, char *direct) +{ + int ret = 0; + if (!(currmode & MODE_BOARD)) + return DONOTHING; + + if (currbid) { + boardheader_t *bp = getbcache(currbid); + assert(bp); + if (is_readonly_board(bp->brdname)) + return DONOTHING; + } + + ret = del_range(ent, fhdr, direct, direct); + if (ret == DIRCHANGED) { + setbtotal(currbid); + } + return ret; } static int @@ -3213,7 +3234,7 @@ del_post(int ent, fileheader_t * fhdr, char *direct) { char reason[PROPER_TITLE_LEN] = ""; char genbuf[100], newpath[PATHLEN] = ""; - int not_owned, is_anon, tusernum, del_ok = 0, as_badpost = 0; + int not_owned, is_anon, tusernum, del_ret = 0, as_badpost = 0; boardheader_t *bp; assert(0<=currbid-1 && currbid-1<MAX_BOARD); @@ -3341,49 +3362,41 @@ del_post(int ent, fileheader_t * fhdr, char *direct) !(currmode & MODE_DIGEST) && !safe_article_delete(ent, fhdr, direct, reason[0] ? reason : NULL)) || #endif - // XXX TODO delete_record is really really dangerous - - // we should verify the header (maybe by filename) is the same. - // currently race condition is easily cause by 2 BMs - !delete_record(direct, sizeof(fileheader_t), ent) - ) { + !delete_fileheader(direct, fhdr, ent)) { // do this immediately after .DIR change in case connection // was closed. setbtotal(currbid); -#ifdef USE_TIME_CAPSULE - del_ok = (cancelpost2(fhdr, newpath, sizeof(newpath)) == 0) ? 1 : 0; -#else - del_ok = (cancelpost(fhdr, not_owned, newpath) == 0) ? 1 : 0; -#endif + del_ret = cancelpost(direct, fhdr, not_owned, newpath, sizeof(newpath)); deleteCrossPost(fhdr, bp->brdname); + move(b_lines - 10, 0); clrtobot(); + prints("\n正在刪除文章: %s\n", fhdr->title); + + // check delete_file_content for del_ret + if (!IS_DELETE_FILE_CONTENT_OK(del_ret)) { + outs("檔案可能已被它人刪除或發生錯誤," + "若持續發生請向" BN_BUGREPORT "報告\n"); + } + if (del_ret == DELETE_FILE_CONTENT_BACKUP_FAILED) { + outs(" " ANSI_COLOR(1;31) "* 檔案備份失敗,請至 " + BN_BUGREPORT "報告" ANSI_RESET "\n"); + } #ifdef ASSESS // badpost assignment - // case one, self-owned, invalid author, or digest mode - should not give bad posts - if (!not_owned || tusernum <= 0 || (currmode & MODE_DIGEST) ) - { - // do nothing - } - // case 2, got error in file deletion (already deleted, also skip badpost) - else if (!del_ok || !*newpath) - { - move_ansi(1, 40); clrtoeol(); - outs("已刪或刪除錯誤(跳過劣文設定)"); - pressanykey(); - } - // case 3, post older than one week (TODO use macro for the duration) - else if (now - atoi(fhdr->filename + 2) > 7 * 24 * 60 * 60) - { + if (!not_owned || tusernum <= 0 || (currmode & MODE_DIGEST) ) { + // case one, self-owned, invalid author, or digest mode - should not give bad posts + } else if (!IS_DELETE_FILE_CONTENT_OK(del_ret) || !*newpath) { + // case 2, got error in file deletion (already deleted, also skip badpost) + outs("劣文設定: 已刪或刪除錯誤 (跳過)\n"); + } else if (now - atoi(fhdr->filename + 2) > 7 * 24 * 60 * 60) { + // case 3, post older than one week (TODO use macro for the duration) + outs("劣文設定: 文章超過一週 (跳過)\n"); + } else { + // case 4, can assign badpost move_ansi(1, 40); clrtoeol(); - outs("文章超過一週(跳過劣文設定)"); - pressanykey(); - } - // case 4, can assign badpost - else - { // TODO not_owned 時也要改變 numpost? - move_ansi(1, 40); clrtoeol(); outs("惡劣文章?(y/N) "); // FIXME 有板主會在這裡不小心斷掉連線所以要小心... // 重要的事最好在前面作完。 @@ -3433,15 +3446,15 @@ del_post(int ent, fileheader_t * fhdr, char *direct) pay(del_fee, "%s 看板 文章自刪清潔費: %s", currboard, fhdr->title); sendalert(cuser.userid, ALERT_PWD_PERM); - vmsgf("您的文章減為 %d 篇,支付清潔費 %d " MONEYNAME "幣", - cuser.numposts, del_fee); + prints("您的文章減為 %d 篇,支付清潔費 %d " MONEYNAME "幣\n", + cuser.numposts, del_fee); } - - if (!del_ok) - vmsg("刪除過程發生錯誤,請向" BN_BUGREPORT "報告"); - + pressanykey(); return DIRCHANGED; - } // delete_record + } else { // delete_fileheader + vmsg("無法刪除檔案記錄。請稍候再試。"); + return DIRCHANGED; + } } // genbuf[0] == 'y' return FULLUPDATE; } @@ -3928,7 +3941,7 @@ push_bottom(int ent, fileheader_t *fhdr, const char *direct) } else{ fhdr->filemode ^= FILE_BOTTOM; - num = delete_record(direct, sizeof(fileheader_t), ent); + num = delete_fileheader(direct, fhdr, ent); } assert(0<=currbid-1 && currbid-1<MAX_BOARD); setbottomtotal(currbid); @@ -4156,7 +4169,7 @@ const onekey_t read_comms[] = { { 0, NULL }, // 'A' 65 { 0, b_config }, // 'B' { 1, do_limitedit }, // 'C' - { 1, del_range }, // 'D' + { 1, del_range_post }, // 'D' { 1, edit_post }, // 'E' { 0, NULL }, // 'F' { 0, NULL }, // 'G' diff --git a/pttbbs/mbbsd/edit.c b/pttbbs/mbbsd/edit.c index 8a42528e..d95e80db 100644 --- a/pttbbs/mbbsd/edit.c +++ b/pttbbs/mbbsd/edit.c @@ -3860,7 +3860,7 @@ vedit2(const char *fpath, int saveheader, int *islocal, char title[STRLEN], int a_menu("編輯輔助器", "etc/editexp", (HasUserPerm(PERM_SYSOP) ? SYSOP : NOBODY), 0, - trans_buffer); + trans_buffer, NULL); currstat = currstat0; currutmp->mode = mode0; } diff --git a/pttbbs/mbbsd/mail.c b/pttbbs/mbbsd/mail.c index 8767b4fe..e5571159 100644 --- a/pttbbs/mbbsd/mail.c +++ b/pttbbs/mbbsd/mail.c @@ -994,7 +994,7 @@ read_new_mail(void * voidfptr, void *optarg) setuserfile(fname, fptr->filename); fptr->filemode |= FILE_READ; - if (substitute_record(currmaildir, fptr, sizeof(*fptr), arg->idc)) + if (substitute_fileheader(currmaildir, fptr, fptr, arg->idc)) return -1; arg->mrd = 1; @@ -1234,10 +1234,13 @@ mail_del(int ent, const fileheader_t * fhdr, const char *direct) } if (vans(msg_del_ny) == 'y') { - if (!delete_record(direct, sizeof(*fhdr), ent)) { + if (!delete_fileheader(direct, fhdr, ent)) { setupmailusage(); setdirpath(genbuf, direct, fhdr->filename); #ifdef USE_TIME_CAPSULE + // TODO we should collect all logf(delete) together + log_filef(genbuf, LOG_CREAT, "\n※ Deleted by: %s (%s) %s", + cuser.userid, fromhost, Cdatelite(&now)); // bypass those recovered files if (strncmp(fhdr->owner, RECYCLE_BIN_OWNER, strlen(RECYCLE_BIN_OWNER)) != 0) @@ -1247,7 +1250,10 @@ mail_del(int ent, const fileheader_t * fhdr, const char *direct) unlink(genbuf); loadmailusage(); return DIRCHANGED; - } + } else { + vmsg("刪除失敗,請確定未多重登入後再重試。"); + return DIRCHANGED; + } } return READ_REDRAW; } @@ -1336,7 +1342,7 @@ mail_unread(int ent, fileheader_t * fhdr, const char *direct) if (fhdr && fhdr->filemode & FILE_READ) { fhdr->filemode &= ~FILE_READ; - substitute_record(direct, fhdr, ent); + substitute_fileheader(direct, fhdr, fhdr, ent); return FULLUPDATE; } #endif // USE_USER_MAIL_UNREAD @@ -1424,7 +1430,7 @@ mail_reply(int ent, fileheader_t * fhdr, const char *direct) !(fhdr->filemode & FILE_REPLIED)) { fhdr->filemode |= FILE_REPLIED; - substitute_ref_record(direct, fhdr, oent); + substitute_fileheader(direct, fhdr, fhdr, oent); } break; } @@ -1689,12 +1695,13 @@ mail_cross_post(int unused_arg, fileheader_t * fhdr, const char *direct) int mail_man(void) { - char buf[PATHLEN], buf1[64]; + char buf[PATHLEN], title[64], backup_path[PATHLEN]; int mode0 = currutmp->mode; int stat0 = currstat; // TODO if someday we put things in user man...? sethomeman(buf, cuser.userid); + sethomedir(backup_path, cuser.userid); // if user already has man directory or permission, // allow entering mail-man folder. @@ -1702,8 +1709,8 @@ mail_man(void) if (!dashd(buf) && !HasUserPerm(PERM_MAILLIMIT)) return DONOTHING; - snprintf(buf1, sizeof(buf1), "%s 的信件夾", cuser.userid); - a_menu(buf1, buf, HasUserPerm(PERM_MAILLIMIT) ? 1 : 0, 0, NULL); + snprintf(title, sizeof(title), "%s 的信件夾", cuser.userid); + a_menu(title, buf, HasUserPerm(PERM_MAILLIMIT) ? 1 : 0, 0, NULL, backup_path); currutmp->mode = mode0; currstat = stat0; return FULLUPDATE; @@ -1740,12 +1747,14 @@ mail_cite(int ent GCC_UNUSED, fileheader_t * fhdr, const char *direct GCC_UNUSED if (*buf) strlcpy(xboard, buf, sizeof(xboard)); if (*xboard && ((bid = getbnum(xboard)) > 0)){ /* XXXbid */ + char backup_path[PATHLEN]; setapath(fpath, xboard); + setbdir(backup_path, xboard); setutmpmode(ANNOUNCE); + // TODO what's the backup_path here? a_menu(xboard, fpath, - HasUserPerm(PERM_ALLBOARD) ? 2 : is_BM_cache(bid) ? 1 : 0, - bid, - NULL); + HasUserPerm(PERM_ALLBOARD) ? 2 : is_BM_cache(bid) ? 1 : 0, + bid, NULL, backup_path); } else { mail_man(); } @@ -1759,21 +1768,31 @@ mail_cite(int ent GCC_UNUSED, fileheader_t * fhdr, const char *direct GCC_UNUSED static int mail_save(int ent GCC_UNUSED, fileheader_t * fhdr GCC_UNUSED, const char *direct GCC_UNUSED) { - char fpath[PATHLEN]; + char fpath[PATHLEN], backup_path[PATHLEN]; char title[TTLEN + 1]; - if (HasUserPerm(PERM_MAILLIMIT)) { - setuserfile(fpath, fhdr->filename); - strlcpy(title, "◇ ", sizeof(title)); - strlcpy(title + 3, fhdr->title, sizeof(title) - 3); - a_copyitem(fpath, title, fhdr->owner, 1); - sethomeman(fpath, cuser.userid); - a_menu(cuser.userid, fpath, 1, 0, NULL); - return FULLUPDATE; - } - return DONOTHING; + if (!HasUserPerm(PERM_MAILLIMIT)) + return DONOTHING; + setuserfile(fpath, fhdr->filename); + strlcpy(title, "◇ ", sizeof(title)); + strlcpy(title + 3, fhdr->title, sizeof(title) - 3); + a_copyitem(fpath, title, fhdr->owner, 1); + sethomeman(fpath, cuser.userid); + sethomedir(backup_path, cuser.userid); + a_menu(cuser.userid, fpath, 1, 0, NULL, backup_path); + return FULLUPDATE; +} + +static int +del_range_mail(int ent, fileheader_t * fhdr, char *direct) +{ + int ret = del_range(ent, fhdr, direct, direct); + if (ret == DIRCHANGED) + setupmailusage(); + return ret; } + #ifdef OUTJOBSPOOL static int mail_waterball(int ent GCC_UNUSED, fileheader_t * fhdr, const char *direct GCC_UNUSED) @@ -1911,7 +1930,7 @@ static const onekey_t mail_comms[] = { { 0, NULL }, // 'A' 65 { 0, NULL }, // 'B' { 0, NULL }, // 'C' - { 1, del_range }, // 'D' + { 1, del_range_mail }, // 'D' { 1, mail_edit }, // 'E' { 0, NULL }, // 'F' { 0, NULL }, // 'G' diff --git a/pttbbs/mbbsd/ordersong.c b/pttbbs/mbbsd/ordersong.c index d4b0b5c4..19f6bdac 100644 --- a/pttbbs/mbbsd/ordersong.c +++ b/pttbbs/mbbsd/ordersong.c @@ -87,7 +87,7 @@ do_order_song(void) if (ans[0] == '1') break; else if (ans[0] == '2') { - a_menu("點歌歌本", SONGBOOK, 0, 0, NULL); + a_menu("點歌歌本", SONGBOOK, 0, 0, NULL, NULL); clear(); } else if (ans[0] == '3') { @@ -114,7 +114,7 @@ do_order_song(void) getdata_str(22, 0, "寄到誰的信箱(真實 ID 或 E-mail)?", address, sizeof(address), LCECHO, receiver); vmsg("接著要選歌囉..進入歌本好好的選一首歌吧..^o^"); - a_menu("點歌歌本", SONGBOOK, 0, 0, trans_buffer); + a_menu("點歌歌本", SONGBOOK, 0, 0, trans_buffer, NULL); if (!trans_buffer[0] || strstr(trans_buffer, "home") || strstr(trans_buffer, "boards") || !(fp = fopen(trans_buffer, "r"))) { unlockutmpmode(); diff --git a/pttbbs/mbbsd/record.c b/pttbbs/mbbsd/record.c index 255f95f9..980503a6 100644 --- a/pttbbs/mbbsd/record.c +++ b/pttbbs/mbbsd/record.c @@ -291,8 +291,7 @@ safe_article_delete(int ent, const fileheader_t *fhdr, const char *direct, const fileheader_t newfhdr; memcpy(&newfhdr, fhdr, sizeof(fileheader_t)); set_safedel_fhdr(&newfhdr, newtitle); - substitute_record(direct, &newfhdr, sizeof(newfhdr), ent); - return 0; + return substitute_fileheader(direct, fhdr, &newfhdr, ent); } int @@ -330,7 +329,105 @@ safe_article_delete_range(const char *direct, int from, int to) } return -1; } -#endif +#endif + +// Delete and archive the physical (except header) of file +// +// if backup_direct is provided, backup according to that directory. +// if backup_path points to buffer, return back the backuped file +// Return -1 if cannot delete file, 0 for success, +// 1 if delete success but backup failed. +int +delete_file_content(const char *direct, const fileheader_t *fh, + const char *backup_direct, + char *backup_path, size_t sz_backup_path) { + char fpath[PATHLEN]; + fileheader_t backup = { {0} }; + int backup_failed = DELETE_FILE_CONTENT_SUCCESS; + + if(!fh->filename[0] || !direct) + return DELETE_FILE_CONTENT_FAILED; + +#ifdef FN_SAFEDEL + if ( +#ifdef FN_SAFEDEL_PREFIX_LEN + strncmp(fh->filename, FN_SAFEDEL, FN_SAFEDEL_PREFIX_LEN) == 0 || +#endif + strcmp(fh->filename, FN_SAFEDEL) == 0 || +#endif + 0) + return DELETE_FILE_CONTENT_SUCCESS; + + if (backup_path) { + assert(backup_direct); + assert(sz_backup_path > 0); + *backup_path = 0; + } + + // solve source file name + setdirpath(fpath, direct, fh->filename); + if (!dashf(fpath)) + return DELETE_FILE_CONTENT_FAILED; + + if (backup_direct && + strncmp(fh->owner, RECYCLE_BIN_OWNER, strlen(RECYCLE_BIN_OWNER)) != 0) { + + log_filef(fpath, LOG_CREAT, "\n※ Deleted by: %s (%s) %s\n", + cuser.userid, fromhost, Cdatelite(&now)); + + // TODO or only memcpy(&backup, fh, sizeof(backup)); ? + strlcpy(backup.owner, fh->owner, sizeof(backup.owner)); + strlcpy(backup.date, fh->date, sizeof(backup.date)); + strlcpy(backup.title, fh->title, sizeof(backup.title)); + strlcpy(backup.filename, fh->filename, sizeof(backup.filename)); + + if (backup_direct != direct && + strcmp(backup_direct, direct) != 0) { + // need to create a new file entry. + char *slash = NULL; + char bakpath[PATHLEN]; + + strlcpy(bakpath, backup_direct, sizeof(bakpath)); + slash = strrchr(bakpath, '/'); + if (slash) + *slash = 0; + if (stampfile_u(bakpath, &backup) == 0 && + Rename(fpath, bakpath) == 0) { + strlcpy(fpath, bakpath, sizeof(fpath)); + } else { + backup_direct = NULL; + backup_failed = 1; + } + } + + // now, always backup according to fpath + if (backup_direct) { +#ifdef USE_TIME_CAPSULE + if (!timecapsule_archive_new_revision( + fpath, &backup, sizeof(backup), + backup_path, sz_backup_path)) + backup_failed = DELETE_FILE_CONTENT_BACKUP_FAILED; +#else + // we can't backup to same folder. + if (strcmp(direct, backup_direct) == 0) { + backup_failed = DELETE_FILE_CONTENT_BACKUP_FAILED; + } else { + if (append_record(backup_direct, &backup, sizeof(backup)) < 0) + backup_failed = DELETE_FILE_CONTENT_BACKUP_FAILED; + if (backup_path) + strlcpy(backup_path, fpath, sz_backup_path); + } + // the fpath is used as-is. + *fpath = 0; +#endif + } + } + + // the file should be already in time capsule + if (*fpath && unlink(fpath) != 0) + return DELETE_FILE_CONTENT_FAILED; + return backup_failed; +} // XXX announce(man) directory uses a smaller range of file names. // TODO merge with common/bbs/fhdr_stamp.c |