1516 lines · 35.1 KB · C
RawDownload
/* * ------------------------------------------------------- * 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.