/* $Id: board.c,v 1.12 2005/08/17 14:36:09 bbs Exp $ */ #include "board.h" #include #include #include #include #include #include #include #include const char * db_Article_Filename(const db_article_t *article) { static char filename[256]; snprintf(filename, sizeof filename, BBSHOME "/boards/%c/%s/%s", article->board->header->brdname[0], article->board->header->brdname, article->header.filename); return filename; } int db_Board_InitFromID(db_board_t *board, db_database_t *db, unsigned id) { if (db==NULL || db->SHM==NULL || !db->SHM->loaded) return 1; if (id>=MAX_BOARD || !db->SHM->bcache[id].brdname[0]) return 1; board->database = db; board->boardid = id; board->header = db->SHM->bcache + id; return 0; } static void Board_File(db_board_t const *board, char const *file, char *buf, size_t bufsize) { char const *name; name = db_Board_Name(board); snprintf(buf, bufsize, BBSHOME "/boards/%c/%s/%s", name[0], name, file); } static void Board_DIR(db_board_t const *board, char *buf, size_t bufsize) { Board_File(board, ".DIR", buf, bufsize); } static void User_File(db_database_t const *db, char const *file, char *buf, size_t bufsize) { char const *name; name = db->userinfo->userid; snprintf(buf, bufsize, BBSHOME "/home/%c/%s/%s", name[0], name, file); } int db_Board_Number(db_board_t const *board) { struct stat sb; char buf[128]; Board_DIR(board, buf, sizeof buf); if (stat(buf, &sb)) return -1; return sb.st_size / sizeof (fileheader_t); } int db_Board_List(db_board_t const *board, unsigned int from, unsigned int to, int (*callback)(db_article_t const *article, void *datum), void *datum) { unsigned int i; int fd; char buf[128]; db_article_t article; if (db_Board_Attribute(board, BRD_GROUPBOARD)) return 1; Board_DIR(board, buf, sizeof buf); fd = open(buf, O_RDONLY); if (fd == -1) return 2; i = (from>0 ? from : 1); if (lseek(fd, ((i - 1) * sizeof (fileheader_t)), SEEK_SET) == -1) { close(fd); return 2; } article.database = board->database; article.board = board; for (; !to || i<=to; ++i) { ssize_t byteread; byteread = read(fd, &article.header, sizeof article.header); if (!byteread) break; if (byteread != sizeof article.header) { close(fd); return 2; } if (callback(&article, datum)) break;; } close(fd); return 0; } static char const * Cdate(time_t *now) { static char const *wday[] = { "星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六" }; static char buf[80]; struct tm *tmp_tm; tmp_tm = localtime(now); snprintf(buf, sizeof buf, "%d年%d月%d日 %s %02d:%02d:%02d", tmp_tm->tm_year+1900, tmp_tm->tm_mon+1, tmp_tm->tm_mday, wday[tmp_tm->tm_wday], tmp_tm->tm_hour, tmp_tm->tm_min, (tmp_tm->tm_sec)%60); return buf; } static int belong(char *filelist, char *key) /* 撿查名單檔案中是否有該項 */ { FILE *fp; int rc = 0; if ((fp = fopen(filelist, "r"))) { char buf[STRLEN], *ptr; while (fgets(buf, STRLEN, fp)) { if ((ptr = strtok(buf, " \t\n\r")) && !strcasecmp(ptr, key)) { rc = 1; break; } } fclose(fp); } return rc; } int db_Board_PostPerm(db_board_t const *board) { char *board_name, *user_id; int board_id, user_level, board_level; char buf[256]; board_name = db_Board_Name(board); if (board->database->userinfo != NULL) { user_id = board->database->userinfo->userid; user_level = board->database->userinfo->userlevel; } else { user_id = "guest"; user_level = 0; } Board_File(board, "water", buf, sizeof buf); if (belong(buf, user_id)) return 0; /* 永遠容許貼文到SYSOP板?無聊,刪! if (!strcasecmp(bname, DEFAULT_BOARD)) return 1; */ /* 無聊至極,刪! if (!strcasecmp(bname, "PttLaw")) return 1; */ if (!(user_level & PERM_POST)) return 0; board_id = db_Board_ID(board); /* 秘密看板特別處理 */ if (board->database->SHM->bcache[board_id].brdattr & BRD_HIDE) return 1; board_level = board->database->SHM->bcache[board_id].level; if (user_level & PERM_VIOLATELAW) { if (board_level & PERM_VIOLATELAW) return 1; else if (user_level & PERM_VIOLATELAW) return 0; } if (board_level & ~PERM_POST) return user_level & (board_level & ~PERM_POST); else return 1; } int db_Board_Post(db_board_t *board, const char *title, const char *context, int signature, int local) { char fullpath[256], header[4096]; int fd, repeat, numofpost, numofsign, signfile, length; char ch; struct stat st; userinfo_t *uinfo; fileheader_t postfile; time_t now; struct { char author[IDLEN + 1]; char board[IDLEN + 1]; char title[66]; time_t date; /* last post time */ int number; /* post number */ } postlog; uinfo = board->database->userinfo; if (uinfo == NULL) /* not logged in */ return 1; /* bug: not working ??? */ if (!db_Board_PostPerm(board)) return 2; repeat = 0; for (;;) { sprintf(postfile.filename, "M.%lu.A.%3.3X", time(&now), repeat++); Board_File(board, postfile.filename, fullpath, sizeof fullpath); fd = open(fullpath, O_WRONLY | O_CREAT | O_EXCL, 0644); if (fd != -1) break; if (errno != EEXIST) return 3; } snprintf(header, sizeof header, "作者: %s (%s) %s: %s\n標題: %s\n時間: %s\n\n", uinfo->userid, uinfo->username, local ? "站內" : "看板", db_Board_Name(board), title, Cdate(&now)); write(fd, header, strlen(header)); write(fd, context, strlen(context)); if (signature != 0) { /* find the number of signatures */ User_File(board->database, "sig.0", fullpath, sizeof fullpath); length = strlen(fullpath); numofsign = 0; for (ch='1'; ch<='9'; ++ch) { fullpath[length-1] = ch; if (!access(fullpath, R_OK)) ++numofsign; } if (numofsign > 0) { /* random signature */ if (signature < 0 || signature > 9) signature = rand() % numofsign + 1; /* copy signature file */ fullpath[length-1] = signature + '0'; signfile = open(fullpath, O_RDONLY); if (fd != -1) { write(fd, "\n--\n", 4); for (;;) { repeat = read(signfile, header, sizeof header); if (repeat <= 0) break; write(fd, header, repeat); } } } } snprintf(header, sizeof header, "\n--\n※ 發信站: %s(%s)\n◆ From: %s\n", BBSNAME, MYHOSTNAME, uinfo->from ); write(fd, header, strlen(header)); close(fd); postfile.recommend = 0; strlcpy(postfile.owner, uinfo->userid, sizeof postfile.owner); strlcpy(postfile.title, title, sizeof postfile.title); postfile.date = now; postfile.money = 0; postfile.filemode = local ? FILE_LOCAL : 0; Board_DIR(board, fullpath, sizeof fullpath); fd = open(fullpath, O_WRONLY | O_APPEND | O_CREAT | O_EXLOCK); if (fd == -1) return 4; write(fd, &postfile, sizeof postfile); fstat(fd, &st); numofpost = st.st_size / sizeof(fileheader_t); board->database->SHM->total[db_Board_ID(board)] = numofpost; if (numofpost > 0) { board->database->SHM->lastposttime[db_Board_ID(board)] = now; } else board->database->SHM->lastposttime[db_Board_ID(board)] = 0; flock(fd, LOCK_UN); close(fd); #if DIRCACHESIZE if (numofpost) { *(int *)&board->database->SHM->dircache[db_Board_ID(board)][0] .filename[0] = 0; } #endif strlcpy(postlog.author, postfile.owner, sizeof postlog.author); strlcpy(postlog.board, db_Board_Name(board), sizeof postlog.board); strlcpy(postlog.title, title, sizeof postlog.title); postlog.date = now; postlog.number = 1; fd = open(BBSHOME ".post", O_WRONLY | O_CREAT | O_EXLOCK, 0644); if (fd != -1) { write(fd, &postlog, sizeof postlog); flock(fd, LOCK_UN); close(fd); } return 0; } int db_Board_Article(db_board_t const *board, unsigned int index, db_article_t *article) { unsigned int i; int fd; char buf[128]; ssize_t byteread; if (db_Board_Attribute(board, BRD_GROUPBOARD)) return 1; Board_DIR(board, buf, sizeof buf); fd = open(buf, O_RDONLY); if (fd == -1) return 2; i = (index>0 ? index : 1); if (lseek(fd, ((i - 1) * sizeof (fileheader_t)), SEEK_SET) == -1) { close(fd); return 2; } article->database = board->database; article->board = board; byteread = read(fd, &article->header, sizeof article->header); if (!byteread) return 2; if (byteread != sizeof article->header) { close(fd); return 2; } close(fd); return 0; }