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 <string.h>
21 #include <stdlib.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include <time.h>
26
27 #ifdef WIN32
28 #include <io.h>
29 #else
30 #include <unistd.h>
31 #endif
32
33 #include "hexchat.h"
34 #include "notify.h"
35 #include "cfgfiles.h"
36 #include "fe.h"
37 #include "server.h"
38 #include "text.h"
39 #include "util.h"
40 #include "hexchatc.h"
41
42
43 GSList *notify_list = 0;
44 int notify_tag = 0;
45
46
47 static char *
despacify_dup(char * str)48 despacify_dup (char *str)
49 {
50 char *p, *res = g_malloc (strlen (str) + 1);
51
52 p = res;
53 while (1)
54 {
55 if (*str != ' ')
56 {
57 *p = *str;
58 if (*p == 0)
59 return res;
60 p++;
61 }
62 str++;
63 }
64 }
65
66 static int
notify_netcmp(char * str,void * serv)67 notify_netcmp (char *str, void *serv)
68 {
69 char *net = despacify_dup (server_get_network (serv, TRUE));
70
71 if (rfc_casecmp (str, net) == 0)
72 {
73 g_free (net);
74 return 0; /* finish & return FALSE from token_foreach() */
75 }
76
77 g_free (net);
78 return 1; /* keep going... */
79 }
80
81 /* monitor this nick on this particular network? */
82
83 static gboolean
notify_do_network(struct notify * notify,server * serv)84 notify_do_network (struct notify *notify, server *serv)
85 {
86 if (!notify->networks) /* ALL networks for this nick */
87 return TRUE;
88
89 if (token_foreach (notify->networks, ',', notify_netcmp, serv))
90 return FALSE; /* network list doesn't contain this one */
91
92 return TRUE;
93 }
94
95 struct notify_per_server *
notify_find_server_entry(struct notify * notify,struct server * serv)96 notify_find_server_entry (struct notify *notify, struct server *serv)
97 {
98 GSList *list = notify->server_list;
99 struct notify_per_server *servnot;
100
101 while (list)
102 {
103 servnot = (struct notify_per_server *) list->data;
104 if (servnot->server == serv)
105 return servnot;
106 list = list->next;
107 }
108
109 /* not found, should we add it, or is this not a network where
110 we're monitoring this nick? */
111 if (!notify_do_network (notify, serv))
112 return NULL;
113
114 servnot = g_new0 (struct notify_per_server, 1);
115 servnot->server = serv;
116 servnot->notify = notify;
117 notify->server_list = g_slist_prepend (notify->server_list, servnot);
118 return servnot;
119 }
120
121 void
notify_save(void)122 notify_save (void)
123 {
124 int fh;
125 struct notify *notify;
126 GSList *list = notify_list;
127
128 fh = hexchat_open_file ("notify.conf", O_TRUNC | O_WRONLY | O_CREAT, 0600, XOF_DOMODE);
129 if (fh != -1)
130 {
131 while (list)
132 {
133 notify = (struct notify *) list->data;
134 write (fh, notify->name, strlen (notify->name));
135 if (notify->networks)
136 {
137 write (fh, " ", 1);
138 write (fh, notify->networks, strlen (notify->networks));
139 }
140 write (fh, "\n", 1);
141 list = list->next;
142 }
143 close (fh);
144 }
145 }
146
147 void
notify_load(void)148 notify_load (void)
149 {
150 int fh;
151 char buf[256];
152 char *sep;
153
154 fh = hexchat_open_file ("notify.conf", O_RDONLY, 0, 0);
155 if (fh != -1)
156 {
157 while (waitline (fh, buf, sizeof buf, FALSE) != -1)
158 {
159 if (buf[0] != '#' && buf[0] != 0)
160 {
161 sep = strchr (buf, ' ');
162 if (sep)
163 {
164 sep[0] = 0;
165 notify_adduser (buf, sep + 1);
166 }
167 else
168 notify_adduser (buf, NULL);
169 }
170 }
171 close (fh);
172 }
173 }
174
175 static struct notify_per_server *
notify_find(server * serv,char * nick)176 notify_find (server *serv, char *nick)
177 {
178 GSList *list = notify_list;
179 struct notify_per_server *servnot;
180 struct notify *notify;
181
182 while (list)
183 {
184 notify = (struct notify *) list->data;
185
186 servnot = notify_find_server_entry (notify, serv);
187 if (!servnot)
188 {
189 list = list->next;
190 continue;
191 }
192
193 if (!serv->p_cmp (notify->name, nick))
194 return servnot;
195
196 list = list->next;
197 }
198
199 return NULL;
200 }
201
202 static void
notify_announce_offline(server * serv,struct notify_per_server * servnot,char * nick,int quiet,const message_tags_data * tags_data)203 notify_announce_offline (server * serv, struct notify_per_server *servnot,
204 char *nick, int quiet,
205 const message_tags_data *tags_data)
206 {
207 session *sess;
208
209 sess = serv->front_session;
210
211 servnot->ison = FALSE;
212 servnot->lastoff = time (0);
213 if (!quiet)
214 EMIT_SIGNAL_TIMESTAMP (XP_TE_NOTIFYOFFLINE, sess, nick, serv->servername,
215 server_get_network (serv, TRUE), NULL, 0,
216 tags_data->timestamp);
217 fe_notify_update (nick);
218 fe_notify_update (0);
219 }
220
221 static void
notify_announce_online(server * serv,struct notify_per_server * servnot,char * nick,const message_tags_data * tags_data)222 notify_announce_online (server * serv, struct notify_per_server *servnot,
223 char *nick, const message_tags_data *tags_data)
224 {
225 session *sess;
226
227 sess = serv->front_session;
228
229 servnot->lastseen = time (0);
230 if (servnot->ison)
231 return;
232
233 servnot->ison = TRUE;
234 servnot->laston = time (0);
235 EMIT_SIGNAL_TIMESTAMP (XP_TE_NOTIFYONLINE, sess, nick, serv->servername,
236 server_get_network (serv, TRUE), NULL, 0,
237 tags_data->timestamp);
238 fe_notify_update (nick);
239 fe_notify_update (0);
240
241 if (prefs.hex_notify_whois_online)
242 {
243
244 /* Let's do whois with idle time (like in /quote WHOIS %s %s) */
245
246 char *wii_str = g_strdup_printf ("%s %s", nick, nick);
247 serv->p_whois (serv, wii_str);
248 g_free (wii_str);
249 }
250 }
251
252 /* handles numeric 601 */
253
254 void
notify_set_offline(server * serv,char * nick,int quiet,const message_tags_data * tags_data)255 notify_set_offline (server * serv, char *nick, int quiet,
256 const message_tags_data *tags_data)
257 {
258 struct notify_per_server *servnot;
259
260 servnot = notify_find (serv, nick);
261 if (!servnot)
262 return;
263
264 notify_announce_offline (serv, servnot, nick, quiet, tags_data);
265 }
266
267 /* handles numeric 604 and 600 */
268
269 void
notify_set_online(server * serv,char * nick,const message_tags_data * tags_data)270 notify_set_online (server * serv, char *nick,
271 const message_tags_data *tags_data)
272 {
273 struct notify_per_server *servnot;
274
275 servnot = notify_find (serv, nick);
276 if (!servnot)
277 return;
278
279 notify_announce_online (serv, servnot, nick, tags_data);
280 }
281
282 /* monitor can send lists for numeric 730/731 */
283
284 void
notify_set_offline_list(server * serv,char * users,int quiet,const message_tags_data * tags_data)285 notify_set_offline_list (server * serv, char *users, int quiet,
286 const message_tags_data *tags_data)
287 {
288 struct notify_per_server *servnot;
289 char nick[NICKLEN];
290 char *token, *chr;
291
292 token = strtok (users, ",");
293 while (token != NULL)
294 {
295 chr = strchr (token, '!');
296 if (chr != NULL)
297 *chr = '\0';
298
299 g_strlcpy (nick, token, sizeof(nick));
300
301 servnot = notify_find (serv, nick);
302 if (servnot)
303 notify_announce_offline (serv, servnot, nick, quiet, tags_data);
304
305 token = strtok (NULL, ",");
306 }
307 }
308
309 void
notify_set_online_list(server * serv,char * users,const message_tags_data * tags_data)310 notify_set_online_list (server * serv, char *users,
311 const message_tags_data *tags_data)
312 {
313 struct notify_per_server *servnot;
314 char nick[NICKLEN];
315 char *token, *chr;
316
317 token = strtok (users, ",");
318 while (token != NULL)
319 {
320 chr = strchr (token, '!');
321 if (chr != NULL)
322 *chr = '\0';
323
324 g_strlcpy (nick, token, sizeof(nick));
325
326 servnot = notify_find (serv, nick);
327 if (servnot)
328 notify_announce_online (serv, servnot, nick, tags_data);
329
330 token = strtok (NULL, ",");
331 }
332 }
333
334 static void
notify_watch(server * serv,char * nick,int add)335 notify_watch (server * serv, char *nick, int add)
336 {
337 char tbuf[256];
338 char addchar = '+';
339
340 if (!add)
341 addchar = '-';
342
343 if (serv->supports_monitor)
344 g_snprintf (tbuf, sizeof (tbuf), "MONITOR %c %s", addchar, nick);
345 else if (serv->supports_watch)
346 g_snprintf (tbuf, sizeof (tbuf), "WATCH %c%s", addchar, nick);
347 else
348 return;
349
350 serv->p_raw (serv, tbuf);
351 }
352
353 static void
notify_watch_all(struct notify * notify,int add)354 notify_watch_all (struct notify *notify, int add)
355 {
356 server *serv;
357 GSList *list = serv_list;
358 while (list)
359 {
360 serv = list->data;
361 if (serv->connected && serv->end_of_motd && notify_do_network (notify, serv))
362 notify_watch (serv, notify->name, add);
363 list = list->next;
364 }
365 }
366
367 static void
notify_flush_watches(server * serv,GSList * from,GSList * end)368 notify_flush_watches (server * serv, GSList *from, GSList *end)
369 {
370 char tbuf[512];
371 GSList *list;
372 struct notify *notify;
373
374 serv->supports_monitor ? strcpy (tbuf, "MONITOR + ") : strcpy (tbuf, "WATCH");
375
376 list = from;
377 while (list != end)
378 {
379 notify = list->data;
380 if (serv->supports_monitor)
381 g_strlcat (tbuf, ",", sizeof(tbuf));
382 else
383 g_strlcat (tbuf, " +", sizeof(tbuf));
384 g_strlcat (tbuf, notify->name, sizeof(tbuf));
385 list = list->next;
386 }
387 serv->p_raw (serv, tbuf);
388 }
389
390 /* called when logging in. e.g. when End of motd. */
391
392 void
notify_send_watches(server * serv)393 notify_send_watches (server * serv)
394 {
395 struct notify *notify;
396 const int format_len = serv->supports_monitor ? 1 : 2; /* just , for monitor or + and space for watch */
397 GSList *list;
398 GSList *point;
399 GSList *send_list = NULL;
400 int len = 0;
401
402 /* Only get the list for this network */
403 list = notify_list;
404 while (list)
405 {
406 notify = list->data;
407
408 if (notify_do_network (notify, serv))
409 {
410 send_list = g_slist_append (send_list, notify);
411 }
412
413 list = list->next;
414 }
415
416 /* Now send that list in batches */
417 point = list = send_list;
418 while (list)
419 {
420 notify = list->data;
421
422 len += strlen (notify->name) + format_len;
423 if (len > 500)
424 {
425 /* Too long send existing list */
426 notify_flush_watches (serv, point, list);
427 len = strlen (notify->name) + format_len;
428 point = list; /* We left off here */
429 }
430
431 list = g_slist_next (list);
432 }
433
434 if (len) /* We had leftovers under 500, send them all */
435 {
436 notify_flush_watches (serv, point, NULL);
437 }
438
439 g_slist_free (send_list);
440 }
441
442 /* called when receiving a ISON 303 - should this func go? */
443
444 void
notify_markonline(server * serv,char * word[],const message_tags_data * tags_data)445 notify_markonline (server *serv, char *word[], const message_tags_data *tags_data)
446 {
447 struct notify *notify;
448 struct notify_per_server *servnot;
449 GSList *list = notify_list;
450 int i, seen;
451
452 while (list)
453 {
454 notify = (struct notify *) list->data;
455 servnot = notify_find_server_entry (notify, serv);
456 if (!servnot)
457 {
458 list = list->next;
459 continue;
460 }
461 i = 4;
462 seen = FALSE;
463 while (*word[i])
464 {
465 if (!serv->p_cmp (notify->name, word[i]))
466 {
467 seen = TRUE;
468 notify_announce_online (serv, servnot, notify->name, tags_data);
469 break;
470 }
471 i++;
472 /* FIXME: word[] is only a 32 element array, limits notify list to
473 about 27 people */
474 if (i > PDIWORDS - 5)
475 {
476 /*fprintf (stderr, _("*** HEXCHAT WARNING: notify list too large.\n"));*/
477 break;
478 }
479 }
480 if (!seen && servnot->ison)
481 {
482 notify_announce_offline (serv, servnot, notify->name, FALSE, tags_data);
483 }
484 list = list->next;
485 }
486 fe_notify_update (0);
487 }
488
489 /* yuck! Old routine for ISON notify */
490
491 static void
notify_checklist_for_server(server * serv)492 notify_checklist_for_server (server *serv)
493 {
494 char outbuf[512];
495 struct notify *notify;
496 GSList *list = notify_list;
497 int i = 0;
498
499 strcpy (outbuf, "ISON ");
500 while (list)
501 {
502 notify = list->data;
503 if (notify_do_network (notify, serv))
504 {
505 i++;
506 strcat (outbuf, notify->name);
507 strcat (outbuf, " ");
508 if (strlen (outbuf) > 460)
509 {
510 /* LAME: we can't send more than 512 bytes to the server, but *
511 * if we split it in two packets, our offline detection wouldn't *
512 work */
513 /*fprintf (stderr, _("*** HEXCHAT WARNING: notify list too large.\n"));*/
514 break;
515 }
516 }
517 list = list->next;
518 }
519
520 if (i)
521 serv->p_raw (serv, outbuf);
522 }
523
524 int
notify_checklist(void)525 notify_checklist (void) /* check ISON list */
526 {
527 struct server *serv;
528 GSList *list = serv_list;
529
530 while (list)
531 {
532 serv = list->data;
533 if (serv->connected && serv->end_of_motd && !serv->supports_watch && !serv->supports_monitor)
534 {
535 notify_checklist_for_server (serv);
536 }
537 list = list->next;
538 }
539 return 1;
540 }
541
542 void
notify_showlist(struct session * sess,const message_tags_data * tags_data)543 notify_showlist (struct session *sess, const message_tags_data *tags_data)
544 {
545 char outbuf[256];
546 struct notify *notify;
547 GSList *list = notify_list;
548 struct notify_per_server *servnot;
549 int i = 0;
550
551 EMIT_SIGNAL_TIMESTAMP (XP_TE_NOTIFYHEAD, sess, NULL, NULL, NULL, NULL, 0,
552 tags_data->timestamp);
553 while (list)
554 {
555 i++;
556 notify = (struct notify *) list->data;
557 servnot = notify_find_server_entry (notify, sess->server);
558 if (servnot && servnot->ison)
559 g_snprintf (outbuf, sizeof (outbuf), _(" %-20s online\n"), notify->name);
560 else
561 g_snprintf (outbuf, sizeof (outbuf), _(" %-20s offline\n"), notify->name);
562 PrintTextTimeStamp (sess, outbuf, tags_data->timestamp);
563 list = list->next;
564 }
565 if (i)
566 {
567 sprintf (outbuf, "%d", i);
568 EMIT_SIGNAL_TIMESTAMP (XP_TE_NOTIFYNUMBER, sess, outbuf, NULL, NULL, NULL,
569 0, tags_data->timestamp);
570 } else
571 EMIT_SIGNAL_TIMESTAMP (XP_TE_NOTIFYEMPTY, sess, NULL, NULL, NULL, NULL, 0,
572 tags_data->timestamp);
573 }
574
575 int
notify_deluser(char * name)576 notify_deluser (char *name)
577 {
578 struct notify *notify;
579 struct notify_per_server *servnot;
580 GSList *list = notify_list;
581
582 while (list)
583 {
584 notify = (struct notify *) list->data;
585 if (!rfc_casecmp (notify->name, name))
586 {
587 fe_notify_update (notify->name);
588 /* Remove the records for each server */
589 while (notify->server_list)
590 {
591 servnot = (struct notify_per_server *) notify->server_list->data;
592 notify->server_list =
593 g_slist_remove (notify->server_list, servnot);
594 g_free (servnot);
595 }
596 notify_list = g_slist_remove (notify_list, notify);
597 notify_watch_all (notify, FALSE);
598 g_free (notify->networks);
599 g_free (notify->name);
600 g_free (notify);
601 fe_notify_update (0);
602 return 1;
603 }
604 list = list->next;
605 }
606 return 0;
607 }
608
609 void
notify_adduser(char * name,char * networks)610 notify_adduser (char *name, char *networks)
611 {
612 struct notify *notify = g_new0 (struct notify, 1);
613
614 notify->name = g_strndup (name, NICKLEN - 1);
615
616 if (networks != NULL)
617 notify->networks = despacify_dup (networks);
618 notify->server_list = 0;
619 notify_list = g_slist_prepend (notify_list, notify);
620 notify_checklist ();
621 fe_notify_update (notify->name);
622 fe_notify_update (0);
623 notify_watch_all (notify, TRUE);
624 }
625
626 gboolean
notify_is_in_list(server * serv,char * name)627 notify_is_in_list (server *serv, char *name)
628 {
629 struct notify *notify;
630 GSList *list = notify_list;
631
632 while (list)
633 {
634 notify = (struct notify *) list->data;
635 if (!serv->p_cmp (notify->name, name))
636 return TRUE;
637 list = list->next;
638 }
639
640 return FALSE;
641 }
642
643 int
notify_isnotify(struct session * sess,char * name)644 notify_isnotify (struct session *sess, char *name)
645 {
646 struct notify *notify;
647 struct notify_per_server *servnot;
648 GSList *list = notify_list;
649
650 while (list)
651 {
652 notify = (struct notify *) list->data;
653 if (!sess->server->p_cmp (notify->name, name))
654 {
655 servnot = notify_find_server_entry (notify, sess->server);
656 if (servnot && servnot->ison)
657 return TRUE;
658 }
659 list = list->next;
660 }
661
662 return FALSE;
663 }
664
665 void
notify_cleanup()666 notify_cleanup ()
667 {
668 GSList *list = notify_list;
669 GSList *nslist, *srvlist;
670 struct notify *notify;
671 struct notify_per_server *servnot;
672 struct server *serv;
673 int valid;
674
675 while (list)
676 {
677 /* Traverse the list of notify structures */
678 notify = (struct notify *) list->data;
679 nslist = notify->server_list;
680 while (nslist)
681 {
682 /* Look at each per-server structure */
683 servnot = (struct notify_per_server *) nslist->data;
684
685 /* Check the server is valid */
686 valid = FALSE;
687 srvlist = serv_list;
688 while (srvlist)
689 {
690 serv = (struct server *) srvlist->data;
691 if (servnot->server == serv)
692 {
693 valid = serv->connected; /* Only valid if server is too */
694 break;
695 }
696 srvlist = srvlist->next;
697 }
698 if (!valid)
699 {
700 notify->server_list =
701 g_slist_remove (notify->server_list, servnot);
702 g_free (servnot);
703 nslist = notify->server_list;
704 } else
705 {
706 nslist = nslist->next;
707 }
708 }
709 list = list->next;
710 }
711 fe_notify_update (0);
712 }
713