summaryrefslogtreecommitdiffstats
path: root/console/cache.c
diff options
context:
space:
mode:
Diffstat (limited to 'console/cache.c')
-rw-r--r--console/cache.c1215
1 files changed, 1215 insertions, 0 deletions
diff --git a/console/cache.c b/console/cache.c
new file mode 100644
index 00000000..5a30c24f
--- /dev/null
+++ b/console/cache.c
@@ -0,0 +1,1215 @@
+/* $Id$ */
+#include "bbs.h"
+
+#ifdef _BBS_UTIL_C_
+# define log_usies(a, b) ;
+# define abort_bbs(a) exit(1)
+#endif
+/*
+ * the reason for "safe_sleep" is that we may call sleep during SIGALRM
+ * handler routine, while SIGALRM is blocked. if we use the original sleep,
+ * we'll never wake up.
+ */
+unsigned int
+safe_sleep(unsigned int seconds)
+{
+ /* jochang sleep有問題時用 */
+ sigset_t set, oldset;
+
+ sigemptyset(&set);
+ sigprocmask(SIG_BLOCK, &set, &oldset);
+ if (sigismember(&oldset, SIGALRM)) {
+ unsigned int retv;
+ log_usies("SAFE_SLEEP ", "avoid hang");
+ sigemptyset(&set);
+ sigaddset(&set, SIGALRM);
+ sigprocmask(SIG_UNBLOCK, &set, NULL);
+ retv = sleep(seconds);
+ sigprocmask(SIG_BLOCK, &set, NULL);
+ return retv;
+ }
+ return sleep(seconds);
+}
+
+/*
+ * section - SHM
+ */
+static void
+attach_err(int shmkey, const char *name)
+{
+ fprintf(stderr, "[%s error] key = %x\n", name, shmkey);
+ fprintf(stderr, "errno = %d: %s\n", errno, strerror(errno));
+ exit(1);
+}
+
+void *
+attach_shm(int shmkey, int shmsize)
+{
+ void *shmptr = (void *)NULL;
+ int shmid;
+
+ shmid = shmget(shmkey, shmsize,
+#ifdef USE_HUGETLB
+ SHM_HUGETLB |
+#endif
+ 0);
+ if (shmid < 0) {
+ // SHM should be created by uhash_loader, NOT mbbsd or other utils
+ attach_err(shmkey, "shmget");
+ } else {
+ shmptr = (void *)shmat(shmid, NULL, 0);
+ if (shmptr == (void *)-1)
+ attach_err(shmkey, "shmat");
+ }
+
+ return shmptr;
+}
+
+void
+attach_SHM(void)
+{
+ SHM = attach_shm(SHM_KEY, SHMSIZE);
+ if(SHM->version != SHM_VERSION) {
+ fprintf(stderr, "Error: SHM->version(%d) != SHM_VERSION(%d)\n", SHM->version, SHM_VERSION);
+ fprintf(stderr, "Please use the source code version corresponding to SHM,\n"
+ "or use ipcrm(1) command to clean share memory.\n");
+ exit(1);
+ }
+ if (!SHM->loaded) /* (uhash) assume fresh shared memory is
+ * zeroed */
+ exit(1);
+ if (SHM->Btouchtime == 0)
+ SHM->Btouchtime = 1;
+ bcache = SHM->bcache;
+ numboards = SHM->Bnumber;
+
+ if (SHM->Ptouchtime == 0)
+ SHM->Ptouchtime = 1;
+
+ if (SHM->Ftouchtime == 0)
+ SHM->Ftouchtime = 1;
+}
+
+/* ----------------------------------------------------- */
+/* semaphore : for critical section */
+/* ----------------------------------------------------- */
+#define SEM_FLG 0600 /* semaphore mode */
+
+#ifndef __FreeBSD__
+/* according to X/OPEN, we have to define it ourselves */
+union semun {
+ int val; /* value for SETVAL */
+ struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */
+ unsigned short int *array; /* array for GETALL, SETALL */
+ struct seminfo *__buf; /* buffer for IPC_INFO */
+};
+#endif
+
+void
+sem_init(int semkey, int *semid)
+{
+ union semun s;
+
+ s.val = 1;
+ *semid = semget(semkey, 1, 0);
+ if (*semid == -1) {
+ *semid = semget(semkey, 1, IPC_CREAT | SEM_FLG);
+ if (*semid == -1)
+ attach_err(semkey, "semget");
+ semctl(*semid, 0, SETVAL, s);
+ }
+}
+
+void
+sem_lock(int op, int semid)
+{
+ struct sembuf sops;
+
+ sops.sem_num = 0;
+ sops.sem_flg = SEM_UNDO;
+ sops.sem_op = op;
+ if (semop(semid, &sops, 1)) {
+ perror("semop");
+ exit(1);
+ }
+}
+
+/*
+ * section - user cache(including uhash)
+ */
+/* uhash ****************************************** */
+/*
+ * the design is this: we use another stand-alone program to create and load
+ * data into the hash. (that program could be run in rc-scripts or something
+ * like that) after loading completes, the stand-alone program sets loaded to
+ * 1 and exits.
+ *
+ * the bbs exits if it can't attach to the shared memory or the hash is not
+ * loaded yet.
+ */
+
+void
+add_to_uhash(int n, const char *id)
+{
+ int *p, h = StringHash(id)%(1<<HASH_BITS);
+ int times;
+ strlcpy(SHM->userid[n], id, sizeof(SHM->userid[n]));
+
+ p = &(SHM->hash_head[h]);
+
+ for (times = 0; times < MAX_USERS && *p != -1; ++times)
+ p = &(SHM->next_in_hash[*p]);
+
+ if (times == MAX_USERS)
+ abort_bbs(0);
+
+ SHM->next_in_hash[*p = n] = -1;
+}
+
+void
+remove_from_uhash(int n)
+{
+/*
+ * note: after remove_from_uhash(), you should add_to_uhash() (likely with a
+ * different name)
+ */
+ int h = StringHash(SHM->userid[n])%(1<<HASH_BITS);
+ int *p = &(SHM->hash_head[h]);
+ int times;
+
+ for (times = 0; times < MAX_USERS && (*p != -1 && *p != n); ++times)
+ p = &(SHM->next_in_hash[*p]);
+
+ if (times == MAX_USERS)
+ abort_bbs(0);
+
+ if (*p == n)
+ *p = SHM->next_in_hash[n];
+}
+
+#if (1<<HASH_BITS)*10 < MAX_USERS
+#warning "Suggest to use bigger HASH_BITS for better searchuser() performance,"
+#warning "searchuser() average chaining MAX_USERS/(1<<HASH_BITS) times."
+#endif
+int
+dosearchuser(const char *userid, char *rightid)
+{
+ int h, p, times;
+ STATINC(STAT_SEARCHUSER);
+ h = StringHash(userid)%(1<<HASH_BITS);
+ p = SHM->hash_head[h];
+
+ for (times = 0; times < MAX_USERS && p != -1 && p < MAX_USERS ; ++times) {
+ if (strcasecmp(SHM->userid[p], userid) == 0) {
+ if(userid[0] && rightid) strcpy(rightid, SHM->userid[p]);
+ return p + 1;
+ }
+ p = SHM->next_in_hash[p];
+ }
+
+ return 0;
+}
+
+int
+searchuser(const char *userid, char *rightid)
+{
+ if(userid[0]=='\0')
+ return 0;
+ return dosearchuser(userid, rightid);
+}
+
+int
+getuser(const char *userid, userec_t *xuser)
+{
+ int uid;
+
+ if ((uid = searchuser(userid, NULL))) {
+ passwd_query(uid, xuser);
+ xuser->money = moneyof(uid);
+ }
+ return uid;
+}
+
+char *
+getuserid(int num)
+{
+ if (--num >= 0 && num < MAX_USERS)
+ return ((char *)SHM->userid[num]);
+ return NULL;
+}
+
+void
+setuserid(int num, const char *userid)
+{
+ if (num > 0 && num <= MAX_USERS) {
+/* Ptt: it may cause problems
+ if (num > SHM->number)
+ SHM->number = num;
+ else
+*/
+ remove_from_uhash(num - 1);
+ add_to_uhash(num - 1, userid);
+ }
+}
+
+#ifndef _BBS_UTIL_C_
+char *
+u_namearray(char buf[][IDLEN + 1], int *pnum, char *tag)
+{
+ register char *ptr, tmp;
+ register int n, total;
+ char tagbuf[STRLEN];
+ int ch, ch2, num;
+
+ if (*tag == '\0') {
+ *pnum = SHM->number;
+ return SHM->userid[0];
+ }
+ for (n = 0; tag[n]; n++)
+ tagbuf[n] = chartoupper(tag[n]);
+ tagbuf[n] = '\0';
+ ch = tagbuf[0];
+ ch2 = ch - 'A' + 'a';
+ total = SHM->number;
+ for (n = num = 0; n < total; n++) {
+ ptr = SHM->userid[n];
+ tmp = *ptr;
+ if (tmp == ch || tmp == ch2) {
+ if (chkstr(tag, tagbuf, ptr))
+ strcpy(buf[num++], ptr);
+ }
+ }
+ *pnum = num;
+ return buf[0];
+}
+#endif
+
+void
+getnewutmpent(const userinfo_t * up)
+{
+/* Ptt:這裡加上 hash 觀念找空的 utmp */
+ register int i;
+ register userinfo_t *uentp;
+ unsigned int p = StringHash(up->userid) % USHM_SIZE;
+ for (i = 0; i < USHM_SIZE; i++, p++) {
+ if (p == USHM_SIZE)
+ p = 0;
+ uentp = &(SHM->uinfo[p]);
+ if (!(uentp->pid)) {
+ memcpy(uentp, up, sizeof(userinfo_t));
+ currutmp = uentp;
+ return;
+ }
+ }
+ exit(1);
+}
+
+int
+apply_ulist(int (*fptr) (const userinfo_t *))
+{
+ register userinfo_t *uentp;
+ register int i, state;
+
+ for (i = 0; i < USHM_SIZE; i++) {
+ uentp = &(SHM->uinfo[i]);
+ if (uentp->pid && (PERM_HIDE(currutmp) || !PERM_HIDE(uentp)))
+ if ((state = (*fptr) (uentp)))
+ return state;
+ }
+ return 0;
+}
+
+userinfo_t *
+search_ulist_pid(int pid)
+{
+ register int i = 0, j, start = 0, end = SHM->UTMPnumber - 1;
+ int *ulist;
+ register userinfo_t *u;
+ if (end == -1)
+ return NULL;
+ ulist = SHM->sorted[SHM->currsorted][8];
+ for (i = ((start + end) / 2);; i = (start + end) / 2) {
+ u = &SHM->uinfo[ulist[i]];
+ j = pid - u->pid;
+ if (!j) {
+ return u;
+ }
+ if (end == start) {
+ break;
+ } else if (i == start) {
+ i = end;
+ start = end;
+ } else if (j > 0)
+ start = i;
+ else
+ end = i;
+ }
+ return 0;
+}
+
+userinfo_t *
+search_ulistn(int uid, int unum)
+{
+ register int i = 0, j, start = 0, end = SHM->UTMPnumber - 1;
+ int *ulist;
+ register userinfo_t *u;
+ if (end == -1)
+ return NULL;
+ ulist = SHM->sorted[SHM->currsorted][7];
+ for (i = ((start + end) / 2);; i = (start + end) / 2) {
+ u = &SHM->uinfo[ulist[i]];
+ j = uid - u->uid;
+ if (j == 0) {
+ for (; i > 0 && uid == SHM->uinfo[ulist[i - 1]].uid; --i)
+ ;/* 指到第一筆 */
+ // piaip Tue Jan 8 09:28:03 CST 2008
+ // many people bugged about that their utmp have invalid
+ // entry on record.
+ // we found them caused by crash process (DEBUGSLEEPING) which
+ // may occupy utmp entries even after process was killed.
+ // because the memory is invalid, it is not safe for those process
+ // to wipe their utmp entry. it should be done by some external
+ // daemon.
+ // however, let's make a little workaround here...
+ for (; unum > 0 && i >= 0 && ulist[i] >= 0 &&
+ SHM->uinfo[ulist[i]].uid == uid; unum--, i++)
+ {
+ if (SHM->uinfo[ulist[i]].mode == DEBUGSLEEPING)
+ unum ++;
+ }
+ if (unum == 0 && i > 0 && ulist[i-1] >= 0 &&
+ SHM->uinfo[ulist[i-1]].uid == uid)
+ return &SHM->uinfo[ulist[i-1]];
+ /*
+ if ( i + unum - 1 >= 0 &&
+ (ulist[i + unum - 1] >= 0 &&
+ uid == SHM->uinfo[ulist[i + unum - 1]].uid ) )
+ return &SHM->uinfo[ulist[i + unum - 1]];
+ */
+ break; /* 超過範圍 */
+ }
+ if (end == start) {
+ break;
+ } else if (i == start) {
+ i = end;
+ start = end;
+ } else if (j > 0)
+ start = i;
+ else
+ end = i;
+ }
+ return 0;
+}
+
+userinfo_t *
+search_ulist_userid(const char *userid)
+{
+ register int i = 0, j, start = 0, end = SHM->UTMPnumber - 1;
+ int *ulist;
+ register userinfo_t * u;
+ if (end == -1)
+ return NULL;
+ ulist = SHM->sorted[SHM->currsorted][0];
+ for (i = ((start + end) / 2);; i = (start + end) / 2) {
+ u = &SHM->uinfo[ulist[i]];
+ j = strcasecmp(userid, u->userid);
+ if (!j) {
+ return u;
+ }
+ if (end == start) {
+ break;
+ } else if (i == start) {
+ i = end;
+ start = end;
+ } else if (j > 0)
+ start = i;
+ else
+ end = i;
+ }
+ return 0;
+}
+
+#ifndef _BBS_UTIL_C_
+int
+count_logins(int uid, int show)
+{
+ register int i = 0, j, start = 0, end = SHM->UTMPnumber - 1, count;
+ int *ulist;
+ userinfo_t *u;
+ if (end == -1)
+ return 0;
+ ulist = SHM->sorted[SHM->currsorted][7];
+ for (i = ((start + end) / 2);; i = (start + end) / 2) {
+ u = &SHM->uinfo[ulist[i]];
+ j = uid - u->uid;
+ if (!j) {
+ for (; i > 0 && uid == SHM->uinfo[ulist[i - 1]].uid; i--);
+ /* 指到第一筆 */
+ for (count = 0; (ulist[i + count] &&
+ (u = &SHM->uinfo[ulist[i + count]]) &&
+ uid == u->uid); count++) {
+ if (show)
+ prints("(%d) 目前狀態為: %-17.16s(來自 %s)\n",
+ count + 1, modestring(u, 0),
+ u->from);
+ }
+ return count;
+ }
+ if (end == start) {
+ break;
+ } else if (i == start) {
+ i = end;
+ start = end;
+ } else if (j > 0)
+ start = i;
+ else
+ end = i;
+ }
+ return 0;
+}
+
+void
+purge_utmp(userinfo_t * uentp)
+{
+ logout_friend_online(uentp);
+ memset(uentp, 0, sizeof(userinfo_t));
+ SHM->UTMPneedsort = 1;
+}
+#endif
+
+/*
+ * section - money cache
+ */
+int
+setumoney(int uid, int money)
+{
+ SHM->money[uid - 1] = money;
+ passwd_update_money(uid);
+ return SHM->money[uid - 1];
+}
+
+int
+deumoney(int uid, int money)
+{
+ if (uid <= 0 || uid > MAX_USERS){
+#if defined(_BBS_UTIL_C_)
+ printf("internal error: deumoney(%d, %d)\n", uid, money);
+#else
+ vmsg("internal error");
+#endif
+ return -1;
+ }
+
+ if (money < 0 && moneyof(uid) < -money)
+ return setumoney(uid, 0);
+ else
+ return setumoney(uid, SHM->money[uid - 1] + money);
+}
+
+/*
+ * section - utmp
+ */
+#if !defined(_BBS_UTIL_C_) /* _BBS_UTIL_C_ 不會有 utmp */
+void
+setutmpmode(unsigned int mode)
+{
+ if (currstat != mode)
+ currutmp->mode = currstat = mode;
+ /* 追蹤使用者 */
+ if (HasUserPerm(PERM_LOGUSER)) {
+ log_user("setutmpmode to %s(%d)\n", modestring(currutmp, 0), mode);
+ }
+}
+
+unsigned int
+getutmpmode(void)
+{
+ if (currutmp)
+ return currutmp->mode;
+ return currstat;
+}
+#endif
+
+/*
+ * section - board cache
+ */
+void touchbtotal(int bid) {
+ assert(0<=bid-1 && bid-1<MAX_BOARD);
+ SHM->total[bid - 1] = 0;
+ SHM->lastposttime[bid - 1] = 0;
+}
+
+
+/**
+ * qsort comparison function - 照板名排序
+ */
+static int
+cmpboardname(const void * i, const void * j)
+{
+ return strcasecmp(bcache[*(int*)i].brdname, bcache[*(int*)j].brdname);
+}
+
+/**
+ * qsort comparison function - 先照群組排序、同一個群組內依板名排
+ */
+static int
+cmpboardclass(const void * i, const void * j)
+{
+ boardheader_t *brd1 = &bcache[*(int*)i], *brd2 = &bcache[*(int*)j];
+ int cmp;
+
+ cmp=strncmp(brd1->title, brd2->title, 4);
+ if(cmp!=0) return cmp;
+ return strcasecmp(brd1->brdname, brd2->brdname);
+}
+
+
+void
+sort_bcache(void)
+{
+ int i;
+ /* critical section 盡量不要呼叫 */
+ /* 只有新增 或移除看板 需要呼叫到 */
+ if(SHM->Bbusystate) {
+ sleep(1);
+ return;
+ }
+ SHM->Bbusystate = 1;
+ for (i = 0; i < SHM->Bnumber; i++) {
+ SHM->bsorted[0][i] = SHM->bsorted[1][i] = i;
+ }
+ qsort(SHM->bsorted[0], SHM->Bnumber, sizeof(int), cmpboardname);
+ qsort(SHM->bsorted[1], SHM->Bnumber, sizeof(int), cmpboardclass);
+
+ for (i = 0; i < SHM->Bnumber; i++) {
+ bcache[i].firstchild[0] = 0;
+ bcache[i].firstchild[1] = 0;
+ }
+ SHM->Bbusystate = 0;
+}
+
+#ifdef _BBS_UTIL_C_
+void
+reload_bcache(void)
+{
+ int i, fd;
+ pid_t pid;
+ for( i = 0 ; i < 10 && SHM->Bbusystate ; ++i ){
+ printf("SHM->Bbusystate is currently locked (value: %d). "
+ "please wait... ", SHM->Bbusystate);
+ sleep(1);
+ }
+
+ SHM->Bbusystate = 1;
+ if ((fd = open(fn_board, O_RDONLY)) > 0) {
+ SHM->Bnumber =
+ read(fd, bcache, MAX_BOARD * sizeof(boardheader_t)) /
+ sizeof(boardheader_t);
+ close(fd);
+ }
+ memset(SHM->lastposttime, 0, MAX_BOARD * sizeof(time4_t));
+ memset(SHM->total, 0, MAX_BOARD * sizeof(int));
+
+ /* 等所有 boards 資料更新後再設定 uptime */
+ SHM->Buptime = SHM->Btouchtime;
+ log_usies("CACHE", "reload bcache");
+ SHM->Bbusystate = 0;
+ sort_bcache();
+
+ printf("load bottom in background");
+ if( (pid = fork()) > 0 )
+ return;
+ setproctitle("loading bottom");
+ for( i = 0 ; i < MAX_BOARD ; ++i )
+ if( SHM->bcache[i].brdname[0] ){
+ char fn[128];
+ int n;
+ sprintf(fn, "boards/%c/%s/" FN_DIR ".bottom",
+ SHM->bcache[i].brdname[0],
+ SHM->bcache[i].brdname);
+ n = get_num_records(fn, sizeof(fileheader_t));
+ if( n > 5 )
+ n = 5;
+ SHM->n_bottom[i] = n;
+ }
+ printf("load bottom done");
+ if( pid == 0 )
+ exit(0);
+ // if pid == -1 should be returned
+}
+
+void resolve_boards(void)
+{
+ while (SHM->Buptime < SHM->Btouchtime) {
+ reload_bcache();
+ }
+ numboards = SHM->Bnumber;
+}
+#endif /* defined(_BBS_UTIL_C_)*/
+
+#if 0
+/* Unused */
+void touch_boards(void)
+{
+ SHM->Btouchtime = COMMON_TIME;
+ numboards = -1;
+ resolve_boards();
+}
+#endif
+
+void addbrd_touchcache(void)
+{
+ SHM->Bnumber++;
+ numboards = SHM->Bnumber;
+ reset_board(numboards);
+ sort_bcache();
+}
+
+void
+reset_board(int bid) /* XXXbid: from 1 */
+{ /* Ptt: 這樣就不用老是touch board了 */
+ int fd;
+ boardheader_t *bhdr;
+
+ if (--bid < 0)
+ return;
+ assert(0<=bid && bid<MAX_BOARD);
+ if (SHM->Bbusystate || COMMON_TIME - SHM->busystate_b[bid] < 10) {
+ safe_sleep(1);
+ } else {
+ SHM->busystate_b[bid] = COMMON_TIME;
+
+ bhdr = bcache;
+ bhdr += bid;
+ if ((fd = open(fn_board, O_RDONLY)) > 0) {
+ lseek(fd, (off_t) (bid * sizeof(boardheader_t)), SEEK_SET);
+ read(fd, bhdr, sizeof(boardheader_t));
+ close(fd);
+ }
+ SHM->busystate_b[bid] = 0;
+
+ buildBMcache(bid + 1); /* XXXbid */
+ }
+}
+
+#ifndef _BBS_UTIL_C_ /* because of HasBoardPerm() in board.c */
+int
+apply_boards(int (*func) (boardheader_t *))
+{
+ register int i;
+ register boardheader_t *bhdr;
+
+ for (i = 0, bhdr = bcache; i < numboards; i++, bhdr++) {
+ if (!(bhdr->brdattr & BRD_GROUPBOARD) && HasBoardPerm(bhdr) &&
+ (*func) (bhdr) == QUIT)
+ return QUIT;
+ }
+ return 0;
+}
+#endif
+
+void
+setbottomtotal(int bid)
+{
+ boardheader_t *bh = getbcache(bid);
+ char fname[PATHLEN];
+ int n;
+
+ assert(0<=bid-1 && bid-1<MAX_BOARD);
+ if(!bh->brdname[0]) return;
+ setbfile(fname, bh->brdname, FN_DIR ".bottom");
+ n = get_num_records(fname, sizeof(fileheader_t));
+ if(n>5)
+ {
+#ifdef DEBUG_BOTTOM
+ log_file("fix_bottom", LOG_CREAT | LOG_VF, "%s n:%d\n", fname, n);
+#endif
+ unlink(fname);
+ SHM->n_bottom[bid-1]=0;
+ }
+ else
+ SHM->n_bottom[bid-1]=n;
+}
+void
+setbtotal(int bid)
+{
+ boardheader_t *bh = getbcache(bid);
+ struct stat st;
+ char genbuf[256];
+ int num, fd;
+
+ assert(0<=bid-1 && bid-1<MAX_BOARD);
+ setbfile(genbuf, bh->brdname, FN_DIR);
+ if ((fd = open(genbuf, O_RDWR)) < 0)
+ return; /* .DIR掛了 */
+ fstat(fd, &st);
+ num = st.st_size / sizeof(fileheader_t);
+ assert(0<=bid-1 && bid-1<MAX_BOARD);
+ SHM->total[bid - 1] = num;
+
+ if (num > 0) {
+ lseek(fd, (off_t) (num - 1) * sizeof(fileheader_t), SEEK_SET);
+ if (read(fd, genbuf, FNLEN) >= 0) {
+ SHM->lastposttime[bid - 1] = (time4_t) atoi(&genbuf[2]);
+ }
+ } else
+ SHM->lastposttime[bid - 1] = 0;
+ close(fd);
+}
+
+void
+touchbpostnum(int bid, int delta)
+{
+ int *total = &SHM->total[bid - 1];
+ assert(0<=bid-1 && bid-1<MAX_BOARD);
+ if (*total)
+ *total += delta;
+}
+
+int
+getbnum(const char *bname)
+{
+ register int i = 0, j, start = 0, end = SHM->Bnumber - 1;
+ int *blist = SHM->bsorted[0];
+ if(SHM->Bbusystate)
+ sleep(1);
+ for (i = ((start + end) / 2);; i = (start + end) / 2) {
+ if (!(j = strcasecmp(bname, bcache[blist[i]].brdname)))
+ return (int)(blist[i] + 1);
+ if (end == start) {
+ break;
+ } else if (i == start) {
+ i = end;
+ start = end;
+ } else if (j > 0)
+ start = i;
+ else
+ end = i;
+ }
+ return 0;
+}
+
+const char *
+postperm_msg(const char *bname)
+{
+ register int i;
+ char buf[PATHLEN];
+ boardheader_t *bp = NULL;
+
+ setbfile(buf, bname, fn_water);
+ if (belong(buf, cuser.userid))
+ return "使用者水桶中";
+
+ if (!strcasecmp(bname, DEFAULT_BOARD))
+ return NULL;
+
+ if (!(i = getbnum(bname)))
+ return "看板不存在";
+
+ assert(0<=i-1 && i-1<MAX_BOARD);
+ bp = getbcache(i);
+
+ if (bp->brdattr & BRD_GUESTPOST)
+ return NULL;
+
+ if (!HasUserPerm(PERM_POST))
+ return "無發文權限";
+
+ /* 秘密看板特別處理 */
+ if (bp->brdattr & BRD_HIDE)
+ return NULL;
+ else if (bp->brdattr & BRD_RESTRICTEDPOST &&
+ !is_hidden_board_friend(i, usernum))
+ return "看板限制發文";
+
+ if (HasUserPerm(PERM_VIOLATELAW) && (bp->level & PERM_VIOLATELAW))
+ return NULL;
+ else if (HasUserPerm(PERM_VIOLATELAW))
+ return "罰單未繳";
+
+ if (!(bp->level & ~PERM_POST))
+ return NULL;
+ if (!HasUserPerm(bp->level & ~PERM_POST))
+ return "未達看板要求權限";
+ return NULL;
+}
+
+int
+haspostperm(const char *bname)
+{
+ return postperm_msg(bname) == NULL ? 1 : 0;
+}
+
+void buildBMcache(int bid) /* bid starts from 1 */
+{
+ char s[IDLEN * 3 + 3], *ptr;
+ int i, uid;
+ char *strtok_pos;
+
+ assert(0<=bid-1 && bid-1<MAX_BOARD);
+ strlcpy(s, getbcache(bid)->BM, sizeof(s));
+ for( i = 0 ; s[i] != 0 ; ++i )
+ if( !isalpha((int)s[i]) && !isdigit((int)s[i]) )
+ s[i] = ' ';
+
+ for( ptr = strtok_r(s, " ", &strtok_pos), i = 0 ;
+ i < MAX_BMs && ptr != NULL ;
+ ptr = strtok_r(NULL, " ", &strtok_pos), ++i )
+ if( (uid = searchuser(ptr, NULL)) != 0 )
+ SHM->BMcache[bid-1][i] = uid;
+ for( ; i < MAX_BMs ; ++i )
+ SHM->BMcache[bid-1][i] = -1;
+}
+
+int is_BM_cache(int bid) /* bid starts from 1 */
+{
+ assert(0<=bid-1 && bid-1<MAX_BOARD);
+ --bid;
+ // XXX hard coded MAX_BMs=4
+ if( currutmp->uid == SHM->BMcache[bid][0] ||
+ currutmp->uid == SHM->BMcache[bid][1] ||
+ currutmp->uid == SHM->BMcache[bid][2] ||
+ currutmp->uid == SHM->BMcache[bid][3] ){
+ cuser.userlevel |= PERM_BM;
+ return 1;
+ }
+ return 0;
+}
+
+/*-------------------------------------------------------*/
+/* PTT cache */
+/*-------------------------------------------------------*/
+int
+filter_aggressive(const char*s)
+{
+ if (
+ /*
+ strstr(s, "此處放較不適當的爭議性字句") != NULL ||
+ */
+ 0
+ )
+ return 1;
+ return 0;
+}
+
+int
+filter_dirtywords(const char*s)
+{
+ if (
+ strstr(s, "幹你娘") != NULL ||
+ 0)
+ return 1;
+ return 0;
+}
+
+#define AGGRESSIVE_FN ".aggressive"
+static char drop_aggressive = 0;
+
+void
+load_aggressive_state()
+{
+ if (dashf(AGGRESSIVE_FN))
+ drop_aggressive = 1;
+ else
+ drop_aggressive = 0;
+}
+
+void
+set_aggressive_state(int s)
+{
+ FILE *fp = NULL;
+ if (s)
+ {
+ fp = fopen(AGGRESSIVE_FN, "wb");
+ fclose(fp);
+ } else {
+ remove(AGGRESSIVE_FN);
+ }
+}
+
+/* cache for 動態看板 */
+void
+reload_pttcache(void)
+{
+ if (SHM->Pbusystate)
+ safe_sleep(1);
+ else { /* jochang: temporary workaround */
+ fileheader_t item, subitem;
+ char pbuf[256], buf[256], *chr;
+ FILE *fp, *fp1, *fp2;
+ int id, aggid, rawid;
+
+ SHM->Pbusystate = 1;
+ SHM->last_film = 0;
+ bzero(SHM->notes, sizeof(SHM->notes));
+ setapath(pbuf, GLOBAL_NOTE);
+ setadir(buf, pbuf);
+
+ load_aggressive_state();
+ id = aggid = rawid = 0; // effective count, aggressive count, total (raw) count
+
+ if ((fp = fopen(buf, "r"))) {
+ // .DIR loop
+ while (fread(&item, sizeof(item), 1, fp)) {
+
+ int chkagg = 0; // should we check aggressive?
+
+ if (item.title[3] != '<' || item.title[8] != '>')
+ continue;
+
+#ifdef GLOBAL_NOTE_AGGCHKDIR
+ // TODO aggressive: only count '<點歌>' section
+ if (strcmp(item.title+3, GLOBAL_NOTE_AGGCHKDIR) == 0)
+ chkagg = 1;
+#endif
+
+ snprintf(buf, sizeof(buf), "%s/%s/" FN_DIR,
+ pbuf, item.filename);
+
+ if (!(fp1 = fopen(buf, "r")))
+ continue;
+
+ // file loop
+ while (fread(&subitem, sizeof(subitem), 1, fp1)) {
+
+ snprintf(buf, sizeof(buf),
+ "%s/%s/%s", pbuf, item.filename,
+ subitem.filename);
+
+ if (!(fp2 = fopen(buf, "r")))
+ continue;
+
+ fread(SHM->notes[id], sizeof(char), sizeof(SHM->notes[0]), fp2);
+ SHM->notes[id][sizeof(SHM->notes[0]) - 1] = 0;
+ rawid ++;
+
+ // filtering
+ if (filter_dirtywords(SHM->notes[id]))
+ {
+ memset(SHM->notes[id], 0, sizeof(SHM->notes[0]));
+ rawid --;
+ }
+ else if (chkagg && filter_aggressive(SHM->notes[id]))
+ {
+ aggid++;
+ // handle aggressive notes by last detemined state
+ if (drop_aggressive)
+ memset(SHM->notes[id], 0, sizeof(SHM->notes[0]));
+ else
+ id++;
+#ifdef _BBS_UTIL_C_
+ // Debug purpose
+ // printf("found aggressive: %s\n", buf);
+#endif
+ }
+ else
+ {
+ id++;
+ }
+
+ fclose(fp2);
+ if (id >= MAX_MOVIE)
+ break;
+
+ } // end of file loop
+ fclose(fp1);
+
+ if (id >= MAX_MOVIE)
+ break;
+ } // end of .DIR loop
+ fclose(fp);
+
+ // decide next aggressive state
+ if (rawid && aggid*3 >= rawid) // if aggressive exceed 1/3
+ set_aggressive_state(1);
+ else
+ set_aggressive_state(0);
+
+#ifdef _BBS_UTIL_C_
+ printf("id(%d)/agg(%d)/raw(%d)\n",
+ id, aggid, rawid);
+#endif
+ }
+ SHM->last_film = id - 1;
+
+ fp = fopen("etc/today_is", "r");
+ if (fp) {
+ fgets(SHM->today_is, 15, fp);
+ if ((chr = strchr(SHM->today_is, '\n')))
+ *chr = 0;
+ SHM->today_is[15] = 0;
+ fclose(fp);
+ }
+ /* 等所有資料更新後再設定 uptime */
+
+ SHM->Puptime = SHM->Ptouchtime;
+ log_usies("CACHE", "reload pttcache");
+ SHM->Pbusystate = 0;
+ }
+}
+
+void
+resolve_garbage(void)
+{
+ int count = 0;
+
+ while (SHM->Puptime < SHM->Ptouchtime) { /* 不用while等 */
+ reload_pttcache();
+ if (count++ > 10 && SHM->Pbusystate) {
+ /*
+ * Ptt: 這邊會有問題 load超過10 秒會所有進loop的process tate = 0
+ * 這樣會所有prcosee都會在load 動態看板 會造成load大增
+ * 但沒有用這個function的話 萬一load passwd檔的process死了
+ * 又沒有人把他 解開 同樣的問題發生在reload passwd
+ */
+ SHM->Pbusystate = 0;
+#ifndef _BBS_UTIL_C_
+ log_usies("CACHE", "refork Ptt dead lock");
+#endif
+ }
+ }
+}
+
+/*-------------------------------------------------------*/
+/* PTT's cache */
+/*-------------------------------------------------------*/
+/* cache for from host 與最多上線人數 */
+void
+reload_fcache(void)
+{
+ if (SHM->Fbusystate)
+ safe_sleep(1);
+ else {
+ FILE *fp;
+
+ SHM->Fbusystate = 1;
+ bzero(SHM->home_ip, sizeof(SHM->home_ip));
+ if ((fp = fopen("etc/domain_name_query.cidr", "r"))) {
+ char buf[256], *ip, *mask;
+ char *strtok_pos;
+
+ SHM->home_num = 0;
+ while (fgets(buf, sizeof(buf), fp)) {
+ if (!buf[0] || buf[0] == '#' || buf[0] == ' ' || buf[0] == '\n')
+ continue;
+
+ if (buf[0] == '@') {
+ SHM->home_ip[0] = 0;
+ SHM->home_mask[0] = 0xFFFFFFFF;
+ SHM->home_num++;
+ continue;
+ }
+
+ ip = strtok_r(buf, " \t", &strtok_pos);
+ if ((mask = strchr(ip, '/')) != NULL) {
+ int shift = 32 - atoi(mask + 1);
+ SHM->home_ip[SHM->home_num] = ipstr2int(ip);
+ SHM->home_mask[SHM->home_num] = (0xFFFFFFFF >> shift ) << shift;
+ }
+ else {
+ SHM->home_ip[SHM->home_num] = ipstr2int(ip);
+ SHM->home_mask[SHM->home_num] = 0xFFFFFFFF;
+ }
+ ip = strtok_r(NULL, " \t", &strtok_pos);
+ if (ip == NULL) {
+ strcpy(SHM->home_desc[SHM->home_num], "雲深不知處");
+ }
+ else {
+ strlcpy(SHM->home_desc[SHM->home_num], ip,
+ sizeof(SHM->home_desc[SHM->home_num]));
+ chomp(SHM->home_desc[SHM->home_num]);
+ }
+ (SHM->home_num)++;
+ if (SHM->home_num == MAX_FROM)
+ break;
+ }
+ fclose(fp);
+ }
+ SHM->max_user = 0;
+
+ /* 等所有資料更新後再設定 uptime */
+ SHM->Fuptime = SHM->Ftouchtime;
+#if !defined(_BBS_UTIL_C_)
+ log_usies("CACHE", "reload fcache");
+#endif
+ SHM->Fbusystate = 0;
+ }
+}
+
+void
+resolve_fcache(void)
+{
+ while (SHM->Fuptime < SHM->Ftouchtime)
+ reload_fcache();
+}
+
+/*
+ * section - hbfl (hidden board friend list)
+ */
+void
+hbflreload(int bid)
+{
+ int hbfl[MAX_FRIEND + 1], i, num, uid;
+ char buf[128];
+ FILE *fp;
+
+ assert(0<=bid-1 && bid-1<MAX_BOARD);
+ memset(hbfl, 0, sizeof(hbfl));
+ setbfile(buf, bcache[bid - 1].brdname, fn_visable);
+ if ((fp = fopen(buf, "r")) != NULL) {
+ for (num = 1; num <= MAX_FRIEND; ++num) {
+ if (fgets(buf, sizeof(buf), fp) == NULL)
+ break;
+ for (i = 0; buf[i] != 0; ++i)
+ if (buf[i] == ' ') {
+ buf[i] = 0;
+ break;
+ }
+ if (strcasecmp(STR_GUEST, buf) == 0 ||
+ (uid = searchuser(buf, NULL)) == 0) {
+ --num;
+ continue;
+ }
+ hbfl[num] = uid;
+ }
+ fclose(fp);
+ }
+ hbfl[0] = COMMON_TIME;
+ memcpy(SHM->hbfl[bid-1], hbfl, sizeof(hbfl));
+}
+
+/* 是否通過板友測試. 如果在板友名單中的話傳回 1, 否則為 0 */
+int
+is_hidden_board_friend(int bid, int uid)
+{
+ int i;
+
+ assert(0<=bid-1 && bid-1<MAX_BOARD);
+ if (SHM->hbfl[bid-1][0] < login_start_time - HBFLexpire)
+ hbflreload(bid);
+ for (i = 1; SHM->hbfl[bid-1][i] != 0 && i <= MAX_FRIEND; ++i) {
+ if (SHM->hbfl[bid-1][i] == uid)
+ return 1;
+ }
+ return 0;
+}
+
+#ifdef USE_COOLDOWN
+void add_cooldowntime(int uid, int min)
+{
+ // Ptt: I will use the number below 15 seconds.
+ time4_t base= now > SHM->cooldowntime[uid - 1]?
+ now : SHM->cooldowntime[uid - 1];
+ base += min*60;
+ base &= 0xFFFFFFF0;
+
+ SHM->cooldowntime[uid - 1] = base;
+}
+void add_posttimes(int uid, int times)
+{
+ if((SHM->cooldowntime[uid - 1] & 0xF) + times <0xF)
+ SHM->cooldowntime[uid - 1] += times;
+ else
+ SHM->cooldowntime[uid - 1] |= 0xF;
+}
+#endif