summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpiaip <piaip@63ad8ddf-47c3-0310-b6dd-a9e9d9715204>2010-11-07 02:15:37 +0800
committerpiaip <piaip@63ad8ddf-47c3-0310-b6dd-a9e9d9715204>2010-11-07 02:15:37 +0800
commit1e225bea45722d52335eca19d4fdc36594be3f00 (patch)
treece142ecb1675df7aceba9b0efcc48c4d9f20040c
parentfac0f8f0c23b5cf8448970970adbba33ffdab6e4 (diff)
downloadpttbbs-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.h12
-rw-r--r--pttbbs/mbbsd/announce.c84
-rw-r--r--pttbbs/mbbsd/bbs.c387
-rw-r--r--pttbbs/mbbsd/edit.c2
-rw-r--r--pttbbs/mbbsd/mail.c65
-rw-r--r--pttbbs/mbbsd/ordersong.c4
-rw-r--r--pttbbs/mbbsd/record.c103
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