1 /*
2  * tclirc.c -- part of irc.mod
3  */
4 /*
5  * Copyright (C) 1997 Robey Pointer
6  * Copyright (C) 1999 - 2021 Eggheads Development Team
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21  */
22 
23 /* Streamlined by answer.
24  */
25 static int tcl_chanlist STDVAR
26 {
27   char nuh[1024];
28   int f;
29   memberlist *m;
30   struct chanset_t *chan;
31   struct flag_record plus = { FR_CHAN | FR_GLOBAL | FR_BOT, 0, 0, 0, 0, 0 },
32                      minus = { FR_CHAN | FR_GLOBAL | FR_BOT, 0, 0, 0, 0, 0},
33                      user = { FR_CHAN | FR_GLOBAL | FR_BOT, 0, 0, 0, 0, 0 };
34 
35   BADARGS(2, 3, " channel ?flags?");
36 
37   chan = findchan_by_dname(argv[1]);
38   if (!chan) {
39     Tcl_AppendResult(irp, "invalid channel: ", argv[1], NULL);
40     return TCL_ERROR;
41   }
42   if (argc == 2) {
43     /* No flag restrictions so just whiz it thru quick */
44     for (m = chan->channel.member; m && m->nick[0]; m = m->next)
45       Tcl_AppendElement(irp, m->nick);
46     return TCL_OK;
47   }
48   break_down_flags(argv[2], &plus, &minus);
49   f = (minus.global || minus.udef_global || minus.chan || minus.udef_chan ||
50        minus.bot);
51   /* Return empty set if asked for flags but flags don't exist */
52   if (!plus.global && !plus.udef_global && !plus.chan && !plus.udef_chan &&
53       !plus.bot && !f)
54     return TCL_OK;
55   minus.match = plus.match ^ (FR_AND | FR_OR);
56 
57   for (m = chan->channel.member; m && m->nick[0]; m = m->next) {
58     if (!m->user) {
59       egg_snprintf(nuh, sizeof nuh, "%s!%s", m->nick, m->userhost);
60       m->user = get_user_by_host(nuh);
61     }
62     get_user_flagrec(m->user, &user, argv[1]);
63     user.match = plus.match;
64     if (flagrec_eq(&plus, &user)) {
65       if (!f || !flagrec_eq(&minus, &user))
66         Tcl_AppendElement(irp, m->nick);
67     }
68   }
69   return TCL_OK;
70 }
71 
72 static int tcl_botisop STDVAR
73 {
74   struct chanset_t *chan, *thechan = NULL;
75 
76   BADARGS(1, 2, " ?channel?");
77 
78   if (argc > 1) {
79     chan = findchan_by_dname(argv[1]);
80     thechan = chan;
81     if (!thechan) {
82       Tcl_AppendResult(irp, "illegal channel: ", argv[1], NULL);
83       return TCL_ERROR;
84     }
85   } else
86     chan = chanset;
87 
88   while (chan && (thechan == NULL || thechan == chan)) {
89     if (me_op(chan)) {
90       Tcl_AppendResult(irp, "1", NULL);
91       return TCL_OK;
92     }
93     chan = chan->next;
94   }
95   Tcl_AppendResult(irp, "0", NULL);
96   return TCL_OK;
97 }
98 
99 static int tcl_botishalfop STDVAR
100 {
101   struct chanset_t *chan, *thechan = NULL;
102 
103   BADARGS(1, 2, " ?channel?");
104 
105   if (argc > 1) {
106     chan = findchan_by_dname(argv[1]);
107     thechan = chan;
108     if (!thechan) {
109       Tcl_AppendResult(irp, "illegal channel: ", argv[1], NULL);
110       return TCL_ERROR;
111     }
112   } else
113     chan = chanset;
114 
115   while (chan && (thechan == NULL || thechan == chan)) {
116     if (me_halfop(chan)) {
117       Tcl_AppendResult(irp, "1", NULL);
118       return TCL_OK;
119     }
120     chan = chan->next;
121   }
122   Tcl_AppendResult(irp, "0", NULL);
123   return TCL_OK;
124 }
125 
126 static int tcl_ischanjuped STDVAR
127 {
128   struct chanset_t *chan;
129 
130   BADARGS(2, 2, " channel");
131 
132   chan = findchan_by_dname(argv[1]);
133   if (chan == NULL) {
134     Tcl_AppendResult(irp, "illegal channel: ", argv[1], NULL);
135     return TCL_ERROR;
136   }
137   if (channel_juped(chan))
138     Tcl_AppendResult(irp, "1", NULL);
139   else
140     Tcl_AppendResult(irp, "0", NULL);
141   return TCL_OK;
142 }
143 
144 static int tcl_botisvoice STDVAR
145 {
146   struct chanset_t *chan, *thechan = NULL;
147   memberlist *mx;
148 
149   BADARGS(1, 2, " ?channel?");
150 
151   if (argc > 1) {
152     chan = findchan_by_dname(argv[1]);
153     thechan = chan;
154     if (!thechan) {
155       Tcl_AppendResult(irp, "illegal channel: ", argv[1], NULL);
156       return TCL_ERROR;
157     }
158   } else
159     chan = chanset;
160 
161   while (chan && (thechan == NULL || thechan == chan)) {
162     if ((mx = ismember(chan, botname)) && chan_hasvoice(mx)) {
163       Tcl_AppendResult(irp, "1", NULL);
164       return TCL_OK;
165     }
166     chan = chan->next;
167   }
168   Tcl_AppendResult(irp, "0", NULL);
169   return TCL_OK;
170 }
171 
172 static int tcl_botonchan STDVAR
173 {
174   struct chanset_t *chan, *thechan = NULL;
175 
176   BADARGS(1, 2, " ?channel?");
177 
178   if (argc > 1) {
179     chan = findchan_by_dname(argv[1]);
180     thechan = chan;
181     if (!thechan) {
182       Tcl_AppendResult(irp, "illegal channel: ", argv[1], NULL);
183       return TCL_ERROR;
184     }
185   } else
186     chan = chanset;
187 
188   while (chan && (thechan == NULL || thechan == chan)) {
189     if (ismember(chan, botname)) {
190       Tcl_AppendResult(irp, "1", NULL);
191       return TCL_OK;
192     }
193     chan = chan->next;
194   }
195   Tcl_AppendResult(irp, "0", NULL);
196   return TCL_OK;
197 }
198 
199 static int tcl_isop STDVAR
200 {
201   struct chanset_t *chan, *thechan = NULL;
202   memberlist *mx;
203 
204   BADARGS(2, 3, " nick ?channel?");
205 
206   if (argc > 2) {
207     chan = findchan_by_dname(argv[2]);
208     thechan = chan;
209     if (!thechan) {
210       Tcl_AppendResult(irp, "illegal channel: ", argv[2], NULL);
211       return TCL_ERROR;
212     }
213   } else
214     chan = chanset;
215 
216   while (chan && (thechan == NULL || thechan == chan)) {
217     if ((mx = ismember(chan, argv[1])) && chan_hasop(mx)) {
218       Tcl_AppendResult(irp, "1", NULL);
219       return TCL_OK;
220     }
221     chan = chan->next;
222   }
223   Tcl_AppendResult(irp, "0", NULL);
224   return TCL_OK;
225 }
226 
227 static int tcl_ishalfop STDVAR
228 {
229   struct chanset_t *chan, *thechan = NULL;
230   memberlist *mx;
231 
232   BADARGS(2, 3, " nick ?channel?");
233 
234   if (argc > 2) {
235     chan = findchan_by_dname(argv[2]);
236     thechan = chan;
237     if (!thechan) {
238       Tcl_AppendResult(irp, "illegal channel: ", argv[2], NULL);
239       return TCL_ERROR;
240     }
241   } else
242     chan = chanset;
243 
244   while (chan && (thechan == NULL || thechan == chan)) {
245     if ((mx = ismember(chan, argv[1])) && chan_hashalfop(mx)) {
246       Tcl_AppendResult(irp, "1", NULL);
247       return TCL_OK;
248     }
249     chan = chan->next;
250   }
251   Tcl_AppendResult(irp, "0", NULL);
252   return TCL_OK;
253 }
254 
255 static int tcl_isvoice STDVAR
256 {
257   struct chanset_t *chan, *thechan = NULL;
258   memberlist *mx;
259 
260   BADARGS(2, 3, " nick ?channel?");
261 
262   if (argc > 2) {
263     chan = findchan_by_dname(argv[2]);
264     thechan = chan;
265     if (!thechan) {
266       Tcl_AppendResult(irp, "illegal channel: ", argv[2], NULL);
267       return TCL_ERROR;
268     }
269   } else
270     chan = chanset;
271 
272   while (chan && (thechan == NULL || thechan == chan)) {
273     if ((mx = ismember(chan, argv[1])) && chan_hasvoice(mx)) {
274       Tcl_AppendResult(irp, "1", NULL);
275       return TCL_OK;
276     }
277     chan = chan->next;
278   }
279   Tcl_AppendResult(irp, "0", NULL);
280   return TCL_OK;
281 }
282 
283 static int tcl_wasop STDVAR
284 {
285   struct chanset_t *chan;
286   memberlist *mx;
287 
288   BADARGS(3, 3, " nick channel");
289 
290   chan = findchan_by_dname(argv[2]);
291   if (chan == NULL) {
292     Tcl_AppendResult(irp, "illegal channel: ", argv[2], NULL);
293     return TCL_ERROR;
294   }
295   if ((mx = ismember(chan, argv[1])) && chan_wasop(mx))
296     Tcl_AppendResult(irp, "1", NULL);
297   else
298     Tcl_AppendResult(irp, "0", NULL);
299   return TCL_OK;
300 }
301 
302 static int tcl_washalfop STDVAR
303 {
304   struct chanset_t *chan;
305   memberlist *mx;
306 
307   BADARGS(3, 3, " nick channel");
308 
309   chan = findchan_by_dname(argv[2]);
310   if (chan == NULL) {
311     Tcl_AppendResult(irp, "illegal channel: ", argv[2], NULL);
312     return TCL_ERROR;
313   }
314   if ((mx = ismember(chan, argv[1])) && chan_washalfop(mx))
315     Tcl_AppendResult(irp, "1", NULL);
316   else
317     Tcl_AppendResult(irp, "0", NULL);
318   return TCL_OK;
319 }
320 
321 static int tcl_onchan STDVAR
322 {
323   struct chanset_t *chan, *thechan = NULL;
324 
325   BADARGS(2, 3, " nickname ?channel?");
326 
327   if (argc > 2) {
328     chan = findchan_by_dname(argv[2]);
329     thechan = chan;
330     if (!thechan) {
331       Tcl_AppendResult(irp, "illegal channel: ", argv[2], NULL);
332       return TCL_ERROR;
333     }
334   } else
335     chan = chanset;
336 
337   while (chan && (thechan == NULL || thechan == chan)) {
338     if (ismember(chan, argv[1])) {
339       Tcl_AppendResult(irp, "1", NULL);
340       return TCL_OK;
341     }
342     chan = chan->next;
343   }
344   Tcl_AppendResult(irp, "0", NULL);
345   return TCL_OK;
346 }
347 
348 static int tcl_handonchan STDVAR
349 {
350   char nuh[1024];
351   struct chanset_t *chan, *thechan = NULL;
352   memberlist *m;
353 
354   BADARGS(2, 3, " handle ?channel?");
355 
356   if (argc > 2) {
357     chan = findchan_by_dname(argv[2]);
358     thechan = chan;
359     if (!thechan) {
360       Tcl_AppendResult(irp, "illegal channel: ", argv[2], NULL);
361       return TCL_ERROR;
362     }
363   } else
364     chan = chanset;
365 
366   while (chan && (thechan == NULL || thechan == chan)) {
367     for (m = chan->channel.member; m && m->nick[0]; m = m->next) {
368       if (!m->user) {
369         egg_snprintf(nuh, sizeof nuh, "%s!%s", m->nick, m->userhost);
370         m->user = get_user_by_host(nuh);
371       }
372       if (m->user && !rfc_casecmp(m->user->handle, argv[1])) {
373         Tcl_AppendResult(irp, "1", NULL);
374         return TCL_OK;
375       }
376     }
377     chan = chan->next;
378   }
379   Tcl_AppendResult(irp, "0", NULL);
380   return TCL_OK;
381 }
382 
383 static int tcl_ischanban STDVAR
384 {
385   struct chanset_t *chan;
386 
387   BADARGS(3, 3, " ban channel");
388 
389   chan = findchan_by_dname(argv[2]);
390   if (chan == NULL) {
391     Tcl_AppendResult(irp, "illegal channel: ", argv[2], NULL);
392     return TCL_ERROR;
393   }
394   if (ischanban(chan, argv[1]))
395     Tcl_AppendResult(irp, "1", NULL);
396   else
397     Tcl_AppendResult(irp, "0", NULL);
398   return TCL_OK;
399 }
400 
401 static int tcl_ischanexempt STDVAR
402 {
403   struct chanset_t *chan;
404 
405   BADARGS(3, 3, " exempt channel");
406 
407   chan = findchan_by_dname(argv[2]);
408   if (chan == NULL) {
409     Tcl_AppendResult(irp, "illegal channel: ", argv[2], NULL);
410     return TCL_ERROR;
411   }
412   if (ischanexempt(chan, argv[1]))
413     Tcl_AppendResult(irp, "1", NULL);
414   else
415     Tcl_AppendResult(irp, "0", NULL);
416   return TCL_OK;
417 }
418 
419 static int tcl_ischaninvite STDVAR
420 {
421   struct chanset_t *chan;
422 
423   BADARGS(3, 3, " invite channel");
424 
425   chan = findchan_by_dname(argv[2]);
426   if (chan == NULL) {
427     Tcl_AppendResult(irp, "illegal channel: ", argv[2], NULL);
428     return TCL_ERROR;
429   }
430   if (ischaninvite(chan, argv[1]))
431     Tcl_AppendResult(irp, "1", NULL);
432   else
433     Tcl_AppendResult(irp, "0", NULL);
434   return TCL_OK;
435 }
436 
437 /* Checks internal tracking of IRC server away status, as updated by IRC
438  * server 352 and AWAY messages. Meant mostly for use with the IRCv3 away-notify
439  * capability, it may not be accurate using only 352s.
440  */
441 static int tcl_isaway STDVAR
442 {
443   struct chanset_t *chan, *thechan = NULL;
444   memberlist *m;
445 
446   BADARGS(2, 3, " nick ?channel?");
447 
448   if (argc > 2) { /* If channel specified, does it exist? */
449     chan = findchan_by_dname(argv[2]);
450     thechan = chan;
451     if (!thechan) {
452       Tcl_AppendResult(irp, "illegal channel: ", argv[2], NULL);
453       return TCL_ERROR;
454     }
455   } else {
456     chan = chanset;
457   }
458   while (chan && (thechan == NULL || thechan == chan)) {
459     if ((m = ismember(chan, argv[1])) && chan_ircaway(m)) {
460       Tcl_AppendResult(irp, "1", NULL);
461       return TCL_OK;
462     }
463     chan = chan->next;
464   }
465   Tcl_AppendResult(irp, "0", NULL);
466   return TCL_OK;
467 }
468 
469 static int tcl_getchanhost STDVAR
470 {
471   struct chanset_t *chan, *thechan = NULL;
472   memberlist *m;
473 
474   BADARGS(2, 3, " nickname ?channel?");
475 
476   if (argc > 2) {
477     chan = findchan_by_dname(argv[2]);
478     thechan = chan;
479     if (!thechan) {
480       Tcl_AppendResult(irp, "illegal channel: ", argv[2], NULL);
481       return TCL_ERROR;
482     }
483   } else
484     chan = chanset;
485 
486   while (chan && (thechan == NULL || thechan == chan)) {
487     m = ismember(chan, argv[1]);
488     if (m) {
489       Tcl_AppendResult(irp, m->userhost, NULL);
490       return TCL_OK;
491     }
492     chan = chan->next;
493   }
494   return TCL_OK;
495 }
496 
497 static int tcl_onchansplit STDVAR
498 {
499   struct chanset_t *chan, *thechan = NULL;
500   memberlist *m;
501 
502   BADARGS(2, 3, " nickname ?channel?");
503 
504   if (argc > 2) {
505     chan = findchan_by_dname(argv[2]);
506     thechan = chan;
507     if (!thechan) {
508       Tcl_AppendResult(irp, "illegal channel: ", argv[2], NULL);
509       return TCL_ERROR;
510     }
511   } else
512     chan = chanset;
513 
514   while (chan && (thechan == NULL || thechan == chan)) {
515     m = ismember(chan, argv[1]);
516     if (m && chan_issplit(m)) {
517       Tcl_AppendResult(irp, "1", NULL);
518       return TCL_OK;
519     }
520     chan = chan->next;
521   }
522   Tcl_AppendResult(irp, "0", NULL);
523   return TCL_OK;
524 }
525 
526 static int tcl_maskhost STDVAR
527 {
528   char *new;
529 
530   BADARGS(2, 3, " nick!user@host ?type?");
531 
532   new = nmalloc(strlen(argv[1]) + 5);
533   if (argc == 3)
534     maskaddr(argv[1], new, atoi(argv[2]));
535   else
536     maskban(argv[1], new);
537   Tcl_AppendResult(irp, new, NULL);
538   nfree(new);
539   return TCL_OK;
540 }
541 
542 static int tcl_getchanidle STDVAR
543 {
544   memberlist *m;
545   struct chanset_t *chan;
546   char s[20];
547   int x;
548 
549   BADARGS(3, 3, " nickname channel");
550 
551   if (!(chan = findchan_by_dname(argv[2]))) {
552     Tcl_AppendResult(irp, "invalid channel: ", argv[2], NULL);
553     return TCL_ERROR;
554   }
555   m = ismember(chan, argv[1]);
556 
557   if (m) {
558     x = (now - (m->last)) / 60;
559     simple_sprintf(s, "%d", x);
560     Tcl_AppendResult(irp, s, NULL);
561     return TCL_OK;
562   }
563   Tcl_AppendResult(irp, "-1", NULL);
564   return TCL_OK;
565 }
566 
tcl_chanmasks(masklist * m,Tcl_Interp * irp)567 static int tcl_chanmasks(masklist *m, Tcl_Interp *irp)
568 {
569   char work[20], *p;
570   EGG_CONST char *list[3];
571 
572   for (; m && m->mask && m->mask[0]; m = m->next) {
573     list[0] = m->mask;
574     list[1] = m->who;
575     simple_sprintf(work, "%d", now - m->timer);
576     list[2] = work;
577     p = Tcl_Merge(3, list);
578     Tcl_AppendElement(irp, p);
579     Tcl_Free((char *) p);
580   }
581   return TCL_OK;
582 }
583 
584 static int tcl_chanbans STDVAR
585 {
586   struct chanset_t *chan;
587 
588   BADARGS(2, 2, " channel");
589 
590   chan = findchan_by_dname(argv[1]);
591   if (chan == NULL) {
592     Tcl_AppendResult(irp, "illegal channel: ", argv[2], NULL);
593     return TCL_ERROR;
594   }
595   return tcl_chanmasks(chan->channel.ban, irp);
596 }
597 
598 static int tcl_chanexempts STDVAR
599 {
600   struct chanset_t *chan;
601 
602   BADARGS(2, 2, " channel");
603 
604   chan = findchan_by_dname(argv[1]);
605   if (chan == NULL) {
606     Tcl_AppendResult(irp, "illegal channel: ", argv[2], NULL);
607     return TCL_ERROR;
608   }
609   return tcl_chanmasks(chan->channel.exempt, irp);
610 }
611 
612 static int tcl_chaninvites STDVAR
613 {
614   struct chanset_t *chan;
615 
616   BADARGS(2, 2, " channel");
617 
618   chan = findchan_by_dname(argv[1]);
619   if (chan == NULL) {
620     Tcl_AppendResult(irp, "illegal channel: ", argv[2], NULL);
621     return TCL_ERROR;
622   }
623   return tcl_chanmasks(chan->channel.invite, irp);
624 }
625 
626 static int tcl_getchanmode STDVAR
627 {
628   struct chanset_t *chan;
629 
630   BADARGS(2, 2, " channel");
631 
632   chan = findchan_by_dname(argv[1]);
633   if (chan == NULL) {
634     Tcl_AppendResult(irp, "invalid channel: ", argv[1], NULL);
635     return TCL_ERROR;
636   }
637   Tcl_AppendResult(irp, getchanmode(chan), NULL);
638   return TCL_OK;
639 }
640 
641 static int tcl_getchanjoin STDVAR
642 {
643   struct chanset_t *chan;
644   char s[21];
645   memberlist *m;
646 
647   BADARGS(3, 3, " nick channel");
648 
649   chan = findchan_by_dname(argv[2]);
650   if (chan == NULL) {
651     Tcl_AppendResult(irp, "invalid channel: ", argv[2], NULL);
652     return TCL_ERROR;
653   }
654   m = ismember(chan, argv[1]);
655 
656   if (m == NULL) {
657     Tcl_AppendResult(irp, argv[1], " is not on ", argv[2], NULL);
658     return TCL_ERROR;
659   }
660   sprintf(s, "%lu", (unsigned long) m->joined);
661   Tcl_AppendResult(irp, s, NULL);
662   return TCL_OK;
663 }
664 
665 static int tcl_channame2dname STDVAR
666 {
667   struct chanset_t *chan;
668 
669   BADARGS(2, 2, " channel-name");
670 
671   chan = findchan(argv[1]);
672   if (chan) {
673     Tcl_AppendResult(irp, chan->dname, NULL);
674     return TCL_OK;
675   } else {
676     Tcl_AppendResult(irp, "invalid channel-name: ", argv[1], NULL);
677     return TCL_ERROR;
678   }
679 }
680 
681 static int tcl_chandname2name STDVAR
682 {
683   struct chanset_t *chan;
684 
685   BADARGS(2, 2, " channel-dname");
686 
687   chan = findchan_by_dname(argv[1]);
688   if (chan) {
689     Tcl_AppendResult(irp, chan->name, NULL);
690     return TCL_OK;
691   } else {
692     Tcl_AppendResult(irp, "invalid channel-dname: ", argv[1], NULL);
693     return TCL_ERROR;
694   }
695 }
696 
697 static int tcl_flushmode STDVAR
698 {
699   struct chanset_t *chan;
700 
701   BADARGS(2, 2, " channel");
702 
703   chan = findchan_by_dname(argv[1]);
704   if (chan == NULL) {
705     Tcl_AppendResult(irp, "invalid channel: ", argv[1], NULL);
706     return TCL_ERROR;
707   }
708   flush_mode(chan, NORMAL);
709   return TCL_OK;
710 }
711 
712 static int tcl_pushmode STDVAR
713 {
714   struct chanset_t *chan;
715   char plus, mode;
716 
717   BADARGS(3, 4, " channel mode ?arg?");
718 
719   chan = findchan_by_dname(argv[1]);
720   if (chan == NULL) {
721     Tcl_AppendResult(irp, "invalid channel: ", argv[1], NULL);
722     return TCL_ERROR;
723   }
724   plus = argv[2][0];
725 
726   mode = argv[2][1];
727   if ((plus != '+') && (plus != '-')) {
728     mode = plus;
729     plus = '+';
730   }
731   if (argc == 4)
732     add_mode(chan, plus, mode, argv[3]);
733   else
734     add_mode(chan, plus, mode, "");
735   return TCL_OK;
736 }
737 
738 static int tcl_resetbans STDVAR
739 {
740   struct chanset_t *chan;
741 
742   BADARGS(2, 2, " channel");
743 
744   chan = findchan_by_dname(argv[1]);
745   if (chan == NULL) {
746     Tcl_AppendResult(irp, "invalid channel ", argv[1], NULL);
747     return TCL_ERROR;
748   }
749   resetbans(chan);
750   return TCL_OK;
751 }
752 
753 static int tcl_resetexempts STDVAR
754 {
755   struct chanset_t *chan;
756 
757   BADARGS(2, 2, " channel");
758 
759   chan = findchan_by_dname(argv[1]);
760   if (chan == NULL) {
761     Tcl_AppendResult(irp, "invalid channel ", argv[1], NULL);
762     return TCL_ERROR;
763   }
764   resetexempts(chan);
765   return TCL_OK;
766 }
767 
768 static int tcl_resetinvites STDVAR
769 {
770   struct chanset_t *chan;
771 
772   BADARGS(2, 2, " channel");
773 
774   chan = findchan_by_dname(argv[1]);
775   if (chan == NULL) {
776     Tcl_AppendResult(irp, "invalid channel ", argv[1], NULL);
777     return TCL_ERROR;
778   }
779   resetinvites(chan);
780   return TCL_OK;
781 }
782 
783 static int tcl_resetchanidle STDVAR
784 {
785   memberlist *m;
786   struct chanset_t *chan;
787 
788   BADARGS(2, 3, " ?nick? channel");
789 
790   chan = findchan_by_dname((argc == 2) ? argv[1] : argv[2]);
791   if (chan == NULL) {
792     Tcl_AppendResult(irp, "invalid channel ",
793                      (argc == 2) ? argv[1] : argv[2], NULL);
794     return TCL_ERROR;
795   }
796 
797   if (argc == 2)
798     for (m = chan->channel.member; m; m = m->next)
799       m->last = now;
800   else {
801     if (!(m = ismember(chan, argv[1]))) {
802       Tcl_AppendResult(irp, argv[1], " is not on ", argv[2], NULL);
803       return TCL_ERROR;
804     }
805     m->last = now;
806   }
807   return TCL_OK;
808 }
809 
810 static int tcl_resetchanjoin STDVAR
811 {
812   memberlist *m;
813   struct chanset_t *chan;
814 
815   BADARGS(2, 3, " ?nick? channel");
816 
817   chan = findchan_by_dname((argc == 2) ? argv[1] : argv[2]);
818   if (chan == NULL) {
819     Tcl_AppendResult(irp, "invalid channel ",
820                      (argc == 2) ? argv[1] : argv[2], NULL);
821     return TCL_ERROR;
822   }
823 
824   if (argc == 2)
825     for (m = chan->channel.member; m; m = m->next)
826       m->joined = now;
827   else {
828     if (!(m = ismember(chan, argv[1]))) {
829       Tcl_AppendResult(irp, argv[1], " is not on ", argv[2], NULL);
830       return TCL_ERROR;
831     }
832     m->joined = now;
833   }
834   return TCL_OK;
835 }
836 
setflags(int * flags,char * argflags)837 static int setflags(int *flags, char *argflags) {
838   char *c;
839 
840   for (c = argflags; *c; c++) {
841     switch(*c) {
842     case 'w':
843       *flags |= CHAN_RESETWHO;
844       break;
845     case 'm':
846       *flags |= CHAN_RESETMODES;
847       break;
848     case 'b':
849       *flags |= CHAN_RESETBANS;
850       break;
851     case 'e':
852       *flags |= CHAN_RESETEXEMPTS;
853       break;
854     case 'I':
855       *flags |= CHAN_RESETINVITED;
856       break;
857     case 't':
858       *flags |= CHAN_RESETTOPIC;
859       break;
860     default:
861       return 1; /* Found a flag we don't support, return an error */
862     }
863   }
864   return 0;
865 }
866 
867 static int tcl_refreshchan STDVAR
868 {
869   int flags = 0;
870   struct chanset_t *chan;
871 
872   BADARGS(2, 3, " channel ?flags?");
873 
874   chan = findchan_by_dname(argv[1]);
875   if (chan == NULL) {
876     Tcl_AppendResult(irp, "invalid channel ", argv[1], NULL);
877     return TCL_ERROR;
878   }
879 
880   if (argc == 2) {
881     reset_chan_info(chan, CHAN_RESETALL, 0);
882     return TCL_OK;
883   }
884   if (setflags(&flags, argv[2])) {       /* Set flags to refresh */
885       Tcl_AppendResult(irp, "invalid refresh flags: ", argv[2], NULL);
886       return TCL_ERROR;
887   } else {
888     reset_chan_info(chan, flags, 0);
889   }
890   return TCL_OK;
891 }
892 
893 static int tcl_resetchan STDVAR
894 {
895   int flags = 0;
896   struct chanset_t *chan;
897 
898   BADARGS(2, 3, " channel ?flags?");
899 
900   chan = findchan_by_dname(argv[1]);
901   if (chan == NULL) {
902     Tcl_AppendResult(irp, "invalid channel ", argv[1], NULL);
903     return TCL_ERROR;
904   }
905 
906   if (argc == 2) {
907     reset_chan_info(chan, CHAN_RESETALL, 1);
908     return TCL_OK;
909   }
910   if (setflags(&flags, argv[2])) {       /* Set flags to refresh */
911       Tcl_AppendResult(irp, "invalid reset flags: ", argv[2], NULL);
912       return TCL_ERROR;
913   } else {
914     reset_chan_info(chan, flags, 1);
915   }
916   return TCL_OK;
917 }
918 
919 static int tcl_topic STDVAR
920 {
921   struct chanset_t *chan;
922 
923   BADARGS(2, 2, " channel");
924 
925   chan = findchan_by_dname(argv[1]);
926   if (chan == NULL) {
927     Tcl_AppendResult(irp, "invalid channel ", argv[1], NULL);
928     return TCL_ERROR;
929   }
930   Tcl_AppendResult(irp, chan->channel.topic, NULL);
931   return TCL_OK;
932 }
933 
934 static int tcl_account2nicks STDVAR
935 {
936   memberlist *m;
937   struct chanset_t *chan, *thechan = NULL;
938   Tcl_Obj *nicks;
939   Tcl_Obj **nicksv = NULL;
940   int nicksc = 0, i, found;
941 
942   BADARGS(2, 3, " account ?channel?");
943 
944   if (argc > 2) {
945     chan = findchan_by_dname(argv[2]);
946     thechan = chan;
947     if (chan == NULL) {
948       Tcl_AppendResult(irp, "invalid channel: ", argv[2], NULL);
949       return TCL_ERROR;
950     }
951   } else
952     chan = chanset;
953 
954   nicks = Tcl_NewListObj(0, NULL);
955   while (chan && (thechan == NULL || thechan == chan)) {
956     for (m = chan->channel.member; m && m->nick[0]; m = m->next) {
957       found = 0;
958       /* Does this user have the account we're looking for? */
959       if (!rfc_casecmp(m->account, argv[1])) {
960         /* Is the nick of the user already in the list? */
961         Tcl_ListObjGetElements(irp, nicks, &nicksc, &nicksv);
962         for (i = 0; i < nicksc; i++) {
963           if (!rfc_casecmp(m->nick, Tcl_GetString(nicksv[i]))) {
964             found = 1;
965             break;
966           }
967         }
968         if (!found) {
969           Tcl_ListObjAppendElement(irp, nicks, Tcl_NewStringObj(m->nick, -1));
970         }
971       }
972     }
973     chan = chan->next;
974   }
975   Tcl_SetObjResult(irp, nicks);
976   return TCL_OK;
977 }
978 
979 static int tcl_hand2nicks STDVAR
980 {
981   char nuh[1024];
982   memberlist *m;
983   struct chanset_t *chan, *thechan = NULL;
984   Tcl_Obj *nicks;
985   Tcl_Obj **nicksv = NULL;
986   int nicksc = 0, i, found;
987 
988   BADARGS(2, 3, " handle ?channel?");
989 
990   if (argc > 2) {
991     chan = findchan_by_dname(argv[2]);
992     thechan = chan;
993     if (chan == NULL) {
994       Tcl_AppendResult(irp, "invalid channel: ", argv[2], NULL);
995       return TCL_ERROR;
996     }
997   } else
998     chan = chanset;
999 
1000   nicks = Tcl_NewListObj(0, NULL);
1001   while (chan && (thechan == NULL || thechan == chan)) {
1002     for (m = chan->channel.member; m && m->nick[0]; m = m->next) {
1003       found = 0;
1004       /* Does this user have the account we're looking for? */
1005       if (!m->user && !m->tried_getuser) {
1006         egg_snprintf(nuh, sizeof nuh, "%s!%s", m->nick, m->userhost);
1007         m->tried_getuser = 1;
1008         m->user = get_user_by_host(nuh);
1009       }
1010       if (m->user && !rfc_casecmp(m->user->handle, argv[1])) {
1011         /* Is the nick of the user already in the list? */
1012         Tcl_ListObjGetElements(irp, nicks, &nicksc, &nicksv);
1013         for (i = 0; i < nicksc; i++) {
1014           if (!rfc_casecmp(m->nick, Tcl_GetString(nicksv[i]))) {
1015             found = 1;
1016             break;
1017           }
1018         }
1019         if (!found) {
1020           Tcl_ListObjAppendElement(irp, nicks, Tcl_NewStringObj(m->nick, -1));
1021         }
1022       }
1023     }
1024     chan = chan->next;
1025   }
1026   Tcl_SetObjResult(irp, nicks);
1027   return TCL_OK;
1028 }
1029 
1030 static int tcl_hand2nick STDVAR
1031 {
1032   char nuh[1024];
1033   memberlist *m;
1034   struct chanset_t *chan, *thechan = NULL;
1035 
1036   BADARGS(2, 3, " handle ?channel?");
1037 
1038   if (argc > 2) {
1039     chan = findchan_by_dname(argv[2]);
1040     thechan = chan;
1041     if (chan == NULL) {
1042       Tcl_AppendResult(irp, "invalid channel: ", argv[2], NULL);
1043       return TCL_ERROR;
1044     }
1045   } else
1046     chan = chanset;
1047 
1048   while (chan && (thechan == NULL || thechan == chan)) {
1049     for (m = chan->channel.member; m && m->nick[0]; m = m->next) {
1050       if (!m->user && !m->tried_getuser) {
1051         egg_snprintf(nuh, sizeof nuh, "%s!%s", m->nick, m->userhost);
1052         m->tried_getuser = 1;
1053         m->user = get_user_by_host(nuh);
1054       }
1055       if (m->user && !rfc_casecmp(m->user->handle, argv[1])) {
1056         Tcl_AppendResult(irp, m->nick, NULL);
1057         return TCL_OK;
1058       }
1059     }
1060     chan = chan->next;
1061   }
1062   return TCL_OK;
1063 }
1064 
1065 static int tcl_nick2hand STDVAR
1066 {
1067   char nuh[1024];
1068   memberlist *m;
1069   struct chanset_t *chan, *thechan = NULL;
1070 
1071   BADARGS(2, 3, " nick ?channel?");
1072 
1073   if (argc > 2) {
1074     chan = findchan_by_dname(argv[2]);
1075     thechan = chan;
1076     if (chan == NULL) {
1077       Tcl_AppendResult(irp, "invalid channel: ", argv[2], NULL);
1078       return TCL_ERROR;
1079     }
1080   } else
1081     chan = chanset;
1082 
1083   while (chan && (thechan == NULL || thechan == chan)) {
1084     m = ismember(chan, argv[1]);
1085     if (m) {
1086       if (!m->user) {
1087         egg_snprintf(nuh, sizeof nuh, "%s!%s", m->nick, m->userhost);
1088         m->user = get_user_by_host(nuh);
1089       }
1090       Tcl_AppendResult(irp, m->user ? m->user->handle : "*", NULL);
1091       return TCL_OK;
1092     }
1093     chan = chan->next;
1094   }
1095   return TCL_OK;
1096 }
1097 
1098 static int tcl_putkick STDVAR
1099 {
1100   struct chanset_t *chan;
1101   int k = 0, l;
1102   char kicknick[512], *nick, *p, *comment = NULL;
1103   memberlist *m;
1104 
1105   BADARGS(3, 4, " channel nick?s? ?comment?");
1106 
1107   chan = findchan_by_dname(argv[1]);
1108   if (chan == NULL) {
1109     Tcl_AppendResult(irp, "illegal channel: ", argv[1], NULL);
1110     return TCL_ERROR;
1111   }
1112   if (argc == 4)
1113     comment = argv[3];
1114   else
1115     comment = "";
1116   if (!me_op(chan) && !me_halfop(chan)) {
1117     Tcl_AppendResult(irp, "need op or halfop", NULL);
1118     return TCL_ERROR;
1119   }
1120 
1121   kicknick[0] = 0;
1122   p = argv[2];
1123   /* Loop through all given nicks */
1124   while (p) {
1125     nick = p;
1126     p = strchr(nick, ',');      /* Search for beginning of next nick */
1127     if (p) {
1128       *p = 0;
1129       p++;
1130     }
1131 
1132     m = ismember(chan, nick);
1133     if (!me_op(chan) && !(me_halfop(chan) && !chan_hasop(m))) {
1134       Tcl_AppendResult(irp, "need op", NULL);
1135       return TCL_ERROR;
1136     }
1137     if (!m)
1138       continue;                 /* Skip non-existent nicks */
1139     m->flags |= SENTKICK;       /* Mark as pending kick */
1140     if (kicknick[0])
1141       strncat(kicknick, ",", sizeof kicknick - strlen(kicknick) - 1);
1142     strncat(kicknick, nick, sizeof kicknick - strlen(kicknick) - 1);     /* Add to local queue */
1143     k++;
1144 
1145     /* Check if we should send the kick command yet */
1146     l = strlen(chan->name) + strlen(kicknick) + strlen(comment);
1147     if (((kick_method != 0) && (k == kick_method)) || (l > 480)) {
1148       dprintf(DP_SERVER, "KICK %s %s :%s\n", chan->name, kicknick, comment);
1149       k = 0;
1150       kicknick[0] = 0;
1151     }
1152   }
1153   /* Clear out all pending kicks in our local kick queue */
1154   if (k > 0)
1155     dprintf(DP_SERVER, "KICK %s %s :%s\n", chan->name, kicknick, comment);
1156   return TCL_OK;
1157 }
1158 
1159 static tcl_cmds tclchan_cmds[] = {
1160   {"chanlist",       tcl_chanlist},
1161   {"botisop",        tcl_botisop},
1162   {"botishalfop",    tcl_botishalfop},
1163   {"botisvoice",     tcl_botisvoice},
1164   {"isop",           tcl_isop},
1165   {"wasop",          tcl_wasop},
1166   {"ishalfop",       tcl_ishalfop},
1167   {"washalfop",      tcl_washalfop},
1168   {"isvoice",        tcl_isvoice},
1169   {"onchan",         tcl_onchan},
1170   {"handonchan",     tcl_handonchan},
1171   {"ischanban",      tcl_ischanban},
1172   {"ischanexempt",   tcl_ischanexempt},
1173   {"ischaninvite",   tcl_ischaninvite},
1174   {"ischanjuped",    tcl_ischanjuped},
1175   {"getchanhost",    tcl_getchanhost},
1176   {"onchansplit",    tcl_onchansplit},
1177   {"maskhost",       tcl_maskhost},
1178   {"getchanidle",    tcl_getchanidle},
1179   {"isaway",         tcl_isaway},
1180   {"chanbans",       tcl_chanbans},
1181   {"chanexempts",    tcl_chanexempts},
1182   {"chaninvites",    tcl_chaninvites},
1183   {"account2nicks",  tcl_account2nicks},
1184   {"hand2nicks",     tcl_hand2nicks},
1185   {"hand2nick",      tcl_hand2nick},
1186   {"nick2hand",      tcl_nick2hand},
1187   {"getchanmode",    tcl_getchanmode},
1188   {"getchanjoin",    tcl_getchanjoin},
1189   {"flushmode",      tcl_flushmode},
1190   {"pushmode",       tcl_pushmode},
1191   {"resetbans",      tcl_resetbans},
1192   {"resetexempts",   tcl_resetexempts},
1193   {"resetinvites",   tcl_resetinvites},
1194   {"resetchanidle",  tcl_resetchanidle},
1195   {"resetchanjoin",  tcl_resetchanjoin},
1196   {"resetchan",      tcl_resetchan},
1197   {"refreshchan",    tcl_refreshchan},
1198   {"topic",          tcl_topic},
1199   {"botonchan",      tcl_botonchan},
1200   {"putkick",        tcl_putkick},
1201   {"channame2dname", tcl_channame2dname},
1202   {"chandname2name", tcl_chandname2name},
1203   {NULL,             NULL}
1204 };
1205