/* $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;
}
/*
* 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);
}
}
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[PATHLEN];
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;
if (!(i = getbnum(bname)))
return "看板不存在";
if (HasUserPerm(PERM_SYSOP))
return NULL;
setbfile(buf, bname, fn_water);
if (file_exist_record(buf, cuser.userid))
return "使用者水桶中";
if (!strcasecmp(bname, DEFAULT_BOARD))
return NULL;
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))
{
// 在罰單的討論相關板可以發文
if (bp->level & PERM_VIOLATELAW)
return NULL;
else
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);
int *pbm = SHM->BMcache[bid-1];
// XXX potential issue: (thanks for mtdas@ptt)
// buildBMcache use -1 as "none".
// some function may call is_BM_cache early
// without having currutmp->uid (maybe?)
// and may get BM permission accidentally.
// quick check
if (!HasUserPerm(PERM_BASIC) || !currutmp->uid || currutmp->uid == -1)
return 0;
// XXX hard coded MAX_BMs=4
if( currutmp->uid == pbm[0] ||
currutmp->uid == pbm[1] ||
currutmp->uid == pbm[2] ||
currutmp->uid == pbm[3] )
{
// auto enable BM permission
if (!HasUserPerm(PERM_BM))
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, BN_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 BN_NOTE_AGGCHKDIR
// TODO aggressive: only count '<點歌>' section
if (strcmp(item.title+3, BN_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