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