📄 chat.cc
字号:
#include "mcl.h"#include "cui.h"#include "Chat.h"#include <stdarg.h>#include <sys/socket.h>#include <arpa/inet.h>#include <sys/ioctl.h>#include <unistd.h>#include <net/if.h>#include "Interpreter.h"ChatServerSocket *chatServerSocket;MUD *chatMUD;static int flagLookup(const char *flag);static const char* adjustFlags(const char *flags, int &flag);static const char *flagString(int,bool);#define CDEBUG (config->getOption(opt_chat_debug))#define CHAT_NAME (config->getStringOption(opt_chat_name))static void writeChat(const char *fmt, ...) __attribute__((format(printf,1,2)));static void writeChatCommon(char *s) { sprintf(s+strlen(s), "%c%c\n", SET_COLOR, fg_white|bg_black); // Actions should probably be in some "chat" MUD char out[MAX_MUD_BUF]; if (embed_interp->run_quietly("sys/output", s, out)) output->print(out); else output->print(s);}// Write the message with some prefix so we can easier distinguish the chat messages from normal stuffstatic void writeChat(const char *fmt, ...) { char buf[MAX_MUD_BUF]; char *s = buf+sprintf(buf, "%c%cCHAT:%c%c ", SET_COLOR, bg_black|fg_green|fg_bold, SET_COLOR, config->getOption(opt_chat_syscolor)); va_list va; va_start(va, fmt); s += vsnprintf(s, sizeof(buf) - (s-buf) - 3, fmt, va); // -1 for the \n, 2 for the color reset va_end(va); writeChatCommon(buf);}// As above, but this is for the actual talking rather than system messagesstatic void writeChatText(const char *fmt, ...) { char buf[MAX_MUD_BUF]; char *s = buf+sprintf(buf, "%c%cCHAT:%c%c ", SET_COLOR, bg_black|fg_green|fg_bold, SET_COLOR, config->getOption(opt_chat_chatcolor)); va_list va; va_start(va, fmt); s += vsnprintf(s, sizeof(buf) - (s-buf) - 3, fmt, va); // -1 for the \n, 2 for the color reset va_end(va); writeChatCommon(buf);}// clean up escape chars, return at most new_len bytesstatic const char* sanitize(const char *data, int len, int new_len) { StaticBuffer buf(new_len+1); char *out = buf; char *end = buf+new_len-4; while (isspace(*data) && len > 0) { // spaces/newlines at the beginning data++; len--; } while (len > 0 && isspace(data[len-1])) len--; ColorConverter col; while (len > 0 && out < end) { const char *color_start; if (*data == '\e') { color_start = data; while(len > 0 && *data != 'm') { data++; len--; } if (*data == 'm') { int color = col.convert((byte*)color_start, data-color_start); if (color) { *out++ = SET_COLOR; *out++ = color; } data++; len--; continue; } } // ignore 0240 (mudmaster soft newline?) and any other control characters if ((byte)*data == 0240 || (byte)*data < 32) ; else if ((byte)*data > 127) out += sprintf(out, "\\%03o", (byte)*data); else *out++ = *data; data++; len--; } *out++ = NUL; return buf;}ChatServerSocket::ChatServerSocket() { is_afk = false; generateId();}const char * ChatServerSocket::findIPAddress() { char intf[INPUT_SIZE]; const char *interfaces = config->getStringOption(opt_chat_interfaces); struct ifreq interface; do { interfaces = one_argument(interfaces, intf, true); if (intf[0]) { strcpy(interface.ifr_name, intf); if (ioctl (getFD(), SIOCGIFADDR, &interface) < 0) // maybe it doesn't exist continue; struct sockaddr_in sock= *((struct sockaddr_in*)&interface.ifr_addr); if (ioctl(getFD(), SIOCGIFFLAGS, &interface) < 0) return NULL; if (interface.ifr_flags & IFF_UP) { return inet_ntoa(sock.sin_addr); } } } while (intf[0]); return NULL;}ChatConnection* ChatServerSocket::findByAddress(const char *ip, int port) { FOREACH(ChatConnection*,c,connections) if (!strcmp(c->getRemoteServerIP(),ip) && c->getRemoteServerPort() == port) return c; return NULL;}ChatConnection* ChatServerSocket::findConnection(const char *name) { // Let's try to compare the names first FOREACH(ChatConnection*,c,connections) if (c->getName() == name || !strcmp(c->getRemoteServerIP(), name)) return c; // Number? int n = atoi(name); if (n > 0) return connections[n-1]; return NULL;}void ChatServerSocket::generateId() { id = random() ^ getpid() ^ current_time;}ChatServerSocket::~ChatServerSocket() { FOREACH(ChatConnection*,c,connections) delete c;}void ChatServerSocket::idle() { FOREACH(ChatConnection*,c,connections) c->idle(); if (!is_afk && tty->last_activity + 300 < current_time) { handleUserCommand("afk", "quiet"); } else if (is_afk && tty->last_activity + 5 > current_time) { handleUserCommand("afk", "quiet"); }}void ChatServerSocket::connectionAccepted(int fd, struct sockaddr_in *remote) { ChatConnection *c = new ChatConnection(fd,remote); connections.insert(c); c->handleIncoming();}void ChatServerSocket::call(const char *name, int port, Protocol protocol) { ChatConnection *c = new ChatConnection(); connections.insert(c); c->connect(name, port, protocol);}void ChatServerSocket::handleUserCommand(const char *name, const char *arg) { if (!strcmp(name, "call")) { char hostname[MAX_INPUT_BUF]; char port_buf[MAX_INPUT_BUF]; int protocol = -1, port; arg = one_argument(arg, hostname, true); if (!strcmp(hostname, "zchat")) protocol = zchat; else if (!strcmp(hostname, "chat")) protocol = mudmaster; if (protocol != -1) arg = one_argument(arg, hostname, true); arg = one_argument(arg, port_buf, true); if (!hostname[0]) writeChat("You must provide a hostname/nickname to call"); else { if (!port_buf[0]) port = 4050; else port = atoi(port_buf); if (port < 1 || port > 65535) writeChat("Invalid port number: %d", port); // What protocol do we use? if (protocol == -1) { if (config->getOption(opt_chat_protocol) == 0) protocol = mudmaster; else protocol = zchat; } // Let's reget our IP address const char *address = findIPAddress(); if (address && strcmp(address, ~ip)) { writeChat("Warning: it looks like our IP address is now %s", address); ip = address; } call(hostname, port, (Protocol)protocol); } } else if (!strcmp(name, "ip")) { computeIPAddress(); } else if (!strcmp(name, "group")) { char group_name[MAX_INPUT_BUF]; arg = one_argument(arg, group_name, true); if (!group_name[0]) writeChat("Chat to what group?"); else if (!arg[0]) writeChat("Chat what to the '%s' group?", group_name); else { bool emote = false, ok = false; if (!strncasecmp(arg, "emote ", 6)) { emote = true; arg += 6; } FOREACH(ChatConnection*,c,connections) if (c->getGroup() == group_name) { ok = true; c->sendTextGroup(group_name, arg, emote); } const char *colored = sanitize(arg,strlen(arg), 1024); if (!ok) writeChat("No connected members of group '%s'", group_name); else { if (emote) writeChat("[emote to %s] %s %s%c%c", group_name, ~CHAT_NAME, colored, SET_COLOR, config->getOption(opt_chat_chatcolor)); else writeChat("You chat to %s: '%s%c%c'", group_name, colored, SET_COLOR, config->getOption(opt_chat_chatcolor)); } } } else if (!strcmp(name, "setgroup")) { char group_name[MAX_INPUT_BUF]; arg = one_argument(arg, group_name, true); if (!group_name[0]) writeChat("Add/remove members to what group?"); else if (!arg[0]) writeChat("Add/remove what members to group %s?", group_name); else { char name[MAX_INPUT_BUF]; arg = one_argument(arg, name, true); while(name[0]) { ChatConnection *c = findConnection(name); if (!c) writeChat("No such connection: %s", name); else if(c->getGroup() == group_name) { c->setGroup(""); writeChat("%s now in no group", c->shortDescription()); } else { c->setGroup(group_name); writeChat("%s now in group %s", c->shortDescription(), group_name); } arg = one_argument(arg, name, true); } } } else if (!strcmp(name, "request")) { if (!arg[0]) writeChat("%cchat.request ALL|<connection name>", CMDCHAR); else if (!strcmp(arg, "all")) { FOREACH(ChatConnection*,c,connections) { c->sendCommand(cmdRequestConnections, ""); c->setFlags(c->getFlags() | flagRequestPending); } } else { ChatConnection *c = findConnection(arg); if (!c) writeChat("No such connection '%s'", arg); else { c->sendCommand(cmdRequestConnections, ""); c->setFlags(c->getFlags() | flagRequestPending); } } } else if (!strcmp(name, "peek")) { if (!arg[0]) writeChat("%cchat.peek ALL|<connection name>", CMDCHAR); else if (!strcmp(arg, "all")) { FOREACH(ChatConnection*,c,connections) { c->sendCommand(cmdPeekConnections, ""); c->setFlags(c->getFlags() | flagRequestPending); } } else { ChatConnection *c = findConnection(arg); if (!c) writeChat("No such connection '%s'", arg); else { c->sendCommand(cmdPeekConnections, ""); c->setFlags(c->getFlags() | flagRequestPending); } } } else if (!strcmp(name, "ping")) { if (!arg[0]) writeChat("%cchat.ping ALL|<connection name>", CMDCHAR); else if (!strcasecmp(arg, "all")) { FOREACH(ChatConnection*, c, connections) c->sendPing(); } else { ChatConnection *c = findConnection(arg); if (!c) writeChat("No such connection: %s", arg); else c->sendPing(); } } else if (!strcmp(name, "list")) { int i = 1; bool verbose = (output->width > 90); FOREACH(ChatConnection *, c, connections) output->printf("%2d %s\n", i++, c->longDescription(verbose)); output->printf("\n%d active connections.\n", i-1); } else if (!strcmp(name, "accept")) { bool ok = false; FOREACH(ChatConnection *, c, connections) if (c->acceptConnection()) { ok = true; break; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -