channels.c

Go to the documentation of this file.
00001 /* Channel-handling routines.
00002  *
00003  * (C) 2003-2013 Anope Team
00004  * Contact us at team@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  *
00012  */
00013 
00014 #include "services.h"
00015 #include "language.h"
00016 
00017 Channel *chanlist[1024];
00018 
00019 #define HASH(chan)      ((chan)[1] ? ((chan)[1]&31)<<5 | ((chan)[2]&31) : 0)
00020 
00021 /**************************** External Calls *****************************/
00022 /*************************************************************************/
00023 
00024 void chan_deluser(User * user, Channel * c)
00025 {
00026     struct c_userlist *u;
00027 
00028     if (c->ci)
00029         update_cs_lastseen(user, c->ci);
00030 
00031     for (u = c->users; u && u->user != user; u = u->next);
00032     if (!u)
00033         return;
00034 
00035     if (u->ud) {
00036         if (u->ud->lastline)
00037             free(u->ud->lastline);
00038         free(u->ud);
00039     }
00040 
00041     if (u->next)
00042         u->next->prev = u->prev;
00043     if (u->prev)
00044         u->prev->next = u->next;
00045     else
00046         c->users = u->next;
00047     free(u);
00048     c->usercount--;
00049 
00050     if (s_BotServ && c->ci && c->ci->bi && c->usercount == BSMinUsers - 1) {
00051         anope_cmd_part(c->ci->bi->nick, c->name, NULL);
00052     }
00053 
00054     /* Channel is permament and shouldn't be deleted */
00055     if (anope_get_permchan_mode() && c->mode & anope_get_permchan_mode())
00056         return;
00057 
00058     if (!c->users)
00059         chan_delete(c);
00060 }
00061 
00062 /*************************************************************************/
00063 
00064 /* Returns a fully featured binary modes string. If complete is 0, the
00065  * eventual parameters won't be added to the string.
00066  */
00067 
00068 char *chan_get_modes(Channel * chan, int complete, int plus)
00069 {
00070     static char res[BUFSIZE];
00071     char *end = res;
00072 
00073     if (chan->mode) {
00074         int n = 0;
00075         CBModeInfo *cbmi = cbmodeinfos;
00076 
00077         do {
00078             if (chan->mode & cbmi->flag)
00079                 *end++ = cbmi->mode;
00080         } while ((++cbmi)->mode != 0 && ++n < sizeof(res) - 1);
00081 
00082         if (complete) {
00083             cbmi = cbmodeinfos;
00084 
00085             do {
00086                 if (cbmi->getvalue && (chan->mode & cbmi->flag) &&
00087                     (plus || !(cbmi->flags & CBM_MINUS_NO_ARG))) {
00088                     char *value = cbmi->getvalue(chan);
00089 
00090                     if (value) {
00091                         *end++ = ' ';
00092                         while (*value)
00093                             *end++ = *value++;
00094                     }
00095                 }
00096             } while ((++cbmi)->mode != 0 && ++n < sizeof(res) - 1);
00097         }
00098     }
00099 
00100     *end = 0;
00101 
00102     return res;
00103 }
00104 
00105 /*************************************************************************/
00106 
00107 /* Retrieves the status of an user on a channel */
00108 
00109 int chan_get_user_status(Channel * chan, User * user)
00110 {
00111     struct u_chanlist *uc;
00112 
00113     for (uc = user->chans; uc; uc = uc->next)
00114         if (uc->chan == chan)
00115             return uc->status;
00116 
00117     return 0;
00118 }
00119 
00120 /*************************************************************************/
00121 
00122 /* Has the given user the given status on the given channel? :p */
00123 
00124 int chan_has_user_status(Channel * chan, User * user, int16 status)
00125 {
00126     struct u_chanlist *uc;
00127 
00128     for (uc = user->chans; uc; uc = uc->next) {
00129         if (uc->chan == chan) {
00130             if (debug) {
00131                 alog("debug: chan_has_user_status wanted %d the user is %d", status, uc->status);
00132             }
00133             return (uc->status & status);
00134         }
00135     }
00136     return 0;
00137 }
00138 
00139 /*************************************************************************/
00140 
00141 /* Remove the status of an user on a channel */
00142 
00143 void chan_remove_user_status(Channel * chan, User * user, int16 status)
00144 {
00145     struct u_chanlist *uc;
00146 
00147     if (debug >= 2)
00148         alog("debug: removing user status (%d) from %s for %s", status,
00149              user->nick, chan->name);
00150 
00151     for (uc = user->chans; uc; uc = uc->next) {
00152         if (uc->chan == chan) {
00153             uc->status &= ~status;
00154             break;
00155         }
00156     }
00157 }
00158 
00159 /*************************************************************************/
00160 
00161 void chan_set_modes(const char *source, Channel * chan, int ac, char **av,
00162                     int check)
00163 {
00164     int add = 1;
00165     char *modes = av[0], mode;
00166     CBMode *cbm;
00167     CMMode *cmm;
00168     CUMode *cum;
00169     unsigned char botmode = 0;
00170     BotInfo *bi;
00171     User *u, *user;
00172     int i, real_ac = ac;
00173     char **real_av = av;
00174 
00175     if (debug)
00176         alog("debug: Changing modes for %s to %s", chan->name,
00177              merge_args(ac, av));
00178 
00179     if (UseTS6 && ircd->ts6) { 
00180         u = find_byuid(source);
00181         if (!u) u = finduser(source);
00182     } else
00183         u = finduser(source);
00184 
00185     if (u && (chan_get_user_status(chan, u) & CUS_DEOPPED)) {
00186         char *s;
00187 
00188         if (debug)
00189             alog("debug: Removing instead of setting due to DEOPPED flag");
00190 
00191         /* Swap adding and removing of the modes */
00192         for (s = av[0]; *s; s++) {
00193             if (*s == '+')
00194                 *s = '-';
00195             else if (*s == '-')
00196                 *s = '+';
00197         }
00198 
00199         /* Set the resulting mode buffer */
00200         anope_cmd_mode(whosends(chan->ci), chan->name, merge_args(ac, av));
00201 
00202         return;
00203     }
00204 
00205     ac--;
00206 
00207     while ((mode = *modes++)) {
00208 
00209         switch (mode) {
00210         case '+':
00211             add = 1;
00212             continue;
00213         case '-':
00214             add = 0;
00215             continue;
00216         }
00217 
00218         if (((int) mode) < 0) {
00219             if (debug)
00220                 alog("Debug: Malformed mode detected on %s.", chan->name);
00221             continue;
00222         }
00223 
00224         if ((cum = &cumodes[(int) mode])->status != 0) {
00225             if (ac == 0) {
00226                 alog("channel: mode %c%c with no parameter (?) for channel %s", add ? '+' : '-', mode, chan->name);
00227                 continue;
00228             }
00229             ac--;
00230             av++;
00231 
00232             if ((cum->flags & CUF_PROTECT_BOTSERV) && !add) {
00233                 if ((bi = findbot(*av))) {
00234                     if (!botmode || botmode != mode) {
00235                         anope_cmd_mode(bi->nick, chan->name, "+%c %s",
00236                                        mode, bi->nick);
00237                         botmode = mode;
00238                         continue;
00239                     } else {
00240                         botmode = mode;
00241                         continue;
00242                     }
00243                 }
00244             } else {
00245                 if ((bi = findbot(*av))) {
00246                     continue;
00247                 }
00248             }
00249 
00250             if (UseTS6 && ircd->ts6) { 
00251                 user = find_byuid(*av);
00252                 if (!user) user = finduser(*av);
00253             } else
00254                 user = finduser(*av);
00255             if (!user) {
00256                 if (debug)
00257                     alog("debug: MODE %s %c%c for nonexistent user %s",
00258                             chan->name, (add ? '+' : '-'), mode, *av);
00259                 continue;
00260             }
00261 
00262             if (debug)
00263                 alog("debug: Setting %c%c on %s for %s", (add ? '+' : '-'),
00264                      mode, chan->name, user->nick);
00265 
00266             if (add) {
00267                 chan_set_user_status(chan, user, cum->status);
00268                 /* If this does +o or +h, remove any DEOPPED flag */
00269                 if (cum->status & CUS_OP || cum->status & CUS_HALFOP)
00270                             chan_remove_user_status(chan, user, CUS_DEOPPED);
00271             } else {
00272                 chan_remove_user_status(chan, user, cum->status);
00273             }
00274 
00275         } else if ((cbm = &cbmodes[(int) mode])->flag != 0) {
00276             if (check >= 0) {
00277                 if (add)
00278                     chan->mode |= cbm->flag;
00279                 else
00280                     chan->mode &= ~cbm->flag;
00281             }
00282 
00283             if (cbm->setvalue) {
00284                 if (add || !(cbm->flags & CBM_MINUS_NO_ARG)) {
00285                     if (ac == 0) {
00286                         alog("channel: mode %c%c with no parameter (?) for channel %s", add ? '+' : '-', mode, chan->name);
00287                         continue;
00288                     }
00289                     ac--;
00290                     av++;
00291                 }
00292                 cbm->setvalue(chan, add ? *av : NULL);
00293             }
00294 
00295             if (check < 0) {
00296                 if (add)
00297                     chan->mode |= cbm->flag;
00298                 else
00299                     chan->mode &= ~cbm->flag;
00300             }
00301         } else if ((cmm = &cmmodes[(int) mode])->addmask) {
00302             if (ac == 0) {
00303                 alog("channel: mode %c%c with no parameter (?) for channel %s", add ? '+' : '-', mode, chan->name);
00304                 continue;
00305             }
00306 
00307             ac--;
00308             av++;
00309             if (add)
00310                 cmm->addmask(chan, *av);
00311             else
00312                 cmm->delmask(chan, *av);
00313         }
00314     }
00315 
00316     /* Don't bounce modes from u:lined clients or servers, bug #1004. *
00317      * We can get UUIDs as well.. don not assume nick ~ Viper */
00318     if (UseTS6 && ircd->ts6) {
00319         user = find_byuid(source);
00320         if (!user) user = finduser(source);
00321     } else
00322         user = finduser(source);
00323 
00324     if ((user && is_ulined(user->server->name)) || is_ulined((char *)source))
00325         return;
00326 
00327     if (check > 0) {
00328         check_modes(chan);
00329 
00330         if ((check < 2) || (check == 3)) {
00331             /* Walk through all users we've set modes for and see if they are
00332              * valid. Invalid modes (like +o with SECUREOPS on) will be removed
00333              */
00334             real_ac--;
00335             real_av++;
00336             for (i = 0; i < real_ac; i++) {
00337                 if (UseTS6 && ircd->ts6) {
00338                     user = find_byuid(*real_av);
00339                     if (!user) user = finduser(*real_av);
00340                 } else
00341                     user = finduser(*real_av);
00342 
00343                 if (!user && UseTS6 && ircd->ts6) user = find_byuid(*real_av);
00344                 if (user && is_on_chan(chan, user)) {
00345                     if (check < 2)
00346                         chan_set_correct_modes(user, chan, 0);
00347                     else if ((chan->ci->flags) && (chan->ci->flags & CI_SECUREOPS))
00348                         chan_set_correct_modes(user, chan, 0);
00349                 }
00350                 real_av++;
00351             }
00352         }
00353     }
00354 }
00355 
00356 /*************************************************************************/
00357 
00358 /* Set the status of an user on a channel */
00359 
00360 void chan_set_user_status(Channel * chan, User * user, int16 status)
00361 {
00362     struct u_chanlist *uc;
00363 
00364     if (debug >= 2)
00365         alog("debug: setting user status (%d) on %s for %s", status,
00366              user->nick, chan->name);
00367 
00368     if (HelpChannel && ircd->supporthelper && (status & CUS_OP)
00369         && (stricmp(chan->name, HelpChannel) == 0)
00370         && (!chan->ci || check_access(user, chan->ci, CA_AUTOOP))) {
00371         if (debug) {
00372             alog("debug: %s being given +h for having %d status in %s",
00373                  user->nick, status, chan->name);
00374         }
00375         common_svsmode(user, "+h", NULL);
00376     }
00377 
00378     for (uc = user->chans; uc; uc = uc->next) {
00379         if (uc->chan == chan) {
00380             uc->status |= status;
00381             break;
00382         }
00383     }
00384 }
00385 
00386 /*************************************************************************/
00387 
00388 /* Return the Channel structure corresponding to the named channel, or NULL
00389  * if the channel was not found.  chan is assumed to be non-NULL and valid
00390  * (i.e. pointing to a channel name of 2 or more characters). */
00391 
00392 Channel *findchan(const char *chan)
00393 {
00394     Channel *c;
00395 
00396     if (!chan || !*chan) {
00397         if (debug) {
00398             alog("debug: findchan() called with NULL values");
00399         }
00400         return NULL;
00401     }
00402 
00403     if (debug >= 3)
00404         alog("debug: findchan(%p)", chan);
00405     c = chanlist[HASH(chan)];
00406     while (c) {
00407         if (stricmp(c->name, chan) == 0) {
00408             if (debug >= 3)
00409                 alog("debug: findchan(%s) -> %p", chan, (void *) c);
00410             return c;
00411         }
00412         c = c->next;
00413     }
00414     if (debug >= 3)
00415         alog("debug: findchan(%s) -> %p", chan, (void *) c);
00416     return NULL;
00417 }
00418 
00419 /*************************************************************************/
00420 
00421 /* Iterate over all channels in the channel list.  Return NULL at end of
00422  * list.
00423  */
00424 
00425 static Channel *current;
00426 static int next_index;
00427 
00428 Channel *firstchan(void)
00429 {
00430     next_index = 0;
00431     while (next_index < 1024 && current == NULL)
00432         current = chanlist[next_index++];
00433     if (debug >= 3)
00434         alog("debug: firstchan() returning %s",
00435              current ? current->name : "NULL (end of list)");
00436     return current;
00437 }
00438 
00439 Channel *nextchan(void)
00440 {
00441     if (current)
00442         current = current->next;
00443     if (!current && next_index < 1024) {
00444         while (next_index < 1024 && current == NULL)
00445             current = chanlist[next_index++];
00446     }
00447     if (debug >= 3)
00448         alog("debug: nextchan() returning %s",
00449              current ? current->name : "NULL (end of list)");
00450     return current;
00451 }
00452 
00453 /*************************************************************************/
00454 
00455 /* Return statistics.  Pointers are assumed to be valid. */
00456 
00457 void get_channel_stats(long *nrec, long *memuse)
00458 {
00459     long count = 0, mem = 0;
00460     Channel *chan;
00461     struct c_userlist *cu;
00462     BanData *bd;
00463     int i;
00464 
00465     for (i = 0; i < 1024; i++) {
00466         for (chan = chanlist[i]; chan; chan = chan->next) {
00467             count++;
00468             mem += sizeof(*chan);
00469             if (chan->topic)
00470                 mem += strlen(chan->topic) + 1;
00471             if (chan->key)
00472                 mem += strlen(chan->key) + 1;
00473             if (ircd->fmode) {
00474                 if (chan->flood)
00475                     mem += strlen(chan->flood) + 1;
00476             }
00477             if (ircd->Lmode) {
00478                 if (chan->redirect)
00479                     mem += strlen(chan->redirect) + 1;
00480             }
00481             if (ircd->jmode) {
00482                 if (chan->throttle)
00483                     mem += strlen(chan->throttle) + 1;
00484             }
00485             mem += get_memuse(chan->bans);
00486             if (ircd->except) {
00487                 mem += get_memuse(chan->excepts);
00488             }
00489             if (ircd->invitemode) {
00490                 mem += get_memuse(chan->invites);
00491             }
00492             for (cu = chan->users; cu; cu = cu->next) {
00493                 mem += sizeof(*cu);
00494                 if (cu->ud) {
00495                     mem += sizeof(*cu->ud);
00496                     if (cu->ud->lastline)
00497                         mem += strlen(cu->ud->lastline) + 1;
00498                 }
00499             }
00500             for (bd = chan->bd; bd; bd = bd->next) {
00501                 if (bd->mask)
00502                     mem += strlen(bd->mask) + 1;
00503                 mem += sizeof(*bd);
00504             }
00505         }
00506     }
00507     *nrec = count;
00508     *memuse = mem;
00509 }
00510 
00511 /*************************************************************************/
00512 
00513 /* Is the given nick on the given channel? */
00514 
00515 int is_on_chan(Channel * c, User * u)
00516 {
00517     struct u_chanlist *uc;
00518 
00519     for (uc = u->chans; uc; uc = uc->next)
00520         if (uc->chan == c)
00521             return 1;
00522 
00523     return 0;
00524 }
00525 
00526 /*************************************************************************/
00527 
00528 /* Is the given nick on the given channel?
00529    This function supports links. */
00530 
00531 User *nc_on_chan(Channel * c, NickCore * nc)
00532 {
00533     struct c_userlist *u;
00534 
00535     if (!c || !nc)
00536         return NULL;
00537 
00538     for (u = c->users; u; u = u->next) {
00539         if (u->user->na && u->user->na->nc == nc
00540             && nick_recognized(u->user))
00541             return u->user;
00542     }
00543     return NULL;
00544 }
00545 
00546 /*************************************************************************/
00547 /*************************** Message Handling ****************************/
00548 /*************************************************************************/
00549 
00550 /* Handle a JOIN command.
00551  *      av[0] = channels to join
00552  */
00553 
00554 void do_join(const char *source, int ac, char **av)
00555 {
00556     User *user;
00557     Channel *chan;
00558     char *s, *t;
00559     struct u_chanlist *c, *nextc;
00560     char *channame;
00561     time_t ts = time(NULL);
00562 
00563     if (UseTS6 && ircd->ts6) {
00564         user = find_byuid(source);
00565         if (!user)
00566             user = finduser(source);
00567     } else {
00568         user = finduser(source);
00569     }
00570     if (!user) {
00571         if (debug) {
00572             alog("debug: JOIN from nonexistent user %s: %s", source,
00573                  merge_args(ac, av));
00574         }
00575         return;
00576     }
00577 
00578     t = av[0];
00579     while (*(s = t)) {
00580         t = s + strcspn(s, ",");
00581         if (*t)
00582             *t++ = 0;
00583 
00584         if (*s == '0') {
00585             c = user->chans;
00586             while (c) {
00587                 nextc = c->next;
00588                 channame = sstrdup(c->chan->name);
00589                 send_event(EVENT_PART_CHANNEL, 3, EVENT_START, user->nick,
00590                            channame);
00591                 chan_deluser(user, c->chan);
00592                 send_event(EVENT_PART_CHANNEL, 3, EVENT_STOP, user->nick,
00593                            channame);
00594                 free(channame);
00595                 free(c);
00596                 c = nextc;
00597             }
00598             user->chans = NULL;
00599             continue;
00600         }
00601 
00602         /* how about not triggering the JOIN event on an actual /part :) -certus */
00603         send_event(EVENT_JOIN_CHANNEL, 3, EVENT_START, source, s);
00604 
00605         /* Make sure check_kick comes before chan_adduser, so banned users
00606          * don't get to see things like channel keys. */
00607         /* If channel already exists, check_kick() will use correct TS.
00608          * Otherwise, we lose. */
00609         if (check_kick(user, s, ts))
00610             continue;
00611 
00612         if (ac == 2) {
00613             ts = strtoul(av[1], NULL, 10);
00614             if (debug) {
00615                 alog("debug: recieved a new TS for JOIN: %ld",
00616                      (long int) ts);
00617             }
00618         }
00619 
00620         chan = findchan(s);
00621         chan = join_user_update(user, chan, s, ts);
00622         chan_set_correct_modes(user, chan, 1);
00623 
00624         send_event(EVENT_JOIN_CHANNEL, 3, EVENT_STOP, source, s);
00625     }
00626 }
00627 
00628 /*************************************************************************/
00629 
00630 /* Handle a KICK command.
00631  *      av[0] = channel
00632  *      av[1] = nick(s) being kicked
00633  *      av[2] = reason
00634  */
00635 
00636 void do_kick(const char *source, int ac, char **av)
00637 {
00638     BotInfo *bi;
00639     ChannelInfo *ci;
00640     User *user;
00641     char *s, *t;
00642     struct u_chanlist *c;
00643     Uid *uid;
00644 
00645     t = av[1];
00646     while (*(s = t)) {
00647         t = s + strcspn(s, ",");
00648         if (*t)
00649             *t++ = 0;
00650 
00651         if (ircd->ts6 && UseTS6)
00652         {
00653                 uid = find_nickuid(s);
00654                 if (uid)
00655                         s = uid->nick;
00656         }
00657 
00658         /* If it is the bot that is being kicked, we make it rejoin the
00659          * channel and stop immediately.
00660          *      --lara
00661          */
00662         if (s_BotServ && (bi = findbot(s)) && (ci = cs_findchan(av[0]))) {
00663             bot_join(ci);
00664             continue;
00665         }
00666 
00667         if (UseTS6 && ircd->ts6) {
00668             user = find_byuid(s);
00669             if (!user) {
00670                 user = finduser(s);
00671             }
00672         } else {
00673             user = finduser(s);
00674         }
00675         if (!user) {
00676             if (debug) {
00677                 alog("debug: KICK for nonexistent user %s on %s: %s", s,
00678                      av[0], merge_args(ac - 2, av + 2));
00679             }
00680             continue;
00681         }
00682         if (debug) {
00683             alog("debug: kicking %s from %s", user->nick, av[0]);
00684         }
00685         for (c = user->chans; c && stricmp(av[0], c->chan->name) != 0;
00686              c = c->next);
00687         if (c) {
00688             send_event(EVENT_CHAN_KICK, 2, user->nick, av[0]);
00689             chan_deluser(user, c->chan);
00690             if (c->next)
00691                 c->next->prev = c->prev;
00692             if (c->prev)
00693                 c->prev->next = c->next;
00694             else
00695                 user->chans = c->next;
00696             free(c);
00697         }
00698     }
00699 }
00700 
00701 /*************************************************************************/
00702 
00703 /* Handle a PART command.
00704  *      av[0] = channels to leave
00705  *      av[1] = reason (optional)
00706  */
00707 
00708 void do_part(const char *source, int ac, char **av)
00709 {
00710     User *user;
00711     char *s, *t;
00712     struct u_chanlist *c;
00713     char *channame;
00714 
00715     if (UseTS6 && ircd->ts6) {
00716         user = find_byuid(source);
00717         if (!user)
00718             user = finduser(source);
00719     } else {
00720         user = finduser(source);
00721     }
00722     if (!user) {
00723         if (debug) {
00724             alog("debug: PART from nonexistent user %s: %s", source,
00725                  merge_args(ac, av));
00726         }
00727         return;
00728     }
00729     t = av[0];
00730     while (*(s = t)) {
00731         t = s + strcspn(s, ",");
00732         if (*t)
00733             *t++ = 0;
00734         if (debug)
00735             alog("debug: %s leaves %s", source, s);
00736         for (c = user->chans; c && stricmp(s, c->chan->name) != 0;
00737              c = c->next);
00738         if (c) {
00739             if (!c->chan) {
00740                 alog("user: BUG parting %s: channel entry found but c->chan NULL", s);
00741                 return;
00742             }
00743             channame = sstrdup(c->chan->name);
00744             send_event(EVENT_PART_CHANNEL, (ac >= 2 ? 4 : 3), EVENT_START,
00745                        user->nick, channame, (ac >= 2 ? av[1] : ""));
00746 
00747             chan_deluser(user, c->chan);
00748             if (c->next)
00749                 c->next->prev = c->prev;
00750             if (c->prev)
00751                 c->prev->next = c->next;
00752             else
00753                 user->chans = c->next;
00754             free(c);
00755 
00756             send_event(EVENT_PART_CHANNEL, (ac >= 2 ? 4 : 3), EVENT_STOP,
00757                        user->nick, channame, (ac >= 2 ? av[1] : ""));
00758             free(channame);
00759         }
00760     }
00761 }
00762 
00763 /*************************************************************************/
00764 
00765 /* Handle a SJOIN command.
00766 
00767    On channel creation, syntax is:
00768 
00769    av[0] = timestamp
00770    av[1] = channel name
00771    av[2|3|4] = modes   \   depends of whether the modes k and l
00772    av[3|4|5] = users   /   are set or not.
00773 
00774    When a single user joins an (existing) channel, it is:
00775 
00776    av[0] = timestamp
00777    av[1] = user
00778 
00779    ============================================================
00780 
00781    Unreal SJOIN
00782 
00783    On Services connect there is
00784    SJOIN !11LkOb #ircops +nt :@Trystan &*!*@*.aol.com "*@*.home.com
00785 
00786    av[0] = time stamp (base64)
00787    av[1] = channel
00788    av[2] = modes
00789    av[3] = users + bans + exceptions
00790 
00791    On Channel Creation or a User joins an existing
00792    Luna.NomadIrc.Net SJOIN !11LkW9 #akill :@Trystan
00793    Luna.NomadIrc.Net SJOIN !11LkW9 #akill :Trystan`
00794 
00795    av[0] = time stamp (base64)
00796    av[1] = channel
00797    av[2] = users
00798 
00799 */
00800 
00801 void do_sjoin(const char *source, int ac, char **av)
00802 {
00803     Channel *c;
00804     User *user;
00805     Server *serv = NULL;
00806     struct c_userlist *cu;
00807     char *s = NULL;
00808     char *end, cubuf[7], *end2, *cumodes[6], *buf;
00809     int is_sqlined = 0;
00810     int ts = 0;
00811     int is_created = 0;
00812     int keep_their_modes = 1;
00813 
00814     if (UseTS6 && ircd->ts6)
00815         serv = findserver_uid(servlist, source);
00816     if (!serv)
00817         serv = findserver(servlist, source);
00818 
00819     if (ircd->sjb64) {
00820         ts = base64dects(av[0]);
00821     } else {
00822         ts = strtoul(av[0], NULL, 10);
00823     }
00824     c = findchan(av[1]);
00825     if (c != NULL) {
00826         if (c->creation_time == 0 || ts == 0)
00827             c->creation_time = 0;
00828         else if (c->creation_time > ts) {
00829             c->creation_time = ts;
00830             for (cu = c->users; cu; cu = cu->next) {
00831                 /* XXX */
00832                 cumodes[0] = "-ov";
00833                 cumodes[1] = cu->user->nick;
00834                 cumodes[2] = cu->user->nick;
00835                 chan_set_modes(source, c, 3, cumodes, 2);
00836             }
00837             if (c->ci)
00838             {
00839                 if (c->ci->bi)
00840                 {
00841                     /* This is ugly, but it always works */
00842                     anope_cmd_part(c->ci->bi->nick, c->name, "TS reop");
00843                     bot_join(c->ci);
00844                 }
00845                 /* Make sure +r is set */
00846                 if (ircd->chanreg && ircd->regmode)
00847                 {
00848                     c->mode |= ircd->regmode;
00849                     anope_cmd_mode(whosends(c->ci), c->name, "+r");
00850                 }
00851             }
00852             /* XXX simple modes and bans */
00853         } else if (c->creation_time < ts)
00854             keep_their_modes = 0;
00855     } else
00856         is_created = 1;
00857 
00858     /* Double check to avoid unknown modes that need parameters */
00859     if (ac >= 4) {
00860         if (ircd->chansqline) {
00861             if (!c)
00862                 is_sqlined = check_chan_sqline(av[1]);
00863         }
00864 
00865         cubuf[0] = '+';
00866         cumodes[0] = cubuf;
00867 
00868         /* We make all the users join */
00869         s = av[ac - 1];         /* Users are always the last element */
00870 
00871         while (*s) {
00872             end = strchr(s, ' ');
00873             if (end)
00874                 *end = 0;
00875 
00876             end2 = cubuf + 1;
00877 
00878 
00879             if (ircd->sjoinbanchar) {
00880                 if (*s == ircd->sjoinbanchar && keep_their_modes) {
00881                     buf = myStrGetToken(s, ircd->sjoinbanchar, 1);
00882                     add_ban(c, buf);
00883                     if (buf) 
00884                         free(buf);
00885                     if (!end)
00886                         break;
00887                     s = end + 1;
00888                     continue;
00889                 }
00890             }
00891             if (ircd->sjoinexchar) {
00892                 if (*s == ircd->sjoinexchar && keep_their_modes) {
00893                     buf = myStrGetToken(s, ircd->sjoinexchar, 1);
00894                     add_exception(c, buf);
00895                     if (buf)
00896                         free(buf);
00897                     if (!end)
00898                         break;
00899                     s = end + 1;
00900                     continue;
00901                 }
00902             }
00903 
00904             if (ircd->sjoininvchar) {
00905                 if (*s == ircd->sjoininvchar && keep_their_modes) {
00906                     buf = myStrGetToken(s, ircd->sjoininvchar, 1);
00907                     add_invite(c, buf);
00908                     if (buf)
00909                         free(buf);
00910                     if (!end)
00911                         break;
00912                     s = end + 1;
00913                     continue;
00914                 }
00915             }
00916 
00917             while (csmodes[(int) *s] != 0)
00918                 *end2++ = csmodes[(int) *s++];
00919             *end2 = 0;
00920 
00921 
00922             if (UseTS6 && ircd->ts6) {
00923                 user = find_byuid(s);
00924                 if (!user)
00925                     user = finduser(s);
00926             } else {
00927                 user = finduser(s);
00928             }
00929 
00930             if (!user) {
00931                 if (debug) {
00932                     alog("debug: SJOIN for nonexistent user %s on %s", s,
00933                          av[1]);
00934                 }
00935                 return;
00936             }
00937 
00938             if (is_sqlined && !is_oper(user)) {
00939                 anope_cmd_kick(s_OperServ, av[1], s, "Q-Lined");
00940             } else {
00941                 if (!check_kick(user, av[1], ts)) {
00942                     send_event(EVENT_JOIN_CHANNEL, 3, EVENT_START,
00943                                user->nick, av[1]);
00944 
00945                     /* Make the user join; if the channel does not exist it
00946                      * will be created there. This ensures that the channel
00947                      * is not created to be immediately destroyed, and
00948                      * that the locked key or topic is not shown to anyone
00949                      * who joins the channel when empty.
00950                      */
00951                     c = join_user_update(user, c, av[1], ts);
00952 
00953                     /* We update user mode on the channel */
00954                     if (end2 - cubuf > 1 && keep_their_modes) {
00955                         int i;
00956 
00957                         for (i = 1; i < end2 - cubuf; i++)
00958                             cumodes[i] = user->nick;
00959                         chan_set_modes(source, c, 1 + (end2 - cubuf - 1),
00960                                        cumodes, 2);
00961                     }
00962 
00963                     if (c->ci && (!serv || is_sync(serv))
00964                         && !c->topic_sync)
00965                         restore_topic(c->name);
00966                     chan_set_correct_modes(user, c, 1);
00967 
00968                     send_event(EVENT_JOIN_CHANNEL, 3, EVENT_STOP,
00969                                user->nick, av[1]);
00970                 }
00971             }
00972 
00973             if (!end)
00974                 break;
00975             s = end + 1;
00976         }
00977 
00978         if (c && keep_their_modes) {
00979             /* We now update the channel mode. */
00980             chan_set_modes(source, c, ac - 3, &av[2], 2);
00981         }
00982 
00983         /* Unreal just had to be different */
00984     } else if (ac == 3 && !ircd->ts6) {
00985         if (ircd->chansqline) {
00986             if (!c)
00987                 is_sqlined = check_chan_sqline(av[1]);
00988         }
00989 
00990         cubuf[0] = '+';
00991         cumodes[0] = cubuf;
00992 
00993         /* We make all the users join */
00994         s = av[2];              /* Users are always the last element */
00995 
00996         while (*s) {
00997             end = strchr(s, ' ');
00998             if (end)
00999                 *end = 0;
01000 
01001             end2 = cubuf + 1;
01002 
01003             while (csmodes[(int) *s] != 0)
01004                 *end2++ = csmodes[(int) *s++];
01005             *end2 = 0;
01006 
01007             if (UseTS6 && ircd->ts6) {
01008                 user = find_byuid(s);
01009                 if (!user)
01010                     user = finduser(s);
01011             } else {
01012                 user = finduser(s);
01013             }
01014 
01015             if (!user) {
01016                 if (debug) {
01017                     alog("debug: SJOIN for nonexistent user %s on %s", s,
01018                          av[1]);
01019                 }
01020                 return;
01021             }
01022 
01023             if (is_sqlined && !is_oper(user)) {
01024                 anope_cmd_kick(s_OperServ, av[1], s, "Q-Lined");
01025             } else {
01026                 if (!check_kick(user, av[1], ts)) {
01027                     send_event(EVENT_JOIN_CHANNEL, 3, EVENT_START,
01028                                user->nick, av[1]);
01029 
01030                     /* Make the user join; if the channel does not exist it
01031                      * will be created there. This ensures that the channel
01032                      * is not created to be immediately destroyed, and
01033                      * that the locked key or topic is not shown to anyone
01034                      * who joins the channel when empty.
01035                      */
01036                     c = join_user_update(user, c, av[1], ts);
01037 
01038                     /* We update user mode on the channel */
01039                     if (end2 - cubuf > 1 && keep_their_modes) {
01040                         int i;
01041 
01042                         for (i = 1; i < end2 - cubuf; i++)
01043                             cumodes[i] = user->nick;
01044                         chan_set_modes(source, c, 1 + (end2 - cubuf - 1),
01045                                        cumodes, 2);
01046                     }
01047 
01048                     chan_set_correct_modes(user, c, 1);
01049 
01050                     send_event(EVENT_JOIN_CHANNEL, 3, EVENT_STOP,
01051                                user->nick, av[1]);
01052                 }
01053             }
01054 
01055             if (!end)
01056                 break;
01057             s = end + 1;
01058         }
01059     } else if (ac == 3 && ircd->ts6) {
01060         if (ircd->chansqline) {
01061             if (!c)
01062                 is_sqlined = check_chan_sqline(av[1]);
01063         }
01064 
01065         cubuf[0] = '+';
01066         cumodes[0] = cubuf;
01067 
01068         /* We make all the users join */
01069         s = sstrdup(source);    /* Users are always the last element */
01070 
01071         while (*s) {
01072             end = strchr(s, ' ');
01073             if (end)
01074                 *end = 0;
01075 
01076             end2 = cubuf + 1;
01077 
01078             while (csmodes[(int) *s] != 0)
01079                 *end2++ = csmodes[(int) *s++];
01080             *end2 = 0;
01081 
01082             if (UseTS6 && ircd->ts6) {
01083                 user = find_byuid(s);
01084                 if (!user)
01085                     user = finduser(s);
01086             } else {
01087                 user = finduser(s);
01088             }
01089             if (!user) {
01090                 if (debug) {
01091                     alog("debug: SJOIN for nonexistent user %s on %s", s,
01092                          av[1]);
01093                 }
01094                 free(s);
01095                 return;
01096             }
01097 
01098             if (is_sqlined && !is_oper(user)) {
01099                 anope_cmd_kick(s_OperServ, av[1], s, "Q-Lined");
01100             } else {
01101                 if (!check_kick(user, av[1], ts)) {
01102                     send_event(EVENT_JOIN_CHANNEL, 3, EVENT_START,
01103                                user->nick, av[1]);
01104 
01105                     /* Make the user join; if the channel does not exist it
01106                      * will be created there. This ensures that the channel
01107                      * is not created to be immediately destroyed, and
01108                      * that the locked key or topic is not shown to anyone
01109                      * who joins the channel when empty.
01110                      */
01111                     c = join_user_update(user, c, av[1], ts);
01112 
01113                     /* We update user mode on the channel */
01114                     if (end2 - cubuf > 1 && keep_their_modes) {
01115                         int i;
01116 
01117                         for (i = 1; i < end2 - cubuf; i++)
01118                             cumodes[i] = user->nick;
01119                         chan_set_modes(source, c, 1 + (end2 - cubuf - 1),
01120                                        cumodes, 2);
01121                     }
01122 
01123                     chan_set_correct_modes(user, c, 1);
01124 
01125                     send_event(EVENT_JOIN_CHANNEL, 3, EVENT_STOP,
01126                                user->nick, av[1]);
01127                 }
01128             }
01129 
01130             if (!end)
01131                 break;
01132             s = end + 1;
01133         }
01134         free(s);
01135     } else if (ac == 2) {
01136         if (UseTS6 && ircd->ts6) {
01137             user = find_byuid(source);
01138             if (!user)
01139                 user = finduser(source);
01140         } else {
01141             user = finduser(source);
01142         }
01143         if (!user) {
01144             if (debug) {
01145                 alog("debug: SJOIN for nonexistent user %s on %s", source,
01146                      av[1]);
01147             }
01148             return;
01149         }
01150 
01151         if (check_kick(user, av[1], ts))
01152             return;
01153 
01154         if (ircd->chansqline) {
01155             if (!c)
01156                 is_sqlined = check_chan_sqline(av[1]);
01157         }
01158 
01159         if (is_sqlined && !is_oper(user)) {
01160             anope_cmd_kick(s_OperServ, av[1], user->nick, "Q-Lined");
01161         } else {
01162             send_event(EVENT_JOIN_CHANNEL, 3, EVENT_START, user->nick,
01163                        av[1]);
01164 
01165             c = join_user_update(user, c, av[1], ts);
01166             if (is_created && c->ci)
01167                 restore_topic(c->name);
01168             chan_set_correct_modes(user, c, 1);
01169 
01170             send_event(EVENT_JOIN_CHANNEL, 3, EVENT_STOP, user->nick,
01171                        av[1]);
01172         }
01173     }
01174 }
01175 
01176 
01177 /*************************************************************************/
01178 
01179 /* Handle a channel MODE command. */
01180 
01181 void do_cmode(const char *source, int ac, char **av)
01182 {
01183     Channel *chan;
01184     ChannelInfo *ci = NULL;
01185     int i;
01186     char *t;
01187 
01188     if (ircdcap->tsmode) {
01189         /* TSMODE for bahamut - leave this code out to break MODEs. -GD */
01190         /* if they don't send it in CAPAB check if we just want to enable it */
01191         if (uplink_capab & ircdcap->tsmode || UseTSMODE) {
01192             for (i = 0; i < strlen(av[1]); i++) {
01193                 if (!isdigit(av[1][i]))
01194                     break;
01195             }
01196             if (av[1][i] == '\0') {
01197                 /* We have a valid TS field in av[1] now, so we can strip it off */
01198                 /* After we swap av[0] and av[1] ofcourse to not break stuff! :) */
01199                 t = av[0];
01200                 av[0] = av[1];
01201                 av[1] = t;
01202                 ac--;
01203                 av++;
01204             } else {
01205                 alog("TSMODE enabled but MODE has no valid TS");
01206             }
01207         }
01208     }
01209 
01210     /* :42XAAAAAO TMODE 1106409026 #ircops +b *!*@*.aol.com */
01211     if (UseTS6 && ircd->ts6) {
01212         if (isdigit(av[0][0])) {
01213             ac--;
01214             av++;
01215         }
01216     }
01217 
01218     chan = findchan(av[0]);
01219     if (!chan) {
01220         if (debug) {
01221             ci = cs_findchan(av[0]);
01222             if (!(ci && (ci->flags & CI_VERBOTEN)))
01223                 alog("debug: MODE %s for nonexistent channel %s",
01224                      merge_args(ac - 1, av + 1), av[0]);
01225         }
01226         return;
01227     }
01228 
01229     /* This shouldn't trigger on +o, etc. */
01230     if (strchr(source, '.') && !av[1][strcspn(av[1], "bovahq")]) {
01231         if (time(NULL) != chan->server_modetime) {
01232             chan->server_modecount = 0;
01233             chan->server_modetime = time(NULL);
01234         }
01235         chan->server_modecount++;
01236     }
01237 
01238     ac--;
01239     av++;
01240     chan_set_modes(source, chan, ac, av, 1);
01241 }
01242 
01243 /*************************************************************************/
01244 
01245 /* Handle a TOPIC command. */
01246 
01247 void do_topic(const char *source, int ac, char **av)
01248 {
01249     Channel *c = findchan(av[0]);
01250     ChannelInfo *ci;
01251     int ts;
01252     time_t topic_time;
01253     char *topicsetter;
01254 
01255     if (ircd->sjb64) {
01256         ts = base64dects(av[2]);
01257         if (debug) {
01258             alog("debug: encoded TOPIC TS %s converted to %d", av[2], ts);
01259         }
01260     } else {
01261         ts = strtoul(av[2], NULL, 10);
01262     }
01263 
01264     topic_time = ts;
01265 
01266     if (!c) {
01267         if (debug) {
01268             alog("debug: TOPIC %s for nonexistent channel %s",
01269                  merge_args(ac - 1, av + 1), av[0]);
01270         }
01271         return;
01272     }
01273 
01274     /* We can be sure that the topic will be in sync here -GD */
01275     c->topic_sync = 1;
01276 
01277     ci = c->ci;
01278 
01279     /* For Unreal, cut off the ! and any futher part of the topic setter.
01280      * This way, nick!ident@host setters will only show the nick. -GD
01281      */
01282     topicsetter = myStrGetToken(av[1], '!', 0);
01283 
01284     /* If the current topic we have matches the last known topic for this
01285      * channel exactly, there's no need to update anything and we can as
01286      * well just return silently without updating anything. -GD
01287      * But we still need to update the topic internally for the channel - Adam
01288      */
01289     if ((ac > 3) && *av[3] && ci && ci->last_topic
01290         && (strcmp(av[3], ci->last_topic) == 0)
01291         && (strcmp(topicsetter, ci->last_topic_setter) == 0)) {
01292 
01293         if (c->topic)
01294             free(c->topic);
01295         c->topic = sstrdup(av[3]);
01296         strscpy(c->topic_setter, topicsetter, sizeof(c->topic_setter));
01297         c->topic_time = topic_time;
01298 
01299         free(topicsetter);
01300         return;
01301     }
01302 
01303     if (check_topiclock(c, topic_time)) {
01304         free(topicsetter);
01305         return;
01306     }
01307 
01308     if (c->topic) {
01309         free(c->topic);
01310         c->topic = NULL;
01311     }
01312     if (ac > 3 && *av[3]) {
01313         c->topic = sstrdup(av[3]);
01314     }
01315 
01316     strscpy(c->topic_setter, topicsetter, sizeof(c->topic_setter));
01317     c->topic_time = topic_time;
01318     free(topicsetter);
01319 
01320     record_topic(av[0]);
01321 
01322     /* Only call events if we are synced with the uplink */
01323     if (serv_uplink && is_sync(serv_uplink)) {
01324         if (ci && ci->last_topic) {
01325             send_event(EVENT_TOPIC_UPDATED, 2, av[0], ci->last_topic);
01326         } else {
01327             send_event(EVENT_TOPIC_UPDATED, 2, av[0], "");
01328         }
01329     }
01330 }
01331 
01332 /*************************************************************************/
01333 /**************************** Internal Calls *****************************/
01334 /*************************************************************************/
01335 
01336 void add_ban(Channel * chan, char *mask)
01337 {
01338     Entry *ban;
01339     /* check for NULL values otherwise we will segfault */
01340     if (!chan || !mask) {
01341         if (debug) {
01342             alog("debug: add_ban called with NULL values");
01343         }
01344         return;
01345     }
01346 
01347     /* Check if the list already exists, if not create it.
01348      * Create a new ban and add it to the list.. ~ Viper */
01349     if (!chan->bans)
01350         chan->bans = list_create();
01351     ban = entry_add(chan->bans, mask);
01352 
01353     if (!ban)
01354         fatal("Creating new ban entry failed");
01355 
01356     /* Check whether it matches a botserv bot after adding internally
01357      * and parsing it through cidr support. ~ Viper */
01358     if (s_BotServ && BSSmartJoin && chan->ci && chan->ci->bi
01359         && chan->usercount >= BSMinUsers) {
01360         BotInfo *bi = chan->ci->bi;
01361 
01362         if (entry_match(ban, bi->nick, bi->user, bi->host, 0)) {
01363             anope_cmd_mode(bi->nick, chan->name, "-b %s", mask);
01364             entry_delete(chan->bans, ban);
01365             return;
01366         }
01367     }
01368 
01369     if (debug)
01370         alog("debug: Added ban %s to channel %s", mask, chan->name);
01371 }
01372 
01373 /*************************************************************************/
01374 
01375 void add_exception(Channel * chan, char *mask)
01376 {
01377     Entry *exception;
01378 
01379     if (!chan || !mask) {
01380         if (debug)
01381             alog("debug: add_exception called with NULL values");
01382         return;
01383     }
01384 
01385     /* Check if the list already exists, if not create it.
01386      * Create a new exception and add it to the list.. ~ Viper */
01387     if (!chan->excepts)
01388         chan->excepts = list_create();
01389     exception = entry_add(chan->excepts, mask);
01390 
01391     if (!exception)
01392         fatal("Creating new exception entry failed");
01393 
01394     if (debug)
01395         alog("debug: Added except %s to channel %s", mask, chan->name);
01396 }
01397 
01398 /*************************************************************************/
01399 
01400 void add_invite(Channel * chan, char *mask)
01401 {
01402     Entry *invite;
01403 
01404     if (!chan || !mask) {
01405         if (debug)
01406             alog("debug: add_invite called with NULL values");
01407         return;
01408     }
01409 
01410     /* Check if the list already exists, if not create it.
01411      * Create a new invite and add it to the list.. ~ Viper */
01412     if (!chan->invites)
01413         chan->invites = list_create();
01414     invite = entry_add(chan->invites, mask);
01415 
01416     if (!invite)
01417         fatal("Creating new exception entry failed");
01418 
01419     if (debug)
01420         alog("debug: Added invite %s to channel %s", mask, chan->name);
01421 }
01422 
01423 /*************************************************************************/
01424 
01434 void chan_set_correct_modes(User * user, Channel * c, int give_modes)
01435 {
01436     char *tmp;
01437     char modebuf[BUFSIZE];
01438     char userbuf[BUFSIZE];
01439     int status;
01440     int add_modes = 0;
01441     int rem_modes = 0;
01442     ChannelInfo *ci;
01443 
01444     if (!c || !(ci = c->ci))
01445         return;
01446 
01447     if ((ci->flags & CI_VERBOTEN) || (*(c->name) == '+'))
01448         return;
01449 
01450     status = chan_get_user_status(c, user);
01451 
01452     if (debug)
01453         alog("debug: Setting correct user modes for %s on %s (current status: %d, %sgiving modes)", user->nick, c->name, status, (give_modes ? "" : "not "));
01454 
01455     /* Changed the second line of this if a bit, to make sure unregistered
01456      * users can always get modes (IE: they always have autoop enabled). Before
01457      * this change, you were required to have a registered nick to be able
01458      * to receive modes. I wonder who added that... *looks at Rob* ;) -GD
01459      */
01460     if (give_modes && (get_ignore(user->nick) == NULL)
01461         && (!user->na || !(user->na->nc->flags & NI_AUTOOP))) {
01462         if (ircd->owner && is_founder(user, ci))
01463             add_modes |= CUS_OWNER;
01464         else if ((ircd->protect || ircd->admin)
01465                  && check_access(user, ci, CA_AUTOPROTECT))
01466             add_modes |= CUS_PROTECT;
01467         if (check_access(user, ci, CA_AUTOOP))
01468             add_modes |= CUS_OP;
01469         else if (ircd->halfop && check_access(user, ci, CA_AUTOHALFOP))
01470             add_modes |= CUS_HALFOP;
01471         else if (check_access(user, ci, CA_AUTOVOICE))
01472             add_modes |= CUS_VOICE;
01473     }
01474 
01475     /* We check if every mode they have is legally acquired here, and remove
01476      * the modes that they're not allowed to have. But only if SECUREOPS is
01477      * on, because else every mode is legal. -GD
01478      * Unless the channel has just been created. -heinz
01479      *     Or the user matches CA_AUTODEOP... -GD
01480      */
01481     if (((ci->flags & CI_SECUREOPS) || (c->usercount == 1 && is_sync(user->server))
01482          || check_access(user, ci, CA_AUTODEOP))
01483         && !is_ulined(user->server->name)) {
01484         if (ircd->owner && (status & CUS_OWNER) && !is_founder(user, ci))
01485             rem_modes |= CUS_OWNER;
01486         if ((ircd->protect || ircd->admin) && (status & CUS_PROTECT)
01487             && !check_access(user, ci, CA_AUTOPROTECT)
01488             && !check_access(user, ci, CA_PROTECTME))
01489             rem_modes |= CUS_PROTECT;
01490         if ((status & CUS_OP) && !check_access(user, ci, CA_AUTOOP)
01491             && !check_access(user, ci, CA_OPDEOPME))
01492             rem_modes |= CUS_OP;
01493         if (ircd->halfop && (status & CUS_HALFOP)
01494             && !check_access(user, ci, CA_AUTOHALFOP)
01495             && !check_access(user, ci, CA_HALFOPME))
01496             rem_modes |= CUS_HALFOP;
01497     }
01498 
01499     /* No modes to add or remove, exit function -GD */
01500     if (!add_modes && !rem_modes)
01501         return;
01502 
01503     /* No need for strn* functions for modebuf, as every possible string
01504      * will always fit in. -GD
01505      */
01506     strcpy(modebuf, "");
01507     strcpy(userbuf, "");
01508     if (add_modes > 0) {
01509         strcat(modebuf, "+");
01510         if ((add_modes & CUS_OWNER) && !(status & CUS_OWNER)) {
01511             tmp = stripModePrefix(ircd->ownerset);
01512             strcat(modebuf, tmp);
01513             free(tmp);
01514             strcat(userbuf, " ");
01515             strcat(userbuf, GET_USER(user));
01516         } else {
01517             add_modes &= ~CUS_OWNER;
01518         }
01519         if ((add_modes & CUS_PROTECT) && !(status & CUS_PROTECT)) {
01520             tmp = stripModePrefix(ircd->adminset);
01521             strcat(modebuf, tmp);
01522             free(tmp);
01523             strcat(userbuf, " ");
01524             strcat(userbuf, GET_USER(user));
01525         } else {
01526             add_modes &= ~CUS_PROTECT;
01527         }
01528         if ((add_modes & CUS_OP) && !(status & CUS_OP)) {
01529             strcat(modebuf, "o");
01530             strcat(userbuf, " ");
01531             strcat(userbuf, GET_USER(user));
01532             rem_modes |= CUS_DEOPPED;
01533         } else {
01534             add_modes &= ~CUS_OP;
01535         }
01536         if ((add_modes & CUS_HALFOP) && !(status & CUS_HALFOP)) {
01537             strcat(modebuf, "h");
01538             strcat(userbuf, " ");
01539             strcat(userbuf, GET_USER(user));
01540             /* Halfops are ops too, having a halfop with CUS_DEOPPED is not good - Adam */
01541             rem_modes |= CUS_DEOPPED;
01542         } else {
01543             add_modes &= ~CUS_HALFOP;
01544         }
01545         if ((add_modes & CUS_VOICE) && !(status & CUS_VOICE)) {
01546             strcat(modebuf, "v");
01547             strcat(userbuf, " ");
01548             strcat(userbuf, GET_USER(user));
01549         } else {
01550             add_modes &= ~CUS_VOICE;
01551         }
01552     }
01553     if (rem_modes > 0) {
01554         strcat(modebuf, "-");
01555         if (rem_modes & CUS_OWNER) {
01556             tmp = stripModePrefix(ircd->ownerset);
01557             strcat(modebuf, tmp);
01558             free(tmp);
01559             strcat(userbuf, " ");
01560             strcat(userbuf, GET_USER(user));
01561         }
01562         if (rem_modes & CUS_PROTECT) {
01563             tmp = stripModePrefix(ircd->adminset);
01564             strcat(modebuf, tmp);
01565             free(tmp);
01566             strcat(userbuf, " ");
01567             strcat(userbuf, GET_USER(user));
01568         }
01569         if (rem_modes & CUS_OP) {
01570             strcat(modebuf, "o");
01571             strcat(userbuf, " ");
01572             strcat(userbuf, GET_USER(user));
01573             /* Do not mark a user as deopped if they are halfopd - Adam */
01574             if (!(add_modes & CUS_HALFOP) && !(status & CUS_HALFOP))
01575                 add_modes |= CUS_DEOPPED;
01576         }
01577         if (rem_modes & CUS_HALFOP) {
01578             strcat(modebuf, "h");
01579             strcat(userbuf, " ");
01580             strcat(userbuf, GET_USER(user));
01581             /* Do not mark a user as deopped if they are opped - Adam */
01582             if (!(add_modes & CUS_OP) && !(status & CUS_OP))
01583                 add_modes |= CUS_DEOPPED;
01584         }
01585     }
01586 
01587     /* Here, both can be empty again due to the "isn't it set already?"
01588      * checks above. -GD
01589      */
01590     if (!add_modes && !rem_modes)
01591         return;
01592 
01593     anope_cmd_mode(whosends(ci), c->name, "%s%s", modebuf, userbuf);
01594     if (add_modes > 0)
01595         chan_set_user_status(c, user, add_modes);
01596     if (rem_modes > 0)
01597         chan_remove_user_status(c, user, rem_modes);
01598 }
01599 
01600 /*************************************************************************/
01601 
01602 /* Add/remove a user to/from a channel, creating or deleting the channel as
01603  * necessary.  If creating the channel, restore mode lock and topic as
01604  * necessary.  Also check for auto-opping and auto-voicing.
01605  */
01606 
01607 void chan_adduser2(User * user, Channel * c)
01608 {
01609     struct c_userlist *u;
01610 
01611     u = scalloc(sizeof(struct c_userlist), 1);
01612     u->next = c->users;
01613     if (c->users)
01614         c->users->prev = u;
01615     c->users = u;
01616     u->user = user;
01617     c->usercount++;
01618 
01619     if (get_ignore(user->nick) == NULL) {
01620         if (c->ci && (check_access(user, c->ci, CA_MEMO))
01621             && (c->ci->memos.memocount > 0)) {
01622             if (c->ci->memos.memocount == 1) {
01623                 notice_lang(s_MemoServ, user, MEMO_X_ONE_NOTICE,
01624                             c->ci->memos.memocount, c->ci->name);
01625             } else {
01626                 notice_lang(s_MemoServ, user, MEMO_X_MANY_NOTICE,
01627                             c->ci->memos.memocount, c->ci->name);
01628             }
01629         }
01630         /* Added channelname to entrymsg - 30.03.2004, Certus */
01631         /* Also, don't send the entrymsg when bursting -GD */
01632         if (c->ci && c->ci->entry_message && is_sync(user->server))
01633             notice_user(whosends(c->ci), user, "[%s] %s", c->name,
01634                         c->ci->entry_message);
01635     }
01636 
01643     if (s_BotServ && c->ci && c->ci->bi) {
01644         if (c->usercount == BSMinUsers)
01645             bot_join(c->ci);
01646         if (c->usercount >= BSMinUsers && (c->ci->botflags & BS_GREET)
01647             && user->na && user->na->nc->greet
01648             && check_access(user, c->ci, CA_GREET)) {
01649             /* Only display the greet if the main uplink we're connected
01650              * to has synced, or we'll get greet-floods when the net
01651              * recovers from a netsplit. -GD
01652              */
01653             if (is_sync(user->server)) {
01654                 anope_cmd_privmsg(c->ci->bi->nick, c->name, "[%s] %s",
01655                                   user->na->nick, user->na->nc->greet);
01656                 c->ci->bi->lastmsg = time(NULL);
01657             }
01658         }
01659     }
01660 }
01661 
01662 /*************************************************************************/
01663 
01664 /* This creates the channel structure (was originally in
01665    chan_adduser, but splitted to make it more efficient to use for
01666    SJOINs). */
01667 
01668 Channel *chan_create(char *chan, time_t ts)
01669 {
01670     Channel *c;
01671     Channel **list;
01672 
01673     if (debug)
01674         alog("debug: Creating channel %s", chan);
01675     /* Allocate pre-cleared memory */
01676     c = scalloc(sizeof(Channel), 1);
01677     strscpy(c->name, chan, sizeof(c->name));
01678     list = &chanlist[HASH(c->name)];
01679     c->next = *list;
01680     if (*list)
01681         (*list)->prev = c;
01682     *list = c;
01683     c->creation_time = ts;
01684     /* Store ChannelInfo pointer in channel record */
01685     c->ci = cs_findchan(chan);
01686     if (c->ci)
01687         c->ci->c = c;
01688     /* Restore locked modes and saved topic */
01689     if (c->ci) {
01690         check_modes(c);
01691         stick_all(c->ci);
01692     }
01693 
01694     if (serv_uplink && is_sync(serv_uplink) && (!(c->topic_sync))) {
01695         restore_topic(chan);
01696     }
01697 
01698     return c;
01699 }
01700 
01701 /*************************************************************************/
01702 
01703 /* This destroys the channel structure, freeing everything in it. */
01704 
01705 void chan_delete(Channel * c)
01706 {
01707     BanData *bd, *next;
01708 
01709     if (debug)
01710         alog("debug: Deleting channel %s", c->name);
01711 
01712     for (bd = c->bd; bd; bd = next) {
01713         if (bd->mask)
01714             free(bd->mask);
01715         next = bd->next;
01716         free(bd);
01717     }
01718 
01719     if (c->ci)
01720         c->ci->c = NULL;
01721 
01722     if (c->topic)
01723         free(c->topic);
01724 
01725     if (c->key)
01726         free(c->key);
01727     if (ircd->fmode) {
01728         if (c->flood)
01729             free(c->flood);
01730     }
01731     if (ircd->Lmode) {
01732         if (c->redirect)
01733             free(c->redirect);
01734     }
01735     if (ircd->jmode) {
01736         if (c->throttle)
01737             free (c->throttle);
01738     }
01739 
01740     if (c->bans && c->bans->count) {
01741         while (c->bans->entries) {
01742             entry_delete(c->bans, c->bans->entries);
01743         }
01744     }
01745 
01746     if (ircd->except) {
01747         if (c->excepts && c->excepts->count) {
01748             while (c->excepts->entries) {
01749                 entry_delete(c->excepts, c->excepts->entries);
01750             }
01751         }
01752     }
01753 
01754     if (ircd->invitemode) {
01755         if (c->invites && c->invites->count) {
01756             while (c->invites->entries) {
01757                 entry_delete(c->invites, c->invites->entries);
01758             }
01759         }
01760     }
01761 
01762     if (c->next)
01763         c->next->prev = c->prev;
01764     if (c->prev)
01765         c->prev->next = c->next;
01766     else
01767         chanlist[HASH(c->name)] = c->next;
01768 
01769     free(c);
01770 }
01771 
01772 /*************************************************************************/
01773 
01774 void del_ban(Channel * chan, char *mask)
01775 {
01776     AutoKick *akick;
01777     Entry *ban;
01778 
01779     /* Sanity check as it seems some IRCD will just send -b without a mask */
01780     if (!mask || !chan->bans || (chan->bans->count == 0))
01781         return;
01782 
01783     ban = elist_find_mask(chan->bans, mask);
01784 
01785     if (ban) {
01786         entry_delete(chan->bans, ban);
01787 
01788         if (debug)
01789             alog("debug: Deleted ban %s from channel %s", mask,
01790                  chan->name);
01791     }
01792 
01793     if (chan->ci && (akick = is_stuck(chan->ci, mask)))
01794         stick_mask(chan->ci, akick);
01795 }
01796 
01797 /*************************************************************************/
01798 
01799 void del_exception(Channel * chan, char *mask)
01800 {
01801     Entry *exception;
01802 
01803     /* Sanity check as it seems some IRCD will just send -e without a mask */
01804     if (!mask || !chan->excepts || (chan->excepts->count == 0))
01805         return;
01806 
01807     exception = elist_find_mask(chan->excepts, mask);
01808 
01809     if (exception) {
01810         entry_delete(chan->excepts, exception);
01811 
01812         if (debug)
01813             alog("debug: Deleted except %s to channel %s", mask,
01814                  chan->name);
01815     }
01816 }
01817 
01818 /*************************************************************************/
01819 
01820 void del_invite(Channel * chan, char *mask)
01821 {
01822     Entry *invite;
01823 
01824     /* Sanity check as it seems some IRCD will just send -I without a mask */
01825     if (!mask || !chan->invites || (chan->invites->count == 0)) {
01826         return;
01827     }
01828 
01829     invite = elist_find_mask(chan->invites, mask);
01830 
01831     if (invite) {
01832         entry_delete(chan->invites, invite);
01833 
01834         if (debug)
01835             alog("debug: Deleted invite %s to channel %s", mask,
01836                  chan->name);
01837     }
01838 }
01839 
01840 
01841 /*************************************************************************/
01842 
01843 char *get_flood(Channel * chan)
01844 {
01845     return chan->flood;
01846 }
01847 
01848 /*************************************************************************/
01849 
01850 char *get_throttle(Channel * chan)
01851 {
01852     return chan->throttle;
01853 }
01854 
01855 /*************************************************************************/
01856 
01857 char *get_key(Channel * chan)
01858 {
01859     return chan->key;
01860 }
01861 
01862 /*************************************************************************/
01863 
01864 char *get_limit(Channel * chan)
01865 {
01866     static char limit[16];
01867 
01868     if (chan->limit == 0)
01869         return NULL;
01870 
01871     snprintf(limit, sizeof(limit), "%lu", (unsigned long int) chan->limit);
01872     return limit;
01873 }
01874 
01875 /*************************************************************************/
01876 
01877 char *get_redirect(Channel * chan)
01878 {
01879     return chan->redirect;
01880 }
01881 
01882 /*************************************************************************/
01883 
01884 /* This is a dummy function part of making anope accept modes
01885  * it does actively parse.. ~ Viper */
01886 char *get_unkwn(Channel * chan)
01887 {
01888     return NULL;
01889 }
01890 
01891 /*************************************************************************/
01892 
01893 Channel *join_user_update(User * user, Channel * chan, char *name,
01894                           time_t chants)
01895 {
01896         struct u_chanlist *c;
01897 
01898         /* If it's a new channel, so we need to create it first. */
01899         if (!chan)
01900                 chan = chan_create(name, chants);
01901         else
01902         {
01903                 /* Check chants against 0, as not every ircd sends JOIN with a TS. */
01904                 if (chan->creation_time > chants && chants != 0)
01905                 {
01906                         struct c_userlist *cu;
01907                         char *modes[6];
01908 
01909                         chan->creation_time = chants;
01910                         for (cu = chan->users; cu; cu = cu->next)
01911                         {
01912                                 /* XXX */
01913                                 modes[0] = "-ov";
01914                                 modes[1] = cu->user->nick;
01915                                 modes[2] = cu->user->nick;
01916                                 chan_set_modes(s_OperServ, chan, 3, modes, 2);
01917                         }
01918                         if (chan->ci)
01919                         {
01920                                 if (chan->ci->bi)
01921                                 {
01922                                         /* This is ugly, but it always works */
01923                                         anope_cmd_part(chan->ci->bi->nick, chan->name, "TS reop");
01924                                         bot_join(chan->ci);
01925                                 }
01926                                 /* Make sure +r is set */
01927                                 if (ircd->chanreg && ircd->regmode)
01928                                 {
01929                                         chan->mode |= ircd->regmode;
01930                                         anope_cmd_mode(whosends(chan->ci), chan->name, "+r");
01931                                 }
01932                         }
01933                         /* XXX simple modes and bans */
01934                 }
01935 
01936         }
01937 
01938         if (debug)
01939                 alog("debug: %s joins %s", user->nick, chan->name);
01940 
01941         c = scalloc(sizeof(*c), 1);
01942         c->next = user->chans;
01943         if (user->chans)
01944                 user->chans->prev = c;
01945         user->chans = c;
01946         c->chan = chan;
01947 
01948         chan_adduser2(user, chan);
01949 
01950         return chan;
01951 }
01952 
01953 /*************************************************************************/
01954 
01955 void set_flood(Channel * chan, char *value)
01956 {
01957     if (chan->flood)
01958         free(chan->flood);
01959     chan->flood = value ? sstrdup(value) : NULL;
01960 
01961     if (debug)
01962         alog("debug: Flood mode for channel %s set to %s", chan->name,
01963              chan->flood ? chan->flood : "no flood settings");
01964 }
01965 
01966 /*************************************************************************/
01967 
01968 void chan_set_throttle(Channel * chan, char *value)
01969 {
01970     if (chan->throttle)
01971         free(chan->throttle);
01972     chan->throttle = value ? sstrdup(value) : NULL;
01973 
01974     if (debug)
01975         alog("debug: Throttle mode for channel %s set to %s", chan->name,
01976             chan->throttle ? chan->throttle : "none");
01977 }
01978 
01979 /*************************************************************************/
01980 
01981 void chan_set_key(Channel * chan, char *value)
01982 {
01983     if (chan->key)
01984         free(chan->key);
01985     chan->key = value ? sstrdup(value) : NULL;
01986 
01987     if (debug)
01988         alog("debug: Key of channel %s set to %s", chan->name,
01989              chan->key ? chan->key : "no key");
01990 }
01991 
01992 /*************************************************************************/
01993 
01994 void set_limit(Channel * chan, char *value)
01995 {
01996     chan->limit = value ? strtoul(value, NULL, 10) : 0;
01997 
01998     if (debug)
01999         alog("debug: Limit of channel %s set to %u", chan->name,
02000              chan->limit);
02001 }
02002 
02003 /*************************************************************************/
02004 
02005 void set_redirect(Channel * chan, char *value)
02006 {
02007     if (chan->redirect)
02008         free(chan->redirect);
02009     chan->redirect = value ? sstrdup(value) : NULL;
02010 
02011     if (debug)
02012         alog("debug: Redirect of channel %s set to %s", chan->name,
02013              chan->redirect ? chan->redirect : "no redirect");
02014 }
02015 
02016 /*************************************************************************/
02017 
02018 /* This is a dummy function to make anope parse a param for a mode,
02019  * yet we don't use that param internally.. ~ Viper */
02020 void set_unkwn(Channel *chan, char *value)
02021 {
02022     /* Do nothing.. */
02023 }
02024 
02025 /*************************************************************************/
02026 
02027 void do_mass_mode(char *modes)
02028 {
02029     int ac;
02030     char **av;
02031     Channel *c;
02032     char *myModes;
02033 
02034     if (!modes) {
02035         return;
02036     }
02037 
02038     /* Prevent modes being altered by split_buf */
02039     myModes = sstrdup(modes);
02040     ac = split_buf(myModes, &av, 1);
02041 
02042     for (c = firstchan(); c; c = nextchan()) {
02043         if (c->bouncy_modes) {
02044             free(av);
02045             free(myModes);
02046             return;
02047         } else {
02048             anope_cmd_mode(s_OperServ, c->name, "%s", modes);
02049             chan_set_modes(s_OperServ, c, ac, av, 1);
02050         }
02051     }
02052     free(av);
02053     free(myModes);
02054 }
02055 
02056 /*************************************************************************/
02057 
02058 void restore_unsynced_topics(void)
02059 {
02060     Channel *c;
02061 
02062     for (c = firstchan(); c; c = nextchan()) {
02063         if (!(c->topic_sync))
02064             restore_topic(c->name);
02065     }
02066 }
02067 
02068 /*************************************************************************/
02069 
02076 Entry *entry_create(char *mask)
02077 {
02078     Entry *entry;
02079     char *nick = NULL, *user, *host, *cidrhost;
02080     uint32 ip, cidr;
02081 
02082     entry = scalloc(1, sizeof(Entry));
02083     entry->type = ENTRYTYPE_NONE;
02084     entry->prev = NULL;
02085     entry->next = NULL;
02086     entry->nick = NULL;
02087     entry->user = NULL;
02088     entry->host = NULL;
02089     entry->mask = sstrdup(mask);
02090 
02091     host = strchr(mask, '@');
02092     if (host) {
02093         *host++ = '\0';
02094         /* If the user is purely a wildcard, ignore it */
02095         if (str_is_pure_wildcard(mask))
02096             user = NULL;
02097         else {
02098 
02099             /* There might be a nick too  */
02100             user = strchr(mask, '!');
02101             if (user) {
02102                 *user++ = '\0';
02103                 /* If the nick is purely a wildcard, ignore it */
02104                 if (str_is_pure_wildcard(mask))
02105                     nick = NULL;
02106                 else
02107                     nick = mask;
02108             } else {
02109                 nick = NULL;
02110                 user = mask;
02111             }
02112         }
02113     } else {
02114         /* It is possibly an extended ban/invite mask, but we do
02115          * not support these at this point.. ~ Viper */
02116         /* If there's no user in the mask, assume a pure wildcard */
02117         user = NULL;
02118         host = mask;
02119     }
02120 
02121     if (nick) {
02122         entry->nick = sstrdup(nick);
02123         /* Check if we have a wildcard user */
02124         if (str_is_wildcard(nick))
02125             entry->type |= ENTRYTYPE_NICK_WILD;
02126         else
02127             entry->type |= ENTRYTYPE_NICK;
02128     }
02129 
02130     if (user) {
02131         entry->user = sstrdup(user);
02132         /* Check if we have a wildcard user */
02133         if (str_is_wildcard(user))
02134             entry->type |= ENTRYTYPE_USER_WILD;
02135         else
02136             entry->type |= ENTRYTYPE_USER;
02137     }
02138 
02139     /* Only check the host if it's not a pure wildcard */
02140     if (*host && !str_is_pure_wildcard(host)) {
02141         if (ircd->cidrchanbei && str_is_cidr(host, &ip, &cidr, &cidrhost)) {
02142             entry->cidr_ip = ip;
02143             entry->cidr_mask = cidr;
02144             entry->type |= ENTRYTYPE_CIDR4;
02145             host = cidrhost;
02146         } else if (ircd->cidrchanbei && strchr(host, '/')) {
02147             /* Most IRCd's don't enforce sane bans therefore it is not
02148              * so unlikely we will encounter this.
02149              * Currently we only support strict CIDR without taking into
02150              * account quirks of every single ircd (nef) that ignore everything
02151              * after the first /cidr. To add this, sanitaze before sending to
02152              * str_is_cidr() as this expects a standard cidr.
02153              * Add it to the internal list (so it is included in for example clear)
02154              * but do not use if during matching.. ~ Viper */
02155             entry->type = ENTRYTYPE_NONE;
02156         } else {
02157             entry->host = sstrdup(host);
02158             if (str_is_wildcard(host))
02159                 entry->type |= ENTRYTYPE_HOST_WILD;
02160             else
02161                 entry->type |= ENTRYTYPE_HOST;
02162         }
02163     }
02164     free(mask);
02165 
02166     return entry;
02167 }
02168 
02169 
02176 Entry *entry_add(EList * list, char *mask)
02177 {
02178     Entry *e;
02179     char *hostmask;
02180 
02181     hostmask = sstrdup(mask);
02182     e = entry_create(hostmask);
02183 
02184     if (!e)
02185         return NULL;
02186 
02187     e->next = list->entries;
02188     e->prev = NULL;
02189 
02190     if (list->entries)
02191         list->entries->prev = e;
02192     list->entries = e;
02193     list->count++;
02194 
02195     return e;
02196 }
02197 
02198 
02204 void entry_delete(EList * list, Entry * e)
02205 {
02206     if (!list || !e)
02207         return;
02208 
02209     if (e->next)
02210         e->next->prev = e->prev;
02211     if (e->prev)
02212         e->prev->next = e->next;
02213 
02214     if (list->entries == e)
02215         list->entries = e->next;
02216 
02217     if (e->nick)
02218         free(e->nick);
02219     if (e->user)
02220         free(e->user);
02221     if (e->host)
02222         free(e->host);
02223     free(e->mask);
02224     free(e);
02225 
02226     list->count--;
02227 }
02228 
02229 
02234 EList *list_create()
02235 {
02236     EList *list;
02237 
02238     list = scalloc(1, sizeof(EList));
02239     list->entries = NULL;
02240     list->count = 0;
02241 
02242     return list;
02243 }
02244 
02245 
02255 int entry_match(Entry * e, char *nick, char *user, char *host, uint32 ip)
02256 {
02257     /* If we don't get an entry, or it s an invalid one, no match ~ Viper */
02258     if (!e || e->type == ENTRYTYPE_NONE)
02259         return 0;
02260 
02261     if (ircd->cidrchanbei && (e->type & ENTRYTYPE_CIDR4) &&
02262         (!ip || (ip && ((ip & e->cidr_mask) != e->cidr_ip))))
02263         return 0;
02264     if ((e->type & ENTRYTYPE_NICK)
02265         && (!nick || stricmp(e->nick, nick) != 0))
02266         return 0;
02267     if ((e->type & ENTRYTYPE_USER)
02268         && (!user || stricmp(e->user, user) != 0))
02269         return 0;
02270     if ((e->type & ENTRYTYPE_HOST)
02271         && (!host || stricmp(e->host, host) != 0))
02272         return 0;
02273     if ((e->type & ENTRYTYPE_NICK_WILD)
02274         && !match_wild_nocase(e->nick, nick))
02275         return 0;
02276     if ((e->type & ENTRYTYPE_USER_WILD)
02277         && !match_wild_nocase(e->user, user))
02278         return 0;
02279     if ((e->type & ENTRYTYPE_HOST_WILD)
02280         && !match_wild_nocase(e->host, host))
02281         return 0;
02282 
02283     return 1;
02284 }
02285 
02293 int entry_match_mask(Entry * e, char *mask, uint32 ip)
02294 {
02295     char *hostmask, *nick, *user, *host;
02296     int res;
02297 
02298     hostmask = sstrdup(mask);
02299 
02300     host = strchr(hostmask, '@');
02301     if (host) {
02302         *host++ = '\0';
02303         user = strchr(hostmask, '!');
02304         if (user) {
02305             *user++ = '\0';
02306             nick = hostmask;
02307         } else {
02308             nick = NULL;
02309             user = hostmask;
02310         }
02311     } else {
02312         nick = NULL;
02313         user = NULL;
02314         host = hostmask;
02315     }
02316 
02317     res = entry_match(e, nick, user, host, ip);
02318 
02319     /* Free the destroyed mask. */
02320     free(hostmask);
02321 
02322     return res;
02323 }
02324 
02334 Entry *elist_match(EList * list, char *nick, char *user, char *host,
02335                    uint32 ip)
02336 {
02337     Entry *e;
02338 
02339     if (!list || !list->entries)
02340         return NULL;
02341 
02342     for (e = list->entries; e; e = e->next) {
02343         if (entry_match(e, nick, user, host, ip))
02344             return e;
02345     }
02346 
02347     /* We matched none */
02348     return NULL;
02349 }
02350 
02358 Entry *elist_match_mask(EList * list, char *mask, uint32 ip)
02359 {
02360     char *hostmask, *nick, *user, *host;
02361     Entry *res;
02362 
02363     if (!list || !list->entries || !mask)
02364         return NULL;
02365 
02366     hostmask = sstrdup(mask);
02367 
02368     host = strchr(hostmask, '@');
02369     if (host) {
02370         *host++ = '\0';
02371         user = strchr(hostmask, '!');
02372         if (user) {
02373             *user++ = '\0';
02374             nick = hostmask;
02375         } else {
02376             nick = NULL;
02377             user = hostmask;
02378         }
02379     } else {
02380         nick = NULL;
02381         user = NULL;
02382         host = hostmask;
02383     }
02384 
02385     res = elist_match(list, nick, user, host, ip);
02386 
02387     /* Free the destroyed mask. */
02388     free(hostmask);
02389 
02390     return res;
02391 }
02392 
02400 static Entry *_elist_match_user(EList * list, User * u, boolean full)
02401 {
02402     Entry *res = NULL;
02403     char *host;
02404     uint32 ip = 0;
02405 
02406     if (!list || !list->entries || !u)
02407         return NULL;
02408 
02409     if (u->hostip == NULL) {
02410         host = host_resolve(u->host);
02411         /* we store the just resolved hostname so we don't
02412          * need to do this again */
02413         if (host) {
02414             u->hostip = sstrdup(host);
02415         }
02416     } else {
02417         host = sstrdup(u->hostip);
02418     }
02419 
02420     /* Convert the host to an IP.. */
02421     if (host)
02422         ip = str_is_ip(host);
02423 
02424     /* Match what we ve got against the lists.. */
02425     if (full)
02426         res = elist_match(list, u->nick, u->username, u->host, ip);
02427     if (!res)
02428         res = elist_match(list, u->nick, u->vident, u->vhost, 0);
02429     if (!res)
02430         res = elist_match(list, u->nick, u->username, u->chost, 0);
02431 
02432     if (host)
02433         free(host);
02434 
02435     return res;
02436 }
02437 
02438 Entry *elist_match_user(EList *list, User *u)
02439 {
02440         return _elist_match_user(list, u, false);
02441 }
02442 
02443 Entry *elist_match_user_full(EList *list, User *u, boolean full)
02444 {
02445         return _elist_match_user(list, u, full);
02446 }
02447 
02454 Entry *elist_find_mask(EList * list, char *mask)
02455 {
02456     Entry *e;
02457 
02458     if (!list || !list->entries || !mask)
02459         return NULL;
02460 
02461     for (e = list->entries; e; e = e->next) {
02462         if (!stricmp(e->mask, mask))
02463             return e;
02464     }
02465 
02466     return NULL;
02467 }
02468 
02474 long get_memuse(EList * list)
02475 {
02476     Entry *e;
02477     long mem = 0;
02478 
02479     if (!list)
02480         return 0;
02481 
02482     mem += sizeof(EList *);
02483     mem += sizeof(Entry *) * list->count;
02484     if (list->entries) {
02485         for (e = list->entries; e; e = e->next) {
02486             if (e->nick)
02487                 mem += strlen(e->nick) + 1;
02488             if (e->user)
02489                 mem += strlen(e->user) + 1;
02490             if (e->host)
02491                 mem += strlen(e->host) + 1;
02492             if (e->mask)
02493                 mem += strlen(e->mask) + 1;
02494         }
02495     }
02496 
02497     return mem;
02498 }
02499 
02500 /*************************************************************************/