static int get_only_postno(const char *dotdir, int fd, FILEHEADER *fhr) { char finfo[PATHLEN]; INFOHEADER info; if (get_last_info(dotdir, fd, &info, FALSE) == -1) { bbslog("ERROR", "Getting INFO_REC."); fprintf(stderr, "ERROR: Getting INFO_REC."); return -1; } if (++info.last_postno > BRC_REALMAXNUM) info.last_postno = 1; /* reset the postno. */ fhr->postno = info.last_postno; info.last_mtime = fhr->mtime; strcpy(info.last_filename, fhr->filename); setdotfile(finfo, dotdir, INFO_REC); if (substitute_record(finfo, &info, IH_SIZE, 1) == -1) { bbslog("ERROR", "Updating INFO_REC. (%s)", finfo); fprintf(stderr, "ERROR: Updating INFO_REC. (%s)", finfo); return -1; } return 0; }
/* * immediately remove article which were mark deleted */ int pack_article(const char *direct) { int fdr, fdw; FILEHEADER fhr; FILE *fhw; char fn_dirty[PATHLEN]; int result = 0; if ((fdr = open(direct, O_RDWR)) < 0) return -1; if (myflock(fdr, LOCK_EX)) { close(fdr); return -1; } fhw = tmpfile(); fdw = fileno(fhw); while (myread(fdr, &fhr, FH_SIZE) == FH_SIZE) { if ((fhr.accessed & FILE_DELE)) { setdotfile(fn_dirty, direct, fhr.filename); unlink(fn_dirty); ++result; } else { if (mywrite(fdw, &fhr, FH_SIZE) != FH_SIZE) { result = -1; break; } } } if (result > 0) result = myfdcp(fdw, fdr); fclose(fhw); flock(fdr, LOCK_UN); close(fdr); return result; }
/* * Delete records pointed to missing article file */ int clean_dirent(const char *direct) { int fdr, fdw, fdt; FILEHEADER fhr; FILE *fhw; char fn_dirty[PATHLEN]; int result = 0; if ((fdr = open(direct, O_RDWR)) < 0) return -1; if (myflock(fdr, LOCK_EX)) { close(fdr); return -1; } fhw = tmpfile(); fdw = fileno(fhw); while (myread(fdr, &fhr, FH_SIZE) == FH_SIZE) { if (fhr.filename[0]) { fhr.filename[sizeof(fhr.filename)-1] = '\0'; setdotfile(fn_dirty, direct, fhr.filename); if ((fdt = open(fn_dirty, O_RDONLY)) > 0) { close(fdt); if (mywrite(fdw, &fhr, FH_SIZE) != FH_SIZE) { result = -1; break; } ++result; } } } if (result > 0) result = myfdcp(fdw, fdr); close(fdw); flock(fdr, LOCK_UN); close(fdr); return result; }
/******************************************************************* * 根據 URLParaType 執行 POST 的要求 * * return HttpRespondType *******************************************************************/ int DoPostRequest(REQUEST_REC * r, BOARDHEADER * board, POST_FILE * pf) { int result, URLParaType; char *form_data, *boardname; result = WEB_ERROR; URLParaType = r->URLParaType; boardname = board->filename; /* Get FORM data */ if ((form_data = GetFormBody(r->content_length, WEBBBS_ERROR_MESSAGE)) == NULL) return WEB_ERROR; #ifdef DEBUG weblog_line(server->debug_log, form_data); fflush(server->debug_log); #endif if (PSCorrect == nLogin && URLParaType == PostSend) { /* PostSend allow username&password in form body without login */ char pass[PASSLEN * 3]; GetPara2(username, "Name", form_data, IDLEN, ""); /* get userdata from form */ GetPara2(pass, "Password", form_data, PASSLEN * 3, ""); Convert(pass, password); PSCorrect = CheckUserPassword(username, password); } if (URLParaType == PostSend || URLParaType == TreaSend || URLParaType == PostEdit || URLParaType == TreaEdit || URLParaType == PostForward || URLParaType == TreaForward || URLParaType == PostDelete || URLParaType == TreaDelete || URLParaType == SkinModify || URLParaType == AccessListModify ) { int perm; /* boardname should set in advance, now in ParseURI() */ if (get_board(board, boardname) <= 0 || board->filename[0] == '\0') return WEB_BOARD_NOT_FOUND; if ((perm = CheckBoardPerm(board, &curuser)) != WEB_OK) return perm; } if (PSCorrect == Correct || (PSCorrect == gLogin && (URLParaType == PostSend || URLParaType == TreaSend)) || URLParaType == UserNew) { int start, end; char path[PATHLEN]; switch (URLParaType) { case PostSend: case TreaSend: if ((result = PostArticle(form_data, board, pf))) { #if 1 if (URLParaType == TreaSend) { if (strlen(pf->POST_NAME)) sprintf(skin_file->filename, "/%streasure/%s/%s/$", BBS_SUBDIR, boardname, pf->POST_NAME); else sprintf(skin_file->filename, "/%streasure/%s/$", BBS_SUBDIR, boardname); } else { sprintf(skin_file->filename, "/%sboards/%s/", BBS_SUBDIR, boardname); } #endif if (PSCorrect == Correct) UpdateUserRec(URLParaType, &curuser, board); } break; case MailSend: if ((result = PostArticle(form_data, board, pf))) { sprintf(skin_file->filename, "/%smail/", BBS_SUBDIR); UpdateUserRec(URLParaType, &curuser, NULL); } break; case PostEdit: case TreaEdit: if ((result = EditArticle(form_data, board, pf))) { sprintf(skin_file->filename, "/%s%s.html", BBS_SUBDIR, pf->POST_NAME); } break; case PostForward: case TreaForward: case MailForward: if ((result = ForwardArticle(form_data, board, pf))) { find_list_range(&start, &end, pf->num, DEFAULT_PAGE_SIZE, pf->total_rec); setdotfile(path, pf->POST_NAME, NULL); sprintf(skin_file->filename, "/%s%s%d-%d", BBS_SUBDIR, path, start, end); } break; case PostDelete: case TreaDelete: case MailDelete: if ((result = DeleteArticle(form_data, board, pf))) { if (URLParaType == PostDelete) { find_list_range(&start, &end, pf->num, DEFAULT_PAGE_SIZE, pf->total_rec); sprintf(skin_file->filename, "/%sboards/%s/%d-%d", BBS_SUBDIR, boardname, start, end); } else if (URLParaType == TreaDelete) { setdotfile(path, pf->POST_NAME, NULL); sprintf(skin_file->filename, "/%s%s", BBS_SUBDIR, path); } else /* MailDelete */ { sprintf(skin_file->filename, "/%smail/", BBS_SUBDIR); } } break; case UserNew: if ((result = NewUser(form_data, &curuser))) sprintf(skin_file->filename, "%s%s%s", HTML_PATH, BBS_SUBDIR, HTML_UserNewOK); break; case UserIdent: if ((result = DoUserIdent(form_data, &curuser))) sprintf(skin_file->filename, "%s%s%s", HTML_PATH, BBS_SUBDIR, HTML_UserIdentOK); break; case UserData: if ((result = UpdateUserData(form_data, &curuser))) sprintf(skin_file->filename, "/%susers/%s", BBS_SUBDIR, HTML_UserData); break; case UserPlan: if ((result = UpdateUserPlan(form_data, &curuser))) sprintf(skin_file->filename, "/%susers/%s", BBS_SUBDIR, HTML_UserPlan); break; case UserSign: if ((result = UpdateUserSign(form_data, &curuser))) sprintf(skin_file->filename, "/%susers/%s", BBS_SUBDIR, HTML_UserSign); break; case UserFriend: if ((result = UpdateUserFriend(form_data, &curuser))) sprintf(skin_file->filename, "/%susers/%s", BBS_SUBDIR, HTML_UserFriend); break; #ifdef WEB_ADMIN case BoardModify: /* admin function */ if (!HAS_PERM(PERM_SYSOP) #ifdef NSYSUBBS || !strstr(request_rec->fromhost, "140.17.12.") #endif ) { sprintf(WEBBBS_ERROR_MESSAGE, "%s 沒有權限修改看板設定", username); result = WEB_ERROR; } else if ((result = ModifyBoard(form_data, board))) sprintf(skin_file->filename, "/%sboards/%s/%s", BBS_SUBDIR, boardname, HTML_BoardModify); break; #endif case SkinModify: /* customize board skins */ if (strcmp(username, board->owner) && !HAS_PERM(PERM_SYSOP)) { sprintf(WEBBBS_ERROR_MESSAGE, "%s 沒有權限修改討論區介面", username); result = WEB_ERROR; } else if (!(board->brdtype & BRD_WEBSKIN)) { sprintf(WEBBBS_ERROR_MESSAGE, "討論區 [%s] 尚未打開自定介面功\能", board->filename); result = WEB_ERROR; } else if ((result = ModifySkin(form_data, board, pf)))
/******************************************************************* * 從 .DIR 中讀取 POST 相關資訊 * * 佈告區 & 精華區 & 信件 通用 *******************************************************************/ int GetPostInfo(BOARDHEADER *board, POST_FILE *pf) { int fd; time_t date; #ifdef USE_MMAP FILEHEADER *fileinfo; int i; #else FILEHEADER fileinfo; #endif char *p, board_dir[PATHLEN]; /* ====== get post info from DIR_REC ====== */ setdotfile(board_dir, pf->POST_NAME, DIR_REC); pf->total_rec = get_num_records(board_dir, FH_SIZE); if((pf->total_rec == 0) || (p = strrchr(pf->POST_NAME, '/')) == NULL) return WEB_FILE_NOT_FOUND; xstrncpy(pf->fh.filename, p+1, STRLEN-8-12-4-4); #if 0 fprintf(fp_out, "[board_dir=%s, total_post=%d, pf->POST_NAME=%s, pf->fh.filename=%s]\n", board_dir, pf->total_rec, pf->POST_NAME, pf->fh.filename); fflush(fp_out); #endif if ((fd = open(board_dir, O_RDWR)) < 0) return WEB_FILE_NOT_FOUND; /* seek from .DIR back is better */ pf->num = pf->total_rec; #ifdef USE_MMAP fileinfo = (FILEHEADER *) mmap((caddr_t) 0, (size_t)(pf->total_rec*FH_SIZE), (PROT_READ | PROT_WRITE), MAP_SHARED, fd, (off_t) 0); if(fileinfo == MAP_FAILED) { sprintf(WEBBBS_ERROR_MESSAGE, "mmap failed: %s %d", strerror(errno), (int)(pf->total_rec*FH_SIZE)); close(fd); return WEB_ERROR; } close(fd); while(pf->num > 0) { if(!strcmp((fileinfo+pf->num-1)->filename, pf->fh.filename)) { if((fileinfo+pf->num-1)->accessed & FILE_DELE) { munmap((void *)fileinfo, pf->total_rec*FH_SIZE); return WEB_FILE_NOT_FOUND; } break; } pf->num--; } if(pf->num < 1) { munmap((void *)fileinfo, pf->total_rec*FH_SIZE); return WEB_FILE_NOT_FOUND; } memcpy(&(pf->fh), fileinfo+pf->num-1, FH_SIZE); #else if(lseek(fd, (FH_SIZE*(pf->total_rec-1)), SEEK_SET) == -1) return WEB_FILE_NOT_FOUND; while(pf->num >= 0) { if(read(fd, &fileinfo, FH_SIZE)==FH_SIZE) { if(!strcmp(fileinfo.filename, pf->fh.filename)) { if(fileinfo.accessed & FILE_DELE) { close(fd); return WEB_FILE_NOT_FOUND; } break; } pf->num--; lseek(fd, -(FH_SIZE*2), SEEK_CUR); } else { close(fd); return WEB_FILE_NOT_FOUND; } } #if 0 /* search from head */ pf->num = 0; while (read(fd, &fileinfo, FH_SIZE) == FH_SIZE) { pf->num++; if (!strcmp(fileinfo.filename, pf->fh.filename)) { if(fileinfo.accessed & FILE_DELE) { close(fd); return WEB_FILE_NOT_FOUND; } break; } } #endif memcpy(&(pf->fh), &(fileinfo), FH_SIZE); #endif /* USE_MMAP */ date = atol((pf->fh.filename) + 2); /* get date from filename */ xstrncpy(pf->date, ctime(&date), STRLEN); if(request_rec->HttpRequestType != GET) { /* skip find last & next post info if not HTTP_GET */ #ifdef USE_MMAP munmap((void *)fileinfo, pf->total_rec*FH_SIZE); #else close(fd); #endif return WEB_OK; } if(request_rec->URLParaType == MailRead && (pf->fh.accessed & FILE_READ) == FALSE) { int maxkeepmail; if (curuser.userlevel == PERM_BM) maxkeepmail = SPEC_MAX_KEEP_MAIL; else maxkeepmail = MAX_KEEP_MAIL; if(curuser.userlevel != PERM_SYSOP && pf->num > maxkeepmail ) /* lthuang */ { #ifdef USE_MMAP munmap((void *)fileinfo, pf->total_rec*FH_SIZE); #else close(fd); #endif sprintf(WEBBBS_ERROR_MESSAGE, "%s 信箱已滿 ( %d 封), 請刪除舊信後再看新信...", curuser.userid, pf->total_rec); return WEB_ERROR; } /* set fileinfo as readed */ #ifdef USE_MMAP (fileinfo+pf->num-1)->accessed |= FILE_READ; #else if (lseek(fd, -FH_SIZE, SEEK_CUR) == -1) { close(fd); return WEB_FILE_NOT_FOUND; } fileinfo.accessed |= FILE_READ; write(fd, &fileinfo, FH_SIZE); #endif } #ifdef TORNADO_OPTIMIZE #if defined(NSYSUBBS1) || defined(NSYSUBBS3) if(isTORNADO && request_rec->URLParaType == PostRead) { if(pf->total_rec - pf->num > TORNADO_GET_MAXPOST) { strcpy(pf->lfname, "-1"); strcpy(pf->nfname, "-1"); #ifdef USE_MMAP munmap((void *)fileinfo, pf->total_rec*FH_SIZE); #else close(fd); #endif return WEB_OK; } } #endif #endif /* get previous post filename */ #ifdef USE_MMAP for(i=pf->num-1; i>0; i--) { if(*((fileinfo+i-1)->filename) != 0x00 && !((fileinfo+i-1)->accessed & FILE_DELE) && !((fileinfo+i-1)->accessed & FILE_TREA)) { xstrncpy(pf->lfname, (fileinfo+i-1)->filename, STRLEN-8); break; } } if(i <= 0) strcpy(pf->lfname, "-1"); #else while(1) { if(lseek(fd, -(FH_SIZE*2), SEEK_CUR) == -1) { strcpy(pf->lfname, "-1"); break; } if(read(fd, &fileinfo, FH_SIZE)==FH_SIZE) { if( *fileinfo.filename != 0x00 && !(fileinfo.accessed & FILE_DELE) && !(fileinfo.accessed & FILE_TREA)) { xstrncpy(pf->lfname, fileinfo.filename, STRLEN-8); break; } } } #endif /* USE_MMAP */ if(strcmp(pf->lfname, "-1")) #ifdef USE_MMAP if((fileinfo+i-1)->accessed & FILE_HTML) #else if(fileinfo.accessed & FILE_HTML) #endif pf->type |= LAST_POST_IS_HTML; #if 0 fprintf(fp_out, "[file_num=%d, last_filename=%s]", pf->num, pf->lfname); fflush(fp_out); #endif /* get next post filename */ #ifdef USE_MMAP for(i=pf->num+1; i<=pf->total_rec; i++) { if(*((fileinfo+i-1)->filename) != 0x00 && !((fileinfo+i-1)->accessed & FILE_DELE) && !((fileinfo+i-1)->accessed & FILE_TREA)) { xstrncpy(pf->nfname, (fileinfo+i-1)->filename, STRLEN-8); break; } } if(i >= pf->total_rec+1) strcpy(pf->nfname, "-1"); #else if (lseek(fd, (long) (FH_SIZE * (pf->num)), SEEK_SET) == -1) { close(fd); return WEB_FILE_NOT_FOUND; } while(1) { if(read(fd, &fileinfo, FH_SIZE)==FH_SIZE) { if( *fileinfo.filename != 0x00 && !(fileinfo.accessed & FILE_DELE) && !(fileinfo.accessed & FILE_TREA)) { xstrncpy(pf->nfname, fileinfo.filename, STRLEN-8); break; } } else { strcpy(pf->nfname, "-1"); break; } } #endif /* USE_MMAP */ if(strcmp(pf->nfname, "-1")) #ifdef USE_MMAP if((fileinfo+i-1)->accessed & FILE_HTML) #else if(fileinfo.accessed & FILE_HTML) #endif pf->type |= NEXT_POST_IS_HTML; #if 0 fprintf(fp_out, "[next_filename=%s]", pf->nfname); fflush(fp_out); #endif #ifdef USE_MMAP munmap((void *)fileinfo, pf->total_rec*FH_SIZE); #else close(fd); #endif return WEB_OK; }
/* * postno is for readrc mechanism * It reads the last postno information from INFO_REC * If failed, scan all .DIR file to find the last postno. * and write it back to INFO_REC. */ int get_last_info(const char *dotdir, int fd, INFOHEADER *info, int force) { char finfo[PATHLEN]; setdotfile(finfo, dotdir, INFO_REC); if (force || (get_record(finfo, info, IH_SIZE, 1) != 0)) { int i, nr, myfd; FILEHEADER lastf, fhtmp; time_t lastmtime = 0, mtime; if (!dotdir && !fd) return -1; if (!fd) { myfd = open(dotdir, O_RDWR | O_CREAT, 0644); if (myfd == -1) return -1; if (myflock(myfd, LOCK_EX)) { close(myfd); return -1; } } else { myfd = fd; } nr = get_num_records_byfd(myfd, FH_SIZE); for (i = 1; i <= nr; ++i) { if (get_record_byfd(myfd, &fhtmp, FH_SIZE, i) == 0) { if (fhtmp.accessed & FILE_DELE) continue; if (fhtmp.mtime) mtime = fhtmp.mtime; else if (fhtmp.filename[0] == 'M') mtime = strtol(fhtmp.filename + 2, NULL, 10); else mtime = 0; if (mtime > lastmtime) { memcpy(&lastf, &fhtmp, FH_SIZE); lastmtime = mtime; } } else { break; } } if (!fd) { flock(myfd, LOCK_UN); close(myfd); } if (i <= nr) return -1; memset(info, 0, IH_SIZE); if (lastmtime) { info->last_postno = lastf.postno; info->last_mtime = lastf.mtime; strcpy(info->last_filename, lastf.filename); } else { /* There is no article yet. */ info->last_postno = 0; info->last_mtime = 0; strcpy(info->last_filename, "M.000000000.A"); } if (substitute_record(finfo, info, IH_SIZE, 1) == -1) return -1; } return 0; }
int recover_dirent(const char *direct) { struct file_list *dl; size_t dl_size; int i = 0, cmp, fdr, fdw; char dirpath[PATHLEN]; FILEHEADER fhr, nfhr; FILE *fhw; int result = 0; setdotfile(dirpath, direct, NULL); dl = get_file_list(dirpath, &dl_size, "M."); if (!dl) return -1; qsort(dl, dl_size, sizeof(struct file_list), cmpfun); if ((fdr = open(direct, O_RDWR)) < 0) return -1; if (myflock(fdr, LOCK_EX)) { close(fdr); return -1; } fhw = tmpfile(); fdw = fileno(fhw); while (myread(fdr, &fhr, FH_SIZE) == FH_SIZE) { cmp = cmpfun(fhr.filename, dl[i].fname); while (cmp > 0) { restore_fileheader(&nfhr, direct, dl[i].fname); dbg("Inserted %s\n", dl[i].fname); dbg("\tDate: %s User: %s Ident: %d\n", nfhr.date, nfhr.owner, nfhr.ident); dbg("\tTitle: %s\n", nfhr.title); if (mywrite(fdw, &nfhr, FH_SIZE) != FH_SIZE) { result = -1; break; } cmp = cmpfun(fhr.filename, dl[++i].fname); ++result; } if (cmp == 0) { ++i; } else { dbg("Missing %s\n", fhr.filename); dbg("\tDate: %s User: %s Ident: %d\n", fhr.date, fhr.owner, fhr.ident); dbg("\tTitle: %s\n", fhr.title); } if (mywrite(fdw, &fhr, FH_SIZE) != FH_SIZE) { result = -1; break; } } if (result > 0) result = myfdcp(fdw, fdr); fclose(fhw); flock(fdr, LOCK_UN); close(fdr); free(dl); return result; }
static void restore_fileheader(FILEHEADER *fhr, const char *direct, const char *fname) { time_t t; struct tm *tmp; char dirpath[PATHLEN], buf[512], *p, *sp, *sp2; USEREC urc; memset(fhr, 0, sizeof(FILEHEADER)); if (!fhr || !fname) return; strcpy(fhr->filename, fname); t = strtol(fname + 2, NULL, 10); tmp = localtime(&t); if (tmp) sprintf(fhr->date, "%02d/%02d/%02d", tmp->tm_year - 11, tmp->tm_mon + 1, tmp->tm_mday); else strcpy(fhr->date, "00/00/00"); setdotfile(dirpath, direct, fname); get_record(dirpath, buf, sizeof(buf), 1); p = strtok_r(buf, " ", &sp); p = strtok_r(NULL, " ", &sp); if (p) { strcpy(fhr->owner, p); if (!strchr(fhr->owner, '.')) { if (get_passwd(&urc, fhr->owner) > 0) fhr->ident = urc.ident; } else { p = strtok_r(fhr->owner, "@.", &sp2); sprintf(buf, "#%s", p); strcpy(fhr->owner, buf); } } else { strcpy(fhr->owner, "UNKNOWN"); } if (sp && (((p = strstr (sp, "標題:")) && (p = p + 5)) || ((p = strstr (sp, "標題:")) && (p = p + 6)) || ((p = strstr (sp, "標 題:")) && (p = p + 7)) || ((p = strstr (sp, "Title:")) && (p = p + 6)) || ((p = strstr (sp, "Subject:")) && (p = p + 8)))) { while (*p == ' ') p++; if (*p != '\n') { strtok (p, "\n"); strcpy (fhr->title, p); } } else { strcpy(fhr->title, "UNKNOWN"); } if (get_only_postno(direct, 0, fhr) == -1) { bbslog("ERROR", "Getting only postno. (%s)", direct); fprintf(stderr, "ERROR: Getting only postno. (%s)", direct); exit(1); } /* * Mark readed for bbspop3d */ if (strstr(direct, "mail")) fhr->accessed |= FILE_READ; }