diff options
author | piaip <piaip@63ad8ddf-47c3-0310-b6dd-a9e9d9715204> | 2008-03-08 00:01:56 +0800 |
---|---|---|
committer | piaip <piaip@63ad8ddf-47c3-0310-b6dd-a9e9d9715204> | 2008-03-08 00:01:56 +0800 |
commit | 1812d5521f71bf6726cd959308cb4628c5a1951c (patch) | |
tree | f61c5be32e335668ddddeca1e28459605a220b6c | |
parent | 80d861dff5d757ef5827054a0633fdbe56ff356a (diff) | |
download | pttbbs-1812d5521f71bf6726cd959308cb4628c5a1951c.tar pttbbs-1812d5521f71bf6726cd959308cb4628c5a1951c.tar.gz pttbbs-1812d5521f71bf6726cd959308cb4628c5a1951c.tar.bz2 pttbbs-1812d5521f71bf6726cd959308cb4628c5a1951c.tar.lz pttbbs-1812d5521f71bf6726cd959308cb4628c5a1951c.tar.xz pttbbs-1812d5521f71bf6726cd959308cb4628c5a1951c.tar.zst pttbbs-1812d5521f71bf6726cd959308cb4628c5a1951c.zip |
- register: Regform2 API prototype (for concurrent registration form validation) [not applied yet]
- file/passwd/assess: API finetune
- menu: menu item finetune
git-svn-id: http://opensvn.csie.org/pttbbs/trunk/pttbbs@3975 63ad8ddf-47c3-0310-b6dd-a9e9d9715204
-rw-r--r-- | include/proto.h | 8 | ||||
-rw-r--r-- | mbbsd/assess.c | 3 | ||||
-rw-r--r-- | mbbsd/file.c | 105 | ||||
-rw-r--r-- | mbbsd/menu.c | 6 | ||||
-rw-r--r-- | mbbsd/passwd.c | 8 | ||||
-rw-r--r-- | mbbsd/register.c | 409 | ||||
-rw-r--r-- | src/libbbsutil/file.c | 8 |
7 files changed, 523 insertions, 24 deletions
diff --git a/include/proto.h b/include/proto.h index 84c2ae5a..1c594cdb 100644 --- a/include/proto.h +++ b/include/proto.h @@ -287,8 +287,11 @@ void reginit_fav(void); /* file */ int file_count_line(const char *file); -int file_append_line(const char *file, const char *string); -int file_exist_record(const char *file, const char *string); +int file_append_line(const char *file, const char *string); // does not append "\n" +int file_append_record(const char *file, const char *key); // will append "\n" +int file_exist_record(const char *file, const char *key); +int file_find_record(const char *file, const char *key); +int file_delete_record(const char *file, const char *key, int case_sensitive); /* friend */ void friend_edit(int type); @@ -828,6 +831,7 @@ int passwd_apply(void *data, int (*fptr)(void *, int, userec_t *)); void passwd_lock(void); void passwd_unlock(void); int passwd_update_money(int num); +void passwd_force_update(int flag); int initcuser(const char *userid); int freecuser(void); diff --git a/mbbsd/assess.c b/mbbsd/assess.c index e4bcc231..4d63a8d8 100644 --- a/mbbsd/assess.c +++ b/mbbsd/assess.c @@ -256,9 +256,8 @@ u_fixgoodpost(void) "%s %s 自動修正優文數: 由 %d 變為 %d\n", Cdate(&now), cuser.userid, cuser.goodpost, newgp); cuser.goodpost = newgp; - if ((currutmp) && (currutmp->alerts & ALERT_PWD_GOODPOST)) - currutmp->alerts &= ~ALERT_PWD_GOODPOST; // use my version // update passwd file here? + passwd_force_update(ALERT_PWD_GOODPOST); passwd_update(usernum, &cuser); vmsgf("更新優文數目為%d。", newgp); } diff --git a/mbbsd/file.c b/mbbsd/file.c index 34af866a..3b657acc 100644 --- a/mbbsd/file.c +++ b/mbbsd/file.c @@ -28,7 +28,7 @@ int file_count_line(const char *file) } /** - * 將 string append 到檔案 file 後端 + * 將 string append 到檔案 file 後端 (不加換行) * @param file 要被 append 的檔 * @param string * @return 成功傳回 0,失敗傳回 -1。 @@ -46,21 +46,43 @@ int file_append_line(const char *file, const char *string) } /** - * 傳回檔案 file 中是否有 string 這個字串。 + * 將 "$key\n" append 到檔案 file 後端 + * @param file 要被 append 的檔 + * @param key 沒有換行的字串 + * @return 成功傳回 0,失敗傳回 -1。 */ -int file_exist_record(const char *file, const char *string) +int file_append_record(const char *file, const char *key) +{ + FILE *fp; + if (!key || !*key) return -1; + if ((fp = fopen(file, "a")) == NULL) + return -1; + flock(fileno(fp), LOCK_EX); + fputs(key, fp); + fputs("\n", fp); + flock(fileno(fp), LOCK_UN); + fclose(fp); + return 0; +} + +/** + * 傳回檔案 file 中 key 所在行數 + */ +int file_find_record(const char *file, const char *key) { FILE *fp; char buf[STRLEN], *ptr; + int i = 0; if ((fp = fopen(file, "r")) == NULL) return 0; while (fgets(buf, STRLEN, fp)) { char *strtok_pos; - if ((ptr = strtok_r(buf, str_space, &strtok_pos)) && !strcasecmp(ptr, string)) { + i++; + if ((ptr = strtok_r(buf, str_space, &strtok_pos)) && !strcasecmp(ptr, key)) { fclose(fp); - return 1; + return i; } } fclose(fp); @@ -68,6 +90,79 @@ int file_exist_record(const char *file, const char *string) } /** + * 傳回檔案 file 中是否有 key + */ +int file_exist_record(const char *file, const char *key) +{ + return file_find_record(file, key) > 0 ? 1 : 0; +} + +/** + * 刪除檔案 file 中以 string 開頭的行 + * @param file 要處理的檔案 + * @param string 尋找的 key name + * @param case_sensitive 是否要處理大小寫 + * @return 成功傳回 0,失敗傳回 -1。 + */ +int +file_delete_record(const char *file, const char *string, int case_sensitive) +{ + // TODO nfp 用 tmpfile() 比較好? 不過 Rename 會變慢... + FILE *fp = NULL, *nfp = NULL; + char fnew[PATHLEN]; + char buf[STRLEN + 1]; + int ret = -1, i = 0; + const size_t toklen = strlen(string); + + if (!toklen) + return 0; + + do { + snprintf(fnew, sizeof(fnew), "%s.%3.3X", file, (unsigned int)(random() & 0xFFF)); + if (access(fnew, 0) != 0) + break; + } while (i++ < 10); // max tries = 10 + + if (access(fnew, 0) == 0) return -1; // cannot create temp file. + + i = 0; + if ((fp = fopen(file, "r")) && (nfp = fopen(fnew, "w"))) { + while (fgets(buf, sizeof(buf), fp)) + { + size_t klen = strcspn(buf, str_space); + if (toklen == klen) + { + if (((case_sensitive && strncmp(buf, string, toklen) == 0) || + (!case_sensitive && strncasecmp(buf, string, toklen) == 0))) + { + // found line. skip it. + i++; + continue; + } + } + // other wise, keep the line. + fputs(buf, nfp); + } + fclose(nfp); nfp = NULL; + if (i > 0) + { + if(Rename(fnew, file) < 0) + ret = -1; + else + ret = 0; + } else { + unlink(fnew); + ret = 0; + } + } + if(fp) + fclose(fp); + if(nfp) + fclose(nfp); + return ret; +} + +/** * 對每一筆 record 做 func 這件事。 * @param file * @param func 處理每筆 record 的 handler,為一 function pointer。 diff --git a/mbbsd/menu.c b/mbbsd/menu.c index 20fed243..7eaaf53c 100644 --- a/mbbsd/menu.c +++ b/mbbsd/menu.c @@ -400,12 +400,12 @@ static const commands_t maillist[] = { {m_new, PERM_READMAIL, "RNew 閱\讀新進郵件"}, {m_read, PERM_READMAIL, "RRead 多功\能讀信選單"}, {m_send, PERM_LOGINOK, "RSend 站內寄信"}, - {x_love, PERM_LOGINOK, "PPaper 情書產生器"}, {mail_list, PERM_LOGINOK, "RMail List 群組寄信"}, + {x_love, PERM_LOGINOK, "PPaper 情書產生器"}, {setforward, PERM_LOGINOK, "FForward " ANSI_COLOR(1;32) "設定信箱自動轉寄" ANSI_RESET}, - {m_sysop, 0, "YYes, sir! 諂媚站長"}, - {m_internet, PERM_INTERNET, "RInternet 寄信到 Internet"}, + {m_sysop, 0, "YYes, sir! 寫信給站長"}, + {m_internet, PERM_INTERNET, "RInternet 寄信到站外"}, {mail_mbox, PERM_INTERNET, "RZip UserHome 把所有私人資料打包回去"}, {built_mail_index, PERM_LOGINOK, "SSavemail 重建信箱索引"}, {mail_all, PERM_SYSOP, "RAll 寄信給所有使用者"}, diff --git a/mbbsd/passwd.c b/mbbsd/passwd.c index 0446ddf2..d0dd4015 100644 --- a/mbbsd/passwd.c +++ b/mbbsd/passwd.c @@ -69,6 +69,14 @@ passwd_update_money(int num) return 0; } +void +passwd_force_update(int flag) +{ + if(!currutmp || (currutmp->alerts & ALERT_PWD) == 0) + return; + currutmp->alerts &= ~flag; +} + int passwd_update(int num, userec_t * buf) { diff --git a/mbbsd/register.c b/mbbsd/register.c index 3eb9e8e3..c71ed66a 100644 --- a/mbbsd/register.c +++ b/mbbsd/register.c @@ -1,11 +1,16 @@ /* $Id$ */ #include "bbs.h" -#define FN_REGISTER_LOG "register.log" +#define FN_REGISTER_LOG "register.log" // global registration history #define FN_JUSTIFY "justify" #define FN_JUSTIFY_WAIT "justify.wait" #define FN_REJECT_NOTIFY "justify.reject" +// New style (Regform2) file names: +#define FN_REGFORM "regform" // registration form in user home +#define FN_REGFORM_LOG "regform.log" // regform history in user home +#define FN_REQLIST "reg.wait" // request list file, in global directory (replacing fn_register) + //////////////////////////////////////////////////////////////////////////// // Password Hash //////////////////////////////////////////////////////////////////////////// @@ -13,7 +18,7 @@ // prototype of crypt() char *crypt(const char *key, const char *salt); -char * +char * genpasswd(char *pw) { if (pw[0]) { @@ -1245,7 +1250,7 @@ regform_accept(const char *userid, const char *justify) } void -regform_reject(const char *userid, char *reason) +regform_reject(const char *userid, const char *reason) { char buf[PATHLEN]; FILE *fp = NULL; @@ -1270,7 +1275,7 @@ regform_reject(const char *userid, char *reason) // last: send notification mkuserdir(muser.userid); - sethomefile(buf, muser.userid, "justify.reject"); + sethomefile(buf, muser.userid, FN_REJECT_NOTIFY); fp = fopen(buf, "wt"); assert(fp); syncnow(); @@ -1289,6 +1294,7 @@ regform_reject(const char *userid, char *reason) mail_muser(muser, "[註冊失敗]", buf); } +// Regform v1 API // read count entries from regsrc to a temp buffer FILE * pull_regform(const char *regfile, char *workfn, int count) @@ -1325,6 +1331,7 @@ pump_regform(const char *regfn, FILE *remains) fclose(fout); } +// New Regform UI static void prompt_regform_ui() { @@ -1393,6 +1400,10 @@ resolve_reason(char *s, int y) } while (strlen(s) < 4); } +//////////////////////////////////////////////////////////////////////////// +// Regform Utilities +//////////////////////////////////////////////////////////////////////////// + // TODO define and use structure instead, even in reg request file. typedef struct { // current format: @@ -1418,6 +1429,7 @@ typedef struct { char phone [20]; } RegformEntry; +// regform format utilities int load_regform_entry(RegformEntry *pre, FILE *fp) { @@ -1459,7 +1471,6 @@ print_regform_entry(const RegformEntry *pre, FILE *fp, int close) fprintf(fp, "career: %s\n", pre->career); fprintf(fp, "addr: %s\n", pre->addr); fprintf(fp, "phone: %s\n", pre->phone); - fprintf(fp, "email: %s\n", "x"); if (close) fprintf(fp, "----\n"); return 1; @@ -1489,6 +1500,386 @@ append_regform(const RegformEntry *pre, const char *logfn, return 1; } +//////////////////////////////////////////////////////////////////////////// +// Regform2 API +//////////////////////////////////////////////////////////////////////////// + +// registration queue +int +regq_append(const char *userid) +{ + if (file_append_record(FN_REQLIST, userid) < 0) + return 0; + return 1; +} + +int +regq_find(const char *userid) +{ + return file_find_record(FN_REQLIST, userid); +} + +int +regq_delete(const char *userid) +{ + return file_delete_record(FN_REQLIST, userid, 0); +} + +// user home regform operation +int +regfrm_exist(const char *userid) +{ + char fn[PATHLEN]; + sethomefile(fn, userid, FN_REGFORM); + return dashf(fn) ? 1 : 0; +} + +int +regfrm_load(const char *userid, RegformEntry *pre) +{ + FILE *fp = NULL; + char fn[PATHLEN]; + int ret = 0; + sethomefile(fn, userid, FN_REGFORM); + if (!dashf(fn)) + return 0; + + fp = fopen(fn, "rt"); + if (!fp) + return 0; + ret = load_regform_entry(pre, fp); + fclose(fp); + return ret; +} + +int +regfrm_save(const char *userid, const RegformEntry *pre) +{ + FILE *fp = NULL; + char fn[PATHLEN]; + int ret = 0; + sethomefile(fn, userid, FN_REGFORM); + + fp = fopen(fn, "wt"); + if (!fp) + return 0; + ret = print_regform_entry(pre, fp, 1); + fclose(fp); + return ret; +} + +int +regfrm_trylock(const char *userid) +{ + int fd = 0; + char fn[PATHLEN]; + sethomefile(fn, userid, FN_REGFORM); + if (!dashf(fn)) return 0; + fd = open(fn, O_RDONLY); + if (fd < 0) return 0; + if (flock(fd, LOCK_EX|LOCK_NB) == 0) + return fd; + close(fd); + return 0; +} + +int +regfrm_unlock(int lockfd) +{ + int fd = lockfd; + if (lockfd <= 0) + return 0; + lockfd = flock(fd, LOCK_UN) == 0 ? 1 : 0; + close(fd); + return lockfd; +} + +// regform processors +int +regfrm_accept(RegformEntry *pre) +{ + char justify[REGLEN+1], buf[STRLEN*2]; + char fn[PATHLEN], fnlog[PATHLEN]; + + // dry run! + vmsg("regfrm_accept"); + return 1; + + sethomefile(fn, pre->userid, FN_REGFORM); + + // build justify string + removespace(pre->phone); + removespace(pre->career); + snprintf(justify, sizeof(justify), + "%s:%s:%s", pre->phone, pre->career, cuser.userid); + + // call handler + regform_accept(pre->userid, justify); + + // append current form to history. + sethomefile(fnlog, pre->userid, FN_REGFORM_LOG); + AppendTail(fn, fnlog, 0); + // global history + snprintf(buf, sizeof(buf), "Approved: %s -> %s\nDate: %s\n", + cuser.userid, pre->userid, Cdate(&now)); + file_append_line(FN_REGISTER_LOG, buf); + AppendTail(fn, FN_REGISTER_LOG, 0); + + // remove from queue + unlink(fn); + regq_delete(pre->userid); + return 1; +} + +int +regfrm_reject(RegformEntry *pre, const char *reason) +{ + char buf[STRLEN*2]; + char fn[PATHLEN]; + + // dry run! + vmsg("regfrm_reject"); + return 1; + + sethomefile(fn, pre->userid, FN_REGFORM); + + // call handler + regform_reject(pre->userid, reason); + + // log it + snprintf(buf, sizeof(buf), "Rejected: %s -> %s [%s]\nDate: %s\n", + cuser.userid, pre->userid, reason, Cdate(&now)); + file_append_line(FN_REGISTER_LOG, buf); + AppendTail(fn, FN_REGISTER_LOG, 0); + + // remove from queue + unlink(fn); + regq_delete(pre->userid); + return 1; +} + +int +regfrm_delete(const char *userid) +{ + char fn[PATHLEN]; + sethomefile(fn, userid, FN_REGFORM); + + // dry run! + vmsgf("regfrm_delete (%s)", userid); + return 1; + + // directly delete. + unlink(fn); + + // remove from queue + regq_delete(userid); + return 1; +} + +// working queue +FILE * +regq_init_pull() +{ + FILE *fp = tmpfile(), *src =NULL; + char buf[STRLEN]; + if (!fp) return NULL; + src = fopen(FN_REQLIST, "rt"); + if (!src) { fclose(fp); return NULL; } + while (fgets(buf, sizeof(buf), src)) + fputs(buf, fp); + fclose(src); + rewind(fp); + return fp; +} + +int +regq_pull(FILE *fp, char *uid) +{ + char buf[STRLEN]; + size_t idlen = 0; + uid[0] = 0; + if (fgets(buf, sizeof(buf), fp) == NULL) + return 0; + idlen = strcspn(buf, str_space); + if (idlen < 1) return 0; + if (idlen > IDLEN) idlen = IDLEN; + strlcpy(uid, buf, idlen+1); + return 1; +} + +int +regq_end_pull(FILE *fp) +{ + // no need to unlink because fp is a tmpfile. + if (!fp) return 0; + fclose(fp); + return 1; +} + +// UI part +int +ui_display_regform_single( + const userec_t *xuser, + const RegformEntry *pre, + int tid, char *reason) +{ + int c; + + while (1) + { + move(1, 0); + user_display(xuser, 1); + move(14, 0); + prints(ANSI_COLOR(1;32) + "--------------- 這是第 %2d 份註冊單 ------------------" + ANSI_RESET "\n", tid); + prints(" %-12s: %s\n", "帳號", pre->userid); + prints("0.%-12s: %s%s\n", "真實姓名", pre->name, + xuser->uflag2 & FOREIGN ? " (外籍)" : + ""); + prints("1.%-12s: %s\n", "服務單位", pre->career); + prints("2.%-12s: %s\n", "目前住址", pre->addr); + prints("3.%-12s: %s\n", "連絡電話", pre->phone); + + move(b_lines, 0); + outs("是否接受此資料(Y/N/Q/Del/Skip)?[S] "); + + c = tolower(igetch() & 0xFF); // round to ASCII + if (c == 'y' || c == 'q' || c == 'd' || c == 's') + return c; + if (c == 'n') + { + int n = 0; + move(3, 0); + outs("\n" ANSI_COLOR(1;31) + "請提出退回申請表原因,按 <Enter> 取消:\n" ANSI_RESET); + for (n = 0; n < REJECT_REASONS; n++) + prints("%d) 請%s\n", n, reasonstr[n]); + outs("\n\n\n"); // preserved for prompt + + getdata(3+2+REJECT_REASONS+1, 0,"退回原因: ", + reason, REASON_LEN, DOECHO); + if (reason[0] == 0) + continue; + // interprete reason + return 'n'; + } + else if (REASON_IN_ABBREV(c)) + { + // quick set + sprintf(reason, "%c", c); + return 'n'; + } + return 's'; + } + // shall never reach here + return 's'; +} + +// sample iterator +void +register_sample() +{ + int lfd = 0; + int tid = 0; + char uid[IDLEN+1]; + char rsn[REASON_LEN]; + FILE *fpregq = regq_init_pull(); + RegformEntry re; + + if (!fpregq) + return; + + while (regq_pull(fpregq, uid)) + { + userec_t muser; + int unum = 0; + int abort = 0; + + // check if user exists. + memset(&muser, 0, sizeof(muser)); + unum = getuser(uid, &muser); + + if (unum < 1) + { + regq_delete(uid); + continue; + } + + // check if regform exists. + if (!regfrm_exist(uid)) + { + // TODO delete here? + regq_delete(uid); + continue; + } + + // TODO check if user is already registered +#if 0 + if (muser.userlevel & PERM_LOGINOK) + { + regfrm_delete(uid); + continue; + } +#endif + + // try to lock + lfd = regfrm_trylock(uid); + if (lfd <= 0) + continue; + + // load it + if (!regfrm_load(uid, &re)) + { + regfrm_delete(uid); + regfrm_unlock(lfd); + // regq_delete(uid); // done in regfrm_delete + continue; + } + + tid ++; + // display regform and process + switch(ui_display_regform_single(&muser, &re, tid, rsn)) + { + case 'a': // accept + regfrm_accept(&re); + break; + + case 'd': // delete + regfrm_delete(uid); + break; + + case 'q': // quit + abort = 1; + break; + + case 'n': // reject + regfrm_reject(&re, rsn); + break; + + case 's': // skip + // do nothing. + break; + + default: // shall never reach here + assert(0); + break; + } + + // final processing + regfrm_unlock(lfd); + + if (abort) + break; + } + regq_end_pull(fpregq); + + // finishing + clear(); move(5, 0); + prints("您審了 %d 份註冊單份。", tid); + pressanykey(); +} + ///////////////////////////////////////////////////////////////////////////// // Regform UI // 處理 Register Form @@ -1759,7 +2150,7 @@ scan_register_form(const char *regfile, int automode, const char *target_uid) fclose(fp); // build reject file - setuserfile(rejfn, "justify.reject"); + sethomefile(rejfn, muser.userid, FN_REJECT_NOTIFY); Copy(buf1, rejfn); } if ((fout = fopen(FN_REGISTER_LOG, "a"))) { @@ -1828,12 +2219,8 @@ scan_register_form(const char *regfile, int automode, const char *target_uid) fclose(fn); unlink(fname); - move(0, 0); - clrtobot(); - - move(5, 0); + clear(); move(5, 0); prints("您審了 %d 份註冊單,AutoScan 審了 %d 份", nSelf, nAuto); - pressanykey(); return (0); } diff --git a/src/libbbsutil/file.c b/src/libbbsutil/file.c index 5cc0b541..21313283 100644 --- a/src/libbbsutil/file.c +++ b/src/libbbsutil/file.c @@ -203,7 +203,11 @@ Rename(const char *src, const char *dst) if (pid == 0) execl("/bin/mv", "mv", "-f", src, dst, (char *)NULL); else if (pid > 0) - waitpid(pid, NULL, 0); + { + int status = -1; + waitpid(pid, &status, 0); + return WEXITSTATUS(status) == 0 ? 0 : -1; + } else return -1; } @@ -262,6 +266,7 @@ AppendTail(const char *src, const char *dst, int off) fo=open(dst, O_WRONLY | O_APPEND | O_CREAT, 0600); if(fo<0) {close(fi); return -1;} + // flock(dst, LOCK_SH); if(off > 0) lseek(fi, (off_t)off, SEEK_SET); @@ -270,6 +275,7 @@ AppendTail(const char *src, const char *dst, int off) { write(fo, buf, bytes); } + // flock(dst, LOCK_UN); close(fo); close(fi); return 0; |