精華區beta SetupBBS 關於我們 聯絡資訊
/*-------------------------------------------------------*/ /* xchatd.c ( NTHU CS MapleBBS Ver 2.36 ) */ /*-------------------------------------------------------*/ /* target : super KTV daemon for chat server */ /* create : 95/03/29 */ /* update : 95/12/15 */ /*-------------------------------------------------------*/ #include "bbs.h" #include <sys/ioctl.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #ifdef LINUX #include <unistd.h> #else #include <sys/uio.h> #endif #ifdef AIX #include <sys/select.h> #endif #if !RELIABLE_SELECT_FOR_WRITE #include <fcntl.h> #endif #if USES_SYS_SELECT_H #include <sys/select.h> #endif #if NO_SETPGID #define setpgid setpgrp #endif #define RESTRICTED(u) (users[(u)].flags == 0) /* guest */ #define SYSOP(u) (users[(u)].flags & PERM_SYSOP) #define HIDE(u) (SYSOP(u) && (users[(u)].flags & PERM_DENYPOST)) #define ROOMOP(u) (users[(u)].flags & PERM_CHATROOM) #define ROOM_LOCKED 0x1 #define ROOM_SECRET 0x2 #define LOCKED(r) (rooms[(r)].flags & ROOM_LOCKED) #define SECRET(r) (rooms[(r)].flags & ROOM_SECRET) #define ROOM_ALL (-2) struct chatuser { int sockfd; /* socket to bbs server */ int utent; /* utable entry for this user */ int room; /* room: -1 means none, 0 means main */ int flags; char userid[IDLEN + 1]; /* real userid */ char chatid[9]; /* chat id */ char ibuf[80]; /* buffer for sending/receiving */ int ibufsize; /* current size of ibuf */ } users[MAXACTIVE]; struct chatroom { char name[IDLEN]; /* name of room; room 0 is "main" */ short occupants; /* number of users in room */ short flags; /* ROOM_LOCKED, ROOM_SECRET */ char invites[MAXACTIVE]; /* Keep track of invites to rooms */ char topic[48]; /* Let the room op to define room topic */ } rooms[MAXROOM]; struct chatcmd { char *cmdstr; void (*cmdfunc) (); int exact; }; int sock = -1; /* the socket for listening */ int nfds; /* number of sockets to select on */ int num_conns; /* current number of connections */ fd_set allfds; /* fd set for selecting */ struct timeval zerotv; /* timeval for selecting */ char chatbuf[256]; /* general purpose buffer */ /* name of the main room (always exists) */ char mainroom[] = "PttHouse"; char maintopic[] = "哇啦啦"; char *msg_not_op = "◆ 您不是這間聊天室的 Op"; char *msg_no_such_id = "◆ 目前沒有人使用 [%s] 這個聊天代號"; char *msg_not_here = "◆ [%s] 不在這間聊天室"; #define HAVE_REPORT #ifdef HAVE_REPORT report(s) char *s; { static int disable = NA; int fd; if (disable) return; if ((fd = open("trace.chatd", O_WRONLY, 0644)) != -1) { char buf[160]; flock(fd, LOCK_EX); lseek(fd, 0, L_XTND); sprintf(buf, "%s\n", s); write(fd, buf, strlen(buf)); flock(fd, LOCK_UN); close(fd); return; } disable = YEA; return; } #else #define report(s) ; #endif is_valid_chatid(id) char *id; { int i, ch; /* woju */ if (*id == '\0' || *id == '/' || *id == '@' || *id == '>') return 0; for (i = 0; (i < 8) && (ch = id[i]); i++) if (ch == '*' || ch == ':') return 0; return 1; } int Isspace(ch) char ch; { return (ch == ' ' || ch == '\t' || ch == 10); } char * nextword(str) char **str; { char *p; while (Isspace(**str)) (*str)++; p = *str; while (**str && !Isspace(**str)) (*str)++; if (**str) { **str = '\0'; (*str)++; } return p; } /* Case Independent strcmp : 1 ==> euqal */ int ci_strcmp(s1, s2) register char *s1, *s2; { register char c1, c2; while (1) { c1 = *s1++; if (c1 >= 'A' && c1 <= 'Z') c1 |= 32; c2 = *s2++; if (c2 >= 'A' && c2 <= 'Z') c2 |= 32; if (c1 != c2) return 0; } return 1; } int chatid_to_indx(chatid) char *chatid; { register int i; for (i = 0; i < MAXACTIVE; i++) { if (users[i].sockfd == -1) continue; if (!strcasecmp(chatid, users[i].chatid)) return i; } return -1; } int fuzzy_chatid_to_indx(chatid) char *chatid; { register int i, indx = -1; int len = strlen(chatid); for (i = 0; i < MAXACTIVE; i++) { if (users[i].sockfd == -1) continue; if (!strncasecmp(chatid, users[i].chatid, len)) { if (len == strlen(users[i].chatid)) return i; if (indx == -1) indx = i; else return -2; } } return indx; } int roomid_to_indx(roomid) char *roomid; { register int i; for (i = 0; i < MAXROOM; i++) { if (i && rooms[i].occupants == 0) continue; report(roomid); report(rooms[i].name); if (!strcasecmp(roomid, rooms[i].name)) return i; } return -1; } void do_send(writefds, str) fd_set *writefds; char *str; { register int i; int len = strlen(str); if (select(nfds, NULL, writefds, NULL, &zerotv) > 0) { for (i = 0; i < nfds; i++) if (FD_ISSET(i, writefds)) send(i, str, len + 1, 0); } } void send_to_room(room, str) int room; char *str; { int i; fd_set writefds; FD_ZERO(&writefds); for (i = 0; i < MAXROOM; i++) { if (users[i].sockfd == -1) continue; if (room == ROOM_ALL || room == users[i].room) /* write(users[i].sockfd, str, strlen(str) + 1); */ FD_SET(users[i].sockfd, &writefds); } do_send(&writefds, str); } void send_to_room_hide(room, str1) int room; char *str1; { int i; fd_set writefds; char str[200]; sprintf(str, ">%s", str1); FD_ZERO(&writefds); for (i = 0; i < MAXROOM; i++) { if (users[i].sockfd == -1 || !HIDE(i)) continue; if (room == ROOM_ALL || room == users[i].room) FD_SET(users[i].sockfd, &writefds); } do_send(&writefds, str); } void send_to_unum(unum, str) int unum; char *str; { fd_set writefds; FD_ZERO(&writefds); FD_SET(users[unum].sockfd, &writefds); do_send(&writefds, str); } void exit_room(unum, disp, msg) int unum; int disp; char *msg; { int oldrnum = users[unum].room; if (oldrnum != -1) { if (--rooms[oldrnum].occupants) { sprintf(chatbuf, "%s EXIT", users[unum].userid); send_to_room_hide(users[unum].room, chatbuf); switch (disp) { case EXIT_LOGOUT: sprintf(chatbuf, "◆ %s 離開了", users[unum].chatid); if (msg && *msg) { strcat(chatbuf, ": "); strncat(chatbuf, msg, 80); } break; case EXIT_LOSTCONN: sprintf(chatbuf, "◆ %s 不見了... :~(", users[unum].chatid); break; case EXIT_KICK: sprintf(chatbuf, "◆ 哈哈!%s 被踢出去了", users[unum].chatid); break; } if (!HIDE(unum)) send_to_room(oldrnum, chatbuf); } } users[unum].flags &= ~PERM_CHATROOM; users[unum].room = -1; } void chat_topic(unum, msg) int unum; char *msg; { int rnum; rnum = users[unum].room; if (!ROOMOP(unum) && !SYSOP(unum)) { send_to_unum(unum, msg_not_op); return; } if (*msg == '\0') { send_to_unum(unum, "※ 請指定話題"); return; } sprintf(chatbuf, "%s TOPIC", users[unum].userid); send_to_room_hide(users[unum].room, chatbuf); strncpy(rooms[rnum].topic, msg, 48); rooms[rnum].topic[48] = '\0'; sprintf(chatbuf, "/t%.47s", msg); send_to_room(rnum, chatbuf); sprintf(chatbuf, "◆ %s 將話題改為 %s", users[unum].chatid, msg); send_to_room(rnum, chatbuf); } enter_room(unum, room, msg) int unum; char *room; char *msg; { int rnum; int op = 0; register int i; rnum = roomid_to_indx(room); if (rnum == -1) { /* new room */ for (i = 1; i < MAXROOM; i++) { if (rooms[i].occupants == 0) { report("new room"); rnum = i; memset(rooms[rnum].invites, 0, MAXACTIVE); strcpy(rooms[rnum].topic, maintopic); strncpy(rooms[rnum].name, room, IDLEN - 1); rooms[rnum].name[IDLEN - 1] = '\0'; rooms[rnum].flags = 0; op++; break; } } if (rnum == -1) { send_to_unum(unum, "※ 無法再新闢包廂了"); return 0; } } if (!SYSOP(unum)) if (LOCKED(rnum) && rooms[rnum].invites[unum] == 0) { send_to_unum(unum, "※ 內有惡犬,非請莫入"); return 0; } exit_room(unum, EXIT_LOGOUT, msg); users[unum].room = rnum; if (op || SYSOP(unum)) users[unum].flags |= PERM_CHATROOM; rooms[rnum].occupants++; rooms[rnum].invites[unum] = 0; sprintf(chatbuf, "%s ENTER", users[unum].userid); send_to_room_hide(users[unum].room, chatbuf); sprintf(chatbuf, "※ %s 進入 [%s] 包廂", users[unum].chatid, rooms[rnum].name); if (HIDE(unum)) send_to_unum(unum, chatbuf); else send_to_room(rnum, chatbuf); sprintf(chatbuf, "/r%s", room); send_to_unum(unum, chatbuf); sprintf(chatbuf, "/t%s", maintopic); send_to_unum(unum, chatbuf); return 0; } void logout_user(unum) { int i, sockfd = users[unum].sockfd; close(sockfd); FD_CLR(sockfd, &allfds); memset(&users[unum], 0, sizeof(users[unum])); users[unum].sockfd = users[unum].utent = users[unum].room = -1; for (i = 0; i < MAXROOM; i++) { if (rooms[i].invites != NULL) rooms[i].invites[unum] = 0; } num_conns--; } print_user_counts(unum) { int i, c, userc = 0, suserc = 0, roomc = 0; for (i = 0; i < MAXROOM; i++) { c = rooms[i].occupants; if (i > 0 && c > 0) { if ((!HIDE(i) || HIDE(unum)) && (!SECRET(i) || SYSOP(unum))) roomc++; } c = users[i].room; if (users[i].sockfd != -1 && c != -1) { if (SECRET(c) && !SYSOP(unum)) suserc++; else if (!HIDE(c) || HIDE(unum)) userc++; } } sprintf(chatbuf, "⊙ 歡迎光臨【小歇茶坊】,目前開了 %d 間包廂", roomc + 1); send_to_unum(unum, chatbuf); sprintf(chatbuf, "⊙ 共有 %d 人來擺\龍門陣", userc + 1); if (suserc) sprintf(chatbuf + strlen(chatbuf), " [%d 人在摸摸茶]", suserc); send_to_unum(unum, chatbuf); return 0; } int login_user(unum, msg) int unum; char *msg; { int i, utent, fd = users[unum].sockfd; char *utentstr; char *level; char *userid; char *chatid; utentstr = nextword(&msg); level = nextword(&msg); userid = nextword(&msg); chatid = nextword(&msg); utent = atoi(utentstr); for (i = 0; i < MAXACTIVE; i++) { if (users[i].sockfd != -1 && users[i].utent == utent) { send_to_unum(unum, CHAT_LOGIN_BOGUS); return -1; } } if (!is_valid_chatid(chatid)) { send_to_unum(unum, CHAT_LOGIN_INVALID); return 0; } if (chatid_to_indx(chatid) != -1) { /* userid in use */ send_to_unum(unum, CHAT_LOGIN_EXISTS); return 0; } report(level); users[unum].utent = utent; users[unum].flags = atoi(level); strcpy(users[unum].userid, userid); strncpy(users[unum].chatid, chatid, 8); users[unum].chatid[8] = '\0'; send_to_unum(unum, CHAT_LOGIN_OK); print_user_counts(unum); enter_room(unum, mainroom, (char *) NULL); return 0; } void chat_list_rooms(unum, msg) int unum; char *msg; { int i, occupants; if (RESTRICTED(unum)) { send_to_unum(unum, "※ 您沒有權限列出現有的聊天室"); return; } send_to_unum(unum, " 談天室名稱 │人數│話題 "); for (i = 0; i < MAXROOM; i++) { occupants = rooms[i].occupants; if (occupants > 0) { if (!SYSOP(unum)) if ((rooms[i].flags & ROOM_SECRET) && (users[unum].room != i)) continue; sprintf(chatbuf, " %-12s│%4d│%s", rooms[i].name, occupants, rooms[i].topic); if (rooms[i].flags & ROOM_LOCKED) strcat(chatbuf, " [鎖住]"); if (rooms[i].flags & ROOM_SECRET) strcat(chatbuf, " [秘密]"); send_to_unum(unum, chatbuf); } } } int chat_do_user_list(unum, msg, whichroom) int unum; char *msg; int whichroom; { int start, stop, curr = 0; int i, rnum, myroom = users[unum].room; while (*msg && Isspace(*msg)) msg++; start = atoi(msg); while (*msg && isdigit(*msg)) msg++; while (*msg && !isdigit(*msg)) msg++; stop = atoi(msg); send_to_unum(unum, " 聊天代號│使用者代號 │聊天室 "); for (i = 0; i < MAXROOM; i++) { rnum = users[i].room; if (users[i].sockfd != -1 && rnum != -1) { if (whichroom != -1 && whichroom != rnum) continue; if (myroom != rnum) { if (RESTRICTED(unum)) continue; if ((rooms[rnum].flags & ROOM_SECRET) && !SYSOP(unum)) continue; } curr++; if (curr < start) continue; else if (stop && (curr > stop)) break; sprintf(chatbuf, " %-8s│%-12s│%s", users[i].chatid, users[i].userid, rooms[rnum].name); if (ROOMOP(i)) strcat(chatbuf, " [Op]"); if (!HIDE(i) || HIDE(unum)) send_to_unum(unum, chatbuf); } } return 0; } void chat_list_by_room(unum, msg) int unum; char *msg; { int whichroom; char *roomstr; roomstr = nextword(&msg); if (*roomstr == '\0') whichroom = users[unum].room; else { if ((whichroom = roomid_to_indx(roomstr)) == -1) { sprintf(chatbuf, "※ 沒有 [%s] 這個聊天室", roomstr); send_to_unum(unum, chatbuf); return; } if ((rooms[whichroom].flags & ROOM_SECRET) && !SYSOP(unum)) { send_to_unum(unum, "※ 無法列出在秘密聊天室的使用者"); return; } } chat_do_user_list(unum, msg, whichroom); } void chat_list_users(unum, msg) int unum; char *msg; { chat_do_user_list(unum, msg, -1); } void chat_map_chatids(unum, whichroom) int unum; int whichroom; { int i, c, myroom, rnum; myroom = users[unum].room; send_to_unum(unum, " 聊天代號 使用者代號 │ 聊天代號 使用者代號 │ 聊天代號 使用者代號 "); for (i = 0, c = 0; i < MAXROOM; i++) { rnum = users[i].room; if (users[i].sockfd != -1 && rnum != -1) { if (whichroom != -1 && whichroom != rnum) continue; if (myroom != rnum) { if (RESTRICTED(unum)) continue; if ((rooms[rnum].flags & ROOM_SECRET) && !SYSOP(unum)) continue; } if (!HIDE(unum) && HIDE(i)) continue; sprintf(chatbuf + (c * 24), " %-8s%c%-12s%s", users[i].chatid, (ROOMOP(i)) ? '*' : ' ', users[i].userid, (c < 2 ? "│" : " ")); if (++c == 3) { send_to_unum(unum, chatbuf); c = 0; } } } if (c > 0) send_to_unum(unum, chatbuf); } void chat_map_chatids_thisroom(unum, msg) int unum; char *msg; { chat_map_chatids(unum, users[unum].room); } void chat_setroom(unum, msg) int unum; char *msg; { char *modestr; int rnum = users[unum].room; int sign = 1; int flag; char *fstr; modestr = nextword(&msg); if (!ROOMOP(unum)) { send_to_unum(unum, msg_not_op); return; } if (*modestr == '+') modestr++; else if (*modestr == '-') { modestr++; sign = 0; } if (*modestr == '\0') { send_to_unum(unum, "※ 請指定狀態指標: {[+(設定)][-(取消)]}{[l(鎖住)][s(秘密)]}"); return; } while (*modestr) { flag = 0; switch (*modestr) { case 'l': case 'L': flag = ROOM_LOCKED; fstr = "鎖住"; break; case 's': case 'S': flag = ROOM_SECRET; fstr = "秘密"; break; default: sprintf(chatbuf, "※ 狀態指標錯誤:[%c]", *modestr); send_to_unum(unum, chatbuf); } if (flag && ((rooms[rnum].flags & flag) != sign * flag)) { rooms[rnum].flags ^= flag; sprintf(chatbuf, "%s SETROOM", users[unum].userid); send_to_room_hide(users[unum].room, chatbuf); sprintf(chatbuf, "※ 本聊天室被 %s %s%s狀態", users[unum].chatid, sign ? "設定為" : "取消", fstr); send_to_room(rnum, chatbuf); } modestr++; } } void chat_nick(unum, msg) int unum; char *msg; { char *chatid; int othernum; chatid = nextword(&msg); if (!is_valid_chatid(chatid)) { send_to_unum(unum, "※ 這個聊天代號是不正確的"); return; } othernum = chatid_to_indx(chatid); if (othernum != -1 && othernum != unum) { send_to_unum(unum, "※ 已經有人捷足先登囉"); return; } chatid[8] = '\0'; sprintf(chatbuf, "%s NICK", users[unum].userid); send_to_room_hide(users[unum].room, chatbuf); sprintf(chatbuf, "※ %s 將聊天代號改為 %s", users[unum].chatid, chatid); if (!HIDE(unum)) send_to_room(users[unum].room, chatbuf); strcpy(users[unum].chatid, chatid); sprintf(chatbuf, "/n%s", users[unum].chatid); send_to_unum(unum, chatbuf); } void chat_private(unum, msg) int unum; char *msg; { char *recipient; int recunum; recipient = nextword(&msg); recunum = fuzzy_chatid_to_indx(recipient); if (recunum < 0) { /* no such user, or ambiguous */ if (recunum == -1) /* woju sprintf(chatbuf, msg_no_such_id, recipient); */ sprintf(chatbuf, "@%s@%s", recipient, msg); else sprintf(chatbuf, "※ 請指明聊天代號", recipient); send_to_unum(unum, chatbuf); return; } if (*msg) { sprintf(chatbuf, "*%s* ", users[unum].chatid); strncat(chatbuf, msg, 80); send_to_unum(recunum, chatbuf); sprintf(chatbuf, "%s> ", users[recunum].chatid); strncat(chatbuf, msg, 80); send_to_unum(unum, chatbuf); } } put_chatid(unum, str) int unum; char *str; { int i; char *chatid = users[unum].chatid; memset(str, ' ', 10); for (i = 0; chatid[i]; i++) str[i] = chatid[i]; str[i] = ':'; str[10] = '\0'; } void chat_allmsg(unum, msg) int unum; char *msg; { if (*msg) { put_chatid(unum, chatbuf); strcat(chatbuf, msg); send_to_room(users[unum].room, chatbuf); } } void chat_act(unum, msg) int unum; char *msg; { if (*msg) { sprintf(chatbuf, "%s %s", users[unum].chatid, msg); send_to_room(users[unum].room, chatbuf); } } void chat_join(unum, msg) int unum; char *msg; { if (RESTRICTED(unum)) { send_to_unum(unum, "※ 您沒有加入其他聊天室的權限"); } else { char *roomid = nextword(&msg); if (*roomid) enter_room(unum, roomid, msg); else send_to_unum(unum, "※ 請指定聊天室的名字"); } } void chat_kick(unum, msg) int unum; char *msg; { char *twit; int rnum = users[unum].room; int recunum; twit = nextword(&msg); if (!ROOMOP(unum) && !SYSOP(unum)) { send_to_unum(unum, msg_not_op); return; } if ((recunum = chatid_to_indx(twit)) == -1 || HIDE(recunum)) { sprintf(chatbuf, msg_no_such_id, twit); send_to_unum(unum, chatbuf); return; } if (rnum != users[recunum].room) { sprintf(chatbuf, msg_not_here, users[recunum].chatid); send_to_unum(unum, chatbuf); return; } sprintf(chatbuf, "%s KICK %s", users[unum].userid, users[recunum].userid); send_to_room_hide(users[unum].room, chatbuf); exit_room(recunum, EXIT_KICK, (char *) NULL); if (rnum == 0) logout_user(recunum); else enter_room(recunum, mainroom, (char *) NULL); } void chat_makeop(unum, msg) int unum; char *msg; { char *newop = nextword(&msg); int rnum = users[unum].room; int recunum; if (!ROOMOP(unum)) { send_to_unum(unum, msg_not_op); return; } if ((recunum = chatid_to_indx(newop)) == -1) { /* no such user */ sprintf(chatbuf, msg_no_such_id, newop); send_to_unum(unum, chatbuf); return; } if (unum == recunum) { sprintf(chatbuf, "※ 您早就已經是 Op 了啊"); send_to_unum(unum, chatbuf); return; } if (rnum != users[recunum].room) { sprintf(chatbuf, msg_not_here, users[recunum].chatid); send_to_unum(unum, chatbuf); return; } users[unum].flags &= ~PERM_CHATROOM; users[recunum].flags |= PERM_CHATROOM; sprintf(chatbuf, "%s MAKEOP %s", users[unum].userid, users[recunum].userid); send_to_room_hide(users[unum].room, chatbuf); sprintf(chatbuf, "※ %s 將 Op 權力轉移給 %s", users[unum].chatid, users[recunum].chatid); send_to_room(rnum, chatbuf); } void chat_invite(unum, msg) int unum; char *msg; { char *invitee = nextword(&msg); int rnum = users[unum].room; int recunum; if (!ROOMOP(unum)) { send_to_unum(unum, msg_not_op); return; } if ((recunum = chatid_to_indx(invitee)) == -1) { sprintf(chatbuf, msg_not_here, invitee); send_to_unum(unum, chatbuf); return; } if (rooms[rnum].invites[recunum] == 1) { sprintf(chatbuf, "※ %s 已經接受過邀請了", users[recunum].chatid); send_to_unum(unum, chatbuf); return; } rooms[rnum].invites[recunum] = 1; sprintf(chatbuf, "※ %s 邀請您到 [%s] 聊天室", users[unum].chatid, rooms[rnum].name); send_to_unum(recunum, chatbuf); sprintf(chatbuf, "※ %s 收到您的邀請了", users[recunum].chatid); send_to_unum(unum, chatbuf); } void chat_broadcast(unum, msg) int unum; char *msg; { if (!SYSOP(unum)) { send_to_unum(unum, "※ 您沒有在聊天室廣播的權力!"); return; } if (*msg == '\0') { send_to_unum(unum, "※ 請指定廣播內容"); return; } sprintf(chatbuf, "%s BROADCAST", users[unum].userid); send_to_room_hide(ROOM_ALL, chatbuf); sprintf(chatbuf, "※ " BOARDNAME "談天室廣播中 [%s].....", users[unum].chatid); send_to_room(ROOM_ALL, chatbuf); sprintf(chatbuf, "◆ %s", msg); send_to_room(ROOM_ALL, chatbuf); } void chat_goodbye(unum, msg) int unum; char *msg; { exit_room(unum, EXIT_LOGOUT, msg); } /* -------------------------------------------- */ /* MUD-like social commands : action */ /* -------------------------------------------- */ struct action { char *verb; /* 動詞 */ char *part1_msg; /* 介詞 */ char *part2_msg; /* 動作 */ }; struct action party_data[] = { {"1", "拿出電鋸,朝", "的那裡鋸了下去..."}, {"2", "開始對", "打手槍"}, {"3", "碰了一聲,", "啊~~了一聲!"}, {"4", "挺起機槍,把", "打成蜂窩"}, {"5", "駕著卡車,然後突然對", "右轉..."}, {"6", "bon quey 了!拿起六號衝向", ""}, {"7", "對", "拋了一個媚眼,把他看死了..."}, {"8", "按了 pause,然後開始對", "破口大罵"}, {"bearhug", "熱情的擁抱", ""}, {"bless", "祝福", "心想事成"}, {"bow", "畢躬畢敬的向", "鞠躬"}, {"caress", "清輕的撫摸著", ""}, {"cry", "向", "嚎啕大哭"}, {"comfort", "溫言安慰", ""}, {"clap", "向", "熱烈鼓掌"}, {"dance", "拉了", "的手翩翩起舞"}, {"dogleg", "對", "狗腿"}, {"drivel", "對著", "流口水"}, {"french", "給", "一個法國式的吻"}, {"giggle", "對著", "傻傻的呆笑"}, {"grin", "對", "露出邪惡的笑容"}, {"growl", "對", "咆哮不已"}, {"hand", "跟", "握手"}, {"hug", "輕輕地擁抱", ""}, {"jab", "溫柔的戳著", ""}, {"kick", "把", "踢的死去活來"}, {"kiss", "給", "一個溫柔的吻"}, {"laugh", "大聲嘲笑", ""}, {"marry", "捧著九百九十九朵玫瑰向", "求婚"}, {"nod", "向", "點頭稱是"}, {"nudge", "用手肘頂", "的肥肚子"}, {"pad", "輕拍", "的肩膀"}, {"pinch", "用力的把", "擰的黑青"}, {"punch", "狠狠揍了", "一頓"}, {"shrug", "無奈地向", "聳了聳肩膀"}, {"sigh", "對", "歎了一口氣"}, {"slap", "啪啪的巴了", "一頓耳光"}, {"smooch", "擁吻著", ""}, {"snicker", "嘿嘿嘿..的對", "竊笑"}, {"sniff", "對", "嗤之以鼻"}, {"sob", "對", "憤憤的說 You S...un Of B...each!"}, {"spank", "用巴掌打", "的臀部"}, {"squeeze", "緊緊地擁抱著", ""}, {"thank", "向", "道謝"}, {"tickle", "咕嘰!咕嘰!搔", "的癢"}, {"wave", "對著", "拼命的搖手"}, {"wink", "對", "神秘的眨眨眼睛"}, {"zap", "對", "瘋狂的攻擊"}, {NULL, NULL, NULL} }; int party_action(unum, cmd, party) int unum; char *cmd; char *party; { int i; for (i = 0; party_data[i].verb; i++) { if (!strcmp(cmd, party_data[i].verb)) { if (*party == '\0') { party = "大家"; } else { int recunum = fuzzy_chatid_to_indx(party); if (recunum < 0) { /* no such user, or ambiguous */ if (recunum == -1) sprintf(chatbuf, msg_no_such_id, party); else sprintf(chatbuf, "※ 請指明聊天代號", party); send_to_unum(unum, chatbuf); return 0; } party = users[recunum].chatid; } sprintf(chatbuf, "%s %s %s %s", users[unum].chatid, party_data[i].part1_msg, party, party_data[i].part2_msg); send_to_room(users[unum].room, chatbuf); return 0; } } return 1; } /* -------------------------------------------- */ /* MUD-like social commands : speak */ /* -------------------------------------------- */ struct action speak_data[] = { {"ask", "詢問", NULL}, {"chant", "歌頌", NULL}, {"cheer", "喝采", NULL}, {"curse", "咒罵", NULL}, {"demand", "要求", NULL}, {"frown", "蹙眉", NULL}, {"groan", "呻吟", NULL}, {"grumble", "發牢騷", 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}, {"yell", "大喊", NULL}, {NULL, NULL, NULL} }; int speak_action(unum, cmd, msg) int unum; char *cmd; char *msg; { int i; for (i = 0; speak_data[i].verb; i++) { if (!strcmp(cmd, speak_data[i].verb)) { if (msg[0]) sprintf(chatbuf, "%s %s: %s", users[unum].chatid, speak_data[i].part1_msg, msg); else sprintf(chatbuf, "%s %s", users[unum].chatid, speak_data[i].part1_msg); send_to_room(users[unum].room, chatbuf); return 0; } } return 1; } /* -------------------------------------------- */ /* MUD-like social commands : condition */ /* -------------------------------------------- */ struct action condition_data[] = { {"applaud", "啪啪啪啪啪啪啪....", NULL}, {"back", "回來坐正繼續奮戰", NULL}, {"blood", "倒在血泊之中", NULL}, {"blush", "臉都紅了", NULL}, {"bokan", "Bo Kan! Bo Kan!", NULL}, {"cough", "咳了幾聲", NULL}, {"die", "暴斃", NULL}, {"faint", "當場昏倒", NULL}, {"flop", "踩到香蕉皮... 滑倒!", NULL}, {"haha", "哇哈哈哈哈哈哈哈哈~~~~!!!!!", NULL}, {"happy", "YA! *^_^*", NULL}, {"hurricane", "Ho---Ryu--Kan!!!", NULL}, {"idle", "呆住了", NULL}, {"lag", "lllllllaaaaaaaaaaaagggggggggggggg.................", NULL}, {"puke", "嘔吐中", NULL}, {"room", "r-o-O-m-r-O-O-Mmm-rRR........", NULL}, {"shake", "搖了搖頭", NULL}, {"sleep", "趴在鍵盤上睡著了,口水流進鍵盤,造成當機!", NULL}, {"so", "就醬子!!", NULL}, /*{"strut", "大搖大擺\地走", NULL},*/ {"tongue", "吐了吐舌頭", NULL}, {"think", "歪著頭想了一下", NULL}, {"tiger", "ㄊㄞ ㄍㄜ!ㄊㄞ ㄍㄜ!ㄊㄞ ㄍㄜ 歐巴桑!!!", NULL}, {"wawa", "哇哇哇~~~~~!!!!! ~~~>_<~~~", NULL}, {"www", "汪汪汪!!!", NULL}, {NULL, NULL, NULL} }; int condition_action(unum, cmd) int unum; char *cmd; { int i; for (i = 0; condition_data[i].verb; i++) { if (!strcmp(cmd, condition_data[i].verb)) { sprintf(chatbuf, "%s %s", users[unum].chatid, condition_data[i].part1_msg); send_to_room(users[unum].room, chatbuf); return 1; } } return 0; } /* -------------------------------------------- */ /* MUD-like social commands : help */ /* -------------------------------------------- */ char *dscrb[] = { "【 Verb + Nick: 動詞 + 對方名字 】 例://french Kaede", "【 Verb + Message:動詞 + 要說的話 】 例://sing I swear...", "【 Verb:動詞 】 ↑↓:舊話重提", NULL}; struct action *verbs[] = {party_data, speak_data, condition_data, NULL}; #define SCREEN_WIDTH 80 #define MAX_VERB_LEN 10 #define VERB_NO 8 void view_action_verb(unum) int unum; { int i, j; char *p; send_to_unum(unum, "/c"); for (i = 0; dscrb[i]; i++) { send_to_unum(unum, dscrb[i]); chatbuf[0] = '\0'; j = 0; while (p = verbs[i][j].verb) { j++; strcat(chatbuf, p); if ((j % VERB_NO) == 0) { send_to_unum(unum, chatbuf); chatbuf[0] = '\0'; } else { strncat(chatbuf, " ", MAX_VERB_LEN - strlen(p)); } } if (j % VERB_NO) send_to_unum(unum, chatbuf); send_to_unum(unum, " "); } } struct chatcmd chatcmdlist[] = { "act", chat_act, 0, "bye", chat_goodbye, 0, "flags", chat_setroom, 0, "invite", chat_invite, 0, "join", chat_join, 0, "kick", chat_kick, 0, "msg", chat_private, 0, "nick", chat_nick, 0, "operator", chat_makeop, 0, "rooms", chat_list_rooms, 0, "whoin", chat_list_by_room, 1, "wall", chat_broadcast, 1, "who", chat_map_chatids_thisroom, 0, "list", chat_list_users, 0, "topic", chat_topic, 0, NULL, NULL, 0 }; int command_execute(unum) int unum; { char *msg = users[unum].ibuf; char *cmd; struct chatcmd *cmdrec; int match = 0; /* Validation routine */ if (users[unum].room == -1) { /* MUST give special /! command if not in the room yet */ if (msg[0] != '/' || msg[1] != '!') return -1; else return (login_user(unum, msg + 2)); } /* If not a /-command, it goes to the room. */ if (msg[0] != '/') { chat_allmsg(unum, msg); return 0; } msg++; cmd = nextword(&msg); if (cmd[0] == '/') { if (!strcmp(cmd + 1, "help") || (cmd[1] == '\0')) { view_action_verb(unum); match = 1; } else if (party_action(unum, cmd + 1, msg) == 0) match = 1; else if (speak_action(unum, cmd + 1, msg) == 0) match = 1; else match = condition_action(unum, cmd + 1); } else { for (cmdrec = chatcmdlist; !match && cmdrec->cmdstr; cmdrec++) { if (cmdrec->exact) match = !strcasecmp(cmd, cmdrec->cmdstr); else match = !strncasecmp(cmd, cmdrec->cmdstr, strlen(cmd)); if (match) cmdrec->cmdfunc(unum, msg); } } if (!match) { sprintf(chatbuf, "◆ 指令錯誤:/%s", cmd); send_to_unum(unum, chatbuf); } memset(users[unum].ibuf, 0, sizeof users[unum].ibuf); return 0; } int process_chat_command(unum) int unum; { register int i; int rc, ibufsize; if ((rc = recv(users[unum].sockfd, chatbuf, sizeof chatbuf, 0)) <= 0) { /* disconnected */ exit_room(unum, EXIT_LOSTCONN, (char *) NULL); return -1; } ibufsize = users[unum].ibufsize; for (i = 0; i < rc; i++) { /* if newline is two characters, throw out the first */ if (chatbuf[i] == '\r') continue; /* carriage return signals end of line */ else if (chatbuf[i] == '\n') { users[unum].ibuf[ibufsize] = chatbuf[i] = '\0'; if (command_execute(unum) == -1) return -1; ibufsize = 0; } /* add other chars to input buffer unless size limit exceeded */ else { if (ibufsize < 79) users[unum].ibuf[ibufsize++] = chatbuf[i]; } } users[unum].ibufsize = ibufsize; return 0; } int main() { struct sockaddr_in sin; register int i; int pid, sr, newsock, sinsize; fd_set readfds; struct timeval *tvptr = NULL; /* ----------------------------- */ /* init variable : rooms & users */ /* ----------------------------- */ strcpy(rooms[0].name, mainroom); strcpy(rooms[0].topic, maintopic); for (i = 0; i < MAXACTIVE; i++) { users[i].chatid[0] = '\0'; users[i].sockfd = users[i].utent = -1; } /* ------------------------------ */ /* bind chat server to port */ /* ------------------------------ */ if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { return -1; } sin.sin_family = AF_INET; sin.sin_port = CHATPORT; sin.sin_addr.s_addr = INADDR_ANY; if (bind(sock, (struct sockaddr *) & sin, sizeof(sin)) < 0) { return -1; } sinsize = sizeof sin; if (getsockname(sock, (struct sockaddr *) & sin, &sinsize) == -1) { perror("getsockname"); exit(1); } if (listen(sock, 5) == -1) { perror("listen"); exit(1); } if (fork()) { exit(0); } setpgid(0, 0); /* ------------------------------ */ /* trap signals */ /* ------------------------------ */ signal(SIGHUP, SIG_IGN); signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); signal(SIGPIPE, SIG_IGN); signal(SIGALRM, SIG_IGN); signal(SIGTERM, SIG_IGN); signal(SIGURG, SIG_IGN); signal(SIGTSTP, SIG_IGN); signal(SIGTTIN, SIG_IGN); signal(SIGTTOU, SIG_IGN); FD_ZERO(&allfds); FD_SET(sock, &allfds); nfds = sock + 1; while (1) { memcpy(&readfds, &allfds, sizeof readfds); if ((sr = select(nfds, &readfds, NULL, NULL, tvptr)) < 0) { if (errno == EINTR) continue; exit(-1); } if (sr == 0) { exit(0); /* normal chat server shutdown */ } if (tvptr) tvptr = NULL; if (FD_ISSET(sock, &readfds)) { sinsize = sizeof sin; newsock = accept(sock, (struct sockaddr *) & sin, &sinsize); if (newsock == -1) { continue; } for (i = 0; i < MAXACTIVE; i++) { if (users[i].sockfd == -1) { users[i].sockfd = newsock; users[i].room = -1; break; } } if (i >= MAXACTIVE) { /* full -- no more chat users */ close(newsock); } else { #if !RELIABLE_SELECT_FOR_WRITE int flags = fcntl(newsock, F_GETFL, 0); flags |= O_NDELAY; fcntl(newsock, F_SETFL, flags); #endif FD_SET(newsock, &allfds); if (newsock >= nfds) nfds = newsock + 1; num_conns++; } } for (i = 0; i < MAXACTIVE; i++) { /* we are done with newsock, so re-use the variable */ newsock = users[i].sockfd; if (newsock != -1 && FD_ISSET(newsock, &readfds)) { if (process_chat_command(i) == -1) { logout_user(i); } } } if (num_conns <= 0) { /* one more pass at select, then we go bye-bye */ tvptr = &zerotv; } } /* NOTREACHED */ }