/* * ------------------------------------------------------- * Copyright (C) 2003-2007 Tommi Saviranta <wnd@iki.fi> * (C) 2002 Lee Hardy <lee@leeh.co.uk> * (C) 1998-2002 Sebastian Kienzl <zap@riot.org> * ------------------------------------------------------- * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifdef HAVE_CONFIG_H#include <config.h>#endif /* ifdef HAVE_CONFIG_H */ #include "server.h"#include "client.h"#include "llist.h"#include "messages.h"#include "irc.h"#include "miau.h"#include "error.h"#include "tools.h"#include "perm.h"#include "ignore.h"#include "commands.h"#include "qlog.h"#include "chanlog.h"#include "privlog.h"#include "log.h"#include "onconnect.h"#include "automode.h"#include "remote.h"#include "etc.h"#include "common.h"#include "dcc.h" #include <string.h>#include <sys/time.h>#include <time.h> #if HAVE_CRYPT_H#include <crypt.h>#endif /* ifdef HAVE_CRYPT_H */ serverlist_type servers;server_info i_server;connection_type c_server; /* * Drop connection to the server. * * Give client (and server) a reason, which may be set to NULL. */voidserver_drop(char *reason){ llist_node *node; llist_node *nextnode; const char *part_reason; if (reason == NULL) { part_reason = SERV_DISCONNECT; } else { part_reason = reason; } /* As we're no longer connected to server, send queue is useless. */ irc_clear_queue(); if (c_server.socket) { char buf[IRC_MSGLEN]; int r; snprintf(buf, IRC_MSGLEN, "QUIT :%s\r\n", reason); buf[IRC_MSGLEN - 1] = '\0'; r = sock_setblock(c_server.socket); irc_write_real(&c_server, buf);#ifdef CHANLOG chanlog_write_entry_all(LOG_QUIT, LOGM_QUIT, get_short_localtime(), status.nickname, reason, "", "");#endif /* ifdef CHANLOG */ sock_close(&c_server); } i_server.connected = 0; /* * Walk active_channels and either remove channels or move to * passive_channels, depending on cfg.rejoin. */ node = active_channels.head; while (node != NULL) { channel_type *data; nextnode = node->next; data = (channel_type *) node->data; #ifdef AUTOMODE /* We no longer know if we're an operator or not. */ data->oper = -1;#endif /* ifdef AUTOMODE */ /* * We don't need to reset channel topic, it will be erased/reset * when joining the channel. */ if (cfg.chandiscon == 1) { irc_mwrite(&c_clients, ":%s NOTICE %s :%s", status.nickname, (char *) data->name, part_reason); } else if (cfg.chandiscon == 2) { /* * RFC 2812 says "Servers MUST be able to parse * arguments in the form of a list of target, but * SHOULD NOT use lists when sending PART messages to * clients." and therefore we don't part all the * channels with one command. */ irc_mwrite(&c_clients, ":%s!%s@%s PART %s :%s", status.nickname, i_client.username, i_client.hostname, (char *) data->name, part_reason); } if (cfg.rejoin == 1) { char *simple; llist_node *node; /* * Need to move channel from active_channels to * passive_channels. Also revert real channel name * (which we know sure sure) back to simple form. * There's no need to reset simple name -- it won't * change. */ simple = channel_simplify_name(data->name); xfree(data->name); data->name = simple; data->name_set = 0; node = llist_create(data); llist_add_tail(node, &passive_channels); } else { /* * * Not moving channels from list to list, therefore * freeing resources. */ channel_free(data); } /* Remove channel node from old list. */ llist_delete(node, &active_channels); node = nextnode; } if (cfg.chandiscon == 3) { irc_mwrite(&c_clients, ":miau PRIVMSG %s: %s", status.nickname, part_reason); } /* Reset server-name. */ xfree(i_server.realname); i_server.realname = xstrdup("miau"); /* Don't try connecting next server right away. */ timers.connect = 0; #ifdef UPTIME status.startup = 0; /* Reset uptime-counter. */#endif /* ifdef UPTIME */} /* void server_drop(char *reason) */ /* * Set "fallback" or "failsafe" server. * * This server is needed when no server have been defined and we need some * real information about the server we're connected to. */voidserver_set_fallback(const llist_node *safenode){ server_type *fallback = (server_type *) servers.servers.head->data; server_type *safe = (server_type *) safenode->data; /* Free old data. */ /* * If we try to replace failsafe-server with failsafe-server, data * will be freed before it can be copied. This is why we won't do * that. */ if (fallback == safe) { return; } xfree(fallback->name); xfree(fallback->password); /* Copy current values to fallback server. */ fallback->name = xstrdup(safe->name); fallback->port = safe->port; fallback->password = (safe->password != NULL) ? xstrdup(safe->password) : NULL; fallback->working = 1; fallback->timeout = safe->timeout;} /* void server_set_fallback(const llist_node *safenode) */ /* * Resets all servers to 'working'. */voidserver_reset(void){ llist_node *node; for (node = servers.servers.head->next; node != NULL; node = node->next) { ((server_type *) node->data)->working = 1; }} /* void server_reset(void) */ /* * Change server. * * If next is set and there are no more servers to connect, miau will either * reset all servers to working or quit, depending on the configuration. * * If disable != 0, old server will be marked as disfunctional. */voidserver_change(int next, int disable){ llist_node *i; if (status.good_server == 1) { status.good_server = 0; return; } i_server.connected = 0; if (servers.amount == 1) { server_check_list(); return; } if (status.reconnectdelay == CONN_DISABLED) { return; } i = i_server.current; if (servers.fresh == 1) { /* * We don't know which server of those new ones we are on, so * let's use the fallback one. It's the server we're on anyway. */ i_server.current = servers.servers.head; i = servers.servers.head; } if (disable == 1 && i != servers.servers.head) { ((server_type *) i_server.current->data)->working = 0; } if (next == 1) { do { i_server.current = i_server.current->next; if (i_server.current == NULL) { i_server.current = servers.servers.head->next; } } while (! ((server_type *) i_server.current->data)->working && i_server.current != i); } if (((server_type *) i_server.current->data)->working == 0) { if (cfg.nevergiveup == 1) { report(MIAU_OUTOFSERVERSNEVER); server_reset(); i_server.current = servers.servers.head->next; } else { error(MIAU_OUTOFSERVERS); exit(EXIT_SUCCESS); } } if (next == 0) { report(SOCK_RECONNECTNOW, ((server_type *) i_server.current->data)->name); timers.connect = cfg.reconnectdelay - 1; if (timers.connect < 0) { timers.connect = 0; } } else if (i == i_server.current) { report(SOCK_RECONNECT, ((server_type *) i_server.current->data)->name, cfg.reconnectdelay); timers.connect = 0; }} /* void server_change(int next, int disable) */ voidserver_commands(char *command, char *param, int *pass){ upcase(command); if (xstrcmp(command, "PING") == 0) { *pass = 0; if (param != NULL && *param == ':') param++; /* Don't make this global (see ERROR) */ if (param) { /* * Although there should be no need to PONG the server * if there is stuff in queue (server shouldn't need * to ping client if it's sending data), we priorize * PONGs over everything else - just in case. */ irc_write_head(&c_server, "PONG :%s", param); } } else if (xstrcmp(command, "ERROR") == 0) { *pass = 0; server_drop((param == NULL) ? SERV_ERR : param); irc_mwrite(&c_clients, MIAU_CLOSINGLINK, (param == NULL) ? SERV_ERR : param); drop_newclient(NULL); error(IRC_SERVERERROR, (param == NULL) ? "unknown" : param); server_change(1, i_server.connected == 0); }} /* void server_commands(char *command, char *param, int *pass) */ static voidparse_msg_me_ctcp(const char *origin, const char *nick, const char *hostname, const char *param1, const char *param2, int cmdindex, int *pass){#ifdef DCCBOUNCE if (c_clients.connected > 0 && cmdindex == CMD_PRIVMSG && cfg.dccbounce && (xstrcasecmp(param2 + 2, "DCC\1") == 0)) { char dcct[IRC_MSGLEN]; strncpy(dcct, param2 + 1, IRC_MSGLEN); if (dcc_initiate(dcct, IRC_MSGLEN, 0)) { irc_mwrite(&c_clients, ":%s PRIVMSG %s :%s", origin, param1, dcct); *pass = 0; } }#endif /* ifdef DCCBOUNCE */#ifdef DCCBOUNCE#ifdef CTCTPREPLIES else#endif /* ifdef CTCPREPLIES */#endif /* ifdef DCCBOUNCE */#ifdef CTCPREPLIES if (! is_ignore(hostname, IGNORE_CTCP) && c_clients.connected == 0 && status.allowreply == 1) { report(CLNT_CTCP, param2 + 1, origin); if (xstrcmp(param2 + 2, "VERSION\1") == 0) { irc_notice(&c_server, nick, VERSIONREPLY); } else if (xstrcmp(param2 + 2, "PING") == 0) { if (strlen(param2 + 1) > 6) { irc_notice(&c_server, nick, "%s", param2 + 1); } } else if (xstrcmp(param2 + 2, "CLIENTINFO\1") == 0) { irc_notice(&c_server, nick, CLIENTINFOREPLY); } ignore_add(hostname, 6, IGNORE_CTCP); status.allowreply = 0; timers.reply = 0; } /* CTCP-replies */ else if (is_ignore(hostname, IGNORE_CTCP) || status.allowreply == 0) { report(CLNT_CTCPNOREPLY, param2 + 1, origin); }#endif /* ifdef CTCPREPLIES */} /* static void parse_msg_me_ctcp(const char *origin, const char *nick, const char *hostname, const char *param1, const char *param2, int cmdindex, int *pass) */ static intparse_msg_me(const char *origin, const char *nick, const char *hostname, const char *param1, const char *param2, int cmdindex, int *pass){ if (is_perm(&ignorelist, origin)) { return 0; } #ifdef PRIVLOG /* Should we log? */ if ((c_clients.connected == 0 && (cfg.privlog & 0x01)) || (c_clients.connected > 0 && (cfg.privlog & 0x02))) { privlog_write(nick, PRIVLOG_IN, cmdindex, param2 + 1); }#endif /* ifdef PRIVLOG */ /* Is this a special (CTCP/DCC) -message ? */ if (param2[1] == '\1') { parse_msg_me_ctcp(origin, nick, hostname, param1, param2, cmdindex, pass); return 0; }#ifdef NEED_CMDPASSWD /* Remote command for bouncer. */ else if (cfg.cmdpasswd != NULL && param2 != NULL && param2[0] != '\0' && param2[1] == ':') { int passok = 0; int passlen; char *lparam; lparam = xstrdup(param2 + 2); passlen = pos(lparam, ' '); if (passlen != -1) { lparam[passlen] = '\0'; if (strlen(cfg.cmdpasswd) == 13) { /* Assume it's crypted */ if (xstrcmp(crypt(lparam, cfg.cmdpasswd), cfg.cmdpasswd) == 0) { passok = 1; } } else if (xstrcmp(lparam, cfg.cmdpasswd) == 0) { passok = 1; } lparam[passlen] = ' '; } if (passok) { char *t; char *command; char *params = NULL; t = strtok(lparam, " "); command = strtok(NULL, " "); if (command != NULL) { params = strchr(command, '\0') + 1; } if (params != NULL) { upcase(command); *pass = remote_cmd(command, params, nick); xfree(lparam); return 0; } } xfree(lparam); }#endif /* ifdef NEED_CMDPASSWD */ /* Normal PRIVMSG/NOTICE to client. */#ifdef INBOX#ifndef QUICKLOG /* * Note that we do inbox here only is privmsglog is enabled and * quicklogging is disabled. */ if (inbox != NULL) { /* termination + validity guaranteed */ fprintf(inbox, "%s <%s> %s\n", get_short_localtime(), origin, param2 + 1); fflush(inbox); }#endif /* ifdef QUICKLOG */#endif /* ifdef INBOX */ if (cfg.forwardmsg) {Lines 1–500 of 1516. Load more
/* * ------------------------------------------------------- * Copyright (C) 2003-2007 Tommi Saviranta <wnd@iki.fi> * (C) 2002 Lee Hardy <lee@leeh.co.uk> * (C) 1998-2002 Sebastian Kienzl <zap@riot.org> * ------------------------------------------------------- * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifdef HAVE_CONFIG_H #include <config.h> #endif /* ifdef HAVE_CONFIG_H */ #include "server.h" #include "client.h" #include "llist.h" #include "messages.h" #include "irc.h" #include "miau.h" #include "error.h" #include "tools.h" #include "perm.h" #include "ignore.h" #include "commands.h" #include "qlog.h" #include "chanlog.h" #include "privlog.h" #include "log.h" #include "onconnect.h" #include "automode.h" #include "remote.h" #include "etc.h" #include "common.h" #include "dcc.h" #include <string.h> #include <sys/time.h> #include <time.h> #if HAVE_CRYPT_H #include <crypt.h> #endif /* ifdef HAVE_CRYPT_H */ serverlist_type servers; server_info i_server; connection_type c_server; /* * Drop connection to the server. * * Give client (and server) a reason, which may be set to NULL. */ void server_drop(char *reason) { llist_node *node; llist_node *nextnode; const char *part_reason; if (reason == NULL) { part_reason = SERV_DISCONNECT; } else { part_reason = reason; } /* As we're no longer connected to server, send queue is useless. */ irc_clear_queue(); if (c_server.socket) { char buf[IRC_MSGLEN]; int r; snprintf(buf, IRC_MSGLEN, "QUIT :%s\r\n", reason); buf[IRC_MSGLEN - 1] = '\0'; r = sock_setblock(c_server.socket); irc_write_real(&c_server, buf); #ifdef CHANLOG chanlog_write_entry_all(LOG_QUIT, LOGM_QUIT, get_short_localtime(), status.nickname, reason, "", ""); #endif /* ifdef CHANLOG */ sock_close(&c_server); } i_server.connected = 0; /* * Walk active_channels and either remove channels or move to * passive_channels, depending on cfg.rejoin. */ node = active_channels.head; while (node != NULL) { channel_type *data; nextnode = node->next; data = (channel_type *) node->data; #ifdef AUTOMODE /* We no longer know if we're an operator or not. */ data->oper = -1; #endif /* ifdef AUTOMODE */ /* * We don't need to reset channel topic, it will be erased/reset * when joining the channel. */ if (cfg.chandiscon == 1) { irc_mwrite(&c_clients, ":%s NOTICE %s :%s", status.nickname, (char *) data->name, part_reason); } else if (cfg.chandiscon == 2) { /* * RFC 2812 says "Servers MUST be able to parse * arguments in the form of a list of target, but * SHOULD NOT use lists when sending PART messages to * clients." and therefore we don't part all the * channels with one command. */ irc_mwrite(&c_clients, ":%s!%s@%s PART %s :%s", status.nickname, i_client.username, i_client.hostname, (char *) data->name, part_reason); } if (cfg.rejoin == 1) { char *simple; llist_node *node; /* * Need to move channel from active_channels to * passive_channels. Also revert real channel name * (which we know sure sure) back to simple form. * There's no need to reset simple name -- it won't * change. */ simple = channel_simplify_name(data->name); xfree(data->name); data->name = simple; data->name_set = 0; node = llist_create(data); llist_add_tail(node, &passive_channels); } else { /* * * Not moving channels from list to list, therefore * freeing resources. */ channel_free(data); } /* Remove channel node from old list. */ llist_delete(node, &active_channels); node = nextnode; } if (cfg.chandiscon == 3) { irc_mwrite(&c_clients, ":miau PRIVMSG %s: %s", status.nickname, part_reason); } /* Reset server-name. */ xfree(i_server.realname); i_server.realname = xstrdup("miau"); /* Don't try connecting next server right away. */ timers.connect = 0; #ifdef UPTIME status.startup = 0; /* Reset uptime-counter. */ #endif /* ifdef UPTIME */ } /* void server_drop(char *reason) */ /* * Set "fallback" or "failsafe" server. * * This server is needed when no server have been defined and we need some * real information about the server we're connected to. */ void server_set_fallback(const llist_node *safenode) { server_type *fallback = (server_type *) servers.servers.head->data; server_type *safe = (server_type *) safenode->data; /* Free old data. */ /* * If we try to replace failsafe-server with failsafe-server, data * will be freed before it can be copied. This is why we won't do * that. */ if (fallback == safe) { return; } xfree(fallback->name); xfree(fallback->password); /* Copy current values to fallback server. */ fallback->name = xstrdup(safe->name); fallback->port = safe->port; fallback->password = (safe->password != NULL) ? xstrdup(safe->password) : NULL; fallback->working = 1; fallback->timeout = safe->timeout; } /* void server_set_fallback(const llist_node *safenode) */ /* * Resets all servers to 'working'. */ void server_reset(void) { llist_node *node; for (node = servers.servers.head->next; node != NULL; node = node->next) { ((server_type *) node->data)->working = 1; } } /* void server_reset(void) */ /* * Change server. * * If next is set and there are no more servers to connect, miau will either * reset all servers to working or quit, depending on the configuration. * * If disable != 0, old server will be marked as disfunctional. */ void server_change(int next, int disable) { llist_node *i; if (status.good_server == 1) { status.good_server = 0; return; } i_server.connected = 0; if (servers.amount == 1) { server_check_list(); return; } if (status.reconnectdelay == CONN_DISABLED) { return; } i = i_server.current; if (servers.fresh == 1) { /* * We don't know which server of those new ones we are on, so * let's use the fallback one. It's the server we're on anyway. */ i_server.current = servers.servers.head; i = servers.servers.head; } if (disable == 1 && i != servers.servers.head) { ((server_type *) i_server.current->data)->working = 0; } if (next == 1) { do { i_server.current = i_server.current->next; if (i_server.current == NULL) { i_server.current = servers.servers.head->next; } } while (! ((server_type *) i_server.current->data)->working && i_server.current != i); } if (((server_type *) i_server.current->data)->working == 0) { if (cfg.nevergiveup == 1) { report(MIAU_OUTOFSERVERSNEVER); server_reset(); i_server.current = servers.servers.head->next; } else { error(MIAU_OUTOFSERVERS); exit(EXIT_SUCCESS); } } if (next == 0) { report(SOCK_RECONNECTNOW, ((server_type *) i_server.current->data)->name); timers.connect = cfg.reconnectdelay - 1; if (timers.connect < 0) { timers.connect = 0; } } else if (i == i_server.current) { report(SOCK_RECONNECT, ((server_type *) i_server.current->data)->name, cfg.reconnectdelay); timers.connect = 0; } } /* void server_change(int next, int disable) */ void server_commands(char *command, char *param, int *pass) { upcase(command); if (xstrcmp(command, "PING") == 0) { *pass = 0; if (param != NULL && *param == ':') param++; /* Don't make this global (see ERROR) */ if (param) { /* * Although there should be no need to PONG the server * if there is stuff in queue (server shouldn't need * to ping client if it's sending data), we priorize * PONGs over everything else - just in case. */ irc_write_head(&c_server, "PONG :%s", param); } } else if (xstrcmp(command, "ERROR") == 0) { *pass = 0; server_drop((param == NULL) ? SERV_ERR : param); irc_mwrite(&c_clients, MIAU_CLOSINGLINK, (param == NULL) ? SERV_ERR : param); drop_newclient(NULL); error(IRC_SERVERERROR, (param == NULL) ? "unknown" : param); server_change(1, i_server.connected == 0); } } /* void server_commands(char *command, char *param, int *pass) */ static void parse_msg_me_ctcp(const char *origin, const char *nick, const char *hostname, const char *param1, const char *param2, int cmdindex, int *pass) { #ifdef DCCBOUNCE if (c_clients.connected > 0 && cmdindex == CMD_PRIVMSG && cfg.dccbounce && (xstrcasecmp(param2 + 2, "DCC\1") == 0)) { char dcct[IRC_MSGLEN]; strncpy(dcct, param2 + 1, IRC_MSGLEN); if (dcc_initiate(dcct, IRC_MSGLEN, 0)) { irc_mwrite(&c_clients, ":%s PRIVMSG %s :%s", origin, param1, dcct); *pass = 0; } } #endif /* ifdef DCCBOUNCE */ #ifdef DCCBOUNCE #ifdef CTCTPREPLIES else #endif /* ifdef CTCPREPLIES */ #endif /* ifdef DCCBOUNCE */ #ifdef CTCPREPLIES if (! is_ignore(hostname, IGNORE_CTCP) && c_clients.connected == 0 && status.allowreply == 1) { report(CLNT_CTCP, param2 + 1, origin); if (xstrcmp(param2 + 2, "VERSION\1") == 0) { irc_notice(&c_server, nick, VERSIONREPLY); } else if (xstrcmp(param2 + 2, "PING") == 0) { if (strlen(param2 + 1) > 6) { irc_notice(&c_server, nick, "%s", param2 + 1); } } else if (xstrcmp(param2 + 2, "CLIENTINFO\1") == 0) { irc_notice(&c_server, nick, CLIENTINFOREPLY); } ignore_add(hostname, 6, IGNORE_CTCP); status.allowreply = 0; timers.reply = 0; } /* CTCP-replies */ else if (is_ignore(hostname, IGNORE_CTCP) || status.allowreply == 0) { report(CLNT_CTCPNOREPLY, param2 + 1, origin); } #endif /* ifdef CTCPREPLIES */ } /* static void parse_msg_me_ctcp(const char *origin, const char *nick, const char *hostname, const char *param1, const char *param2, int cmdindex, int *pass) */ static int parse_msg_me(const char *origin, const char *nick, const char *hostname, const char *param1, const char *param2, int cmdindex, int *pass) { if (is_perm(&ignorelist, origin)) { return 0; } #ifdef PRIVLOG /* Should we log? */ if ((c_clients.connected == 0 && (cfg.privlog & 0x01)) || (c_clients.connected > 0 && (cfg.privlog & 0x02))) { privlog_write(nick, PRIVLOG_IN, cmdindex, param2 + 1); } #endif /* ifdef PRIVLOG */ /* Is this a special (CTCP/DCC) -message ? */ if (param2[1] == '\1') { parse_msg_me_ctcp(origin, nick, hostname, param1, param2, cmdindex, pass); return 0; } #ifdef NEED_CMDPASSWD /* Remote command for bouncer. */ else if (cfg.cmdpasswd != NULL && param2 != NULL && param2[0] != '\0' && param2[1] == ':') { int passok = 0; int passlen; char *lparam; lparam = xstrdup(param2 + 2); passlen = pos(lparam, ' '); if (passlen != -1) { lparam[passlen] = '\0'; if (strlen(cfg.cmdpasswd) == 13) { /* Assume it's crypted */ if (xstrcmp(crypt(lparam, cfg.cmdpasswd), cfg.cmdpasswd) == 0) { passok = 1; } } else if (xstrcmp(lparam, cfg.cmdpasswd) == 0) { passok = 1; } lparam[passlen] = ' '; } if (passok) { char *t; char *command; char *params = NULL; t = strtok(lparam, " "); command = strtok(NULL, " "); if (command != NULL) { params = strchr(command, '\0') + 1; } if (params != NULL) { upcase(command); *pass = remote_cmd(command, params, nick); xfree(lparam); return 0; } } xfree(lparam); } #endif /* ifdef NEED_CMDPASSWD */ /* Normal PRIVMSG/NOTICE to client. */ #ifdef INBOX #ifndef QUICKLOG /* * Note that we do inbox here only is privmsglog is enabled and * quicklogging is disabled. */ if (inbox != NULL) { /* termination + validity guaranteed */ fprintf(inbox, "%s <%s> %s\n", get_short_localtime(), origin, param2 + 1); fflush(inbox); } #endif /* ifdef QUICKLOG */ #endif /* ifdef INBOX */ if (cfg.forwardmsg) {