1 /* X-Chat
2 * Copyright (C) 1998 Peter Zelezny.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22
23 #include "hexchat.h"
24 #include "modes.h"
25 #include "fe.h"
26 #include "notify.h"
27 #include "tree.h"
28 #include "hexchatc.h"
29 #include "util.h"
30
31
32 int
nick_cmp_az_ops(server * serv,struct User * user1,struct User * user2)33 nick_cmp_az_ops (server *serv, struct User *user1, struct User *user2)
34 {
35 unsigned int access1 = user1->access;
36 unsigned int access2 = user2->access;
37 int pos;
38
39 if (access1 != access2)
40 {
41 for (pos = 0; pos < USERACCESS_SIZE; pos++)
42 {
43 if ((access1&(1<<pos)) && (access2&(1<<pos)))
44 break;
45 if ((access1&(1<<pos)) && !(access2&(1<<pos)))
46 return -1;
47 if (!(access1&(1<<pos)) && (access2&(1<<pos)))
48 return 1;
49 }
50 }
51
52 return serv->p_cmp (user1->nick, user2->nick);
53 }
54
55 int
nick_cmp_alpha(struct User * user1,struct User * user2,server * serv)56 nick_cmp_alpha (struct User *user1, struct User *user2, server *serv)
57 {
58 return serv->p_cmp (user1->nick, user2->nick);
59 }
60
61 /*
62 insert name in appropriate place in linked list. Returns row number or:
63 -1: duplicate
64 */
65
66 static int
userlist_insertname(session * sess,struct User * newuser)67 userlist_insertname (session *sess, struct User *newuser)
68 {
69 if (!sess->usertree)
70 {
71 sess->usertree = tree_new ((tree_cmp_func *)nick_cmp_alpha, sess->server);
72 }
73
74 return tree_insert (sess->usertree, newuser);
75 }
76
77 void
userlist_set_away(struct session * sess,char * nick,unsigned int away)78 userlist_set_away (struct session *sess, char *nick, unsigned int away)
79 {
80 struct User *user;
81
82 user = userlist_find (sess, nick);
83 if (user)
84 {
85 if (user->away != away)
86 {
87 user->away = away;
88 /* rehash GUI */
89 fe_userlist_rehash (sess, user);
90 if (away)
91 fe_userlist_update (sess, user);
92 }
93 }
94 }
95
96 void
userlist_set_account(struct session * sess,char * nick,char * account)97 userlist_set_account (struct session *sess, char *nick, char *account)
98 {
99 struct User *user;
100
101 user = userlist_find (sess, nick);
102 if (user)
103 {
104 if (strcmp (account, "*") == 0)
105 {
106 g_clear_pointer (&user->account, g_free);
107 } else if (g_strcmp0 (user->account, account))
108 {
109 g_free (user->account);
110 user->account = g_strdup (account);
111 }
112
113 /* gui doesnt currently reflect login status, maybe later
114 fe_userlist_rehash (sess, user); */
115 }
116 }
117
118 int
userlist_add_hostname(struct session * sess,char * nick,char * hostname,char * realname,char * servername,char * account,unsigned int away)119 userlist_add_hostname (struct session *sess, char *nick, char *hostname,
120 char *realname, char *servername, char *account, unsigned int away)
121 {
122 struct User *user;
123 gboolean do_rehash = FALSE;
124
125 user = userlist_find (sess, nick);
126 if (user)
127 {
128 if (hostname && (!user->hostname || strcmp(user->hostname, hostname)))
129 {
130 if (prefs.hex_gui_ulist_show_hosts)
131 do_rehash = TRUE;
132 g_free (user->hostname);
133 user->hostname = g_strdup (hostname);
134 }
135 if (realname && *realname && g_strcmp0 (user->realname, realname) != 0)
136 {
137 g_free (user->realname);
138 user->realname = g_strdup (realname);
139 }
140 if (!user->servername && servername)
141 user->servername = g_strdup (servername);
142 if (!user->account && account && strcmp (account, "0") != 0)
143 user->account = g_strdup (account);
144 if (away != 0xff)
145 {
146 if (user->away != away)
147 do_rehash = TRUE;
148 user->away = away;
149 }
150
151 fe_userlist_update (sess, user);
152 if (do_rehash)
153 fe_userlist_rehash (sess, user);
154
155 return 1;
156 }
157 return 0;
158 }
159
160 static int
free_user(struct User * user,gpointer data)161 free_user (struct User *user, gpointer data)
162 {
163 g_free (user->realname);
164 g_free (user->hostname);
165 g_free (user->servername);
166 g_free (user->account);
167 g_free (user);
168
169 return TRUE;
170 }
171
172 void
userlist_free(session * sess)173 userlist_free (session *sess)
174 {
175 tree_foreach (sess->usertree, (tree_traverse_func *)free_user, NULL);
176 tree_destroy (sess->usertree);
177
178 sess->usertree = NULL;
179 sess->me = NULL;
180
181 sess->ops = 0;
182 sess->hops = 0;
183 sess->voices = 0;
184 sess->total = 0;
185 }
186
187 void
userlist_clear(session * sess)188 userlist_clear (session *sess)
189 {
190 fe_userlist_clear (sess);
191 userlist_free (sess);
192 fe_userlist_numbers (sess);
193 }
194
195 static int
find_cmp(const char * name,struct User * user,server * serv)196 find_cmp (const char *name, struct User *user, server *serv)
197 {
198 return serv->p_cmp ((char *)name, user->nick);
199 }
200
201 struct User *
userlist_find(struct session * sess,const char * name)202 userlist_find (struct session *sess, const char *name)
203 {
204 int pos;
205
206 if (sess->usertree)
207 return tree_find (sess->usertree, name,
208 (tree_cmp_func *)find_cmp, sess->server, &pos);
209
210 return NULL;
211 }
212
213 struct User *
userlist_find_global(struct server * serv,char * name)214 userlist_find_global (struct server *serv, char *name)
215 {
216 struct User *user;
217 session *sess;
218 GSList *list = sess_list;
219 while (list)
220 {
221 sess = (session *) list->data;
222 if (sess->server == serv)
223 {
224 user = userlist_find (sess, name);
225 if (user)
226 return user;
227 }
228 list = list->next;
229 }
230 return NULL;
231 }
232
233 static void
update_counts(session * sess,struct User * user,char prefix,int level,int offset)234 update_counts (session *sess, struct User *user, char prefix,
235 int level, int offset)
236 {
237 switch (prefix)
238 {
239 case '@':
240 user->op = level;
241 sess->ops += offset;
242 break;
243 case '%':
244 user->hop = level;
245 sess->hops += offset;
246 break;
247 case '+':
248 user->voice = level;
249 sess->voices += offset;
250 break;
251 }
252 }
253
254 void
userlist_update_mode(session * sess,char * name,char mode,char sign)255 userlist_update_mode (session *sess, char *name, char mode, char sign)
256 {
257 int access;
258 int offset = 0;
259 int level;
260 int pos;
261 char prefix;
262 struct User *user;
263
264 user = userlist_find (sess, name);
265 if (!user)
266 return;
267
268 /* remove from binary trees, before we loose track of it */
269 tree_remove (sess->usertree, user, &pos);
270 fe_userlist_remove (sess, user);
271
272 /* which bit number is affected? */
273 access = mode_access (sess->server, mode, &prefix);
274
275 if (sign == '+')
276 {
277 level = TRUE;
278 if (!(user->access & (1 << access)))
279 {
280 offset = 1;
281 user->access |= (1 << access);
282 }
283 } else
284 {
285 level = FALSE;
286 if (user->access & (1 << access))
287 {
288 offset = -1;
289 user->access &= ~(1 << access);
290 }
291 }
292
293 /* now what is this users highest prefix? e.g. @ for ops */
294 user->prefix[0] = get_nick_prefix (sess->server, user->access);
295
296 /* update the various counts using the CHANGED prefix only */
297 update_counts (sess, user, prefix, level, offset);
298
299 /* insert it back into its new place */
300 tree_insert (sess->usertree, user);
301 fe_userlist_insert (sess, user, FALSE);
302 fe_userlist_numbers (sess);
303 }
304
305 int
userlist_change(struct session * sess,char * oldname,char * newname)306 userlist_change (struct session *sess, char *oldname, char *newname)
307 {
308 struct User *user = userlist_find (sess, oldname);
309 int pos;
310
311 if (user)
312 {
313 tree_remove (sess->usertree, user, &pos);
314 fe_userlist_remove (sess, user);
315
316 safe_strcpy (user->nick, newname, NICKLEN);
317
318 tree_insert (sess->usertree, user);
319 fe_userlist_insert (sess, user, FALSE);
320
321 return 1;
322 }
323
324 return 0;
325 }
326
327 int
userlist_remove(struct session * sess,char * name)328 userlist_remove (struct session *sess, char *name)
329 {
330 struct User *user;
331
332 user = userlist_find (sess, name);
333 if (!user)
334 return FALSE;
335
336 userlist_remove_user (sess, user);
337 return TRUE;
338 }
339
340 void
userlist_remove_user(struct session * sess,struct User * user)341 userlist_remove_user (struct session *sess, struct User *user)
342 {
343 int pos;
344 if (user->voice)
345 sess->voices--;
346 if (user->op)
347 sess->ops--;
348 if (user->hop)
349 sess->hops--;
350 sess->total--;
351 fe_userlist_numbers (sess);
352 fe_userlist_remove (sess, user);
353
354 if (user == sess->me)
355 sess->me = NULL;
356
357 tree_remove (sess->usertree, user, &pos);
358 free_user (user, NULL);
359 }
360
361 void
userlist_add(struct session * sess,char * name,char * hostname,char * account,char * realname,const message_tags_data * tags_data)362 userlist_add (struct session *sess, char *name, char *hostname,
363 char *account, char *realname, const message_tags_data *tags_data)
364 {
365 struct User *user;
366 int row, prefix_chars;
367 unsigned int acc;
368
369 acc = nick_access (sess->server, name, &prefix_chars);
370
371 notify_set_online (sess->server, name + prefix_chars, tags_data);
372
373 user = g_new0 (struct User, 1);
374
375 user->access = acc;
376
377 /* assume first char is the highest level nick prefix */
378 if (prefix_chars)
379 user->prefix[0] = name[0];
380
381 /* add it to our linked list */
382 if (hostname)
383 user->hostname = g_strdup (hostname);
384 safe_strcpy (user->nick, name + prefix_chars, NICKLEN);
385 /* is it me? */
386 if (!sess->server->p_cmp (user->nick, sess->server->nick))
387 user->me = TRUE;
388 /* extended join info */
389 if (sess->server->have_extjoin)
390 {
391 if (account && *account)
392 user->account = g_strdup (account);
393 if (realname && *realname)
394 user->realname = g_strdup (realname);
395 }
396
397 row = userlist_insertname (sess, user);
398
399 /* duplicate? some broken servers trigger this */
400 if (row == -1)
401 {
402 g_free (user->hostname);
403 g_free (user->account);
404 g_free (user->realname);
405 g_free (user);
406 return;
407 }
408
409 sess->total++;
410
411 /* most ircds don't support multiple modechars in front of the nickname
412 for /NAMES - though they should. */
413 while (prefix_chars)
414 {
415 update_counts (sess, user, name[0], TRUE, 1);
416 name++;
417 prefix_chars--;
418 }
419
420 if (user->me)
421 sess->me = user;
422
423 fe_userlist_insert (sess, user, FALSE);
424 if(sess->end_of_names)
425 fe_userlist_numbers (sess);
426 }
427
428 static int
rehash_cb(struct User * user,session * sess)429 rehash_cb (struct User *user, session *sess)
430 {
431 fe_userlist_rehash (sess, user);
432 return TRUE;
433 }
434
435 void
userlist_rehash(session * sess)436 userlist_rehash (session *sess)
437 {
438 tree_foreach (sess->usertree, (tree_traverse_func *)rehash_cb, sess);
439 }
440
441 static int
flat_cb(struct User * user,GSList ** list)442 flat_cb (struct User *user, GSList **list)
443 {
444 *list = g_slist_prepend (*list, user);
445 return TRUE;
446 }
447
448 GSList *
userlist_flat_list(session * sess)449 userlist_flat_list (session *sess)
450 {
451 GSList *list = NULL;
452
453 tree_foreach (sess->usertree, (tree_traverse_func *)flat_cb, &list);
454 return g_slist_reverse (list);
455 }
456
457 static int
double_cb(struct User * user,GList ** list)458 double_cb (struct User *user, GList **list)
459 {
460 *list = g_list_prepend(*list, user);
461 return TRUE;
462 }
463
464 GList *
userlist_double_list(session * sess)465 userlist_double_list(session *sess)
466 {
467 GList *list = NULL;
468
469 tree_foreach (sess->usertree, (tree_traverse_func *)double_cb, &list);
470 return list;
471 }
472