channels.c

Go to the documentation of this file.
00001 /* Channel-handling routines.
00002  *
00003  * (C) 2003-2007 Anope Team
00004  * Contact us at info@anope.org
00005  *
00006  * Please read COPYING and README for further details.
00007  *
00008  * Based on the original code of Epona by Lara.
00009  * Based on the original code of Services by Andy Church. 
00010  * 
00011  * $Id: channels.c 1322 2007-12-28 19:12:02Z geniusdex $
00012  *
00013  */
00014 
00015 #include "services.h"
00016 #include "language.h"
00017 
00018 Channel *chanlist[1024];
00019 
00020 #define HASH(chan)  ((chan)[1] ? ((chan)[1]&31)<<5 | ((chan)[2]&31) : 0)
00021 
00022 /**************************** External Calls *****************************/
00023 /*************************************************************************/
00024 
00025 void chan_deluser(User * user, Channel * c)
00026 {
00027     struct c_userlist *u;
00028 
00029     if (c->ci)
00030         update_cs_lastseen(user, c->ci);
00031 
00032     for (u = c->users; u && u->user != user; u = u->next);
00033     if (!u)
00034         return;
00035 
00036     if (u->ud) {
00037         if (u->ud->lastline)
00038             free(u->ud->lastline);
00039         free(u->ud);
00040     }
00041 
00042     if (u->next)
00043         u->next->prev = u->prev;
00044     if (u->prev)
00045         u->prev->next = u->next;
00046     else
00047         c->users = u->next;
00048     free(u);
00049     c->usercount--;
00050 
00051     if (s_BotServ && c->ci && c->ci->bi && c->usercount == BSMinUsers - 1) {
00052         anope_cmd_part(c->ci->bi->nick, c->name, NULL);
00053     }
00054 
00055     if (!c->users)
00056         chan_delete(c);
00057 }
00058 
00059 /*************************************************************************/
00060 
00061 /* Returns a fully featured binary modes string. If complete is 0, the
00062  * eventual parameters won't be added to the string.
00063  */
00064 
00065 char *chan_get_modes(Channel * chan, int complete, int plus)
00066 {
00067     static char res[BUFSIZE];
00068     char *end = res;
00069 
00070     if (chan->mode) {
00071         int n = 0;
00072         CBModeInfo *cbmi = cbmodeinfos;
00073 
00074         do {
00075             if (chan->mode & cbmi->flag)
00076                 *end++ = cbmi->mode;
00077         } while ((++cbmi)->flag != 0 && ++n < sizeof(res) - 1);
00078 
00079         if (complete) {
00080             cbmi = cbmodeinfos;
00081 
00082             do {
00083                 if (cbmi->getvalue && (chan->mode & cbmi->flag) &&
00084                     (plus || !(cbmi->flags & CBM_MINUS_NO_ARG))) {
00085                     char *value = cbmi->getvalue(chan);
00086 
00087                     if (value) {
00088                         *end++ = ' ';
00089                         while (*value)
00090                             *end++ = *value++;
00091                     }
00092                 }
00093             } while ((++cbmi)->flag != 0 && ++n < sizeof(res) - 1);
00094         }
00095     }
00096 
00097     *end = 0;
00098 
00099     return res;
00100 }
00101 
00102 /*************************************************************************/
00103 
00104 /* Retrieves the status of an user on a channel */
00105 
00106 int chan_get_user_status(Channel * chan, User * user)
00107 {
00108     struct u_chanlist *uc;
00109 
00110     for (uc = user->chans; uc; uc = uc->next)
00111         if (uc->chan == chan)
00112             return uc->status;
00113 
00114     return 0;
00115 }
00116 
00117 /*************************************************************************/
00118 
00119 /* Has the given user the given status on the given channel? :p */
00120 
00121 int chan_has_user_status(Channel * chan, User * user, int16 status)
00122 {
00123     struct u_chanlist *uc;
00124 
00125     for (uc = user->chans; uc; uc = uc->next) {
00126         if (uc->chan == chan) {
00127             if (debug) {
00128                 alog("debug: chan_has_user_status wanted %d the user is %d", status, uc->status);
00129             }
00130             return (uc->status & status);
00131         }
00132     }
00133     return 0;
00134 }
00135 
00136 /*************************************************************************/
00137 
00138 /* Remove the status of an user on a channel */
00139 
00140 void chan_remove_user_status(Channel * chan, User * user, int16 status)
00141 {
00142     struct u_chanlist *uc;
00143 
00144     if (debug >= 2)
00145         alog("debug: removing user status (%d) from %s for %s", status,
00146              user->nick, chan->name);
00147 
00148     for (uc = user->chans; uc; uc = uc->next) {
00149         if (uc->chan == chan) {
00150             uc->status &= ~status;
00151             break;
00152         }
00153     }
00154 }
00155 
00156 /*************************************************************************/
00157 
00158 void chan_set_modes(const char *source, Channel * chan, int ac, char **av,
00159                     int check)
00160 {
00161     int add = 1;
00162     char *modes = av[0], mode;
00163     CBMode *cbm;
00164     CMMode *cmm;
00165     CUMode *cum;
00166     unsigned char botmode = 0;
00167     BotInfo *bi;
00168     User *u, *user;
00169     int i, real_ac = ac;
00170     char **real_av = av;
00171 
00172     if (debug)
00173         alog("debug: Changing modes for %s to %s", chan->name,
00174              merge_args(ac, av));
00175 
00176     u = finduser(source);
00177     if (u && (chan_get_user_status(chan, u) & CUS_DEOPPED)) {
00178         char *s;
00179 
00180         if (debug)
00181             alog("debug: Removing instead of setting due to DEOPPED flag");
00182 
00183         /* Swap adding and removing of the modes */
00184         for (s = av[0]; *s; s++) {
00185             if (*s == '+')
00186                 *s = '-';
00187             else if (*s == '-')
00188                 *s = '+';
00189         }
00190 
00191         /* Set the resulting mode buffer */
00192         anope_cmd_mode(whosends(chan->ci), chan->name, merge_args(ac, av));
00193 
00194         return;
00195     }
00196 
00197     ac--;
00198 
00199     while ((mode = *modes++)) {
00200 
00201         switch (mode) {
00202         case '+':
00203             add = 1;
00204             continue;
00205         case '-':
00206             add = 0;
00207             continue;
00208         }
00209 
00210         if (((int) mode) < 0) {
00211             if (debug)
00212                 alog("Debug: Malformed mode detected on %s.", chan->name);
00213             continue;
00214         }
00215 
00216         if ((cum = &cumodes[(int) mode])->status != 0) {
00217             if (ac == 0) {
00218                 alog("channel: mode %c%c with no parameter (?) for channel %s", add ? '+' : '-', mode, chan->name);
00219                 continue;
00220             }
00221             ac--;
00222             av++;
00223 
00224             if ((cum->flags & CUF_PROTECT_BOTSERV) && !add) {
00225                 if ((bi = findbot(*av))) {
00226                     if (!botmode || botmode != mode) {
00227                         anope_cmd_mode(bi->nick, chan->name, "+%c %s",
00228                                        mode, bi->nick);
00229                         botmode = mode;
00230                         continue;
00231                     } else {
00232                         botmode = mode;
00233                         continue;
00234                     }
00235                 }
00236             } else {
00237                 if ((bi = findbot(*av))) {
00238                     continue;
00239                 }
00240             }
00241 
00242             if (!(user = finduser(*av))
00243                 && !(UseTS6 && ircd->ts6 && (user = find_byuid(*av)))) {
00244                 if (debug) {
00245                     alog("debug: MODE %s %c%c for nonexistent user %s",
00246                          chan->name, (add ? '+' : '-'), mode, *av);
00247                 }
00248                 continue;
00249             }
00250 
00251             if (debug)
00252                 alog("debug: Setting %c%c on %s for %s", (add ? '+' : '-'),
00253                      mode, chan->name, user->nick);
00254 
00255             if (add) {
00256                 chan_set_user_status(chan, user, cum->status);
00257                 /* If this does +o, remove any DEOPPED flag */
00258                 if (cum->status & CUS_OP)
00259                     chan_remove_user_status(chan, user, CUS_DEOPPED);
00260             } else {
00261                 chan_remove_user_status(chan, user, cum->status);
00262             }
00263 
00264         } else if ((cbm = &cbmodes[(int) mode])->flag != 0) {
00265             if (check >= 0) {
00266                 if (add)
00267                     chan->mode |= cbm->flag;
00268                 else
00269                     chan->mode &= ~cbm->flag;
00270             }
00271 
00272             if (cbm->setvalue) {
00273                 if (add || !(cbm->flags & CBM_MINUS_NO_ARG)) {
00274                     if (ac == 0) {
00275                         alog("channel: mode %c%c with no parameter (?) for channel %s", add ? '+' : '-', mode, chan->name);
00276                         continue;
00277                     }
00278                     ac--;
00279                     av++;
00280                 }
00281                 cbm->setvalue(chan, add ? *av : NULL);
00282             }
00283 
00284             if (check < 0) {
00285                 if (add)
00286                     chan->mode |= cbm->flag;
00287                 else
00288                     chan->mode &= ~cbm->flag;
00289             }
00290         } else if ((cmm = &cmmodes[(int) mode])->addmask) {
00291             if (ac == 0) {
00292                 alog("channel: mode %c%c with no parameter (?) for channel %s", add ? '+' : '-', mode, chan->name);
00293                 continue;
00294             }
00295 
00296             ac--;
00297             av++;
00298             if (add)
00299                 cmm->addmask(chan, *av);
00300             else
00301                 cmm->delmask(chan, *av);
00302         }
00303     }
00304 
00305     if (check > 0) {
00306         check_modes(chan);
00307 
00308         if (check < 2) {
00309             /* Walk through all users we've set modes for and see if they are
00310              * valid. Invalid modes (like +o with SECUREOPS on) will be removed
00311              */
00312             real_ac--;
00313             real_av++;
00314             for (i = 0; i < real_ac; i++) {
00315                 if ((user = finduser(*real_av)) && is_on_chan(chan, user))
00316                     chan_set_correct_modes(user, chan, 0);
00317                 real_av++;
00318             }
00319         }
00320     }
00321 }
00322 
00323 /*************************************************************************/
00324 
00325 /* Set the status of an user on a channel */
00326 
00327 void chan_set_user_status(Channel * chan, User * user, int16 status)
00328 {
00329     struct u_chanlist *uc;
00330 
00331     if (debug >= 2)
00332         alog("debug: setting user status (%d) on %s for %s", status,
00333              user->nick, chan->name);
00334 
00335     if (HelpChannel && ircd->supporthelper && (status & CUS_OP)
00336         && (stricmp(chan->name, HelpChannel) == 0)
00337         && (!chan->ci || check_access(user, chan->ci, CA_AUTOOP))) {
00338         if (debug) {
00339             alog("debug: %s being given +h for having %d status in %s",
00340                  user->nick, status, chan->name);
00341         }
00342         common_svsmode(user, "+h", NULL);
00343     }
00344 
00345     for (uc = user->chans; uc; uc = uc->next) {
00346         if (uc->chan == chan) {
00347             uc->status |= status;
00348             break;
00349         }
00350     }
00351 }
00352 
00353 /*************************************************************************/
00354 
00355 /* Return the Channel structure corresponding to the named channel, or NULL
00356  * if the channel was not found.  chan is assumed to be non-NULL and valid
00357  * (i.e. pointing to a channel name of 2 or more characters). */
00358 
00359 Channel *findchan(const char *chan)
00360 {
00361     Channel *c;
00362 
00363     if (!chan || !*chan) {
00364         if (debug) {
00365             alog("debug: findchan() called with NULL values");
00366         }
00367         return NULL;
00368     }
00369 
00370     if (debug >= 3)
00371         alog("debug: findchan(%p)", chan);
00372     c = chanlist[HASH(chan)];
00373     while (c) {
00374         if (stricmp(c->name, chan) == 0)
00375             return c;
00376         c = c->next;
00377     }
00378     if (debug >= 3)
00379         alog("debug: findchan(%s) -> %p", chan, (void *) c);
00380     return NULL;
00381 }
00382 
00383 /*************************************************************************/
00384 
00385 /* Iterate over all channels in the channel list.  Return NULL at end of
00386  * list.
00387  */
00388 
00389 static Channel *current;
00390 static int next_index;
00391 
00392 Channel *firstchan(void)
00393 {
00394     next_index = 0;
00395     while (next_index < 1024 && current == NULL)
00396         current = chanlist[next_index++];
00397     if (debug >= 3)
00398         alog("debug: firstchan() returning %s",
00399              current ? current->name : "NULL (end of list)");
00400     return current;
00401 }
00402 
00403 Channel *nextchan(void)
00404 {
00405     if (current)
00406         current = current->next;
00407     if (!current && next_index < 1024) {
00408         while (next_index < 1024 && current == NULL)
00409             current = chanlist[next_index++];
00410     }
00411     if (debug >= 3)
00412         alog("debug: nextchan() returning %s",
00413              current ? current->name : "NULL (end of list)");
00414     return current;
00415 }
00416 
00417 /*************************************************************************/
00418 
00419 /* Return statistics.  Pointers are assumed to be valid. */
00420 
00421 void get_channel_stats(long *nrec, long *memuse)
00422 {
00423     long count = 0, mem = 0;
00424     Channel *chan;
00425     struct c_userlist *cu;
00426     BanData *bd;
00427     int i, j;
00428 
00429     for (i = 0; i < 1024; i++) {
00430         for (chan = chanlist[i]; chan; chan = chan->next) {
00431             count++;
00432             mem += sizeof(*chan);
00433             if (chan->topic)
00434                 mem += strlen(chan->topic) + 1;
00435             if (chan->key)
00436                 mem += strlen(chan->key) + 1;
00437             if (ircd->fmode) {
00438                 if (chan->flood)
00439                     mem += strlen(chan->flood) + 1;
00440             }
00441             if (ircd->Lmode) {
00442                 if (chan->redirect)
00443                     mem += strlen(chan->redirect) + 1;
00444             }
00445             mem += sizeof(char *) * chan->bansize;
00446             for (j = 0; j < chan->bancount; j++) {
00447                 if (chan->bans[j])
00448                     mem += strlen(chan->bans[j]) + 1;
00449             }
00450             if (ircd->except) {
00451                 mem += sizeof(char *) * chan->exceptsize;
00452                 for (j = 0; j < chan->exceptcount; j++) {
00453                     if (chan->excepts[j])
00454                         mem += strlen(chan->excepts[j]) + 1;
00455                 }
00456             }
00457             if (ircd->invitemode) {
00458                 mem += sizeof(char *) * chan->invitesize;
00459                 for (j = 0; j < chan->invitecount; j++) {
00460                     if (chan->invite[j])
00461                         mem += strlen(chan->invite[j]) + 1;
00462                 }
00463             }
00464             for (cu = chan->users; cu; cu = cu->next) {
00465                 mem += sizeof(*cu);
00466                 if (cu->ud) {
00467                     mem += sizeof(*cu->ud);
00468                     if (cu->ud->lastline)
00469                         mem += strlen(cu->ud->lastline) + 1;
00470                 }
00471             }
00472             for (bd = chan->bd; bd; bd = bd->next) {
00473                 if (bd->mask)
00474                     mem += strlen(bd->mask) + 1;
00475                 mem += sizeof(*bd);
00476             }
00477         }
00478     }
00479     *nrec = count;
00480     *memuse = mem;
00481 }
00482 
00483 /*************************************************************************/
00484 
00485 /* Is the given nick on the given channel? */
00486 
00487 int is_on_chan(Channel * c, User * u)
00488 {
00489     struct u_chanlist *uc;
00490 
00491     for (uc = u->chans; uc; uc = uc->next)
00492         if (uc->chan == c)
00493             return 1;
00494 
00495     return 0;
00496 }
00497 
00498 /*************************************************************************/
00499 
00500 /* Is the given nick on the given channel?
00501    This function supports links. */
00502 
00503 User *nc_on_chan(Channel * c, NickCore * nc)
00504 {
00505     struct c_userlist *u;
00506 
00507     if (!c || !nc)
00508         return NULL;
00509 
00510     for (u = c->users; u; u = u->next) {
00511         if (u->user->na && u->user->na->nc == nc
00512             && nick_recognized(u->user))
00513             return u->user;
00514     }
00515     return NULL;
00516 }
00517 
00518 /*************************************************************************/
00519 /*************************** Message Handling ****************************/
00520 /*************************************************************************/
00521 
00522 /* Handle a JOIN command.
00523  *  av[0] = channels to join
00524  */
00525 
00526 void do_join(const char *source, int ac, char **av)
00527 {
00528     User *user;
00529     Channel *chan;
00530     char *s, *t;
00531     struct u_chanlist *c, *nextc;
00532     char *channame;
00533 
00534     if (UseTS6 && ircd->ts6) {
00535         user = find_byuid(source);
00536         if (!user)
00537             user = finduser(source);
00538     } else {
00539         user = finduser(source);
00540     }
00541     if (!user) {
00542         if (debug) {
00543             alog("debug: JOIN from nonexistent user %s: %s", source,
00544                  merge_args(ac, av));
00545         }
00546         return;
00547     }
00548 
00549     t = av[0];
00550     while (*(s = t)) {
00551         t = s + strcspn(s, ",");
00552         if (*t)
00553             *t++ = 0;
00554 
00555         if (*s == '0') {
00556             c = user->chans;
00557             while (c) {
00558                 nextc = c->next;
00559                 channame = sstrdup(c->chan->name);
00560                 send_event(EVENT_PART_CHANNEL, 3, EVENT_START, user->nick,
00561                            channame);
00562                 chan_deluser(user, c->chan);
00563                 send_event(EVENT_PART_CHANNEL, 3, EVENT_STOP, user->nick,
00564                            channame);
00565                 free(channame);
00566                 free(c);
00567                 c = nextc;
00568             }
00569             user->chans = NULL;
00570             continue;
00571         }
00572 
00573         /* how about not triggering the JOIN event on an actual /part :) -certus */
00574         send_event(EVENT_JOIN_CHANNEL, 3, EVENT_START, source, s);
00575 
00576         /* Make sure check_kick comes before chan_adduser, so banned users
00577          * don't get to see things like channel keys. */
00578         /* If channel already exists, check_kick() will use correct TS.
00579          * Otherwise, we lose. */
00580         if (check_kick(user, s, time(NULL)))
00581             continue;
00582 
00583         chan = findchan(s);
00584         chan = join_user_update(user, chan, s, time(NULL));
00585         chan_set_correct_modes(user, chan, 1);
00586 
00587         send_event(EVENT_JOIN_CHANNEL, 3, EVENT_STOP, source, s);
00588     }
00589 }
00590 
00591 /*************************************************************************/
00592 
00593 /* Handle a KICK command.
00594  *  av[0] = channel
00595  *  av[1] = nick(s) being kicked
00596  *  av[2] = reason
00597  */
00598 
00599 void do_kick(const char *source, int ac, char **av)
00600 {
00601     BotInfo *bi;
00602     ChannelInfo *ci;
00603     User *user;
00604     char *s, *t;
00605     struct u_chanlist *c;
00606 
00607     t = av[1];
00608     while (*(s = t)) {
00609         t = s + strcspn(s, ",");
00610         if (*t)
00611             *t++ = 0;
00612 
00613         /* If it is the bot that is being kicked, we make it rejoin the
00614          * channel and stop immediately.
00615          *      --lara
00616          */
00617         if (s_BotServ && (bi = findbot(s)) && (ci = cs_findchan(av[0]))) {
00618             bot_join(ci);
00619             continue;
00620         }
00621 
00622         if (UseTS6 && ircd->ts6) {
00623             user = find_byuid(s);
00624             if (!user) {
00625                 user = finduser(s);
00626             }
00627         } else {
00628             user = finduser(s);
00629         }
00630         if (!user) {
00631             if (debug) {
00632                 alog("debug: KICK for nonexistent user %s on %s: %s", s,
00633                      av[0], merge_args(ac - 2, av + 2));
00634             }
00635             continue;
00636         }
00637         if (debug) {
00638             alog("debug: kicking %s from %s", user->nick, av[0]);
00639         }
00640         for (c = user->chans; c && stricmp(av[0], c->chan->name) != 0;
00641              c = c->next);
00642         if (c) {
00643             send_event(EVENT_CHAN_KICK, 2, user->nick, av[0]);
00644             chan_deluser(user, c->chan);
00645             if (c->next)
00646                 c->next->prev = c->prev;
00647             if (c->prev)
00648                 c->prev->next = c->next;
00649             else
00650                 user->chans = c->next;
00651             free(c);
00652         }
00653     }
00654 }
00655 
00656 /*************************************************************************/
00657 
00658 /* Handle a PART command.
00659  *  av[0] = channels to leave
00660  *  av[1] = reason (optional)
00661  */
00662 
00663 void do_part(const char *source, int ac, char **av)
00664 {
00665     User *user;
00666     char *s, *t;
00667     struct u_chanlist *c;
00668     char *channame;
00669 
00670     if (UseTS6 && ircd->ts6) {
00671         user = find_byuid(source);
00672         if (!user)
00673             user = finduser(source);
00674     } else {
00675         user = finduser(source);
00676     }
00677     if (!user) {
00678         if (debug) {
00679             alog("debug: PART from nonexistent user %s: %s", source,
00680                  merge_args(ac, av));
00681         }
00682         return;
00683     }
00684     t = av[0];
00685     while (*(s = t)) {
00686         t = s + strcspn(s, ",");
00687         if (*t)
00688             *t++ = 0;
00689         if (debug)
00690             alog("debug: %s leaves %s", source, s);
00691         for (c = user->chans; c && stricmp(s, c->chan->name) != 0;
00692              c = c->next);
00693         if (c) {
00694             if (!c->chan) {
00695                 alog("user: BUG parting %s: channel entry found but c->chan NULL", s);
00696                 return;
00697             }
00698             channame = sstrdup(c->chan->name);
00699             send_event(EVENT_PART_CHANNEL, (ac >= 2 ? 4 : 3), EVENT_START,
00700                        user->nick, 
00701 channame, (ac >= 2 ? av[1] : ""));
00702 
00703             chan_deluser(user, c->chan);
00704             if (c->next)
00705                 c->next->prev = c->prev;
00706             if (c->prev)
00707                 c->prev->next = c->next;
00708             else
00709                 user->chans = c->next;
00710             free(c);
00711 
00712             send_event(EVENT_PART_CHANNEL, (ac >= 2 ? 4 : 3), EVENT_STOP,
00713                        user->nick, 
00714 channame, (ac >= 2 ? av[1] : ""));
00715             free(channame);
00716         }
00717     }
00718 }
00719 
00720 /*************************************************************************/
00721 
00722 /* Handle a SJOIN command.
00723 
00724    On channel creation, syntax is:
00725 
00726    av[0] = timestamp
00727    av[1] = channel name
00728    av[2|3|4] = modes   \   depends of whether the modes k and l
00729    av[3|4|5] = users   /   are set or not.
00730 
00731    When a single user joins an (existing) channel, it is:
00732 
00733    av[0] = timestamp
00734    av[1] = user
00735 
00736    ============================================================
00737 
00738    Unreal SJOIN
00739 
00740    On Services connect there is
00741    SJOIN !11LkOb #ircops +nt :@Trystan &*!*@*.aol.com "*@*.home.com
00742 
00743    av[0] = time stamp (base64)
00744    av[1] = channel
00745    av[2] = modes
00746    av[3] = users + bans + exceptions
00747 
00748    On Channel Creation or a User joins an existing
00749    Luna.NomadIrc.Net SJOIN !11LkW9 #akill :@Trystan
00750    Luna.NomadIrc.Net SJOIN !11LkW9 #akill :Trystan`
00751 
00752    av[0] = time stamp (base64)
00753    av[1] = channel
00754    av[2] = users
00755 
00756 */
00757 
00758 void do_sjoin(const char *source, int ac, char **av)
00759 {
00760     Channel *c;
00761     User *user;
00762     Server *serv;
00763     struct c_userlist *cu;
00764     char *s = NULL;
00765     char *end, cubuf[7], *end2, *cumodes[6];
00766     int is_sqlined = 0;
00767     int ts = 0;
00768     int is_created = 0;
00769     int keep_their_modes = 1;
00770 
00771     serv = findserver(servlist, source);
00772 
00773     if (ircd->sjb64) {
00774         ts = base64dects(av[0]);
00775     } else {
00776         ts = strtoul(av[0], NULL, 10);
00777     }
00778     c = findchan(av[1]);
00779     if (c != NULL) {
00780         if (c->creation_time == 0 || ts == 0)
00781             c->creation_time = 0;
00782         else if (c->creation_time > ts) {
00783             c->creation_time = ts;
00784             for (cu = c->users; cu; cu = cu->next) {
00785                 /* XXX */
00786                 cumodes[0] = "-ov";
00787                 cumodes[1] = user->nick;
00788                 cumodes[2] = user->nick;
00789                 chan_set_modes(source, c, 3, cumodes, 2);
00790             }
00791             if (c->ci && c->ci->bi) {
00792                 /* This is ugly, but it always works */
00793                 anope_cmd_part(c->ci->bi->nick, c->name, "TS reop");
00794                 bot_join(c->ci);
00795             }
00796             /* XXX simple modes and bans */
00797         } else if (c->creation_time < ts)
00798             keep_their_modes = 0;
00799     } else
00800         is_created = 1;
00801 
00802     /* Double check to avoid unknown modes that need parameters */
00803     if (ac >= 4) {
00804         if (ircd->chansqline) {
00805             if (!c)
00806                 is_sqlined = check_chan_sqline(av[1]);
00807         }
00808 
00809         cubuf[0] = '+';
00810         cumodes[0] = cubuf;
00811 
00812         /* We make all the users join */
00813         s = av[ac - 1];         /* Users are always the last element */
00814 
00815         while (*s) {
00816             end = strchr(s, ' ');
00817             if (end)
00818                 *end = 0;
00819 
00820             end2 = cubuf + 1;
00821 
00822 
00823             if (ircd->sjoinbanchar) {
00824                 if (*s == ircd->sjoinbanchar && keep_their_modes) {
00825                     add_ban(c, myStrGetToken(s, ircd->sjoinbanchar, 1));
00826                     if (!end)
00827                         break;
00828                     s = end + 1;
00829                     continue;
00830                 }
00831             }
00832             if (ircd->sjoinexchar) {
00833                 if (*s == ircd->sjoinexchar && keep_their_modes) {
00834                     add_exception(c,
00835                                   myStrGetToken(s, ircd->sjoinexchar, 1));
00836                     if (!end)
00837                         break;
00838                     s = end + 1;
00839                     continue;
00840                 }
00841             }
00842 
00843             if (ircd->sjoininvchar) {
00844                 if (*s == ircd->sjoininvchar && keep_their_modes) {
00845                     add_invite(c, myStrGetToken(s, ircd->sjoininvchar, 1));
00846                     if (!end)
00847                         break;
00848                     s = end + 1;
00849                     continue;
00850                 }
00851             }
00852 
00853             while (csmodes[(int) *s] != 0)
00854                 *end2++ = csmodes[(int) *s++];
00855             *end2 = 0;
00856 
00857 
00858             if (UseTS6 && ircd->ts6) {
00859                 user = find_byuid(s);
00860                 if (!user)
00861                     user = finduser(s);
00862             } else {
00863                 user = finduser(s);
00864             }
00865 
00866             if (!user) {
00867                 if (debug) {
00868                     alog("debug: SJOIN for nonexistent user %s on %s", s,
00869                          av[1]);
00870                 }
00871                 return;
00872             }
00873 
00874             if (is_sqlined && !is_oper(user)) {
00875                 anope_cmd_kick(s_OperServ, av[1], s, "Q-Lined");
00876             } else {
00877                 if (!check_kick(user, av[1], ts)) {
00878                     send_event(EVENT_JOIN_CHANNEL, 3, EVENT_START,
00879                                user->nick, av[1]);
00880 
00881                     /* Make the user join; if the channel does not exist it
00882                      * will be created there. This ensures that the channel
00883                      * is not created to be immediately destroyed, and
00884                      * that the locked key or topic is not shown to anyone
00885                      * who joins the channel when empty.
00886                      */
00887                     c = join_user_update(user, c, av[1], ts);
00888 
00889                     /* We update user mode on the channel */
00890                     if (end2 - cubuf > 1 && keep_their_modes) {
00891                         int i;
00892 
00893                         for (i = 1; i < end2 - cubuf; i++)
00894                             cumodes[i] = user->nick;
00895                         chan_set_modes(source, c, 1 + (end2 - cubuf - 1),
00896                                        cumodes, 2);
00897                     }
00898 
00899                     if (c->ci && (!serv || is_sync(serv))
00900                         && !c->topic_sync)
00901                         restore_topic(c->name);
00902                     chan_set_correct_modes(user, c, 1);
00903 
00904                     send_event(EVENT_JOIN_CHANNEL, 3, EVENT_STOP,
00905                                user->nick, av[1]);
00906                 }
00907             }
00908 
00909             if (!end)
00910                 break;
00911             s = end + 1;
00912         }
00913 
00914         if (c && keep_their_modes) {
00915             /* We now update the channel mode. */
00916             chan_set_modes(source, c, ac - 3, &av[2], 2);
00917         }
00918 
00919         /* Unreal just had to be different */
00920     } else if (ac == 3 && !ircd->ts6) {
00921         if (ircd->chansqline) {
00922             if (!c)
00923                 is_sqlined = check_chan_sqline(av[1]);
00924         }
00925 
00926         cubuf[0] = '+';
00927         cumodes[0] = cubuf;
00928 
00929         /* We make all the users join */
00930         s = av[2];              /* Users are always the last element */
00931 
00932         while (*s) {
00933             end = strchr(s, ' ');
00934             if (end)
00935                 *end = 0;
00936 
00937             end2 = cubuf + 1;
00938 
00939             while (csmodes[(int) *s] != 0)
00940                 *end2++ = csmodes[(int) *s++];
00941             *end2 = 0;
00942 
00943             if (UseTS6 && ircd->ts6) {
00944                 user = find_byuid(s);
00945                 if (!user)
00946                     user = finduser(s);
00947             } else {
00948                 user = finduser(s);
00949             }
00950 
00951             if (!user) {
00952                 if (debug) {
00953                     alog("debug: SJOIN for nonexistent user %s on %s", s,
00954                          av[1]);
00955                 }
00956                 return;
00957             }
00958 
00959             if (is_sqlined && !is_oper(user)) {
00960                 anope_cmd_kick(s_OperServ, av[1], s, "Q-Lined");
00961             } else {
00962                 if (!check_kick(user, av[1], ts)) {
00963                     send_event(EVENT_JOIN_CHANNEL, 3, EVENT_START,
00964                                user->nick, av[1]);
00965 
00966                     /* Make the user join; if the channel does not exist it
00967                      * will be created there. This ensures that the channel
00968                      * is not created to be immediately destroyed, and
00969                      * that the locked key or topic is not shown to anyone
00970                      * who joins the channel when empty.
00971                      */
00972                     c = join_user_update(user, c, av[1], ts);
00973 
00974                     /* We update user mode on the channel */
00975                     if (end2 - cubuf > 1 && keep_their_modes) {
00976                         int i;
00977 
00978                         for (i = 1; i < end2 - cubuf; i++)
00979                             cumodes[i] = user->nick;
00980                         chan_set_modes(source, c, 1 + (end2 - cubuf - 1),
00981                                        cumodes, 2);
00982                     }
00983 
00984                     chan_set_correct_modes(user, c, 1);
00985 
00986                     send_event(EVENT_JOIN_CHANNEL, 3, EVENT_STOP,
00987                                user->nick, av[1]);
00988                 }
00989             }
00990 
00991             if (!end)
00992                 break;
00993             s = end + 1;
00994         }
00995     } else if (ac == 3 && ircd->ts6) {
00996         if (ircd->chansqline) {
00997             if (!c)
00998                 is_sqlined = check_chan_sqline(av[1]);
00999         }
01000 
01001         cubuf[0] = '+';
01002         cumodes[0] = cubuf;
01003 
01004         /* We make all the users join */
01005         s = sstrdup(source);    /* Users are always the last element */
01006 
01007         while (*s) {
01008             end = strchr(s, ' ');
01009             if (end)
01010                 *end = 0;
01011 
01012             end2 = cubuf + 1;
01013 
01014             while (csmodes[(int) *s] != 0)
01015                 *end2++ = csmodes[(int) *s++];
01016             *end2 = 0;
01017 
01018             if (UseTS6 && ircd->ts6) {
01019                 user = find_byuid(s);
01020                 if (!user)
01021                     user = finduser(s);
01022             } else {
01023                 user = finduser(s);
01024             }
01025             if (!user) {
01026                 if (debug) {
01027                     alog("debug: SJOIN for nonexistent user %s on %s", s,
01028                          av[1]);
01029                 }
01030                 free(s);
01031                 return;
01032             }
01033 
01034             if (is_sqlined && !is_oper(user)) {
01035                 anope_cmd_kick(s_OperServ, av[1], s, "Q-Lined");
01036             } else {
01037                 if (!check_kick(user, av[1], ts)) {
01038                     send_event(EVENT_JOIN_CHANNEL, 3, EVENT_START,
01039                                user->nick, av[1]);
01040 
01041                     /* Make the user join; if the channel does not exist it
01042                      * will be created there. This ensures that the channel
01043                      * is not created to be immediately destroyed, and
01044                      * that the locked key or topic is not shown to anyone
01045                      * who joins the channel when empty.
01046                      */
01047                     c = join_user_update(user, c, av[1], ts);
01048 
01049                     /* We update user mode on the channel */
01050                     if (end2 - cubuf > 1 && keep_their_modes) {
01051                         int i;
01052 
01053                         for (i = 1; i < end2 - cubuf; i++)
01054                             cumodes[i] = user->nick;
01055                         chan_set_modes(source, c, 1 + (end2 - cubuf - 1),
01056                                        cumodes, 2);
01057                     }
01058 
01059                     chan_set_correct_modes(user, c, 1);
01060 
01061                     send_event(EVENT_JOIN_CHANNEL, 3, EVENT_STOP,
01062                                user->nick, av[1]);
01063                 }
01064             }
01065 
01066             if (!end)
01067                 break;
01068             s = end + 1;
01069         }
01070         free(s);
01071     } else if (ac == 2) {
01072         if (UseTS6 && ircd->ts6) {
01073             user = find_byuid(source);
01074             if (!user)
01075                 user = finduser(s);
01076         } else {
01077             user = finduser(source);
01078         }
01079         if (!user) {
01080             if (debug) {
01081                 alog("debug: SJOIN for nonexistent user %s on %s", source,
01082                      av[1]);
01083             }
01084             return;
01085         }
01086 
01087         if (check_kick(user, av[1], ts))
01088             return;
01089 
01090         if (ircd->chansqline) {
01091             if (!c)
01092                 is_sqlined = check_chan_sqline(av[1]);
01093         }
01094 
01095         if (is_sqlined && !is_oper(user)) {
01096             anope_cmd_kick(s_OperServ, av[1], user->nick, "Q-Lined");
01097         } else {
01098             send_event(EVENT_JOIN_CHANNEL, 3, EVENT_START, user->nick,
01099                        av[1]);
01100 
01101             c = join_user_update(user, c, av[1], ts);
01102             if (is_created && c->ci)
01103                 restore_topic(c->name);
01104             chan_set_correct_modes(user, c, 1);
01105 
01106             send_event(EVENT_JOIN_CHANNEL, 3, EVENT_STOP, user->nick,
01107                        av[1]);
01108         }
01109     }
01110 }
01111 
01112 
01113 /*************************************************************************/
01114 
01115 /* Handle a channel MODE command. */
01116 
01117 void do_cmode(const char *source, int ac, char **av)
01118 {
01119     Channel *chan;
01120     ChannelInfo *ci = NULL;
01121     int i;
01122     char *t;
01123 
01124     if (ircdcap->tsmode) {
01125         /* TSMODE for bahamut - leave this code out to break MODEs. -GD */
01126         /* if they don't send it in CAPAB check if we just want to enable it */
01127         if (uplink_capab & ircdcap->tsmode || UseTSMODE) {
01128             for (i = 0; i < strlen(av[1]); i++) {
01129                 if (!isdigit(av[1][i]))
01130                     break;
01131             }
01132             if (av[1][i] == '\0') {
01133                 /* We have a valid TS field in av[1] now, so we can strip it off */
01134                 /* After we swap av[0] and av[1] ofcourse to not break stuff! :) */
01135                 t = av[0];
01136                 av[0] = av[1];
01137                 av[1] = t;
01138                 ac--;
01139                 av++;
01140             } else {
01141                 alog("TSMODE enabled but MODE has no valid TS");
01142             }
01143         }
01144     }
01145 
01146     /* :42XAAAAAO TMODE 1106409026 #ircops +b *!*@*.aol.com */
01147     if (UseTS6 && ircd->ts6) {
01148         if (isdigit(av[0][0])) {
01149             ac--;
01150             av++;
01151         }
01152     }
01153 
01154     chan = findchan(av[0]);
01155     if (!chan) {
01156         ci = cs_findchan(av[0]);
01157         if (!(ci && (ci->flags & CI_VERBOTEN)))
01158             if (debug) {
01159                 alog("debug: MODE %s for nonexistent channel %s",
01160                      merge_args(ac - 1, av + 1), av[0]);
01161             }
01162         return;
01163     }
01164 
01165     /* This shouldn't trigger on +o, etc. */
01166     if (strchr(source, '.') && !av[1][strcspn(av[1], "bovahq")]) {
01167         if (time(NULL) != chan->server_modetime) {
01168             chan->server_modecount = 0;
01169             chan->server_modetime = time(NULL);
01170         }
01171         chan->server_modecount++;
01172     }
01173 
01174     ac--;
01175     av++;
01176