/* $Id: webbbs.c,v 2.1 2006/07/17 13:30:09 bbs Exp $ */ #include #include #include #include #include #include #include #include #include #include /*********************************************************************/ /* constants */ #define web_NumOfStyle 2 #define EvenColor "#F4F4FF" #define OddColor "#EEEEFF" static const char *const string_file[web_NumOfStyle] = { "cweb-1.txt", "cweb-1.txt" }; static const char replace_escape = '$'; typedef enum web_state_t { web_LOGINPAGE = 0, web_LOGIN = 1, web_LOGOUT = 2, web_SETTING = 3, web_MAINMENU = 10, web_BOARDROOT = 11, web_BOARD = 12, web_ARTICLE = 13, web_POSTEDIT = 14, web_POST = 15, web_NEWACCOUNT = 20, web_REGISTER = 21, web_THREAD = 22, web_THREADLIST = 23, web_CGIADMIN = 9999 } web_state_t; const int WEB_IDLETIMEOUT = 1800; const int WEB_SLEEPTIME = 120; const int WEB_STYLETIMEOUT = 1209600; /*********************************************************************/ /* variables */ char *ProgramName = "webbbs.cgi"; struct db_thread_t { db_database_t *db; int pagesize; int count; int from; char *title; } db_thread_t; struct web_strings_t { struct { char *page, *body; } mainmenu; struct { char *page, *success, *fail; } login; struct { char *page; } relogin; struct { char *success, *fail; } logout; struct { char *select; } setting; struct { char *header, *loop, *root, *footer; } boardlist; struct { char *header, *loop, *footer; } board; struct { char *header, *loop, *footer; } thread; struct { char *edit, *deny, *success; } post; struct { char *request, *success, *failure; } reg; } web_strings[web_NumOfStyle], *web_string = web_strings; /*********************************************************************/ /* macros */ #define ARRAY_LENGTH(array) (sizeof (array) / sizeof *(array)) /*********************************************************************/ void web_error_message(char const *msg) { printf("CONTENT-TYPE: text/plain\r\n\r\n%s\n", msg); } void web_http_header(char const *moreheader) { printf("CONTENT-TYPE: text/html; CHARSET=Big5\r\n%s\r\n " , moreheader); } /*********************************************************************/ static void setstringdata(void *dest, char *str, void *datum) { *(char **)((char *)datum + ((char *)dest - (char *)web_strings)) = str; } static struct web_strfile_t strfile[] = { { "mainmenu.page", setstringdata, &web_strings->mainmenu.page }, { "mainmenu.body", setstringdata, &web_strings->mainmenu.body }, { "login.page", setstringdata, &web_strings->login.page }, { "login.success", setstringdata, &web_strings->login.success }, { "login.fail", setstringdata, &web_strings->login.fail }, { "relogin.page", setstringdata, &web_strings->relogin.page }, { "logout.success", setstringdata, &web_strings->logout.success }, { "logout.fail", setstringdata, &web_strings->logout.fail }, { "setting.select", setstringdata, &web_strings->setting.select }, { "boardlist.header", setstringdata, &web_strings->boardlist.header }, { "boardlist.loop", setstringdata, &web_strings->boardlist.loop }, { "boardlist.root", setstringdata, &web_strings->boardlist.root }, { "boardlist.footer", setstringdata, &web_strings->boardlist.footer }, { "board.header", setstringdata, &web_strings->board.header }, { "board.loop", setstringdata, &web_strings->board.loop }, { "board.footer", setstringdata, &web_strings->board.footer }, { "thread.header", setstringdata, &web_strings->thread.header }, { "thread.loop", setstringdata, &web_strings->thread.loop }, { "thread.footer", setstringdata, &web_strings->thread.footer }, { "post.edit", setstringdata, &web_strings->post.edit }, { "post.deny", setstringdata, &web_strings->post.deny }, { "post.success", setstringdata, &web_strings->post.success }, { "reg.request", setstringdata, &web_strings->reg.request }, { "reg.success", setstringdata, &web_strings->reg.success }, { "reg.failure", setstringdata, &web_strings->reg.failure } }; static int read_strings(void) { auto int i; for (i=0; imainmenu.body, replace_escape, replace, ARRAY_LENGTH(replace), web_replace_write, file, NULL); } void web_mainmenu(db_database_t *db, struct web_strings_t *strings) { struct web_replace_t replace[] = { { 'P', web_replace_STRING, ProgramName }, { 'M', web_replace_FUNCTION, web_mainmenu_body } }; db_Database_SetCurrentState(db, MMENU); web_http_header(""); web_ReplaceOutput(strings->mainmenu.page, replace_escape, replace, ARRAY_LENGTH(replace), web_replace_write, stdout, NULL); } /*********************************************************************/ /* login and logout */ static db_database_t *web_dummy_db; static void web_dummy_sigLogout(int sig) { db_Database_Logout(web_dummy_db); exit(EXIT_SUCCESS); } /* Argument db is a logged in database handle, and it will logout when web_dummy() return. */ void web_dummy(db_database_t *db) { close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); web_dummy_db = db; web_dummy_db->userinfo->pid = getpid(); signal(SIGTERM, web_dummy_sigLogout); do { sleep(WEB_SLEEPTIME); } while (time(NULL) - db->userinfo->lastact < WEB_IDLETIMEOUT); db_Database_Logout(web_dummy_db); exit(EXIT_SUCCESS); } void web_login(db_database_t *db, const char *id, const char *passwd) { int session, pagesize; char buf[128]; struct web_replace_t replace[] = { { 'P', web_replace_STRING, ProgramName }, { 'M', web_replace_FUNCTION, web_mainmenu_body }, { 's', web_replace_UINT, &session } }; pagesize = 20; if (!web_GetCookie("pagesize", buf, sizeof buf)) { pagesize = atoi(buf); if (pagesize < 10 || pagesize > 500) pagesize = 20; } db_Database_Init(db); if (db_Database_Login(db, id, passwd, getenv("REMOTE_ADDR"))) { web_http_header(""); /* web_ReplaceOutput(web_string->login.fail, replace_escape, replace, ARRAY_LENGTH(replace), web_replace_write, stdout, NULL);*/ return; } if (!fork()) { web_dummy(db); exit(EXIT_SUCCESS); } session = db_Database_Session(db); snprintf(buf, sizeof buf, "CACHE-CONTROL: no-cache,no-store,max-age=0\r\n" "PRAGMA: no-cache\r\n" "EXPIRES: 0\r\n" "SET-COOKIE: session=%08X%08X\r\n", session, db->userinfo->random); web_http_header(buf); /* web_ReplaceOutput(web_string->login.success, replace_escape, replace, ARRAY_LENGTH(replace), web_replace_write, stdout, NULL); */ } void web_logout(db_database_t *db) { struct web_replace_t replace[] = { { 'P', web_replace_STRING, ProgramName }, { 'M', web_replace_FUNCTION, web_mainmenu_body } }; char buf[250]; snprintf(buf, sizeof buf, "CACHE-CONTROL: no-cache,no-store,max-age=0\r\n" "PRAGMA: no-cache\r\n" "EXPIRES: 0\r\n" "SET-COOKIE: session=; max-age=0\r\n"); web_http_header(buf); if (db->userinfo == NULL || kill(db->userinfo->pid, SIGTERM) == -1) { /* logout fail */ /* web_ReplaceOutput(web_string->logout.fail, replace_escape, replace, ARRAY_LENGTH(replace), web_replace_write, stdout, NULL);*/ } else { /* logout success */ /* web_ReplaceOutput(web_string->logout.success, replace_escape, replace, ARRAY_LENGTH(replace), web_replace_write, stdout, NULL);*/ } } void web_loginpage(db_database_t *db) { char *userid = (db->userinfo != NULL) ? db->userinfo->userid : "guest"; char *username = (db->userinfo != NULL) ? db->userinfo->username : "訪客"; unsigned int I = IDLEN+2, W = PASSLEN+2; struct web_replace_t replace[] = { { 'P', web_replace_STRING, ProgramName }, { 'U', web_replace_STRING, userid }, { 'N', web_replace_STRING, username }, { 'I', web_replace_UINT, &I }, { 'W', web_replace_UINT, &W } }; // web_http_header(""); if (strcmp(userid,"guest")==0) { web_ReplaceOutput(web_string->login.page, replace_escape, replace, ARRAY_LENGTH(replace), web_replace_write, stdout, NULL); } else web_ReplaceOutput(web_string->login.success, replace_escape, replace, ARRAY_LENGTH(replace), web_replace_write, stdout, NULL); } void web_reloginpage(void) { unsigned int I = IDLEN+2, W = PASSLEN+2; struct web_replace_t replace[] = { { 'P', web_replace_STRING, ProgramName }, { 'I', web_replace_UINT, &I }, { 'W', web_replace_UINT, &W } }; char buf[250]; snprintf(buf, sizeof buf, "CACHE-CONTROL: no-cache,no-store,max-age=0\r\n" "PRAGMA: no-cache\r\n" "EXPIRES: 0\r\n" "SET-COOKIE: session=; max-age=0\r\n"); web_http_header(buf); web_ReplaceOutput(web_string->relogin.page, replace_escape, replace, ARRAY_LENGTH(replace), web_replace_write, stdout, NULL); } /*********************************************************************/ void web_setting_page() { char cookie[128], pagesize[32], style[32]; struct web_replace_t replace[] = { { 'P', web_replace_STRING, ProgramName }, { 'M', web_replace_FUNCTION, web_mainmenu_body } }; web_GetQuery("pagesize", pagesize, sizeof pagesize); web_GetQuery("style", style, sizeof style); snprintf(cookie, sizeof cookie, "SET-COOKIE: pagesize=%s; max-age=%d\r\n" "SET-COOKIE: style=%s; max-age=%d\r\n", pagesize, WEB_STYLETIMEOUT, style, WEB_STYLETIMEOUT); web_http_header(cookie); web_ReplaceOutput(web_string->setting.select, replace_escape, replace, ARRAY_LENGTH(replace), web_replace_write, stdout, NULL); } /*********************************************************************/ /* show a board */ static void web_board_showmarker(void *_file, void *_article) { db_article_t *const article = _article; if (article->header.filemode & FILE_NOREPLY) { fputs("X", stdout); } else if (article->header.filemode & FILE_DIGEST) { fputs("*", stdout); } else if (article->header.filemode & FILE_MARKED) { fputs("m", stdout); } else { fputs(" ", stdout); } } static void web_board_showtitle(void *_file, void *_article) { db_article_t *const article = _article; web_PrintString(_file, article->header.title); } static int web_threadarticle_cb(db_article_t const *article, void *datum) { struct db_thread_t *thread_data = datum; db_database_t *db = thread_data->db; unsigned int *count = &(thread_data->count); unsigned int *from = &(thread_data->from); unsigned char *title = thread_data->title; FILE *file; const char *filename; int uid; userec_t user; uid = db_Database_FindUser(db, &article->header.owner); if (uid > -1) db_Database_ReadUserRecord(uid, &user ); /* make the string of date */ if (strstr(&article->header.title, title)) { filename = db_Article_Filename(article); if ((file = fopen(filename, "r")) == NULL) { printf("找不到文章所屬檔案"); return; } table_article(db, file, article, *from); close(file); ++*count; } ++*from; return 0; } static int web_threadlist_cb(db_article_t const *article, void *datum) { struct db_thread_t *thread_data = datum; db_database_t *db = thread_data->db; unsigned int *count = &(thread_data->count); unsigned int *from = &(thread_data->from); unsigned int *pagesize = &(thread_data->pagesize); unsigned char *title = thread_data->title; struct tm *posttime; char date_str[16]; char bg_str[10]; unsigned int r_i = db_Board_ID(article->board); unsigned int r_r = db_Article_NumRecommend(article); struct web_replace_t replace[] = { { 'P', web_replace_STRING, ProgramName }, { 'M', web_replace_FUNCTION, web_mainmenu_body }, { 'i', web_replace_UINT, &r_i }, { 'n', web_replace_FUNCTION, web_board_showtitle }, { 'r', web_replace_UINT, &r_r }, { 'd', web_replace_STRING, date_str }, { 'z', web_replace_STRING, bg_str }, { 'm', web_replace_FUNCTION, web_board_showmarker }, { 'a', web_replace_STRING, &article->header.owner }, { 't', web_replace_FUNCTION, web_board_showtitle } }; /* make the string of date */ if (strstr(&article->header.title, "Re: ")==0) { if ((*count >= *from ) && (*count < *from + *pagesize)) { snprintf(bg_str, sizeof bg_str, EvenColor); if ((*count % 2) == 0) snprintf(bg_str, sizeof bg_str, OddColor); posttime = localtime(&article->header.date); snprintf(date_str, sizeof date_str, "%02d/%02d/%04d", posttime->tm_mday, posttime->tm_mon+1, posttime->tm_year+1900); web_ReplaceOutput(web_string->thread.loop, replace_escape, replace, ARRAY_LENGTH(replace), web_replace_write, stdout, (void *)article); } ++*count; } return 0; } static int web_board_cb(db_article_t const *article, void *datum) { unsigned int *count = datum; struct tm *posttime; char date_str[16]; char bg_str[10]; unsigned int r_i = db_Board_ID(article->board); unsigned int r_r = db_Article_NumRecommend(article); struct web_replace_t replace[] = { { 'P', web_replace_STRING, ProgramName }, { 'M', web_replace_FUNCTION, web_mainmenu_body }, { 'i', web_replace_UINT, &r_i }, { 'n', web_replace_UINT, count }, { 'r', web_replace_UINT, &r_r }, { 'd', web_replace_STRING, date_str }, { 'z', web_replace_STRING, bg_str }, { 'm', web_replace_FUNCTION, web_board_showmarker }, { 'a', web_replace_STRING, &article->header.owner }, { 't', web_replace_FUNCTION, web_board_showtitle } }; /* make the string of date */ snprintf(bg_str, sizeof bg_str, EvenColor); if ((*count % 2) == 0) snprintf(bg_str, sizeof bg_str, OddColor); posttime = localtime(&article->header.date); snprintf(date_str, sizeof date_str, "%02d/%02d/%04d", posttime->tm_mday, posttime->tm_mon+1, posttime->tm_year+1900); web_ReplaceOutput(web_string->board.loop, replace_escape, replace, ARRAY_LENGTH(replace), web_replace_write, stdout, (void *)article); ++*count; return 0; } void web_threadlist_show(db_database_t *db, db_board_t const *board, int from, int pagesize) { char *userid = (board->database->userinfo != NULL) ? board->database->userinfo->userid : "guest"; char *username = (board->database->userinfo != NULL) ? board->database->userinfo->username : "訪客"; unsigned int numpost = db_Board_Number(board); struct db_thread_t t_data; int totalpage = 0; unsigned int pk = 0; // struct hash_table ht; int i; struct web_replace_t replace[] = { { 'P', web_replace_STRING, ProgramName }, { 'M', web_replace_FUNCTION, web_mainmenu_body }, { 'U', web_replace_STRING, userid }, { 'N', web_replace_STRING, username }, { 'b', web_replace_UINT, (void *)&board->boardid }, { 'e', web_replace_STRING, db_Board_Name(board) }, { 'c', web_replace_STRING, db_Board_Title(board) }, { 'm', web_replace_STRING, db_Board_BoardMasters(board) }, }; db_Database_SetCurrentState(board->database, READNEW); web_http_header(""); web_ReplaceOutput(web_string->thread.header, replace_escape, replace, ARRAY_LENGTH(replace), web_replace_write, stdout, NULL); t_data.count = 0; t_data.pagesize = 20; t_data.from = from; t_data.db = db; t_data.title = "b"; // initialize(ht, 16384); db_Board_List(board, 1, numpost, web_threadlist_cb, &t_data); totalpage = (t_data.count - 1) / pagesize + 1; printf("
Page "); for (i=0; i", ProgramName, web_THREADLIST, board->boardid , i*pagesize +1); printf("%d ", i + 1); } printf("
"); web_ReplaceOutput(web_string->thread.footer, replace_escape, replace, ARRAY_LENGTH(replace), web_replace_write, stdout, NULL); } void web_thread_show(db_database_t *db, db_board_t const *board, int pagesize, char *title) { char *userid = (board->database->userinfo != NULL) ? board->database->userinfo->userid : "guest"; char *username = (board->database->userinfo != NULL) ? board->database->userinfo->username : "訪客"; unsigned int numpost = db_Board_Number(board); struct db_thread_t thread_data; unsigned int pk = 0; struct db_board_t *group; struct web_replace_t replace[] = { { 'P', web_replace_STRING, ProgramName }, { 'M', web_replace_FUNCTION, web_mainmenu_body }, { 'U', web_replace_STRING, userid }, { 'N', web_replace_STRING, username }, { 'b', web_replace_UINT, (void *)&board->boardid }, { 'e', web_replace_STRING, db_Board_Name(board) }, { 'c', web_replace_STRING, db_Board_Title(board) }, { 'm', web_replace_STRING, db_Board_BoardMasters(board) } }; db_Database_SetCurrentState(board->database, READNEW); web_http_header(""); web_ReplaceOutput(web_string->board.header, replace_escape, replace, ARRAY_LENGTH(replace), web_replace_write, stdout, NULL); // group = board->header->grup; // printf("THE BOARD2: %d
", &board->groupid); // printf("THE BOARD: %s ", &group->header->title); // (db_board_t *)(&board->header->parent)); thread_data.count = 0; thread_data.pagesize = 25; thread_data.db = db; thread_data.title = title; thread_data.from = 1; db_Board_List(board, 1, numpost, web_threadarticle_cb, &thread_data); web_ReplaceOutput(web_string->board.footer, replace_escape, replace, ARRAY_LENGTH(replace), web_replace_write, stdout, NULL); } void web_board_show(db_database_t *db, db_board_t const *board, int from, int pagesize) { char *userid = (board->database->userinfo != NULL) ? board->database->userinfo->userid : "guest"; char *username = (board->database->userinfo != NULL) ? board->database->userinfo->username : "訪客"; unsigned int numpost = db_Board_Number(board); struct db_thread_t thread_data; unsigned int count = from; unsigned int pk = 0; unsigned int to = from+pagesize < numpost ? from+pagesize : numpost; unsigned int r_u = (from > pagesize ? from-pagesize : 1); unsigned int r_l = (numpost+5 > pagesize ? numpost-pagesize+5 : 1); unsigned int r_d = (to <= r_l ? to : r_l); struct web_replace_t replace[] = { { 'P', web_replace_STRING, ProgramName }, { 'M', web_replace_FUNCTION, web_mainmenu_body }, { 'U', web_replace_STRING, userid }, { 'N', web_replace_STRING, username }, { 'b', web_replace_UINT, (void *)&board->boardid }, { 'e', web_replace_STRING, db_Board_Name(board) }, { 'c', web_replace_STRING, db_Board_Title(board) }, { 'm', web_replace_STRING, db_Board_BoardMasters(board) }, { 'n', web_replace_UINT, &from }, { 'u', web_replace_UINT, &r_u }, { 'd', web_replace_UINT, &r_d }, { 'l', web_replace_UINT, &r_l } }; db_Database_SetCurrentState(board->database, READNEW); if (from < 0) from = 0; web_http_header(""); web_ReplaceOutput(web_string->board.header, replace_escape, replace, ARRAY_LENGTH(replace), web_replace_write, stdout, NULL); thread_data.count = 0; thread_data.pagesize = 25; thread_data.db = db; thread_data.title = "二b中"; db_Board_List(board, from, to, web_board_cb, &count); web_ReplaceOutput(web_string->board.footer, replace_escape, replace, ARRAY_LENGTH(replace), web_replace_write, stdout, NULL); } /*********************************************************************/ /* show boardlist */ static int web_boardlist_cb(db_board_t const *board, void *datum) { int from; int numpost; db_article_t article; unsigned int *count = datum; char grp_str[8], forward_str[8]; struct tm *posttime; char title[TTLEN+1]; char owner[IDLEN+2]; char bg_str[10]; char date_str[16]; struct web_replace_t replace[] = { { 'P', web_replace_STRING, ProgramName }, { 'M', web_replace_FUNCTION, web_mainmenu_body }, { 'i', web_replace_UINT, (void *)&board->boardid }, { 'n', web_replace_UINT, &from }, { 'a', web_replace_STRING, db_Board_Name(board) }, { 'c', web_replace_STRING, grp_str }, { 'f', web_replace_STRING, forward_str }, { 't', web_replace_STRING, db_Board_Title(board)+7 }, { 'b', web_replace_STRING, board->header->BM }, { 'k', web_replace_UINT , &numpost }, { 'g', web_replace_STRING, bg_str }, { 'x', web_replace_STRING, date_str }, { 'y', web_replace_STRING, title }, { 'z', web_replace_STRING, owner }, }; from = db_Board_Number(board) - 20; //*(int *)datum + 5; if (from < 1) from = 1; numpost = db_Board_Number(board); snprintf(bg_str, sizeof bg_str, EvenColor); if ((*count % 2) == 0) snprintf(bg_str, sizeof bg_str, OddColor); ++*count; strncpy(grp_str, db_Board_Title(board), 4); grp_str[4] = '\0'; strncpy(forward_str, db_Board_Title(board)+5, 2); forward_str[2] = '\0'; if (numpost > 0) { db_Board_Article(board, numpost, &article); snprintf(title, sizeof title, "%s", article.header.title ); snprintf(owner, sizeof owner, "%s", article.header.owner ); posttime = localtime(&article.header.date); snprintf(date_str, sizeof date_str, "%02d/%02d/%04d", posttime->tm_mday, posttime->tm_mon+1, posttime->tm_year+1900); } else { strcpy(title, ""); strcpy(owner, ""); strcpy(date_str, ""); } if (numpost >= 0) { web_ReplaceOutput(web_string->boardlist.loop, replace_escape, replace, ARRAY_LENGTH(replace), web_replace_write, stdout, NULL); } else web_ReplaceOutput(web_string->boardlist.root, replace_escape, replace, ARRAY_LENGTH(replace), web_replace_write, stdout, NULL); return 0; } void web_boardlist_show(db_boardlist_t *list, int pagesize) { char *userid = (list->database->userinfo != NULL) ? list->database->userinfo->userid : "guest"; char *username = (list->database->userinfo != NULL) ? list->database->userinfo->username : "訪客"; struct web_replace_t replace[] = { { 'P', web_replace_STRING, ProgramName }, { 'M', web_replace_FUNCTION, web_mainmenu_body }, { 'U', web_replace_STRING, userid }, { 'N', web_replace_STRING, username } }; int count = 0; db_Database_SetCurrentState(list->database, READBRD); web_ReplaceOutput(web_string->boardlist.header, replace_escape, replace, ARRAY_LENGTH(replace), web_replace_write, stdout, NULL); if (db_BoardList_List(list, 0, 0, web_boardlist_cb, &count)) printf("\r\n

db_BoardList_List() error

\r\n"); web_ReplaceOutput(web_string->boardlist.footer, replace_escape, replace, ARRAY_LENGTH(replace), web_replace_write, stdout, NULL); } /* Display the root boardlist */ void web_boardlist_root(db_database_t *db, int pagesize) { db_boardlist_t list; if (db_BoardList_InitRoot(&list, db)) { web_error_message("內部錯誤,不能列出板區列表"); return; } web_boardlist_show(&list, pagesize); db_BoardList_Done(&list); } void web_boardlist_sublist(db_database_t *db, db_board_t *board, int pagesize) { db_boardlist_t list; if (db_BoardList_InitRoot(&list, db)) { web_error_message("內部錯誤,不能列出看板列表"); return; } db_BoardList_InitSublist(&list, board); web_http_header(""); web_boardlist_show(&list, pagesize); db_BoardList_Done(&list); } /*********************************************************************/ /* display an article */ /* Note: This two functions, quote_string() and display_article(), is copy from BBS2WWW-2.01. */ /* BBS2WWW -- WWW Interface for Firebird Bulletin Board System Copyright (C) 1996-2001 Computer Application Studio. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ static int quote_string (char *buf) { if (strstr (buf, "的大作中提到: 】") != NULL || strstr (buf, "□ 引用發信人:") != NULL || strstr (buf, "==>[作者]:") != NULL || (strstr (buf, "==>") != NULL && strstr (buf, ") 提到:") != NULL) || (strstr (buf, "【 在") != NULL && strstr (buf, "的來信中提到: 】") != NULL) || (strstr (buf, "==>") != NULL && strstr (buf, "] 文中提到:") != NULL) || (strstr (buf, "==> 在") != NULL && strstr (buf, "的文章中提到:") != NULL) || (strstr (buf, "==> 於") != NULL && strstr (buf, "文中述及:") != NULL) || (strstr (buf, "※ 引述") != NULL && strstr (buf, "之銘言:") != NULL)) return 1; else return 0; } static void display_article (FILE * fp, int allow_html) { char buf[512], tmp[80], *ptr; char from_host[50] = ""; int index = 0, n, len, quote = 1, infont = 0, inblink = 0, inbold = 0, inunderline = 0, signature = 0; // lap int line = 0; int display = 1; printf ("
\n");
  // lap

  while (fgets (buf, 512, fp) != NULL)
    {
      index = 0;
      display = 1;
      if (quote == 1 && quote_string (buf) == 1)
	{
	  printf ("%s", buf);
	  quote = 0;
	}
      else
	{
	  if (strcmp (buf, "--\n") == 0)
          {
	    signature++;
            index += 2;
            printf("");
            if (signature==1) printf("
"); } if (signature>0) { if (strstr(buf, "※ 發信站: ")==0) { display=0; } if (strstr(buf, "◆ From: ")==0) { display=0; strcpy(from_host, &buf[10]); } } if (buf[0] == ':' || buf[0] == '>') printf (""); if (display==1) while (buf[index] != '\0') { if (buf[index] != '\x1b' || buf[index + 1] != '[') { if (allow_html == 0 && signature == 0 && buf[index] == '<') printf ("<"); else if (allow_html == 0 && signature == 0 && buf[index] == '>') printf (">"); else { if (line>2) printf ("%c", buf[index]); if (buf[index] == '\n') { line++; } } index++; } else { index += 2; n = 0; while (buf[index + n] != 'm' && buf[index + n] != '\0') n++; if (buf[index + n] == 'm') { len = (n > 79) ? 79 : (n + 1); strncpy (tmp, buf + index, (size_t) len); tmp[len] = '\0'; index += n + 1; if (tmp[0] == 'm') strcpy (tmp, "0;"); else tmp[len - 1] = ';'; ptr = strtok (tmp, ";"); while (ptr) { n = atoi (ptr); switch (n) { case 0: if (infont == 1) /* 狀態還原 */ printf (""); if (inblink == 1) printf (""); if (inbold == 1) printf (""); if (inunderline == 1) printf (""); infont = inblink = inbold = inunderline = 0; break; case 1: if (inbold == 0) /* 高亮度 */ printf (""); inbold = 1; break; case 4: if (inunderline == 0) /* 下划線 */ printf (""); inunderline = 1; break; case 5: if (inblink == 0) /* 閃爍 */ printf (""); inblink = 1; break; case 30: if (infont == 1) /* 黑色 */ printf ("
"); printf (""); infont = 1; break; case 31: if (infont == 1) /* 紅色 */ printf (""); printf (""); infont = 1; break; case 32: if (infont == 1) /* 綠色 */ printf (""); printf (""); infont = 1; break; case 33: if (infont == 1) /* 黃色 */ printf (""); printf (""); infont = 1; break; case 34: if (infont == 1) /* 藍色 */ printf (""); printf (""); infont = 1; break; case 35: if (infont == 1) /* 粉紅色 */ printf (""); printf (""); infont = 1; break; case 36: if (infont == 1) /* 淺藍色 */ printf (""); printf (""); infont = 1; break; case 37: if (infont == 1) /* 白色 */ printf (""); printf (""); infont = 1; break; } ptr = strtok (NULL, ";"); } } else { index += n; } } } if (buf[0] == ':' || buf[0] == '>') printf (""); } } if (infont == 1) printf (""); if (inblink == 1) printf (""); if (inbold == 1) printf (""); if (inunderline == 1) printf (""); printf ("from hkday.net
\n"); } static int web_article_cb(db_article_t const *article, void *datum) { db_article_t *save = datum; db_Article_InitAssign(save, article); return 0; } void table_article(db_database_t *db, FILE * FP, db_article_t * article, int from) { struct tm *posttime; char date_str[16]; int uid; userec_t user; db_board_t *board = *(&article->board); uid = db_Database_FindUser(db, &article->header.owner); if (uid > -1) db_Database_ReadUserRecord(uid, &user ); posttime = localtime(&article->header.date); snprintf(date_str, sizeof date_str, "%02d/%02d/%04d", posttime->tm_mday, posttime->tm_mon+1, posttime->tm_year+1900); printf (""); printf ("
"); printf (""), printf (""); printf (""); printf ("
"); if (uid > -1) printf ("", user.sex); printf (&article->header.owner); printf ("
"); if (uid > -1) { printf ("

%s", user.username); printf ("
Login: %d", user.numlogins); printf ("
Posts: %d", user.numposts); printf ("
"); printf ("
From: %s", user.lasthost); } printf ("
"); printf (&article->header.title); printf ("
"); printf ("posted on %s", date_str); printf ("
"); /* printf ("\n", ProgramName, board->boardid, from); printf ("");*/ printf ("\n", ProgramName, board->boardid, from, from); printf (""); printf ("
"); display_article(FP, 0); printf("
"); } void web_article_show(db_database_t *db, int bid, int from) { db_board_t board; db_article_t article; const char *filename; struct tm *posttime; char date_str[16]; FILE *file; db_Database_SetCurrentState(db, READING); if (db_Board_InitFromID(&board, db, bid)) { web_error_message("找不到文章所屬看板"); return; } article.board = NULL; if (db_Board_List(&board, from, from, web_article_cb, &article) || article.board == NULL) { web_error_message("找不到文章"); return; } filename = db_Article_Filename(&article); if ((file = fopen(filename, "r")) == NULL) { printf("找不到文章所屬檔案"); return; } web_http_header(""); printf( "CONTENT-TYPE: text/html\r\n" "\r\n" "閱\讀文章" /* ""*/ ""); table_article(db, file, &article, from); fclose(file); printf("回到看板\n", ProgramName, bid, from); printf("回應\n", ProgramName, bid, from, from); if (from > 1) printf("上一篇\n", ProgramName, bid, from-1); if (from < db_Board_Number(article.board)) printf("下一篇\n", ProgramName, bid, from+1); printf("
"); // web_mainmenu_body(stdout, NULL); printf(""); printf("\r\n"); } /*********************************************************************/ /* post an article */ struct postedit_userdata { db_board_t *board; int quote; }; static void web_postedit_quote(void *_outfile, void *_userdata) { FILE *const outfile = _outfile; struct postedit_userdata *const userdata = _userdata; db_article_t article; const char *filename; FILE *quotefile; char buf[512], *ptr; if (userdata->quote > 0) { article.board = NULL; if (db_Board_List( userdata->board, userdata->quote, userdata->quote, web_article_cb, &article) || article.board == NULL) { return; } filename = db_Article_Filename(&article); quotefile = fopen(filename, "r"); if (quotefile == NULL) { fprintf(outfile, "錯誤:找不到文章所屬檔案\n"); return; } /* get author's name from the first line */ fgets(buf, sizeof buf, quotefile); if ((ptr = strrchr(buf, ')'))) ptr[1] = '\0'; else if ((ptr = strrchr(buf, '\n'))) ptr[0] = '\0'; if ((ptr = strchr(buf, ':'))) { while (*(++ptr) == ' '); } else { ptr = article.header.owner; } web_PrintString(outfile, "※ 引述《"); web_PrintString(outfile, ptr); web_PrintString(outfile, "》之銘言:\n"); /* remove article header */ while (fgets(buf, sizeof buf, quotefile) && buf[0] != '\n') ; while (fgets(buf, sizeof buf, quotefile) != NULL) { if (buf[0] == '-' && buf[1] == '-') break; if (buf[0] != '\n' && strncmp(buf, ": : ", 4) != 0) { printf(": "); web_PrintString(outfile, buf); } } fclose(quotefile); } } static void web_postedit_title(void *_outfile, void *_userdata) { FILE *const outfile = _outfile; struct postedit_userdata *const userdata = _userdata; db_article_t article; char const *title; if (userdata->quote > 0) { article.board = NULL; if (db_Board_List( userdata->board, userdata->quote, userdata->quote, web_article_cb, &article) || article.board == NULL) { return; } title = article.header.title; if (strncmp(title, "Re: ", 4) != 0) web_PrintString(outfile, "Re: "); web_PrintString(outfile, title); } } void web_postedit_show(db_board_t *board, int from, int pagesize, int quote) { db_database_t const *db = board->database; if (db->userinfo == NULL) { web_http_header(""); web_loginpage(&db); } else { char *userid = db->userinfo->userid; char *username = db->userinfo->username; struct web_replace_t replace[] = { { 'P', web_replace_STRING, ProgramName }, { 'M', web_replace_FUNCTION, web_mainmenu_body }, { 'U', web_replace_STRING, userid }, { 'N', web_replace_STRING, username }, { 'b', web_replace_UINT, (void *)&board->boardid }, { 'e', web_replace_STRING, db_Board_Name(board) }, { 'c', web_replace_STRING, db_Board_Title(board) }, { 'm', web_replace_STRING, db_Board_BoardMasters(board) }, { 'f', web_replace_UINT, &from }, { 'q', web_replace_FUNCTION, web_postedit_quote }, { 't', web_replace_FUNCTION, web_postedit_title } }; struct postedit_userdata data = { board, quote }; if (!db_Board_PostPerm(board)) { web_http_header(""); web_ReplaceOutput(web_string->post.deny, replace_escape, replace, ARRAY_LENGTH(replace), web_replace_write, stdout, &data); } else { web_http_header(""); web_ReplaceOutput(web_string->post.edit, replace_escape, replace, ARRAY_LENGTH(replace), web_replace_write, stdout, &data); } } } void web_post_show(db_board_t *board, int from, int pagesize, int signature) { char *title, *context = NULL; int i, j; if (web_GetQueryAllocate("title", &title)) { web_error_message("Error: No title is found."); return; } if (web_GetQueryAllocate("context", &context)) { web_error_message("Error: No text is found."); goto exit1; } /* remove '\r' from input */ i = j = 0; for (;;) { if (context[i] != '\r') { context[j] = context[i]; ++j; } if (context[i] == '\0') break; ++i; } if (db_Board_Post(board, title, context, signature, 0)) { web_error_message("Error: Fail to post article."); goto exit2; } web_http_header(""); { struct web_replace_t replace[] = { { 'P', web_replace_STRING, ProgramName }, { 'M', web_replace_FUNCTION, web_mainmenu_body }, { 't', web_replace_STRING, title }, { 'x', web_replace_STRING, context }, { 'b', web_replace_UINT, (void *)&board->boardid }, { 'e', web_replace_STRING, db_Board_Name(board) }, { 'c', web_replace_STRING, db_Board_Title(board) }, { 'm', web_replace_STRING, db_Board_BoardMasters(board) }, { 'n', web_replace_UINT, &from } }; web_ReplaceOutput(web_string->post.success, replace_escape, replace, ARRAY_LENGTH(replace), web_replace_write, stdout, NULL); } exit2: free(context); exit1: free(title); } /*********************************************************************/ void web_newaccount_show(void) { unsigned int I = IDLEN+2, W = PASSLEN+2; struct web_replace_t replace[] = { { 'P', web_replace_STRING, ProgramName }, { 'I', web_replace_UINT, &I }, { 'W', web_replace_UINT, &W } }; web_http_header(""); web_ReplaceOutput(web_string->reg.request, replace_escape, replace, ARRAY_LENGTH(replace), web_replace_write, stdout, NULL); } int db_register_account(char *user, char *passwd) { /* return 0 if success; otherwise, return an error code */ return 1; } void web_register_show(void) { char *user, *passwd; int result; web_GetQueryAllocate("user", &user); web_GetQueryAllocate("passwd", &passwd); result = db_register_account(user, passwd); free(user); free(passwd); if (result == 0) { web_http_header(""); fputs(web_string->reg.success, stdout); } else { web_http_header(""); fputs(web_string->reg.failure, stdout); } } /*********************************************************************/ int main_board_init(db_board_t *board, db_database_t *db) { int bid; char buf[32]; bid = -1; if (!web_GetQuery("bid", buf, sizeof buf)) bid = atoi(buf); if (db_Board_InitFromID(board, db, bid)) { web_error_message("db_Board_InitFromID() error\n"); return 1; } else { return 0; } } void main_board(db_database_t *db, int bid, int from, int pagesize) { db_board_t board; if (db_Board_InitFromID(&board, db, bid)) { web_error_message("db_Board_InitFromID() error"); } else if (board.header->level != 0) { web_error_message("Incorrect board ID"); } else if (db_Board_isSublist(&board)) { web_boardlist_sublist(db, &board, pagesize); } else { web_board_show(db, &board, from, pagesize); } } main_board_thread(db_database_t *db, int bid, int pagesize, char* title) { db_board_t board; if (db_Board_InitFromID(&board, db, bid)) { web_error_message("db_Board_InitFromID() error"); } else if (board.header->level != 0) { web_error_message("Incorrect board ID"); } else if (db_Board_isSublist(&board)) { web_boardlist_sublist(db, &board, pagesize); } else { web_thread_show(db, &board, pagesize, title); } } main_board_threadlist(db_database_t *db, int bid, int pagesize, char* title) { db_board_t board; if (db_Board_InitFromID(&board, db, bid)) { web_error_message("db_Board_InitFromID() error"); } else if (board.header->level != 0) { web_error_message("Incorrect board ID"); } else if (db_Board_isSublist(&board)) { web_boardlist_sublist(db, &board, pagesize); } else { web_threadlist_show(db, &board, pagesize, title); } } int main_loop(void) { db_database_t db; db_board_t board; char buf[128]; enum web_state_t state; int session, style, bid, from, quote, pagesize, signature; char *user, *passwd, *title; db_Database_Init(&db); style = 0; if (!web_GetCookie("style", buf, sizeof buf)) style = atoi(buf); if (style < 0 || style >= web_NumOfStyle) style = 0; web_string = web_strings + style; state = web_BOARDROOT; if (!web_GetQuery("state", buf, sizeof buf)) state = atoi(buf); if (state==web_LOGIN) { web_GetQueryAllocate("user", &user); web_GetQueryAllocate("passwd", &passwd); web_login(&db, user, passwd); free(user); free(passwd); web_boardlist_root(&db, pagesize); web_loginpage(&db); db_Database_Done(&db); web_ClearCGI(); return 0; } session = -1; if (!web_GetCookie("session", buf, sizeof buf)) { unsigned int random; if (strlen(buf) != 16) { web_reloginpage(); return 0; } sscanf(buf, "%8X%8X", &session, &random); if (db_Database_LoginSession(&db, session)) { // web_reloginpage(); return 0; } if (db.userinfo->random != random) { // web_reloginpage(); return 0; } else db_Database_ClearIdleTime(&db); } pagesize = 20; if (!web_GetCookie("pagesize", buf, sizeof buf)) { pagesize = atoi(buf); if (pagesize < 10 || pagesize > 500) pagesize = 20; } switch (state) { case web_LOGINPAGE: web_loginpage(&db); break; case web_LOGOUT: web_logout(&db); web_boardlist_root(&db, pagesize); web_loginpage(&db); break; case web_SETTING: web_setting_page(); break; case web_MAINMENU: web_mainmenu(&db, web_string); break; case web_BOARDROOT: web_http_header(""); web_boardlist_root(&db, pagesize); web_loginpage(&db); break; case web_BOARD: bid = -1; if (!web_GetQuery("bid", buf, sizeof buf)) bid = atoi(buf); from = 0; if (!web_GetQuery("from", buf, sizeof buf)) from = atoi(buf); main_board(&db, bid, from, pagesize); web_loginpage(&db); break; case web_THREADLIST: bid = -1; if (!web_GetQuery("bid", buf, sizeof buf)) bid = atoi(buf); from = 0; if (!web_GetQuery("from", buf, sizeof buf)) from = atoi(buf); main_board_threadlist(&db, bid, from, pagesize); web_loginpage(&db); break; case web_THREAD: bid = -1; if (!web_GetQuery("bid", buf, sizeof buf)) bid = atoi(buf); web_GetQueryAllocate("title", &title); strcpy(buf, title); main_board_thread(&db, bid, pagesize, buf); free(title); web_loginpage(&db); break; case web_ARTICLE: bid = -1; if (!web_GetQuery("bid", buf, sizeof buf)) bid = atoi(buf); from = 0; if (!web_GetQuery("from", buf, sizeof buf)) from = atoi(buf); web_article_show(&db, bid, from); break; case web_POSTEDIT: if (db.userinfo == NULL) { web_error_message("你必須先登入才能夠貼文"); } else { from = 0; if (!web_GetQuery("from", buf, sizeof buf)) from = atoi(buf); quote = -1; if (!web_GetQuery("quote", buf, sizeof buf)) quote = atoi(buf); if (!main_board_init(&board, &db)) web_postedit_show(&board, from, pagesize, quote); } break; case web_POST: from = 0; if (!web_GetQuery("from", buf, sizeof buf)) from = atoi(buf); signature = -1; if (!web_GetQuery("signature", buf, sizeof buf)) signature = atoi(buf); if (!main_board_init(&board, &db)) web_post_show(&board, from, pagesize, signature); break; case web_NEWACCOUNT: web_newaccount_show(); break; default: web_error_message("Incorrect state"); } db_Database_Done(&db); web_ClearCGI(); return 0; } int main(int argc, char **argv) { static db_database_t db; FILE *file; unsigned int seed; if (read_strings()) { web_error_message("String initialize error"); return EXIT_FAILURE; } /* dynamic detection of program name is disabled if ((ProgramName = strdup(basename(argv[0]))) == NULL) { web_error_message("Program initialize error"); return EXIT_FAILURE; } */ seed = time(NULL); file = fopen("/dev/random", "r"); if (file != NULL) { fread(&seed, sizeof seed, 1, file); fclose(file); } srand(seed); if (db_Database_Init(&db)) { web_error_message("Cannot attach shared memory"); return EXIT_FAILURE; } while (FCGI_Accept() >= 0) { if (main_loop()) break; } db_Database_Done(&db); /* dynamic detection of program name is disabled free(ProgramName); */ return EXIT_SUCCESS; }