diff options
Diffstat (limited to 'util/xchatd.c')
-rw-r--r-- | util/xchatd.c | 3504 |
1 files changed, 3504 insertions, 0 deletions
diff --git a/util/xchatd.c b/util/xchatd.c new file mode 100644 index 00000000..46fba147 --- /dev/null +++ b/util/xchatd.c @@ -0,0 +1,3504 @@ +/* $Id: xchatd.c,v 1.1 2002/03/07 15:13:46 in2 Exp $ */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <netdb.h> +#include <fcntl.h> +#include <signal.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <time.h> +#include <sys/resource.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include "config.h" +#include "pttstruct.h" +#include "util.h" +#include "perm.h" +#include "common.h" +#include "xchatd.h" + +#define SERVER_USAGE +#define WATCH_DOG +#undef MONITOR /* 監督 chatroom 活動以解決糾紛 */ +#undef DEBUG /* 程式除錯之用 */ + +#ifdef DEBUG +#define MONITOR +#endif + +static int gline; + +#ifdef WATCH_DOG +#define MYDOG gline = __LINE__ +#else +#define MYDOG /* NOOP */ +#endif + + + +#define CHAT_PIDFILE "log/chat.pid" +#define CHAT_LOGFILE "log/chat.log" +#define CHAT_INTERVAL (60 * 30) +#define SOCK_QLEN 1 + + +/* name of the main room (always exists) */ + + +#define MAIN_NAME "main" +#define MAIN_TOPIC "烹茶可貢西天佛" + + +#define ROOM_LOCKED 1 +#define ROOM_SECRET 2 +#define ROOM_OPENTOPIC 4 +#define ROOM_HANDUP 8 +#define ROOM_ALL (NULL) + + +#define LOCKED(room) (room->rflag & ROOM_LOCKED) +#define SECRET(room) (room->rflag & ROOM_SECRET) +#define OPENTOPIC(room) (room->rflag & ROOM_OPENTOPIC) +#define RHANDUP(room) (room->rflag & ROOM_HANDUP) + +#define RESTRICTED(usr) (usr->uflag == 0) /* guest */ +#define CHATSYSOP(usr) (usr->uflag & ( PERM_SYSOP | PERM_CHATROOM)) +/* Thor: SYSOP 與 CHATROOM都是 chat總管 */ +#define PERM_ROOMOP PERM_CHAT /* Thor: 借 PERM_CHAT為 PERM_ROOMOP */ +#define PERM_HANDUP PERM_BM /* 借 PERM_BM 為有沒有舉手過 */ +#define PERM_SAY PERM_NOTOP /* 借 PERM_NOTOP 為有沒有發表權 */ + +/* 進入時需清空 */ +/* Thor: ROOMOP為房間管理員 */ +#define ROOMOP(usr) (usr->uflag & ( PERM_ROOMOP | PERM_SYSOP | PERM_CHATROOM)) +#define CLOAK(usr) (usr->uflag & PERM_CLOAK) +#define HANDUP(usr) (usr->uflag & PERM_HANDUP) +#define SAY(usr) (usr->uflag & PERM_SAY) +/* Thor: 聊天室隱身術 */ + + +/* ----------------------------------------------------- */ +/* ChatRoom data structure */ +/* ----------------------------------------------------- */ + +typedef struct ChatRoom ChatRoom; +typedef struct ChatUser ChatUser; +typedef struct UserList UserList; +typedef struct ChatCmd ChatCmd; +typedef struct ChatAction ChatAction; + +struct ChatUser +{ + struct ChatUser *unext; + int sock; /* user socket */ + int talksock; /* talk socket */ + ChatRoom *room; + UserList *ignore; + int userno; + int uflag; + int clitype; /* Xshadow: client type. 1 for common client, + * 0 for bbs only client */ + time_t uptime; /* Thor: unused */ + char userid[IDLEN + 1]; /* real userid */ + char chatid[9]; /* chat id */ + char lasthost[30]; /* host address */ + char ibuf[80]; /* buffer for non-blocking receiving */ + int isize; /* current size of ibuf */ +}; + + +struct ChatRoom +{ + struct ChatRoom *next, *prev; + char name[IDLEN]; + char topic[48]; /* Let the room op to define room topic */ + int rflag; /* ROOM_LOCKED, ROOM_SECRET, ROOM_OPENTOPIC */ + int occupants; /* number of users in room */ + UserList *invite; +}; + + +struct UserList +{ + struct UserList *next; + int userno; + char userid[IDLEN + 1]; +}; + + +struct ChatCmd +{ + char *cmdstr; + void (*cmdfunc) (); + int exact; +}; + + +static ChatRoom mainroom; +static ChatUser *mainuser; +static fd_set mainfds; +static int maxfds; /* number of sockets to select on */ +static int totaluser; /* current number of connections */ +static struct timeval zerotv; /* timeval for selecting */ +static char chatbuf[256]; /* general purpose buffer */ +static int common_client_command; + +static char msg_not_op[] = "◆ 您不是這間聊天室的 Op"; +static char msg_no_such_id[] = "◆ 目前沒有人使用 [%s] 這個聊天代號"; +static char msg_not_here[] = "◆ [%s] 不在這間聊天室"; + + +#define FUZZY_USER ((ChatUser *) -1) + + +typedef struct userec_t ACCT; + +/* ----------------------------------------------------- */ +/* acct_load for check acct */ +/* ----------------------------------------------------- */ + +int +acct_load(acct, userid) + ACCT *acct; + char *userid; +{ + int id; + if((id=searchuser(userid))<0) + { + return -1; + } + else + { + return get_record(FN_PASSWD, acct, sizeof(ACCT), id); + } +} + + +/* ----------------------------------------------------- */ +/* str_lower for check acct */ +/* ----------------------------------------------------- */ +void +str_lower(dst, src) + char *dst, *src; +{ + register int ch; + + do + { + ch = *src++; + if (ch >= 'A' && ch <= 'Z') + ch |= 0x20; + *dst++ = ch; + } while (ch); +} + +/* + * str_ncpy() - similar to strncpy(3) but terminates string always with '\0' + * if n != 0, and doesn't do padding + */ + +void +str_ncpy(dst, src, n) + char *dst; + char *src; + int n; +{ + char *end; + + end = dst + n; + + do + { + n = (dst == end) ? 0 : *src++; + *dst++ = n; + } while (n); +} + + +/* ----------------------------------------------------- */ +/* usr_fpath for check acct */ +/* ----------------------------------------------------- */ +char *str_home_file = "home/%c/%s/%s"; + +void +usr_fpath(buf, userid, fname) + char *buf, *userid, *fname; +{ + sprintf(buf, str_home_file, userid[0], userid, fname); +} + +/* ----------------------------------------------------- */ +/* chkpasswd for check passwd */ +/* ----------------------------------------------------- */ +char *crypt(); +static char pwbuf[PASSLEN]; + +int +chkpasswd(passwd, test) + char *passwd, *test; +{ + char *pw; + + str_ncpy(pwbuf, test, PASSLEN); + pw = crypt(pwbuf, passwd); + return (!strncmp(pw, passwd, PASSLEN)); +} + +/* ----------------------------------------------------- */ +/* operation log and debug information */ +/* ----------------------------------------------------- */ + + +static int flog; /* log file descriptor */ + + +static void +logit(key, msg) + char *key; + char *msg; +{ + time_t now; + struct tm *p; + char buf[512]; + + time(&now); + p = localtime(&now); + sprintf(buf, "%02d/%02d %02d:%02d:%02d %-13s%s\n", + p->tm_mon + 1, p->tm_mday, + p->tm_hour, p->tm_min, p->tm_sec, key, msg); + write(flog, buf, strlen(buf)); +} + + +static void +log_init() +{ + flog = open(CHAT_LOGFILE, O_WRONLY | O_CREAT | O_APPEND, 0644); + logit("START", "chat daemon"); +} + + +static void +log_close() +{ + close(flog); +} + + +#ifdef DEBUG +static void +debug_user() +{ + register ChatUser *user; + int i; + char buf[80]; + + i = 0; + for (user = mainuser; user; user = user->unext) + { + sprintf(buf, "%d) %s %s", ++i, user->userid, user->chatid); + logit("DEBUG_U", buf); + } +} + + +static void +debug_room() +{ + register ChatRoom *room; + int i; + char buf[80]; + + i = 0; + room = &mainroom; + + do + { + sprintf(buf, "%d) %s %d", ++i, room->name, room->occupants); + logit("DEBUG_R", buf); + } while (room = room->next); +} +#endif /* DEBUG */ + + +/* ----------------------------------------------------- */ +/* string routines */ +/* ----------------------------------------------------- */ + + +static int valid_chatid(register char *id) { + register int ch, len; + + for(len = 0; (ch = *id); id++) { + /* Thor: check for endless */ + MYDOG; + + if(ch == '/' || ch == '*' || ch == ':') + return 0; + if(++len > 8) + return 0; + } + return len; +} + +/* Case Independent strcmp : 1 ==> euqal */ + + +static int +str_equal(s1, s2) + register unsigned char *s1, *s2; /* Thor: 加上 unsigned, + * 避免中文的問題 */ +{ + register int c1, c2; + + for (;;) + { /* Thor: check for endless */ + MYDOG; + + c1 = *s1; + if (c1 >= 'A' && c1 <= 'Z') + c1 |= 32; + + c2 = *s2; + if (c2 >= 'A' && c2 <= 'Z') + c2 |= 32; + + if (c1 != c2) + return 0; + + if (!c1) + return 1; + + s1++; + s2++; + } +} + + +/* ----------------------------------------------------- */ +/* match strings' similarity case-insensitively */ +/* ----------------------------------------------------- */ +/* str_match(keyword, string) */ +/* ----------------------------------------------------- */ +/* 0 : equal ("foo", "foo") */ +/* -1 : mismatch ("abc", "xyz") */ +/* ow : similar ("goo", "good") */ +/* ----------------------------------------------------- */ + + +static int +str_match(s1, s2) + register unsigned char *s1, *s2; /* Thor: 加上 unsigned, + * 避免中文的問題 */ +{ + register int c1, c2; + + for (;;) + { /* Thor: check for endless */ + MYDOG; + + c2 = *s2; + c1 = *s1; + if (!c1) + { + return c2; + } + + if (c1 >= 'A' && c1 <= 'Z') + c1 |= 32; + + if (c2 >= 'A' && c2 <= 'Z') + c2 |= 32; + + if (c1 != c2) + return -1; + + s1++; + s2++; + } +} + + +/* ----------------------------------------------------- */ +/* search user/room by its ID */ +/* ----------------------------------------------------- */ + + +static ChatUser * +cuser_by_userid(userid) + char *userid; +{ + register ChatUser *cu; + + for (cu = mainuser; cu; cu = cu->unext) + { + MYDOG; + + if (str_equal(userid, cu->userid)) + break; + } + return cu; +} + + +static ChatUser * +cuser_by_chatid(chatid) + char *chatid; +{ + register ChatUser *cu; + + for (cu = mainuser; cu; cu = cu->unext) + { + MYDOG; + + if (str_equal(chatid, cu->chatid)) + break; + } + return cu; +} + + +static ChatUser * +fuzzy_cuser_by_chatid(chatid) + char *chatid; +{ + register ChatUser *cu, *xuser; + int mode; + + xuser = NULL; + + for (cu = mainuser; cu; cu = cu->unext) + { + MYDOG; + + mode = str_match(chatid, cu->chatid); + if (mode == 0) + return cu; + + if (mode > 0) + { + if (xuser == NULL) + xuser = cu; + else + return FUZZY_USER; /* 符合者大於 2 人 */ + } + } + return xuser; +} + + +static ChatRoom *croom_by_roomid(char *roomid) { + register ChatRoom *room; + + room = &mainroom; + do { + MYDOG; + + if(str_equal(roomid, room->name)) + break; + } while((room = room->next)); + return room; +} + + +/* ----------------------------------------------------- */ +/* UserList routines */ +/* ----------------------------------------------------- */ + + +static void +list_free(list) + UserList *list; +{ + UserList *tmp; + + while (list) + { + MYDOG; + + tmp = list->next; + + free(list); + MYDOG; + list = tmp; + } +} + + +static void +list_add(list, user) + UserList **list; + ChatUser *user; +{ + UserList *node; + + MYDOG; + + if((node = (UserList *) malloc(sizeof(UserList)))) { + /* Thor: 防止空間不夠 */ + strcpy(node->userid, user->userid); + node->userno = user->userno; + node->next = *list; + *list = node; + } + MYDOG; +} + + +static int +list_delete(list, userid) + UserList **list; + char *userid; +{ + UserList *node; + + while((node = *list)) { + MYDOG; + + if (str_equal(node->userid, userid)) + { + *list = node->next; + MYDOG; + free(node); + MYDOG; + return 1; + } + list = &node->next; /* Thor: list要跟著前進 */ + } + + return 0; +} + + +static int +list_belong(list, userno) + UserList *list; + int userno; +{ + while (list) + { + MYDOG; + + if (userno == list->userno) + return 1; + list = list->next; + } + return 0; +} + + +/* ------------------------------------------------------ */ +/* non-blocking socket routines : send message to users */ +/* ------------------------------------------------------ */ + + +static void +do_send(nfds, wset, msg, number) + int nfds; + fd_set *wset; + char *msg; + int number; +{ + int sr; + + /* Thor: for future reservation bug */ + + zerotv.tv_sec = 0; + zerotv.tv_usec = 16384; /* Ptt: 改成16384 避免不按時for loop吃cpu time + 16384 約每秒64次 */ + + MYDOG; + + sr = select(nfds + 1, NULL, wset, NULL, &zerotv); + + MYDOG; + + if (sr > 0) + { + register int len; + + len = strlen(msg) + 1; + while (nfds >= 0) + { + MYDOG; + + if (FD_ISSET(nfds, wset)) + { + MYDOG; + send(nfds, msg, len, 0);/* Thor: 如果buffer滿了, 仍會 block */ + MYDOG; + if (--sr <= 0) + return; + } + nfds--; + } + } +} + + +static void +send_to_room(room, msg, userno, number) + ChatRoom *room; + char *msg; + int userno; + int number; +{ + ChatUser *cu; + fd_set wset, *wptr; + int sock, max; + static char sendbuf[256]; + int clitype; /* 分為 bbs client 及 common client 兩次處理 */ + + for (clitype = (number == MSG_MESSAGE || !number) ? 0 : 1; clitype < 2; clitype++) + { + + FD_ZERO(wptr = &wset); + max = -1; + + for (cu = mainuser; cu; cu = cu->unext) + { + MYDOG; + + if (room == cu->room || room == ROOM_ALL) + { + if (cu->clitype == clitype && (!userno || !list_belong(cu->ignore, userno))) + { + sock = cu->sock; + FD_SET(sock, wptr); + if (max < sock) + max = sock; + } + } + } + + if (max < 0) + continue; + + if (clitype) + { + if (strlen(msg)) + sprintf(sendbuf, "%3d %s", number, msg); + else + sprintf(sendbuf, "%3d", number); + + do_send(max, wptr, sendbuf); + } + else + do_send(max, wptr, msg); + } +} + + +static void +send_to_user(user, msg, userno, number) + ChatUser *user; + char *msg; + int userno; + int number; +{ + if (!user->clitype && number && number != MSG_MESSAGE) + return; + + if (!userno || !list_belong(user->ignore, userno)) + { + fd_set wset, *wptr; + int sock; + static char sendbuf[256]; + + sock = user->sock; + FD_ZERO(wptr = &wset); + FD_SET(sock, wptr); + + if (user->clitype) + { + if (strlen(msg)) + sprintf(sendbuf, "%3d %s", number, msg); + else + sprintf(sendbuf, "%3d", number); + do_send(sock, wptr, sendbuf); + } + else + do_send(sock, wptr, msg); + } +} + +#if 0 +static void +send_to_sock(sock, msg) /* Thor: unused */ + int sock; + char *msg; +{ + fd_set wset, *wptr; + + FD_ZERO(wptr = &wset); + FD_SET(sock, wptr); + do_send(sock, wptr, msg); +} +#endif + +/* ----------------------------------------------------- */ + +static void +room_changed(room) + ChatRoom *room; +{ + if (!room) + return; + + sprintf(chatbuf, "= %s %d %d %s", room->name, room->occupants, room->rflag, room->topic); + send_to_room(ROOM_ALL, chatbuf, 0, MSG_ROOMNOTIFY); +} + +static void +user_changed(cu) + ChatUser *cu; +{ + if (!cu) + return; + + sprintf(chatbuf, "= %s %s %s %s", cu->userid, cu->chatid, cu->room->name, cu->lasthost); + if (ROOMOP(cu)) + strcat(chatbuf, " Op"); + send_to_room(cu->room, chatbuf, 0, MSG_USERNOTIFY); +} + +static void +exit_room(user, mode, msg) + ChatUser *user; + int mode; + char *msg; +{ + ChatRoom *room; + + if((room = user->room)) { + user->room = NULL; + user->uflag &= ~PERM_ROOMOP; + + if (--room->occupants > 0) + { + char *chatid; + + chatid = user->chatid; + switch (mode) + { + case EXIT_LOGOUT: + + sprintf(chatbuf, "◆ %s 離開了 ...", chatid); + if (msg && *msg) + { + strcat(chatbuf, ": "); + msg[79] = 0; /* Thor:防止太長 */ + strncat(chatbuf, msg, 80); + } + break; + + case EXIT_LOSTCONN: + + sprintf(chatbuf, "◆ %s 成了斷線的風箏囉", chatid); + break; + + case EXIT_KICK: + + sprintf(chatbuf, "◆ 哈哈!%s 被踢出去了", chatid); + break; + } + if (!CLOAK(user)) /* Thor: 聊天室隱身術 */ + send_to_room(room, chatbuf, 0, MSG_MESSAGE); + + sprintf(chatbuf, "- %s", user->userid); + send_to_room(room, chatbuf, 0, MSG_USERNOTIFY); + room_changed(room); + + return; + } + + else if (room != &mainroom) + { /* Thor: 人數為0時,不是mainroom才free */ + register ChatRoom *next; + +#ifdef DEBUG + debug_room(); +#endif + + sprintf(chatbuf, "- %s", room->name); + send_to_room(ROOM_ALL, chatbuf, 0, MSG_ROOMNOTIFY); + + room->prev->next = room->next; + if((next = room->next)) + next->prev = room->prev; + list_free(room->invite); + + MYDOG; + free(room); + MYDOG; + +#ifdef DEBUG + debug_room(); +#endif + } + } +} + + +/* ----------------------------------------------------- */ +/* chat commands */ +/* ----------------------------------------------------- */ + +/* ----------------------------------------------------- */ +/* (.ACCT) 使用者帳號 (account) subroutines */ +/* ----------------------------------------------------- */ + +static char datemsg[32]; + +char * +Ctime(clock) + time_t *clock; +{ + struct tm *t = localtime(clock); + static char week[] = "日一二三四五六"; + + sprintf(datemsg, "%d年%2d月%2d日%3d:%02d:%02d 星期%.2s", + t->tm_year - 11, t->tm_mon + 1, t->tm_mday, + t->tm_hour, t->tm_min, t->tm_sec, &week[t->tm_wday << 1]); + return (datemsg); +} + +static void +chat_query(cu, msg) + ChatUser *cu; + char *msg; +{ + char str[256]; + int i; + ACCT xuser; + FILE *fp; + + if (acct_load(&xuser, msg) >= 0) + { + sprintf(chatbuf, "%s(%s) 共上站 %d 次,文章 %d 篇", + xuser.userid, xuser.username, xuser.numlogins, xuser.numposts); + send_to_user(cu, chatbuf, 0, MSG_MESSAGE); + + sprintf(chatbuf, "最近(%s)從(%s)上站", Ctime(&xuser.lastlogin), + (xuser.lasthost[0] ? xuser.lasthost : "外太空")); + send_to_user(cu, chatbuf, 0, MSG_MESSAGE); + + usr_fpath(chatbuf, xuser.userid, "plans"); + fp = fopen(chatbuf, "rt"); + i = 0; + while (fp && fgets(str, 256, fp)) + { + if (!strlen(str)) + continue; + + str[strlen(str) - 1] = 0; + send_to_user(cu, str, 0, MSG_MESSAGE); + if (++i >= MAX_QUERYLINES) + break; + } + fclose(fp); + } + else + { + sprintf(chatbuf, msg_no_such_id, msg); + send_to_user(cu, chatbuf, 0, MSG_MESSAGE); + } +} + +static void +chat_clear(cu, msg) + ChatUser *cu; + char *msg; +{ + if (cu->clitype) + send_to_user(cu, "", 0, MSG_CLRSCR); + else + send_to_user(cu, "/c", 0, MSG_MESSAGE); +} + +static void +chat_date(cu, msg) + ChatUser *cu; + char *msg; +{ + time_t thetime; + + time(&thetime); + sprintf(chatbuf, "◆ 標準時間: %s", Ctime(&thetime)); + send_to_user(cu, chatbuf, 0, MSG_MESSAGE); +} + + +static void +chat_topic(cu, msg) + ChatUser *cu; + char *msg; +{ + ChatRoom *room; + char *topic; + + if (!ROOMOP(cu) && !OPENTOPIC(cu->room)) + { + send_to_user(cu, msg_not_op, 0, MSG_MESSAGE); + return; + } + + if (*msg == '\0') + { + send_to_user(cu, "※ 請指定話題", 0, MSG_MESSAGE); + return; + } + + room = cu->room; + topic = room->topic; /* Thor: room 有可能 NULL嗎?? */ + strncpy(topic, msg, 47); + topic[47] = '\0'; + + if (cu->clitype) + send_to_room(room, topic, 0, MSG_TOPIC); + else + { + sprintf(chatbuf, "/t%s", topic); + send_to_room(room, chatbuf, 0, 0); + } + + room_changed(room); + + sprintf(chatbuf, "◆ %s 將話題改為 [1;32m%s[m", cu->chatid, topic); + if (!CLOAK(cu)) /* Thor: 聊天室隱身術 */ + send_to_room(room, chatbuf, 0, MSG_MESSAGE); +} + + +static void +chat_version(cu, msg) + ChatUser *cu; + char *msg; +{ + sprintf(chatbuf, "%d %d", XCHAT_VERSION_MAJOR, XCHAT_VERSION_MINOR); + send_to_user(cu, chatbuf, 0, MSG_VERSION); +} + +static void +chat_nick(cu, msg) + ChatUser *cu; + char *msg; +{ + char *chatid, *str; + ChatUser *xuser; + + chatid = nextword(&msg); + chatid[8] = '\0'; + if (!valid_chatid(chatid)) + { + send_to_user(cu, "※ 這個聊天代號是不正確的", 0, MSG_MESSAGE); + return; + } + + xuser = cuser_by_chatid(chatid); + if (xuser != NULL && xuser != cu) + { + send_to_user(cu, "※ 已經有人捷足先登囉", 0, MSG_MESSAGE); + return; + } + + str = cu->chatid; + + sprintf(chatbuf, "※ %s 將聊天代號改為 [1;33m%s[m", str, chatid); + if (!CLOAK(cu)) /* Thor: 聊天室隱身術 */ + send_to_room(cu->room, chatbuf, cu->userno, MSG_MESSAGE); + + strcpy(str, chatid); + + user_changed(cu); + + if (cu->clitype) + send_to_user(cu, chatid, 0, MSG_NICK); + else + { + sprintf(chatbuf, "/n%s", chatid); + send_to_user(cu, chatbuf, 0, 0); + } +} + +static void +chat_list_rooms(cuser, msg) + ChatUser *cuser; + char *msg; +{ + ChatRoom *cr, *room; + + if (RESTRICTED(cuser)) + { + send_to_user(cuser, "※ 您沒有權限列出現有的聊天室", 0, MSG_MESSAGE); + return; + } + + if (common_client_command) + send_to_user(cuser, "", 0, MSG_ROOMLISTSTART); + else + send_to_user(cuser, "[7m 談天室名稱 │人數│話題 [m", 0, MSG_MESSAGE); + + room = cuser->room; + cr = &mainroom; + do + { + MYDOG; + + + if (!SECRET(cr) || CHATSYSOP(cuser) || (cr == room && ROOMOP(cuser))) + { + if (common_client_command) + { + sprintf(chatbuf, "%s %d %d %s", cr->name, cr->occupants, cr->rflag, cr->topic); + send_to_user(cuser, chatbuf, 0, MSG_ROOMLIST); + } + else + { + sprintf(chatbuf, " %-12s│%4d│%s", cr->name, cr->occupants, cr->topic); + if (LOCKED(cr)) + strcat(chatbuf, " [鎖住]"); + if (SECRET(cr)) + strcat(chatbuf, " [秘密]"); + if (OPENTOPIC(cr)) + strcat(chatbuf, " [話題]"); + send_to_user(cuser, chatbuf, 0, MSG_MESSAGE); + } + + } + } while((cr = cr->next)); + + if (common_client_command) + send_to_user(cuser, "", 0, MSG_ROOMLISTEND); +} + + +static void +chat_do_user_list(cu, msg, theroom) + ChatUser *cu; + char *msg; + ChatRoom *theroom; +{ + ChatRoom *myroom, *room; + ChatUser *user; + + int start, stop, curr = 0; + start = atoi(nextword(&msg)); + stop = atoi(nextword(&msg)); + + myroom = cu->room; + +#ifdef DEBUG + logit(cu->chatid, "do user list"); +#endif + + if (common_client_command) + send_to_user(cu, "", 0, MSG_USERLISTSTART); + else + send_to_user(cu, "[7m 聊天代號│使用者代號 │聊天室 [m", 0, MSG_MESSAGE); + + for (user = mainuser; user; user = user->unext) + { + MYDOG; + + + room = user->room; + if ((theroom != ROOM_ALL) && (theroom != room)) + continue; + + if (myroom != room) + { + if (RESTRICTED(cu) || + (room && SECRET(room) && !CHATSYSOP(cu))) + continue; + } + + if (CLOAK(user)) /* Thor: 隱身術 */ + continue; + + + curr++; + if (start && curr < start) + continue; + else if (stop && (curr > stop)) + break; + + if (common_client_command) + { + if (!room) + continue; /* Xshadow: 還沒進入任何房間的就不列出 */ + + sprintf(chatbuf, "%s %s %s %s", user->chatid, user->userid, room->name, user->lasthost); + if (ROOMOP(user)) + strcat(chatbuf, " Op"); + } + else + { + sprintf(chatbuf, " %-8s│%-12s│%s", user->chatid, user->userid, room ? room->name : "[在門口徘徊]"); + if (ROOMOP(user)) + strcat(chatbuf, " [Op]"); + } + +#ifdef DEBUG + logit("list_U", chatbuf); +#endif + + send_to_user(cu, chatbuf, 0, common_client_command ? MSG_USERLIST : MSG_MESSAGE); + } + if (common_client_command) + send_to_user(cu, "", 0, MSG_USERLISTEND); +} + +static void +chat_list_by_room(cu, msg) + ChatUser *cu; + char *msg; +{ + ChatRoom *whichroom; + char *roomstr; + + roomstr = nextword(&msg); + if (*roomstr == '\0') + whichroom = cu->room; + else + { + if ((whichroom = croom_by_roomid(roomstr)) == NULL) + { + sprintf(chatbuf, "※ 沒有 [%s] 這個聊天室", roomstr); + send_to_user(cu, chatbuf, 0, MSG_MESSAGE); + return; + } + + if (whichroom != cu->room && SECRET(whichroom) && !CHATSYSOP(cu)) + { /* Thor: 要不要測同一room雖SECRET但可以列? + * Xshadow: 我改成同一 room 就可以列 */ + send_to_user(cu, "※ 無法列出在秘密聊天室的使用者", 0, MSG_MESSAGE); + return; + } + } + chat_do_user_list(cu, msg, whichroom); +} + + +static void +chat_list_users(cu, msg) + ChatUser *cu; + char *msg; +{ + chat_do_user_list(cu, msg, ROOM_ALL); +} + +static void +chat_chatroom(cu, msg) + ChatUser *cu; + char *msg; +{ + if (common_client_command) + send_to_user(cu, "批踢踢茶藝館 4 21", 0, MSG_CHATROOM); +} + +static void +chat_map_chatids(cu, whichroom) + ChatUser *cu; /* Thor: 還沒有作不同間的 */ + ChatRoom *whichroom; +{ + int c; + ChatRoom *myroom, *room; + ChatUser *user; + + /* myroom = cu->room; */ + myroom = whichroom; + send_to_user(cu, + "[7m 聊天代號 使用者代號 │ 聊天代號 使用者代號 │ 聊天代號 使用者代號 [m", 0, MSG_MESSAGE); + + c = 0; + + for (user = mainuser; user; user = user->unext) + { + MYDOG; + + room = user->room; + MYDOG; + if (whichroom != ROOM_ALL && whichroom != room) + continue; + MYDOG; + if (myroom != room) + { + if (RESTRICTED(cu) || /* Thor: 要先check room 是不是空的 */ + (room && SECRET(room) && !CHATSYSOP(cu))) + continue; + } + MYDOG; + if (CLOAK(user)) /* Thor:隱身術 */ + continue; + sprintf(chatbuf + (c * 24), " %-8s%c%-12s%s", + user->chatid, ROOMOP(user) ? '*' : ' ', + user->userid, (c < 2 ? "│" : " ")); + MYDOG; + if (++c == 3) + { + send_to_user(cu, chatbuf, 0, MSG_MESSAGE); + c = 0; + } + MYDOG; + } + if (c > 0) + send_to_user(cu, chatbuf, 0, MSG_MESSAGE); +} + + +static void +chat_map_chatids_thisroom(cu, msg) + ChatUser *cu; + char *msg; +{ + chat_map_chatids(cu, cu->room); +} + + +static void +chat_setroom(cu, msg) + ChatUser *cu; + char *msg; +{ + char *modestr; + ChatRoom *room; + char *chatid; + int sign; + int flag; + char *fstr = NULL; + + if (!ROOMOP(cu)) + { + send_to_user(cu, msg_not_op, 0, MSG_MESSAGE); + return; + } + + modestr = nextword(&msg); + sign = 1; + if (*modestr == '+') + modestr++; + else if (*modestr == '-') + { + modestr++; + sign = 0; + } + if (*modestr == '\0') + { + send_to_user(cu, + "※ 請指定狀態: {[+(設定)][-(取消)]}{[l(鎖住)][s(秘密)][t(開放話題)}", 0, MSG_MESSAGE); + return; + } + + room = cu->room; + chatid = cu->chatid; + + while (*modestr) + { + flag = 0; + switch (*modestr) + { + case 'l': + case 'L': + flag = ROOM_LOCKED; + fstr = "鎖住"; + break; + + case 's': + case 'S': + flag = ROOM_SECRET; + fstr = "秘密"; + break; + + case 't': + case 'T': + flag = ROOM_OPENTOPIC; + fstr = "開放話題"; + break; + case 'h': + case 'H': + flag = ROOM_OPENTOPIC; + fstr = "舉手發言"; + break; + + default: + sprintf(chatbuf, "※ 狀態錯誤:[%c]", *modestr); + send_to_user(cu, chatbuf, 0, MSG_MESSAGE); + } + + /* Thor: check room 是不是空的, 應該不是空的 */ + if (flag && (room->rflag & flag) != sign * flag) + { + room->rflag ^= flag; + sprintf(chatbuf, "※ 本聊天室被 %s %s [%s] 狀態", + chatid, sign ? "設定為" : "取消", fstr); + if (!CLOAK(cu)) /* Thor: 聊天室隱身術 */ + send_to_room(room, chatbuf, 0, MSG_MESSAGE); + } + modestr++; + } + room_changed(room); +} + +static char *chat_msg[] = +{ + "[//]help", "MUD-like 社交動詞", + "[/h]elp op", "談天室管理員專用指令", + "[/a]ct <msg>", "做一個動作", + "[/b]ye [msg]", "道別", + "[/c]lear [/d]ate", "清除螢幕 目前時間", + /* "[/d]ate", "目前時間", *//* Thor: 指令太多 */ + +#if 0 + "[/f]ire <user> <msg>", "發送熱訊", /* Thor.0727: 和 flag 衝key */ +#endif + + "[/i]gnore [user]", "忽略使用者", + "[/j]oin <room>", "建立或加入談天室", + "[/l]ist [start [stop]]", "列出談天室使用者", + "[/m]sg <id|user> <msg>", "跟 <id> 說悄悄話", + "[/n]ick <id>", "將談天代號換成 <id>", + "[/p]ager", "切換呼叫器", + "[/q]uery <user>", "查詢網友", + "[/r]oom", "列出一般談天室", + "[/t]ape", "開關錄音機", + "[/u]nignore <user>", "取消忽略", + +#if 0 + "[/u]sers", "列出站上使用者", +#endif + + "[/w]ho", "列出本談天室使用者", + "[/w]hoin <room>", "列出談天室<room> 的使用者", + NULL +}; + + +static char *room_msg[] = +{ + "[/f]lag [+-][lsth]", "設定鎖定、秘密、開放話題、舉手發言", + "[/i]nvite <id>", "邀請 <id> 加入談天室", + "[/kick] <id>", "將 <id> 踢出談天室", + "[/o]p <id>", "將 Op 的權力轉移給 <id>", + "[/topic] <text>", "換個話題", + "[/w]all", "廣播 (站長專用)", + NULL +}; + + +static void +chat_help(cu, msg) + ChatUser *cu; + char *msg; +{ + char **table, *str; + + if (str_equal(nextword(&msg), "op")) + { + send_to_user(cu, "談天室管理員專用指令", 0, MSG_MESSAGE); + table = room_msg; + } + else + { + table = chat_msg; + } + + while((str = *table++)) { + sprintf(chatbuf, " %-20s- %s", str, *table++); + send_to_user(cu, chatbuf, 0, MSG_MESSAGE); + } +} + + +static void +chat_private(cu, msg) + ChatUser *cu; + char *msg; +{ + char *recipient; + ChatUser *xuser; + int userno; + + userno = 0; + recipient = nextword(&msg); + xuser = (ChatUser *) fuzzy_cuser_by_chatid(recipient); + if (xuser == NULL) + { /* Thor.0724: 用 userid也可傳悄悄話 */ + xuser = cuser_by_userid(recipient); + } + if (xuser == NULL) + { + sprintf(chatbuf, msg_no_such_id, recipient); + } + else if (xuser == FUZZY_USER) + { /* ambiguous */ + strcpy(chatbuf, "※ 請指明聊天代號"); + } + else if (*msg) + { + userno = cu->userno; + sprintf(chatbuf, "[1m*%s*[m ", cu->chatid); + msg[79] = 0; /* Thor:防止太長 */ + strncat(chatbuf, msg, 80); + send_to_user(xuser, chatbuf, userno, MSG_MESSAGE); + + if (xuser->clitype) + { /* Xshadow: 如果對方是用 client 上來的 */ + sprintf(chatbuf, "%s %s ", cu->userid, cu->chatid); + msg[79] = 0; + strncat(chatbuf, msg, 80); + send_to_user(xuser, chatbuf, userno, MSG_PRIVMSG); + } + if (cu->clitype) + { + sprintf(chatbuf, "%s %s ", xuser->userid, xuser->chatid); + msg[79] = 0; + strncat(chatbuf, msg, 80); + send_to_user(cu, chatbuf, 0, MSG_MYPRIVMSG); + } + + sprintf(chatbuf, "%s> ", xuser->chatid); + strncat(chatbuf, msg, 80); + } + else + { + sprintf(chatbuf, "※ 您想對 %s 說什麼話呢?", xuser->chatid); + } + send_to_user(cu, chatbuf, userno, MSG_MESSAGE); /* Thor: userno 要改成 0 + * 嗎? */ +} + + +static void +chat_cloak(cu, msg) + ChatUser *cu; + char *msg; +{ + if (CHATSYSOP(cu)) + { + cu->uflag ^= PERM_CLOAK; + sprintf(chatbuf, "◆ %s", CLOAK(cu) ? MSG_CLOAKED : MSG_UNCLOAK); + send_to_user(cu, chatbuf, 0, MSG_MESSAGE); + } +} + + + +/* ----------------------------------------------------- */ + + +static void +arrive_room(cuser, room) + ChatUser *cuser; + ChatRoom *room; +{ + char *rname; + + /* Xshadow: 不必送給自己, 反正換房間就會重新 build user list */ + sprintf(chatbuf, "+ %s %s %s %s", cuser->userid, cuser->chatid, room->name, cuser->lasthost); + if (ROOMOP(cuser)) + strcat(chatbuf, " Op"); + send_to_room(room, chatbuf, 0, MSG_USERNOTIFY); + + cuser->room = room; + room->occupants++; + rname = room->name; + + room_changed(room); + + if (cuser->clitype) + { + send_to_user(cuser, rname, 0, MSG_ROOM); + send_to_user(cuser, room->topic, 0, MSG_TOPIC); + } + else + { + sprintf(chatbuf, "/r%s", rname); + send_to_user(cuser, chatbuf, 0, 0); + sprintf(chatbuf, "/t%s", room->topic); + send_to_user(cuser, chatbuf, 0, 0); + } + + sprintf(chatbuf, "※ [32;1m%s[m 進入 [33;1m[%s][m 包廂", + cuser->chatid, rname); + if (!CLOAK(cuser)) /* Thor: 聊天室隱身術 */ + send_to_room(room, chatbuf, cuser->userno, MSG_MESSAGE); +} + + +static int +enter_room(cuser, rname, msg) + ChatUser *cuser; + char *rname; + char *msg; +{ + ChatRoom *room; + int create; + + create = 0; + room = croom_by_roomid(rname); + if (room == NULL) + { + /* new room */ + +#ifdef MONITOR + logit(cuser->userid, "create new room"); +#endif + + MYDOG; + + room = (ChatRoom *) malloc(sizeof(ChatRoom)); + MYDOG; + if (room == NULL) + { + send_to_user(cuser, "※ 無法再新闢包廂了", 0, MSG_MESSAGE); + return 0; + } + + memset(room, 0, sizeof(ChatRoom)); + memcpy(room->name, rname, IDLEN - 1); + strcpy(room->topic, "這是一個新天地"); + + sprintf(chatbuf, "+ %s 1 0 %s", room->name, room->topic); + send_to_room(ROOM_ALL, chatbuf, 0, MSG_ROOMNOTIFY); + + if (mainroom.next != NULL) + mainroom.next->prev = room; + room->next = mainroom.next; + mainroom.next = room; + room->prev = &mainroom; + + create = 1; + } + else + { + if (cuser->room == room) + { + sprintf(chatbuf, "※ 您本來就在 [%s] 聊天室囉 :)", rname); + send_to_user(cuser, chatbuf, 0, MSG_MESSAGE); + return 0; + } + + if (!CHATSYSOP(cuser) && LOCKED(room) && !list_belong(room->invite, cuser->userno)) + { + send_to_user(cuser, "※ 內有惡犬,非請莫入", 0, MSG_MESSAGE); + return 0; + } + } + + exit_room(cuser, EXIT_LOGOUT, msg); + arrive_room(cuser, room); + + if (create) + cuser->uflag |= PERM_ROOMOP; + + return 0; +} + + +static void +logout_user(cuser) + ChatUser *cuser; +{ + int sock; + ChatUser *xuser, *prev; + +#ifdef DEBUG + logit("before", "logout"); + debug_user(); +#endif + + sock = cuser->sock; + shutdown(sock, 2); + close(sock); + + MYDOG; + + FD_CLR(sock, &mainfds); + +#if 0 /* Thor: 也許不差這一個 */ + if (sock >= maxfds) + maxfds = sock - 1; +#endif + + list_free(cuser->ignore); + +#ifdef DEBUG + debug_user(); +#endif + + xuser = mainuser; + if (xuser == cuser) + { + mainuser = cuser->unext; + } + else + { + do + { + prev = xuser; + xuser = xuser->unext; + if (xuser == cuser) + { + prev->unext = cuser->unext; + break; + } + } while (xuser); + } + + MYDOG; + +#ifdef DEBUG + sprintf(chatbuf, "%p", cuser); + logit("free cuser", chatbuf); +#endif + + free(cuser); + +#ifdef DEBUG + logit("after", "logout"); + debug_user(); +#endif + +#if 0 + next = cuser->next; + prev = cuser->prev; + prev->next = next; + if (next) + next->prev = prev; + + if (cuser) + free(cuser); + MYDOG; + +#endif + + totaluser--; +} + + +static void +print_user_counts(cuser) + ChatUser *cuser; +{ + ChatRoom *room; + int num, userc, suserc, roomc, number; + + userc = suserc = roomc = 0; + + room = &mainroom; + do + { + MYDOG; + + num = room->occupants; + if (SECRET(room)) + { + suserc += num; + if (CHATSYSOP(cuser)) + roomc++; + } + else + { + userc += num; + roomc++; + } + } while((room = room->next)); + + number = (cuser->clitype) ? MSG_MOTD : MSG_MESSAGE; + + sprintf(chatbuf, + "☉ 歡迎光臨【批踢踢茶藝館】,目前開了 [1;31m%d[m 間包廂", roomc); + send_to_user(cuser, chatbuf, 0, number); + + sprintf(chatbuf, "☉ 共有 [1;36m%d[m 人來擺\龍門陣", userc); + if (suserc) + sprintf(chatbuf + strlen(chatbuf), " [%d 人在秘密聊天室]", suserc); + send_to_user(cuser, chatbuf, 0, number); +} + + +static int +login_user(cu, msg) + ChatUser *cu; + char *msg; +{ + int utent; + + char *level; + char *userid; + char *chatid; + struct sockaddr_in from; + int fromlen; + struct hostent *hp; + + + ACCT acct; + char buf[20]; + + /* + * Thor.0819: SECURED_CHATROOM : /! userid chatid passwd , userno + * el 在check完passwd後取得 + */ + /* Xshadow.0915: common client support : /-! userid chatid password */ + + /* 傳參數:userlevel, userid, chatid */ + + /* client/server 版本依據 userid 抓 .PASSWDS 判斷 userlevel */ + + userid = nextword(&msg); + chatid = nextword(&msg); + + +#ifdef DEBUG + logit("ENTER", userid); +#endif + /* Thor.0730: parse space before passwd */ + level = msg; + + /* Thor.0813: 跳過一空格即可, 因為反正如果chatid有空格, 密碼也不對 */ + /* 就算密碼對, 也不會怎麼樣:p */ + /* 可是如果密碼第一個字是空格, 那跳太多空格會進不來... */ + if (*level == ' ') + level++; + + /* Thor.0729: load acct */ + if (!*userid || (acct_load(&acct, userid) < 0)) + { + +#ifdef DEBUG + logit("noexist", chatid); +#endif + + if (cu->clitype) + send_to_user(cu, "錯誤的使用者代號", 0, ERR_LOGIN_NOSUCHUSER); + else + send_to_user(cu, CHAT_LOGIN_INVALID, 0, 0); + + return -1; + } + else if(strncmp(level, acct.passwd, PASSLEN) && + !chkpasswd(acct.passwd, level)) + { + +#ifdef DEBUG + logit("fake", chatid); +#endif + + if (cu->clitype) + send_to_user(cu, "密碼錯誤", 0, ERR_LOGIN_PASSERROR); + else + send_to_user(cu, CHAT_LOGIN_INVALID, 0, 0); + return -1; + } + else + { + /* Thor.0729: if ok, read level. */ + sprintf(buf, "%d", acct.userlevel); + level = buf; + /* Thor.0819: read userno for client/server bbs */ + utent = searchuser(acct.userid); + } + + /* Thor.0819: for client/server bbs */ +/* + for (xuser = mainuser; xuser; xuser = xuser->unext) + { + MYDOG; + + if (xuser->userno == utent) + { + + #ifdef DEBUG + logit("enter", "bogus"); + #endif + if (cu->clitype) + send_to_user(cu, "請勿派遣分身進入聊天室 !!", 0, ERR_LOGIN_USERONLINE); + else + send_to_user(cu, CHAT_LOGIN_BOGUS, 0, 0); + return -1; + } + } +*/ + if (!valid_chatid(chatid)) + { + +#ifdef DEBUG + logit("enter", chatid); +#endif + + if (cu->clitype) + send_to_user(cu, "不合法的聊天室代號 !!", 0, ERR_LOGIN_NICKERROR); + else + send_to_user(cu, CHAT_LOGIN_INVALID, 0, 0); + return 0; + } + +#ifdef DEBUG + debug_user(); +#endif + + if (cuser_by_chatid(chatid) != NULL) + { + /* chatid in use */ + +#ifdef DEBUG + logit("enter", "duplicate"); +#endif + + if (cu->clitype) + send_to_user(cu, "這個代號已經有人使用", 0, ERR_LOGIN_NICKINUSE); + else + send_to_user(cu, CHAT_LOGIN_EXISTS, 0, 0); + return 0; + } + + cu->userno = utent; + cu->uflag = atoi(level) & ~(PERM_ROOMOP | PERM_CLOAK | PERM_HANDUP | PERM_SAY); + /* Thor: 進來先清空ROOMOP(同PERM_CHAT), CLOAK */ + strcpy(cu->userid, userid); + memcpy(cu->chatid, chatid, 8); + cu->chatid[8] = '\0'; + + /* Xshadow: 取得 client 的來源 */ + fromlen = sizeof(from); + if (!getpeername(cu->sock, (struct sockaddr *) & from, &fromlen)) + { + if ((hp = gethostbyaddr((char *) &from.sin_addr, sizeof(struct in_addr), from.sin_family))) + { + strcpy(cu->lasthost, hp->h_name); + } + else + strcpy(cu->lasthost, (char *) inet_ntoa(from.sin_addr)); + + } + else + { + strcpy(cu->lasthost, "[外太空]"); + } + + if (cu->clitype) + send_to_user(cu, "順利", 0, MSG_LOGINOK); + else + send_to_user(cu, CHAT_LOGIN_OK, 0, 0); + + arrive_room(cu, &mainroom); + + send_to_user(cu, "", 0, MSG_MOTDSTART); + print_user_counts(cu); + send_to_user(cu, "", 0, MSG_MOTDEND); + +#ifdef DEBUG + logit("enter", "OK"); +#endif + + return 0; +} + + +static void +chat_act(cu, msg) + ChatUser *cu; + char *msg; +{ + if (*msg && (!RHANDUP(cu->room) || SAY(cu) || ROOMOP(cu))) + { + sprintf(chatbuf, "%s [36m%s[m", cu->chatid, msg); + send_to_room(cu->room, chatbuf, cu->userno, MSG_MESSAGE); + } +} + + +static void +chat_ignore(cu, msg) + ChatUser *cu; + char *msg; +{ + + if (RESTRICTED(cu)) + { + strcpy(chatbuf, "※ 您沒有 ignore 別人的權利"); + } + else + { + char *ignoree; + + ignoree = nextword(&msg); + if (*ignoree) + { + ChatUser *xuser; + + xuser = cuser_by_userid(ignoree); + + if (xuser == NULL) + { + + sprintf(chatbuf, msg_no_such_id, ignoree); + +#if 0 + sprintf(chatbuf, "◆ 談天室現在沒有 [%s] 這號人物", ignoree); +#endif + } + else if (xuser == cu || CHATSYSOP(xuser) || + (ROOMOP(xuser) && (xuser->room == cu->room))) + { + sprintf(chatbuf, "◆ 不可以 ignore [%s]", ignoree); + } + else + { + + if (list_belong(cu->ignore, xuser->userno)) + { + sprintf(chatbuf, "※ %s 已經被凍結了", xuser->chatid); + } + else + { + list_add(&(cu->ignore), xuser); + sprintf(chatbuf, "◆ 將 [%s] 打入冷宮了 :p", xuser->chatid); + } + } + } + else + { + UserList *list; + + if((list = cu->ignore)) + { + int len; + char buf[16]; + + send_to_user(cu, "◆ 這些人被打入冷宮了:", 0, MSG_MESSAGE); + len = 0; + do + { + sprintf(buf, "%-13s", list->userid); + strcpy(chatbuf + len, buf); + len += 13; + if (len >= 78) + { + send_to_user(cu, chatbuf, 0, MSG_MESSAGE); + len = 0; + } + } while((list = list->next)); + + if (len == 0) + return; + } + else + { + strcpy(chatbuf, "◆ 您目前並沒有 ignore 任何人"); + } + } + } + + send_to_user(cu, chatbuf, 0, MSG_MESSAGE); +} + + +static void +chat_unignore(cu, msg) + ChatUser *cu; + char *msg; +{ + char *ignoree; + + ignoree = nextword(&msg); + + if (*ignoree) + { + sprintf(chatbuf, (list_delete(&(cu->ignore), ignoree)) ? + "◆ [%s] 不再被你冷落了" : + "◆ 您並未 ignore [%s] 這號人物", ignoree); + } + else + { + strcpy(chatbuf, "◆ 請指明 user ID"); + } + send_to_user(cu, chatbuf, 0, MSG_MESSAGE); +} + + +static void +chat_join(cu, msg) + ChatUser *cu; + char *msg; +{ + if (RESTRICTED(cu)) + { + send_to_user(cu, "※ 您沒有加入其他聊天室的權限", 0, MSG_MESSAGE); + } + else + { + char *roomid = nextword(&msg); + + if (*roomid) + enter_room(cu, roomid, msg); + else + send_to_user(cu, "※ 請指定聊天室的名字", 0, MSG_MESSAGE); + } +} + + +static void +chat_kick(cu, msg) + ChatUser *cu; + char *msg; +{ + char *twit; + ChatUser *xuser; + ChatRoom *room; + + if (!ROOMOP(cu)) + { + send_to_user(cu, msg_not_op, 0, MSG_MESSAGE); + return; + } + + twit = nextword(&msg); + xuser = cuser_by_chatid(twit); + + if (xuser == NULL) + { + sprintf(chatbuf, msg_no_such_id, twit); + send_to_user(cu, chatbuf, 0, MSG_MESSAGE); + return; + } + + room = cu->room; + if (room != xuser->room || CLOAK(xuser)) + { /* Thor: 聊天室隱身術 */ + sprintf(chatbuf, msg_not_here, twit); + send_to_user(cu, chatbuf, 0, MSG_MESSAGE); + return; + } + + if (CHATSYSOP(xuser)) + { /* Thor: 踢不走 CHATSYSOP */ + sprintf(chatbuf, "◆ 不可以 kick [%s]", twit); + send_to_user(cu, chatbuf, 0, MSG_MESSAGE); + return; + } + + exit_room(xuser, EXIT_KICK, (char *) NULL); + + if (room == &mainroom) + logout_user(xuser); + else + enter_room(xuser, MAIN_NAME, (char *) NULL); +} + + +static void +chat_makeop(cu, msg) + ChatUser *cu; + char *msg; +{ + char *newop; + ChatUser *xuser; + ChatRoom *room; + + if (!ROOMOP(cu)) + { + send_to_user(cu, msg_not_op, 0, MSG_MESSAGE); + return; + } + + newop = nextword(&msg); + xuser = cuser_by_chatid(newop); + + if (xuser == NULL) + { + sprintf(chatbuf, msg_no_such_id, newop); + send_to_user(cu, chatbuf, 0, MSG_MESSAGE); + return; + } + + if (cu == xuser) + { + sprintf(chatbuf, "※ 您早就已經是 Op 了啊"); + send_to_user(cu, chatbuf, 0, MSG_MESSAGE); + return; + } + + room = cu->room; + + if (room != xuser->room || CLOAK(xuser)) + { /* Thor: 聊天室隱身術 */ + sprintf(chatbuf, msg_not_here, xuser->chatid); + send_to_user(cu, chatbuf, 0, MSG_MESSAGE); + return; + } + + cu->uflag &= ~PERM_ROOMOP; + xuser->uflag |= PERM_ROOMOP; + + user_changed(cu); + user_changed(xuser); + + sprintf(chatbuf, "※ %s 將 Op 權力轉移給 %s", + cu->chatid, xuser->chatid); + if (!CLOAK(cu)) /* Thor: 聊天室隱身術 */ + send_to_room(room, chatbuf, 0, MSG_MESSAGE, MSG_MESSAGE); +} + + + +static void +chat_invite(cu, msg) + ChatUser *cu; + char *msg; +{ + char *invitee; + ChatUser *xuser; + ChatRoom *room; + UserList **list; + + if (!ROOMOP(cu)) + { + send_to_user(cu, msg_not_op, 0, MSG_MESSAGE); + return; + } + + invitee = nextword(&msg); + xuser = cuser_by_chatid(invitee); + if (xuser == NULL) + { + sprintf(chatbuf, msg_no_such_id, invitee); + send_to_user(cu, chatbuf, 0, MSG_MESSAGE); + return; + } + + room = cu->room; /* Thor: 是否要 check room 是否 NULL ? */ + list = &(room->invite); + + if (list_belong(*list, xuser->userno)) + { + sprintf(chatbuf, "※ %s 已經接受過邀請了", xuser->chatid); + send_to_user(cu, chatbuf, 0, MSG_MESSAGE); + return; + } + list_add(list, xuser); + + sprintf(chatbuf, "※ %s 邀請您到 [%s] 聊天室", + cu->chatid, room->name); + send_to_user(xuser, chatbuf, 0, MSG_MESSAGE); /* Thor: 要不要可以 ignore? */ + sprintf(chatbuf, "※ %s 收到您的邀請了", xuser->chatid); + send_to_user(cu, chatbuf, 0, MSG_MESSAGE); +} + + +static void +chat_broadcast(cu, msg) + ChatUser *cu; + char *msg; +{ + if (!CHATSYSOP(cu)) + { + send_to_user(cu, "※ 您沒有在聊天室廣播的權力!", 0, MSG_MESSAGE); + return; + } + if (*msg == '\0') + { + send_to_user(cu, "※ 請指定廣播內容", 0, MSG_MESSAGE); + return; + } + sprintf(chatbuf, "[1m※ " BBSNAME "談天室廣播中 [%s].....[m", + cu->chatid); + send_to_room(ROOM_ALL, chatbuf, 0, MSG_MESSAGE); + sprintf(chatbuf, "◆ %s", msg); + send_to_room(ROOM_ALL, chatbuf, 0, MSG_MESSAGE); +} + + +static void +chat_goodbye(cu, msg) + ChatUser *cu; + char *msg; +{ + exit_room(cu, EXIT_LOGOUT, msg); + /* Thor: 要不要加 logout_user(cu) ? */ +} + + +/* --------------------------------------------- */ +/* MUD-like social commands : action */ +/* --------------------------------------------- */ + +struct ChatAction +{ + char *verb; /* 動詞 */ + char *chinese; /* 中文翻譯 */ + char *part1_msg; /* 介詞 */ + char *part2_msg; /* 動作 */ +}; + + +static ChatAction party_data[] = +{ + {"aluba", "阿魯巴", "把", "架上柱子阿魯巴!!"}, + {"aodre", "景仰", "對", "的景仰有如滔滔江水,連綿不絕……"}, + {"bearhug", "熱擁", "熱情的擁抱", ""}, + {"blade", "一刀", "一刀啟程把", "送上西天"}, + {"bless", "祝福", "祝福", "心想事成"}, + {"board", "主機板", "把", "抓去跪主機板"}, + {"bokan", "氣功\", "雙掌微合,蓄勢待發……突然間,電光乍現,對", "使出了Bo--Kan!"}, + {"bow", "鞠躬", "畢躬畢敬的向", "鞠躬"}, + {"box", "幕之內", "開始輪擺\式移位,對", "作肝臟攻擊"}, + {"boy", "平底鍋", "從背後拿出了平底鍋,把", "敲昏了"}, + {"bye", "掰掰", "向", "說掰掰!!"}, + {"call", "呼喚", "大聲的呼喚,啊~~", "啊~~~你在哪裡啊啊啊啊~~~~"}, + {"caress", "輕撫", "輕輕的撫摸著", ""}, + {"clap", "鼓掌", "向", "熱烈鼓掌"}, + {"claw", "抓抓", "從貓咪樂園借了隻貓爪,把", "抓得死去活來"}, + {"comfort", "安慰", "溫言安慰", ""}, + {"cong", "恭喜", "從背後拿出了拉炮,呯!呯!恭喜", ""}, + {"cpr", "口對口", "對著", "做口對口人工呼吸"}, + {"cringe", "乞憐", "向", "卑躬屈膝,搖尾乞憐"}, + {"cry", "大哭", "向", "嚎啕大哭"}, + {"dance", "跳舞", "拉了", "的手翩翩起舞" }, + {"destroy", "毀滅", "祭起了『極大毀滅咒文』,轟向", ""}, + {"dogleg", "狗腿", "對", "狗腿"}, + {"drivel", "流口水", "對著", "流口水"}, + {"envy", "羨慕", "向", "流露出羨慕的眼光"}, + {"eye", "送秋波", "對", "頻送秋波"}, + {"fire", "銬問", "拿著火紅的鐵棒走向", ""}, + {"forgive", "原諒", "接受道歉,原諒了", ""}, + {"french", "法式吻", "把舌頭伸到", "喉嚨裡∼∼∼哇!一個浪漫的法國氏深吻"}, + {"giggle", "傻笑", "對著", "傻傻的呆笑"}, + {"glue", "補心", "用三秒膠,把", "的心黏了起來"}, + {"goodbye", "告別", "淚\眼汪汪的向", "告別"}, + {"grin", "奸笑", "對", "露出邪惡的笑容"}, + {"growl", "咆哮", "對", "咆哮不已"}, + {"hand", "握手", "跟", "握手"}, + {"hide", "躲", "躲在", "背後"}, + {"hospitl", "送醫院", "把", "送進醫院"}, + {"hug", "擁抱", "輕輕地擁抱", ""}, + {"hrk", "昇龍拳", "沉穩了身形,匯聚了內勁,對", "使出了一記Ho--Ryu--Kan!!!"}, + {"jab", "戳人", "溫柔的戳著", ""}, + {"judo", "過肩摔", "抓住了", "的衣襟,轉身……啊,是一記過肩摔!"}, + {"kickout", "踢", "用大腳把", "踢到山下去了"}, + {"kick", "踢人", "把", "踢的死去活來"}, + {"kiss", "輕吻", "輕吻", "的臉頰"}, + {"laugh", "嘲笑", "大聲嘲笑", ""}, + {"levis", "給我", "說:給我", "!其餘免談!"}, + {"lick", "舔", "狂舔", ""}, + {"lobster", "壓制", "施展逆蝦形固定,把", "壓制在地板上"}, + {"love", "表白", "對", "深情的表白"}, + {"marry", "求婚", "捧著九百九十九朵玫瑰向", "求婚"}, + {"no", "不要啊", "拼命對著", "搖頭~~~~不要啊~~~~"}, + {"nod", "點頭", "向", "點頭稱是"}, + {"nudge", "頂肚子", "用手肘頂", "的肥肚子"}, + {"pad", "拍肩膀", "輕拍", "的肩膀"}, + {"pettish", "撒嬌", "跟", "嗲聲嗲氣地撒嬌"}, + {"pili", "霹靂", "使出 君子風 天地根 般若懺 三式合一打向", "~~~~~~"}, + {"pinch", "擰人", "用力的把", "擰的黑青"}, + {"roll", "打滾", "放出多爾袞的音樂,", "在地上滾來滾去"}, + {"protect", "保護", "保護著", ""}, + {"pull", "拉", "死命地拉住", "不放"}, + {"punch", "揍人", "狠狠揍了", "一頓"}, + {"rascal", "耍賴", "跟", "耍賴"}, + {"recline", "入懷", "鑽到", "的懷裡睡著了……"}, + {"respond", "負責", "安慰", "說:『不要哭,我會負責的……』"}, + {"shrug", "聳肩", "無奈地向", "聳了聳肩膀"}, + {"sigh", "歎氣", "對", "歎了一口氣"}, + {"slap", "打耳光", "啪啪的巴了", "一頓耳光"}, + {"smooch", "擁吻", "擁吻著", ""}, + {"snicker", "竊笑", "嘿嘿嘿..的對", "竊笑"}, + {"sniff", "不屑", "對", "嗤之以鼻"}, + {"spank", "打屁屁", "用巴掌打", "的臀部"}, + {"squeeze", "緊擁", "緊緊地擁抱著", ""}, + {"sysop", "召喚", "叫出了批踢踢,把", "踩扁了!"}, + {"thank", "感謝", "向", "感謝得五體投地"}, + {"tickle", "搔癢", "咕嘰!咕嘰!搔", "的癢"}, + {"wake", "搖醒", "輕輕地把", "搖醒"}, + {"wave", "揮手", "對著", "拼命的搖手"}, + {"welcome", "歡迎", "歡迎", "進來八卦一下"}, + {"what", "什麼", "說:『", "哩公瞎密哇隴聽某???﹖?』"}, + {"whip", "鞭子", "手上拿著蠟燭,用鞭子痛打", ""}, + {"wink", "眨眼", "對", "神秘的眨眨眼睛"}, + {"zap", "猛攻", "對", "瘋狂的攻擊"}, + {NULL, NULL, NULL, NULL} +}; + +static int +chicken_action(cu, cmd, party) + ChatUser *cu; + char *cmd; + char *party; +{ +return 0; +} +static int +party_action(cu, cmd, party) + ChatUser *cu; + char *cmd; + char *party; +{ + ChatAction *cap; + char *verb; + + for (cap = party_data; (verb = cap->verb); cap++) + { + MYDOG; + + if (str_equal(cmd, verb)) + { + if (*party == '\0') + { + party = "大家"; + } + else + { + ChatUser *xuser; + + xuser = fuzzy_cuser_by_chatid(party); + if (xuser == NULL) + { /* Thor.0724: 用 userid也嘛通 */ + xuser = cuser_by_userid(party); + } + + if (xuser == NULL) + { + sprintf(chatbuf, msg_no_such_id, party); + send_to_user(cu, chatbuf, 0, MSG_MESSAGE); + return 0; + } + else if (xuser == FUZZY_USER) + { + sprintf(chatbuf, "※ 請指明聊天代號"); + send_to_user(cu, chatbuf, 0, MSG_MESSAGE); + return 0; + } + else if (cu->room != xuser->room || CLOAK(xuser)) + { + sprintf(chatbuf, msg_not_here, party); + send_to_user(cu, chatbuf, 0, MSG_MESSAGE); + return 0; + } + else + { + party = xuser->chatid; + } + } + sprintf(chatbuf, "[1;32m%s [31m%s[33m %s [31m%s[m", + cu->chatid, cap->part1_msg, party, cap->part2_msg); + send_to_room(cu->room, chatbuf, cu->userno, MSG_MESSAGE); + return 0; /* Thor: cu->room 是否為 NULL? */ + } + } + return 1; +} + + +/* --------------------------------------------- */ +/* MUD-like social commands : speak */ +/* --------------------------------------------- */ + + +static ChatAction speak_data[] = +{ + + { + "ask", "詢問", "問", NULL + }, + { + "chant", "歌頌", "高聲歌頌", NULL + }, + { + "cheer", "喝采", "喝采", NULL + }, + { + "chuckle", "輕笑", "輕笑", NULL + }, + { + "curse", "暗幹", "暗幹", NULL + }, + /* {"curse", "咒罵", NULL}, */ + { + "demand", "要求", "要求", NULL + }, + { + "frown", "皺眉頭", "蹙眉", NULL + }, + { + "groan", "呻吟", "呻吟", NULL + }, + { + "grumble", "發牢騷", "發牢騷", NULL + }, + { + "guitar", "彈唱", "邊彈著吉他,邊唱著", NULL + }, + /* {"helpme", "呼救","大聲呼救",NULL}, */ + { + "hum", "喃喃", "喃喃自語", NULL + }, + { + "moan", "怨嘆", "怨嘆", NULL + }, + { + "notice", "強調", "強調", NULL + }, + { + "order", "命令", "命令", NULL + }, + { + "ponder", "沈思", "沈思", NULL + }, + { + "pout", "噘嘴", "噘著嘴說", NULL + }, + { + "pray", "祈禱", "祈禱", NULL + }, + { + "request", "懇求", "懇求", NULL + }, + { + "shout", "大罵", "大罵", NULL + }, + { + "sing", "唱歌", "唱歌", NULL + }, + { + "smile", "微笑", "微笑", NULL + }, + { + "smirk", "假笑", "假笑", NULL + }, + { + "swear", "發誓", "發誓", NULL + }, + { + "tease", "嘲笑", "嘲笑", NULL + }, + { + "whimper", "嗚咽", "嗚咽的說", NULL + }, + { + "yawn", "哈欠", "邊打哈欠邊說", NULL + }, + { + "yell", "大喊", "大喊", NULL + }, + { + NULL, NULL, NULL, NULL + } +}; + + +static int +speak_action(cu, cmd, msg) + ChatUser *cu; + char *cmd; + char *msg; +{ + ChatAction *cap; + char *verb; + + for (cap = speak_data; (verb = cap->verb); cap++) + { + MYDOG; + + if (str_equal(cmd, verb)) + { + sprintf(chatbuf, "[1;32m%s [31m%s:[33m %s[m", + cu->chatid, cap->part1_msg, msg); + send_to_room(cu->room, chatbuf, cu->userno, MSG_MESSAGE); + return 0; /* Thor: cu->room 是否為 NULL? */ + } + } + return 1; +} + + +/* -------------------------------------------- */ +/* MUD-like social commands : condition */ +/* -------------------------------------------- */ + + +static ChatAction condition_data[] = +{ + { + "applaud", "拍手", "啪啪啪啪啪啪啪....", NULL + }, + { + "ayo", "唉呦喂", "唉呦喂~~~", NULL + }, + { + "back", "坐回來", "回來坐正繼續奮戰", NULL + }, + { + "blood", "在血中", "倒在血泊之中", NULL + }, + { + "blush", "臉紅", "臉都紅了", NULL + }, + { + "broke", "心碎", "的心破碎成一片一片的", NULL + }, /* Thor.0731:應觀眾要求 */ + /* {"bokan", "Bo Kan! Bo Kan!", NULL}, */ + { + "careles", "沒人理", "嗚∼∼都沒有人理我 :~~~~", NULL + }, + { + "chew", "嗑瓜子", "很悠閒的嗑起瓜子來了", NULL + }, + { + "climb", "爬山", "自己慢慢爬上山來……", NULL + }, + { + "cold", "感冒了", "感冒了,媽媽不讓我出去玩 :~~~(", NULL + }, + { + "cough", "咳嗽", "咳了幾聲", NULL + }, + { + "die", "暴斃", "當場暴斃", NULL + }, + { + "faint", "昏倒", "當場昏倒", NULL + }, + { + "flop", "香蕉皮", "踩到香蕉皮... 滑倒!", NULL + }, + { + "fly", "飄飄然", "飄飄然", NULL + }, + { + "frown", "蹙眉", "蹙眉", NULL + }, + { + "gold", "拿金牌", "唱著:『金ㄍㄠˊ金ㄍㄠˊ 出國比賽! 得冠軍,拿金牌,光榮倒鄧來!』", NULL + }, + { + "gulu", "肚子餓", "的肚子發出咕嚕~~~咕嚕~~~的聲音", NULL + }, + { + "haha", "哇哈哈", "哇哈哈哈.....^o^", NULL + }, + /* {"haha", "大笑","哇哈哈哈哈哈哈哈哈~~~~!!!!!", NULL}, */ + { + "helpme", "求救", "大喊~~~救命啊~~~~", NULL + }, + { + "hoho", "呵呵笑", "呵呵呵笑個不停", NULL + }, + { + "happy", "高興", "高興得在地上打滾", NULL + }, + /* {"happy", "高興", "YA! *^_^*", NULL}, */ + /* {"happy", "", "r-o-O-m....聽了真爽!", NULL}, */ + /* {"hurricane", "Ho---Ryu--Kan!!!", NULL}, */ + { + "idle", "呆住了", "呆住了", NULL + }, + { + "jacky", "晃晃", "痞子般的晃來晃去", NULL + }, + +#if 0 + /* Thor.0729: 不知其意 */ + { + "lag", "網路慢", "lllllllaaaaaaaaaaaagggggggggggggg.................", NULL + }, +#endif + + { + "luck", "幸運", "哇!福氣啦!", NULL + }, + { + "macarn", "一種舞", "開始跳起了MaCaReNa∼∼∼∼", NULL + }, + { + "miou", "喵喵", "喵喵口苗口苗∼∼∼∼∼", NULL + }, + { + "mouth", "扁嘴", "扁嘴中!!", NULL + }, + { + "nani", "怎麼會", ":奈ㄝ啊捏??", NULL + }, + { + "nose", "流鼻血", "流鼻血", NULL + }, + { + "puke", "嘔吐", "嘔吐中", NULL + }, + /* {"puke", "真噁心,我聽了都想吐", NULL}, */ + { + "rest", "休息", "休息中,請勿打擾", NULL + }, + { + "reverse", "翻肚", "翻肚", NULL + }, + { + "room", "開房間", "r-o-O-m-r-O-O-Mmm-rRR........", NULL + }, + { + "shake", "搖頭", "搖了搖頭", NULL + }, + { + "sleep", "睡著", "趴在鍵盤上睡著了,口水流進鍵盤,造成當機!", NULL + }, + /* {"sleep", "Zzzzzzzzzz,真無聊,都快睡著了", NULL}, */ + { + "so", "就醬子", "就醬子!!", NULL + }, + { + "sorry", "道歉", "嗚啊!!我對不起大家,我對不起國家社會~~~~~~嗚啊~~~~~", NULL + }, + { + "story", "講古", "開始講古了", NULL + }, + { + "strut", "搖擺\走", "大搖大擺\地走", NULL + }, + { + "suicide", "自殺", "自殺", NULL + }, + { + "tea", "泡茶", "泡了壺好茶", NULL + }, + { + "think", "思考", "歪著頭想了一下", NULL + }, + { + "tongue", "吐舌", "吐了吐舌頭", NULL + }, + { + "wall", "撞牆", "跑去撞牆", NULL + }, + { + "wawa", "哇哇", "哇哇哇~~~~~!!!!! ~~~>_<~~~", NULL + }, + /* {"wawa","哇哇哇......>_<",NULL}, */ + { + "www", "汪汪", "汪汪汪!!!", NULL + }, + { + "zzz", "打呼", "呼嚕~~~~ZZzZzzZZZzzZzzzZZ...", NULL + }, + + { + NULL, NULL, NULL, NULL + } +}; + + +static int +condition_action(cu, cmd) + ChatUser *cu; + char *cmd; +{ + ChatAction *cap; + char *verb; + + for (cap = condition_data; (verb = cap->verb); cap++) + { + MYDOG; + + if (str_equal(cmd, verb)) + { + sprintf(chatbuf, "[1;32m%s [31m%s[m", + cu->chatid, cap->part1_msg); + send_to_room(cu->room, chatbuf, cu->userno, MSG_MESSAGE); + return 1; /* Thor: cu->room 是否為 NULL? */ + } + } + return 0; +} + + +/* --------------------------------------------- */ +/* MUD-like social commands : help */ +/* --------------------------------------------- */ + + +static char *dscrb[] = +{ + "[1;37m【 Verb + Nick: 動詞 + 對方名字 】[36m 例://kick piggy[m", + "[1;37m【 Verb + Message:動詞 + 要說的話 】[36m 例://sing 天天天藍[m", + "[1;37m【 Verb:動詞 】 ↑↓:舊話重提[m", NULL +}; +ChatAction *catbl[] = +{ + party_data, speak_data, condition_data, NULL +}; + +static void +chat_partyinfo(cu, msg) + ChatUser *cu; + char *msg; +{ + if (!common_client_command) + return; /* only allow common client to retrieve it */ + + sprintf(chatbuf, "3 動作 交談 狀態"); + send_to_user(cu, chatbuf, 0, MSG_PARTYINFO); +} + +static void +chat_party(cu, msg) + ChatUser *cu; + char *msg; +{ + int kind, i; + ChatAction *cap; + + if (!common_client_command) + return; + + kind = atoi(nextword(&msg)); + if (kind < 0 || kind > 2) + return; + + sprintf(chatbuf, "%d %s", kind, kind == 2 ? "I" : ""); + + /* Xshadow: 只有 condition 才是 immediate mode */ + send_to_user(cu, chatbuf, 0, MSG_PARTYLISTSTART); + + cap = catbl[kind]; + for (i = 0; cap[i].verb; i++) + { + sprintf(chatbuf, "%-10s %-20s", cap[i].verb, cap[i].chinese); + /* for (j=0;j<1000000;j++); */ + send_to_user(cu, chatbuf, 0, MSG_PARTYLIST); + } + + sprintf(chatbuf, "%d", kind); + send_to_user(cu, chatbuf, 0, MSG_PARTYLISTEND); +} + + +#define SCREEN_WIDTH 80 +#define MAX_VERB_LEN 8 +#define VERB_NO 10 + +static void +view_action_verb(cu, cmd) /* Thor.0726: 新加動詞分類顯示 */ + register ChatUser *cu; + char cmd; +{ + register int i; + register char *p, *q, *data, *expn; + register ChatAction *cap; + + send_to_user(cu, "/c", 0, MSG_CLRSCR); + + data = chatbuf; + + if (cmd < '1' || cmd > '3') + { /* Thor.0726: 寫得不好, 想辦法改進... */ + for (i = 0; (p = dscrb[i]); i++) + { + sprintf(data, " [//]help %d - MUD-like 社交動詞 第 %d 類", i + 1, i + 1); + MYDOG; + send_to_user(cu, data, 0, MSG_MESSAGE); + send_to_user(cu, p, 0, MSG_MESSAGE); + send_to_user(cu, " ", 0, MSG_MESSAGE); /* Thor.0726: 換行, 需要 " " + * 嗎? */ + } + } + else + { + i = cmd - '1'; + + send_to_user(cu, dscrb[i], 0, MSG_MESSAGE); + + expn = chatbuf + 100; /* Thor.0726: 應該不會overlap吧? */ + + *data = '\0'; + *expn = '\0'; + + cap = catbl[i]; + + for (i = 0; (p = cap[i].verb); i++) + { + MYDOG; + q = cap[i].chinese; + + strcat(data, p); + strcat(expn, q); + + if (((i + 1) % VERB_NO) == 0) + { + send_to_user(cu, data, 0, MSG_MESSAGE); + send_to_user(cu, expn, 0, MSG_MESSAGE); /* Thor.0726: 顯示中文註解 */ + *data = '\0'; + *expn = '\0'; + } + else + { + strncat(data, " ", MAX_VERB_LEN - strlen(p)); + strncat(expn, " ", MAX_VERB_LEN - strlen(q)); + } + } + if (i % VERB_NO) + { + send_to_user(cu, data, 0, MSG_MESSAGE); + send_to_user(cu, expn, 0, MSG_MESSAGE); /* Thor.0726: 顯示中文註解 */ + } + } + /* send_to_user(cu, " ",0); *//* Thor.0726: 換行, 需要 " " 嗎? */ +} + +void view_chicken_help(cu) /* Ptt: 鬥雞程式 的help */ + register ChatUser *cu; +{ + +} + +/* ----------------------------------------------------- */ +/* chat user service routines */ +/* ----------------------------------------------------- */ + + +static ChatCmd chatcmdlist[] = +{ + {"act", chat_act, 0}, + {"bye", chat_goodbye, 0}, + {"chatroom", chat_chatroom, 1}, /* Xshadow: for common client */ + {"clear", chat_clear, 0}, + {"cloak", chat_cloak, 2}, + {"date", chat_date, 0}, + {"flags", chat_setroom, 0}, + {"help", chat_help, 0}, + {"ignore", chat_ignore, 1}, + {"invite", chat_invite, 0}, + {"join", chat_join, 0}, + {"kick", chat_kick, 1}, + {"msg", chat_private, 0}, + {"nick", chat_nick, 0}, + {"operator", chat_makeop, 0}, + {"party", chat_party, 1}, /* Xshadow: party data for common client */ + {"partyinfo", chat_partyinfo, 1}, /* Xshadow: party info for common + * client */ + + {"query", chat_query, 0}, + + {"room", chat_list_rooms, 0}, + {"unignore", chat_unignore, 1}, + {"whoin", chat_list_by_room, 1}, + {"wall", chat_broadcast, 2}, + + {"who", chat_map_chatids_thisroom, 0}, + {"list", chat_list_users, 0}, + {"topic", chat_topic, 1}, + {"version", chat_version, 1}, + + {NULL, NULL, 0} +}; + +/* Thor: 0 不用 exact, 1 要 exactly equal, 2 秘密指令 */ + + +static int +command_execute(cu) + ChatUser *cu; +{ + char *cmd, *msg; + ChatCmd *cmdrec; + int match, ch; + + msg = cu->ibuf; + match = *msg; + + /* Validation routine */ + + if (cu->room == NULL) + { + /* MUST give special /! or /-! command if not in the room yet */ + + if (match == '/' && ((ch = msg[1]) == '!' || (ch == '-' && msg[2] == '!'))) + { + cu->clitype = (ch == '-') ? 1 : 0; + return (login_user(cu, msg + 2 + cu->clitype)); + } + else + return -1; + } + + /* If not a /-command, it goes to the room. */ + + if (match != '/') + { + if (match) + { + char buf[16]; + + sprintf(buf, "%s:", cu->chatid); + sprintf(chatbuf, "%-10s%s", buf, msg); + if (!CLOAK(cu)) /* Thor: 聊天室隱身術 */ + send_to_room(cu->room, chatbuf, cu->userno, MSG_MESSAGE); + /* Thor: 要 check cu->room NULL嗎? */ + + } + return 0; + } + + msg++; + cmd = nextword(&msg); + match = 0; + + if (*cmd == '/') + { + cmd++; + if (!*cmd || str_equal(cmd, "help")) + { + /* Thor.0726: 動詞分類 */ + cmd = nextword(&msg); + view_action_verb(cu, *cmd); + match = 1; + } + else if (party_action(cu, cmd, msg) == 0) + match = 1; + else if (speak_action(cu, cmd, msg) == 0) + match = 1; + else + match = condition_action(cu, cmd); + } + else if(*cmd == '.') + { + cmd++; + if (!*cmd || str_equal(cmd, "help")) + { + view_chicken_help(cu); + match = 1; + } + else match = chicken_action(cu, cmd, msg); + } + else + { + char *str; + + common_client_command = 0; + if((*cmd == '-')) { + if(cu->clitype) { + cmd++; /* Xshadow: 指令從下一個字元才開始 */ + common_client_command = 1; + } + } + for(cmdrec = chatcmdlist; (str = cmdrec->cmdstr); cmdrec++) + { + MYDOG; + + switch (cmdrec->exact) + { + case 1: /* exactly equal */ + match = str_equal(cmd, str); + break; + case 2: /* Thor: secret command */ + if (CHATSYSOP(cu)) + match = str_equal(cmd, str); + break; + default: /* not necessary equal */ + match = str_match(cmd, str) >= 0; + break; + } + + if (match) + { + cmdrec->cmdfunc(cu, msg); + break; + } + } + } + + if (!match) + { + sprintf(chatbuf, "◆ 指令錯誤:/%s", cmd); + send_to_user(cu, chatbuf, 0, MSG_MESSAGE); + } + return 0; +} + + +/* ----------------------------------------------------- */ +/* serve chat_user's connection */ +/* ----------------------------------------------------- */ + + +static int +cuser_serve(cu) + ChatUser *cu; +{ + register int ch, len, isize; + register char *str, *cmd; + static char buf[80]; + + str = buf; + len = recv(cu->sock, str, sizeof(buf) - 1, 0); + if (len <= 0) + { + /* disconnected */ + + exit_room(cu, EXIT_LOSTCONN, (char *) NULL); + return -1; + } + +#if 0 + /* Xshadow: 將送達的資料忠實紀錄下來 */ + memcpy(logbuf, buf, sizeof(buf)); + for (ch = 0; ch < sizeof(buf); ch++) + if (!logbuf[ch]) + logbuf[ch] = '$'; + + logbuf[len + 1] = '\0'; + logit("recv: ", logbuf); +#endif + +#if 0 + logit(cu->userid, str); +#endif + + isize = cu->isize; + cmd = cu->ibuf + isize; + while (len--) + { + MYDOG; + + ch = *str++; + + if (ch == '\r' || !ch) + continue; + if (ch == '\n') + { + *cmd = '\0'; + + isize = 0; + cmd = cu->ibuf; + + if (command_execute(cu) < 0) + return -1; + + continue; + } + if (isize < 79) + { + *cmd++ = ch; + isize++; + } + } + cu->isize = isize; + return 0; +} + + +/* ----------------------------------------------------- */ +/* chatroom server core routines */ +/* ----------------------------------------------------- */ + +static int +start_daemon() +{ + int fd, value; + char buf[80]; + struct sockaddr_in fsin; + struct linger ld; + struct rlimit limit; + time_t dummy; + struct tm *dummy_time; + + /* + * 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(&dummy); + dummy_time = gmtime(&dummy); + dummy_time = localtime(&dummy); + strftime(buf, 80, "%d/%b/%Y:%H:%M:%S", dummy_time); + + /* --------------------------------------------------- */ + /* speed-hacking DNS resolve */ + /* --------------------------------------------------- */ + + gethostname(buf, sizeof(buf)); + + /* Thor: 萬一server尚未接受connection, 就回去的話, client 第一次會進入失敗 */ + /* 所以移至 listen 後 */ + + /* --------------------------------------------------- */ + /* detach daemon process */ + /* --------------------------------------------------- */ + + close(0); + close(1); + close(2); + + if (fork()) + exit(0); + + chdir(BBSHOME); + + setsid(); + + /* --------------------------------------------------- */ + /* adjust the resource limit */ + /* --------------------------------------------------- */ + + getrlimit(RLIMIT_NOFILE, &limit); + limit.rlim_cur = limit.rlim_max; + setrlimit(RLIMIT_NOFILE, &limit); + +#if 0 + while (fd) + { + close(--fd); + } + + value = getpid(); + setpgrp(0, value); + + if ((fd = open("/dev/tty", O_RDWR)) >= 0) + { + ioctl(fd, TIOCNOTTY, 0); /* Thor : 為什麼還要用 tty? */ + close(fd); + } +#endif + + fd = open(CHAT_PIDFILE, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd >= 0) + { + /* sprintf(buf, "%5d\n", value); */ + sprintf(buf, "%5d\n", getpid()); + write(fd, buf, 6); + close(fd); + } + +#if 0 + /* ------------------------------ */ + /* trap signals */ + /* ------------------------------ */ + + for (fd = 1; fd < NSIG; fd++) + { + + signal(fd, SIG_IGN); + } +#endif + + fd = socket(PF_INET, SOCK_STREAM, 0); + +#if 0 + value = fcntl(fd, F_GETFL, 0); + fcntl(fd, F_SETFL, value | O_NDELAY); +#endif + + value = 1; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &value, sizeof(value)); + +#if 0 + setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &value, sizeof(value)); + + value = 81920; + setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &value, sizeof(value)); +#endif + + ld.l_onoff = ld.l_linger = 0; + setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &ld, sizeof(ld)); + + memset((char *) &fsin, 0, sizeof(fsin)); + fsin.sin_family = AF_INET; + fsin.sin_port = htons(NEW_CHATPORT); + fsin.sin_addr.s_addr = htonl(INADDR_ANY); + + if (bind(fd, (struct sockaddr *) & fsin, sizeof(fsin)) < 0) + exit(1); + + listen(fd, SOCK_QLEN); + + return fd; +} + + +static void +free_resource(fd) + int fd; +{ + static int loop = 0; + register ChatUser *user; + register int sock, num; + + num = 0; + for (user = mainuser; user; user = user->unext) + { + MYDOG; + + num++; + sock = user->sock; + if (fd < sock) + fd = sock; + } + + sprintf(chatbuf, "%d, %d user (%d -> %d)", ++loop, num, maxfds, fd); + logit("LOOP", chatbuf); + + maxfds = fd + 1; +} + + +#ifdef SERVER_USAGE +static void +server_usage() +{ + struct rusage ru; + char buf[2048]; + + if (getrusage(RUSAGE_SELF, &ru)) + return; + + sprintf(buf, "\n[Server Usage]\n\n" + "user time: %.6f\n" + "system time: %.6f\n" + "maximum resident set size: %lu P\n" + "integral resident set size: %lu\n" + "page faults not requiring physical I/O: %ld\n" + "page faults requiring physical I/O: %ld\n" + "swaps: %ld\n" + "block input operations: %ld\n" + "block output operations: %ld\n" + "messages sent: %ld\n" + "messages received: %ld\n" + "signals received: %ld\n" + "voluntary context switches: %ld\n" + "involuntary context switches: %ld\n" + "gline: %d\n\n", + + (double) ru.ru_utime.tv_sec + (double) ru.ru_utime.tv_usec / 1000000.0, + (double) ru.ru_stime.tv_sec + (double) ru.ru_stime.tv_usec / 1000000.0, + ru.ru_maxrss, + ru.ru_idrss, + ru.ru_minflt, + ru.ru_majflt, + ru.ru_nswap, + ru.ru_inblock, + ru.ru_oublock, + ru.ru_msgsnd, + ru.ru_msgrcv, + ru.ru_nsignals, + ru.ru_nvcsw, + ru.ru_nivcsw, + gline); + + write(flog, buf, strlen(buf)); +} +#endif + + +static void +abort_server() +{ + log_close(); + exit(1); +} + + +static void +reaper() +{ + int state; + + while (waitpid(-1, &state, WNOHANG | WUNTRACED) > 0) + { + MYDOG; + } +} + + +int +main() +{ + register int msock, csock, nfds; + register ChatUser *cu; + register fd_set *rptr, *xptr; + fd_set rset, xset; + struct timeval tv; + time_t uptime, tmaintain; + + msock = start_daemon(); + + setgid(BBSGID); + setuid(BBSUID); + + log_init(); + + signal(SIGBUS, SIG_IGN); + signal(SIGSEGV, SIG_IGN); + signal(SIGPIPE, SIG_IGN); + signal(SIGURG, SIG_IGN); + + signal(SIGCHLD, reaper); + signal(SIGTERM, abort_server); + +#ifdef SERVER_USAGE + signal(SIGPROF, server_usage); +#endif + + /* ----------------------------- */ + /* init variable : rooms & users */ + /* ----------------------------- */ + + mainuser = NULL; + memset(&mainroom, 0, sizeof(mainroom)); + strcpy(mainroom.name, MAIN_NAME); + strcpy(mainroom.topic, MAIN_TOPIC); + + /* ----------------------------------- */ + /* main loop */ + /* ----------------------------------- */ + +#if 0 + /* Thor: 在listen 後才回client, 每次進來就會成功 */ + if (fork()) + exit(0); +#endif + + FD_ZERO(&mainfds); + FD_SET(msock, &mainfds); + rptr = &rset; + xptr = &xset; + maxfds = msock + 1; + + tmaintain = time(0) + CHAT_INTERVAL; + + for (;;) + { + uptime = time(0); + if (tmaintain < uptime) + { + tmaintain = uptime + CHAT_INTERVAL; + + /* client/server 版本利用 ping-pong 方法判斷 user 是不是還活著 */ + /* 如果 client 已經結束了,就釋放其 resource */ + + free_resource(msock); + } + + MYDOG; + + memcpy(rptr, &mainfds, sizeof(fd_set)); + memcpy(xptr, &mainfds, sizeof(fd_set)); + + /* Thor: for future reservation bug */ + + tv.tv_sec = CHAT_INTERVAL; + tv.tv_usec = 0; + + MYDOG; + + nfds = select(maxfds, rptr, NULL, xptr, &tv); + + MYDOG; + /* free idle user & chatroom's resource when no traffic */ + + if (nfds == 0) + { + continue; + } + + /* check error condition */ + + if (nfds < 0) + { + csock = errno; + continue; + } + + /* accept new connection */ + + if (FD_ISSET(msock, rptr)) + { + for (;;) + { + MYDOG; /* Thor: check for endless */ + csock = accept(msock, NULL, NULL); + + if (csock >= 0) + { + MYDOG; + if((cu = (ChatUser *) malloc(sizeof(ChatUser)))) + { + memset(cu, 0, sizeof(ChatUser)); + cu->sock = csock; + + cu->unext = mainuser; + mainuser = cu; + +#if 0 + if (mainuser.next) + mainuser.next->prev = cu; + cu->next = mainuser.next; + mainuser.next = cu; + cu->prev = &mainuser; +#endif + + totaluser++; + FD_SET(csock, &mainfds); + if (csock >= maxfds) + maxfds = csock + 1; + +#ifdef DEBUG + logit("accept", "OK"); +#endif + } + else + { + close(csock); + logit("accept", "malloc fail"); + } + MYDOG; + + break; + } + + csock = errno; + if (csock != EINTR) + { + break; + } + } + + FD_CLR(msock, rptr); + + if (--nfds <= 0) + continue; + } + + for (cu = mainuser; cu; cu = cu->unext) + { + MYDOG; + + csock = cu->sock; + + if (FD_ISSET(csock, xptr)) + { + logout_user(cu); + FD_CLR(csock, xptr); + } + else if (FD_ISSET(csock, rptr)) + { + if (cuser_serve(cu) < 0) + logout_user(cu); + } + else + { + continue; + } + + FD_CLR(csock, rptr); + if (--nfds <= 0) + break; + } + + /* end of main loop */ + } +} |