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 (i_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 /*MODULE_DATA_INIT(nick);*/
81 nick->type = module_get_uniq_id("NICK", 0);
82 nick->chat_type = channel->chat_type;
84 nick_hash_add(channel, nick);
85 signal_emit("nicklist new", 2, channel, nick);
88 /* Set host address for nick */
89 void nicklist_set_host(CHANNEL_REC *channel, NICK_REC *nick, const char *host)
91 g_return_if_fail(channel != NULL);
92 g_return_if_fail(nick != NULL);
93 g_return_if_fail(host != NULL);
95 g_free_not_null(nick->host);
96 nick->host = g_strdup(host);
98 signal_emit("nicklist host changed", 2, channel, nick);
101 static void nicklist_destroy(CHANNEL_REC *channel, NICK_REC *nick)
103 signal_emit("nicklist remove", 2, channel, nick);
105 if (channel->ownnick == nick)
106 channel->ownnick = NULL;
108 /*MODULE_DATA_DEINIT(nick);*/
110 g_free_not_null(nick->realname);
111 g_free_not_null(nick->host);
115 /* Remove nick from list */
116 void nicklist_remove(CHANNEL_REC *channel, NICK_REC *nick)
118 g_return_if_fail(IS_CHANNEL(channel));
119 g_return_if_fail(nick != NULL);
121 nick_hash_remove(channel, nick);
122 nicklist_destroy(channel, nick);
125 static void nicklist_rename_list(SERVER_REC *server, void *new_nick_id,
126 const char *old_nick, const char *new_nick,
129 CHANNEL_REC *channel;
133 for (tmp = nicks; tmp != NULL; tmp = tmp->next->next) {
135 nickrec = tmp->next->data;
137 /* remove old nick from hash table */
138 nick_hash_remove(channel, nickrec);
140 if (new_nick_id != NULL)
141 nickrec->unique_id = new_nick_id;
143 g_free(nickrec->nick);
144 nickrec->nick = g_strdup(new_nick);
146 /* add new nick to hash table */
147 nick_hash_add(channel, nickrec);
149 signal_emit("nicklist changed", 3, channel, nickrec, old_nick);
154 void nicklist_rename(SERVER_REC *server, const char *old_nick,
155 const char *new_nick)
157 nicklist_rename_list(server, NULL, old_nick, new_nick,
158 nicklist_get_same(server, old_nick));
161 void nicklist_rename_unique(SERVER_REC *server,
162 void *old_nick_id, const char *old_nick,
163 void *new_nick_id, const char *new_nick)
165 nicklist_rename_list(server, new_nick_id, old_nick, new_nick,
166 nicklist_get_same_unique(server, old_nick_id));
169 static NICK_REC *nicklist_find_wildcards(CHANNEL_REC *channel,
175 nicks = nicklist_getnicks(channel);
177 for (tmp = nicks; tmp != NULL; tmp = tmp->next) {
180 if (mask_match_address(channel->server, mask,
181 nick->nick, nick->host))
185 return tmp == NULL ? NULL : nick;
188 GSList *nicklist_find_multiple(CHANNEL_REC *channel, const char *mask)
190 GSList *nicks, *tmp, *next;
192 g_return_val_if_fail(IS_CHANNEL(channel), NULL);
193 g_return_val_if_fail(mask != NULL, NULL);
195 nicks = nicklist_getnicks(channel);
196 for (tmp = nicks; tmp != NULL; tmp = next) {
197 NICK_REC *nick = tmp->data;
200 if (!mask_match_address(channel->server, mask,
201 nick->nick, nick->host))
202 nicks = g_slist_remove(nicks, tmp->data);
209 NICK_REC *nicklist_find(CHANNEL_REC *channel, const char *nick)
211 g_return_val_if_fail(IS_CHANNEL(channel), NULL);
212 g_return_val_if_fail(nick != NULL, NULL);
214 return g_hash_table_lookup(channel->nicks, nick);
217 NICK_REC *nicklist_find_unique(CHANNEL_REC *channel, const char *nick,
222 g_return_val_if_fail(IS_CHANNEL(channel), NULL);
223 g_return_val_if_fail(nick != NULL, NULL);
225 rec = g_hash_table_lookup(channel->nicks, nick);
226 while (rec != NULL && rec->unique_id != id)
232 /* Find nick mask, wildcards allowed */
233 NICK_REC *nicklist_find_mask(CHANNEL_REC *channel, const char *mask)
238 g_return_val_if_fail(IS_CHANNEL(channel), NULL);
239 g_return_val_if_fail(mask != NULL, NULL);
241 nick = g_strdup(mask);
242 host = strchr(nick, '!');
243 if (host != NULL) *host++ = '\0';
245 if (strchr(nick, '*') || strchr(nick, '?')) {
247 return nicklist_find_wildcards(channel, mask);
250 nickrec = g_hash_table_lookup(channel->nicks, nick);
253 while (nickrec != NULL) {
254 if (nickrec->host != NULL &&
255 match_wildcards(host, nickrec->host))
257 nickrec = nickrec->next;
264 static void get_nicks_hash(gpointer key, NICK_REC *rec, GSList **list)
266 while (rec != NULL) {
267 *list = g_slist_append(*list, rec);
272 /* Get list of nicks */
273 GSList *nicklist_getnicks(CHANNEL_REC *channel)
277 g_return_val_if_fail(IS_CHANNEL(channel), NULL);
280 g_hash_table_foreach(channel->nicks, (GHFunc) get_nicks_hash, &list);
285 CHANNEL_REC *channel;
288 } NICKLIST_GET_SAME_REC;
290 static void get_nicks_same_hash(gpointer key, NICK_REC *nick,
291 NICKLIST_GET_SAME_REC *rec)
293 while (nick != NULL) {
294 if (g_strcasecmp(nick->nick, rec->nick) == 0) {
295 rec->list = g_slist_append(rec->list, rec->channel);
296 rec->list = g_slist_append(rec->list, nick);
303 GSList *nicklist_get_same(SERVER_REC *server, const char *nick)
305 NICKLIST_GET_SAME_REC rec;
308 g_return_val_if_fail(IS_SERVER(server), NULL);
312 for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
313 rec.channel = tmp->data;
314 g_hash_table_foreach(rec.channel->nicks,
315 (GHFunc) get_nicks_same_hash, &rec);
321 CHANNEL_REC *channel;
324 } NICKLIST_GET_SAME_UNIQUE_REC;
326 static void get_nicks_same_hash_unique(gpointer key, NICK_REC *nick,
327 NICKLIST_GET_SAME_UNIQUE_REC *rec)
329 while (nick != NULL) {
330 if (nick->unique_id == rec->id) {
331 rec->list = g_slist_append(rec->list, rec->channel);
332 rec->list = g_slist_append(rec->list, nick);
340 GSList *nicklist_get_same_unique(SERVER_REC *server, void *id)
342 NICKLIST_GET_SAME_UNIQUE_REC rec;
345 g_return_val_if_fail(IS_SERVER(server), NULL);
346 g_return_val_if_fail(id != NULL, NULL);
350 for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
351 rec.channel = tmp->data;
352 g_hash_table_foreach(rec.channel->nicks,
353 (GHFunc) get_nicks_same_hash_unique,
359 /* nick record comparision for sort functions */
360 int nicklist_compare(NICK_REC *p1, NICK_REC *p2)
362 int status1, status2;
364 if (p1 == NULL) return -1;
365 if (p2 == NULL) return 1;
367 /* we assign each status (op, halfop, voice, normal) a number
368 * and compare them. this is easier than 100,000 if's and
390 if (status1 < status2)
392 else if (status1 > status2)
395 return g_strcasecmp(p1->nick, p2->nick);
398 static void nicklist_update_flags_list(SERVER_REC *server, int gone,
399 int serverop, GSList *nicks)
402 CHANNEL_REC *channel;
405 g_return_if_fail(IS_SERVER(server));
407 for (tmp = nicks; tmp != NULL; tmp = tmp->next->next) {
409 rec = tmp->next->data;
411 rec->last_check = time(NULL);
413 if (gone != -1 && (int)rec->gone != gone) {
415 signal_emit("nicklist gone changed", 2, channel, rec);
418 if (serverop != -1 && (int)rec->serverop != serverop) {
419 rec->serverop = serverop;
420 signal_emit("nicklist serverop changed", 2, channel, rec);
426 void nicklist_update_flags(SERVER_REC *server, const char *nick,
427 int gone, int serverop)
429 nicklist_update_flags_list(server, gone, serverop,
430 nicklist_get_same(server, nick));
433 void nicklist_update_flags_unique(SERVER_REC *server, void *id,
434 int gone, int serverop)
436 nicklist_update_flags_list(server, gone, serverop,
437 nicklist_get_same_unique(server, id));
440 /* Specify which nick in channel is ours */
441 void nicklist_set_own(CHANNEL_REC *channel, NICK_REC *nick)
443 NICK_REC *first, *next;
445 channel->ownnick = nick;
447 /* move our nick in the list to first, makes some things easier
448 (like handling multiple identical nicks in fe-messages.c) */
449 first = g_hash_table_lookup(channel->nicks, nick->nick);
450 if (first->next == NULL)
456 while (first->next != nick)
460 g_hash_table_insert(channel->nicks, nick->nick, nick);
463 static void sig_channel_created(CHANNEL_REC *channel)
465 g_return_if_fail(IS_CHANNEL(channel));
467 channel->nicks = g_hash_table_new((GHashFunc) g_istr_hash,
468 (GCompareFunc) g_istr_equal);
471 static void nicklist_remove_hash(gpointer key, NICK_REC *nick,
472 CHANNEL_REC *channel)
476 while (nick != NULL) {
478 nicklist_destroy(channel, nick);
483 static void sig_channel_destroyed(CHANNEL_REC *channel)
485 g_return_if_fail(IS_CHANNEL(channel));
487 g_hash_table_foreach(channel->nicks,
488 (GHFunc) nicklist_remove_hash, channel);
489 g_hash_table_destroy(channel->nicks);
492 static NICK_REC *nick_nfind(CHANNEL_REC *channel, const char *nick, int len)
497 tmpnick = g_strndup(nick, len);
498 rec = g_hash_table_lookup(channel->nicks, tmpnick);
501 /* if there's multiple, get the one with identical case */
502 while (rec->next != NULL) {
503 if (strcmp(rec->nick, tmpnick) == 0)
513 /* Check is `msg' is meant for `nick'. */
514 int nick_match_msg(CHANNEL_REC *channel, const char *msg, const char *nick)
516 const char *msgstart, *orignick;
519 g_return_val_if_fail(nick != NULL, FALSE);
520 g_return_val_if_fail(msg != NULL, FALSE);
522 if (channel != NULL && channel->server->nick_match_msg != NULL)
523 return channel->server->nick_match_msg(msg, nick);
525 /* first check for identical match */
527 if (g_strncasecmp(msg, nick, len) == 0 && !isalnumhigh((int) msg[len]))
536 /* check if it matches for alphanumeric parts of nick */
537 while (*nick != '\0' && *msg != '\0') {
538 if (i_toupper(*nick) == i_toupper(*msg)) {
541 } else if (i_isalnum(*msg) && !i_isalnum(*nick)) {
542 /* some strange char in your nick, pass it */
550 if (msg != msgstart && !isalnumhigh(*msg)) {
551 /* at least some of the chars in line matched the
552 nick, and msg continue with non-alphanum character,
553 this might be for us.. */
555 /* remove the rest of the non-alphanum chars
556 from nick and check if it then matches. */
558 while (*nick != '\0' && !i_isalnum(*nick))
568 /* no match. check if this is a message to multiple people
569 (like nick1,nick2: text) */
570 while (*msg != '\0' && *msg != ' ' && *msg != ',') msg++;
581 return FALSE; /* didn't match */
584 return TRUE; /* matched without fuzzyness */
586 /* matched with some fuzzyness .. check if there's an exact match
587 for some other nick in the same channel. */
588 return nick_nfind(channel, msgstart, (int) (msg-msgstart)) == NULL;
591 void nicklist_init(void)
593 signal_add_first("channel created", (SIGNAL_FUNC) sig_channel_created);
594 signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
597 void nicklist_deinit(void)
599 signal_remove("channel created", (SIGNAL_FUNC) sig_channel_created);
600 signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
602 module_uniq_destroy("NICK");