4 Copyright (C) 1999-2000 Timo Sirainen
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.
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.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 #define isalnumhigh(a) \
31 (isalnum(a) || (unsigned char) (a) >= 128)
33 static void nick_hash_add(CHANNEL_REC *channel, NICK_REC *nick)
39 list = g_hash_table_lookup(channel->nicks, nick->nick);
41 g_hash_table_insert(channel->nicks, nick->nick, nick);
43 /* multiple nicks with same name */
44 while (list->next != NULL)
49 if (nick == channel->ownnick) {
50 /* move our own nick to beginning of the nick list.. */
51 nicklist_set_own(channel, nick);
55 static void nick_hash_remove(CHANNEL_REC *channel, NICK_REC *nick)
59 list = g_hash_table_lookup(channel->nicks, nick->nick);
63 if (list == nick || list->next == NULL) {
64 g_hash_table_remove(channel->nicks, nick->nick);
65 if (list->next != NULL) {
66 g_hash_table_insert(channel->nicks, nick->next->nick,
70 while (list->next != nick)
72 list->next = nick->next;
76 /* Add new nick to list */
77 void nicklist_insert(CHANNEL_REC *channel, NICK_REC *nick)
79 nick->type = module_get_uniq_id("NICK", 0);
80 nick->chat_type = channel->chat_type;
82 nick_hash_add(channel, nick);
83 signal_emit("nicklist new", 2, channel, nick);
86 /* Set host address for nick */
87 void nicklist_set_host(CHANNEL_REC *channel, NICK_REC *nick, const char *host)
89 g_return_if_fail(channel != NULL);
90 g_return_if_fail(nick != NULL);
91 g_return_if_fail(host != NULL);
93 g_free_not_null(nick->host);
94 nick->host = g_strdup(host);
96 signal_emit("nicklist host changed", 2, channel, nick);
99 static void nicklist_destroy(CHANNEL_REC *channel, NICK_REC *nick)
101 signal_emit("nicklist remove", 2, channel, nick);
104 g_free_not_null(nick->realname);
105 g_free_not_null(nick->host);
109 /* Remove nick from list */
110 void nicklist_remove(CHANNEL_REC *channel, NICK_REC *nick)
112 g_return_if_fail(IS_CHANNEL(channel));
113 g_return_if_fail(nick != NULL);
115 nick_hash_remove(channel, nick);
116 nicklist_destroy(channel, nick);
119 static void nicklist_rename_list(SERVER_REC *server, void *new_nick_id,
120 const char *old_nick, const char *new_nick,
123 CHANNEL_REC *channel;
127 for (tmp = nicks; tmp != NULL; tmp = tmp->next->next) {
129 nickrec = tmp->next->data;
131 /* remove old nick from hash table */
132 nick_hash_remove(channel, nickrec);
134 if (new_nick_id != NULL)
135 nickrec->unique_id = new_nick_id;
137 g_free(nickrec->nick);
138 nickrec->nick = g_strdup(new_nick);
140 /* add new nick to hash table */
141 nick_hash_add(channel, nickrec);
143 signal_emit("nicklist changed", 3, channel, nickrec, old_nick);
148 void nicklist_rename(SERVER_REC *server, const char *old_nick,
149 const char *new_nick)
151 nicklist_rename_list(server, NULL, old_nick, new_nick,
152 nicklist_get_same(server, old_nick));
155 void nicklist_rename_unique(SERVER_REC *server,
156 void *old_nick_id, const char *old_nick,
157 void *new_nick_id, const char *new_nick)
159 nicklist_rename_list(server, new_nick_id, old_nick, new_nick,
160 nicklist_get_same_unique(server, old_nick_id));
163 static NICK_REC *nicklist_find_wildcards(CHANNEL_REC *channel,
169 nicks = nicklist_getnicks(channel);
171 for (tmp = nicks; tmp != NULL; tmp = tmp->next) {
174 if (mask_match_address(channel->server, mask,
175 nick->nick, nick->host))
179 return tmp == NULL ? NULL : nick;
182 GSList *nicklist_find_multiple(CHANNEL_REC *channel, const char *mask)
184 GSList *nicks, *tmp, *next;
186 g_return_val_if_fail(IS_CHANNEL(channel), NULL);
187 g_return_val_if_fail(mask != NULL, NULL);
189 nicks = nicklist_getnicks(channel);
190 for (tmp = nicks; tmp != NULL; tmp = next) {
191 NICK_REC *nick = tmp->data;
194 if (!mask_match_address(channel->server, mask,
195 nick->nick, nick->host))
196 nicks = g_slist_remove(nicks, tmp->data);
203 NICK_REC *nicklist_find(CHANNEL_REC *channel, const char *nick)
205 g_return_val_if_fail(IS_CHANNEL(channel), NULL);
206 g_return_val_if_fail(nick != NULL, NULL);
208 return g_hash_table_lookup(channel->nicks, nick);
211 NICK_REC *nicklist_find_unique(CHANNEL_REC *channel, const char *nick,
216 g_return_val_if_fail(IS_CHANNEL(channel), NULL);
217 g_return_val_if_fail(nick != NULL, NULL);
219 rec = g_hash_table_lookup(channel->nicks, nick);
220 while (rec != NULL && rec->unique_id != id)
226 /* Find nick mask, wildcards allowed */
227 NICK_REC *nicklist_find_mask(CHANNEL_REC *channel, const char *mask)
232 g_return_val_if_fail(IS_CHANNEL(channel), NULL);
233 g_return_val_if_fail(mask != NULL, NULL);
235 nick = g_strdup(mask);
236 host = strchr(nick, '!');
237 if (host != NULL) *host++ = '\0';
239 if (strchr(nick, '*') || strchr(nick, '?')) {
241 return nicklist_find_wildcards(channel, mask);
244 nickrec = g_hash_table_lookup(channel->nicks, nick);
247 while (nickrec != NULL) {
248 if (nickrec->host != NULL &&
249 match_wildcards(host, nickrec->host))
251 nickrec = nickrec->next;
258 static void get_nicks_hash(gpointer key, NICK_REC *rec, GSList **list)
260 while (rec != NULL) {
261 *list = g_slist_append(*list, rec);
266 /* Get list of nicks */
267 GSList *nicklist_getnicks(CHANNEL_REC *channel)
271 g_return_val_if_fail(IS_CHANNEL(channel), NULL);
274 g_hash_table_foreach(channel->nicks, (GHFunc) get_nicks_hash, &list);
279 CHANNEL_REC *channel;
282 } NICKLIST_GET_SAME_REC;
284 static void get_nicks_same_hash(gpointer key, NICK_REC *nick,
285 NICKLIST_GET_SAME_REC *rec)
287 while (nick != NULL) {
288 if (g_strcasecmp(nick->nick, rec->nick) == 0) {
289 rec->list = g_slist_append(rec->list, rec->channel);
290 rec->list = g_slist_append(rec->list, nick);
297 GSList *nicklist_get_same(SERVER_REC *server, const char *nick)
299 NICKLIST_GET_SAME_REC rec;
302 g_return_val_if_fail(IS_SERVER(server), NULL);
306 for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
307 rec.channel = tmp->data;
308 g_hash_table_foreach(rec.channel->nicks,
309 (GHFunc) get_nicks_same_hash, &rec);
315 CHANNEL_REC *channel;
318 } NICKLIST_GET_SAME_UNIQUE_REC;
320 static void get_nicks_same_hash_unique(gpointer key, NICK_REC *nick,
321 NICKLIST_GET_SAME_UNIQUE_REC *rec)
323 while (nick != NULL) {
324 if (nick->unique_id == rec->id) {
325 rec->list = g_slist_append(rec->list, rec->channel);
326 rec->list = g_slist_append(rec->list, nick);
334 GSList *nicklist_get_same_unique(SERVER_REC *server, void *id)
336 NICKLIST_GET_SAME_UNIQUE_REC rec;
339 g_return_val_if_fail(IS_SERVER(server), NULL);
340 g_return_val_if_fail(id != NULL, NULL);
344 for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
345 rec.channel = tmp->data;
346 g_hash_table_foreach(rec.channel->nicks,
347 (GHFunc) get_nicks_same_hash_unique,
353 /* nick record comparision for sort functions */
354 int nicklist_compare(NICK_REC *p1, NICK_REC *p2)
356 if (p1 == NULL) return -1;
357 if (p2 == NULL) return 1;
359 if (p1->op && !p2->op) return -1;
360 if (!p1->op && p2->op) return 1;
363 if (p1->voice && !p2->voice) return -1;
364 if (!p1->voice && p2->voice) return 1;
367 return g_strcasecmp(p1->nick, p2->nick);
370 static void nicklist_update_flags_list(SERVER_REC *server, int gone,
371 int serverop, GSList *nicks)
374 CHANNEL_REC *channel;
377 g_return_if_fail(IS_SERVER(server));
379 for (tmp = nicks; tmp != NULL; tmp = tmp->next->next) {
381 rec = tmp->next->data;
383 rec->last_check = time(NULL);
385 if (gone != -1 && (int)rec->gone != gone) {
387 signal_emit("nicklist gone changed", 2, channel, rec);
390 if (serverop != -1 && (int)rec->serverop != serverop) {
391 rec->serverop = serverop;
392 signal_emit("nicklist serverop changed", 2, channel, rec);
398 void nicklist_update_flags(SERVER_REC *server, const char *nick,
399 int gone, int serverop)
401 nicklist_update_flags_list(server, gone, serverop,
402 nicklist_get_same(server, nick));
405 void nicklist_update_flags_unique(SERVER_REC *server, void *id,
406 int gone, int serverop)
408 nicklist_update_flags_list(server, gone, serverop,
409 nicklist_get_same_unique(server, id));
412 /* Specify which nick in channel is ours */
413 void nicklist_set_own(CHANNEL_REC *channel, NICK_REC *nick)
415 NICK_REC *first, *next;
417 channel->ownnick = nick;
419 /* move our nick in the list to first, makes some things easier
420 (like handling multiple identical nicks in fe-messages.c) */
421 first = g_hash_table_lookup(channel->nicks, nick->nick);
422 if (first->next == NULL)
428 while (first->next != nick)
432 g_hash_table_insert(channel->nicks, nick->nick, nick);
435 static void sig_channel_created(CHANNEL_REC *channel)
437 g_return_if_fail(IS_CHANNEL(channel));
439 channel->nicks = g_hash_table_new((GHashFunc) g_istr_hash,
440 (GCompareFunc) g_istr_equal);
443 static void nicklist_remove_hash(gpointer key, NICK_REC *nick,
444 CHANNEL_REC *channel)
448 while (nick != NULL) {
450 nicklist_destroy(channel, nick);
455 static void sig_channel_destroyed(CHANNEL_REC *channel)
457 g_return_if_fail(IS_CHANNEL(channel));
459 g_hash_table_foreach(channel->nicks,
460 (GHFunc) nicklist_remove_hash, channel);
461 g_hash_table_destroy(channel->nicks);
464 static NICK_REC *nick_nfind(CHANNEL_REC *channel, const char *nick, int len)
469 tmpnick = g_strndup(nick, len);
470 rec = g_hash_table_lookup(channel->nicks, tmpnick);
473 /* if there's multiple, get the one with identical case */
474 while (rec->next != NULL) {
475 if (strcmp(rec->nick, tmpnick) == 0)
485 /* Check is `msg' is meant for `nick'. */
486 int nick_match_msg(CHANNEL_REC *channel, const char *msg, const char *nick)
488 const char *msgstart, *orignick;
491 g_return_val_if_fail(nick != NULL, FALSE);
492 g_return_val_if_fail(msg != NULL, FALSE);
494 if (channel != NULL && channel->server->nick_match_msg != NULL)
495 return channel->server->nick_match_msg(msg, nick);
497 /* first check for identical match */
499 if (g_strncasecmp(msg, nick, len) == 0 && !isalnumhigh((int) msg[len]))
508 /* check if it matches for alphanumeric parts of nick */
509 while (*nick != '\0' && *msg != '\0') {
510 if (toupper(*nick) == toupper(*msg)) {
513 } else if (isalnum(*msg) && !isalnum(*nick)) {
514 /* some strange char in your nick, pass it */
522 if (msg != msgstart && !isalnumhigh(*msg)) {
523 /* at least some of the chars in line matched the
524 nick, and msg continue with non-alphanum character,
525 this might be for us.. */
527 /* remove the rest of the non-alphanum chars
528 from nick and check if it then matches. */
530 while (*nick != '\0' && !isalnum(*nick))
540 /* no match. check if this is a message to multiple people
541 (like nick1,nick2: text) */
542 while (*msg != '\0' && *msg != ' ' && *msg != ',') msg++;
553 return FALSE; /* didn't match */
556 return TRUE; /* matched without fuzzyness */
558 /* matched with some fuzzyness .. check if there's an exact match
559 for some other nick in the same channel. */
560 return nick_nfind(channel, msgstart, (int) (msg-msgstart)) == NULL;
563 void nicklist_init(void)
565 signal_add_first("channel created", (SIGNAL_FUNC) sig_channel_created);
566 signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
569 void nicklist_deinit(void)
571 signal_remove("channel created", (SIGNAL_FUNC) sig_channel_created);
572 signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
574 module_uniq_destroy("NICK");