ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/modules/protocol/officeirc.C
Revision: 1.2
Committed: Sat Jul 21 01:29:09 2007 UTC (19 years, 1 month ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.1: +2 -2 lines
Log Message:
- moved to new documentation system
- fixed small build error

File Contents

# User Rev Content
1 pippijn 1.1 /*
2     * Copyright © 2003-2004 E. Will et al.
3     * Copyright © 2005-2006 Atheme Development Group
4 pippijn 1.2 * Rights to this code are documented in doc/pod/license.pod.
5 pippijn 1.1 *
6     * This file contains reverse-engineered IRCXPRO 1.2/OfficeIRC support.
7     *
8 pippijn 1.2 * $Id: officeirc.C,v 1.1 2007-07-19 08:24:55 pippijn Exp $
9 pippijn 1.1 */
10    
11     #include "atheme.h"
12     #include <account/mynick.h>
13     #include "uplink.h"
14     #include "pmodule.h"
15     #include "protocol/officeirc.h"
16    
17     static char const rcsid[] = "$Id";
18    
19     struct officeirc_listeners : callback::has_listeners
20     {
21     };
22    
23     officeirc_listeners listeners;
24    
25     DECLARE_MODULE_V1 ("protocol/officeirc", true, _modinit, NULL, rcsid, "The Ermyth Team <http://ermyth.schmorp.de>");
26    
27     /* *INDENT-OFF* */
28    
29     ircd_t officeirc = {
30     "officeirc/ircxpro", /* IRCd name */
31     "$", /* TLD Prefix, used by Global. */
32     false, /* Whether or not we use IRCNet/TS6 UID */
33     false, /* Whether or not we use RCOMMAND */
34     true, /* Whether or not we support channel owners. */
35     false, /* Whether or not we support channel protection. */
36     false, /* Whether or not we support halfops. */
37     false, /* Whether or not we use P10 */
38     false, /* Whether or not we use vHosts. */
39     0, /* Oper-only cmodes */
40     0, /* Integer flag for owner channel flag. */
41     0, /* Integer flag for protect channel flag. */
42     0, /* Integer flag for halfops. */
43     "+q", /* Mode we set for owner. */
44     "+", /* Mode we set for protect. */
45     "+", /* Mode we set for halfops. */
46     PROTOCOL_OFFICEIRC, /* Protocol type */
47     0, /* Permanent cmodes */
48     "b", /* Ban-like cmodes */
49     0, /* Except mchar */
50     0, /* Invex mchar */
51     0 /* Flags */
52     };
53    
54     cmode_t officeirc_mode_list[] = {
55     { 'i', CMODE_INVITE },
56     { 'm', CMODE_MOD },
57     { 'n', CMODE_NOEXT },
58     { 'p', CMODE_PRIV },
59     { 's', CMODE_SEC },
60     { 't', CMODE_TOPIC },
61     { 'c', CMODE_NOCOLOR },
62     { 'R', CMODE_REGONLY },
63     { '\0', 0 }
64     };
65    
66     extmode officeirc_ignore_mode_list[] = {
67     { '\0', 0 }
68     };
69    
70     cmode_t officeirc_status_mode_list[] = {
71     { 'q', CMODE_OWNER | CMODE_OP },
72     { 'o', CMODE_OP },
73     { 'v', CMODE_VOICE },
74     { '\0', 0 }
75     };
76    
77     cmode_t officeirc_prefix_mode_list[] = {
78     { '.', CMODE_OWNER | CMODE_OP },
79     { '@', CMODE_OP },
80     { '+', CMODE_VOICE },
81     { '\0', 0 }
82     };
83    
84     /* *INDENT-ON* */
85    
86     /* login to our uplink */
87     static unsigned int
88     officeirc_server_login (void)
89     {
90     int ret;
91    
92     ret = sts ("PASS %s", curr_uplink->pass);
93     if (ret == 1)
94     return 1;
95    
96     me.bursting = true;
97    
98     sts ("SERVER %s 1 :%s", me.name, me.desc);
99    
100     services_init ();
101    
102     return 0;
103     }
104    
105     /* introduce a client */
106     static void
107     officeirc_introduce_nick (user_t *u)
108     {
109     /* NICK nenolod 1 1171690800 ~nenolod 69.60.119.15[69.60.119.15] ircxserver01 0 :Unknown */
110    
111     sts ("NICK %s 1 %ld %s %s[0.0.0.0] %s 0 :%s", u->nick, u->ts, u->user, u->host, me.name, u->gecos);
112     sts (":%s MODE %s +%s", u->nick, u->nick, "io");
113     }
114    
115     /* invite a user to a channel */
116     static void
117     officeirc_invite_sts (user_t *sender, user_t *target, channel_t *channel)
118     {
119     sts (":%s INVITE %s %s", sender->nick, target->nick, channel->name);
120     }
121    
122     static void
123     officeirc_quit_sts (user_t *u, const char *reason)
124     {
125     if (!me.connected)
126     return;
127    
128     sts (":%s QUIT :%s", u->nick, reason);
129     }
130    
131     /* WALLOPS wrapper */
132     static void
133     officeirc_wallops_sts (const char *text)
134     {
135     sts (":%s GLOBOPS :%s", me.name, text);
136     }
137    
138     /* join a channel */
139     static void
140     officeirc_join_sts (channel_t *c, user_t *u, bool isnew, char *modes)
141     {
142     if (isnew)
143     {
144     sts (":%s JOIN %s", u->nick, c->name);
145     if (modes[0] && modes[1])
146     sts (":%s MODE %s %s", u->nick, c->name, modes);
147     }
148     else
149     {
150     sts (":%s JOIN %s", u->nick, c->name);
151     }
152     sts (":%s MODE %s +q %s", u->nick, c->name, u->nick);
153     }
154    
155     /* kicks a user from a channel */
156     static void
157     officeirc_kick (char *from, char *channel, char *to, char *reason)
158     {
159     channel_t *chan = channel_find (channel);
160     user_t *user = user_find (to);
161    
162     if (!chan || !user)
163     return;
164    
165     sts (":%s KICK %s %s :%s", from, channel, to, reason);
166    
167     chanuser_delete (chan, user);
168     }
169    
170     /* PRIVMSG wrapper */
171     static void
172     officeirc_msg (const char *from, const char *target, const char *fmt, ...)
173     {
174     va_list ap;
175     char buf[BUFSIZE];
176    
177     va_start (ap, fmt);
178     vsnprintf (buf, BUFSIZE, fmt, ap);
179     va_end (ap);
180    
181     sts (":%s PRIVMSG %s :%s", from, target, buf);
182     }
183    
184     /* NOTICE wrapper */
185     static void
186     officeirc_notice_user_sts (user_t *from, user_t *target, const char *text)
187     {
188     sts (":%s NOTICE %s :%s", from ? from->nick : me.name, target->nick, text);
189     }
190    
191     static void
192     officeirc_notice_global_sts (user_t *from, const char *mask, const char *text)
193     {
194     node_t *n;
195     tld_t *tld;
196    
197     if (!strcmp (mask, "*"))
198     {
199     LIST_FOREACH (n, tldlist.head)
200     {
201     tld = static_cast<tld_t *> (n->data);
202     sts (":%s NOTICE %s*%s :%s", from ? from->nick : me.name, ircd->tldprefix, tld->name, text);
203     }
204     }
205     else
206     sts (":%s NOTICE %s%s :%s", from ? from->nick : me.name, ircd->tldprefix, mask, text);
207     }
208    
209     static void
210     officeirc_notice_channel_sts (user_t *from, channel_t *target, const char *text)
211     {
212     sts (":%s NOTICE %s :%s", from ? from->nick : me.name, target->name, text);
213     }
214    
215     static void
216     officeirc_numeric_sts (char *from, int numeric, char *target, char *fmt, ...)
217     {
218     va_list ap;
219     char buf[BUFSIZE];
220    
221     va_start (ap, fmt);
222     vsnprintf (buf, BUFSIZE, fmt, ap);
223     va_end (ap);
224    
225     sts (":%s %d %s %s", from, numeric, target, buf);
226     }
227    
228     /* KILL wrapper */
229     static void
230     officeirc_skill (char *from, char *nick, char *fmt, ...)
231     {
232     va_list ap;
233     char buf[BUFSIZE];
234    
235     va_start (ap, fmt);
236     vsnprintf (buf, BUFSIZE, fmt, ap);
237     va_end (ap);
238    
239     sts (":%s KILL %s :%s!%s!%s (%s)", from, nick, from, from, from, buf);
240     }
241    
242     /* PART wrapper */
243     static void
244     officeirc_part_sts (channel_t *c, user_t *u)
245     {
246     sts (":%s PART %s", u->nick, c->name);
247     }
248    
249     /* server-to-server KLINE wrapper */
250     static void
251     officeirc_kline_sts (char *server, char *user, char *host, long duration, char *reason)
252     {
253     if (!me.connected)
254     return;
255    
256     sts (":%s ACCESS * ADD AKILL 0 *!%s@%s$* %ld 0 %s %ld 0 :%s", opersvs.nick, host, user, time (NULL), opersvs.nick, duration, reason);
257     }
258    
259     /* server-to-server UNKLINE wrapper */
260     static void
261     officeirc_unkline_sts (char *server, char *user, char *host)
262     {
263     if (!me.connected)
264     return;
265    
266     sts (":%s ACCESS * DELETE AKILL 0 *!%s@%s$* %ld 0 %s 0 0 :", opersvs.nick, host, user, time (NULL), opersvs.nick);
267     }
268    
269     /* topic wrapper */
270     static void
271     officeirc_topic_sts (channel_t *c, const char *setter, time_t ts, time_t prevts, const char *topic)
272     {
273     if (!me.connected)
274     return;
275    
276     if (ts < prevts || prevts == 0)
277     sts (":%s TOPIC %s %s %ld :%s", chansvs.nick, c->name, setter, ts, topic);
278     else if (prevts > 1)
279     {
280     ts = prevts - 1;
281     sts (":%s TOPIC %s %s %ld :%s", chansvs.nick, c->name, "topictime.wrong", ts, topic);
282     c->topicts = ts;
283     }
284     else
285     {
286     notice (chansvs.nick, c->name, "Unable to change topic to: %s", topic);
287     c->topicts = 1;
288     }
289     }
290    
291     /* mode wrapper */
292     static void
293     officeirc_mode_sts (char *sender, channel_t *target, char *modes)
294     {
295     if (!me.connected)
296     return;
297    
298     sts (":%s MODE %s %s", sender, target->name, modes);
299     }
300    
301     /* ping wrapper */
302     static void
303     officeirc_ping_sts (void)
304     {
305     if (!me.connected)
306     return;
307    
308     sts ("PING :%s", me.name);
309     }
310    
311     /* protocol-specific stuff to do on login */
312     static void
313     officeirc_on_login (char *origin, char *user, char *wantedhost)
314     {
315     user_t *u = user_find (origin);
316    
317     if (!me.connected || u == NULL)
318     return;
319    
320     /* Can only do this for nickserv, and can only record identified
321     * state if logged in to correct nick, sorry -- jilles
322     */
323     if (should_reg_umode (u))
324     sts (":%s SVSMODE %s +rd %ld", nicksvs.nick, origin, time (NULL));
325     }
326    
327     /* protocol-specific stuff to do on login */
328     static bool
329     officeirc_on_logout (char *origin, char *user, char *wantedhost)
330     {
331     if (!me.connected)
332     return false;
333    
334     if (!nicksvs.no_nick_ownership)
335     sts (":%s SVSMODE %s -r+d %ld", nicksvs.nick, origin, time (NULL));
336     return false;
337     }
338    
339     static void
340     officeirc_jupe (const char *server, const char *reason)
341     {
342     if (!me.connected)
343     return;
344    
345     server_delete (server);
346     sts (":%s SQUIT %s :%s", opersvs.nick, server, reason);
347     sts (":%s SERVER %s 2 :%s", me.name, server, reason);
348     }
349    
350     static void
351     m_topic (sourceinfo_t *si, int parc, char *parv[])
352     {
353     channel_t *c = channel_find (parv[0]);
354    
355     if (!c)
356     return;
357    
358     handle_topic_from (si, c, parv[1], atol (parv[2]), parv[3]);
359     }
360    
361     static void
362     m_ping (sourceinfo_t *si, int parc, char *parv[])
363     {
364     /* reply to PING's */
365     sts (":%s PONG %s %s", me.name, me.name, parv[0]);
366     }
367    
368     static void
369     m_pong (sourceinfo_t *si, int parc, char *parv[])
370     {
371     server_t *s;
372    
373     /* someone replied to our PING */
374     if (!parv[0])
375     return;
376     s = server_find (parv[0]);
377     if (s == NULL)
378     return;
379     handle_eob (s);
380    
381     if (irccasecmp (me.actual, parv[0]))
382     return;
383    
384     me.uplinkpong = NOW;
385    
386     /* -> :test.projectxero.net PONG test.projectxero.net :shrike.malkier.net */
387     if (me.bursting)
388     {
389     #ifdef HAVE_GETTIMEOFDAY
390     e_time (burstime, &burstime);
391    
392     slog (LG_INFO, "m_pong(): finished synching with uplink (%d %s)", (tv2ms (&burstime) > 1000) ? (tv2ms (&burstime) / 1000) : tv2ms (&burstime), (tv2ms (&burstime) > 1000) ? "s" : "ms");
393    
394     wallops ("Finished synching to network in %d %s.", (tv2ms (&burstime) > 1000) ? (tv2ms (&burstime) / 1000) : tv2ms (&burstime), (tv2ms (&burstime) > 1000) ? "s" : "ms");
395     #else
396     slog (LG_INFO, "m_pong(): finished synching with uplink");
397     wallops ("Finished synching to network.");
398     #endif
399    
400     me.bursting = false;
401     }
402     }
403    
404     static void
405     m_privmsg (sourceinfo_t *si, int parc, char *parv[])
406     {
407     if (parc != 2)
408     return;
409    
410     handle_message (si, parv[0], false, parv[1]);
411     }
412    
413     static void
414     m_notice (sourceinfo_t *si, int parc, char *parv[])
415     {
416     if (parc != 2)
417     return;
418    
419     handle_message (si, parv[0], true, parv[1]);
420     }
421    
422     static void
423     m_part (sourceinfo_t *si, int parc, char *parv[])
424     {
425     int chanc;
426     char *chanv[256];
427     int i;
428    
429     chanc = sjtoken (parv[0], ',', chanv);
430     for (i = 0; i < chanc; i++)
431     {
432     slog (LG_DEBUG, "m_part(): user left channel: %s -> %s", si->su->nick, chanv[i]);
433    
434     chanuser_delete (channel_find (chanv[i]), si->su);
435     }
436     }
437    
438     static void
439     m_nick (sourceinfo_t *si, int parc, char *parv[])
440     {
441     server_t *s;
442     bool realchange;
443    
444     if (parc == 8)
445     {
446     char *host, *ip;
447    
448     s = server_find (parv[5]);
449     if (!s)
450     {
451     slog (LG_DEBUG, "m_nick(): new user on nonexistant server: %s", parv[6]);
452     return;
453     }
454    
455     slog (LG_DEBUG, "m_nick(): new user on `%s': %s", s->name, parv[0]);
456    
457     host = sstrdup (parv[4]);
458     ip = strchr (host, '[');
459    
460     if (ip != NULL)
461     {
462     *ip++ = '\0';
463     ip = strchr (ip, ']');
464    
465     if (ip != NULL)
466     *ip = '\0';
467     }
468    
469     user_add (parv[0], parv[3], host, ip, NULL, NULL, parv[7], s, atoi (parv[2]));
470    
471     free (host);
472    
473     /* Note: cannot rely on umode +r to see if they're identified
474     * -- jilles */
475    
476     handle_nickchange (user_find (parv[0]));
477     }
478    
479     /* if it's only 2 then it's a nickname change */
480     else if (parc == 2)
481     {
482     if (!si->su)
483     {
484     slog (LG_DEBUG, "m_nick(): server trying to change nick: %s", si->s != NULL ? si->s->name : "<none>");
485     return;
486     }
487    
488     slog (LG_DEBUG, "m_nick(): nickname change from `%s': %s", si->su->nick, parv[0]);
489    
490     realchange = irccasecmp (si->su->nick, parv[0]);
491    
492     user_changenick (si->su, parv[0], atoi (parv[1]));
493    
494     /* fix up +r if necessary -- jilles */
495     if (realchange && !nicksvs.no_nick_ownership)
496     {
497     if (should_reg_umode (si->su))
498     /* changed nick to registered one, reset +r */
499     sts (":%s SVSMODE %s +rd %ld", nicksvs.nick, parv[0], NOW);
500     else
501     /* changed from registered nick, remove +r */
502     sts (":%s SVSMODE %s -r+d %ld", nicksvs.nick, parv[0], NOW);
503     }
504    
505     handle_nickchange (si->su);
506     }
507     else
508     {
509     int i;
510     slog (LG_DEBUG, "m_nick(): got NICK with wrong number of params");
511    
512     for (i = 0; i < parc; i++)
513     slog (LG_DEBUG, "m_nick(): parv[%d] = %s", i, parv[i]);
514     }
515     }
516    
517     static void
518     m_quit (sourceinfo_t *si, int parc, char *parv[])
519     {
520     if (si->su->server == me.me)
521     {
522     slog (LG_DEBUG, "m_quit(): not destroying own user %s (fake direction)", si->su->nick);
523     return;
524     }
525     slog (LG_DEBUG, "m_quit(): user leaving: %s", si->su->nick);
526    
527     /* user_delete() takes care of removing channels and so forth */
528     user_delete (si->su);
529     }
530    
531     static void
532     m_mode (sourceinfo_t *si, int parc, char *parv[])
533     {
534     if (*parv[0] == '#')
535     channel_mode (NULL, channel_find (parv[0]), parc - 1, &parv[1]);
536     else
537     user_mode (user_find (parv[0]), parv[1]);
538     }
539    
540     static void
541     m_kick (sourceinfo_t *si, int parc, char *parv[])
542     {
543     user_t *u = user_find (parv[1]);
544     channel_t *c = channel_find (parv[0]);
545    
546     /* -> :rakaur KICK #shrike rintaun :test */
547     slog (LG_DEBUG, "m_kick(): user was kicked: %s -> %s", parv[1], parv[0]);
548    
549     if (!u)
550     {
551     slog (LG_DEBUG, "m_kick(): got kick for nonexistant user %s", parv[1]);
552     return;
553     }
554    
555     if (!c)
556     {
557     slog (LG_DEBUG, "m_kick(): got kick in nonexistant channel: %s", parv[0]);
558     return;
559     }
560    
561     if (!chanuser_find (c, u))
562     {
563     slog (LG_DEBUG, "m_kick(): got kick for %s not in %s", u->nick, c->name);
564     return;
565     }
566    
567     chanuser_delete (c, u);
568    
569     /* if they kicked us, let's rejoin */
570     if (is_internal_client (u))
571     {
572     slog (LG_DEBUG, "m_kick(): %s got kicked from %s; rejoining", u->nick, parv[0]);
573     join (parv[0], u->nick);
574     }
575     }
576    
577     static void
578     m_kill (sourceinfo_t *si, int parc, char *parv[])
579     {
580     handle_kill (si, parv[0], parc > 1 ? parv[1] : "<No reason given>");
581     }
582    
583     static void
584     m_squit (sourceinfo_t *si, int parc, char *parv[])
585     {
586     slog (LG_DEBUG, "m_squit(): server leaving: %s from %s", parv[0], parv[1]);
587     server_delete (parv[0]);
588     }
589    
590     static void
591     m_server (sourceinfo_t *si, int parc, char *parv[])
592     {
593     server_t *s;
594    
595     slog (LG_DEBUG, "m_server(): new server: %s", parv[0]);
596     s = handle_server (si, parv[0], NULL, atoi (parv[1]), parv[2]);
597    
598     if (s != NULL && s->uplink != me.me)
599     {
600     /* elicit PONG for EOB detection; pinging uplink is
601     * already done elsewhere -- jilles
602     */
603     sts (":%s PING %s %s", me.name, me.name, s->name);
604     }
605     }
606    
607     static void
608     m_stats (sourceinfo_t *si, int parc, char *parv[])
609     {
610     handle_stats (si->su, parv[0][0]);
611     }
612    
613     static void
614     m_admin (sourceinfo_t *si, int parc, char *parv[])
615     {
616     handle_admin (si->su);
617     }
618    
619     static void
620     m_version (sourceinfo_t *si, int parc, char *parv[])
621     {
622     handle_version (si->su);
623     }
624    
625     static void
626     m_info (sourceinfo_t *si, int parc, char *parv[])
627     {
628     handle_info (si->su);
629     }
630    
631     static void
632     m_whois (sourceinfo_t *si, int parc, char *parv[])
633     {
634     handle_whois (si->su, parv[1]);
635     }
636    
637     static void
638     m_trace (sourceinfo_t *si, int parc, char *parv[])
639     {
640     handle_trace (si->su, parv[0], parc >= 2 ? parv[1] : NULL);
641     }
642    
643     static void
644     m_away (sourceinfo_t *si, int parc, char *parv[])
645     {
646     handle_away (si->su, parc >= 1 ? parv[0] : NULL);
647     }
648    
649     static void
650     m_join (sourceinfo_t *si, int parc, char *parv[])
651     {
652     chanuser_t *cu;
653     node_t *n, *tn;
654     int chanc;
655     char *chanv[256];
656     int i;
657    
658     /* JOIN 0 is really a part from all channels */
659     if (parv[0][0] == '0')
660     {
661     LIST_FOREACH_SAFE (n, tn, si->su->channels.head)
662     {
663     cu = (chanuser_t *) n->data;
664     chanuser_delete (cu->chan, si->su);
665     }
666     }
667     else
668     {
669     chanc = sjtoken (parv[0], ',', chanv);
670     for (i = 0; i < chanc; i++)
671     {
672     channel_t *c = channel_find (chanv[i]);
673    
674     if (!c)
675     {
676     slog (LG_DEBUG, "m_join(): new channel: %s", parv[0]);
677     c = channel_add (chanv[i], NOW, si->su->server);
678     /* Tell the core to check mode locks now,
679     * otherwise it may only happen after the next
680     * mode change.
681     * DreamForge does not allow any redundant modes
682     * so this will not look ugly. -- jilles */
683     /* If this is in a burst, a MODE with the
684     * simple modes will follow so we can skip
685     * this. -- jilles */
686     if (!me.bursting)
687     channel_mode_va (NULL, c, 1, "+");
688     }
689     chanuser_add (c, si->su->nick);
690     }
691     }
692     }
693    
694     static void
695     m_pass (sourceinfo_t *si, int parc, char *parv[])
696     {
697     if (strcmp (curr_uplink->pass, parv[0]))
698     {
699     slog (LG_INFO, "m_pass(): password mismatch from uplink; aborting");
700     runflags |= RF_SHUTDOWN;
701     }
702     }
703    
704     static void
705     m_error (sourceinfo_t *si, int parc, char *parv[])
706     {
707     slog (LG_INFO, "m_error(): error from server: %s", parv[0]);
708     }
709    
710     static void
711     m_motd (sourceinfo_t *si, int parc, char *parv[])
712     {
713     handle_motd (si->su);
714     }
715    
716     static void
717     m_njoin (sourceinfo_t *si, int parc, char *parv[])
718     {
719     /* -> :ircxserver01 NJOIN #take 1073516550 :.qbot */
720    
721     channel_t *c;
722     unsigned int userc;
723     char *userv[256];
724     unsigned int i;
725     time_t ts;
726     char *p;
727     bool keep_new_modes = true;
728    
729     /* :origin SJOIN chan ts :users */
730     if (si->s == NULL)
731     return;
732    
733     c = channel_find (parv[0]);
734     ts = atol (parv[1]);
735    
736     if (!c)
737     {
738     slog (LG_DEBUG, "m_njoin(): new channel: %s", parv[1]);
739     c = channel_add (parv[0], ts, si->s);
740     /* Tell the core to check mode locks now,
741     * otherwise it may only happen after the next
742     * mode change.
743     * DreamForge does not allow any redundant modes
744     * so this will not look ugly. -- jilles */
745     channel_mode_va (NULL, c, 1, "+");
746     }
747    
748     if (ts == 0 || c->ts == 0)
749     {
750     if (c->ts != 0)
751     slog (LG_INFO, "m_sjoin(): server %s changing TS on %s from %ld to 0", si->s->name, c->name, (long) c->ts);
752     c->ts = 0;
753     hook_call_event ("channel_tschange", c);
754     }
755     else if (ts < c->ts)
756     {
757     chanuser_t *cu;
758     node_t *n;
759    
760     /* the TS changed. a TS change requires the following things
761     * to be done to the channel: reset all modes to nothing, remove
762     * all status modes on known users on the channel (including ours),
763     * and set the new TS.
764     */
765    
766     clear_simple_modes (c);
767    
768     LIST_FOREACH (n, c->members.head)
769     {
770     cu = (chanuser_t *) n->data;
771     if (cu->user->server == me.me)
772     {
773     /* it's a service, reop */
774     sts (":%s MODE %s +o %s", CLIENT_NAME (cu->user), c->name, CLIENT_NAME (cu->user));
775     cu->modes = CMODE_OP;
776     }
777     else
778     cu->modes = 0;
779     }
780    
781     slog (LG_DEBUG, "m_sjoin(): TS changed for %s (%ld -> %ld)", c->name, c->ts, ts);
782    
783     c->ts = ts;
784     hook_call_event ("channel_tschange", c);
785     }
786     else if (ts > c->ts)
787     keep_new_modes = false;
788    
789     userc = sjtoken (parv[parc - 1], ' ', userv);
790    
791     if (keep_new_modes)
792     for (i = 0; i < userc; i++)
793     chanuser_add (c, userv[i]);
794     else
795     for (i = 0; i < userc; i++)
796     {
797     p = userv[i];
798     while (*p == '@' || *p == '%' || *p == '+' || *p == '.')
799     p++;
800     chanuser_add (c, p);
801     }
802    
803     if (c->nummembers == 0)
804     channel_delete (c);
805     }
806    
807     static void
808     nick_group (hook_user_req_t * hdata)
809     {
810     user_t *u;
811    
812     u = hdata->si->su != NULL && !irccasecmp (hdata->si->su->nick, hdata->mn->nick) ? hdata->si->su : user_find_named (hdata->mn->nick);
813     if (u != NULL && should_reg_umode (u))
814     sts (":%s SVSMODE %s +rd %ld", nicksvs.nick, u->nick, time (NULL));
815     }
816    
817     static void
818     nick_ungroup (hook_user_req_t * hdata)
819     {
820     user_t *u;
821    
822     u = hdata->si->su != NULL && !irccasecmp (hdata->si->su->nick, hdata->mn->nick) ? hdata->si->su : user_find_named (hdata->mn->nick);
823     if (u != NULL && !nicksvs.no_nick_ownership)
824     sts (":%s SVSMODE %s -r+d %ld", nicksvs.nick, u->nick, time (NULL));
825     }
826    
827     void
828     _modinit (module_t *m)
829     {
830     /* Symbol relocation voodoo. */
831     server_login = &officeirc_server_login;
832     introduce_nick = &officeirc_introduce_nick;
833     quit_sts = &officeirc_quit_sts;
834     wallops_sts = &officeirc_wallops_sts;
835     join_sts = &officeirc_join_sts;
836     kick = &officeirc_kick;
837     msg = &officeirc_msg;
838     notice_user_sts = &officeirc_notice_user_sts;
839     notice_global_sts = &officeirc_notice_global_sts;
840     notice_channel_sts = &officeirc_notice_channel_sts;
841     numeric_sts = &officeirc_numeric_sts;
842     skill = &officeirc_skill;
843     part_sts = &officeirc_part_sts;
844     kline_sts = &officeirc_kline_sts;
845     unkline_sts = &officeirc_unkline_sts;
846     topic_sts = &officeirc_topic_sts;
847     mode_sts = &officeirc_mode_sts;
848     ping_sts = &officeirc_ping_sts;
849     ircd_on_login = &officeirc_on_login;
850     ircd_on_logout = &officeirc_on_logout;
851     jupe = &officeirc_jupe;
852     invite_sts = &officeirc_invite_sts;
853    
854     mode_list = officeirc_mode_list;
855     ignore_mode_list = officeirc_ignore_mode_list;
856     status_mode_list = officeirc_status_mode_list;
857     prefix_mode_list = officeirc_prefix_mode_list;
858    
859     ircd = &officeirc;
860    
861     pcommand_add ("PING", m_ping, 1, MSRC_USER | MSRC_SERVER);
862     pcommand_add ("PONG", m_pong, 1, MSRC_SERVER);
863     pcommand_add ("PRIVMSG", m_privmsg, 2, MSRC_USER);
864     pcommand_add ("NOTICE", m_notice, 2, MSRC_UNREG | MSRC_USER | MSRC_SERVER);
865     pcommand_add ("PART", m_part, 1, MSRC_USER);
866     pcommand_add ("NICK", m_nick, 2, MSRC_USER | MSRC_SERVER);
867     pcommand_add ("QUIT", m_quit, 1, MSRC_USER);
868     pcommand_add ("MODE", m_mode, 2, MSRC_USER | MSRC_SERVER);
869     pcommand_add ("KICK", m_kick, 2, MSRC_USER | MSRC_SERVER);
870     pcommand_add ("KILL", m_kill, 1, MSRC_USER | MSRC_SERVER);
871     pcommand_add ("SQUIT", m_squit, 1, MSRC_USER | MSRC_SERVER);
872     pcommand_add ("SERVER", m_server, 3, MSRC_UNREG | MSRC_SERVER);
873     pcommand_add ("STATS", m_stats, 2, MSRC_USER);
874     pcommand_add ("ADMIN", m_admin, 1, MSRC_USER);
875     pcommand_add ("VERSION", m_version, 1, MSRC_USER);
876     pcommand_add ("INFO", m_info, 1, MSRC_USER);
877     pcommand_add ("WHOIS", m_whois, 2, MSRC_USER);
878     pcommand_add ("TRACE", m_trace, 1, MSRC_USER);
879     pcommand_add ("AWAY", m_away, 0, MSRC_USER);
880     pcommand_add ("JOIN", m_join, 1, MSRC_USER);
881     pcommand_add ("PASS", m_pass, 1, MSRC_UNREG);
882     pcommand_add ("ERROR", m_error, 1, MSRC_UNREG | MSRC_SERVER);
883     pcommand_add ("TOPIC", m_topic, 4, MSRC_USER | MSRC_SERVER);
884     pcommand_add ("MOTD", m_motd, 1, MSRC_USER);
885     pcommand_add ("NJOIN", m_njoin, 3, MSRC_SERVER);
886    
887     hook_add_event ("nick_group");
888     hook_add_hook ("nick_group", (void (*)(void *)) nick_group);
889     hook_add_event ("nick_ungroup");
890     hook_add_hook ("nick_ungroup", (void (*)(void *)) nick_ungroup);
891    
892     m->mflags = MODTYPE_CORE;
893    
894     pmodule_loaded = true;
895     }
896    
897     /* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
898     * vim:ts=8
899     * vim:sw=8
900     * vim:noexpandtab
901     */