static int common_pager_exit_handler(int r, const char *fpath) { // post processing switch(r) { case RET_DOSYSOPEDIT: r = FULLUPDATE; if (!check_sysop_edit_perm(fpath)) break; log_filef("log/security", LOG_CREAT, "%u %s %d %s admin edit file=%s\n", (int)now, Cdate(&now), getpid(), cuser.userid, fpath); veditfile(fpath); break; case RET_COPY2TMP: r = FULLUPDATE; if (HasUserPerm(PERM_BASIC)) { char buf[PATHLEN]; getdata(b_lines - 1, 0, "把這篇文章收入到暫存檔?[y/N] ", buf, 4, LCECHO); if (buf[0] != 'y') break; setuserfile(buf, ask_tmpbuf(b_lines - 1)); Copy(fpath, buf); } break; case RET_SELECTBRD: r = FULLUPDATE; if (currstat == READING) r = Select(); break; case RET_DOCHESSREPLAY: r = FULLUPDATE; if (HasUserPerm(PERM_BASIC)) ChessReplayGame(fpath); break; #if defined(USE_BBSLUA) && !defined(DISABLE_BBSLUA_IN_PAGER) case RET_DOBBSLUA: r = FULLUPDATE; // check permission again if (HasUserPerm(PERM_BASIC)) bbslua(fpath); break; #endif } return r; }
int log_user(const char *fmt, ...) { char msg[256], filename[256]; va_list ap; va_start(ap, fmt); vsnprintf(msg , sizeof(msg), fmt, ap); va_end(ap); sethomefile(filename, cuser.userid, "USERLOG"); return log_filef(filename, LOG_CREAT, "%s: %s %s", cuser.userid, msg, Cdate(&now)); }
static int pwcuFinalCUser(userec_t *u) { assert(usernum > 0 && usernum <= MAX_USERS); assert(strcmp(u->userid, cuser.userid) == 0); #ifdef DEBUG log_filef("log/pwcu_exitsave.log", LOG_CREAT, "%s FinalCUser invoked at %s\n", cuser.userid, Cdatelite(&now)); #endif if (passwd_sync_update(usernum, u) != 0) return -1; return 0; }
// XXX this is a little different - only invoked at exist, // so no need to sync back to cuser. int pwcuExitSave () { // determine dirty if (pwcu_dirty || cuser.withme != currutmp->withme || cuser.pager != currutmp->pager || cuser.invisible != currutmp->invisible) { // maybe dirty, let's work harder. PWCU_START(); pwcu_dirty = 1; // XXX we may work harder to determine if this is a real // dirty cache, however maybe it's not that important. // configure new utmp values u.withme = currutmp->withme; u.pager = currutmp->pager; u.invisible = currutmp->invisible; // configure those changed by 'not important variables' API u.signature = cuser.signature; u.pager_ui_type = cuser.pager_ui_type; // u.money = moneyof(usernum); // should be already updated by deumoney #ifdef DEBUG log_filef("log/pwcu_exitsave.log", LOG_CREAT, "%s exit %s at %s\n", cuser.userid, pwcu_dirty ? "DIRTY" : "CLEAN", Cdatelite(&now)); #endif PWCU_END(); // XXX return 0 here (PWCU_END), following code is not executed. } #ifdef DEBUG log_filef("log/pwcu_exitsave.log", LOG_CREAT, "%s exit %s at %s\n", cuser.userid, pwcu_dirty ? "DIRTY" : "CLEAN", Cdatelite(&now)); #endif return 0; }
static int pwcuInitCUser(userec_t *u) { assert(usernum > 0 && usernum <= MAX_USERS); if (passwd_sync_query(usernum, u) != 0) return -1; #ifdef DEBUG log_filef("log/pwcu_exitsave.log", LOG_CREAT, "%s InitCUser invoked at %s\n", cuser.userid, Cdatelite(&now)); #endif assert(strncmp(u->userid, cuser.userid, IDLEN) == 0); if (strncmp(u->userid, cuser.userid, IDLEN) != 0) return -1; return 0; }
int append_record_forward(char *fpath, fileheader_t * record, int size, const char *origid) { FILE *fp; char buf[PATHLEN]; char address[64] = ""; char fwd_title[STRLEN] = ""; int r; // No matter what, append it, and return if that failed. r = append_record(fpath, record, size); if (r < 0) return r; // #ifdef USE_MAIL_AUTO_FORWARD if (strlen(fpath) + strlen(FN_FORWARD) >= PATHLEN) { log_filef("log/invalid_append_record_forward", LOG_CREAT, "%s %s %s\n", Cdatelite(&now), cuser.userid, fpath); return -1; } setdirpath(buf, fpath, FN_FORWARD); fp = fopen(buf, "r"); if (!fp) return 0; // Load and setup address address[0] = 0; fscanf(fp, "%63s", address); fclose(fp); chomp(address); strip_blank(address, address); #ifdef UNTRUSTED_FORWARD_TIMEBOMB if (dasht(buf) < UNTRUSTED_FORWARD_TIMEBOMB) { // We may unlink here, but for systems with timebomb, // just leave it alone and let user see it in login screen. // unlink(buf); return 0; } #endif if (get_num_records(fpath, sizeof(fileheader_t)) > MAX_KEEPMAIL_HARDLIMIT) { unlink(buf); // TODO add a mail so that origid knows what happened. LOG_IF(LOG_CONF_INTERNETMAIL, log_filef("log/internet_mail.log", LOG_CREAT, "%s [%s] (%s -> %s) mailbox overflow (%d > %d)\n", Cdatelite(&now), __FUNCTION__, origid, address, get_num_records(fpath, sizeof(fileheader_t)), MAX_KEEPMAIL_HARDLIMIT)); return 0; } if (!*address || strchr(address, '@') == NULL || strcasestr(address, str_mail_address)) { #ifndef UNTRUSTED_FORWARD_TIMEBOMB // delete the setting if we don't have timebombs. unlink(buf); LOG_IF(LOG_CONF_INTERNETMAIL, log_filef("log/internet_mail.log", LOG_CREAT, "%s [%s] Removed bad address: %s (%s)\n", Cdatelite(&now), __FUNCTION__, address, origid)); #endif return 0; } setdirpath(buf, fpath, record->filename); // because too many user set wrong forward address, // let's put their own address instead. // and again because some really stupid user // does not understand they've set auto-forward, // let's mark this in the title. snprintf(fwd_title, sizeof(fwd_title)-1, "[自動轉寄] %s", record->title); bsmtp(buf, fwd_title, address, origid); LOG_IF(LOG_CONF_INTERNETMAIL, log_filef("log/internet_mail.log", LOG_CREAT, "%s [%s] %s -> (%s) %s: %s\n", Cdatelite(&now), __FUNCTION__, cuser.userid, origid, address, fwd_title)); // #endif // USE_MAIL_AUTO_FORWARD return 0; }
// 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_content2(const char *direct, const fileheader_t *fh, const char *backup_direct, char *backup_path, size_t sz_backup_path, const char *reason) { 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_BACKUP_FAILED; // FIXME some announcements were made by sym-links, especial for those // in mail. we should ignore them. if (backup_direct && !dashl(fpath) && strncmp(fh->owner, RECYCLE_BIN_OWNER, strlen(RECYCLE_BIN_OWNER)) != 0) { // FIXME maybe for non-board files we should do this by simply touching // file instead of full log. if (reason && *reason) log_filef(fpath, LOG_CREAT, "\n※ 刪除原因: %s", reason); 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; }
int a_menu_rec(const char *maintitle, const char *path, int lastlevel, int lastbid, char *trans_buffer, a_menu_session_t *sess, const int *preselect, // we don't change root's value (but may change root pointer) // we may change parent's value (but never change parent pointer) const menu_t *root, menu_t* const parent) { menu_t me = {0}; char fname[PATHLEN]; int ch, returnvalue = FULLUPDATE; assert(sess); // prevent deep resursive directories if (strlen(path) + FNLEN >= PATHLEN) { // it is not save to enter such directory. return returnvalue; } if(trans_buffer) trans_buffer[0] = '\0'; if (parent) { parent->next = &me; } else { assert(root == NULL); root = &me; } me.header_size = p_lines; me.header = (fileheader_t *) calloc(me.header_size, FHSZ); me.path = path; strlcpy(me.mtitle, maintitle, sizeof(me.mtitle)); setadir(fname, me.path); me.num = get_num_records(fname, FHSZ); me.bid = lastbid; /* 精華區-tree 中部份結構屬於 cuser ==> BM */ if (!(me.level = lastlevel)) { char *ptr; // warning: this is only valid for me.level. // is_uBM should not do anything except returning test result: // for ex, setting user BM permission automatically. // such extra behavior will result in any sub-op to have PERM_BM // ability, which leads to entering BM board without authority. // Thanks to mtdas@ptt for reporting this exploit. if (HasBasicUserPerm(PERM_LOGINOK) && !HasUserPerm(PERM_NOCITIZEN) && (ptr = strrchr(me.mtitle, '['))) me.level = is_uBM(ptr + 1, cuser.userid); } me.page = A_INVALID_PAGE; if (preselect && !*preselect) preselect = NULL; me.now = preselect ? (*preselect -1) : 0; for (;;) { if (me.now >= me.num) me.now = me.num - 1; if (me.now < 0) me.now = 0; if (me.now < me.page || me.now >= me.page + me.header_size) { me.page = me.now - ((me.page == 10000 && me.now > p_lines / 2) ? (p_lines / 2) : (me.now % p_lines)); if (!a_showmenu(&me)) { // some directories are invalid, restart! sess->bReturnToRoot = 1; break; } } if (preselect && *preselect && preselect[1]) { // if this is not the last preselect entry, enter it ch = KEY_ENTER; } else { ch = cursor_key(2 + me.now - me.page, 0); } if (ch == 'q' || ch == 'Q' || ch == KEY_LEFT) break; // TODO maybe we should let 1-9=simple search and z=tree-search // TODO or let 'z' prefix means 'back to root' if ((ch >= '1' && ch <= '9') || (ch == 'z' || ch == 'Z')) { int n = a_multi_search_num(ch, sess); me.page = A_INVALID_PAGE; if (n > 0) { // simple (single) selection me.now = n-1; me.page = 10000; // I don't know what's the magic value 10000... } else if (n == 0 && sess->z_indexes[0] == 0) { // empty/invalid input } else { // n == 0 with multiple selects preselect = sess->z_indexes; if (*preselect < 0) { // return to root first? if (parent) { sess->bReturnToRoot = 1; return DONOTHING; } // already in root preselect ++; } // handle first preselect (maybe zero due to previous 'already in root') if (*preselect > 0) me.now = *preselect - 1; else preselect = NULL; } continue; } switch (ch) { case KEY_UP: case 'k': if (--me.now < 0) me.now = me.num - 1; break; case KEY_DOWN: case 'j': if (++me.now >= me.num) me.now = 0; break; case KEY_PGUP: case Ctrl('B'): if (me.now >= p_lines) me.now -= p_lines; else if (me.now > 0) me.now = 0; else me.now = me.num - 1; break; case ' ': case KEY_PGDN: case Ctrl('F'): if (me.now < me.num - p_lines) me.now += p_lines; else if (me.now < me.num - 1) me.now = me.num - 1; else me.now = 0; break; case KEY_HOME: case '0': me.now = 0; break; case KEY_END: case '$': me.now = me.num - 1; break; case '?': case '/': if(me.num) { me.now = a_searchtitle(&me, ch == '?'); me.page = A_INVALID_PAGE; } break; case 'h': a_showhelp(me.level); me.page = A_INVALID_PAGE; break; case Ctrl('W'): a_where_am_i(root, me.now, me.header[me.now - me.page].title); vmsg(NULL); me.page = A_INVALID_PAGE; break; case 'e': case 'E': snprintf(fname, sizeof(fname), "%s/%s", path, me.header[me.now - me.page].filename); if (dashf(fname) && me.level >= MANAGER) { int edflags = 0; *quote_file = 0; # ifdef BN_BBSMOVIE if (me.bid && strcmp(getbcache(me.bid)->brdname, BN_BBSMOVIE) == 0) { edflags |= EDITFLAG_UPLOAD; edflags |= EDITFLAG_ALLOWLARGE; } # endif // BN_BBSMOVIE if (vedit2(fname, NA, NULL, edflags) != -1) { char fpath[PATHLEN]; fileheader_t fhdr; strlcpy(fpath, path, sizeof(fpath)); stampfile(fpath, &fhdr); unlink(fpath); strlcpy(fhdr.filename, me.header[me.now - me.page].filename, sizeof(fhdr.filename)); strlcpy(me.header[me.now - me.page].owner, cuser.userid, sizeof(me.header[me.now - me.page].owner)); setadir(fpath, path); substitute_record(fpath, me.header + me.now - me.page, sizeof(fhdr), me.now + 1); } me.page = A_INVALID_PAGE; } break; case 't': case 'c': if (me.now < me.num) { if (!isvisible_man(&me)) break; snprintf(fname, sizeof(fname), "%s/%s", path, me.header[me.now - me.page].filename); /* XXX: dirty fix 應該要改成如果發現該目錄裡面有隱形目錄的話才拒絕. 不過這樣的話須要整個搜一遍, 而且目前判斷該資料是目錄 還是檔案竟然是用 fstat(2) 而不是直接存在 .DIR 內 |||b 須等該資料寫入 .DIR 內再 implement才有效率. */ if( !me.level && !HasUserPerm(PERM_SYSOP) && (me.bid==0 || !is_BM_cache(me.bid)) && dashd(fname) ) vmsg("只有板主才可以拷貝目錄唷!"); else a_copyitem(fname, me.header[me.now - me.page].title, 0, 1); me.page = A_INVALID_PAGE; /* move down */ if (++me.now >= me.num) me.now = 0; break; } case KEY_ENTER: case KEY_RIGHT: case 'r': if (me.now >= me.num || me.now < 0) { preselect = NULL; continue; } else { fileheader_t *fhdr = &me.header[me.now - me.page]; const int *newselect = preselect ? preselect+1 : NULL; preselect = NULL; if (!isvisible_man(&me)) break; #ifdef DEBUG vmsgf("%s/%s", &path[11], fhdr->filename);; #endif snprintf(fname, sizeof(fname), "%s/%s", path, fhdr->filename); if (dashf(fname)) { int more_result; while ((more_result = more(fname, YEA))) { /* Ptt 範本精靈 plugin */ if (trans_buffer && (currstat == EDITEXP || currstat == OSONG)) { char ans[4]; move(22, 0); clrtoeol(); getdata(22, 1, currstat == EDITEXP ? "要把範例加入到文章內嗎?[y/N]" : "確定要選這篇嗎?[y/N]", ans, sizeof(ans), LCECHO); if (ans[0] == 'y') { strlcpy(trans_buffer, fname, PATHLEN); sess->bReturnToRoot = 1; if (currstat == OSONG) { log_filef(FN_USSONG, LOG_CREAT, "%s\n", fhdr->title); } free(me.header); return FULLUPDATE; } } if (more_result == READ_PREV) { if (--me.now < 0) { me.now = 0; break; } } else if (more_result == READ_NEXT) { if (++me.now >= me.num) { me.now = me.num - 1; break; } /* we only load me.header_size pages */ if (me.now - me.page >= me.header_size) break; } else break; if (!isvisible_man(&me)) break; snprintf(fname, sizeof(fname), "%s/%s", path, me.header[me.now - me.page].filename); if (!dashf(fname)) break; } } else if (dashd(fname)) { returnvalue = a_menu_rec(me.header[me.now - me.page].title, fname, me.level, me.bid, trans_buffer, sess, newselect, root, &me); if (returnvalue == DONOTHING) { // DONOTHING will only be caused by previous a_multi_search_num + preselect. assert(sess->bReturnToRoot); if (!parent) { // we've reached root menu! assert(sess->z_indexes[0] == -1); sess->bReturnToRoot = 0; returnvalue = FULLUPDATE; preselect = sess->z_indexes+1; // skip first 'return to root' if (*preselect > 0) me.now = *preselect-1; } } else { returnvalue = FULLUPDATE; } me.next = NULL; /* Ptt 強力跳出recursive */ if (sess->bReturnToRoot) { free(me.header); return returnvalue; } } me.page = A_INVALID_PAGE; } break; case 'F': case 'U': if (me.now < me.num) { fileheader_t *fhdr = &me.header[me.now - me.page]; if (!isvisible_man(&me)) break; snprintf(fname, sizeof(fname), "%s/%s", path, fhdr->filename); if (HasBasicUserPerm(PERM_LOGINOK) && dashf(fname)) { a_forward(path, fhdr, ch /* == 'U' */ ); /* By CharlieL */ } else vmsg("無法轉寄此項目"); me.page = A_INVALID_PAGE; } break; } if (me.level >= MANAGER) { switch (ch) { case 'n': a_newitem(&me, ADDITEM); me.page = A_INVALID_PAGE; break; case 'g': a_newitem(&me, ADDGROUP); me.page = A_INVALID_PAGE; break; case 'p': a_pasteitem(&me, 1); me.page = A_INVALID_PAGE; break; case 'f': a_editsign(&me); me.page = A_INVALID_PAGE; break; case Ctrl('P'): a_pastetagpost(&me, -1); returnvalue = DIRCHANGED; me.page = A_INVALID_PAGE; break; case Ctrl('A'): a_pastetagpost(&me, 1); returnvalue = DIRCHANGED; me.page = A_INVALID_PAGE; break; case 'a': a_appenditem(&me, 1); me.page = A_INVALID_PAGE; break; } if (me.num) switch (ch) { case 'm': a_moveitem(&me); me.page = A_INVALID_PAGE; break; case 'D': /* Ptt me.page = -1; */ a_delrange(&me, sess->backup_dir); me.page = A_INVALID_PAGE; break; case 'd': a_delete(&me, sess->backup_dir); me.page = A_INVALID_PAGE; break; case 'H': a_hideitem(&me); me.page = A_INVALID_PAGE; break; case 'T': a_newtitle(&me); me.page = A_INVALID_PAGE; break; #ifdef CHESSCOUNTRY case 'L': a_setchesslist(&me); break; #endif } } if (me.level >= SYSOP) { switch (ch) { case 'N': a_showname(&me); me.page = A_INVALID_PAGE; break; } } } free(me.header); return returnvalue; }