1 /********************************************************************\
2   * BitlBee -- An IRC to other IM-networks gateway                     *
3   *                                                                    *
4   * Copyright 2002-2012 Wilmer van der Gaast and others                *
5   \********************************************************************/
6 
7 /* Stuff to handle, save and search IRC buddies                         */
8 
9 /*
10   This program is free software; you can redistribute it and/or modify
11   it under the terms of the GNU General Public License as published by
12   the Free Software Foundation; either version 2 of the License, or
13   (at your option) any later version.
14 
15   This program is distributed in the hope that it will be useful,
16   but WITHOUT ANY WARRANTY; without even the implied warranty of
17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18   GNU General Public License for more details.
19 
20   You should have received a copy of the GNU General Public License with
21   the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
22   if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
23   Fifth Floor, Boston, MA  02110-1301  USA
24 */
25 
26 #include "bitlbee.h"
27 #include "ipc.h"
28 
irc_user_new(irc_t * irc,const char * nick)29 irc_user_t *irc_user_new(irc_t *irc, const char *nick)
30 {
31 	irc_user_t *iu = g_new0(irc_user_t, 1);
32 
33 	iu->irc = irc;
34 	iu->nick = g_strdup(nick);
35 	iu->user = iu->host = iu->fullname = iu->nick;
36 
37 	iu->key = g_strdup(nick);
38 	nick_lc(irc, iu->key);
39 	g_hash_table_insert(irc->nick_user_hash, iu->key, iu);
40 
41 	return iu;
42 }
43 
irc_user_free(irc_t * irc,irc_user_t * iu)44 int irc_user_free(irc_t *irc, irc_user_t *iu)
45 {
46 	static struct im_connection *last_ic;
47 	static char *msg;
48 
49 	if (!iu) {
50 		return 0;
51 	}
52 
53 	if (iu->bu &&
54 	    (iu->bu->ic->flags & OPT_LOGGING_OUT) &&
55 	    iu->bu->ic != last_ic) {
56 		char host_prefix[] = "bitlbee.";
57 		char *s;
58 
59 		/* Irssi recognises netsplits by quitmsgs with two
60 		   hostnames, where a hostname is a "word" with one
61 		   of more dots. Mangle no-dot hostnames a bit. */
62 		if (strchr(irc->root->host, '.')) {
63 			*host_prefix = '\0';
64 		}
65 
66 		last_ic = iu->bu->ic;
67 		g_free(msg);
68 		if (!set_getbool(&irc->b->set, "simulate_netsplit")) {
69 			msg = g_strdup("Account off-line");
70 		} else if ((s = strchr(iu->bu->ic->acc->user, '@'))) {
71 			msg = g_strdup_printf("%s%s %s", host_prefix,
72 			                      irc->root->host, s + 1);
73 		} else {
74 			msg = g_strdup_printf("%s%s %s.%s",
75 			                      host_prefix, irc->root->host,
76 			                      iu->bu->ic->acc->prpl->name, irc->root->host);
77 		}
78 	} else if (!iu->bu || !(iu->bu->ic->flags & OPT_LOGGING_OUT)) {
79 		g_free(msg);
80 		msg = g_strdup("Removed");
81 		last_ic = NULL;
82 	}
83 	irc_user_quit(iu, msg);
84 
85 	g_hash_table_remove(irc->nick_user_hash, iu->key);
86 
87 	g_free(iu->nick);
88 	if (iu->nick != iu->user) {
89 		g_free(iu->user);
90 	}
91 	if (iu->nick != iu->host) {
92 		g_free(iu->host);
93 	}
94 	if (iu->nick != iu->fullname) {
95 		g_free(iu->fullname);
96 	}
97 	g_free(iu->pastebuf);
98 	if (iu->pastebuf_timer) {
99 		b_event_remove(iu->pastebuf_timer);
100 	}
101 	g_free(iu->key);
102 	g_free(iu);
103 
104 	return 1;
105 }
106 
irc_user_by_name(irc_t * irc,const char * nick)107 irc_user_t *irc_user_by_name(irc_t *irc, const char *nick)
108 {
109 	char key[strlen(nick) + 1];
110 
111 	strcpy(key, nick);
112 	if (nick_lc(irc, key)) {
113 		return g_hash_table_lookup(irc->nick_user_hash, key);
114 	} else {
115 		return NULL;
116 	}
117 }
118 
irc_user_set_nick(irc_user_t * iu,const char * new)119 int irc_user_set_nick(irc_user_t *iu, const char *new)
120 {
121 	irc_t *irc = iu->irc;
122 	irc_user_t *new_iu;
123 	char key[strlen(new) + 1];
124 	GSList *cl;
125 
126 	strcpy(key, new);
127 	if (iu == NULL || !nick_lc(irc, key) ||
128 	    ((new_iu = irc_user_by_name(irc, new)) && new_iu != iu)) {
129 		return 0;
130 	}
131 
132 	for (cl = irc->channels; cl; cl = cl->next) {
133 		irc_channel_t *ic = cl->data;
134 
135 		/* Send a NICK update if we're renaming our user, or someone
136 		   who's in the same channel like our user. */
137 		if (iu == irc->user ||
138 		    ((ic->flags & IRC_CHANNEL_JOINED) &&
139 		     irc_channel_has_user(ic, iu))) {
140 			irc_send_nick(iu, new);
141 			break;
142 		}
143 	}
144 
145 	g_hash_table_remove(irc->nick_user_hash, iu->key);
146 
147 	if (iu->nick == iu->user) {
148 		iu->user = NULL;
149 	}
150 	if (iu->nick == iu->host) {
151 		iu->host = NULL;
152 	}
153 	if (iu->nick == iu->fullname) {
154 		iu->fullname = NULL;
155 	}
156 	g_free(iu->nick);
157 	iu->nick = g_strdup(new);
158 	if (iu->user == NULL) {
159 		iu->user = g_strdup(iu->nick);
160 	}
161 	if (iu->host == NULL) {
162 		iu->host = g_strdup(iu->nick);
163 	}
164 	if (iu->fullname == NULL) {
165 		iu->fullname = g_strdup(iu->nick);
166 	}
167 
168 	g_free(iu->key);
169 	iu->key = g_strdup(key);
170 	g_hash_table_insert(irc->nick_user_hash, iu->key, iu);
171 
172 	if (iu == irc->user) {
173 		ipc_to_master_str("NICK :%s\r\n", new);
174 	}
175 
176 	return 1;
177 }
178 
irc_user_cmp(gconstpointer a_,gconstpointer b_)179 gint irc_user_cmp(gconstpointer a_, gconstpointer b_)
180 {
181 	const irc_user_t *a = a_, *b = b_;
182 
183 	return strcmp(a->key, b->key);
184 }
185 
irc_user_get_away(irc_user_t * iu)186 const char *irc_user_get_away(irc_user_t *iu)
187 {
188 	irc_t *irc = iu->irc;
189 	bee_user_t *bu = iu->bu;
190 
191 	if (iu == irc->user) {
192 		return set_getstr(&irc->b->set, "away");
193 	} else if (bu) {
194 		if (!bu->flags & BEE_USER_ONLINE) {
195 			return "Offline";
196 		} else if (bu->flags & BEE_USER_AWAY) {
197 			if (bu->status_msg) {
198 				static char ret[MAX_STRING];
199 				g_snprintf(ret, MAX_STRING - 1, "%s (%s)",
200 				           bu->status ? : "Away", bu->status_msg);
201 				return ret;
202 			} else {
203 				return bu->status ? : "Away";
204 			}
205 		}
206 	}
207 
208 	return NULL;
209 }
210 
irc_user_quit(irc_user_t * iu,const char * msg)211 void irc_user_quit(irc_user_t *iu, const char *msg)
212 {
213 	GSList *l;
214 	gboolean send_quit = FALSE;
215 
216 	if (!iu) {
217 		return;
218 	}
219 
220 	for (l = iu->irc->channels; l; l = l->next) {
221 		irc_channel_t *ic = l->data;
222 		send_quit |= irc_channel_del_user(ic, iu, IRC_CDU_SILENT, NULL) &&
223 		             (ic->flags & IRC_CHANNEL_JOINED);
224 	}
225 
226 	if (send_quit) {
227 		irc_send_quit(iu, msg);
228 	}
229 }
230 
231 /* User-type dependent functions, for root/NickServ: */
root_privmsg(irc_user_t * iu,const char * msg)232 static gboolean root_privmsg(irc_user_t *iu, const char *msg)
233 {
234 	char cmd[strlen(msg) + 1];
235 
236 	strcpy(cmd, msg);
237 	root_command_string(iu->irc, cmd);
238 
239 	return TRUE;
240 }
241 
root_ctcp(irc_user_t * iu,char * const * ctcp)242 static gboolean root_ctcp(irc_user_t *iu, char * const *ctcp)
243 {
244 	if (g_strcasecmp(ctcp[0], "VERSION") == 0) {
245 		irc_send_msg_f(iu, "NOTICE", iu->irc->user->nick, "\001%s %s\001",
246 		               ctcp[0], PACKAGE " " BITLBEE_VERSION);
247 	} else if (g_strcasecmp(ctcp[0], "PING") == 0) {
248 		irc_send_msg_f(iu, "NOTICE", iu->irc->user->nick, "\001%s %s\001",
249 		               ctcp[0], ctcp[1] ? : "");
250 	}
251 
252 	return TRUE;
253 }
254 
255 const struct irc_user_funcs irc_user_root_funcs = {
256 	root_privmsg,
257 	root_ctcp,
258 };
259 
260 /* Echo to yourself: */
self_privmsg(irc_user_t * iu,const char * msg)261 static gboolean self_privmsg(irc_user_t *iu, const char *msg)
262 {
263 	irc_send_msg(iu, "PRIVMSG", iu->nick, msg, NULL);
264 
265 	return TRUE;
266 }
267 
268 const struct irc_user_funcs irc_user_self_funcs = {
269 	self_privmsg,
270 };
271