/*-------------------------------------------------------*/
/* board.c ( NTHU CS MapleBBS Ver 2.36 ) */
/*-------------------------------------------------------*/
/* target : 看板、群組功能 */
/* create : 95/03/29 */
/* update : 95/12/15 */
/*-------------------------------------------------------*/
#include "bbs.h"
#define BRC_MAXSIZE 24576
#define BRC_MAXNUM 80
#define BRC_STRLEN 15
#define BRC_ITEMSIZE (BRC_STRLEN + 1 + BRC_MAXNUM * sizeof( int ))
int brc_size, brc_changed = 0;
int brc_list[BRC_MAXNUM], brc_num;
char brc_buf[BRC_MAXSIZE];
char brc_name[BRC_STRLEN];
static time_t brc_expire_time;
extern int numboards;
extern boardheader *bcache;
typedef struct
{
int pos, total;
char *name, *title, *BM;
uschar unread, zap, bvote;
} boardstat;
boardstat *nbrd;
int *zapbuf;
int brdnum, yank_flag = 0;
char *boardprefix;
static char *str_local_board = "☆○◇□◎△"; /* 代表 local board class */
/* ----------------------------------------------------- */
/* home/userid/.boardrc maintain */
/* ----------------------------------------------------- */
static char *fn_boardrc = ".boardrc";
static char *
brc_getrecord(ptr, name, pnum, list)
char *ptr, *name;
int *pnum, *list;
{
int num;
char *tmp;
strncpy(name, ptr, BRC_STRLEN);
ptr += BRC_STRLEN;
num = (*ptr++) & 0xff;
tmp = ptr + num * sizeof(int);
if (num > BRC_MAXNUM)
num = BRC_MAXNUM;
*pnum = num;
memcpy(list, ptr, num * sizeof(int));
return tmp;
}
static char *
brc_putrecord(ptr, name, num, list)
char *ptr, *name;
int num, *list;
{
if (num > 0 && list[0] > brc_expire_time)
{
if (num > BRC_MAXNUM)
num = BRC_MAXNUM;
while (num > 1 && list[num - 1] < brc_expire_time)
num--;
strncpy(ptr, name, BRC_STRLEN);
ptr += BRC_STRLEN;
*ptr++ = num;
memcpy(ptr, list, num * sizeof(int));
ptr += num * sizeof(int);
}
return ptr;
}
void
brc_update()
{
if (brc_changed && cuser.userlevel)
{
char dirfile[STRLEN], *ptr;
char tmp_buf[BRC_MAXSIZE - BRC_ITEMSIZE], *tmp;
char tmp_name[BRC_STRLEN];
int tmp_list[BRC_MAXNUM], tmp_num;
int fd, tmp_size;
ptr = brc_buf;
if (brc_num > 0)
ptr = brc_putrecord(ptr, brc_name, brc_num, brc_list);
setuserfile(dirfile, fn_boardrc);
if ((fd = open(dirfile, O_RDONLY)) != -1)
{
tmp_size = read(fd, tmp_buf, sizeof(tmp_buf));
close(fd);
}
else
{
tmp_size = 0;
}
tmp = tmp_buf;
while (tmp < &tmp_buf[tmp_size] && (*tmp >= ' ' && *tmp <= 'z'))
{
tmp = brc_getrecord(tmp, tmp_name, &tmp_num, tmp_list);
if (strncmp(tmp_name, currboard, BRC_STRLEN))
ptr = brc_putrecord(ptr, tmp_name, tmp_num, tmp_list);
}
brc_size = (int) (ptr - brc_buf);
if ((fd = open(dirfile, O_WRONLY | O_CREAT, 0644)) != -1)
{
ftruncate(fd, 0);
write(fd, brc_buf, brc_size);
close(fd);
}
brc_changed = 0;
}
}
int
brc_initial(boardname)
char *boardname;
{
char dirfile[STRLEN], *ptr;
int fd;
if (strcmp(currboard, boardname) == 0)
{
return brc_num;
}
brc_update();
strcpy(currboard, boardname);
if (brc_buf[0] == '\0')
{
setuserfile(dirfile, fn_boardrc);
if ((fd = open(dirfile, O_RDONLY)) != -1)
{
brc_size = read(fd, brc_buf, sizeof(brc_buf));
close(fd);
}
else
{
brc_size = 0;
}
}
ptr = brc_buf;
while (ptr < &brc_buf[brc_size] && (*ptr >= ' ' && *ptr <= 'z'))
{
ptr = brc_getrecord(ptr, brc_name, &brc_num, brc_list);
if (strncmp(brc_name, currboard, BRC_STRLEN) == 0)
return brc_num;
}
strncpy(brc_name, boardname, BRC_STRLEN);
brc_num = brc_list[0] = 1;
return 0;
}
void
brc_addlist(fname)
char *fname;
{
int ftime, n, i;
if (!cuser.userlevel)
return;
ftime = atoi(&fname[2]);
if (ftime <= brc_expire_time
/* || fname[0] != 'M' || fname[1] != '.' */ )
{
return;
}
if (brc_num <= 0)
{
brc_list[brc_num++] = ftime;
brc_changed = 1;
return;
}
if ((brc_num == 1) && (ftime < brc_list[0]))
return;
for (n = 0; n < brc_num; n++)
{
if (ftime == brc_list[n])
{
return;
}
else if (ftime > brc_list[n])
{
if (brc_num < BRC_MAXNUM)
brc_num++;
for (i = brc_num - 1; --i >= n; brc_list[i + 1] = brc_list[i]);
brc_list[n] = ftime;
brc_changed = 1;
return;
}
}
if (brc_num < BRC_MAXNUM)
{
brc_list[brc_num++] = ftime;
brc_changed = 1;
}
}
int
brc_unread(fname)
char *fname;
{
int ftime, n;
ftime = atoi(&fname[2]);
if (ftime <= brc_expire_time
/* || fname[0] != 'M' || fname[1] != '.' */ )
return 0;
if (brc_num <= 0)
return 1;
for (n = 0; n < brc_num; n++)
{
if (ftime > brc_list[n])
return 1;
else if (ftime == brc_list[n])
return 0;
}
return 0;
}
/* ----------------------------------------------------- */
/* .bbsrc processing */
/* ----------------------------------------------------- */
char *str_bbsrc = ".bbsrc";
static void
load_zapbuf()
{
register int n, size;
char fname[60];
/* MAXBOARDS ==> 至多看得見 4 個新板 */
n = numboards + 4;
size = n * sizeof(int);
zapbuf = (int *) malloc(size);
while (n)
zapbuf[--n] = login_start_time;
setuserfile(fname, str_bbsrc);
if ((n = open(fname, O_RDONLY, 0600)) != -1)
{
read(n, zapbuf, size);
close(n);
}
if (!nbrd)
nbrd = (boardstat *) malloc(MAXBOARD * sizeof(boardstat));
brc_expire_time = login_start_time - 30 * 86400;
}
static void
save_zapbuf()
{
register int fd, size;
char fname[60];
setuserfile(fname, str_bbsrc);
if ((fd = open(fname, O_WRONLY | O_CREAT, 0600)) != -1)
{
size = numboards * sizeof(int);
write(fd, zapbuf, size);
close(fd);
}
}
/*
woju
Ref: bbs.c: brdperm(char* brdname, char* userid)
*/
int
Ben_Perm(bptr)
boardheader *bptr;
{
register int level;
register char *ptr, *str;
char buf[64], manager[IDLEN + 1];
level = bptr->level;
if ((level & PERM_POSTMASK) || HAS_PERM(level))
return 1;
ptr = bptr->BM;
if (ptr[0] <= ' ')
return 0;
if (is_BM(ptr))
return 1;
/* 祕密看板:核對首席板主的好友名單 */
if ((level & 0xffff) != PERM_SYSOP)
return 0;
setbfile(buf, bptr->brdname, fn_visable);
return (belong(buf, cuser.userid));
}
static void
load_boards()
{
boardheader *bptr;
boardstat *ptr;
char brdclass[5];
int n;
resolve_boards();
if (!zapbuf)
load_zapbuf();
brdnum = 0;
for (n = 0; n < numboards; n++)
{
bptr = &bcache[n];
if (bptr->brdname[0] == '\0')
continue;
if (boardprefix)
{
if (boardprefix == str_local_board)
{
strncpy(brdclass, bptr->title + 5, 2);
brdclass[2] = '\0';
}
else
{
strncpy(brdclass, bptr->title, 4);
brdclass[4] = '\0';
}
/* Ptt 沒用的東西
if (!strncmp(boardprefix, "Collection", strlen("Collection"))) {
if ((bptr->level & PERM_POST) == 0)
continue;
}
else
*/
if (strstr(boardprefix, brdclass) == NULL)
continue;
}
else
{
strncpy(brdclass, bptr->title + 5, 2);
brdclass[2] = '\0';
if (strstr("Σ", brdclass) != NULL)
continue;
}
if (Ben_Perm(bptr) && (yank_flag == 1 ||
yank_flag == 2 && have_author(bptr->brdname) ||
yank_flag != 2 && zapbuf[n]))
{
ptr = &nbrd[brdnum++];
ptr->name = bptr->brdname;
ptr->title = bptr->title;
ptr->BM = bptr->BM;
ptr->pos = n;
ptr->total = -1;
ptr->bvote = bptr->bvote;
ptr->zap = (zapbuf[n] == 0);
}
}
/* 如果 user 將所有 boards 都 zap 掉了 */
if (!brdnum && !boardprefix)
if (yank_flag == 0)
yank_flag = 1;
else if (yank_flag == 2)
yank_flag = 0;
}
static int
search_board(num)
{
char genbuf[IDLEN + 2];
/* Ptt get out
if (num >= 0) {
getdata(b_lines - 1, 0, "請輸入看板名稱:", genbuf, IDLEN + 1, DOECHO);
move(b_lines - 1, 0);
clrtoeol();
move(b_lines, 0);
if (genbuf[0])
{
int n = num + 1;
str_lower(genbuf, genbuf);
while (n != num)
{
if (++n >= brdnum) n = 0;
if (strstr_lower(nbrd[n].name, genbuf))
return n;
if (++n >= brdnum)
n = 0;
}
}
}
else {
*/
move(0,0);
clrtoeol();
CreateNameList();
for (num = 0; num < brdnum; num++)
AddNameList(nbrd[num].name);
namecomplete(MSG_SELECT_BOARD, genbuf);
for (num = 0; num < brdnum; num++)
if (!strcmp(nbrd[num].name, genbuf))
return num;
/*
}
*/
return -1;
}
static int
check_newpost(ptr)
boardstat *ptr;
{
fileheader fh;
struct stat st;
char fname[FNLEN];
char genbuf[200];
int fd, total;
ptr->total = ptr->unread = 0;
setbdir(genbuf, ptr->name);
if ((fd = open(genbuf, O_RDWR)) < 0)
return 0;
fstat(fd, &st);
total = st.st_size / sizeof(fh);
if (total <= 0)
{
close(fd);
return 0;
}
ptr->total = total;
if (!brc_initial(ptr->name))
{
ptr->unread = 1;
}
else
{
lseek(fd, (total - 1) * sizeof(fh), SEEK_SET);
if (read(fd, fname, FNLEN) > 0 && brc_unread(fname))
{
ptr->unread = 1;
}
}
close(fd);
return 1;
}
static int
unread_position(dirfile, ptr)
char *dirfile;
boardstat *ptr;
{
fileheader fh;
char fname[FNLEN];
register int num, fd, step, total;
total = ptr->total;
num = total + 1;
if (ptr->unread && (fd = open(dirfile, O_RDWR)) > 0)
{
if (!brc_initial(ptr->name))
{
num = 1;
}
else
{
num = total - 1;
step = 4;
while (num > 0)
{
lseek(fd, num * sizeof(fh), SEEK_SET);
if (read(fd, fname, FNLEN) <= 0 || !brc_unread(fname))
break;
num -= step;
if (step < 32)
step += step >> 1;
}
if (num < 0)
num = 0;
while (num < total)
{
lseek(fd, num * sizeof(fh), SEEK_SET);
if (read(fd, fname, FNLEN) <= 0 || brc_unread(fname))
break;
num++;
}
}
close(fd);
}
if (num < 0)
num = 0;
return num;
}
static void
brdlist_foot()
{
prints(" 選擇看板 (c)新文章模式 (v/V)標記已讀/未讀 (y)篩選%s (z)切換選擇 ",
yank_flag ? "部份" : "全部");
}
/*
woju
*/
have_author(char* brdname)
{
char dirname[40];
extern cmpfowner();
setbdir(dirname, brdname);
str_lower(currowner, currauthor);
return search_rec(dirname, cmpfowner);
}
static void
show_brdlist(head, clsflag, newflag)
{
if (clsflag)
{
showtitle("看板列表", BoardName);
prints("[←]主選單 [→]閱\讀 [↑↓]選擇 [y]載入 [S]排序 [/]搜尋 [TAB]文摘‧看板 [h]求助\n"
"%-20s 類別 轉信%-31s投票 板 主 ",
newflag ? "總數 未讀 看 板" : " 編號 看 板", " 中 文 敘 述");
move(b_lines, 0);
brdlist_foot();
}
if (brdnum > 0)
{
boardstat *ptr;
int myrow; /* Ptt add color */
static char *color[7]={"","","","","","",""};
myrow = 2;
while (++myrow < b_lines)
{
move(myrow, 0);
if (head < brdnum)
{
ptr = &nbrd[head++];
if (ptr->total == -1)
check_newpost(ptr);
if (yank_flag == 2)
prints("%5d %c ", head, 'A');
else if (!newflag)
prints("%5d %s", head, ptr->zap ? "- " : ptr->unread ? "‧" :
"。");
else if (ptr->zap)
{
ptr->total = ptr->unread = 0;
outs(" ﹣ ﹣");
}
else
{
if (ptr->total == -1)
check_newpost(ptr);
if (newflag)
prints("%6d%s", ptr->total, ptr->unread ? "‧" : "。");
}
prints("%-13s%s%5.5s%-37s%c %.13s",
ptr->name,
color[(unsigned int)(ptr->title[1]+ptr->title[2]+ptr->title[3]+ptr->title[0])%7],
ptr->title,ptr->title+5, " VR"[ptr->bvote],
ptr->BM);
}
clrtoeol();
}
}
}
char *choosebrdhelp[] = {
"\0看板選單輔助說明",
"\01基本指令",
"(p)(↑) 上一個看板",
"(n)(↓) 下一個看板",
"(P)(^B)(PgUp) 上一頁看板",
"(N)(^F)(PgDn) 下一頁看板",
"($) 最後一個看板",
"(數字) 跳至該項目",
"\01進階指令",
"(TAB) 看板/文摘 模式切換",
"(r)(→)(Rtn) 進入多功\能閱\讀選單",
"(q)(←) 回到主選單",
"(z) 訂閱\/反訂閱\看板",
"(y) 列出/不列出所有看板",
"(v/V) 通通看完/全部未讀",
"(S) 按照字母/分類排序",
"(/) 搜尋看板",
NULL};
/* 根據 title 或 name 做 sort */
static
int
cmpboard(brd, tmp)
boardstat *brd, *tmp;
{
register int type = 0;
if (!type)
{
type = strncmp(brd->title, tmp->title, 4);
type *= 256;
type += strcasecmp(brd->name, tmp->name);
}
if (!(cuser.uflag & BRDSORT_FLAG))
type = strcasecmp(brd->name, tmp->name);
return type;
}
static void
choose_board(newflag)
{
static int num = 0;
boardstat *ptr;
int head, ch, tmp,tmp1;
char genbuf[200],*prefixtmp,brdclass[3];
extern time_t board_visit_time;
setutmpmode(newflag ? READNEW : READBRD);
brdnum = 0;
if (!cuser.userlevel) /* guest yank all boards */
yank_flag = 1;
do
{
if (brdnum <= 0)
{
load_boards();
if (brdnum <= 0)
break;
qsort(nbrd, brdnum, sizeof(boardstat), cmpboard);
head = -1;
}
if (num < 0)
num = 0;
else if (num >= brdnum)
num = brdnum - 1;
if (head < 0)
{
/* if (newflag) */
{
tmp = num;
while (num < brdnum)
{
ptr = &nbrd[num];
if (ptr->total == -1)
check_newpost(ptr);
if (ptr->unread)
break;
num++;
}
if (num >= brdnum)
num = tmp;
}
head = (num / p_lines) * p_lines;
show_brdlist(head, 1, newflag);
}
else if (num < head || num >= head + p_lines)
{
head = (num / p_lines) * p_lines;
show_brdlist(head, 0, newflag);
}
ch = cursor_key(3 + num - head, 0);
switch (ch)
{
case 'e':
case KEY_LEFT:
case EOF:
ch = 'q';
case 'q':
break;
case 'c':
if (yank_flag == 2) {
newflag = yank_flag = 0;
brdnum = -1;
}
show_brdlist(head, 1, newflag ^= 1);
break;
/*
Ptt
case 'A':
case 'a': {
if (yank_flag != 2 ) {
sprintf(genbuf, "%s", currauthor);
if (getdata(1, 0,"尋找作者:", genbuf, IDLEN + 2, DOECHO))
strncpy(currauthor, genbuf, IDLEN + 2);
if (*currauthor)
yank_flag = 2;
else
yank_flag= 0;
}
else
yank_flag = 0;
brdnum = -1;
show_brdlist(head, 1, newflag);
break;
}
*/
case KEY_PGUP:
case 'P':
case 'b':
case Ctrl('B'):
if (num)
{
num -= p_lines;
break;
}
case KEY_END:
case '$':
num = brdnum - 1;
break;
case ' ':
case KEY_PGDN:
case 'N':
case Ctrl('F'):
if (num == brdnum - 1)
num = 0;
else
num += p_lines;
break;
/*
woju
*/
case Ctrl('R'): if (currutmp->msgs[0].last_pid) {
show_last_call_in();
my_write(currutmp->msgs[0].last_pid, "水球丟回去:");
show_brdlist(head, 1, newflag);
}
break;
case Ctrl('C'):
cal();
show_brdlist(head, 1, newflag);
break;
case KEY_ESC: if (KEY_ESC_arg == 'n') {
edit_note();
show_brdlist(head, 1, newflag);
}
break;
case Ctrl('U'):
t_users();
show_brdlist(head, 1, newflag);
break;
case Ctrl('I'):
t_idle();
show_brdlist(head, 1, newflag);
break;
case KEY_UP:
case 'p':
case 'k':
if (num-- <= 0)
num = brdnum - 1;
break;
case KEY_DOWN:
case 'n':
case 'j':
if (++num < brdnum)
break;
case '0':
case KEY_HOME:
num = 0;
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if ((tmp = search_num(ch, brdnum)) >= 0)
num = tmp;
brdlist_foot();
break;
case 'h':
show_help(choosebrdhelp);
show_brdlist(head, 1, newflag);
break;
case Ctrl('A'):
Announce();
show_brdlist(head, 1, newflag);
break;
case '/':
if ((tmp = search_board(num)) >= 0)
num = tmp;
show_brdlist(head, 1, newflag);
break;
case 'S':
cuser.uflag ^= BRDSORT_FLAG;
qsort(nbrd, brdnum, sizeof(boardstat), cmpboard);
head = 999;
break;
case 'y':
if (yank_flag == 2)
yank_flag = 0;
else
yank_flag ^= 1;
brdnum = -1;
break;
case 'z': /* opus: no perm check ? */
if (HAS_PERM(PERM_BASIC))
{
ptr = &nbrd[num];
ptr->zap = !ptr->zap;
ptr->total = -1;
zapbuf[ptr->pos] = (ptr->zap ? 0 : login_start_time);
head = 999;
}
break;
case 'v':
case 'V':
ptr = &nbrd[num];
brc_initial(ptr->name);
if (ch == 'v')
{
ptr->unread = 0;
zapbuf[ptr->pos] = time((time_t *) &brc_list[0]);
}
else
zapbuf[ptr->pos] = brc_list[0] = ptr->unread = 1;
brc_num = brc_changed = 1;
brc_update();
show_brdlist(head, 0, newflag);
break;
case 's':
if ((tmp = search_board(-1)) < 0) {
show_brdlist(head, 1, newflag);
break;
}
num = tmp;
case KEY_RIGHT:
case '\n':
case '\r':
case 'r':
{
char buf[STRLEN];
ptr = &nbrd[num];
strncpy(brdclass, ptr->title + 5, 2);
brdclass[2] = '\0';
if (strstr("Σ",brdclass) == NULL) /* 非sub class */
{
brc_initial(ptr->name);
if (yank_flag == 2)
{
setbdir(buf, currboard);
tmp = have_author(currboard) - 1;
head = tmp - t_lines / 2;
getkeep(buf, head > 1 ? head : 1, -(tmp + 1));
}
else if (newflag)
{
setbdir(buf, currboard);
tmp = unread_position(buf, ptr);
head = tmp - t_lines / 2;
getkeep(buf, head > 1 ? head : 1, tmp + 1);
}
board_visit_time = zapbuf[ptr->pos];
if (!ptr->zap)
time((time_t *) &zapbuf[ptr->pos]);
Read();
ptr->total = head = -1;
setutmpmode(newflag ? READNEW : READBRD);
}
else /* sub class */
{
move(12,1);
prefixtmp = boardprefix;
tmp1=num; num=0;
boardprefix = ptr->title+7;
choose_board(0);
num=tmp1;
boardprefix = prefixtmp;
brdnum = -1;
}
}
}
} while (ch != 'q');
save_zapbuf();
}
int
board()
{
choose_board(0);
return 0;
}
int
local_board()
{
boardprefix = str_local_board;
choose_board(0);
return 0;
}
int
Boards()
{
boardprefix = NULL;
choose_board(0);
return 0;
}
int
New()
{
int mode0 = currutmp->mode;
int stat0 = currstat;
char currboard0[IDLEN + 2];
boardprefix = NULL;
choose_board(1);
currutmp->mode = mode0;
currstat = stat0;
return 0;
}