1 #ifdef RCS
2 static char rcsid[]="$Id: seen.c,v 1.1.1.1 2000/11/13 02:42:47 holsta Exp $";
3 #endif
4 /******************************************************************************
5  *                    Internetting Cooperating Programmers
6  * ----------------------------------------------------------------------------
7  *
8  *  ____    PROJECT
9  * |  _ \  __ _ _ __   ___ ___ _ __
10  * | | | |/ _` | '_ \ / __/ _ \ '__|
11  * | |_| | (_| | | | | (_|  __/ |
12  * |____/ \__,_|_| |_|\___\___|_|   the IRC bot
13  *
14  * All files in this archive are subject to the GNU General Public License.
15  *
16  * $Source: /cvsroot/dancer/dancer/src/seen.c,v $
17  * $Revision: 1.1.1.1 $
18  * $Date: 2000/11/13 02:42:47 $
19  * $Author: holsta $
20  * $State: Exp $
21  * $Locker:  $
22  *
23  * ---------------------------------------------------------------------------
24  *****************************************************************************/
25 
26 #include "dancer.h"
27 #include "trio.h"
28 #include "strio.h"
29 #include "function.h"
30 #include "user.h"
31 #include "transfer.h"
32 #include "seen.h"
33 
34 /* --- Global ----------------------------------------------------- */
35 
36 extern time_t now;
37 
38 extern char seenfile[];
39 extern int seenmonths;
40 extern itemident *current;
41 
42 int numSeenNicks = 0; /* This is not actually very truthful */
43 int numSeenHosts = 0;
44 
45 struct Seenstruct SeenHead = {
46   NULL, NULL, NULL,
47   NULL, NULL,
48   0, 0,
49   NULL, NULL,
50   0
51 };
52 
53 itemseen *seenHead = &SeenHead;
54 
55 itemaux **hashnick = NULL;
56 itemseen **hashsite = NULL;
57 
58 
59 static long changedseenitem = 0;
60 
61 /* --- SeenReport ------------------------------------------------- */
62 
SeenReport(char * from,char * nick,time_t departure,itemseen * ps)63 static void SeenReport(char *from, char *nick, time_t departure, itemseen *ps)
64 {
65   snapshot;
66   switch (ps->type) {
67 
68     case SEENLEAVE:
69       Sendf(from, GetText(msg_seenleave),
70             nick, ps->host, TimeAgo(departure),
71             ps->msg ? " (" : "", ps->msg ? ps->msg : "", ps->msg ? ")" : "");
72       break;
73 
74     case SEENQUIT:
75       Sendf(from, GetText(msg_seenquit),
76             nick, ps->host, TimeAgo(departure), ps->msg ? ps->msg : "");
77       break;
78 
79     case SEENKICK:
80       Sendf(from, GetText(msg_seenkick), nick, ps->host,
81             TimeAgo(departure), ps->by ? ps->by : "", ps->msg ? ps->msg : "");
82       break;
83 
84     case SEENLEFT:
85       Sendf(from, GetText(msg_seenleft), nick, ps->host, TimeAgo(departure));
86       break;
87 
88     case SEENQUITED:
89     case SEENERROR:
90       Sendf(from, GetText(msg_seenquited), nick, ps->host, TimeAgo(departure));
91       break;
92 
93     case SEENKICKED:
94       Sendf(from, GetText(msg_seenkicked),
95             nick, ps->host, TimeAgo(departure), ps->by ? ps->by : "");
96       break;
97 
98     case SEENCHANGE:
99       Sendf(from, GetText(msg_seenchange), nick, ps->host, TimeAgo(departure));
100 
101     default:
102       break;
103 
104   }
105 }
106 
107 /* --- SeenMostNick ----------------------------------------------- */
108 
SeenMostNick(char * nick)109 itemaux *SeenMostNick(char *nick)
110 {
111   long max = 0;
112   itemaux *p, *pp = NULL;
113 
114   snapshot;
115   for (p = hashnick[HashU(nick)]; p; p = p->next) {
116     if (IRCEqual(p->nick, nick)) {
117       if (p->count >= max) {
118         max = p->count;
119         pp = p;
120       }
121     }
122   }
123   return pp;
124 }
125 
126 /* --- SeenRecentNick --------------------------------------------- */
127 
SeenRecentNick(char * nick)128 itemaux *SeenRecentNick(char *nick)
129 {
130   time_t recent = 0;
131   itemaux *p, *pp = NULL;
132 
133   snapshot;
134   for (p = hashnick[HashU(nick)]; p; p = p->next) {
135     if (IRCEqual(p->nick, nick)) {
136       if (p->departure > recent) {
137         recent = p->departure;
138         pp = p;
139       }
140     }
141   }
142   return pp;
143 }
144 
145 /* --- SeenRecentRootNick ----------------------------------------- */
146 
SeenRecentRootNick(char * nick)147 itemaux *SeenRecentRootNick(char *nick)
148 {
149   time_t recent = 0;
150   itemaux *p, *pp = NULL;
151 
152   snapshot;
153   for (p = hashnick[HashU(nick)]; p; p = p->next) {
154     if (IRCEqual(p->nick, nick)) {
155       if (p->root->departure > recent) {
156         recent = p->root->departure;
157         pp = p;
158       }
159     }
160   }
161   return pp;
162 }
163 
164 /* --- SeenNick --------------------------------------------------- */
165 
SeenNick(char * from,char * nick,char opt)166 void SeenNick(char *from, char *nick, char opt)
167 {
168   extern char nickname[];
169   bool hit = FALSE;
170   itemaux *pa = NULL;
171 
172   snapshot;
173   if (0 == opt) {
174     if (FindNick(nick)) {
175       if (IRCEqual(nickname, nick)) {
176         Send(from, GetText(msg_yes_i_am_here));
177         return;
178       }
179       else if (IRCEqual(current->nick, nick)) {
180         Send(from, GetText(msg_yes_i_see_you));
181         return;
182       }
183       else {
184         Send(from, GetText(msg_seen_already_here));
185         return;
186       }
187     }
188     else if (FindSplitNick(nick)) {
189       Sendf(from, GetText(msg_seensplit), nick);
190       return;
191     }
192   }
193 
194   switch (opt) {
195 
196     case 'A': /* All */
197       for (pa = hashnick[HashU(nick)]; pa; pa = pa->next) {
198         if (IRCEqual(pa->nick, nick)) {
199           hit = TRUE;
200           SeenReport(from, nick, pa->root->departure, pa->root);
201         }
202       }
203       break;
204 
205     case 'M': /* Most often used */
206       pa = SeenMostNick(nick);
207       break;
208 
209     default:  /* Most recent */
210       /*
211        * gr8ron January 24th, 1999:
212        * After a little discussion with Bagder we came to the conclusion
213        * that it was time to drop checks for X->root->departure here unless
214        * explicitly requested. SeenRecentNick() uses X->departure now.
215        * SeenRecentRootNick() uses X->root->departure instead.
216        * Also, SeenAddNick() sets the departure time for nick to root's
217        * departure time when there is none supplied or when it's 0.
218        */
219       pa = SeenRecentNick(nick);
220       break;
221 
222   }
223 
224   if (pa)
225     SeenReport(from, nick, pa->root->departure, pa->root);
226   else if (!hit)
227     Sendf(from, GetText(msg_never_seen), nick);
228 }
229 
230 /* --- SeenMatch -------------------------------------------------- */
231 
232 #define SEEN_MAX_DISPLAYED 15
233 
SeenMatch(char * from,char * pattern)234 void SeenMatch(char *from, char *pattern)
235 {
236   char nickpattern[NICKLEN+1] = "";
237   char *hostpattern;
238   int count = 0;
239   ulong nicksig, hostsig, max;
240   itemseen *ps;
241   itemaux *pa, *pam;
242 
243   snapshot;
244   hostpattern = StrIndex(pattern, '!');
245   if (hostpattern) {
246     StrScan(pattern, "%"NICKLENTXT"[^!]", nickpattern);
247     nicksig = HashSignatureU(nickpattern);
248     hostpattern++;
249   }
250   else {
251     hostpattern = pattern;
252   }
253 
254   hostsig = HashSignatureU(hostpattern);
255 
256   for (ps = seenHead->link; ps; ps = ps->link) {
257     if (((ps->signature & hostsig) == hostsig) && Match(ps->host, hostpattern)) {
258       if (nickpattern[0]) {
259         for (pa = ps->first; pa; pa = pa->link) {
260           if (((pa->signature & nicksig) == nicksig) && Match(pa->nick, nickpattern)) {
261             SeenReport(from, pa->nick, pa->departure, ps);
262             if (SEEN_MAX_DISPLAYED <= ++count)
263               break;
264           }
265         }
266       }
267       else {
268         max = 0;
269         for (pa = pam = ps->first; pa; pa = pa->link) {
270           if (pa->count > max) {
271             pam = pa;
272             max = pa->count;
273           }
274         }
275 
276         SeenReport(from, pam->nick, pam->root->departure, ps);
277         count++;
278       }
279 
280       if (SEEN_MAX_DISPLAYED <= count) {
281         Send(from, GetText(msg_seen_bad_pattern));
282         break;
283       }
284     }
285   }
286 
287   if (0 == count)
288     Sendf(from, GetText(msg_never_seen_match), pattern);
289 }
290 
291 /* --- Seen ------------------------------------------------------- */
292 
Seen(char * from,char * line)293 void Seen(char *from, char *line)
294 {
295   extern char *errfrom;
296   extern bool public;
297   extern long levels[];
298   extern itemopt option;
299   char *who;
300   char opt = (char)0;
301   bool illegalpub = FALSE;
302 
303   snapshot;
304   if (GetOption(line)) {
305     switch (toupper(option.copt)) {
306 
307       case 'A': /* All */
308         illegalpub = TRUE;
309         break;
310 
311       case 'M': /* Most used */
312       case 'R': /* Recent */
313         break;
314 
315       default:
316         break;
317 
318     }
319     opt = toupper(option.copt);
320     line = option.newpos;
321   }
322 
323   who = StrTokenize(line, " ");
324   if (who && *who) {
325     if (public && illegalpub) {
326       Send(errfrom, GetText(msg_no_public_options));
327     }
328     else if (public && StrIndex(line, '*')) {
329       Send(errfrom, GetText(msg_no_public_wildcards));
330     }
331     else {
332       if (strpbrk(who, "@*")) {
333         if (current->level >= LEVELPUB) {
334           SeenMatch(from, who);
335         }
336         else {
337           /* Pretend to be stupid */
338           Sendf(errfrom, GetText(msg_never_seen), who);
339         }
340       }
341       else
342         SeenNick(from, who, opt);
343     }
344   }
345   else
346     CmdSyntax(errfrom, "SEEN");
347 }
348 
349 /* --- FreeSeen --------------------------------------------------- */
350 
FreeSeen(itemseen * ps)351 void FreeSeen(itemseen *ps)
352 {
353   snapshot;
354   if (ps) {
355     if (ps->usite)
356       StrFree(ps->usite);
357     if (ps->host)
358       StrFree(ps->host);
359     if (ps->by)
360       StrFree(ps->by);
361     if (ps->msg)
362       StrFree(ps->msg);
363     free(ps);
364   }
365 }
366 
367 /* --- SeenNewSeen ------------------------------------------------ */
368 
SeenNewSeen(char * usite,char * host)369 itemseen *SeenNewSeen(char *usite, char *host)
370 {
371   itemseen *ps;
372 
373   snapshot;
374   /* Allocate a structure for the new site */
375   ps = NewEntry(itemseen);
376   if (ps) {
377     ps->usite = StrDuplicate(usite);
378     if (ps->usite) {
379       ps->host = StrDuplicate(host);
380       if (ps->host) {
381         ps->signature = HashSignatureU(ps->host);
382         return ps;
383       }
384       StrFree(ps->usite);
385     }
386     free(ps);
387   }
388   return NULL;
389 }
390 
391 /* --- SeenLinkSeen ----------------------------------------------- */
392 
SeenLinkSeen(itemseen * ps,unsigned int x)393 static void SeenLinkSeen(itemseen *ps, unsigned int x)
394 {
395   snapshot;
396   ps->link       = seenHead->link;
397   seenHead->link = ps;
398   ps->next       = hashsite[x];
399   hashsite[x]    = ps;
400   numSeenHosts++;
401 }
402 
403 /* --- SeenNewAux ------------------------------------------------- */
404 
SeenNewAux(char * nick)405 itemaux *SeenNewAux(char *nick)
406 {
407   itemaux *pa;
408 
409   snapshot;
410   /* Allocate a structure for the new aux */
411   pa = NewEntry(itemaux);
412   if (pa) {
413     pa->nick = StrDuplicate(nick);
414     if (pa->nick) {
415       pa->signature = HashSignatureU(pa->nick);
416       return pa;
417     }
418     free(pa);
419   }
420   return NULL;
421 }
422 
423 /* --- SeenLinkAux ------------------------------------------------ */
424 
SeenLinkAux(itemseen * ps,itemaux * pa,unsigned int x)425 static void SeenLinkAux(itemseen *ps, itemaux *pa, unsigned int x)
426 {
427   snapshot;
428   pa->link    = ps->first;
429   ps->first   = pa;
430   pa->next    = hashnick[x];
431   hashnick[x] = pa;
432   pa->root    = ps;
433   numSeenNicks++;
434 }
435 
436 /* --- SeenInsert ------------------------------------------------- */
437 
SeenInsert(itemguest * g,int type,char * by,char * msg)438 void SeenInsert(itemguest *g, int type, char *by, char *msg)
439 {
440   unsigned int xsite;
441   itemseen *ps;
442   itemaux *pa = NULL;
443 
444   snapshot;
445 
446   /* Check whether usite already exists */
447   xsite = Hash(g->ident->userdomain);
448   for (ps = hashsite[xsite]; ps; ps = ps->next) {
449     if (StrEqual(ps->usite, g->ident->userdomain))
450       break;
451   }
452 
453   /* If the usite exists */
454   if (ps) {
455     /* Check whether nick has been used from this usite before */
456     for (pa = ps->first; pa; pa = pa->link) {
457       if (IRCEqual(pa->nick, g->ident->nick))
458         break;
459     }
460 
461     /* Keep the last seen host in ps->host */
462     if (!StrEqualCase(ps->host, g->ident->host)) {
463       char *pointer;
464 
465       pointer = StrDuplicate(g->ident->host);
466       if (pointer) {
467         StrFree(ps->host);
468         ps->host = pointer;
469         ps->signature = HashSignatureU(ps->host);
470       }
471     }
472   }
473 
474   if (NULL == pa) {
475     /* We need a new aux structure */
476     pa = SeenNewAux(g->ident->nick);
477     if (pa) {
478       if (NULL == ps) {
479         /* We need a new usite */
480         ps = SeenNewSeen(g->ident->userdomain, g->ident->host);
481         if (ps)
482           SeenLinkSeen(ps, xsite);
483       }
484 
485       if (ps) {
486         SeenLinkAux(ps, pa, HashU(g->ident->nick));
487       }
488       else {
489         StrFree(pa->nick);
490         free(pa);
491         pa = NULL;
492       }
493     }
494   }
495 
496   /* Finally update the new seen information */
497   if (ps) {
498     if (pa) {
499       pa->count++;
500       pa->departure = now;
501     }
502 
503     ps->departure = now;
504     ps->type = type;
505     if (ps->by)
506       StrFree(ps->by);
507     ps->by = (by && by[0]) ? StrDuplicate(by) : NULL;
508     if (ps->msg)
509       StrFree(ps->msg);
510     ps->msg = (msg && msg[0]) ? StrDuplicate(msg) : NULL;
511 
512     changedseenitem++;
513   }
514 }
515 
516 /* --- SeenInsertAll ---------------------------------------------- */
517 
SeenInsertAll(int type,char * by,char * msg)518 void SeenInsertAll(int type, char *by, char *msg)
519 {
520   extern itemguest *guestHead;
521   itemguest *g;
522 
523   snapshot;
524   for (g = First(guestHead); g; g = Next(g)) {
525     SeenInsert(g, type, by, msg);
526   }
527 }
528 
529 /* --- SeenAdd ---------------------------------------------------- */
530 
SeenAdd(char * line)531 itemseen *SeenAdd(char *line)
532 {
533   char host[MIDBUFFER];
534   char by[MIDBUFFER]  = "";
535   char msg[BIGBUFFER] = "";
536   char *usite;
537   ulong departure;
538   int type;
539   itemseen *ps = NULL;
540 
541   snapshot;
542   if (3 <= StrScan(line, "%"MIDBUFFERTXT"s %lu %d %"MIDBUFFERTXT"s :%"BIGBUFFERTXT"[^\n]",
543                    host, &departure, &type, by, msg)) {
544 
545     /* Simple sanity check, a host name must include the @-letter */
546     if (NULL == StrIndex(host, '@'))
547       return NULL;
548 
549     usite = Userdomain(host);
550     if (usite) {
551       ps = SeenNewSeen(usite, host);
552       if (ps) {
553         ps->departure = departure;
554         ps->type = type;
555         ps->by = (by[0] && ('.' != by[0])) ? StrDuplicate(by) : NULL;
556         ps->msg = msg[0] ? StrDuplicate(msg) : NULL;
557       }
558       StrFree(usite);
559     }
560   }
561   return ps;
562 }
563 
564 /* --- SeenAddNick ------------------------------------------------ */
565 
SeenAddNick(itemseen * ps,char * line)566 void SeenAddNick(itemseen *ps, char *line)
567 {
568   char nick[NICKLEN+1], tmp[NICKLEN+1];
569   time_t departure = 0;
570   ulong count = 1;
571   itemaux *pa;
572 
573   snapshot;
574   if (1 <= StrScan(line, "%"NICKLENTXT"s %d %d", nick, &count, &departure)) {
575     /*
576      * Only add nicknames that use legal characters, as other names are
577      * likely to be a proof of a broken seen file or similar
578      */
579     if (StrScan(nick, "%[0-9A-~-]", tmp) && StrEqualCase(nick, tmp)) {
580       pa = SeenNewAux(nick);
581       if (pa) {
582         /*
583          * We can now link the allocated seen structure to seenlist
584          * if it has not been linked before.
585          */
586         if (NULL == ps->first)
587           SeenLinkSeen(ps, Hash(ps->usite));
588 
589         SeenLinkAux(ps, pa, HashU(pa->nick));
590         pa->count = count; /* Might be 1 if field wasn't set */
591         /*
592          * pa->departure is now set to departure time of the root if the
593          * corresponding field isn't set or when its set to 0.
594          */
595         pa->departure = departure ? departure : ps->departure;
596       }
597     }
598   }
599 }
600 
601 /* --- SeenInit --------------------------------------------------- */
602 
SeenInit(void)603 bool SeenInit(void)
604 {
605   char line[MAXLINE];
606   itemseen *ps = NULL;
607   FILE *f;
608 
609   snapshot;
610   hashnick = (itemaux  **)calloc(HASHSIZE, sizeof(itemaux *));
611   hashsite = (itemseen **)calloc(HASHSIZE, sizeof(itemseen *));
612 
613   if (hashnick && hashsite && seenfile[0]) {
614     f = fopen(seenfile, "r");
615     if (f) {
616       while (fgets(line, sizeof(line), f)) {
617         switch (line[0]) {
618 
619           case ' ':
620             if (ps)
621               SeenAddNick(ps, &line[1]);
622             break;
623 
624           default:
625             /* We have to free sites that have no nicks connected */
626             if (ps && (NULL == ps->first))
627               FreeSeen(ps);
628             ps = SeenAdd(line);
629             break;
630 
631         }
632       }
633       fclose(f);
634 
635       if (ps && (NULL == ps->first))
636         FreeSeen(ps);
637 
638       return TRUE;
639     }
640   }
641 
642   return FALSE;
643 }
644 
645 /* --- SeenSave --------------------------------------------------- */
646 
SeenSave(void)647 void SeenSave(void)
648 {
649   char tempfile[MIDBUFFER];
650   bool ok = TRUE;
651   itemseen *ps;
652   itemaux *pa;
653   FILE *f;
654 
655   snapshot;
656   if ((char)0 == seenfile[0])
657     return;
658 
659   if (0 == changedseenitem) {
660     Logf(LOG, "No seen item changed, no save performed.");
661     return;
662   }
663 
664   Logf(LOG, "Saving seen data \"%s\"", seenfile);
665 
666   StrFormatMax(tempfile, sizeof(tempfile), "%s~", seenfile);
667 
668   f = fopen(tempfile, "w");
669   if (f) {
670     for (ps = seenHead->link; ps && ok; ps = ps->link) {
671       if ((ps->departure + seenmonths*SECINMONTH) >= now) {
672         /*
673          * Only save if the user departed within 'seenmonths' months ago!
674          */
675         if (0 > fprintf(f, "%s %ld %d %s :%s\n",
676                         ps->host, ps->departure, ps->type,
677                         ps->by ? ps->by : ".",
678                         ps->msg ? ps->msg : "")) {
679           ok = FALSE;
680           break;
681         }
682         for (pa = ps->first; pa; pa = pa->link) {
683           if ((pa->departure + seenmonths*SECINMONTH) >= now) {
684             /*
685              * Here too. We only save the nicks this person has used within
686              * the 'seenmonths' period. We shouldn't let old sins live on...
687              */
688             if (0 > fprintf(f, " %s %ld %ld\n",
689                             pa->nick, pa->count, pa->departure)) {
690               ok = FALSE;
691               break;
692             }
693           }
694         }
695       }
696     }
697     fclose(f);
698 
699     if (ok) {
700       rename(tempfile, seenfile);
701       changedseenitem = 0;
702     }
703   }
704 }
705 
706 /* --- SeenCleanup ------------------------------------------------ */
707 
SeenCleanup(void)708 void SeenCleanup(void)
709 {
710   itemseen *ps, *tps;
711   itemaux *pa, *tpa;
712 
713   snapshot;
714   SeenSave();
715 
716   /* Remember to free all allocated memory in structures */
717   for (ps = seenHead->link; ps; ps = tps) {
718     tps = ps->link;
719     for (pa = ps->first; pa; pa = tpa) {
720       tpa = pa->link;
721       if (pa->nick)
722         StrFree(pa->nick);
723       free(pa);
724     }
725     FreeSeen(ps);
726   }
727 
728   if (hashnick)
729     free(hashnick);
730   if (hashsite)
731     free(hashsite);
732 }
733