1 /*
2  notifylist.c : irssi
3 
4     Copyright (C) 1999-2000 Timo Sirainen
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License along
17     with this program; if not, write to the Free Software Foundation, Inc.,
18     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20 
21 #include "module.h"
22 #include "modules.h"
23 #include "signals.h"
24 #include "settings.h"
25 
26 #include "irc.h"
27 #include "irc-channels.h"
28 #include "servers-redirect.h"
29 #include "masks.h"
30 #include "nicklist.h"
31 
32 #include "notifylist.h"
33 #include "notify-setup.h"
34 
35 GSList *notifies;
36 
notifylist_add(const char * mask,const char * ircnets,int away_check)37 NOTIFYLIST_REC *notifylist_add(const char *mask, const char *ircnets,
38 			       int away_check)
39 {
40 	NOTIFYLIST_REC *rec;
41 
42 	g_return_val_if_fail(mask != NULL, NULL);
43 
44 	rec = g_new0(NOTIFYLIST_REC, 1);
45         rec->mask = g_strdup(mask);
46 	rec->ircnets = ircnets == NULL || *ircnets == '\0' ? NULL :
47 		g_strsplit(ircnets, " ", -1);
48 	rec->away_check = away_check;
49 
50         notifylist_add_config(rec);
51 
52         notifies = g_slist_append(notifies, rec);
53 	signal_emit("notifylist new", 1, rec);
54 	return rec;
55 }
56 
notify_destroy(NOTIFYLIST_REC * rec)57 static void notify_destroy(NOTIFYLIST_REC *rec)
58 {
59 	if (rec->ircnets != NULL) g_strfreev(rec->ircnets);
60 	g_free(rec->mask);
61         g_free(rec);
62 }
63 
notifylist_destroy_all(void)64 void notifylist_destroy_all(void)
65 {
66 	g_slist_foreach(notifies, (GFunc) notify_destroy, NULL);
67 	g_slist_free(notifies);
68 
69 	notifies = NULL;
70 }
71 
notifylist_remove(const char * mask)72 void notifylist_remove(const char *mask)
73 {
74 	NOTIFYLIST_REC *rec;
75 
76 	g_return_if_fail(mask != NULL);
77 
78 	rec = notifylist_find(mask, "*");
79 	if (rec == NULL) return;
80 
81 	notifylist_remove_config(rec);
82 	notifies = g_slist_remove(notifies, rec);
83 	signal_emit("notifylist remove", 1, rec);
84 
85         notify_destroy(rec);
86 }
87 
notifylist_ircnets_match(NOTIFYLIST_REC * rec,const char * ircnet)88 int notifylist_ircnets_match(NOTIFYLIST_REC *rec, const char *ircnet)
89 {
90 	char **tmp;
91 
92 	if (rec->ircnets == NULL) return TRUE;
93 	if (ircnet == NULL) return FALSE;
94 	if (g_strcmp0(ircnet, "*") == 0) return TRUE;
95 
96 	for (tmp = rec->ircnets; *tmp != NULL; tmp++) {
97 		if (g_ascii_strcasecmp(*tmp, ircnet) == 0)
98 			return TRUE;
99 	}
100 
101 	return FALSE;
102 }
103 
notifylist_find(const char * mask,const char * ircnet)104 NOTIFYLIST_REC *notifylist_find(const char *mask, const char *ircnet)
105 {
106 	NOTIFYLIST_REC *best;
107 	GSList *tmp;
108 	int len;
109 
110 	best = NULL;
111 	len = strlen(mask);
112 	for (tmp = notifies; tmp != NULL; tmp = tmp->next) {
113 		NOTIFYLIST_REC *rec = tmp->data;
114 
115 		/* check mask */
116 		if (g_ascii_strncasecmp(rec->mask, mask, len) != 0 ||
117 		    (rec->mask[len] != '\0' && rec->mask[len] != '!')) continue;
118 
119 		/* check ircnet */
120 		if (rec->ircnets == NULL) {
121 			best = rec;
122 			continue;
123 		}
124 
125 		if (notifylist_ircnets_match(rec, ircnet))
126 			return rec;
127 	}
128 
129 	return best;
130 }
131 
notifylist_ison_server(IRC_SERVER_REC * server,const char * nick)132 int notifylist_ison_server(IRC_SERVER_REC *server, const char *nick)
133 {
134 	NOTIFY_NICK_REC *rec;
135 
136 	g_return_val_if_fail(nick != NULL, FALSE);
137 	g_return_val_if_fail(IS_IRC_SERVER(server), FALSE);
138 
139 	rec = notify_nick_find(server, nick);
140 	return rec != NULL && rec->host_ok && rec->away_ok;
141 }
142 
notifylist_ison_serverlist(const char * nick,const char * taglist)143 static IRC_SERVER_REC *notifylist_ison_serverlist(const char *nick, const char *taglist)
144 {
145 	IRC_SERVER_REC *server;
146 	char **list, **tmp;
147 
148 	g_return_val_if_fail(nick != NULL, NULL);
149 	g_return_val_if_fail(taglist != NULL, NULL);
150 
151 	list = g_strsplit(taglist, " ", -1);
152 
153 	server = NULL;
154 	for (tmp = list; *tmp != NULL; tmp++) {
155 		server = (IRC_SERVER_REC *) server_find_chatnet(*tmp);
156 
157 		if (IS_IRC_SERVER(server) &&
158 		    notifylist_ison_server(server, nick))
159 			break;
160 	}
161 	g_strfreev(list);
162 
163 	return tmp == NULL ? NULL : server;
164 }
165 
notifylist_ison(const char * nick,const char * serverlist)166 IRC_SERVER_REC *notifylist_ison(const char *nick, const char *serverlist)
167 {
168 	GSList *tmp;
169 
170 	g_return_val_if_fail(nick != NULL, FALSE);
171 	g_return_val_if_fail(serverlist != NULL, FALSE);
172 
173 	if (*serverlist != '\0')
174 		return notifylist_ison_serverlist(nick, serverlist);
175 
176 	/* any server.. */
177 	for (tmp = servers; tmp != NULL; tmp = tmp->next) {
178 		IRC_SERVER_REC *server = tmp->data;
179 
180 		if (IS_IRC_SERVER(server) &&
181 		    notifylist_ison_server(server, nick))
182 			return tmp->data;
183 	}
184 
185 	return NULL;
186 }
187 
notifylist_init_server(IRC_SERVER_REC * server)188 static void notifylist_init_server(IRC_SERVER_REC *server)
189 {
190 	MODULE_SERVER_REC *rec;
191 
192 	g_return_if_fail(server != NULL);
193 
194 	if (!IS_IRC_SERVER(server))
195 		return;
196 
197 	rec = g_new0(MODULE_SERVER_REC,1 );
198 	MODULE_DATA_SET(server, rec);
199 }
200 
notifylist_deinit_server(IRC_SERVER_REC * server)201 static void notifylist_deinit_server(IRC_SERVER_REC *server)
202 {
203 	MODULE_SERVER_REC *mserver;
204 	NOTIFY_NICK_REC *rec;
205 
206 	g_return_if_fail(server != NULL);
207 
208 	if (!IS_IRC_SERVER(server))
209 		return;
210 
211 	mserver = MODULE_DATA(server);
212 	while (mserver->notify_users != NULL) {
213 		rec = mserver->notify_users->data;
214 
215 		mserver->notify_users = g_slist_remove(mserver->notify_users, rec);
216 		notify_nick_destroy(rec);
217 	}
218 	g_free(mserver);
219 	MODULE_DATA_UNSET(server);
220 }
221 
notifylist_left(IRC_SERVER_REC * server,NOTIFY_NICK_REC * rec)222 void notifylist_left(IRC_SERVER_REC *server, NOTIFY_NICK_REC *rec)
223 {
224 	MODULE_SERVER_REC *mserver;
225 
226 	mserver = MODULE_DATA(server);
227 	mserver->notify_users = g_slist_remove(mserver->notify_users, rec);
228 
229 	if (rec->host_ok && rec->away_ok) {
230 		signal_emit("notifylist left", 6,
231 			    server, rec->nick,
232 			    rec->user, rec->host,
233 			    rec->realname, rec->awaymsg);
234 	}
235 
236 	notify_nick_destroy(rec);
237 }
238 
event_quit(IRC_SERVER_REC * server,const char * data,const char * nick)239 static void event_quit(IRC_SERVER_REC *server, const char *data,
240 		       const char *nick)
241 {
242 	NOTIFY_NICK_REC *rec;
243 
244 	if (*data == ':') data++; /* quit message */
245 
246 	rec = notify_nick_find(server, nick);
247 	if (rec != NULL) notifylist_left(server, rec);
248 }
249 
notifylist_check_join(IRC_SERVER_REC * server,const char * nick,const char * userhost,const char * realname,int away)250 static void notifylist_check_join(IRC_SERVER_REC *server, const char *nick,
251 				  const char *userhost, const char *realname, int away)
252 {
253 	NOTIFYLIST_REC *notify;
254 	NOTIFY_NICK_REC *rec;
255 	char *user, *host;
256 
257 	if (nick == NULL)
258 		return;
259 
260 	notify = notifylist_find(nick, server->connrec->chatnet);
261 	if (notify == NULL) return;
262 
263 	rec = notify_nick_find(server, nick);
264 	if (rec != NULL && rec->join_announced) return;
265 	if (rec == NULL) rec = notify_nick_create(server, nick);
266 
267 	user = g_strdup(userhost == NULL ? "" : userhost);
268 	host = strchr(user, '@');
269 	if (host != NULL) *host++ = '\0'; else host = "";
270 
271 	if (!mask_match(SERVER(server), notify->mask, nick, user, host)) {
272 		g_free(user);
273 		return;
274 	}
275 
276 	if (notify->away_check && away == -1) {
277 		/* we need to know if the nick is away */
278 		g_free(user);
279 		return;
280 	}
281 
282 	g_free_not_null(rec->user);
283 	g_free_not_null(rec->host);
284 	g_free_not_null(rec->realname);
285 	rec->user = g_strdup(user);
286 	rec->host = g_strdup(host);
287 	rec->realname = realname == NULL || *realname == '\0' ? NULL : g_strdup(realname);
288 
289 	if (away != -1) rec->away = away;
290 	rec->host_ok = TRUE;
291 	rec->join_announced = TRUE;
292 	rec->away_ok = !notify->away_check || !rec->away;
293 
294 	signal_emit("notifylist joined", 6,
295 		    server, rec->nick, rec->user, rec->host, realname, NULL);
296 	g_free(user);
297 }
298 
event_privmsg(IRC_SERVER_REC * server,const char * data,const char * nick,const char * address)299 static void event_privmsg(IRC_SERVER_REC *server, const char *data,
300 			  const char *nick, const char *address)
301 {
302 	if (nick != NULL) {
303 		notifylist_check_join(server, nick, address, "", -1);
304 	}
305 }
306 
event_join(IRC_SERVER_REC * server,const char * data,const char * nick,const char * address)307 static void event_join(IRC_SERVER_REC *server, const char *data,
308 		       const char *nick, const char *address)
309 {
310 	notifylist_check_join(server, nick, address, "", -1);
311 }
312 
sig_channel_wholist(IRC_CHANNEL_REC * channel)313 static void sig_channel_wholist(IRC_CHANNEL_REC *channel)
314 {
315 	GSList *nicks, *tmp;
316 
317 	nicks = nicklist_getnicks(CHANNEL(channel));
318 	for (tmp = nicks; tmp != NULL; tmp = tmp->next) {
319 		NICK_REC *rec = tmp->data;
320 
321 		notifylist_check_join(channel->server, rec->nick, rec->host, rec->realname, rec->gone);
322 	}
323         g_slist_free(nicks);
324 }
325 
irc_notifylist_init(void)326 void irc_notifylist_init(void)
327 {
328 	notifylist_read_config();
329 
330 	notifylist_commands_init();
331 	notifylist_ison_init();
332 	notifylist_whois_init();
333 	signal_add("server connected", (SIGNAL_FUNC) notifylist_init_server);
334 	signal_add("server destroyed", (SIGNAL_FUNC) notifylist_deinit_server);
335 	signal_add("event quit", (SIGNAL_FUNC) event_quit);
336 	signal_add("event privmsg", (SIGNAL_FUNC) event_privmsg);
337 	signal_add("event join", (SIGNAL_FUNC) event_join);
338 	signal_add("channel wholist", (SIGNAL_FUNC) sig_channel_wholist);
339 	signal_add("setup reread", (SIGNAL_FUNC) notifylist_read_config);
340 
341 	settings_check();
342 	module_register("notifylist", "irc");
343 }
344 
irc_notifylist_deinit(void)345 void irc_notifylist_deinit(void)
346 {
347 	notifylist_commands_deinit();
348 	notifylist_ison_deinit();
349 	notifylist_whois_deinit();
350 
351 	signal_remove("server connected", (SIGNAL_FUNC) notifylist_init_server);
352 	signal_remove("server destroyed", (SIGNAL_FUNC) notifylist_deinit_server);
353 	signal_remove("event quit", (SIGNAL_FUNC) event_quit);
354 	signal_remove("event privmsg", (SIGNAL_FUNC) event_privmsg);
355 	signal_remove("event join", (SIGNAL_FUNC) event_join);
356 	signal_remove("channel wholist", (SIGNAL_FUNC) sig_channel_wholist);
357 	signal_remove("setup reread", (SIGNAL_FUNC) notifylist_read_config);
358 
359 	notifylist_destroy_all();
360 }
361