summaryrefslogtreecommitdiffstats
path: root/mbbsd/mbbsd.c
diff options
context:
space:
mode:
authorin2 <in2@63ad8ddf-47c3-0310-b6dd-a9e9d9715204>2002-03-07 23:13:44 +0800
committerin2 <in2@63ad8ddf-47c3-0310-b6dd-a9e9d9715204>2002-03-07 23:13:44 +0800
commitae31e19f92e717919ac8e3db9039eb38d2b89aae (patch)
treec70164d6a1852344f44b04a653ae2815043512af /mbbsd/mbbsd.c
downloadpttbbs-ae31e19f92e717919ac8e3db9039eb38d2b89aae.tar
pttbbs-ae31e19f92e717919ac8e3db9039eb38d2b89aae.tar.gz
pttbbs-ae31e19f92e717919ac8e3db9039eb38d2b89aae.tar.bz2
pttbbs-ae31e19f92e717919ac8e3db9039eb38d2b89aae.tar.lz
pttbbs-ae31e19f92e717919ac8e3db9039eb38d2b89aae.tar.xz
pttbbs-ae31e19f92e717919ac8e3db9039eb38d2b89aae.tar.zst
pttbbs-ae31e19f92e717919ac8e3db9039eb38d2b89aae.zip
Initial revision
git-svn-id: http://opensvn.csie.org/pttbbs/pttbbs/trunk/pttbbs@1 63ad8ddf-47c3-0310-b6dd-a9e9d9715204
Diffstat (limited to 'mbbsd/mbbsd.c')
-rw-r--r--mbbsd/mbbsd.c1465
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;
+}