diff options
Diffstat (limited to 'mbbsd/mbbsd.c')
-rw-r--r-- | mbbsd/mbbsd.c | 1465 |
1 files changed, 1465 insertions, 0 deletions
diff --git a/mbbsd/mbbsd.c b/mbbsd/mbbsd.c new file mode 100644 index 00000000..c4849da5 --- /dev/null +++ b/mbbsd/mbbsd.c @@ -0,0 +1,1465 @@ +/* $Id: mbbsd.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <setjmp.h> +#include <signal.h> +#include <unistd.h> +#include <fcntl.h> +#include <syslog.h> +#include <errno.h> +#include <netdb.h> +#include <time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/telnet.h> +#include "config.h" +#include "pttstruct.h" +#include "common.h" +#include "perm.h" +#include "modes.h" +#include "proto.h" +#ifdef FreeBSD + #include <machine/limits.h> +#else + #include <limits.h> +#endif + +#define SOCKET_QLEN 4 +#define TH_LOW 100 +#define TH_HIGH 120 + +extern int t_lines, t_columns; /* Screen size / width */ +extern int b_lines; /* Screen bottom line number: t_lines-1 */ +extern userinfo_t *currutmp; +extern int curr_idle_timeout; + +static void do_aloha (char *hello); + +static jmp_buf byebye; + +int talkrequest = NA; + +static char remoteusername[40] = "?"; + +extern struct fromcache_t *fcache; +extern struct utmpfile_t *utmpshm; +extern int fcache_semid; + +static unsigned char enter_uflag; +static int use_shell_login_mode = 0; + +char fromhost[STRLEN] = "\0"; + +static struct sockaddr_in xsin; + +/* set signal handler, which won't be reset once signal comes */ +static void +signal_restart (int signum, void (*handler) (int)) +{ + struct sigaction act; + act.sa_handler = handler; + memset (&(act.sa_mask), 0, sizeof (sigset_t)); + act.sa_flags = 0; + sigaction (signum, &act, NULL); +} + +static void +start_daemon () +{ + int n; + char buf[80]; + + /* + * More idiot speed-hacking --- the first time conversion makes the C + * library open the files containing the locale definition and time zone. + * If this hasn't happened in the parent process, it happens in the + * children, once per connection --- and it does add up. + */ + time_t dummy = time (NULL); + struct tm *dummy_time = localtime (&dummy); + + strftime (buf, 80, "%d/%b/%Y:%H:%M:%S", dummy_time); + + if ((n = fork ())){ + exit (0); + } + + /* rocker.011018: it's a good idea to close all unexcept fd!! */ + n = getdtablesize (); + while (n) + close (--n); + /* rocker.011018: we don't need to remember original tty, + so request a new session id */ + setsid (); + + /* rocker.011018: after new session, + we should insure the process is clean daemon */ + if ((n = fork ())){ + exit (0); + } +} + +static void +reapchild (int sig) +{ + int state, pid; + + while ((pid = waitpid (-1, &state, WNOHANG | WUNTRACED)) > 0) + ; +} + +#define BANNER \ +"【" BBSNAME "】◎ 台大流行網 ◎(" MYHOSTNAME ") 調幅(" MYIP ") " +/* +#define BANNER \ +"【" BBSNAME "】◎ 台大流行網 ◎(" MYHOSTNAME ")\r\n"\ +" 調幅(" MYIP ") " +*/ +/* check load and print approriate banner string in buf */ +static int +chkload (char *buf) +{ + char cpu_load[30]; + int i; + + i = cpuload (cpu_load); + + sprintf (buf, BANNER" 系統負荷\r\n %s %s \r\n", cpu_load, + (i > MAX_CPULOAD ? ",高負荷量,請稍後再來 " + "(請利用port 3000~3010連線)" : "")); +#ifdef INSCREEN + strcpy(buf, (i > MAX_CPULOAD ? BANNER + "高負荷量,請稍後再來(請利用port 3000~3010連線)" : "")); +#else + sprintf(buf, BANNER "%s\r\n", + (i > MAX_CPULOAD ? "高負荷量,請稍後再來(請利用port 3000~3010連線)":"")); +#endif + if (i > MAX_CPULOAD) + return 1; + else if (i > MAX_CPULOAD / 2) + curr_idle_timeout = 10 * 60; + else + curr_idle_timeout = 30 * 60; + + return 0; +} + +extern userec_t cuser; + +void +log_user (char *msg) +{ + char filename[200]; + + sprintf (filename, BBSHOME "/home/%c/%s/USERLOG", + cuser.userid[0], cuser.userid); + log_file (filename, msg); +} + +extern time_t login_start_time; + +void +log_usies (char *mode, char *mesg) +{ + char genbuf[200]; + time_t now = time (0); + + if (!mesg) + sprintf (genbuf, cuser.userid[0] ? "%s %s %-12s Stay:%d (%s)" : + "%s %s %s Stay:%d (%s)", + Cdate (&now), mode, cuser.userid, + (int)(now - login_start_time) / 60, cuser.username); + else + sprintf (genbuf, cuser.userid[0] ? "%s %s %-12s %s" : "%s %s %s%s", + Cdate (&now), mode, cuser.userid, mesg); + log_file (FN_USIES, genbuf); + + /* 追蹤使用者 */ + if (HAS_PERM (PERM_LOGUSER)) + log_user (genbuf); +} + +static void +setflags (int mask, int value) +{ + if (value) + cuser.uflag |= mask; + else + cuser.uflag &= ~mask; +} + +extern int usernum; +extern int currmode; + +void +u_exit (char *mode) +{ + //userec_t xuser; + int diff = (time (0) - login_start_time) / 60; + + reload_money(); + auto_backup (); + save_brdbuf(); + setflags (PAGER_FLAG, currutmp->pager != 1); + setflags (CLOAK_FLAG, currutmp->invisible); + + cuser.invisible = currutmp->invisible; + cuser.pager = currutmp->pager; + cuser.mind = currutmp->mind; + if (!(HAS_PERM (PERM_SYSOP) && HAS_PERM (PERM_DENYPOST)) && + !currutmp->invisible ) + do_aloha ("<<下站通知>> -- 我走囉!"); + + purge_utmp (currutmp); + if ((cuser.uflag != enter_uflag) || (currmode & MODE_DIRTY) || diff) + { + if (!diff && cuser.numlogins) + cuser.numlogins = --cuser.numlogins; + /* Leeym 上站停留時間限制式 */ + } + passwd_update (usernum, &cuser); + log_usies (mode, NULL); +} + +static void +system_abort () +{ + if (currmode) + u_exit ("ABORT"); + + clear (); + refresh (); + fprintf (stdout, "謝謝光臨, 記得常來喔 !\n"); + exit (0); +} + +void +abort_bbs (int sig) +{ + if (currmode) + u_exit ("AXXED"); + exit (0); +} + +static void +abort_bbs_debug (int sig) +{ + static int reentrant = 0; + + if (!reentrant){ + reentrant = 1; + if (currmode) + u_exit ("AXXED"); + setproctitle("debug me!(%d)",sig); + sleep(3600); /* wait 60 mins for debug */ + } + exit (0); +} + +/* 登錄 BBS 程式 */ +static void +mysrand () +{ + srand (time (NULL) + currutmp->pid); /* 時間跟 pid 當 rand 的 seed */ +} + +extern userec_t xuser; + +int +dosearchuser (char *userid) +{ + if ((usernum = getuser (userid))) + memcpy (&cuser, &xuser, sizeof (cuser)); + else + memset (&cuser, 0, sizeof (cuser)); + return usernum; +} + +static void +talk_request () +{ + bell (); + bell (); + if (currutmp->msgcount){ + char buf[200]; + time_t now = time (0); + + sprintf (buf, "\033[33;41m★%s\033[34;47m [%s] %s \033[0m", + utmpshm->uinfo[currutmp->destuip].userid, my_ctime (&now), + (currutmp->sig == 2)? "重要消息廣播!(請Ctrl-U,l查看熱訊記錄)" + : "呼叫、呼叫,聽到請回答"); + move (0, 0); + clrtoeol (); + outs (buf); + refresh (); + } + else{ + unsigned char mode0 = currutmp->mode; + char c0 = currutmp->chatid[0]; + screenline_t *screen0 = calloc (t_lines, sizeof (screenline_t)); + extern screenline_t *big_picture; + + currutmp->mode = 0; + currutmp->chatid[0] = 1; + memcpy (screen0, big_picture, t_lines * sizeof (screenline_t)); + talkreply (); + currutmp->mode = mode0; + currutmp->chatid[0] = c0; + memcpy (big_picture, screen0, t_lines * sizeof (screenline_t)); + free (screen0); + redoscr (); + } +} + +extern char *fn_writelog; +FILE *fp_writelog = NULL; + +void +show_last_call_in (int save) +{ + char buf[200]; + sprintf (buf, "\033[1;33;46m★%s\033[37;45m %s \033[m", + currutmp->msgs[0].userid, currutmp->msgs[0].last_call_in); + move (b_lines, 0); + clrtoeol (); + refresh (); + outmsg (buf); + + if (save){ + char genbuf[200]; + time_t now; + if (!fp_writelog){ + sethomefile (genbuf, cuser.userid, fn_writelog); + fp_writelog = fopen (genbuf, "a"); + } + if (fp_writelog){ + time (&now); + fprintf (fp_writelog, "%s \033[0m[%s]\n", buf, Cdatelite (&now)); + } + } +} + +extern unsigned int currstat; +water_t water[6], *swater[6], *water_which=&water[0]; +char water_usies=0; +extern int watermode; +static int add_history_water(water_t *w, msgque_t *msg) +{ + memcpy(&w->msg[w->top], msg, sizeof(msgque_t)); + w->top++; + w->top %= WATERMODE(WATER_OFO) ? 5 : MAX_REVIEW; + + if (w->count < MAX_REVIEW) + w->count++; + + return w->count; +} + +static int +add_history(msgque_t *msg) +{ + int i,j; + water_t *tmp; + add_history_water(&water[0], msg); + for(i = 0 ; i < 5 && swater[i] ; i++ ) + if( swater[i]->pid == msg->pid ) + break; + if( i != 5 ){ + if( !swater[i] ){ + water_usies = i + 1; + swater[i] = &water[i + 1]; + strcpy(swater[i]->userid, msg->userid); + swater[i]->pid = msg->pid; + } + tmp = swater[i]; + } + else{ + tmp = swater[4]; + memset(swater[4], 0, sizeof (water_t)); + strcpy(swater[4]->userid, msg->userid); + swater[4]->pid = msg->pid; + i = 4; + } + + for( j = i ; j > 0 ; j-- ) + swater[j] = swater[j - 1]; + swater[0] = tmp; + add_history_water(swater[0], msg); + + if(WATERMODE(WATER_ORIG) || WATERMODE(WATER_NEW) ){ + if( watermode > 0 && + (water_which == swater[0] || water_which == &water[0]) ){ + if (watermode < water_which->count) + watermode++; + t_display_new(); + } + } + return i; +} + +static void +write_request (int sig) +{ + struct tm *ptime; + time_t now; + + time (&now); + ptime = localtime (&now); + + if (currutmp->pager != 0 && + cuser.userlevel != 0 && + currutmp->msgcount != 0 && + currutmp->mode != TALK && + currutmp->mode != EDITING && + currutmp->mode != CHATING && + currutmp->mode != PAGE && + currutmp->mode != IDLE && + currutmp->mode != MAILALL && currutmp->mode != MONITOR) + { + int i; + char c0 = currutmp->chatid[0]; + int currstat0 = currstat; + unsigned char mode0 = currutmp->mode; + + currutmp->mode = 0; + currutmp->chatid[0] = 2; + currstat = XMODE; + + do + { + bell (); + show_last_call_in (1); + igetch (); + currutmp->msgcount--; + if (currutmp->msgcount >= MAX_MSGS) + { + /* this causes chaos... jochang */ + raise (SIGFPE); + } + + add_history(&currutmp->msgs[0]); + for (i = 0; i < currutmp->msgcount; i++) + currutmp->msgs[i] = currutmp->msgs[i + 1]; + } + while (currutmp->msgcount); + currutmp->chatid[0] = c0; + currutmp->mode = mode0; + currstat = currstat0; + } + else + { + bell (); + show_last_call_in (1); + add_history(&currutmp->msgs[0]); + + refresh (); + currutmp->msgcount = 0; + } +} + +#if 0 +static void +write_request (int sig) +{ + int i, mtimemin, wu; + static char inlock = 0; + if( inlock ) /* 如果已經進來了 (表示上個水球還沒有處理完, + 新的水球又進來) 則不做任何事直接 return */ + return; + inlock = 1; + do{ + for( wu = 0 ; wu < 5 ; ++wu ) + if( water[wu].pid == currutmp->msgs[0].pid ) + break; + if( wu == 5 ){ + for( i = 0, mtimemin = INT_MAX ; i < 5 ; ++i ) + if( water[i].pid == 0 ){ + ++water_usies; + wu = i; + break; + } + else if( water[i].mtime < mtimemin ){ + mtimemin = water[i].mtime; + wu = i; + } + water[wu].pid = currutmp->msgs[0].pid; + strcpy(water[wu].userid, currutmp->msgs[0].userid); + water[wu].msgtop = 0; + for( i = 0 ; i < 5 ; ++i ) + water[wu].msg[i][0] = 0; + } + water[wu].mtime = time(NULL); + strncpy(water[wu].msg[ (int)water[wu].msgtop ], + currutmp->msgs[0].last_call_in, 64); + ++water[wu].msgtop; + water[wu].msgtop %= 5; + + bell (); + show_last_call_in (1); + refresh(); + + if( watermode == 0 ){ /* in waterball selection mode + if( wu != 0 ){ + water_scr(water_which, 0); + qsort(water, 5, sizeof(water_t), cmpwatermtime); + for( i = 0 ; i < 5 ; ++i ) + if( water[i].pid == 0 ) + break; + else + water_scr(i, 0); + water_scr(water_which = 0, 1); + refresh(); + } */ + } + --currutmp->msgcount; + for( i = 0 ; i < currutmp->msgcount - 1 ; ++i ) + currutmp->msgs[i] = currutmp->msgs[i + 1]; + } while( currutmp->msgcount > 0 ); + inlock = 0; +} +#endif + +static void +multi_user_check () +{ + register userinfo_t *ui; + register pid_t pid; + char genbuf[3]; + + if (HAS_PERM (PERM_SYSOP)) + return; /* don't check sysops */ + + if (cuser.userlevel){ + if (!(ui = (userinfo_t *) search_ulist (usernum))) + return; /* user isn't logged in */ + + pid = ui->pid; + if (!pid /*|| (kill(pid, 0) == -1) */ ) + return; /* stale entry in utmp file */ + + getdata (b_lines - 1, 0, "您想刪除其他重複的 login (Y/N)嗎?[Y] ", + genbuf, 3, LCECHO); + + if (genbuf[0] != 'n'){ + if (pid > 0) + kill (pid, SIGHUP); + log_usies ("KICK ", cuser.username); + } + else{ + if (search_ulistn(usernum, 3)!=NULL) + system_abort (); /* Goodbye(); */ + } + } + else{ + /* allow multiple guest user */ + if (search_ulistn(usernum, 100)!=NULL){ + outs ("\n抱歉,目前已有太多 guest, 請稍後再試。\n"); + pressanykey (); + oflush (); + exit (1); + } + } +} + +/* bad login */ +static char str_badlogin[] = "logins.bad"; + +static void +logattempt (char *uid, char type) +{ + char fname[40]; + int fd, len; + char genbuf[200]; + + sprintf (genbuf, "%c%-12s[%s] %s@%s\n", type, uid, + Cdate (&login_start_time), remoteusername, fromhost); + len = strlen (genbuf); + if ((fd = open (str_badlogin, O_WRONLY | O_CREAT | O_APPEND, 0644)) > 0){ + write (fd, genbuf, len); + close (fd); + } + if (type == '-'){ + sprintf (genbuf, "[%s] %s\n", Cdate (&login_start_time), fromhost); + len = strlen (genbuf); + sethomefile (fname, uid, str_badlogin); + if ((fd = open (fname, O_WRONLY | O_CREAT | O_APPEND, 0644)) > 0){ + write (fd, genbuf, len); + close (fd); + } + } +} + +extern char *str_new; +extern char *err_uid; + +static void +login_query () +{ + char uid[IDLEN + 1], passbuf[PASSLEN]; + int attempts; + char genbuf[200]; + extern struct utmpfile_t *utmpshm; + resolve_utmp (); + attach_uhash (); + attempts = utmpshm->number; + show_file ("etc/Welcome", 1, -1, NO_RELOAD); + output ("1", 1); + if (attempts >= MAX_ACTIVE){ + outs ("由於人數太多,請您稍後再來。\n"); + refresh (); + exit (1); + } + + /* hint */ + + attempts = 0; + while (1){ + if (attempts++ >= LOGINATTEMPTS){ + more ("etc/goodbye", NA); + pressanykey (); + exit (1); + } + getdata (20, 0, "請輸入代號,或以[guest]參觀,以[new]註冊:", + uid, IDLEN + 1, DOECHO); + if (strcasecmp (uid, str_new) == 0){ +#ifdef LOGINASNEW + new_register (); + break; +#else + outs ("本系統目前無法以 new 註冊, 請用 guest 進入\n"); + continue; +#endif + } + else if (uid[0] == '\0' || !dosearchuser (uid)){ + outs (err_uid); + } + else if (strcmp (uid, STR_GUEST)){ + getdata (21, 0, MSG_PASSWD, passbuf, PASSLEN, NOECHO); + passbuf[8] = '\0'; + + if (!checkpasswd (cuser.passwd, passbuf) + /* || (HAS_PERM(PERM_SYSOP) && !use_shell_login_mode) */ ){ + logattempt (cuser.userid, '-'); + outs (ERR_PASSWD); + } + else{ + logattempt (cuser.userid, ' '); + if (strcasecmp ("SYSOP", cuser.userid) == 0) + cuser.userlevel = PERM_BASIC | PERM_CHAT | PERM_PAGE | + PERM_POST | PERM_LOGINOK | PERM_MAILLIMIT | + PERM_CLOAK | PERM_SEECLOAK | PERM_XEMPT | + PERM_DENYPOST | PERM_BM | PERM_ACCOUNTS | + PERM_CHATROOM | PERM_BOARD | PERM_SYSOP | PERM_BBSADM; + break; + } + } + else{ /* guest */ + cuser.userlevel = 0; + cuser.uflag = COLOR_FLAG | PAGER_FLAG | BRDSORT_FLAG | MOVIE_FLAG; + break; + } + } + multi_user_check (); + sethomepath (genbuf, cuser.userid); + mkdir (genbuf, 0755); +} + +void +add_distinct (char *fname, char *line) +{ + FILE *fp; + int n = 0; + + if ((fp = fopen (fname, "a+"))){ + char buffer[80]; + char tmpname[100]; + FILE *fptmp; + + strcpy (tmpname, fname); + strcat (tmpname, "_tmp"); + if (!(fptmp = fopen (tmpname, "w"))){ + fclose (fp); + return; + } + rewind (fp); + while (fgets (buffer, 80, fp)){ + char *p = buffer + strlen (buffer) - 1; + + if (p[-1] == '\n' || p[-1] == '\r') + p[-1] = 0; + if (!strcmp (buffer, line)) + break; + sscanf (buffer + strlen (buffer) + 2, "%d", &n); + fprintf (fptmp, "%s%c#%d\n", buffer, 0, n); + } + + if (feof (fp)) + fprintf (fptmp, "%s%c#1\n", line, 0); + else{ + sscanf (buffer + strlen (buffer) + 2, "%d", &n); + fprintf (fptmp, "%s%c#%d\n", buffer, 0, n + 1); + while (fgets (buffer, 80, fp)){ + sscanf (buffer + strlen (buffer) + 2, "%d", &n); + fprintf (fptmp, "%s%c#%d\n", buffer, 0, n); + } + } + fclose (fp); + fclose (fptmp); + unlink (fname); + rename (tmpname, fname); + } +} + +void +del_distinct (char *fname, char *line) +{ + FILE *fp; + int n = 0; + + if ((fp = fopen (fname, "r"))){ + char buffer[80]; + char tmpname[100]; + FILE *fptmp; + + strcpy (tmpname, fname); + strcat (tmpname, "_tmp"); + if (!(fptmp = fopen (tmpname, "w"))){ + fclose (fp); + return; + } + rewind (fp); + while (fgets (buffer, 80, fp)){ + char *p = buffer + strlen (buffer) - 1; + + if (p[-1] == '\n' || p[-1] == '\r') + p[-1] = 0; + if (!strcmp (buffer, line)) + break; + sscanf (buffer + strlen (buffer) + 2, "%d", &n); + fprintf (fptmp, "%s%c#%d\n", buffer, 0, n); + } + + if (!feof (fp)) + while (fgets (buffer, 80, fp)){ + sscanf (buffer + strlen (buffer) + 2, "%d", &n); + fprintf (fptmp, "%s%c#%d\n", buffer, 0, n); + } + fclose (fp); + fclose (fptmp); + unlink (fname); + rename (tmpname, fname); + } +} + +#ifdef WHERE +static int +where (char *from) +{ + register int i = 0, count = 0, j; + + for (j = 0; j < fcache->top; j++){ + char *token = strtok (fcache->domain[j], "&"); + + i = 0; + count = 0; + while (token){ + if (strstr (from, token)) + count++; + token = strtok (NULL, "&"); + i++; + } + if (i == count) + break; + } + if (i != count) + return 0; + return j; +} +#endif + +static void +check_BM () +{ + int i; + boardheader_t *bhdr; + extern boardheader_t *bcache; + extern int numboards; + + cuser.userlevel &= ~PERM_BM; + for (i = 0, bhdr = bcache; i < numboards && !is_BM (bhdr->BM); i++, bhdr++) + ; +} + +extern pid_t currpid; +extern crosspost_t postrecord; + +static void +setup_utmp (int mode) +{ + userinfo_t uinfo; + /* + char buf[80]; + char remotebuf[1024]; + time_t now = time(NULL); + */ + memset (&uinfo, 0, sizeof (uinfo)); + uinfo.pid = currpid = getpid (); + uinfo.uid = usernum; + uinfo.mode = currstat = mode; + uinfo.msgcount = 0; + uinfo.mailalert = load_mailalert (cuser.userid); + if (!(cuser.numlogins % 20) && cuser.userlevel & PERM_BM) + check_BM (); /* Ptt 自動取下離職板主權力 */ + + uinfo.userlevel = cuser.userlevel; + uinfo.sex = cuser.sex % 8; + uinfo.lastact = time (NULL); + + postrecord.times = 0; /* 計算crosspost數 */ + + strcpy (uinfo.userid, cuser.userid); + strcpy (uinfo.realname, cuser.realname); + strcpy (uinfo.username, cuser.username); + strncpy (uinfo.from, fromhost, 23); + + uinfo.five_win = cuser.five_win; + uinfo.five_lose = cuser.five_lose; + uinfo.five_tie = cuser.five_tie; + + uinfo.invisible = cuser.invisible % 2; + uinfo.pager = cuser.pager%5; + uinfo.mind = cuser.mind; + uinfo.brc_id = 0; +#ifdef WHERE + uinfo.from_alias = where (fromhost); +#else + uinfo.from_alias = 0; +#endif +#ifndef FAST_LOGIN + setuserfile (buf, "remoteuser"); + + strcpy (remotebuf, fromhost); + strcat (remotebuf, ctime (&now)); + remotebuf[strlen (remotebuf) - 1] = 0; + add_distinct (buf, remotebuf); +#endif + if (enter_uflag & CLOAK_FLAG) + uinfo.invisible = YEA; + getnewutmpent (&uinfo); +#ifndef _BBS_UTIL_C_ + friend_load (); +#endif +} + +extern char margs[]; +extern char *str_sysop; +extern char *loginview_file[NUMVIEWFILE][2]; + +static void +user_login () +{ + char ans[4], i; + char genbuf[200]; + struct tm *ptime, *tmp; + time_t now; + int a; + /*** Heat:廣告詞 + char *ADV[] = { + "7/17 @LIVE 亂彈, 何欣穗 的 入場卷要送給 ptt 的愛用者!", + "欲知詳情請看 PttAct 板!!", + }; ***/ + + log_usies ("ENTER", fromhost); + setproctitle ("%s: %s", margs, cuser.userid); + resolve_garbage (); + resolve_fcache (); + resolve_boards (); + memset( &water[0],0,sizeof(water_t) * 6); + strcpy(water[0].userid, " 全部 "); + /* 初始化 uinfo、flag、mode */ + setup_utmp (LOGIN); + mysrand (); /* 初始化: random number 增加user跟時間的差異 */ + currmode = MODE_STARTED; + enter_uflag = cuser.uflag; + + /* get local time */ + time (&now); + ptime = localtime (&now); + tmp = localtime (&cuser.lastlogin); + if ((a = utmpshm->number) > fcache->max_user){ + fcache->max_user = a; + fcache->max_time = now; + } + init_brdbuf(); + brc_initial (DEFAULT_BOARD); + set_board (); + /* 畫面處理開始 */ + if (!(HAS_PERM (PERM_SYSOP) && HAS_PERM (PERM_DENYPOST)) && !currutmp->invisible ) + do_aloha ("<<上站通知>> -- 我來啦!"); + if (ptime->tm_mday == cuser.day && ptime->tm_mon + 1 == cuser.month){ + more ("etc/Welcome_birth", NA); + currutmp->birth = 1; + } + else{ +#ifdef MULTI_WELCOME_LOGIN + char buf[80]; + int nScreens; + for( nScreens = 0 ; nScreens < 10 ; ++nScreens ){ + sprintf(buf, "etc/Welcome_login.%d", nScreens); + if( access(buf, 0) < 0 ) + break; + } + printf("%d\n", nScreens); + if( nScreens == 0 ){ // multi screen error? + more ("etc/Welcome_login", NA); + } + else{ + sprintf(buf, "etc/Welcome_login.%d", (int)login_start_time % nScreens); + more (buf, NA); + } +#else + more ("etc/Welcome_login", NA); +#endif +// pressanykey(); +// more("etc/CSIE_Week", NA); + currutmp->birth = 0; + } + + if (cuser.userlevel){/* not guest */ + move (t_lines - 4, 0); + prints (" 歡迎您第 \033[1;33m%d\033[0;37m 度拜訪本站," + "上次您是從 \033[1;33m%s\033[0;37m 連往本站,\n" + " 我記得那天是 \033[1;33m%s\033[0;37m。\n", + ++cuser.numlogins, cuser.lasthost, Cdate (&cuser.lastlogin)); + pressanykey (); + + if (currutmp->birth && tmp->tm_mday != ptime->tm_mday){ + more ("etc/birth.post", YEA); + brc_initial ("WhoAmI"); + set_board (); + do_post (); + } + setuserfile (genbuf, str_badlogin); + if (more (genbuf, NA) != -1){ + getdata (b_lines - 1, 0, "您要刪除以上錯誤嘗試的記錄嗎(Y/N)?[Y]", + ans, 3, LCECHO); + if (*ans != 'n') + unlink (genbuf); + } + check_register (); + strncpy (cuser.lasthost, fromhost, 16); + cuser.lasthost[15] = '\0'; + restore_backup (); + } + else if (!strcmp (cuser.userid, STR_GUEST)){ + char *nick[13] = { + "椰子", "貝殼", "內衣", "寶特瓶", "翻車魚", + "樹葉", "浮萍", "鞋子", "潛水艇", "魔王", + "鐵罐", "考卷", "大美女" + }; + char *name[13] = { + "大王椰子", "鸚鵡螺", "比基尼", "可口可樂", "仰泳的魚", + "憶", "高岡屋", "AIR Jordon", "紅色十月號", "批踢踢", + "SASAYA椰奶", "鴨蛋", "布魯克鱈魚香絲" + }; + char *addr[13] = { + "天堂樂園", "大海", "綠島小夜曲", "美國", "綠色珊瑚礁", + "遠方", "原本海", "NIKE", "蘇聯", "男八618室", + "愛之味", "天上", "藍色珊瑚礁" + }; + i = login_start_time % 13; + sprintf (cuser.username, "海邊漂來的%s", nick[(int) i]); + sprintf (currutmp->username, cuser.username); + sprintf (cuser.realname, name[(int) i]); + sprintf (currutmp->realname, cuser.realname); + sprintf (cuser.address, addr[(int) i]); + cuser.sex = i % 8; + currutmp->pager = 2; + pressanykey (); + } + else + pressanykey (); + + if (!PERM_HIDE (currutmp)) + cuser.lastlogin = login_start_time; + + passwd_update (usernum, &cuser); + + for (i = 0; i < NUMVIEWFILE; i++) + if ((cuser.loginview >> i) & 1) + more (loginview_file[(int) i][0], YEA); + + +} + +static void +do_aloha (char *hello) +{ + FILE *fp; + char userid[80]; + char genbuf[200]; + + setuserfile (genbuf, "aloha"); + if ((fp = fopen (genbuf, "r"))){ + sprintf (genbuf, hello); + while (fgets (userid, 80, fp)){ + userinfo_t *uentp; + int tuid; + + if ((tuid = searchuser (userid)) && tuid != usernum && + (uentp = (userinfo_t *) search_ulist (tuid)) && + isvisible(uentp, currutmp)){ + my_write (uentp->pid, genbuf, uentp->userid, 2); + } + } + fclose (fp); + } +} + +static void +do_term_init () +{ + term_init (); + initscr (); +} + +extern char *fn_register; +extern int showansi; + +static void +start_client () +{ + extern struct commands_t cmdlist[]; +#if FORCE_PROCESS_REGISTER_FORM + int nreg; +#endif + + /* system init */ + nice (2); /* Ptt: lower priority */ + login_start_time = time (0); + currmode = 0; + + signal (SIGHUP, abort_bbs); + signal (SIGTERM, abort_bbs); + signal (SIGPIPE, abort_bbs); + + signal (SIGINT, abort_bbs_debug); + signal (SIGQUIT, abort_bbs_debug); + signal (SIGILL, abort_bbs_debug); + signal (SIGABRT, abort_bbs_debug); + signal (SIGFPE, abort_bbs_debug); + signal (SIGBUS, abort_bbs_debug); + signal (SIGSEGV, abort_bbs_debug); + + signal_restart (SIGUSR1, talk_request); + signal_restart (SIGUSR2, write_request); + + dup2 (0, 1); + + do_term_init (); + signal (SIGALRM, abort_bbs); + alarm (600); + login_query (); /* Ptt 加上login time out */ + user_login (); + m_init (); + +#if FORCE_PROCESS_REGISTER_FORM + if (HAS_PERM (PERM_SYSOP) && (nreg = dashs (fn_register) / 163) > 100){ + char cpu_load[30]; + if (cpuload (cpu_load) > MAX_CPULOAD * 2 / 3) + /* DickG: 根據目前的 load 來決定要審核的數目 */ + scan_register_form (fn_register, 1, nreg / 20); + else + scan_register_form (fn_register, 1, nreg / 10); + } +#endif + if (HAVE_PERM (PERM_SYSOP | PERM_BM)) + b_closepolls (); + if (!(cuser.uflag & COLOR_FLAG)) + showansi = 0; +#ifdef DOTIMEOUT + /* init_alarm(); */// cause strange logout with saving post. + signal (SIGALRM, SIG_IGN); +#else + signal (SIGALRM, SIG_IGN); +#endif + if (chkmailbox ()) + m_read (); + + domenu (MMENU, "主功\能表", (currutmp->mailalert ? 'M' : 'C'), cmdlist); +} + +/* FSA (finite state automata) for telnet protocol */ +static void +telnet_init () +{ + static char svr[] = { + IAC, DO, TELOPT_TTYPE, + IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE, + IAC, WILL, TELOPT_ECHO, + IAC, WILL, TELOPT_SGA + }; + char *cmd; + int n, len, rset; + struct timeval to; + char buf[64]; + for (n = 0, cmd = svr; n < 4; n++){ + len = (n == 1 ? 6 : 3); + write (0, cmd, len); + cmd += len; + to.tv_sec = 3; + to.tv_usec = 0; + rset=1; + if (select (1, (fd_set *) & rset, NULL, NULL, &to) > 0) + recv(0, buf, sizeof (buf),0); + } +} + +/* 取得 remote user name 以判定身份 */ +/* + * rfc931() speaks a common subset of the RFC 931, AUTH, TAP, IDENT and RFC + * 1413 protocols. It queries an RFC 931 etc. compatible daemon on a remote + * host to look up the owner of a connection. The information should not be + * used for authentication purposes. This routine intercepts alarm signals. + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#define STRN_CPY(d,s,l) { strncpy((d),(s),(l)); (d)[(l)-1] = 0; } +#define RFC931_TIMEOUT 10 +#define RFC931_PORT 113 /* Semi-well-known port */ +#define ANY_PORT 0 /* Any old port will do */ + +/* timeout - handle timeouts */ +static void +timeout (int sig) +{ + longjmp (byebye, sig); +} + +static void +getremotename (struct sockaddr_in *from, char *rhost, char *rname) +{ + + /* get remote host name */ + +#ifdef FAST_LOGIN + strcpy (rhost, (char *) inet_ntoa (from->sin_addr)); +#else + struct sockaddr_in our_sin; + struct sockaddr_in rmt_sin; + unsigned rmt_port, rmt_pt; + unsigned our_port, our_pt; + FILE *fp; + char buffer[512], user[80], *cp; + int s; + static struct hostent *hp; + + + hp = NULL; + if (setjmp (byebye) == 0){ + signal (SIGALRM, timeout); + alarm (3); + hp = gethostbyaddr ((char *) &from->sin_addr, sizeof (struct in_addr), + from->sin_family); + alarm (0); + } + strcpy (rhost, hp ? hp->h_name : (char *) inet_ntoa (from->sin_addr)); + +/* + * Use one unbuffered stdio stream for writing to and for reading from the + * RFC931 etc. server. This is done because of a bug in the SunOS 4.1.x + * stdio library. The bug may live in other stdio implementations, too. + * When we use a single, buffered, bidirectional stdio stream ("r+" or "w+" + * mode) we read our own output. Such behaviour would make sense with + * resources that support random-access operations, but not with sockets. + */ + + s = sizeof (our_sin); + if (getsockname (0, (struct sockaddr *) &our_sin, &s) < 0) + return; + + if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0) + return; + + if (!(fp = fdopen (s, "r+"))){ + close (s); + return; + } + /* Set up a timer so we won't get stuck while waiting for the server. */ + if (setjmp (byebye) == 0){ + signal (SIGALRM, timeout); + alarm (RFC931_TIMEOUT); + +/* + * Bind the local and remote ends of the query socket to the same IP + * addresses as the connection under investigation. We go through all + * this trouble because the local or remote system might have more than + * one network address. The RFC931 etc. client sends only port numbers; + * the server takes the IP addresses from the query socket. + */ + our_pt = ntohs (our_sin.sin_port); + our_sin.sin_port = htons (ANY_PORT); + + rmt_sin = *from; + rmt_pt = ntohs (rmt_sin.sin_port); + rmt_sin.sin_port = htons (RFC931_PORT); + + setbuf (fp, (char *) 0); + s = fileno (fp); + + if (bind (s, (struct sockaddr *) &our_sin, sizeof (our_sin)) >= 0 && + connect (s, (struct sockaddr *) &rmt_sin, sizeof (rmt_sin)) >= 0){ +/* + * Send query to server. Neglect the risk that a 13-byte write would + * have to be fragmented by the local system and cause trouble with + * buggy System V stdio libraries. + */ + fprintf (fp, "%u,%u\r\n", rmt_pt, our_pt); + fflush (fp); +/* + * Read response from server. Use fgets()/sscanf() so we can work + * around System V stdio libraries that incorrectly assume EOF when a + * read from a socket returns less than requested. + */ + if (fgets (buffer, sizeof (buffer), fp) && !ferror (fp) + && !feof (fp) + && sscanf (buffer, "%u , %u : USERID :%*[^:]:%79s", &rmt_port, + &our_port, user) == 3 && rmt_pt == rmt_port + && our_pt == our_port){ + +/* + * Strip trailing carriage return. It is part of the protocol, not + * part of the data. + */ + if ((cp = (char *) strchr (user, '\r'))) + *cp = 0; + strcpy (rname, user); + } + } + alarm (0); + } + fclose (fp); +#endif +} + +static int +bind_port (int port) +{ + int sock, on; + + sock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + + on = 1; + setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)); + setsockopt (sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof (on)); + + on = 0; + setsockopt (sock, SOL_SOCKET, SO_LINGER, (char *) &on, sizeof (on)); + + xsin.sin_port = htons (port); + if (bind (sock, (struct sockaddr *) &xsin, sizeof xsin) < 0){ + syslog (LOG_INFO, "bbsd bind_port can't bind to %d", port); + exit (1); + } + if (listen (sock, SOCKET_QLEN) < 0){ + syslog (LOG_INFO, "bbsd bind_port can't listen to %d", port); + exit (1); + } + return sock; +} + + +/*******************************************************/ + + +static void shell_login (int argc, char *argv[], char *envp[]); +static void daemon_login (int argc, char *argv[], char *envp[]); +static int check_ban_and_load (int fd); +#ifdef SUPPORT_GB +extern int current_font_type; +#endif + +int +main (int argc, char *argv[], char *envp[]) +{ + /* avoid SIGPIPE */ + signal (SIGPIPE, SIG_IGN); + + /* avoid erroneous signal from other mbbsd */ + signal (SIGUSR1, SIG_IGN); + signal (SIGUSR2, SIG_IGN); + + /* check if invoked as "bbs" */ + if (argc == 3) + shell_login (argc, argv, envp); + else + daemon_login (argc, argv, envp); + + return 0; +} + +static void +shell_login (int argc, char *argv[], char *envp[]) +{ + + /* Give up root privileges: no way back from here */ + setgid (BBSGID); + setuid (BBSUID); + chdir (BBSHOME); + + /* mmap passwd file */ + if (passwd_mmap ()) + exit (1); + + use_shell_login_mode = 1; + initsetproctitle (argc, argv, envp); + + /* copy fromindent: Standard input:1138: Error:Unexpected end of file + the original "bbs" */ + if (argc > 1){ + strcpy (fromhost, argv[1]); + if (argc > 3) + strcpy (remoteusername, argv[3]); + } + + close (2); + /* don't close fd 1, at least init_tty need it */ + + init_tty (); + if (check_ban_and_load (0)){ + exit (0); + } + start_client (); +} + +static void +daemon_login (int argc, char *argv[], char *envp[]) +{ + int msock, csock; /* socket for Master and Child */ + FILE *fp; + int listen_port = 23; + int len_of_sock_addr; + char buf[256]; + + /* setup standalone */ + + start_daemon(); + + signal_restart(SIGCHLD, reapchild); + + /* choose port */ + if(argc == 1) + listen_port = 3006; + else if(argc >= 2) + listen_port = atoi(argv[1]); + + sprintf(margs, "%s %d ", argv[0],listen_port); + + /* port binding */ + xsin.sin_family = AF_INET; + msock = bind_port(listen_port); + if(msock<0) { + syslog(LOG_INFO, "mbbsd bind_port failed.\n"); + exit(1); + } + + + initsetproctitle(argc, argv, envp); + setproctitle("%s: listening ", margs); + + /* Give up root privileges: no way back from here */ + setgid(BBSGID); + setuid(BBSUID); + chdir(BBSHOME); + + /* mmap passwd file */ + if(passwd_mmap()) + { + exit(1); + } + sprintf(buf, "run/mbbsd.%d.pid", listen_port); + if((fp = fopen(buf, "w"))) { + fprintf(fp, "%d\n", getpid()); + fclose(fp); + } + + /* main loop */ + for(;;) { + len_of_sock_addr = sizeof(xsin); + csock = accept(msock, (struct sockaddr *)&xsin, &len_of_sock_addr); + + if(csock < 0) { + if(errno!=EINTR) sleep(1); + continue; + } + + if(check_ban_and_load(csock)) + { + close(csock); + continue; + } + + if(fork()==0) + break; + else + close(csock); + + } + /* here is only child running */ + + setproctitle("%s: ...login wait... ", margs); + close(msock); + dup2(csock, 0); + close(csock); + + getremotename(&xsin, fromhost, remoteusername); + telnet_init(); + start_client(); + close(0); + close(1); +} + +/* check if we're banning login and if the load is too high. + if login is permitted, return 0; + else return -1; + approriate message is output to fd. +*/ +static int check_ban_and_load(int fd) +{ + FILE *fp; + static char buf[256]; + static time_t chkload_time = 0; + static int overload = 0; /* overload or banned, update every 1 sec */ + static int banned = 0; + + if((time(0) - chkload_time) > 1) { + overload = chkload(buf); + banned = !access(BBSHOME "/BAN",R_OK) && + (strcmp(fromhost, "localhost") != 0); + chkload_time = time(0); + } + + write(fd, buf, strlen(buf)); + + if(banned && (fp = fopen(BBSHOME "/BAN", "r"))) { + while(fgets(buf, 256, fp)) + write(fd, buf, strlen(buf)); + fclose(fp); + } + + if(banned || overload) + return -1; + +#ifdef INSCREEN + write(fd, INSCREEN, strlen(INSCREEN)); +#endif + + return 0; +} |