Main Page | Modules | Namespace List | Class Hierarchy | Class List | File List | Class Members | File Members | Related Pages

chanserv.c

Go to the documentation of this file.
00001 
00015 /*
00016  * Copyright (c) 1996-1997 Chip Norkus
00017  * Copyright (c) 1997 Max Byrd
00018  * Copyright (c) 1997 Greg Poma
00019  * Copyright (c) 2000-2001 James Hess
00020  * All rights reserved.
00021  *
00022  * Redistribution and use in source and binary forms, with or without
00023  * modification, are permitted provided that the following conditions
00024  * are met:
00025  * 1. Redistributions of source code must retain the above copyright
00026  *    notice, this list of conditions and the following disclaimer.
00027  * 2. Redistributions in binary form must reproduce the above copyright
00028  *    notice, this list of conditions and the following disclaimer in the
00029  *    documentation and/or other materials provided with the distribution.
00030  * 3. Neither the name of the authors nor the names of its contributors
00031  *    may be used to endorse or promote products derived from this software
00032  *    without specific prior written permission.
00033  *
00034  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
00035  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00036  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00037  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
00038  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00039  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00040  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00041  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00042  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00043  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00044  * SUCH DAMAGE.
00045  */
00046 
00047 #include "services.h"
00048 #include "chanserv.h"
00049 #include "nickserv.h"
00050 #include "macro.h"
00051 #include "queue.h"
00052 #include "hash.h"
00053 #include "db.h"
00054 #include "log.h"
00055 #include "email.h"
00056 #include "interp.h"
00057 #include "chantrig.h"
00058 #include "hash/md5pw.h"
00059 
00060 #define ARGLEN 150
00061 #define DEFIFZERO(value, default) ((value) ? (value) : (default))
00062 
00066 const int TOPIC_MAX = 350;
00067 
00068 ChanList *firstChan = NULL;     
00069 ChanList *lastChan = NULL;      
00070 RegChanList *firstRegChan = NULL;   
00071 RegChanList *lastRegChan = NULL;    
00072 
00073 const char *GetAuthChKey(const char*, const char*, time_t, u_int32_t);
00074 const char *PrintPass(u_char pI[], char enc);
00075 char *urlEncode(const char *);
00076 
00077 /* void initChanData(ChanList *); */
00078 
00079 /* all these are ONLY for channel linked list funcs */
00080 void createGhostChannel(char *);
00081 void deleteGhostChannel(char *);
00082 void deleteTimedGhostChannel(char *);
00083 
00084 static cmd_return do_chanop(services_cmd_id, UserList *,
00085                             RegChanList * chan, const char *nick,
00086                             int level);
00087 static cmd_return do_chanop_del(UserList * isfrom, RegChanList * chan,
00088                                 const char *cTargetNick, int tarLevel);
00089 static cmd_return do_chanop_add(UserList * isfrom, RegChanList * chan,
00090                                 const char *cTargetNick, int tarLevel);
00091 static cmd_return do_chanop_list(UserList * isfrom, RegChanList * chan,
00092                                  const char *cTargetNick, int tarLevel);
00093 
00095 typedef struct _cs_settbl__
00096 {
00097     char *cmd;                  
00098       cmd_return(*func) (struct _cs_settbl__ *, UserList *, RegChanList *,
00099                          char **, int); 
00100     int aclvl;                  
00101     size_t xoff;                
00102     const char *optname;        
00103     unsigned long int optlen;   
00104 }
00105 cs_settbl_t;
00106 
00107 cmd_return cs_set_passwd(cs_settbl_t *, UserList *, RegChanList *, char **,
00108                          int);
00109 cmd_return cs_set_memolvl(cs_settbl_t *, UserList *, RegChanList *,
00110                           char **, int);
00111 cmd_return cs_set_founder(cs_settbl_t *, UserList *, RegChanList *,
00112                           char **, int);
00113 cmd_return cs_set_string(cs_settbl_t *, UserList *, RegChanList *, char **,
00114                          int);
00115 cmd_return cs_set_fixed(cs_settbl_t *, UserList *, RegChanList *, char **,
00116                         int);
00117 cmd_return cs_set_bool(cs_settbl_t *, UserList *, RegChanList *, char **,
00118                        int);
00119 cmd_return cs_set_mlock(cs_settbl_t *, UserList *, RegChanList *, char **,
00120                         int);
00121 cmd_return cs_set_restrict(cs_settbl_t *, UserList *, RegChanList *,
00122                            char **, int);
00123 cmd_return cs_set_topiclock(cs_settbl_t *, UserList *, RegChanList *,
00124                             char **, int);
00125 cmd_return cs_set_encrypt(cs_settbl_t *, UserList *, RegChanList *,
00126                             char **, int);
00127 
00128 // *INDENT-OFF*
00130 interp::service_cmd_t chanserv_commands[] = {
00131   /* string     function         Flags      L */
00132   { "help",     cs_help,         0, LOG_NO, 0,      2},
00133   { "info",     cs_info,         0, LOG_NO, 0,      5},
00134   { "acc*",     cs_access,       0, LOG_NO, CMD_MATCH,  3},
00135   { "chanop",   cs_chanop,       0, LOG_NO, CMD_REG,    3},
00136   { "aop",      cs_chanop,       0, LOG_NO, CMD_REG,    3},
00137   { "maop",     cs_chanop,       0, LOG_NO, CMD_REG,    3},
00138   { "msop",     cs_chanop,       0, LOG_NO, CMD_REG,    3},
00139   { "sop",      cs_chanop,       0, LOG_NO, CMD_REG,    3},
00140   { "founder",  cs_chanop,       0, LOG_NO, CMD_REG,    3},
00141   { "mfounder", cs_chanop,       0, LOG_NO, CMD_REG,    3},
00142   { "akick",    cs_akick,        0, LOG_NO, CMD_REG,    3},
00143   { "register", cs_register,     0, LOG_NO, CMD_REG,    20},
00144   { "identify", cs_identify,     0, LOG_NO, CMD_REG,    15},
00145   { "id",       cs_identify,     0, LOG_NO, CMD_REG,    15},
00146   { "addop",    cs_addop,        0, LOG_NO, CMD_REG,    10},
00147   { "addak*",   cs_addak,        0, LOG_NO, CMD_REG | CMD_MATCH,    10},
00148   { "delop",    cs_addop,        0, LOG_NO, CMD_REG,    10},
00149   { "mdeop",    cs_mdeop,        0, LOG_NO, CMD_REG,    10},
00150   { "mkick",    cs_mkick,        0, LOG_NO, CMD_REG,    10},
00151   { "wipeak*",  cs_wipeak,       0, LOG_NO, CMD_REG | CMD_MATCH,    10},
00152   { "wipeop",   cs_wipeop,   OOPER|ODMOD,   LOG_OK, CMD_REG | CMD_MATCH,    10},
00153   { "delak*",   cs_delak,        0, LOG_NO, CMD_REG | CMD_MATCH,    10},
00154   { "listop*",  cs_listop,       0, LOG_NO, CMD_REG | CMD_MATCH,    5},
00155   { "listak*",  cs_listak,       0, LOG_NO, CMD_REG | CMD_MATCH,    5},
00156   { "delete",   cs_delete,  OOPER|OCBANDEL, LOG_NO, CMD_REG,        0},
00157   { "drop",     cs_drop,         0, LOG_NO, CMD_REG,    20},
00158   { "op",       cs_op,           0, LOG_NO, 0,      20},
00159   { "deop",     cs_deop,         0, LOG_NO, CMD_REG,    20},
00160   { "clist",    cs_clist,        0, LOG_NO, CMD_REG,     5},
00161   { "banish",   cs_banish,       0, LOG_NO, CMD_REG,     0},
00162   { "clean",    cs_clean,        0, LOG_NO, CMD_REG,     3},
00163   { "close",    cs_close,        0, LOG_NO, CMD_REG,     0},
00164   { "hold",     cs_hold,         0, LOG_NO, CMD_REG,     0},
00165   { "mark",     cs_mark,         0, LOG_NO, CMD_REG,     0},
00166   { "m*lock",   cs_modelock,     0, LOG_NO, CMD_REG | CMD_MATCH,     3},
00167   { "restrict", cs_restrict,     0, LOG_NO, CMD_REG,     3},
00168   { "t*lock",   cs_topiclock,    0, LOG_NO, CMD_REG | CMD_MATCH,     3},
00169   { "set",      cs_set,          0, LOG_NO, CMD_REG,     3},
00170   { "sendpass", cs_getpass,      0, LOG_NO, CMD_REG,     5},
00171   { "getpass",  cs_getpass,      0, LOG_NO, CMD_REG,     5},
00172   { "setpass",  cs_setpass,      0, LOG_NO, CMD_REG,     5},
00173   { "setrealpass",  cs_setrealpass,  OOPER, LOG_OK, CMD_REG,     5},
00174 #ifndef NOGRPCMD
00175   { "getrealpass",cs_getrealpass,    0, LOG_NO, CMD_REG,     5},
00176 #endif
00177   { "save",     cs_save,     0, LOG_NO, CMD_REG,     15},
00178   { "unban",    cs_unban,    0, LOG_NO, CMD_REG,      5},
00179   { "invite",   cs_invite,   0, LOG_NO, CMD_REG,      3},
00180   { "list",     cs_list,     0, LOG_NO, CMD_REG,      5},
00181   { "whois",    cs_whois,    0, LOG_NO, CMD_REG,      5},
00182   { "log",  cs_log,     OOPER,  LOG_NO, CMD_REG,      1},
00183   { "dmod", cs_dmod,        OOPER|ODMOD, LOG_OK, CMD_REG,   3},
00184   { "trigger",  cs_trigger, OROOT,  LOG_DB, CMD_REG,    5},
00185   { NULL }
00186 };
00187 
00189 static struct {
00190    int lev;
00191    const char *v[3];
00192 }
00193 oplev_table[] = {
00194      { 0,   {"User",        "user",     "User"      }},
00195      { 1,   {"MAOP(1)",     "miniop(1)",    "MiniOp(1)" }},
00196      { 2,   {"MAOP(2)",     "miniop-",  "MiniOp(2)" }},
00197      { MAOP,{"MAOP",        "miniop",   "MiniOp"    }},
00198      { 4,   {"MAOP(4)",     "miniop(4)",    "MiniOp(4)" }},
00199      { AOP, {"AOP",         "aop",      "AutoOp"    }},
00200      { 6,   {"AOP(6)",      "aop(6)",   "AutoOp(6)" }},
00201      { 7,   {"AOP(7)",      "aop(7)",   "AutoOp(7)" }},
00202      { MSOP,{"MSOP",        "msop",     "MiniSop"   }},
00203      { 9,   {"MSOP(9)",     "msop(9)",  "MiniSop(9)"    }},
00204      { SOP, {"SOP",         "sop",      "SuperOp"   }},
00205      { 11,  {"SOP(11)",     "sop(11)",  "SuperOp(11)"   }},
00206      { 12,  {"SOP(12)",     "sop(12)",  "SuperOp(12)"   }},
00207      { MFOUNDER, {"MFOUNDER","mfounder","MiniFounder"   }},
00208      { 14,  {"MFOUNDER(14)","mfounder(14)", "MiniFounder(14)"}},
00209      { FOUNDER, {"FOUNDER", "founder",  "Founder"   }},
00210      { 16,  { "UNDEFINED",  "undefined",    "Undefined" }},
00211 };
00212 
00213 // *INDENT-ON*
00214 
00215 /*------------------------------------------------------------------------*/
00216 
00217 /* Handle bad password attempts -Mysid (JH) */
00228 int BadPwChan(UserList *nick, RegChanList *target) 
00229 {
00230     if (nick == NULL || target == NULL)
00231         return 0;
00232 
00233     if (nick->badpws < UCHAR_MAX)
00234         nick->badpws++;
00235     if (target->badpws < UCHAR_MAX)
00236         target->badpws++;
00237 
00238     if ((nick->badpws > CPW_TH_SENDER_1 && target->badpws > CPW_TH_TARGET_1)
00239            || (nick->badpws > CPW_TH_SENDER_2 && target->badpws > CPW_TH_TARGET_2)
00240        || (nick->badpws > CPW_TH_SENDER_3 && target->badpws > CPW_TH_TARGET_3))
00241     {
00242         sSend(":%s GLOBOPS :Possible password guess attempt %s (%u) -> %s (%u)",
00243             ChanServ, nick->nick, (u_int)nick->badpws,
00244             target->name, (u_int)target->badpws);
00245     }
00246 
00247     if (addFlood(nick, FLOODVAL_BADPW))
00248         return 1;
00249     return 0;
00250 }
00251 
00256 void GoodPwChan(UserList *nick, RegChanList *target)
00257 {
00258     if (nick == NULL || target == NULL)
00259         return;
00260 
00261     if (target->badpws > 0) {
00262         sSend(":%s NOTICE %s :%s had %d failed password attempts since last identify.",
00263             ChanServ, nick->nick, target->name, target->badpws);
00264     }
00265 
00266     target->badpws = 0;
00267 }
00268 
00269 /*------------------------------------------------------------------------*/
00270 
00271 /* The following bunch of functions all deal with channels and registered
00272  * ones, ignore them unless you need to fiddle with the linked lists for
00273  * channels. 
00274  */
00275 
00285 void addChan(ChanList * newchan)
00286 {
00287     HashKeyVal hashEnt = getHashKey(newchan->name) % CHANHASHSIZE;
00288 
00289     if (getChanData(newchan->name)) {
00290         FREE(newchan);
00291         return;
00292     }
00293 
00294     if (firstChan == NULL) {
00295         firstChan = newchan;
00296         newchan->previous = NULL;
00297     } else {
00298         lastChan->next = newchan;
00299         newchan->previous = lastChan;
00300     }
00301     lastChan = newchan;
00302     newchan->next = NULL;
00303 
00304     if (!ChanHash[hashEnt].chan) {
00305         ChanHash[hashEnt].chan = newchan;
00306         newchan->hashprev = NULL;
00307     } else {
00308         ChanHash[hashEnt].lastchan->hashnext = newchan;
00309         newchan->hashprev = ChanHash[hashEnt].lastchan;
00310     }
00311 
00312     ChanHash[hashEnt].lastchan = newchan;
00313     newchan->hashnext = NULL;
00314 }
00315 
00326 void addRegChan(RegChanList * newchan)
00327 {
00328     HashKeyVal hashEnt = getHashKey(newchan->name) % CHANHASHSIZE;
00329 
00330     if (getRegChanData(newchan->name)) {
00331         freeRegChan(newchan);
00332         return;
00333     }
00334 
00335     if (!firstRegChan) {
00336         firstRegChan = newchan;
00337         newchan->previous = NULL;
00338     } else {
00339         lastRegChan->next = newchan;
00340         newchan->previous = lastRegChan;
00341     }
00342     lastRegChan = newchan;
00343     newchan->next = NULL;
00344 
00345     if (!RegChanHash[hashEnt].chan) {
00346         RegChanHash[hashEnt].chan = newchan;
00347         newchan->hashprev = NULL;
00348     } else {
00349         RegChanHash[hashEnt].lastchan->hashnext = newchan;
00350         newchan->hashprev = RegChanHash[hashEnt].lastchan;
00351     }
00352 
00353     RegChanHash[hashEnt].lastchan = newchan;
00354     newchan->hashnext = NULL;
00355 }
00356 
00371 void addChanUser(ChanList * channel, cNickList * newnick)
00372 {
00373     HashKeyVal hashEnt;
00374     if (newnick == NULL) {
00375         return;
00376     }
00377 
00378     if (channel->firstUser == NULL) {
00379         channel->firstUser = newnick;
00380         newnick->previous = NULL;
00381     } else {
00382         channel->lastUser->next = newnick;
00383         newnick->previous = channel->lastUser;
00384     }
00385 
00386     channel->lastUser = newnick;
00387     newnick->next = NULL;
00388 
00389     hashEnt = getHashKey(newnick->person->nick) % CHANUSERHASHSIZE;
00390 
00391     if (channel->users[hashEnt].item == NULL) {
00392         channel->users[hashEnt].item = newnick;
00393         newnick->hashprev = NULL;
00394     } else {
00395         channel->users[hashEnt].lastitem->hashnext = newnick;
00396         newnick->hashprev = channel->users[hashEnt].lastitem;
00397     }
00398 
00399     channel->users[hashEnt].lastitem = newnick;
00400     newnick->hashnext = NULL;
00401 
00402 }
00403 
00404 
00405 
00418 void addChanBan(ChanList * channel, cBanList * item)
00419 {
00420     if (channel->firstBan == NULL) {
00421         channel->firstBan = item;
00422         item->previous = NULL;
00423     } else {
00424         channel->lastBan->next = item;
00425         item->previous = channel->lastBan;
00426     }
00427 
00428     channel->lastBan = item;
00429     item->next = NULL;
00430 }
00431 
00432 
00446 void addChanAkick(RegChanList * channel, cAkickList * item)
00447 {
00448     if (channel->firstAkick == NULL) {
00449         channel->firstAkick = item;
00450         item->previous = NULL;
00451     } else {
00452         channel->lastAkick->next = item;
00453         item->previous = channel->lastAkick;
00454     }
00455 
00456     channel->lastAkick = item;
00457     item->next = NULL;
00458     channel->akicks++;
00459     indexAkickItems(channel);
00460 }
00461 
00476 void addChanOp(RegChanList * channel, cAccessList * item)
00477 {
00478     HashKeyVal hashEnt;
00479 
00480     if (channel == NULL || item == NULL)
00481         return;
00482 
00483     if (channel->firstOp == NULL) {
00484         channel->firstOp = item;
00485         item->previous = NULL;
00486     } else {
00487         channel->lastOp->next = item;
00488         item->previous = channel->lastOp;
00489     }
00490 
00491     channel->lastOp = item;
00492     item->next = NULL;
00493     hashEnt = ( item->nickId.getHashKey() % OPHASHSIZE );
00494 
00495     if (channel->op[hashEnt].item == NULL) {
00496         channel->op[hashEnt].item = item;
00497         item->hashprev = NULL;
00498     } else {
00499         channel->op[hashEnt].lastitem->hashnext = item;
00500         item->hashprev = channel->op[hashEnt].lastitem;
00501     }
00502 
00503     channel->op[hashEnt].lastitem = item;
00504     item->hashnext = NULL;
00505     channel->ops++;
00506     indexOpItems(channel);
00507 }
00508 
00519 void delChan(ChanList * killme)
00520 {
00521     HashKeyVal hashEnt;
00522     ChanList *tmpchan;
00523 
00524     if (killme == NULL)
00525         return;
00526 
00527     if (killme->previous)
00528         killme->previous->next = killme->next;
00529     else
00530         firstChan = killme->next;
00531 
00532     if (killme->next)
00533         killme->next->previous = killme->previous;
00534     else
00535         lastChan = killme->previous;
00536 
00537     hashEnt = getHashKey(killme->name) % CHANHASHSIZE;
00538     for (tmpchan = ChanHash[hashEnt].chan; tmpchan;
00539          tmpchan = tmpchan->hashnext) {
00540         if (tmpchan == killme) {
00541             if (killme->hashprev)
00542                 killme->hashprev->hashnext = killme->hashnext;
00543             else
00544                 ChanHash[hashEnt].chan = killme->hashnext;
00545 
00546             if (killme->hashnext)
00547                 killme->hashnext->hashprev = killme->hashprev;
00548             else
00549                 ChanHash[hashEnt].lastchan = killme->hashprev;
00550         }
00551     }
00552 
00553     FREE(killme);
00554 }
00555 
00569 void delRegChan(RegChanList * killme)
00570 {
00571     HashKeyVal hashEnt;
00572     RegChanList *tmpchan;
00573     RegNickIdMap *ptrMap;
00574 
00575     if (killme == NULL)
00576         return;
00577 
00578     if (killme->previous)
00579         killme->previous->next = killme->next;
00580     else
00581         firstRegChan = killme->next;
00582 
00583     if (killme->next)
00584         killme->next->previous = killme->previous;
00585     else
00586         lastRegChan = killme->previous;
00587 
00588     hashEnt = getHashKey(killme->name) % CHANHASHSIZE;
00589     for (tmpchan = RegChanHash[hashEnt].chan; tmpchan;
00590          tmpchan = tmpchan->hashnext) {
00591         if (tmpchan == killme) {
00592             if (killme->hashprev)
00593                 killme->hashprev->hashnext = killme->hashnext;
00594             else
00595                 RegChanHash[hashEnt].chan = killme->hashnext;
00596 
00597             if (killme->hashnext)
00598                 killme->hashnext->hashprev = killme->hashprev;
00599             else
00600                 RegChanHash[hashEnt].lastchan = killme->hashprev;
00601         }
00602     }
00603 
00604     ptrMap = killme->founderId.getIdMap();
00605 
00606     if (ptrMap && ptrMap->nick) {
00607         ptrMap->nick->chans--;
00608     }
00609 
00610     freeRegChan(killme);
00611 }
00612 
00623 void freeRegChan(RegChanList * killme)
00624 {
00625     if (killme->id.nick)
00626         FREE(killme->id.nick);
00627     if (killme->url)
00628         FREE(killme->url);
00629     if (killme->autogreet)
00630         FREE(killme->autogreet);
00631     if (killme->markby)
00632         FREE(killme->markby);
00633     FREE(killme);
00634 }
00635 
00636 
00650 void delChanUser(ChanList * channel, cNickList * nick, int doCleanChan)
00651 {
00652     HashKeyVal hashEnt, i = 0;
00653     cNickList *tmpnick;
00654 
00655     if (nick == NULL || channel == NULL)
00656         return;
00657 
00658     if (nick->previous != NULL)
00659         nick->previous->next = nick->next;
00660     else
00661         channel->firstUser = nick->next;
00662 
00663     if (nick->next != NULL)
00664         nick->next->previous = nick->previous;
00665     else
00666         channel->lastUser = nick->previous;
00667 
00668     hashEnt = getHashKey(nick->person->nick) % CHANUSERHASHSIZE;
00669 
00670     for (tmpnick = channel->users[hashEnt].item; tmpnick;
00671          tmpnick = tmpnick->hashnext) {
00672         if (tmpnick == nick) {
00673             if (nick->hashprev != NULL)
00674                 nick->hashprev->hashnext = nick->hashnext;
00675             else
00676                 channel->users[hashEnt].item = nick->hashnext;
00677 
00678             if (nick->hashnext != NULL)
00679                 nick->hashnext->hashprev = nick->hashprev;
00680             else
00681                 channel->users[hashEnt].lastitem = nick->hashprev;
00682         }
00683     }
00684 
00685     /* delChanUser() ought to take care of this instead of redundancy */
00686     if (nick->person)
00687         for (i = 0; i < NICKCHANHASHSIZE; i++)
00688             if (nick->person->chan[i] == channel)
00689                 nick->person->chan[i] = NULL;
00690     FREE(nick);
00691 
00692     /*
00693      * if there are no more users on the channel, get it out of
00694      * out memory
00695      */
00696     if (doCleanChan && channel->firstUser == NULL)
00697         delChan(channel);
00698 }
00699 
00708 void delChanBan(ChanList * channel, cBanList * item)
00709 {
00710     if (item->previous)
00711         item->previous->next = item->next;
00712     else
00713         channel->firstBan = item->next;
00714 
00715     if (item->next)
00716         item->next->previous = item->previous;
00717     else
00718         channel->lastBan = item->previous;
00719 
00720     FREE(item);
00721 }
00722 
00723 
00732 void delChanAkick(RegChanList * channel, cAkickList * item)
00733 {
00734     if (item->previous)
00735         item->previous->next = item->next;
00736     else
00737         channel->firstAkick = item->next;
00738 
00739     if (item->next)
00740         item->next->previous = item->previous;
00741     else
00742         channel->lastAkick = item->previous;
00743 
00744     FREE(item);
00745     channel->akicks--;
00746     indexAkickItems(channel);
00747 }
00748 
00759 void delChanOp(RegChanList * channel, cAccessList * item)
00760 {
00761     HashKeyVal hashEnt;
00762     cAccessList *tmpnick;
00763 
00764     if (item->previous)
00765         item->previous->next = item->next;
00766     else
00767         channel->firstOp = item->next;
00768 
00769     if (item->next)
00770         item->next->previous = item->previous;
00771     else
00772         channel->lastOp = item->previous;
00773 
00774     hashEnt = item->nickId.getHashKey() % OPHASHSIZE;
00775     for (tmpnick = channel->op[hashEnt].item; tmpnick;
00776          tmpnick = tmpnick->hashnext) {
00777         if (tmpnick == item) {
00778             if (item->hashprev)
00779                 item->hashprev->hashnext = item->hashnext;
00780             else
00781                 channel->op[hashEnt].item = item->hashnext;
00782 
00783             if (item->hashnext)
00784                 item->hashnext->hashprev = item->hashprev;
00785             else
00786                 channel->op[hashEnt].lastitem = item->hashprev;
00787         }
00788     }
00789 
00790     FREE(item);
00791     channel->ops--;
00792     indexOpItems(channel);
00793 }
00794 
00803 ChanList *getChanData(char *name)
00804 {
00805     HashKeyVal hashEnt;
00806     if (!name)
00807         return NULL;
00808 
00809     hashEnt = getHashKey(name) % CHANHASHSIZE;
00810 
00811     if (ChanHash[hashEnt].chan == NULL)
00812         return NULL;
00813     else {
00814         ChanList *tmpchan;
00815 
00816         for (tmpchan = ChanHash[hashEnt].chan; tmpchan;
00817              tmpchan = tmpchan->hashnext) {
00818             if (!strcasecmp(tmpchan->name, name))
00819                 return tmpchan;
00820         }
00821 
00822         return NULL;
00823     }
00824 }
00825 
00835 RegChanList *getRegChanData(char *name)
00836 {
00837     HashKeyVal hashEnt;
00838     if (name == NULL)
00839         return NULL;
00840 
00841     hashEnt = getHashKey(name) % CHANHASHSIZE;
00842 
00843     if (RegChanHash[hashEnt].chan == NULL)
00844         return NULL;
00845     else {
00846         RegChanList *tmpchan;
00847         for (tmpchan = RegChanHash[hashEnt].chan; tmpchan;
00848              tmpchan = tmpchan->hashnext) {
00849             if (!strcasecmp(tmpchan->name, name))
00850                 return tmpchan;
00851         }
00852         return NULL;
00853     }
00854 }
00855 
00861 char ChanGetEnc(RegChanList *rcl)
00862 {
00863         if (rcl)
00864                 return ((rcl->flags & CENCRYPT) ? '$' : '@');
00865         return '@';
00866 }
00867 
00873 char ChanGetIdent(RegChanList *rcl)
00874 {
00875     if (rcl)
00876         return ((rcl->flags & CIDENT) ? 1 : 0);
00877     return 0;
00878 }
00879 
00891 cNickList *getChanUserData(ChanList * chan, UserList * data)
00892 {
00893     HashKeyVal hashEnt;
00894     cNickList *tmpnick;
00895 
00896     if (data == NULL || chan == NULL)
00897         return NULL;
00898 
00899     hashEnt = getHashKey(data->nick) % CHANUSERHASHSIZE;
00900 
00901     if (chan->users[hashEnt].item == NULL)
00902         return NULL;
00903 
00904     for (tmpnick = chan->users[hashEnt].item; tmpnick != NULL;
00905          tmpnick = tmpnick->hashnext) if (tmpnick->person != NULL
00906                                           && tmpnick->person == data)
00907             return tmpnick;
00908 
00909     return NULL;
00910 }
00911 
00919 #define process_op_item(x) { \
00920     if (x) { \
00921         if (checkAccessNick) \
00922         { \
00923             strncpyzt(checkAccessNick, tmpNickName, NICKLEN); \
00924         } \
00925         if (highest < (x)->uflags) { \
00926             highest = (x)->uflags; \
00927         } \
00928     } \
00929 }
00930 
00950 int getMiscChanOp(RegChanList * chan, char *nick, int id,
00951                   char *checkAccessNick)
00952 {
00953     int highest = 0;
00954     UserList *tmp;
00955     cAccessList *tmpnick;
00956     const char *tmpNickName;
00957 
00958     if (!nick || !chan)
00959         return 0;
00960 
00961     tmp = getNickData(nick);
00962 
00963     /* Channel record but not open registration? No access. */
00964     if (!tmp || ((chan->flags & (CBANISH | CCLOSE)) && !isOper(tmp)))
00965         return 0;
00966 
00967     /* First check for founder or chan id */
00968     if (isFounder(chan, tmp)) {
00969         if (checkAccessNick)
00970             strncpyzt(checkAccessNick, "founder(PW)", NICKLEN);
00971         return FOUNDER;
00972     }
00973 
00974     if (id) {
00975         /* If we have to be identified, find an exact match only */
00976         UserList *check = getNickData(nick);
00977         RegNickList *reg2;
00978         if (check && check->reg) {
00979             if (isIdentified(check, check->reg)) {
00980                 tmpnick = getChanOpData(chan, nick);
00981 
00982                 if (tmpnick) {
00983                     tmpNickName = tmpnick->nickId.getNick();
00984                     if (tmpNickName)
00985                         process_op_item(tmpnick);
00986                 }
00987             }
00988         }
00989         if (check && check->id.nick
00990             && (reg2 = getRegNickData(check->id.nick))) {
00991             if (isIdentified(check, reg2)) {
00992                 tmpnick = getChanOpData(chan, reg2->nick);
00993                 if (tmpnick) {
00994                     tmpNickName = (tmpnick->nickId.getNick());
00995                     if (tmpNickName && (tmpnick->uflags > highest))
00996                         process_op_item(tmpnick);
00997                 }
00998             }
00999         }
01000     } else {
01001         /* Otherwise, search for masks to remote nicks too */
01002         RegNickList *reg2 =
01003             tmp->id.nick ? getRegNickData(tmp->id.nick) : NULL;
01004 
01005         if (reg2 && isIdentified(tmp, reg2)
01006             && (tmpnick = getChanOpData(chan, reg2->nick))
01007             && (tmpNickName = ((tmpnick->nickId).getNick())))
01008             process_op_item(tmpnick);
01009         if (isRecognized(tmp, tmp->reg)
01010             && (tmpnick = getChanOpData(chan, nick))
01011             && (tmpNickName = ((tmpnick->nickId).getNick()))) 
01012         {
01013             process_op_item(tmpnick);
01014         } else {
01015             for (tmpnick = chan->firstOp; tmpnick; tmpnick = tmpnick->next) {
01016                 if (!(tmpNickName = (tmpnick->nickId).getNick()))
01017                     continue;
01018                 if (checkAccess
01019                     (tmp->user, tmp->host,
01020                      getRegNickData(tmpNickName)) == 1)
01021                     process_op_item(tmpnick);
01022             }
01023         }
01024     }
01025 
01026 
01027     /* Iff the don't have op access, look for an akick */
01028     if (!highest) {
01029         cAkickList *tmpak;
01030         char hostmask[HOSTLEN + USERLEN + NICKLEN];
01031         char hostmask2[HOSTLEN + USERLEN + NICKLEN];
01032 
01033         snprintf(hostmask, HOSTLEN + USERLEN + NICKLEN, "%s!%s@%s",
01034                  tmp->nick, tmp->user, tmp->host);
01035         snprintf(hostmask2, HOSTLEN + USERLEN + NICKLEN, "%s!%s@%s",
01036                  tmp->nick, tmp->user, genHostMask(tmp->host));
01037 
01038         for (tmpak = chan->firstAkick; tmpak; tmpak = tmpak->next) {
01039             if (!match(tmpak->mask, hostmask)
01040                 || !match(tmpak->mask, hostmask2)) {
01041                 highest = -1;
01042                 break;
01043             }
01044         }
01045     }
01046 
01047     /* invasive edit removed -- better method in use */
01048 
01049     /* If forced xfer then no access other than oper using DMOD/Override */
01050     if ((chan->flags & CFORCEXFER) && !opFlagged(tmp, OVERRIDE))
01051         return 0;
01052 
01053     return highest;
01054 #undef process_op_item
01055 }
01056 
01065 int getChanOp(RegChanList * chan, char *nick)
01066 {
01067     /* This is now just a wrapper to the getMiscChanop function */
01068     if (!chan || !nick)
01069         return 0;
01070 
01071     return getMiscChanOp(chan, nick, (chan->flags & CIDENT) == CIDENT,
01072                          NULL);
01073 }
01074 
01085 int getChanOpId(RegChanList * chan, char *nick)
01086 {
01087     /* Get Chanop level, but require identification */
01088     if (!chan || !nick)
01089         return 0;
01090     return getMiscChanOp(chan, nick, 1, NULL);
01091 }
01092 
01105 cAccessList *getChanOpData(const RegChanList * chan, const char *nick)
01106 {
01107     RegNickList *rNickPtr;
01108     HashKeyVal hashEnt;
01109 
01110     if (nick == NULL || chan == NULL)
01111         return NULL;
01112 
01113     rNickPtr = getRegNickData(nick);
01114 
01115     if (rNickPtr == NULL)
01116         return NULL;
01117 
01118     hashEnt = rNickPtr->regnum.getHashKey() % OPHASHSIZE;
01119 
01120     if (chan->op[hashEnt].item == NULL)
01121         return NULL;
01122     else {
01123         cAccessList *tmpnick;
01124 
01125         for (tmpnick = chan->op[hashEnt].item; tmpnick;
01126              tmpnick = tmpnick->hashnext) {
01127             if (tmpnick->nickId == rNickPtr->regnum)
01128                 return tmpnick;
01129         }
01130         return NULL;
01131     }
01132 }
01133 
01141 cBanList *getChanBan(ChanList * chan, char *ban)
01142 {
01143     cBanList *tmp;
01144 
01145     if (chan == NULL || ban == NULL)
01146         return NULL;
01147 
01148     for (tmp = chan->firstBan; tmp; tmp = tmp->next) {
01149         if (!strcasecmp(ban, tmp->ban))
01150             return tmp;
01151     }
01152 
01153     return NULL;
01154 }
01155 
01163 cAkickList *getChanAkick(RegChanList * chan, char *akick)
01164 {
01165     cAkickList *tmp;
01166 
01167     if (chan == NULL || akick == NULL)
01168         return NULL;
01169 
01170     for (tmp = chan->firstAkick; tmp; tmp = tmp->next) {
01171         if (!strcasecmp(akick, tmp->mask))
01172             return tmp;
01173     }
01174 
01175     return NULL;
01176 }
01177 
01185 void indexAkickItems(RegChanList * chan)
01186 {
01187     cAkickList *tmp;
01188     int i = 1;
01189     for (tmp = chan->firstAkick; tmp; tmp = tmp->next) {
01190         tmp->index = i;
01191         i++;
01192     }
01193 }
01194 
01202 void indexOpItems(RegChanList * chan)
01203 {
01204     cAccessList *tmp;
01205     int i = 1;
01206     for (tmp = chan->firstOp; tmp; tmp = tmp->next) {
01207         tmp->index = i;
01208         i++;
01209     }
01210 }
01211 
01218 void clearChanIdent(RegChanList * chan)
01219 {
01220     if (!chan)
01221         return;
01222     if (chan->id.nick)
01223         FREE(chan->id.nick);
01224     chan->id.nick = NULL;
01225     chan->id.idnum = RegId(0, 0);
01226     chan->id.timestamp = 0;
01227 }
01228 
01238 int isFounder(RegChanList * chan, UserList * nick)
01239 {
01240     UserList *tmp;
01241 
01242     if ((chan->flags & CFORCEXFER) && !opFlagged(nick, OVERRIDE))
01243         return 0;
01244 
01245     if (chan->id.nick && (tmp = getNickData(chan->id.nick))) {
01246         if (tmp->timestamp < chan->id.timestamp
01247             && tmp->idnum == chan->id.idnum
01248             && !strcasecmp(nick->nick, chan->id.nick)) return 1;
01249         else if (!strcasecmp(nick->nick, chan->id.nick))
01250             clearChanIdent(chan);
01251     } else if (chan->id.nick)
01252         clearChanIdent(chan);
01253 
01254     if (nick->reg && (chan->founderId == nick->reg->regnum)
01255         && isRecognized(nick, nick->reg) && chan->facc)
01256         return 1;
01257     else
01258         return 0;
01259 }
01260 
01266 int is_sn_chan(char *ch_name)
01267 {
01268     if (!ch_name || !*ch_name)
01269         return 0;
01270 
01272     if (!strcasecmp(ch_name, PLUSLCHAN) || !strcasecmp(ch_name, LOGCHAN))
01273         return 1;
01274     return 0;
01275 }
01276 
01284 void initRegChanData(RegChanList * chan)
01285 {
01286     long i;
01287 
01288     chan->memolevel = 3;
01289     for (i = 0; i < OPHASHSIZE; i++) {
01290         chan->op[i].item = NULL;
01291         chan->op[i].lastitem = NULL;
01292     }
01293     chan->url = NULL;
01294 }
01295 
01307 void sendToChanOps(ChanList * chan, char *format, ...)
01308 {
01309     char buffer[IRCBUF];
01310     va_list stuff;
01311 
01312     if (!chan || (chan->reg && chan->reg->flags & CQUIET))
01313         return;
01314 
01315     va_start(stuff, format);
01316     vsnprintf(buffer, sizeof(buffer), format, stuff);
01317     va_end(stuff);
01318 
01319     sSend(":%s NOTICE @%s :(Ops:%s) %s", ChanServ, chan->name, chan->name,
01320           buffer);
01321 }
01322 
01323 
01335 void sendToChanOpsAlways(ChanList * chan, char *format, ...)
01336 {
01337     char buffer[IRCBUF];
01338     va_list stuff;
01339 
01340     va_start(stuff, format);
01341     vsnprintf(buffer, sizeof(buffer), format, stuff);
01342     va_end(stuff);
01343 
01344     sSend(":%s NOTICE @%s :(Ops:%s) %s", ChanServ, chan->name, chan->name,
01345           buffer);
01346 }
01347 
01371 void banKick(ChanList * chan, UserList * nick, char *format, ...)
01372 {
01373     char user[USERLEN], host[HOSTLEN], theirmask[USERLEN + HOSTLEN + 3];
01374     char buffer[256];
01375     va_list stuff;
01376     cBanList *tmpban;
01377     cNickList *channick;
01378 
01379     strncpyzt(user, nick->user, USERLEN);
01380     strncpyzt(host, nick->host, HOSTLEN);
01381 
01382     mask(user, host, 1, theirmask);
01383 
01384     sSend(":%s MODE %s +b %s", ChanServ, chan->name, theirmask);
01385     tmpban = (cBanList *) oalloc(sizeof(cBanList));
01386     strncpyzt(tmpban->ban, theirmask, sizeof(tmpban->ban));
01387     addChanBan(chan, tmpban);
01388 #ifdef CDEBUG
01389     sSend(":%s PRIVMSG " DEBUGCHAN " :Added ban %s on %s", ChanServ,
01390           theirmask, chan->name);
01391 #endif
01392     va_start(stuff, format);
01393     vsnprintf(buffer, sizeof(buffer), format, stuff);
01394     va_end(stuff);
01395 
01396     channick = getChanUserData(chan, nick);
01397 
01398     sSend(":%s KICK %s %s :%s", ChanServ, chan->name, nick->nick, buffer);
01399     delChanUser(chan, channick, 1);
01400 }
01401 
01411 char *initModeStr(ChanList * chan)
01412 {
01413     char mode[IRCBUF];
01414     char parastr[IRCBUF] = "";
01415     static char mstr[IRCBUF];
01416     int l;
01417 
01418 
01419     mstr[0] = '\0';
01420     if (!chan || !chan->reg)
01421         return mstr;
01422     *parastr = '\0';
01423 
01424     makeModeLockStr(chan->reg, mode);
01425     if (mode[0] != 0) {
01426         if ((chan->reg->mlock & PM_L)) {
01427             l = strlen(parastr);
01428             snprintf(parastr + l, IRCBUF - l, " %ld", chan->reg->limit);
01429         }
01430 
01431         if (((chan->reg->mlock & PM_K) || (chan->reg->mlock & MM_K)) &&
01432                      ((l = strlen(parastr)) < IRCBUF)) 
01433         {
01434             snprintf(parastr + l, IRCBUF - l, " %s", chan->reg->key
01435                     && *chan->reg->key ? chan->reg->key : "*");
01436         }
01437     }
01438     if (*parastr)
01439         snprintf(mstr, IRCBUF, "%s%s", mode, parastr);
01440     else
01441         snprintf(mstr, IRCBUF, "%s", mode);
01442     return mstr;
01443 }
01444 
01453 void createGhostChannel(char *chan)
01454 {
01455     RegChanList *channel;
01456 
01457     if ((channel = getRegChanData(chan)))
01458         channel->flags |= CCSJOIN;
01459 
01460     sSend(":%s JOIN %s", ChanServ, chan);
01461     sSend(":%s MODE %s +i 1", ChanServ, chan);
01462 }
01463 
01473 void deleteGhostChannel(char *chan)
01474 {
01475     RegChanList *channel;
01476 
01477     if ((channel = getRegChanData(chan)))
01478         channel->flags &= ~CCSJOIN;
01479 
01480     sSend(":%s PART %s :Ahhh, all finished here!", ChanServ, chan);
01481 }
01482 
01488 void deleteTimedGhostChannel(char *chan)
01489 {
01490     deleteGhostChannel(chan);
01491     FREE(chan);
01492 }
01493 
01494 
01512 void makeModeLockStr(RegChanList * chan, char *modelock)
01513 {
01514     char *p = modelock;
01515     bzero(modelock, 20);
01516 
01517     *p++ = '+';
01518     if (chan->mlock & PM_I)
01519         *p++ = 'i';
01520     if (chan->mlock & PM_L)
01521         *p++ = 'l';
01522     if (chan->mlock & PM_K)
01523         *p++ = 'k';
01524     if (chan->mlock & PM_M)
01525         *p++ = 'm';
01526     if (chan->mlock & PM_N)
01527         *p++ = 'n';
01528     if (chan->mlock & PM_P)
01529         *p++ = 'p';
01530     if (chan->mlock & PM_S)
01531         *p++ = 's';
01532     if (chan->mlock & PM_T)
01533         *p++ = 't';
01534     if (chan->mlock & PM_H)
01535         *p++ = 'H';
01536     if (chan->mlock & PM_C)
01537         *p++ = 'c';
01538 
01539     if (*(p - 1) == '+')        /* If the previous char is '+' */
01540         p = modelock;           /* Then erase it */
01541 
01542     if (chan->mlock >= 257) {   /* If there is a minus mlock */
01543         *p++ = '-';
01544 
01545         if (chan->mlock & MM_I)
01546             *p++ = 'i';
01547         if (chan->mlock & MM_L)
01548             *p++ = 'l';
01549         if (chan->mlock & MM_K)
01550             *p++ = 'k';
01551         if (chan->mlock & MM_M)
01552             *p++ = 'm';
01553         if (chan->mlock & MM_N)
01554             *p++ = 'n';
01555         if (chan->mlock & MM_P)
01556             *p++ = 'p';
01557         if (chan->mlock & MM_S)
01558             *p++ = 's';
01559         if (chan->mlock & MM_T)
01560             *p++ = 't';
01561         if (chan->mlock & MM_H)
01562             *p++ = 'H';
01563         if (chan->mlock & MM_C)
01564             *p++ = 'c';
01565     }
01566 
01567     if (p == modelock)
01568         strcpy(modelock, "(none)");
01569     else
01570         *p++ = 0;
01571 }
01572 
01584 void addUserToChan(UserList * nick, char *channel)
01585 {
01586     char chan[CHANLEN], *badTemp = NULL;
01587     int a = 0;
01588     ChanList *tmp;
01589     cNickList *person;
01590 
01591     dlogEntry("addUserToChan(%s, %s)", nick, channel);
01592 
01593     if (nick == NULL || channel == NULL || channel[0] == '\0')
01594         return;
01595 
01596     if (*channel == ':')
01597         channel++;
01598 
01599     while (*channel) {
01600         bzero(chan, CHANLEN);
01601         a = 0;
01602         badTemp = NULL;
01603 
01604         while (*channel != ',' && *channel != 0) {
01605             if (a < (CHANLEN - 1))
01606                 chan[a] = *channel;
01607             else if (!badTemp)
01608                 badTemp = channel;
01609             a++;
01610             channel++;
01611         }
01612 
01613         if (badTemp && a >= CHANLEN) {
01614             chan[CHANLEN - 1] = '\0';
01615             sSend(":%s GLOBOPS :%s Joining channel > CHANLEN (%s%s)", ChanServ, nick->nick, chan, badTemp ? badTemp : "");
01616             continue;
01617         }
01618 
01619         chan[a] = 0;
01620 
01621         if (*channel)
01622             channel++;
01623 
01624         if (chan[0] == '0')
01625             remFromAllChans(nick);
01626 
01627         else if (chan[0] != '+') {
01628 
01629             tmp = getChanData(chan);
01630             if (tmp == NULL)
01631                 tmp = (ChanList *) oalloc(sizeof(ChanList));
01632 
01633             if (tmp->firstUser == NULL) {
01634                 strncpyzt(tmp->name, chan, CHANLEN);
01635                 person = (cNickList *) oalloc(sizeof(cNickList));
01636                 person->person = nick;
01637                 person->op = 0;
01638 
01639                 addChan(tmp);
01640                 addChanUser(tmp, person);
01641 
01643                 for (a = 0; a < NICKCHANHASHSIZE; a++) {
01644                     if (!nick->chan[a]) {
01645                         nick->chan[a] = tmp;
01646                         break;
01647                     }
01648                 }
01649 #ifdef CDEBUG
01650                 sSend(":%s PRIVMSG " DEBUGCHAN
01651                       " :Created channel %s, added %s to it", ChanServ,
01652                       chan, nick->nick);
01653 #endif
01654                 tmp->reg = getRegChanData(chan);
01655 
01656                 if (tmp->reg) {
01657                     char mode[20];
01658                     char themask[NICKLEN + USERLEN + HOSTLEN + 3];
01659 
01660                     a = getChanOp(tmp->reg, nick->nick);
01661 
01662                     if (a == -1) {
01663                         cAkickList *blah;
01664                         char userhost[NICKLEN + USERLEN + HOSTLEN + 3];
01665                         char userhostM[NICKLEN + USERLEN + HOSTLEN + 3];
01666 
01667                         snprintf(userhost, sizeof(userhost),
01668                                  "%s!%s@%s", nick->nick, nick->user,
01669                                  nick->host);
01670                         snprintf(userhostM, sizeof(userhostM),
01671                                  "%s!%s@%s", nick->nick, nick->user,
01672                                  genHostMask(nick->host));
01673 
01674                         for (blah = tmp->reg->firstAkick; blah;
01675                              blah = blah->next) {
01676                             strncpyzt(themask, blah->mask,
01677                                       sizeof(themask));
01678                             if (!match(blah->mask, userhost)
01679                                 || !match(blah->mask, userhostM)) {
01680                                 cBanList *newban =
01681                                     (cBanList *) oalloc(sizeof(cBanList));
01682 
01683                                 sSend(":%s MODE %s +b %s", ChanServ,
01684                                       tmp->reg->name, themask);
01685                                 strncpyzt(newban->ban, themask, sizeof(newban->ban));
01686                                 addChanBan(tmp, newban);
01687                                 break;
01688                             }
01689                         }
01690 
01691                         createGhostChannel(tmp->name);
01692                         timer(10, deleteTimedGhostChannel,
01693                               strdup(tmp->name));
01694                         sSend
01695                             (":%s KICK %s %s :You have been permanently banned from this channel.",
01696                              ChanServ, tmp->reg->name, nick->nick);
01697                         delChanUser(tmp, person, 1);
01698                         return;
01699                     }
01700 
01701                     if ((tmp->reg->flags & (CBANISH | CCLOSE))
01702                         && !isOper(nick)) {
01703                         sSend(":%s MODE %s +isnt-o %s", ChanServ,
01704                               tmp->name, nick->nick);
01705                         createGhostChannel(tmp->name);
01706                         timer(30, deleteTimedGhostChannel,
01707                               strdup(tmp->name));
01708                         banKick(tmp, nick,
01709                                 "this channel is closed/banished");
01710                         return;
01711                     }
01712                     if (a < tmp->reg->restrictlevel && (a < MFOUNDER)) {
01713                         createGhostChannel(tmp->name);
01714                         timer(30, deleteTimedGhostChannel,
01715                               strdup(tmp->name));
01716                         banKick(tmp, nick,
01717                                 "this channel is restricted to level %i and above",
01718                                 tmp->reg->restrictlevel);
01719                         return;
01720                     }
01721                     if (!a) {
01722                         if (tmp->reg->mlock & PM_I) {
01723                             createGhostChannel(tmp->name);
01724                             timer(30, deleteTimedGhostChannel,
01725                                   strdup(tmp->name));
01726                             banKick(tmp, nick,
01727                                     "you must be invited to join this channel.");
01728                             return;
01729                         }
01730                         if ((tmp->reg->mlock & PM_K)
01731                             && !(tmp->reg->flags & CCSJOIN)) {
01732                             char *p;
01733                             createGhostChannel(tmp->name);
01734                             timer(30, deleteTimedGhostChannel,
01735                                   strdup(tmp->name));
01736                             sSend(":%s MODE %s +i-o %s", ChanServ,
01737                                   tmp->name, nick->nick);
01738                             sSend(":%s KICK %s %s :%s", ChanServ,
01739                                   tmp->name, nick->nick,
01740                                   "this is a keyed channel.");
01741                             sSend(":%s MODE %s -i", ChanServ, tmp->name);
01742                             if ((p = initModeStr(tmp))) {
01743                                 sSend(":%s MODE %s %s", ChanServ,
01744                                       tmp->name, p);
01745 #ifdef IRCD_MLOCK
01746                                 sSend(":%s MLOCK %s %s", ChanServ,
01747                                       tmp->name, p);
01748 #endif
01749                             }
01750                             remUserFromChan(nick, tmp->name);
01751                             return;
01752                         }
01753                     }
01754                     if ((a < AOP) && (a >= MAOP)) {
01755                         sSend(":%s MODE %s +v %s", ChanServ, tmp->name,
01756                               nick->nick);
01757                         person->op |= CHANVOICE;
01758                         tmp->reg->timestamp = CTime;
01759                         if (tmp && tmp->name
01760                             && !strcasecmp(tmp->name, HELPOPS_CHAN)
01761                             && (getChanOp(tmp->reg, nick->nick) >= 4)) {
01762                             sSend(":ChanServ MODE %s :+h", nick->nick);
01763                             nick->oflags |= NISHELPOP;
01764                         }
01765                     } else if (a && a >= AOP) {
01766                         sSend(":%s MODE %s +o %s", ChanServ, tmp->name,
01767                               nick->nick);
01768                         person->op |= CHANOP;
01769                         tmp->reg->timestamp = CTime;
01770                         if (tmp && tmp->name
01771                             && !strcasecmp(tmp->name, HELPOPS_CHAN)) {
01772                             sSend(":ChanServ MODE %s :+h", nick->nick);
01773                             nick->oflags |= NISHELPOP;
01774                         }
01775                     }
01776                     if (tmp->reg->flags & CKTOPIC)
01777                         if (tmp->reg->topic)
01778                             sSend(":%s TOPIC %s %s %lu :%s", ChanServ,
01779                                   tmp->name, tmp->reg->tsetby,
01780                                   tmp->reg->ttimestamp, tmp->reg->topic);
01781                     if (tmp->reg->autogreet)
01782                         sSend(":%s NOTICE %s :[%s] %s", ChanServ,
01783                               nick->nick, tmp->name, tmp->reg->autogreet);
01784 
01785                     makeModeLockStr(tmp->reg, mode);
01786 
01787                     if (mode[0] != 0) {
01788                         char parastr[IRCBUF * 2];
01789 
01790                         *parastr = '\0';
01791                         if ((tmp->reg->mlock & PM_L))
01792                             sprintf(parastr + strlen(parastr), " %ld",
01793                                     tmp->reg->limit);
01794                         if ((tmp->reg->mlock & PM_K)
01795                             || (tmp->reg->mlock & MM_K)) sprintf(parastr +
01796                                                                  strlen
01797                                                                  (parastr),
01798                                                                  " %s",
01799                                                                  tmp->reg->
01800                                                                  key
01801                                                                  && *tmp->
01802                                                                  reg->
01803                                                                  key ?
01804                                                                  tmp->reg->
01805                                                                  key :
01806                                                                  "*");
01807                         sSend(":%s MODE %s %s%s", ChanServ, tmp->name,
01808                               mode, parastr);
01809 #ifdef IRCD_MLOCK
01810                         sSend(":%s MLOCK %s %s%s", ChanServ, tmp->name,
01811                               mode, parastr);
01812 #endif
01813                     }
01814                 }
01815             } else {
01816                 person = (cNickList *) oalloc(sizeof(cNickList));
01817                 person->person = nick;
01818                 person->op = 0;
01819                 addChanUser(tmp, person);
01820 
01821                 for (a = 0; a < NICKCHANHASHSIZE; a++) {
01822                     if (!nick->chan[a]) {
01823                         nick->chan[a] = tmp;
01824                         break;
01825                     }
01826                 }
01827                 if (tmp->reg) {
01828                     char themask[NICKLEN + USERLEN + HOSTLEN + 3];
01829 
01830                     a = getChanOp(tmp->reg, nick->nick);
01831                     if (a == -1) {
01832                         cAkickList *blah;
01833                         char userhost[NICKLEN + USERLEN + HOSTLEN + 3];
01834                         char userhostM[NICKLEN + USERLEN + HOSTLEN + 3];
01835 
01836                         snprintf(userhost, sizeof(userhost),
01837                                  "%s!%s@%s", nick->nick, nick->user,
01838                                  nick->host);
01839                         snprintf(userhostM, sizeof(userhostM),
01840                                  "%s!%s@%s", nick->nick, nick->user,
01841                                  genHostMask(nick->host));
01842 
01843                         for (blah = tmp->reg->firstAkick; blah;
01844                              blah = blah->next) {
01845                             strncpyzt(themask, blah->mask,
01846                                       sizeof(themask));
01847                             if (!match(blah->mask, userhost)
01848                                 || !match(blah->mask, userhostM)) {
01849                                 cBanList *newban =
01850                                     (cBanList *) oalloc(sizeof(cBanList));
01851                                 sSend(":%s MODE %s +b %s", ChanServ, chan,
01852                                       themask);
01853                                 strncpyzt(newban->ban, themask, sizeof(newban->ban));
01854                                 addChanBan(tmp, newban);
01855                                 break;
01856                             }
01857                         }
01858                         sSend
01859                             (":%s KICK %s %s :You have been permanently banned from this channel.",
01860                              ChanServ, chan, nick->nick);
01861                         delChanUser(tmp, person, 1);
01862                         return;
01863                     }
01864                     if ((tmp->reg->flags & (CBANISH | CCLOSE))
01865                         && !isOper(nick)) {
01866                         banKick(tmp, nick,
01867                                 "this channel is closed/banished");
01868                         return;
01869                     }
01870                     if (a < tmp->reg->restrictlevel) {
01871                         banKick(tmp, nick,
01872                                 "this channel is restricted to level %i+",
01873                                 tmp->reg->restrictlevel);
01874                         return;
01875                     }
01876                     if ((a >= MAOP) && (a < AOP)) {
01877                         sSend(":%s MODE %s +v %s", ChanServ, tmp->name,
01878                               nick->nick);
01879                         person->op |= CHANVOICE;
01880                         tmp->reg->timestamp = CTime;
01881                         if (tmp && tmp->name && tmp->reg
01882                             && !strcasecmp(tmp->name, HELPOPS_CHAN)
01883                             && (getChanOp(tmp->reg, nick->nick) >= 4)) {
01884                             sSend(":ChanServ MODE %s :+h", nick->nick);
01885                             nick->oflags |= NISHELPOP;
01886                         }
01887                     } else if (a && a >= AOP) {
01888                         sSend(":%s MODE %s +o %s", ChanServ, tmp->name,
01889                               nick->nick);
01890                         person->op |= CHANOP;
01891                         tmp->reg->timestamp = CTime;
01892                         if (tmp && tmp->name
01893                             && !strcasecmp(tmp->name, HELPOPS_CHAN)) {
01894                             sSend(":ChanServ MODE %s :+h", nick->nick);
01895                             nick->oflags |= NISHELPOP;
01896                         }
01897                     }
01898                     if (tmp->reg->autogreet)
01899                         sSend(":%s NOTICE %s :[%s] %s", ChanServ,
01900                               nick->nick, tmp->name, tmp->reg->autogreet);
01901                 }
01902 #ifdef CDEBUG
01903                 sSend(":%s PRIVMSG " DEBUGCHAN " :Added %s to channel %s",
01904                       ChanServ, nick->nick, chan);
01905 #endif
01906             }
01907         }
01908     }
01909 }
01910 
01918 void remUserFromChan(UserList * nick, char *channel)
01919 {
01920     char chan[CHANLEN], *badTemp = NULL;
01921     int a;
01922     ChanList *tmp;
01923     cNickList *person;
01924 
01925     if (*channel == ':')
01926         channel++;
01927 
01928     while (*channel) {
01929         bzero(chan, CHANLEN);
01930         badTemp = NULL;
01931         a = 0;
01932 
01933         while (*channel != ',' && *channel != 0) {
01934             if (a < (CHANLEN - 1))
01935                 chan[a] = *channel;
01936             else if (!badTemp)
01937                 badTemp = channel;
01938             a++;
01939             channel++;
01940         }
01941         chan[a] = 0;
01942         a = 0;
01943 
01944         if (badTemp) {
01945             continue;
01946         }
01947 
01948         if (*channel)
01949             channel++;
01950 
01951         if (chan[0] != '+') {
01952             tmp = getChanData(chan);
01953 
01954             if (tmp != NULL) {
01955                 person = getChanUserData(tmp, nick);
01956                 if (person == NULL) {
01957                     /* 
01958                      * I guess killing a user with that PART hack
01959                      * in here would be bad :)
01960                      */
01961                     /*nDesynch(nick->nick, "PART"); */
01962                     return;
01963                 }
01964 
01965                 if (tmp && (nick->oflags & NISHELPOP)
01966                     && !(nick->oflags & NISOPER) && tmp->name
01967                     && !strcasecmp(tmp->name, HELPOPS_CHAN)
01968                     && tmp->reg && (getChanOp(tmp->reg, nick->nick) <= 5)) {
01969                     sSend(":ChanServ MODE %s :-h", nick->nick);
01970                     nick->oflags &= ~NISHELPOP;
01971                 }
01972 
01973                 for (a = 0; a < NICKCHANHASHSIZE; a++) {
01974                     if (nick->chan[a] == tmp) {
01975                         nick->chan[a] = NULL;
01976                         break;
01977                     }
01978                 }
01979                 delChanUser(tmp, person, 1);
01980                 tmp = NULL;
01981 #ifdef CDEBUG
01982                 sSend(":%s PRIVMSG " DEBUGCHAN " :Removed %s from %s",
01983                       ChanServ, nick->nick, chan);
01984 #endif
01985 
01986             }
01987         }
01988     }
01989 }
01990 
01996 void remFromAllChans(UserList * nick)
01997 {
01998     int i, a;
01999 
02000 #ifdef CDEBUG
02001     sSend(":%s PRIVMSG " DEBUGCHAN " :Clearing %s from ALL channels",
02002           ChanServ, nick->nick);
02003 #endif
02004     if (nick == NULL)
02005         return;
02006 
02007     for (i = 0; i < NICKCHANHASHSIZE; i++) {
02008         if (nick->chan[i]) {
02009             cNickList *ctmp = getChanUserData(nick->chan[i], nick);
02010 
02011             if (ctmp)
02012                 delChanUser(nick->chan[i], ctmp, 1);
02013             nick->chan[i] = NULL;
02014 
02015             /* purge any duplicates */
02016             for (a = i; a < NICKCHANHASHSIZE; a++)
02017                 if (nick->chan[i] && nick->chan[a] == nick->chan[i])
02018                     nick->chan[a] = NULL;
02019         }
02020     }
02021 }
02022 
02029 void changeNickOnAllChans(UserList * oldnick, UserList * newnick)
02030 {
02031     int i;
02032     cNickList *tmpnick, *addnick;
02033 
02034 #ifdef CDEBUG
02035     sSend(":%s PRIVMSG " DEBUGCHAN " :Changing nick for %s->%s", ChanServ,
02036           oldnick->nick, newnick->nick);
02037 #endif
02038 
02039     for (i = 0; i < NICKCHANHASHSIZE; i++) {
02040         if (oldnick->chan[i]) {
02041             addnick = (cNickList *) oalloc(sizeof(cNickList));
02042             tmpnick = getChanUserData(oldnick->chan[i], oldnick);
02043             if (!tmpnick)
02044                 FREE(addnick);
02045             else {
02046                 addnick->op = tmpnick->op;
02047                 addnick->person = newnick;
02048                 newnick->chan[i] = oldnick->chan[i];
02049                 addChanUser(newnick->chan[i], addnick);
02050                 delChanUser(oldnick->chan[i], tmpnick, 1);
02051             }
02052         } else
02053             newnick->chan[i] = NULL;
02054     }
02055 }
02056 
02057 
02065 void setChanMode(char **args, int numargs)
02066 {
02067     int on = 1, onarg = 4, i = 0;
02068     ChanList *tmp = getChanData(args[2]);
02069     cBanList *tmpban;
02070     cNickList *tmpnick;
02071     char mode[20];
02072 
02073     if (tmp == NULL)
02074         return;
02075 
02076     if (numargs < 3)
02077         return;
02078 
02079     for (i = 0; args[3][i]; i++) {
02080         switch (args[3][i]) {
02081         case '+':
02082             on = 1;
02083             break;
02084         case '-':
02085             on = 0;
02086             break;
02087 
02088         case 'b':
02089             if (on) {
02090                 tmpban = (cBanList *) oalloc(sizeof(cBanList));
02091                 strncpyzt(tmpban->ban, args[onarg], sizeof(tmpban->ban));
02092                 addChanBan(tmp, tmpban);
02093 #ifdef CDEBUG
02094                 sSend(":%s PRIVMSG " DEBUGCHAN " :Added ban %s on %s",
02095                       ChanServ, args[onarg], tmp->name);
02096 #endif
02097             } else {
02098                 tmpban = getChanBan(tmp, args[onarg]);
02099                 if (tmpban) {
02100 #ifdef CDEBUG
02101                     sSend(":%s PRIVMSG " DEBUGCHAN
02102                           " :Removed ban %s on %s", ChanServ, args[onarg],
02103                           tmp->name);
02104 #endif
02105                     delChanBan(tmp, tmpban);
02106                 }
02107             }
02108             onarg++;
02109             break;
02110 
02111         case 'i':
02112             if (on) {
02113                 tmp->modes |= PM_I;
02114 #ifdef CDEBUG
02115                 sSend(":%s PRIVMSG " DEBUGCHAN " :Set %s +i", ChanServ,
02116                       tmp->name);
02117 #endif
02118             } else {
02119                 tmp->modes &= ~(PM_I);
02120 #ifdef CDEBUG
02121                 sSend(":%s PRIVMSG " DEBUGCHAN " :Set %s -i", ChanServ,
02122                       tmp->name);
02123 #endif
02124             }
02125             break;
02126         case 'l':
02127             if (on) {
02128                 tmp->modes |= PM_L;
02129 #ifdef CDEBUG
02130                 sSend(":%s PRIVMSG " DEBUGCHAN " :Set %s +l %lu", ChanServ,
02131                       tmp->name, tmp->reg->limit);
02132 #endif
02133             } else {
02134                 tmp->modes &= ~(PM_L);
02135 #ifdef CDEBUG
02136                 sSend(":%s PRIVMSG " DEBUGCHAN " :Set %s -l", ChanServ,
02137                       tmp->name);
02138 #endif
02139             }
02140             break;
02141         case 'k':
02142             if (on) {
02143                 tmp->modes |= PM_K;
02144 #ifdef CDEBUG
02145                 sSend(":%s PRIVMSG " DEBUGCHAN " :Set %s +k %s", ChanServ,
02146                       tmp->name, tmp->reg->key);
02147 #endif
02148             } else {
02149                 tmp->modes &= ~(PM_K);
02150 #ifdef CDEBUG
02151                 sSend(":%s PRIVMSG " DEBUGCHAN " :Set %s -k %s", ChanServ,
02152                       tmp->reg->name, args[onarg]);
02153 #endif
02154             }
02155             onarg++;
02156             break;
02157         case 'm':
02158             if (on) {
02159                 tmp->modes |= PM_M;
02160 #ifdef CDEBUG
02161                 sSend(":%s PRIVMSG " DEBUGCHAN " :Set %s +m", ChanServ,
02162                       tmp->name);
02163 #endif
02164             } else {
02165                 tmp->modes &= ~(PM_M);
02166 #ifdef CDEBUG
02167                 sSend(":%s PRIVMSG " DEBUGCHAN " :Set %s -m", ChanServ,
02168                       tmp->name);
02169 #endif
02170             }
02171             break;
02172         case 'n':
02173             if (on) {
02174                 tmp->modes |= PM_N;
02175 #ifdef CDEBUG
02176                 sSend(":%s PRIVMSG " DEBUGCHAN " :Set %s +n", ChanServ,
02177                       tmp->name);
02178 #endif
02179             } else {
02180                 tmp->modes &= ~(PM_N);
02181 #ifdef CDEBUG
02182                 sSend(":%s PRIVMSG " DEBUGCHAN " :Set %s -m", ChanServ,
02183                       tmp->name);
02184 #endif
02185             }
02186             break;
02187         case 'o':
02188             if (on) {
02189                 tmpnick = getChanUserData(tmp, getNickData(args[onarg]));
02190                 if (!tmpnick) {
02191                 }
02192                 /* do nothing */
02193                 else if (tmp->reg) {
02194                     if (getChanOp(tmp->reg, args[onarg]) < 5
02195                         && !(tmp->reg->flags & CFORCEXFER)
02196                         && (tmp->reg->flags & COPGUARD
02197                             || tmp->firstUser == tmp->lastUser)) {
02198                         sSend
02199                             (":%s NOTICE %s :You are not allowed ops in %s",
02200                              ChanServ, args[onarg], tmp->name);
02201                         sSend(":%s MODE %s -o %s", ChanServ, tmp->name,
02202                               args[onarg]);
02203                     } else {
02204                         tmpnick->op |= CHANOP;
02205 #ifdef CDEBUG
02206                         sSend(":%s PRIVMSG " DEBUGCHAN " :Oped %s in %s",
02207                               ChanServ, args[onarg], tmp->name);
02208 #endif
02209                     }
02210                 } else {
02211                     tmpnick->op |= CHANOP;
02212 #ifdef CDEBUG
02213                     sSend(":%s PRIVMSG " DEBUGCHAN " :Oped %s in %s",
02214                           ChanServ, args[onarg], tmp->name);
02215 #endif
02216                 }
02217             } else {
02218                 tmpnick = getChanUserData(tmp, getNickData(args[onarg]));
02219                 if (tmpnick) {
02220                     if (tmp->reg) {
02221                         int t_lev = getChanOp(tmp->reg, args[onarg]);
02222                         int s_lev = getChanOp(tmp->reg, (args[0] /*+ 1*/));
02223 
02224                         if ((t_lev > s_lev) && (t_lev >= AOP)
02225                             && tmp->reg->flags & CPROTOP)
02226                             sSend(":%s MODE %s +o %s", ChanServ, tmp->name,
02227                                   args[onarg]);
02228                         else
02229                             tmpnick->op &= ~CHANOP;
02230                     } else
02231                         tmpnick->op &= ~CHANOP;
02232 #ifdef CDEBUG
02233                     sSend(":%s PRIVMSG " DEBUGCHAN " :DeOped %s in %s",
02234                           ChanServ, args[onarg], tmp->name);
02235 #endif
02236                 }
02237             }
02238             onarg++;
02239             break;
02240         case 'p':
02241             if (on) {
02242                 tmp->modes |= PM_P;
02243 #ifdef CDEBUG
02244                 sSend(":%s PRIVMSG " DEBUGCHAN " :Set %s +p", ChanServ,
02245                       tmp->name);
02246 #endif
02247             } else {
02248                 tmp->modes &= ~(PM_P);
02249 #ifdef CDEBUG
02250                 sSend(":%s PRIVMSG " DEBUGCHAN " :Set %s -p", ChanServ,
02251                       tmp->name);
02252 #endif
02253             }
02254             break;
02255         case 's':
02256             if (on) {
02257                 tmp->modes |= PM_S;
02258 #ifdef CDEBUG
02259                 sSend(":%s PRIVMSG " DEBUGCHAN " :Set %s +s", ChanServ,
02260                       tmp->name);
02261 #endif
02262             } else {
02263                 tmp->modes &= ~(PM_S);
02264 #ifdef CDEBUG
02265                 sSend(":%s PRIVMSG " DEBUGCHAN " :Set %s -s", ChanServ,
02266                       tmp->name);
02267 #endif
02268             }
02269             break;
02270         case 't':
02271             if (on) {
02272                 tmp->modes |= PM_T;
02273 #ifdef CDEBUG
02274                 sSend(":%s PRIVMSG " DEBUGCHAN " :Set %s +t", ChanServ,
02275                       tmp->name);
02276 #endif
02277             } else {
02278                 tmp->modes &= ~(PM_T);
02279 #ifdef CDEBUG
02280                 sSend(":%s PRIVMSG " DEBUGCHAN " :Set %s -t", ChanServ,
02281                       tmp->name);
02282 #endif
02283             }
02284             break;
02285 
02286         case 'H':
02287             if (on)
02288                 tmp->modes |= PM_H;
02289             else
02290                 tmp->modes &= ~PM_H;
02291             break;
02292 
02293         case 'c':
02294             if (on) tmp->modes |= PM_C;
02295             else tmp->modes &= ~PM_C;
02296             break;
02297 
02298         case 'v':
02299             if (on) {
02300                 tmpnick = getChanUserData(tmp, getNickData(args[onarg]));
02301                 if (tmpnick) {
02302                     tmpnick->op |= CHANVOICE;
02303 #ifdef CDEBUG
02304                     sSend(":%s PRIVMSG " DEBUGCHAN " :Oped %s in %s",
02305                           ChanServ, args[onarg], tmp->name);
02306 #endif
02307                 }
02308             } else {
02309                 tmpnick = getChanUserData(tmp, getNickData(args[onarg]));
02310                 if (tmpnick) {
02311                     tmpnick->op &= ~CHANVOICE;
02312 #ifdef CDEBUG
02313                     sSend(":%s PRIVMSG " DEBUGCHAN " :DeOped %s in %s",
02314                           ChanServ, args[onarg], tmp->name);
02315 #endif
02316                 }
02317             }
02318             onarg++;
02319             break;
02320         }
02321 
02322     }
02323 
02324     if (tmp->reg) {
02325         makeModeLockStr(tmp->reg, mode);
02326         if (mode[0] != 0) {
02327             char parastr[IRCBUF * 2];
02328 
02329             *parastr = '\0';
02330             if ((tmp->reg->mlock & PM_L))
02331                 sprintf(parastr + strlen(parastr), " %ld",
02332                         tmp->reg->limit);
02333             if ((tmp->reg->mlock & PM_K) || (tmp->reg->mlock & MM_K))
02334                 sprintf(parastr + strlen(parastr), " %s",
02335                         (tmp->reg->key
02336                          && *tmp->reg->key ? tmp->reg->key : "*"));
02337             sSend(":%s MODE %s %s%s", ChanServ, tmp->name, mode, parastr);
02338 #ifdef IRCD_MLOCK
02339             sSend(":%s MLOCK %s %s%s", ChanServ, tmp->name, mode, parastr);
02340 #endif
02341         }
02342     }
02343 }
02344 
02355 void setChanTopic(char **args, int numargs)
02356 {
02357     RegChanList *tmp;
02358     char tmptopic[IRCBUF + 1];
02359 /*  u_int16_t    i; */
02360 
02361     if (numargs < 3)
02362         return;
02363 
02364     /*
02365      * If this isn't a channel we care to think about too hard, ignore
02366      * the channel topic.
02367      */
02368     tmp = getRegChanData(args[2]);
02369     if (tmp == NULL)
02370         return;
02371 
02372     /*
02373      * If someone who is too low to set the topic tries, set it back
02374      * to what we want it to be.
02375      */
02376     if ((numargs < 4) || getChanOp(tmp, args[3]) < tmp->tlocklevel) {
02377         if (tmp->topic)
02378             sSend(":%s TOPIC %s %s %lu :%s", myname, tmp->name,
02379                   tmp->tsetby, tmp->ttimestamp, tmp->topic);
02380         return;
02381     }
02382 
02383     if (tmp->topic)
02384         FREE(tmp->topic);
02385     strncpyzt(tmp->tsetby, args[3], NICKLEN);
02386     tmp->ttimestamp = atol(args[4]);
02387 
02388     /*
02389      * copy in the topic.  Make certain we don't overrun
02390      * the buffer.  Skip a leading : if present...  If it is not
02391      * present, this isn't a valid command.
02392      */
02393     if (*args[5] == ':')
02394         args[5]++;
02395 
02396     /*
02397      * Due to our somewhat broken parsing system... the topic part
02398      * isn't passed in one argument, it is passed in lots. :/
02399      */
02400     bzero(tmptopic, TOPIC_MAX);
02401     parse_str(args, numargs, 5, tmptopic, TOPIC_MAX);
02402     tmptopic[TOPIC_MAX] = '\0';
02403 
02404     tmp->topic = strdup(tmptopic);
02405 }
02406 
02419 const char *opLevelName(int level, int x_case)
02420 {
02421     static char *undef_string[3] = {
02422         "Op", "operator", "Operator"
02423     };
02424 
02425     if (x_case > 2 || x_case < 0)
02426         x_case = level = 0;
02427     if (level == -1 || level > FOUNDER)
02428         return undef_string[x_case];
02429     return oplev_table[level].v[x_case];
02430 }
02431 
02439 int opNameLevel(const char *name)
02440 {
02441     char *p;
02442     int level = 0, j = 0;
02443 
02444     if (!name || !*name || strlen(name) > 15)
02445         return -1;
02446     if (!strcasecmp(name, "off") || !strcasecmp(name, "user"))
02447         return 0;
02448 
02449     if (isdigit(*name)) {
02450         level = atoi(name);
02451         if (level >= 0 && level <= FOUNDER)
02452             return level;
02453     }
02454 
02455 
02456     if (*name && (p = strchr(name, '(')) && *p == '(' && isdigit(p[1])) {
02457         if ((isdigit(p[2]) && p[3] == ')') || p[2] == ')')
02458             return atoi(p + 1);
02459     }
02460 
02461     for (level = 0; level <= FOUNDER; level++) {
02462         for (j = 0; j < 3; j++)
02463             if (!strcasecmp(oplev_table[level].v[j], name))
02464                 return level;
02465     }
02466     return -1;
02467 }
02468 
02472 void syncChanData(time_t next)
02473 {
02474     nextCsync = next;
02475     saveChanData(firstRegChan);
02476 }
02477 
02478 /*------------------------------------------------------------------------*/
02479 
02480 /*--------------------------------------------------------------------*/
02481 
02482 int ValidChannelName(const char* nmtoken)
02483 {
02484     const char *p;
02485 
02486     if (!nmtoken || !*nmtoken)
02487         return 0;
02488     if ((nmtoken[0] != '#' && nmtoken[0] != '+') || !nmtoken[1])
02489         return 0;
02490     for(p = nmtoken; *p; p++) {
02491         if (!iscntrl(*p) && !isspace(*p) && (unsigned char)*p != 0xA0)
02492             continue;
02493         return 0;
02494     }
02495     return 1;
02496 }
02497 
02498 /*--------------------------------------------------------------------*/
02503 void expireChans(char *arg)
02504 {
02505     time_t timestart;
02506     time_t timeend;
02507     RegChanList *regchan;
02508     RegChanList *next;
02509     ChanList *chan;
02510     /*RegNickList   *nick; */
02511     char backup[50];
02512     int i;
02513 
02514     arg = arg;                  /* shut up gcc */
02515 
02516     i = 0;
02517     mostchans = 0;
02518     timestart = time(NULL);
02519 
02520     strftime(backup, 49, "chanserv/backups/chanserv.db%d%m%Y",
02521              localtime(&timestart));
02522     rename("chanserv/chanserv.db", backup);
02523     saveChanData(firstRegChan);
02524 
02525     for (regchan = firstRegChan; regchan; regchan = next) {
02526         next = regchan->next;   /* save this, since regchan might die */
02527         mostchans++;
02528 
02529         if ((timestart - regchan->timestamp) >= CHANDROPTIME) {
02530             if ((regchan->flags & (CHOLD | CBANISH)))
02531                 continue;
02532 #if 0
02533             printf("Dropping channel %s: %ld %ld %ld\n", regchan->name,
02534                    timestart, regchan->timestamp,
02535                    timestart - regchan->timestamp);
02536 #endif
02537 
02538             chan = getChanData(regchan->name);
02539             if (chan != NULL)
02540                 chan->reg = NULL;
02541             chanlog->log(NULL, CSE_EXPIRE, regchan->name);
02542             delRegChan(regchan);
02543             i++;
02544         }
02545     }
02546 
02547     timeend = time(NULL);
02548     sSend(":%s GLOBOPS :ChanServ EXPIRE(%d/%lu) %ld seconds", ChanServ, i,
02549           mostchans, (timeend - timestart));
02550     timer((int)(2.5 * 3600), (void (*)(char *))expireChans, NULL);
02551 }
02552 
02553 /*------------------------------------------------------------------------*/
02554 
02555 /* -------------------------------------------------------------------- */
02556 /* The ChanServ command parser                                   -Mysid */
02557 /* -------------------------------------------------------------------- */
02558 
02562 void sendToChanServ(UserList * nick, char **args, int numargs)
02563 {
02564     char *from = nick->nick;
02565     interp::parser * cmd;
02566 
02567     /* small "hack" to make services work in either /cs #channel cmd
02568      * or /cs cmd #channel way */
02569     char crud[IRCBUF];
02570 
02571     if (index(args[0], '#')) {
02572         counterOldCSFmt++;
02573         strncpyzt(crud, args[0], IRCBUF);
02574         strcpy(args[0], args[1]);
02575         strcpy(args[1], crud);
02576     }
02577 
02578     cmd =
02579         new interp::parser(ChanServ, getOpFlags(nick), chanserv_commands,
02580                            args[0]);
02581     if (!cmd)
02582         return;
02583 
02584     if ((cmd->getCmdFlags() & CMD_REG) && !nick->reg) {
02585         sSend
02586             (":%s NOTICE %s :You nick must be registered to use that command.",
02587              ChanServ, from);
02588         return;
02589     }
02590 
02591     switch (cmd->run(nick, args, numargs)) {
02592     default:
02593         break;
02594     case RET_FAIL:
02595         sSend(":%s NOTICE %s :Unknown command %s.\r\n"
02596               ":%s NOTICE %s :Please try /msg %s HELP", ChanServ, from,
02597               args[0], ChanServ, from, ChanServ);
02598         break;
02599     case RET_OK_DB:
02600         sSend(":%s NOTICE %s :Next database synch(save) in %ld minutes.",
02601               ChanServ, nick->nick, (long)((nextCsync - CTime) / 60));
02602         break;
02603     }
02604 }
02605 
02606 /*--------------------------------------------------------------------*/
02607 
02613 CCMD(cs_help)
02614 {
02615     help(nick->nick, ChanServ, args, numargs);
02616     return RET_OK;
02617 }
02618 
02619 /*--------------------------------------------------------------------*/
02620 
02628 CCMD(cs_chanop)
02629 {
02630     const char *from = nick->nick;
02631     const char *cmdName = "op", *cTargNick = NULL;
02632     int targLevel = -1, i;
02633     int is_add = 0, is_del = 0, is_chanop = 0;
02634     char cmd_x[25] = "";
02635     RegChanList *chan;
02636 
02637     struct
02638     {
02639         const char *name;
02640         int lev;
02641     } op_alias_table[] = {
02642         { "maop", MAOP }, { "aop", AOP },
02643         { "msop", MSOP}, { "sop", SOP },
02644         { "mfounder", MFOUNDER}, { "founder", FOUNDER },
02645         { NULL }
02646     };
02647 
02648     cmd_x[0] = '\0';
02649 
02650     if (numargs >= 1 && args[0] && *args[0]) {
02651         for (i = 0; args[0][i] && i < 25; i++)
02652             cmd_x[i] = toupper(args[0][i]);
02653         cmd_x[i] = '\0';
02654     }
02655 
02656         if (cmd_x[0] == '-' && strcmp(cmd_x, "-ops"))
02657             strcpy(cmd_x, "chanop");
02658 
02659     if (numargs < 3) {
02660         sSend(":%s NOTICE %s :Usage: %s ADD <nick>", ChanServ, from,
02661               cmd_x);
02662         sSend(":%s NOTICE %s :       %s DEL <nick | index#>", ChanServ,
02663               from, cmd_x);
02664         sSend(":%s NOTICE %s :       %s LIST", ChanServ, from, cmd_x);
02665         sSend
02666             (":%s NOTICE %s :Type `/msg %s HELP %s', for more information.",
02667              ChanServ, from, ChanServ, cmd_x);
02668         return RET_SYNTAX;
02669     }
02670 
02671     if (!(chan = getRegChanData(args[1]))) {
02672         sSend(":%s NOTICE %s :%s is not a registered channel.", ChanServ,
02673               from, args[1]);
02674         return RET_NOTARGET;
02675     }
02676 
02677     if (!(tolower(args[0][0]) == 'c' && !strcasecmp(args[0] + 1, "hanop"))) {
02678         for (i = 0; op_alias_table[i].name; i++)
02679             if (op_alias_table[i].name[0] == tolower(*args[0])
02680                 && !strcasecmp(1 + op_alias_table[i].name, 1 + args[0]))
02681                 break;
02682         if (op_alias_table[i].name) {
02683             targLevel = op_alias_table[i].lev;
02684             cmdName = op_alias_table[i].name;
02685         }
02686     } else
02687         is_chanop = 1;
02688 
02689     if ((is_add = !strcasecmp(args[2], "add"))
02690         || (is_del = !strncasecmp(args[2], "del", 3))) {
02691         if (numargs < 3) {
02692             sSend(":%s NOTICE %s :%s who?", ChanServ, from,
02693                   tolower(*args[2]) == 'd' ? "delete" : "add");
02694             return RET_SYNTAX;
02695         }
02696 
02697         cTargNick = args[3];
02698         if (is_add) {
02699             if ((numargs >= 5)) {
02700                 int newTargLevel = opNameLevel(args[4]);
02701 
02702                 if (newTargLevel < 1 || newTargLevel > FOUNDER)
02703                     newTargLevel = -1;
02704 
02705                 if (!is_chanop) {
02706                     if ((newTargLevel > targLevel)
02707                         || (newTargLevel < MAOP - 1)
02708                         || (newTargLevel <= AOP && targLevel == MSOP)
02709                         || (newTargLevel < MSOP && targLevel == SOP)
02710                         || (newTargLevel <= SOP && targLevel == MFOUNDER)
02711                         ) {
02712                         sSend
02713                             (":%s NOTICE %s :Error: Invalid level for %s command.",
02714                              ChanServ, from, cmdName);
02715                         sSend
02716                             (":%s NOTICE %s :Type `/msg %s HELP %s', for more information.",
02717                              ChanServ, from, ChanServ, cmd_x);
02718                         return RET_SYNTAX;
02719                     }
02720                 }
02721                 targLevel = newTargLevel;
02722             }
02723             return do_chanop(CS_ADDOP, nick, chan, cTargNick, targLevel);
02724         } else
02725             return do_chanop(CS_DELOP, nick, chan, cTargNick, targLevel);
02726     } else if (!strcasecmp(args[2], "list")) {
02727         if ((numargs >= 4))
02728             cTargNick = args[3];
02729         return do_chanop(CS_LISTOP, nick, chan, cTargNick, targLevel);
02730     } else {
02731         sSend(":%s NOTICE %s :Unknown %s command %s.\r"
02732               ":%s NOTICE %s :/msg %s HELP for assistance", ChanServ, from,
02733               cmdName, args[2], ChanServ, from, ChanServ);
02734         return RET_SYNTAX;
02735     }
02736 
02737     return RET_OK;
02738 }
02739 
02740 /*--------------------------------------------------------------------*/
02741 
02754 CCMD(cs_akick)
02755 {
02756     const char *from = nick->nick;
02757     char theCmd[256];
02758     int i = 3;
02759 
02760     if (numargs < 2) {
02761         sSend(":%s NOTICE %s :Channel name required", ChanServ, from);
02762 
02763         return RET_SYNTAX;
02764     }
02765 
02766     if (numargs < 3) {
02767         sSend(":%s NOTICE %s :Not enough arguments to command", ChanServ,
02768               from);
02769         return RET_SYNTAX;
02770     }
02771 
02772     strncpyzt(theCmd, args[2], sizeof(theCmd));
02773 
02774     while (i < numargs) {
02775         strncpyzt(args[i - 1], args[i], ARGLEN);
02776         i++;
02777     }
02778     numargs--;
02779 
02780     if (!strcasecmp(theCmd, "add"))
02781         return cs_addak(nick, args, numargs);
02782     else if (!match("del*", theCmd))
02783         return cs_delak(nick, args, numargs);
02784     else if (!strcasecmp(theCmd, "list"))
02785         return cs_listak(nick, args, numargs);
02786     else {
02787         sSend(":%s NOTICE %s :Unknown akick command %s.\r"
02788               ":%s NOTICE %s :/msg %s HELP for assistance", ChanServ, from,
02789               args[2], ChanServ, from, ChanServ);
02790         return RET_SYNTAX;
02791     }
02792     return RET_OK;
02793 }
02794 
02795 /*--------------------------------------------------------------------*/
02796 
02806 CCMD(cs_mdeop)
02807 {
02808     char *from = nick->nick;
02809     char accNick[NICKLEN+1];
02810     ChanList *chan;
02811     cNickList *tmp;
02812     int aclvl;
02813 
02814     if (numargs < 2) {
02815         sSend(":%s NOTICE %s :You must specify a channel", ChanServ, from);
02816         return RET_SYNTAX;
02817     }
02818 
02819     chan = getChanData(args[1]);
02820     if (!chan) {
02821         sSend(":%s NOTICE %s :%s is not currently active", ChanServ, from,
02822               args[1]);
02823         return RET_NOTARGET;
02824     }
02825 
02826     chan->reg = getRegChanData(args[1]);
02827     if (!chan->reg) {
02828         sSend(":%s NOTICE %s :%s is not registered", ChanServ, from,
02829               args[1]);
02830         return RET_NOTARGET;
02831     }
02832 
02833     aclvl = getMiscChanOp(chan->reg, from, ChanGetIdent(chan->reg), accNick);
02834     if (opFlagged(nick, OVERRIDE | OSERVOP)
02835         || opFlagged(nick, OVERRIDE | ODMOD)) aclvl = FOUNDER + 1;
02836 
02837     if (MAOP > aclvl) {
02838         sSend
02839             (":%s NOTICE %s :You must be at least level %i in the channel op list to MDEOP.",
02840              ChanServ, from, MAOP);
02841         return RET_NOPERM;
02842     }
02843     sendToChanOpsAlways(chan, "%s!%s is using command MDEOP on %s", accNick, from,
02844                         chan->name);
02845     for (tmp = chan->firstUser; tmp; tmp = tmp->next) {
02846         if (aclvl > getChanOp(chan->reg, tmp->person->nick)) {
02847             sSend(":%s MODE %s -o %s", ChanServ, chan->name,
02848                   tmp->person->nick);
02849             tmp->op = 0;
02850         } else
02851             sSend
02852                 (":%s NOTICE %s :Your access is not high enough to deop %s",
02853                  ChanServ, from, tmp->person->nick);
02854     }
02855     return RET_OK;
02856 }
02857 
02858 /*--------------------------------------------------------------------*/
02859 
02869 CCMD(cs_mkick)
02870 {
02871     char *from = nick->nick;
02872     char accNick[NICKLEN+1];
02873     ChanList *chan;
02874     cNickList *tmp;
02875     cNickList *next;
02876     int aclvl;
02877 
02878     if (numargs < 2) {
02879         sSend(":%s NOTICE %s :You must specify a channel", ChanServ, from);
02880         return RET_SYNTAX;
02881     }
02882 
02883     chan = getChanData(args[1]);
02884     if (chan == NULL) {
02885         sSend(":%s NOTICE %s :%s is not currently active", ChanServ, from,
02886               args[1]);
02887         return RET_NOTARGET;
02888     }
02889 
02890     if (chan->reg == NULL) {
02891         sSend(":%s NOTICE %s :%s is not registered", ChanServ, from,
02892               args[1]);
02893         return RET_NOTARGET;
02894     }
02895 
02896     aclvl = getMiscChanOp(chan->reg, from, 1, accNick);
02897 
02898     if ((SOP > aclvl) && (!isOper(nick))) {
02899         sSend
02900             (":%s NOTICE %s :You must be at least level %i in the channel op list to MKICK.",
02901              ChanServ, from, SOP);
02902         return RET_NOPERM;
02903     }
02904 
02905     if (isOper(nick) && (SOP > aclvl)) {
02906         if (numargs > 2) {
02907             if (strncmp(args[2], "-o", 2) == 0) {
02908                 sSend
02909                     (":%s GLOBOPS :%s is using MKICK on %s (without SOP level)",
02910                      ChanServ, from, chan->name);
02911             } else {
02912                 sSend
02913                     (":%s NOTICE %s :You are an oper but not a channel op, to override MKICK limitations, use -o",
02914                      ChanServ, from);
02915                 return RET_FAIL;
02916             }
02917         } else {
02918             sSend
02919                 (":%s NOTICE %s :You are an oper but not a channel op, to override MKICK limitations, use -o",
02920                  ChanServ, from);
02921             return RET_FAIL;
02922         }
02923     }
02924 
02925     createGhostChannel(chan->name);
02926     sSend(":%s MODE %s +b *!*@*", ChanServ, chan->name);
02927     timer(10, deleteTimedGhostChannel, strdup(chan->name));
02928 
02929     sendToChanOpsAlways(chan, "%s!%s is using MKICK on %s", accNick, nick->nick, from, chan->name);
02930 
02931     for (tmp = chan->firstUser; tmp; tmp = next) {
02932         next = tmp->next;
02933 
02934         sSend(":%s KICK %s %s", ChanServ, chan->name, tmp->person->nick);
02935         delChanUser(chan, tmp, 1);
02936     }
02937     return RET_OK;
02938 }
02939 
02940 /*--------------------------------------------------------------------*/
02941 
02947 void sendChannelInfo(UserList *nick, RegChanList *chan, int fTerse)
02948 {
02949     char *from = nick->nick;
02950     char buf[IRCBUF];
02951     char timejunk[80];
02952     char modestring[20];
02953     const char *tmpcChar;
02954     unsigned int i;
02955     int hidden_details = 0;
02956     RegNickList *founderInfo;
02957 
02958     struct { 
02959         flag_t flags;
02960         const char *brief, *alt;
02961         const char *line;
02962     }
02963     csflagOptTab[] = {
02964         { CIDENT,   "ident", "Ident",  "Ident. Ops must identify to their nick before being recognised by ChanServ" }, 
02965         { COPGUARD, "opguard", "SecuredOps", "Op Guard. Only users in ChanServ's op list can have ops in the channel" }, 
02966         { CKTOPIC,  "keeptopic", "\"Sticky\" Topics", "Preserve Topic. ChanServ will remember and re-set the last topic when the channel is empty" }, 
02967         { CPROTOP,  "protect ops", NULL, "Protected Ops. Ops cannot be deopped by another op with lower access" }, 
02968         { CQUIET,   "quiet", "Quiet", "Quiet Changes. ChanServ will not inform channel ops when channel options are changed" }, 
02969         { CHOLD,    "\2held\2", "held", "Held. This channel will not expire." }, 
02970     };
02971 
02972     if ((chan->mlock & (PM_S)) || (chan->flags & (CBANISH)))
02973         hidden_details = 2; /* 2 means hide details */
02974     else if ((chan->mlock & (PM_I)) || (chan->mlock & (PM_K)))
02975         hidden_details = 1; /* 1 means hide autogreet */
02976 
02977     if (hidden_details) {
02978         if (isOper(nick) || 
02979             getMiscChanOp(chan, nick->nick, 1, NULL) >= MAOP)
02980         hidden_details = 0;
02981     }
02982 
02983     if ((chan->flags & CBANISH))
02984         sSend(":%s NOTICE %s :%s is BANISHED.", ChanServ, from, chan->name);
02985     else if ((chan->flags & CCLOSE))
02986         sSend(":%s NOTICE %s :\2This channel is CLOSED\2", ChanServ, from);
02987     else
02988     {
02989         founderInfo = chan->founderId.getNickItem();
02990 
02991         switch(fTerse)
02992         {
02993             case 2:
02994                 sSend(":%s NOTICE %s :\2***\2 Info on %s",
02995                       ChanServ, from, chan->name);      
02996                 break;
02997             case 1:
02998                 sSend(":%s NOTICE %s :Information on %s (terse):",
02999                       ChanServ, from, chan->name);
03000                 break;
03001             case 0: default:
03002 
03003             sSend(":%s NOTICE %s :Information on %s:",
03004                   ChanServ, from, chan->name);
03005                 break;
03006         }
03007 
03008         if (founderInfo != NULL)
03009         {
03010 #if defined(NETNICK) && defined(NETNICKFOUNDERLINE)
03011             if (strcasecmp(founderInfo->nick, NETNICK) == 0)
03012             {
03013                 sSend(":%s NOTICE %s :Founder    : %s", ChanServ, from, NETNICKFOUNDERLINE);
03014             }
03015             else
03016 #endif
03017                 sSend(":%s NOTICE %s :Founder    : %s (%s@%s)", ChanServ, from, founderInfo->nick, founderInfo->user, regnick_ugethost(nick, founderInfo));
03018         }
03019         else if ((tmpcChar = chan->founderId.getNick()) != NULL) {
03020             if (*tmpcChar != '\0' && *tmpcChar != '*')
03021                 sSend(":%s NOTICE %s :Founder    : %s", ChanServ, from, tmpcChar);
03022         }
03023 
03024         if (chan->desc[0] != '\0')
03025             sSend(":%s NOTICE %s :Description: %s", ChanServ, from,
03026                   chan->desc);
03027 
03029         if (!hidden_details && chan->autogreet != NULL)
03030             sSend(":%s NOTICE %s :Autogreet  : %s", ChanServ, from,
03031                   chan->autogreet);
03032 
03033         if ((hidden_details < 2) && chan->topic) {
03034             sSend(":%s NOTICE %s :Topic      : %s (%s)", ChanServ, from,
03035                   chan->topic, chan->tsetby);
03036         }
03037         if (chan->url)
03038             sSend(":%s NOTICE %s :Url        : %s", ChanServ, from, chan->url);
03039         buf[0] = '\0';
03040 
03041         if (!fTerse)
03042             sSend(":%s NOTICE %s :Options:", ChanServ, from);
03043 
03044         for(i = 0; i < sizeof(csflagOptTab) / sizeof(csflagOptTab[0]); i++)
03045         {
03046             if ((chan->flags & csflagOptTab[i].flags) == csflagOptTab[i].flags)
03047             {
03048                 if (fTerse)
03049                 {
03050                     if (buf[0] != '\0')
03051                         strcat(buf, ", ");
03052                     if (fTerse == 2 && csflagOptTab[i].alt)
03053                         strcat(buf, csflagOptTab[i].alt);
03054                     else
03055                         strcat(buf, csflagOptTab[i].brief);
03056                 }
03057                 else
03058                     sSend(":%s NOTICE %s :%s", ChanServ, from, csflagOptTab[i].line);
03059 
03060             }
03061         }
03062 
03063         if (fTerse)
03064         {
03065             if (fTerse == 2) {              
03066                 if (chan->restrictlevel)
03067                     sprintf(buf + strlen(buf), "%sRestricted(%d)",
03068                             buf[0] != '\0' ? ", " : "", chan->restrictlevel);
03069                 if (chan->tlocklevel > AOP)
03070                     sprintf(buf + strlen(buf), "%sTopic Lock(%d)",
03071                             buf[0] != '\0' ? ", " : "", chan->tlocklevel);              
03072             }
03073             sSend(":%s NOTICE %s :Options    : %s", ChanServ, from, buf);
03074             if (chan->memolevel > 0)
03075                 sSend(":%s NOTICE %s :Memo Level : %s", ChanServ, from, opLevelName(chan->memolevel, 0));
03076         }
03077 
03078         makeModeLockStr(chan, modestring);
03079 
03080         if (fTerse) 
03081         {
03082             if (fTerse != 2)
03083             {
03084                 if (*modestring != '\0')
03085                     sSend(":%s NOTICE %s :Mode Locked to: \002%s\002", ChanServ, from,
03086                           modestring);
03087                 sSend(":%s NOTICE %s :Topic Lock: Level %2i+, Restricted: Level %2i+",
03088                  ChanServ, from, chan->tlocklevel, chan->restrictlevel);
03089             }
03090             else {
03091                 if (*modestring != '\0')
03092                     sSend(":%s NOTICE %s :Mode Lock  : %s", ChanServ, from,
03093                       modestring);
03094             }
03095         }
03096         else
03097         {
03098             if (*modestring == '\0')
03099                 strcpy(modestring, "(none)");
03100 
03101             sSend
03102                 (":%s NOTICE %s :Mode Locked to: \002%s\002 = These modes will be enforced on the channel",
03103                  ChanServ, from, modestring);
03104 
03105             sSend(":%s NOTICE %s :Topic Lock: \002Level %i+\002 = %s",
03106                   ChanServ, from, chan->tlocklevel,
03107                   (chan->tlocklevel == 0) ? "Any channel operator can change the topic" :
03108                   "Only operators with this access level or higher can change the topic");
03109             sSend(":%s NOTICE %s :Restrict Lock: \002Level %i+\002 = %s",
03110                   ChanServ, from, chan->restrictlevel,
03111                   (chan->restrictlevel == 0) ? "Anyone can join this channel" :
03112                   "Only operators with this access level or higher can join");
03113         }
03114 
03115         if (strftime(timejunk, 80, "%a %Y-%b-%d %T %Z",
03116                  localtime(&chan->timereg)) > 0)
03117         {
03118             if (fTerse != 2)
03119                 sSend(":%s NOTICE %s :Channel registered at: %s", ChanServ, from,
03120                       timejunk);
03121             else
03122                 sSend(":%s NOTICE %s :Registered : %s", ChanServ, from,
03123                       timejunk);
03124         }
03125 
03126         if (strftime(timejunk, 80, "%a %Y-%b-%d %T %Z",
03127                  localtime(&chan->timestamp)) > 0)
03128         {
03129             if (fTerse != 2)
03130                 sSend(":%s NOTICE %s :Channel last used: %s", ChanServ, from,
03131                       timejunk);
03132             else
03133                 sSend(":%s NOTICE %s :Last Used  : %s", ChanServ, from,
03134                       timejunk);
03135         }
03136     }
03137 
03138     if (isOper(nick)) {
03139         sSend(":%s NOTICE %s :+ Oper Info: %i Ops   %i Akicks", ChanServ, from,
03140               chan->ops, chan->akicks);
03141 
03142         if (chan->flags & CMARK) {
03143             sSend(":%s NOTICE %s :+ Channel MARKED%s%s.", ChanServ, from,
03144                   chan->markby ? " by " : "",
03145                   chan->markby ? chan->markby : "");
03146         }
03147     }
03148 
03149     /*if (numargs > 2 && args[2])
03150        tzapply(DEF_TIMEZONE); */
03151 
03152     if (fTerse != 2)
03153         sSend(":%s NOTICE %s :End of information on %s", ChanServ, from,
03154               chan->name);
03155     else
03156         sSend(":%s NOTICE %s :\2***\2 End of info", ChanServ, from);
03157 }
03158 
03170 CCMD(cs_info)
03171 {
03172     char *from = nick->nick;
03173     RegChanList *chan;
03174     int i, fTerse;
03175 
03176     if (addFlood(nick, 5))
03177         return RET_KILLED;
03178 
03179     fTerse = (nick->reg && (nick->reg->flags & NTERSE));
03180 
03181     for(i = 1; i < numargs; i++) {
03182         if (args[i][0] != '-')
03183             break;
03184         if (strcmp(args[i], "-long") == 0 || strcmp(args[i], "-verbose") == 0)
03185             fTerse = 0;
03186         else if (strcmp(args[i], "-terse") == 0 || strcmp(args[i], "-short") == 0)
03187             fTerse = 1;
03188         else if (strcmp(args[i], "-2") == 0)
03189             fTerse = 2;
03190     }
03191 
03192     if (i >= numargs) {
03193         sSend(":%s NOTICE %s :You must specify a channel", ChanServ, from);
03194         return RET_SYNTAX;
03195     }
03196 
03197     chan = getRegChanData(args[i]);
03198 
03199     if (!chan) {
03200         sSend(":%s NOTICE %s :%s is not registered", ChanServ, from,
03201               args[i]);
03202         return RET_NOTARGET;
03203     }
03204 
03205     /*if (chan && numargs > 2 && args[2])
03206        tzapply(args[2]); */
03207 
03208     sendChannelInfo(nick, chan, fTerse);
03209 
03210     return RET_OK;
03211 }
03212 
03213 /*--------------------------------------------------------------------*/
03214 
03225 CCMD(cs_access)
03226 {
03227     char *from = nick->nick;
03228     char checkAccessNick[NICKLEN + 1];
03229     RegChanList *tmp;
03230     UserList *tmpnick;
03231     RegNickList *tmprnick;
03232     int level;
03233 
03234     if (numargs < 2) {
03235         sSend(":%s NOTICE %s :You must specify at least a channel",
03236               ChanServ, from);
03237         return RET_SYNTAX;
03238     }
03239 
03240     tmp = getRegChanData(args[1]);
03241 
03242     if (!tmp) {
03243         sSend(":%s NOTICE %s :%s is not registered", ChanServ, from,
03244               args[1]);
03245         return RET_NOTARGET;
03246     }
03247 
03248     if (numargs >= 3)
03249         if (getNickData(args[2]) == NULL) {
03250             sSend(":%s NOTICE %s :%s is not online", ChanServ, from,
03251                   args[2]);
03252             return RET_NOTARGET;
03253         }
03254 
03255     level =
03256         getMiscChanOp(tmp, (numargs >= 3) ? args[2] : nick->nick,
03257                       ChanGetIdent(tmp), checkAccessNick);
03258     tmpnick = getNickData((numargs >= 3) ? args[2] : nick->nick);
03259     tmprnick = getRegNickData(checkAccessNick);
03260 
03261     if (level == FOUNDER && *checkAccessNick == '*')
03262         sSend(":%s NOTICE %s :%s %s identified as the founder of %s",
03263               ChanServ, from, (numargs >= 3) ? args[2] : "You",
03264               (numargs >= 3) ? "is" : "are", tmp->name);
03265     else if (level < 1)
03266         sSend(":%s NOTICE %s :%s %s access level %i on %s", ChanServ, from,
03267               (numargs >= 3) ? args[2] : "You",
03268               (numargs >= 3) ? "has" : "have", level, tmp->name);
03269     else if (tmprnick && tmpnick && isIdentified(tmpnick, tmprnick))
03270         sSend
03271             (":%s NOTICE %s :%s %s access level %i on %s via identification to %s",
03272              ChanServ, from, (numargs >= 3) ? args[2] : "You",
03273              (numargs >= 3) ? "has" : "have", level, tmp->name,
03274              checkAccessNick);
03275     else
03276         sSend
03277             (":%s NOTICE %s :%s %s access level %i on %s from access list of %s",
03278              ChanServ, from, (numargs >= 3) ? args[2] : "You",
03279              (numargs >= 3) ? "has" : "have", level, tmp->name,
03280              checkAccessNick);
03281     return RET_OK;
03282 }
03283 
03284 /*--------------------------------------------------------------------*/
03285 
03290 CCMD(cs_register)
03291 {
03292     char *from = nick->nick;
03293     char descbuf[IRCBUF];
03294     char pw_reason[IRCBUF];
03295     cNickList *tmp;
03296     cAccessList *founder;
03297     ChanList *chan;
03298     RegChanList *reg;
03299     int override = 0;
03300 
03301     if (numargs < 4) {
03302         sSend
03303             (":%s NOTICE %s :You must specify a channel, a password, and a description for that channel to register it",
03304              ChanServ, from);
03305         sSend
03306             (":%s NOTICE %s :See \2/msg %s HELP REGISTER\2 for more information",
03307              ChanServ, from, ChanServ);
03308         return RET_SYNTAX;
03309     }
03310 
03311     chan = getChanData(args[1]);
03312     tmp = getChanUserData(chan, nick);
03313 
03314     if (!ValidChannelName(args[1])) {
03315         sSend(":%s NOTICE %s :Invalid channel name, %s.", ChanServ, from,
03316               args[1]);
03317         return RET_EFAULT;
03318     }
03319 
03320     if (!nick || !nick->reg || !isIdentified(nick, nick->reg)) {
03321         sSend
03322             (":%s NOTICE %s :You must be identified to register a channel",
03323              NickServ, from);
03324         return RET_FAIL;
03325     }
03326 
03327     if (chan && chan->reg) {
03328         sSend(":%s NOTICE %s :%s is already registered", ChanServ, from,
03329               args[1]);
03330         return RET_FAIL;
03331     }
03332 
03333     if (is_sn_chan(args[1]) && !opFlagged(nick, OOPER | OVERRIDE)) {
03334         sSend(":%s NOTICE %s :This channel is reserved for " NETWORK ".",
03335               ChanServ, from);
03336         return RET_NOPERM;
03337     }
03338 
03339         if (isPasswordAcceptable(args[2], pw_reason) == 0) {
03340                 sSend(":%s NOTICE %s :Sorry, %s isn't a password that you can use.",
03341                         ChanServ, from, args[1]);
03342                 sSend(":%s NOTICE %s :%s", ChanServ, from, pw_reason);
03343                 return RET_EFAULT;
03344         }
03345 
03346     if (PASSLEN < strlen(args[2])) {
03347         sSend
03348             (":%s NOTICE %s :Your password is too long, it must be %d characters or less",
03349              ChanServ, from, PASSLEN);
03350         return RET_EFAULT;
03351     }
03352 
03353 #if CHANDESCBUF < IRCBUF
03354     parse_str(args, numargs, 3, descbuf, CHANDESCBUF + 1);
03355 #else
03356     parse_str(args, numargs, 3, descbuf, IRCBUF);
03357 #endif
03358 
03359     if (strlen(descbuf) >= CHANDESCBUF) {
03360         sSend(":%s NOTICE %s :Your description is too long, it must be %d characters or less.",
03361              ChanServ, from, CHANDESCBUF - 1);
03362         return RET_EFAULT;
03363     }
03364 
03365     if (nick->reg->chans >= ChanLimit && !opFlagged(nick, OVERRIDE)) {
03366         sSend(":%s NOTICE %s :You have registered too many channels",
03367               ChanServ, from);
03368 #ifdef REGLIMITYELL
03369         sSend(":%s GLOBOPS :%s is trying to register too many channels",
03370               ChanServ, from);
03371 #endif
03372         return RET_FAIL;
03373     }
03374 
03375     if (!chan || !tmp || !tmp->op) {
03376         if (!opFlagged(nick, OVERRIDE | OSERVOP)
03377             && !opFlagged(nick, OVERRIDE | ODMOD)) {
03378             sSend
03379                 (":%s NOTICE %s :You must be both inside, and oped, in the channel you wish to register",
03380                  ChanServ, from);
03381             return RET_FAIL;
03382         }
03383     }
03384 
03385     if ((override = opFlagged(nick, OVERRIDE))) {
03386         sSend(":%s NOTICE %s :Override registration ok.", ChanServ, from);
03387         sSend(":%s GLOBOPS :%s used override register on channel %s",
03388               ChanServ, from, args[1]);
03389     }
03390 
03391     nick->reg->chans++;
03392     reg = (RegChanList *) oalloc(sizeof(RegChanList));
03393     initRegChanData(reg);
03394     chan->reg = reg;
03395     strcpy(reg->name, chan->name);
03396     reg->founderId = nick->reg->regnum;
03397     reg->flags |= CENCRYPT;
03398     pw_enter_password(args[2], reg->password, ChanGetEnc(reg));
03399 
03400     reg->timereg = CTime;
03401     reg->timestamp = CTime;
03402     sendToChanOpsAlways(chan, "%s just registered %s", from, args[1]);
03403     sSend
03404         (":%s NOTICE %s :%s is now registered under your nick: \002%s\002",
03405          ChanServ, from, args[1], from);
03406     sSend(":%s NOTICE %s :You now have level 15 access.", ChanServ, from);
03407     sSend(":%s NOTICE %s :Your password is \002%s\002 DO NOT FORGET IT",
03408           ChanServ, from, args[2]);
03409     sSend(":%s NOTICE %s :We are NOT responsible for lost passwords.",
03410           ChanServ, from);
03411     sSend(":%s NOTICE %s :Your channel is modelocked +tn-k", ChanServ,
03412           from);
03413     sSend(":%s NOTICE %s :And topic preservation is set on", ChanServ,
03414           from);
03415     sSend
03416         (":%s NOTICE %s :For information on channel maintenance, /msg %s HELP",
03417          ChanServ, from, ChanServ);
03418     founder = (cAccessList *) oalloc(sizeof(cAccessList));
03419     founder->nickId = nick->reg->regnum;
03420     founder->uflags = FOUNDER;
03421 
03422     addChanOp(chan->reg, founder);
03423     chan->reg->flags |= CKTOPIC;
03424     chan->reg->flags |= CIDENT;
03425     chan->reg->mlock |= PM_N;
03426     chan->reg->mlock |= PM_T;
03427     chan->reg->mlock |= MM_H;
03428     chan->reg->mlock |= MM_K;
03429     chan->reg->facc = 1;
03430     addRegChan(reg);
03431     mostchans++;
03432     chanlog->log(nick, CS_REGISTER, chan->name, 0,
03433                  override ? "(override)" : "");
03434     strncpyzt(chan->reg->desc, descbuf, CHANDESCBUF);
03435     return RET_OK_DB;
03436 }
03437 
03438 /*--------------------------------------------------------------------*/
03439 
03450 CCMD(cs_identify)
03451 {
03452     char *from = nick->nick;
03453     RegChanList *chan;
03454     cAccessList *newitem;
03455 
03456     if (numargs < 3) {
03457         sSend
03458             (":%s NOTICE %s :You must specify a channel and password to identify with",
03459              ChanServ, from);
03460         return RET_SYNTAX;
03461     }
03462     chan = getRegChanData(args[1]);
03463 
03464     if (!chan) {
03465         sSend(":%s NOTICE %s :The channel is not registered.", ChanServ,
03466               from);
03467         return RET_NOTARGET;
03468     }
03469 
03470     if ((chan->flags & (CBANISH | CCLOSE)) && !isRoot(nick)) {
03471         sSend(":%s NOTICE %s :You cannot identify to that channel because it is closed or banished.",
03472              ChanServ, from);
03473         return RET_FAIL;
03474     }
03475 
03476     if ((chan->flags & CFORCEXFER)) {
03477                 sSend(":%s NOTICE %s :An operator has disabled the current "
03478                       "channel password for a forced change of founder.",
03479                       ChanServ, from);
03480         return RET_FAIL;
03481     }
03482 
03483         if (isMD5Key(args[2]))
03484         {
03485               struct auth_data auth_info[] = {{
03486                       nick->auth_cookie,
03487                       nick->idnum.getHashKey(),
03488                       2
03489               }};
03490 
03491             if (!Valid_md5key(args[2], auth_info, chan->name, chan->password, ChanGetEnc(chan)))
03492             {
03493                 sSend(":%s NOTICE %s :Invalid MD5 key.", NickServ, nick->nick);
03494                 nick->auth_cookie = 0;
03495                 if (BadPwChan(nick, chan))
03496                     return RET_KILLED;
03497 
03498                  return RET_BADPW;
03499             }
03500 
03501             /* Valid */
03502         nick->auth_cookie = 0;
03503         }
03504     else if (!Valid_pw(args[2], chan->password, ChanGetEnc(chan))) {
03505         sSend(":%s NOTICE %s :Invalid password", ChanServ, from);
03506 
03507         if (!(chan->flags & CFORCEXFER)) {
03508             if (BadPwChan(nick, chan))
03509                 return RET_KILLED;
03510         }
03511 
03512         return RET_BADPW;
03513     }
03514 
03516     if (nick->reg && nick->reg->regnum == chan->founderId)
03517         chan->facc = 1;
03518     else {
03519         if (chan->id.nick)
03520             FREE(chan->id.nick);
03521         chan->id.nick = (char *)oalloc(strlen(nick->nick) + 1);
03522         chan->id.timestamp = CTime;
03523         chan->id.idnum = nick->idnum;
03524         strcpy(chan->id.nick, nick->nick);
03525         chan->facc = 0;
03526     }
03527     chan->timestamp = CTime;
03528     sSend(":%s NOTICE %s :You are now the identified founder of %s",
03529           ChanServ, from, chan->name);
03530     /* If a founder identifies, but is not in the ops list, that's generally a bad thing. */
03531 
03532     if (!chan->facc)
03533         return RET_OK;
03534 
03535     if (nick->reg && nick->caccess > 1)
03536     {
03537         newitem = getChanOpData(chan, from);
03538 
03539         if (newitem == NULL) {
03540             newitem = (cAccessList *) oalloc(sizeof(cAccessList));
03541             newitem->uflags = FOUNDER;
03542             newitem->nickId = nick->reg->regnum;
03543             addChanOp(chan, newitem);
03544         } else if (newitem->uflags < FOUNDER)
03545             newitem->uflags = FOUNDER;
03546     }
03547 
03548     GoodPwChan(nick, chan);
03549     chan->chpw_key = 0;
03550 
03551     return RET_OK;
03552 }
03553 
03554 /*--------------------------------------------------------------------*/
03555 
03562 CCMD(cs_addop)
03563 {
03564     char *from = nick->nick;
03565     RegChanList *chan;
03566     services_cmd_id thisCmd = CS_ADDOP;
03567     int targLevel;
03568 
03569     if (tolower(*args[0]) == 'd')
03570         thisCmd = CS_DELOP;
03571 
03572     if (numargs < 3) {
03573         if (thisCmd == CS_ADDOP)
03574             sSend(":%s NOTICE %s :You must specify both a channel to add "
03575                   "to, and a nick for ADDOP.", ChanServ, from);
03576         else
03577             sSend
03578                 (":%s NOTICE %s :You must specify both a channel to delete "
03579                  "from, and a nickname to remove.", ChanServ, from);
03580         return RET_SYNTAX;
03581     }
03582 
03583     chan = getRegChanData(args[1]);
03584     if (chan == NULL) {
03585         sSend(":%s NOTICE %s :%s channel is not registered.", ChanServ,
03586               from, args[1]);
03587         return RET_NOTARGET;
03588     }
03589 
03590     targLevel = (numargs < 4) ? -1 : opNameLevel(args[3]);
03591 
03592     if ((thisCmd == CS_ADDOP) && targLevel <= 0)
03593         targLevel = AOP;
03594 
03595     return do_chanop(thisCmd, nick, chan, args[2], targLevel);
03596 }
03597 
03607 static cmd_return do_chanop(services_cmd_id cmd,
03608                             UserList * isfrom,
03609                             RegChanList * chan,
03610                             const char *cTargetNick,
03611                             int tarLevel
03612 )
03613 {
03614     int mylevel;
03615 
03616     if (!isfrom || !chan || (!cTargetNick && cmd != CS_LISTOP))
03617         return RET_FAIL;
03618     mylevel = getChanOp(chan, isfrom->nick);
03619 
03620         if (isFounder(chan, isfrom))
03621         mylevel = FOUNDER;
03622 
03623     if ((AOP > mylevel) && !opFlagged(isfrom, OOPER | OVERRIDE)) {
03624         sSend
03625             (":%s NOTICE %s :Your access is too low to modify the channel oplist.",
03626              ChanServ, isfrom->nick);
03627         return RET_NOPERM;
03628     }
03629 
03630     if (chan->ops >= ChanMaxOps(chan) && (cmd == CS_ADDOP)
03631         && !opFlagged(isfrom, OVERRIDE)) {
03632         sSend(":%s NOTICE %s :%s has too many ops", ChanServ, isfrom->nick,
03633               chan->name);
03634         sSend(":%s GLOBOPS :%s tries to add too many ops to %s.", ChanServ, isfrom->nick, chan->name);
03635 
03636         if (addFlood(isfrom, 10))
03637             return RET_KILLED;
03638 
03639         return RET_FAIL;
03640     }
03641 
03642     if (!opFlagged(isfrom, OVERRIDE | ODMOD | OOPER)
03643         && getChanOpId(chan, isfrom->nick) < AOP) {
03644         sSend
03645             (":%s NOTICE %s :You must identify to modify channel op lists.",
03646              ChanServ, isfrom->nick);
03647         return RET_NOPERM;
03648     }
03649 
03650     switch (cmd) {
03651     default:
03652         break;
03653     case CS_ADDOP:
03654         return do_chanop_add(isfrom, chan, cTargetNick, tarLevel);
03655         break;
03656     case CS_DELOP:
03657         return do_chanop_del(isfrom, chan, cTargetNick, tarLevel);
03658         break;
03659     case CS_LISTOP:
03660         return do_chanop_list(isfrom, chan, cTargetNick, tarLevel);
03661         break;
03662     }
03663 
03664     return RET_FAIL;
03665 }
03666 
03675 static cmd_return do_chanop_add(UserList * isfrom, 
03676                                 RegChanList * chan,
03677                                 const char *cTargetNick, 
03678                                 int tarLevel
03679 )
03680 {
03681     const char *from = isfrom->nick;
03682     char accNick[NICKLEN+1];
03683     cAccessList *targetEntry, *anop;
03684     RegNickList *tarRegNick;
03685     int currentlevel;
03686     int maxEdit = FOUNDER;
03687 
03688     int mylevel = getMiscChanOp(chan, isfrom->nick, ChanGetIdent(chan), accNick);
03689 
03690     if ((mylevel < FOUNDER) && isOper(isfrom) && cTargetNick
03691         && opFlagged(isfrom, ODMOD | OVERRIDE)) {
03692         sSend(":%s GLOBOPS :%s using override add on %s",
03693               ChanServ, isfrom->nick, chan->name);
03694         mylevel = FOUNDER + 1;
03695     }
03696     targetEntry = getChanOpData(chan, cTargetNick);
03697     tarRegNick = getRegNickData(cTargetNick);
03698 
03699     if (tarRegNick == NULL) {
03700         sSend
03701             (":%s NOTICE %s :The nickname %s is not registered, nicknames must be registered to add as ops",
03702              ChanServ, from, cTargetNick);
03703         return RET_NOTARGET;
03704     }
03705 
03706     if (mylevel <= AOP)
03707         maxEdit = 0;
03708     else if (mylevel < MSOP)
03709         maxEdit = 1;
03710     else if (mylevel < SOP)
03711         maxEdit = MAOP;
03712     else if (mylevel < FOUNDER)
03713         maxEdit = mylevel - 1;
03714     else
03715         maxEdit = mylevel;
03716 
03717     if (targetEntry) {
03718         if (tarLevel == -1) {
03719             sSend(":%s NOTICE %s :%s is already in the channel %s "
03720                   "list, and you did not specify a valid level to change"
03721                   " them to.", ChanServ, isfrom->nick,
03722                   opLevelName(tarLevel, 0), cTargetNick);
03723             return RET_FAIL;
03724         }
03725 
03726         if (tarLevel < 1 || tarLevel > FOUNDER) {
03727             sSend
03728                 (":%s NOTICE %s :The level you specified (%d) is not a valid level",
03729                  ChanServ, from, tarLevel);
03730             return RET_EFAULT;
03731         }
03732 
03733         if ((anop = getChanOpData(chan, cTargetNick)))
03734             currentlevel = anop->uflags;
03735         else
03736             currentlevel = 0;
03737 
03738         if ((!opFlagged(isfrom, ODMOD | OVERRIDE)
03739                 && !isFounder(chan, isfrom)) && mylevel != FOUNDER) {
03740             if (maxEdit >= mylevel)
03741                 maxEdit = mylevel - 1;
03742 
03743             if (mylevel < tarLevel || maxEdit < tarLevel) {
03744                 sSend
03745                     (":%s NOTICE %s :A%s %s cannot promote an operator to level %s.",
03746                      ChanServ, from, mylevel == MAOP ? "n" : "",
03747                      opLevelName(mylevel, 2), opLevelName(tarLevel, 2));
03748                 return RET_NOPERM;
03749             }
03750 
03751             if (maxEdit < currentlevel || mylevel < currentlevel) {
03752                 sSend
03753                     (":%s NOTICE %s :A%s %s cannot modify the access level of a%s %s.",
03754                      ChanServ, from, mylevel == 5 ? "n" : "",
03755                      opLevelName(mylevel, 2),
03756                      currentlevel == MAOP ? "n" : "",
03757                      opLevelName(currentlevel, 2));
03758                 return RET_NOPERM;
03759             }
03760         }
03761 
03762         /* It seems this person can modify the level to whatever level
03763          * was specified */
03764         sSend(":%s NOTICE %s :%s was %s from %s to %s.", ChanServ, from,
03765               cTargetNick,
03766               ((tarLevel > targetEntry->uflags) ? "promoted" : "demoted"),
03767               opLevelName(targetEntry->uflags, 2), opLevelName(tarLevel,
03768                                                                2));
03769 
03770         targetEntry->uflags = tarLevel;
03771         return RET_OK_DB;
03772     }
03773 
03774     if (tarLevel == -1)
03775         tarLevel = MAOP;
03776 
03777     if ((!opFlagged(isfrom, ODMOD | OVERRIDE) && !isFounder(chan, isfrom)
03778              && mylevel != FOUNDER)) {
03779         if (maxEdit >= FOUNDER)
03780             maxEdit = FOUNDER - 1;
03781     }
03782 
03783     if (tarLevel > maxEdit) {
03784         sSend
03785             (":%s NOTICE %s :Permission denied -- you cannot add ops at levels higher than %s (%d).",
03786              ChanServ, from, opLevelName(maxEdit, 2), maxEdit);
03787         return RET_NOPERM;
03788     }
03789 
03790     if ((tarRegNick->flags & NOADDOP) && 
03791              (!isOper(isfrom) ||
03792                 (!opFlagged(isfrom, OVERRIDE | OSERVOP)
03793         && !opFlagged(isfrom, OVERRIDE | ODMOD))) ) {
03794         sSend
03795             (":%s NOTICE %s :The nickname %s has set a feature which disallows the adding of this nickname to an oplist.",
03796              ChanServ, from, cTargetNick);
03797         return RET_NOPERM;
03798     }
03799 
03800     targetEntry = (cAccessList *) oalloc(sizeof(cAccessList));
03801 
03802     if (tarLevel < 1 || tarLevel > FOUNDER) {
03803         sSend
03804             (":%s NOTICE %s :The level you specified (%d) is not a valid level",
03805              ChanServ, from, tarLevel);
03806         FREE(targetEntry);
03807         return RET_EFAULT;
03808     }
03809 
03810     if (!tarLevel)
03811         targetEntry->uflags = AOP;
03812     else
03813         targetEntry->uflags = tarLevel;
03814 
03815     targetEntry->nickId = tarRegNick->regnum;
03816     sSend(":%s NOTICE %s :Added op(%i) %s(%i)", ChanServ, from,
03817           chan->ops + 1, cTargetNick, targetEntry->uflags);
03818     sendToChanOps(getChanData(chan->name), "%s!%s added op %s(%i)",
03819                               accNick, from, cTargetNick,
03820                               targetEntry->uflags);
03821     addChanOp(chan, targetEntry);
03822     return RET_OK_DB;
03823 }
03824 
03833 static cmd_return do_chanop_del(UserList * isfrom, RegChanList * chan,
03834                                 const char *cTargetNick, int tarLevel)
03835 {
03836     char *from = isfrom->nick;
03837     cAccessList *delop;
03838     const char *tmpNick;
03839     int a, x;
03840     int maxEdit = FOUNDER;
03841 
03842     if ((x = atoi(cTargetNick))) {
03843         for (delop = chan->firstOp; delop; delop = delop->next) {
03844             if (tarLevel != delop->uflags && tarLevel != -1)
03845                 continue;
03846             if (delop->index == x)
03847                 break;
03848         }
03849         if (delop == NULL) {
03850             sSend
03851                 (":%s NOTICE %s :%s is not an index in the %s list for %s",
03852                  ChanServ, from, cTargetNick,
03853                  tarLevel == -1 ? "ops" : opLevelName(tarLevel, 2),
03854                  chan->name);
03855             return RET_NOTARGET;
03856         }
03857     } else {
03858             delop = getChanOpData(chan, cTargetNick);
03859             if (delop && tarLevel != -1 && tarLevel != delop->uflags)
03860                 delop = NULL;
03861     }
03862 
03863     if (delop == NULL) {
03864         sSend(":%s NOTICE %s :%s is not in the %s list for %s", ChanServ,
03865               from, cTargetNick,
03866               tarLevel == -1 ? "ops" : opLevelName(tarLevel, 2),
03867               chan->name);
03868         return RET_NOTARGET;
03869     }
03870 
03871     a = getChanOp(chan, from);
03872 
03873     if (opFlagged(isfrom, OOPER | ODMOD | OVERRIDE)) {
03874         sSend(":%s GLOBOPS :%s using directmod on %s (delop %s [%d])",
03875               ChanServ, isfrom->nick, chan->name, cTargetNick, tarLevel);
03876         a = FOUNDER;
03877     }
03878 
03879     if (a < AOP)
03880         maxEdit = 0;
03881     else if (a < MSOP)
03882         maxEdit = 1;
03883     else if (a < SOP)
03884         maxEdit = MAOP;
03885     else if (a < FOUNDER)
03886         maxEdit = a - 1;
03887     else
03888         maxEdit = a;
03889 
03890     if ((delop->uflags > maxEdit) && (!isfrom->reg || delop->nickId != isfrom->reg->regnum)) {
03891 /***=======
03892 *   if((newitem->uflags >= MFOUNDER && FOUNDER > mylevel)
03893 *      || (newitem->uflags >= MSOP && MFOUNDER > mylevel)
03894 *      || ((newitem->uflags > (mylevel - 5)) && MFOUNDER > mylevel)
03895 *      || (mylevel < MSOP)) {
03896 **>>>>>>> 1.230.2.23*/
03897         sSend(":%s NOTICE %s :Access denied", ChanServ, from);
03898         return RET_NOPERM;
03899     }
03900 
03901     tmpNick = delop->nickId.getNick();
03902 
03903     if (tmpNick)
03904     {
03905         sendToChanOps(getChanData(chan->name), "%s deleted op %s(%i)", from,
03906                       tmpNick, delop->uflags);
03907         sSend(":%s NOTICE %s :Deleted op %s on %s", ChanServ, from,
03908               tmpNick, chan->name);
03909     }
03910     else {
03911         sendToChanOps(getChanData(chan->name), "%s deleted op %s(%i)", from,
03912                       "*deactivated*", delop->uflags);
03913     }
03914 
03915     delChanOp(chan, delop);
03916     return RET_OK_DB;
03917 }
03918 
03919 
03928 static cmd_return do_chanop_list(UserList * isfrom, RegChanList * chan,
03929                                  const char *cTargetNick, int tarLevel)
03930 {
03931     cAccessList *tmp;
03932     const char *from = isfrom->nick;
03933     const char *tmpName;
03934     RegNickList *tmpOpRnl;
03935     MaskData *tmpMask = NULL;
03936     int i = 0, x = 0;
03937 
03938     if (cTargetNick) 
03939     {
03940             tmpMask = make_mask();
03941 
03942             if (split_userhost(cTargetNick, tmpMask) < 0)
03943             {
03944                 free_mask(tmpMask);
03945                 tmpMask = NULL;
03946             }
03947     }
03948 
03949     if (tarLevel == -1)
03950     {
03951         sSend(":%s NOTICE %s :   %-3s Listing For %s", ChanServ, from,
03952               "Op", chan->name);
03953         sSend(":%s NOTICE %s :(Index) (Level) (Nick)", ChanServ, from);
03954     }
03955     else {
03956         sSend(":%s NOTICE %s :%s Search results for channel %s", ChanServ, from,
03957               opLevelName(tarLevel, 2), chan->name);
03958     }
03959 
03960 
03961 
03962     for (tmp = chan->firstOp; tmp; tmp = tmp->next, i++) {
03963         if (!(tarLevel == -1 || tmp->uflags == tarLevel))
03964             continue;
03965         tmpOpRnl = (tmp->nickId).getNickItem();
03966         tmpName  = tmpOpRnl ? tmpOpRnl->nick : "*deactivated*";
03967 
03968         if (cTargetNick && match(cTargetNick, tmpName))
03969         {
03970             if (tmpMask == NULL || tmpOpRnl == NULL)
03971                 continue;
03972 
03973             if (match(tmpMask->nick, tmpOpRnl->nick) ||
03974                 match(tmpMask->user, tmpOpRnl->user) ||
03975                 match(tmpMask->host, regnick_ugethost(isfrom, tmpOpRnl, 0)))
03976                 continue;
03977         }
03978 
03979         if (tarLevel == -1)
03980         {
03981             sSend(":%s NOTICE %s :(%-3i) (%-4i)  (%-30s)", ChanServ, from,
03982               tmp->index, tmp->uflags, tmpName);
03983         }
03984         else 
03985         {
03986             if (tmpOpRnl)
03987                 sSend(":%s NOTICE %s :%4i %s (%s@%s)", ChanServ, from,
03988                   tmp->index, tmpName, tmpOpRnl->user, regnick_ugethost(isfrom, tmpOpRnl));
03989             else
03990                 sSend(":%s NOTICE %s :%4i %s", ChanServ, from,
03991                   tmp->index, tmpName);
03992         }
03993 
03994 
03995         x++;
03996     }
03997 
03998     if (tarLevel == -1) {
03999         sSend(":%s NOTICE %s :%i op elements searched", ChanServ, from, i);
04000         sSend(":%s NOTICE %s :%i matched given conditions", ChanServ, from, x);
04001     }
04002     else {
04003         sSend(":%s NOTICE %s :[%i match(es) found]", ChanServ, from, x);
04004     }
04005 
04006     if (tmpMask) {
04007         free_mask(tmpMask);
04008     }
04009 
04010     return RET_OK;
04011 }
04012 
04013 /*--------------------------------------------------------------------*/
04014 
04018 int cleanChopList(RegChanList *chan)
04019 {
04020     cAccessList *delbad, *delbad_next;
04021     int removedBad = 0;
04022 
04023     for(delbad = chan->firstOp; delbad; delbad = delbad_next) {
04024         delbad_next = delbad->next;
04025 
04026         if (!delbad->nickId.getNick()) {
04027             delChanOp(chan, delbad);
04028             ++removedBad;
04029         }
04030     }
04031 
04032     return removedBad;
04033 }
04034 
04039 CCMD(cs_clean)
04040 {
04041     char *from = nick->nick;
04042     RegChanList *chan;
04043     int removedBad = 0, aclvl;
04044 
04045     if (numargs < 2) {
04046         sSend(":%s NOTICE %s :You must specify a channel", ChanServ, from);
04047         return RET_SYNTAX;
04048     }
04049 
04050     chan = getRegChanData(args[1]);
04051 
04052     if (chan == NULL) {
04053         sSend(":%s NOTICE %s :%s is not registered", ChanServ, from,
04054               args[1]);
04055         return RET_NOTARGET;
04056     }
04057 
04058     aclvl = getChanOp(chan, from);
04059 
04060     if ((MSOP > aclvl) && (!isOper(nick))) {
04061         sSend
04062             (":%s NOTICE %s :You must be at least level %i in the channel op list to CLEAN.",
04063              ChanServ, from, MSOP);
04064         return RET_NOPERM;
04065     }
04066 
04067     removedBad = cleanChopList(chan);
04068 
04069     if (removedBad)
04070     {
04071         sSend(":%s NOTICE %s :Removed %d chanop entries that were no longer valid.",
04072              ChanServ, from, removedBad);
04073         return RET_OK_DB;
04074     }
04075     else
04076     {
04077         sSend(":%s NOTICE %s :No bad op entries found.",
04078              ChanServ, from);
04079         return RET_OK_DB;
04080     }
04081 }
04082 
04087 CCMD(cs_addak)
04088 {
04089     char *from = nick->nick;
04090     cAkickList *newitem;
04091     RegChanList *chan;
04092     int i;
04093 
04094     if (numargs < 3) {
04095         sSend
04096             (":%s NOTICE %s :You must specify both a channel to add to, and a mask for ADDAK",
04097              ChanServ, from);
04098         return RET_SYNTAX;
04099     }
04100 
04101     chan = getRegChanData(args[1]);
04102     if (!chan) {
04103         sSend
04104             (":%s NOTICE %s :The channel must be registered to allow addition/removal of akicks",
04105              ChanServ, from);
04106         return RET_FAIL;
04107     }
04108 
04109     if (MSOP > getChanOp(chan, from)
04110         && (!opFlagged(nick, OVERRIDE | ODMOD | OOPER))) {
04111         sSend(":%s NOTICE %s :Your access is too low to add akicks",
04112               ChanServ, from);
04113         return RET_NOPERM;
04114     }
04115 
04116     if (chan->akicks >= (int)ChanMaxAkicks(chan) && (!opFlagged(nick, OVERRIDE))) {
04117         sSend(":%s NOTICE %s :%s has too many akicks", ChanServ, from,
04118               chan->name);
04119         sSend(":%s GLOBOPS :%s tries to add too many akicks to %s.", ChanServ, from, chan->name);
04120 
04121         if (addFlood(nick, 10))
04122             return RET_KILLED;
04123         return RET_FAIL;
04124     }
04125     newitem = getChanAkick(chan, args[2]);
04126     if (newitem) {
04127         sSend(":%s NOTICE %s :%s is already in the akick list", ChanServ,
04128               from, args[2]);
04129         return RET_FAIL;
04130     }
04131     if (!index(args[2], '!') || !index(args[2], '@')) {
04132         sSend(":%s NOTICE %s :Invalid AKick mask %s", ChanServ, from,
04133               args[2]);
04134         return RET_EFAULT;
04135     }
04136 
04137     newitem = (cAkickList *) oalloc(sizeof(cAkickList));
04138     strncpyzt(newitem->mask, args[2], USERLEN + HOSTLEN + 2);
04139     snprintf(newitem->reason, 51 + NICKLEN, "%s:", from);
04140     for (i = 3;
04141          i < numargs
04142          && (strlen(newitem->reason) + strlen(args[i]) + 2) <= NICKLEN + 51; i++) {
04143         strcat(newitem->reason, " ");
04144         strcat(newitem->reason, args[i]);
04145     }
04146     newitem->added = CTime;
04147     addChanAkick(chan, newitem);
04148     sendToChanOps(getChanData(chan->name), "%s added akick %s (%s)", from,
04149                   newitem->mask, newitem->reason);
04150     sSend(":%s NOTICE %s :Added Akick %s to %s", ChanServ, from,
04151           newitem->mask, chan->name);
04152     return RET_OK_DB;
04153 }
04154 
04159 CCMD(cs_wipeak)
04160 {
04161     char *from = nick->nick;
04162     RegChanList *chan;
04163     cAkickList *curr_akick;
04164     cAkickList *next_akick;
04165 
04166 
04167     if (numargs < 2) {
04168         sSend(":%s NOTICE %s :You must specify a channel.", ChanServ,
04169               from);
04170         return RET_SYNTAX;
04171     }
04172 
04173     chan = getRegChanData(args[1]);
04174 
04175     if (chan == NULL) {
04176         sSend(":%s NOTICE %s :The channel is not registered", ChanServ,
04177               from);
04178         return RET_NOTARGET;
04179     }
04180 
04181     if (!opFlagged(nick, OVERRIDE | ODMOD)
04182         && (MFOUNDER > getChanOp(chan, from))) {
04183         sSend(":%s NOTICE %s :Access denied", ChanServ, from);
04184         return RET_NOPERM;
04185     }
04186 
04187     for (curr_akick = chan->firstAkick; curr_akick != (cAkickList *) NULL;
04188          curr_akick = next_akick) {
04189         next_akick = curr_akick->next;
04190         delChanAkick(chan, curr_akick);
04191     }
04192 
04193     sendToChanOps(getChanData(chan->name), "%s wiped the akick list.",
04194                   from);
04195     sSend(":%s NOTICE %s :All Akicks removed.", ChanServ, from);
04196     return RET_OK_DB;
04197 }
04198 
04199 
04204 CCMD(cs_wipeop)
04205 {
04206     char *from = nick->nick;
04207     RegChanList *chan;
04208     cAccessList *curr_op;
04209     cAccessList *next_op;
04210 
04211 
04212     if (numargs < 2) {
04213         sSend(":%s NOTICE %s :You must specify a channel.", ChanServ,
04214               from);
04215         return RET_SYNTAX;
04216     }
04217 
04218     chan = getRegChanData(args[1]);
04219 
04220     if (!opFlagged(nick, OVERRIDE | ODMOD)) {
04221         sSend(":%s NOTICE %s :Permission Denied -- this command is only available through DMOD and when using override mode.", ChanServ, from);
04222         return RET_NOPERM;
04223     }
04224 
04225     if (chan == NULL) {
04226         sSend(":%s NOTICE %s :The channel is not registered", ChanServ,
04227               from);
04228         return RET_NOTARGET;
04229     }
04230 
04231     for (curr_op = chan->firstOp; curr_op != (cAccessList *) NULL;
04232          curr_op = next_op) {
04233         next_op = curr_op->next;
04234         delChanOp(chan, curr_op);
04235     }
04236 
04237     sSend(":%s NOTICE %s :All chanops removed.", ChanServ, from);
04238     return RET_OK_DB;
04239 }
04240 
04245 CCMD(cs_delak)
04246 {
04247     char *from = nick->nick;
04248     RegChanList *chan;
04249     cAkickList *delak;
04250     int x;
04251 
04252     if (numargs < 3) {
04253         sSend
04254             (":%s NOTICE %s :You must specify a channel and the AKick to delete",
04255              ChanServ, from);
04256         return RET_SYNTAX;
04257     }
04258 
04259     chan = getRegChanData(args[1]);
04260 
04261     if (chan == NULL) {
04262         sSend(":%s NOTICE %s :The channel is not registered", ChanServ,
04263               from);
04264         return RET_NOTARGET;
04265     }
04266 
04267     if ((x = atoi(args[2]))) {
04268         for (delak = chan->firstAkick; delak; delak = delak->next) {
04269             if (delak->index == x)
04270                 break;
04271         }
04272     } else
04273         delak = getChanAkick(chan, args[2]);
04274 
04275     if (delak == NULL) {
04276         sSend(":%s NOTICE %s :%s is not in the akick list for %s",
04277               ChanServ, from, args[2], chan->name);
04278         return RET_NOTARGET;
04279     }
04280 
04281     if (MSOP > getChanOp(chan, from) && !opFlagged(nick, OVERRIDE | ODMOD)) {
04282         sSend(":%s NOTICE %s :Access denied", ChanServ, from);
04283         return RET_NOPERM;
04284     }
04285 
04286     sendToChanOps(getChanData(chan->name), "%s deleted akick %s", from,
04287                   delak->mask);
04288     sSend(":%s NOTICE %s :Removed AKick %s on %s", ChanServ, from,
04289           delak->mask, chan->name);
04290     delChanAkick(chan, delak);
04291     return RET_OK_DB;
04292 }
04293 
04294 /*--------------------------------------------------------------------*/
04295 
04300 CCMD(cs_listop)
04301 {
04302     char *from = nick->nick;
04303     int levelsearch = 0;
04304     char namesearch[NICKLEN];
04305 
04306     RegChanList *tmpchan;
04307 
04308     bzero(namesearch, NICKLEN);
04309 
04310     if (numargs == 1) {
04311         sSend(":%s NOTICE %s :You must specify a channel", ChanServ, from);
04312         return RET_SYNTAX;
04313     }
04314 
04315     if (numargs == 2) {
04316         levelsearch = -1;
04317         namesearch[0] = 0;
04318     }
04319     if (numargs == 3) {
04320         sSend(":%s NOTICE %s :Improper command format", ChanServ, from);
04321         return RET_SYNTAX;
04322     }
04323     if (numargs == 4) {
04324         if (!strcasecmp(args[2], "-l"))
04325             levelsearch = atoi(args[3]);
04326         else if (!strcasecmp(args[2], "-n"))
04327             strncpyzt(namesearch, args[3], NICKLEN);
04328         else {
04329             sSend(":%s NOTICE %s :Unknown search parameter", ChanServ,
04330                   from);
04331             return RET_SYNTAX;
04332         }
04333     }
04334 
04335     tmpchan = getRegChanData(args[1]);
04336 
04337     if ((tmpchan == NULL)) {
04338         sSend(":%s NOTICE %s :%s is not registered", ChanServ, from,
04339               args[1]);
04340         return RET_NOTARGET;
04341     }
04342 
04343     if ((getChanOp(tmpchan, from) < 1) && !isOper(nick)) {
04344         sSend(":%s NOTICE %s :Access denied", ChanServ, from);
04345         return RET_NOPERM;
04346     }
04347 
04348     return do_chanop_list(nick, tmpchan, namesearch[0] ? namesearch : NULL,
04349                           levelsearch);
04350 }
04351 
04352 /*--------------------------------------------------------------------*/
04353 
04358 CCMD(cs_listak)
04359 {
04360     char *from = nick->nick;
04361     char aktime[35];
04362     RegChanList *tmpchan;
04363     int i = 0, x = 0;
04364     char namesearch[NICKLEN + USERLEN + HOSTLEN + 5];
04365     cAkickList *tmp;
04366 
04367     bzero(namesearch, NICKLEN + USERLEN + HOSTLEN);
04368     aktime[0] = '\0';
04369 
04370     if (numargs == 1) {
04371         sSend(":%s NOTICE %s :You must specify a channel", ChanServ, from);
04372         return RET_SYNTAX;
04373     }
04374 
04375     if (numargs == 2)
04376         namesearch[0] = 0;
04377 
04378     if (numargs == 3) {
04379         sSend(":%s NOTICE %s :Improper command format", ChanServ, from);
04380         return RET_SYNTAX;
04381     }
04382 
04383     if (numargs == 4) {
04384         if (!strcasecmp(args[2], "-n")) {
04385             strncpyzt(namesearch, args[3], NICKLEN + USERLEN + HOSTLEN);
04386             namesearch[sizeof(namesearch) - 1] = '\0';
04387         } else {
04388             sSend(":%s NOTICE %s :Unknown search parameter", ChanServ,
04389                   from);
04390             return RET_SYNTAX;
04391         }
04392     }
04393 
04394     tmpchan = getRegChanData(args[1]);
04395 
04396     if (tmpchan == NULL) {
04397         sSend(":%s NOTICE %s :%s is not registered", ChanServ, from,
04398               args[1]);
04399         return RET_NOTARGET;
04400     }
04401 
04402     if ((getChanOp(tmpchan, from) < MAOP) && !isOper(nick)) {
04403         sSend(":%s NOTICE %s :Access denied", ChanServ, from);
04404         return RET_NOPERM;
04405     }
04406 
04407     sSend(":%s NOTICE %s :Akick Listing For %s", ChanServ, from, args[1]);
04408     sSend(":%s NOTICE %s :(Index) (Mask)", ChanServ, from);
04409     sSend(":%s NOTICE %s :(Time [GMT])     (Reason)", ChanServ, from);
04410 
04411     for (tmp = tmpchan->firstAkick; tmp; tmp = tmp->next, i++) {
04412         if (namesearch[0]) {
04413             if (!match(namesearch, tmp->mask)) {
04414                 strftime(aktime, sizeof(aktime), "%a %Y-%b-%d %T",
04415                          gmtime(&tmp->added));
04416                 aktime[sizeof(aktime) - 1] = '\0';
04417                 sSend(":%s NOTICE %s :(%-3i) (%-s)", ChanServ, from,
04418                       tmp->index, tmp->mask);
04419                 sSend(":%s NOTICE %s :(%-20s) (%-s)", ChanServ, from,
04420                       aktime, tmp->reason);
04421                 x++;
04422                 if ((x >= 15) && !isOper(nick) && (x % 3) == 0) {
04423                     if (nick->floodlevel.GetLev() >= 66) {
04424                         sSend(":%s NOTICE %s :Command cancelled (flood protection).", ChanServ, nick->nick);
04425                         sSend(":%s NOTICE %s :Please wait at least 30 seconds before sending services further commands.", ChanServ, nick->nick);
04426                         return RET_FAIL;
04427                     }
04428                     if (addFlood(nick, 1))
04429                         return RET_FAIL;
04430                 }
04431             }
04432         } else {
04433             strftime(aktime, sizeof(aktime), "%a %Y-%b-%d %T",
04434                      gmtime(&tmp->added));
04435             aktime[sizeof(aktime) - 1] = '\0';
04436             sSend(":%s NOTICE %s :(%-3i) (%-s)", ChanServ, from,
04437                   tmp->index, tmp->mask);
04438             sSend(":%s NOTICE %s :(%-20s) (%-s)", ChanServ, from, aktime,
04439                   tmp->reason);
04440             x++;
04441             if ((x >= 15) && !isOper(nick) && (x % 3) == 0) {
04442                 if (nick->floodlevel.GetLev() >= 66) {
04443                     sSend(":%s NOTICE %s :Command cancelled (flood protection).", ChanServ, nick->nick);
04444                     sSend(":%s NOTICE %s :Please wait at least 30 seconds before sending services further commands.", ChanServ, nick->nick);
04445                     return RET_FAIL;
04446                 }
04447                 if (addFlood(nick, 1))
04448                     return RET_FAIL;
04449             }
04450         }
04451     }
04452 
04453     sSend(":%s NOTICE %s :%i akick elements searched", ChanServ, from, i);
04454     sSend(":%s NOTICE %s :%i matched given conditions", ChanServ, from, x);
04455     return RET_OK;
04456 }
04457 
04458 /*--------------------------------------------------------------------*/
04459 
04464 CCMD(cs_drop)
04465 {
04466     RegChanList *tmp;
04467     ChanList *chan;
04468     char *from = nick->nick;
04469 
04470     if (numargs < 3) {
04471         sSend(":%s NOTICE %s :You must specify a channel and password",
04472               ChanServ, from);
04473         return RET_SYNTAX;
04474     }
04475 
04476     tmp = getRegChanData(args[1]);
04477 
04478     if (tmp == NULL) {
04479         sSend(":%s NOTICE %s :The channel is not registered", ChanServ,
04480               from);
04481         return RET_NOTARGET;
04482     }
04483 
04484     if ((tmp->flags & (CBANISH | CCLOSE)) && !isRoot(nick)) {
04485         sSend(":%s NOTICE %s :You cannot drop that channel because it is closed or banished.",
04486              ChanServ, from);
04487         return RET_EFAULT;
04488     }
04489 
04490     if (isFounder(tmp, nick) == 0) {
04491         sSend(":%s NOTICE %s :Access denied", ChanServ, from);
04492         return RET_NOPERM;
04493     }
04494 
04495     if (!Valid_pw(args[2], tmp->password, ChanGetEnc(tmp))) {
04496         sSend(":%s NOTICE %s :Incorrect password, this has been logged",
04497               ChanServ, from);
04498         chanlog->log(nick, CS_DROP, args[1], LOGF_BADPW);
04499 
04500         if (BadPwChan(nick, tmp))
04501             return RET_KILLED;
04502 
04503         return RET_BADPW;
04504     }
04505 
04506     if ((chan = getChanData(tmp->name)))
04507         sendToChanOps(chan, "%s just dropped %s", from, args[1]);
04508     sSend
04509         (":%s NOTICE %s :Channel registration for %s has been discontinued",
04510          ChanServ, from, args[1]);
04511     chan = getChanData(args[1]);
04512     if (chan)
04513         chan->reg = NULL;
04514     chanlog->log(nick, CS_DROP, args[1], 0);
04515 
04516     GoodPwChan(nick, tmp);
04517     delRegChan(tmp);
04518     return RET_OK_DB;
04519 }
04520 
04521 /*--------------------------------------------------------------------*/
04522 
04528 CCMD(cs_op)
04529 {
04530     char *from = nick->nick;
04531     int a, mylevel;
04532     RegChanList *tmp;
04533     ChanList *chan;
04534     cNickList *tmpnick;
04535 
04536     if (numargs < 2) {
04537         sSend(":%s NOTICE %s :You must specify a channel", ChanServ, from);
04538         return RET_SYNTAX;
04539     }
04540 
04541     tmp = getRegChanData(args[1]);
04542     chan = getChanData(args[1]);
04543     if (chan == NULL) {
04544         sSend(":%s NOTICE %s :%s: No such channel", ChanServ, from,
04545               args[1]);
04546         return RET_NOTARGET;
04547     }
04548     if (tmp == NULL) {
04549         sSend
04550             (":%s NOTICE %s :Cannot receive ops on non registered channel",
04551              ChanServ, from);
04552         return RET_FAIL;
04553     }
04554 
04555     if (numargs == 3)
04556         tmpnick = getChanUserData(chan, getNickData(args[2]));
04557     else
04558         tmpnick = getChanUserData(chan, nick);
04559 
04560     if (tmpnick == NULL) {
04561         sSend(":%s NOTICE %s :%s is not in %s", ChanServ, from,
04562               (numargs == 3) ? args[2] : from, args[1]);
04563         return RET_FAIL;
04564     }
04565 
04566     a = getChanOp(tmp, (numargs == 3) ? args[2] : from);
04567 
04568     if (a == 0 || ((mylevel = getChanOp(tmp, from)) < MAOP)) {
04569         sSend(":%s NOTICE %s :Access denied", ChanServ, from);
04570         return RET_NOPERM;
04571     }
04572 
04573     if ((mylevel < MFOUNDER) && (mylevel <= AOP) && (mylevel < a)) {
04574         sSend
04575             (":%s NOTICE %s :Permission denied -- a(n) %s cannot use this command on a(n) %s.",
04576              ChanServ, from, opLevelName(mylevel, 0), opLevelName(a, 0));
04577         return RET_NOPERM;
04578     }
04579 
04580     if (getChanData(tmp->name)
04581         && strcmp(from, (numargs == 3) ? args[2] : from)) {
04582         if (mylevel < AOP) {
04583             sSend(":%s NOTICE %s :Access Denied", ChanServ, from);
04584             return RET_NOPERM;
04585         }
04586         sendToChanOpsAlways(getChanData(tmp->name),
04587                             "%s used \002OP\002 on %s", from,
04588                             (numargs == 3) ? args[2] : from);
04589     }
04590 
04591     if (mylevel < AOP || a < AOP) {
04592         sSend(":%s MODE %s +v %s", ChanServ, args[1],
04593               (numargs == 3) ? args[2] : from);
04594         tmpnick->op |= CHANVOICE;
04595         tmp->timestamp = CTime;
04596 
04597 
04598         if (tmp && tmp->name && !strcasecmp(tmp->name, HELPOPS_CHAN)
04599             && (getChanOp(tmp, nick->nick) >= 4) && (numargs < 3)) {
04600             sSend(":ChanServ MODE %s :+h", from);
04601             nick->oflags |= NISHELPOP;
04602         }
04603 
04604         return RET_OK;
04605     } else {
04606         sSend(":%s MODE %s +o %s", ChanServ, args[1],
04607               (numargs == 3) ? args[2] : from);
04608         tmpnick->op |= CHANOP;
04609         tmp->timestamp = CTime;
04610 
04611         if (tmp && tmp->name && tmp && !strcasecmp(tmp->name, HELPOPS_CHAN)
04612             && !strcasecmp(from, (numargs >= 3) ? args[2] : from)
04613             && (getChanOp(tmp, nick->nick) >= 4) && (numargs < 3)) {
04614             sSend(":ChanServ MODE %s :+h", from);
04615             nick->oflags |= NISHELPOP;
04616         }
04617 
04618         return RET_OK;
04619     }
04620     return RET_OK;
04621 }
04622 
04623 /*--------------------------------------------------------------------*/
04624 
04629 CCMD(cs_deop)
04630 {
04631     char *from = nick->nick;
04632     UserList *target;
04633     ChanList *chan;
04634     cNickList *deopto;
04635     int mylevel, victlevel, self_deop = 0;
04636 
04637     if (numargs < 3) {
04638         sSend(":%s NOTICE %s :You must specify a channel and user to deop",
04639               ChanServ, from);
04640         return RET_SYNTAX;
04641     }
04642 
04643     chan = getChanData(args[1]);
04644 
04645     if (chan == NULL) {
04646         sSend(":%s NOTICE %s :%s: No such channel", ChanServ, from,
04647               args[1]);
04648         return RET_NOTARGET;
04649     }
04650 
04651     if (chan->reg == NULL) {
04652         sSend(":%s NOTICE %s :%s: Is not registered", ChanServ, from,
04653               args[1]);
04654         return RET_NOTARGET;
04655     }
04656 
04657     deopto = getChanUserData(chan, getNickData(args[2]));
04658     target = getNickData((numargs >= 3) ? args[2] : from);
04659 
04660     if (deopto == NULL) {
04661         sSend(":%s NOTICE %s :%s is not in %s", ChanServ, from, args[2],
04662               args[1]);
04663         return RET_NOTARGET;
04664     }
04665 
04666     if ((mylevel = getChanOp(chan->reg, from)) < MAOP) {
04667         sSend(":%s NOTICE %s :Access denied", ChanServ, from);
04668         return RET_NOPERM;
04669     }
04670 
04671     victlevel = getChanOp(chan->reg, args[2]);
04672 
04673     if (!strcmp(from, (numargs == 3) ? args[2] : from))
04674         self_deop = 1;
04675 
04676     if ((victlevel > mylevel) || ((mylevel <= MAOP) && !self_deop)) {
04677         sSend
04678             (":%s NOTICE %s :Permission denied -- a level %d operator may not deop another level %d operator.",
04679              ChanServ, from, mylevel, victlevel);
04680         return RET_NOPERM;
04681     }
04682 
04683     if (chan && chan->reg && target && (target->oflags & NISHELPOP)
04684         && !(target->oflags & NISOPER) && chan->name
04685         && !strcasecmp(chan->name, HELPOPS_CHAN)
04686         && (getChanOp(chan->reg, target->nick) <= 5)) {
04687         sSend(":ChanServ MODE %s :-h", target->nick);
04688         target->oflags &= ~NISHELPOP;
04689     }
04690 
04691     sendToChanOpsAlways(getChanData(chan->reg->name),
04692                         "%s used \002DEOP\002 on %s", from, args[2]);
04693     sSend(":%s MODE %s -ov %s %s", ChanServ, args[1], args[2], args[2]);
04694     deopto->op &= ~CHANOP;
04695     return RET_OK;
04696 }
04697 
04698 /*--------------------------------------------------------------------*/
04699 
04707 CCMD(cs_banish)
04708 {
04709     char *from = nick->nick;
04710     ChanList *ochan;
04711     RegChanList *chan;
04712 
04713     if (!isServop(nick) && !opFlagged(nick, OOPER | OCBANDEL)) {
04714         sSend(":%s NOTICE %s :Access denied", ChanServ, from);
04715         return RET_NOPERM;
04716     }
04717 
04718     if (numargs < 2) {
04719         sSend(":%s NOTICE %s :Must supply at least one argument", ChanServ,
04720               from);
04721         return RET_SYNTAX;
04722     }
04723 
04724     if (!isRoot(nick) && is_sn_chan(args[1])) {
04725         sSend(":%s NOTICE %s :That channel cannot be banished (magic channel).", ChanServ,
04726               from);
04727         return RET_EFAULT;
04728     }
04729 
04730     chan = getRegChanData(args[1]);
04731     if ((*args[1] != '#' && *args[1] != '+')) {
04732         sSend(":%s NOTICE %s :Invalid channel name.", ChanServ, from);
04733         return RET_EFAULT;
04734     }
04735 
04736     if (numargs < 3) {
04737         ochan = getChanData(args[1]);
04738         if (!chan) {
04739             chan = (RegChanList *) oalloc(sizeof(RegChanList));
04740             initRegChanData(chan);
04741 
04742             strncpyzt(chan->name, args[1], CHANLEN);
04743 
04744             addRegChan(chan);
04745             mostchans++;
04746             if (ochan)
04747                 ochan->reg = chan;
04748             chan->founderId = RegId(0, 1);
04749             chan->password[0] = '\0';
04750 
04751             /* strcpy(chan->password, "\2\2\33\377\2\2"); */
04752             chan->timereg = chan->timestamp = CTime;
04753             chan->flags |= CBANISH|CENCRYPT;
04754             chan->mlock = (PM_N | PM_T | MM_K);
04755         }
04756         chan->flags |= CBANISH;
04757 
04758         sSend(":%s NOTICE %s :%s is now \2BANISHED\2.", ChanServ, from,
04759               args[1]);
04760         sSend(":%s GLOBOPS :%s set banish on %s.", ChanServ, from,
04761               args[1]);
04762         chanlog->log(nick, CS_BANISH, chan->name);
04763         return RET_OK_DB;
04764     }
04765 
04766     if (!strcasecmp(args[2], "on") || !strcasecmp(args[2], "yes")
04767         || !strcasecmp(args[2], "banish")) {
04768         sSend(":%s NOTICE %s :%s is now \2BANISHED\2.", ChanServ, from,
04769               args[1]);
04770         sSend(":%s GLOBOPS :%s set banish on %s.", ChanServ, from,
04771               args[1]);
04772         chan->flags |= CBANISH;
04773         chanlog->log(nick, CS_BANISH, chan->name, LOGF_ON);
04774         return RET_OK_DB;
04775     } else if (!strcasecmp(args[2], "off") || !strcasecmp(args[2], "no")) {
04776         sSend(":%s NOTICE %s :%s is now \2not banished\2.", ChanServ, from,
04777               args[1]);
04778         chan->flags &= ~CBANISH;
04779         chanlog->log(nick, CS_BANISH, chan->name, LOGF_OFF);
04780         return RET_OK_DB;
04781     } else {
04782         sSend(":%s NOTICE %s :Unrecognized usage.", ChanServ, from);
04783         sSend(":%s NOTICE %s :BANISH <#channel> [<on|off>]", ChanServ,
04784               from);
04785         return RET_SYNTAX;
04786     }
04787     return RET_OK;
04788 }
04789 
04790 
04791 /*--------------------------------------------------------------------*/
04792 
04801 CCMD(cs_close)
04802 {
04803     char *from = nick->nick;
04804     RegChanList *chan;
04805 
04806     if (!isServop(nick) && !opFlagged(nick, OOPER | OCBANDEL)) {
04807         sSend(":%s NOTICE %s :Access denied", ChanServ, from);
04808         return RET_NOPERM;
04809     }
04810 
04811     if (numargs < 2) {
04812         sSend(":%s NOTICE %s :Must supply at least one argument", ChanServ,
04813               from);
04814         return RET_SYNTAX;
04815     }
04816 
04817     chan = getRegChanData(args[1]);
04818     if (chan == NULL) {
04819         sSend(":%s NOTICE %s :Channel not found", ChanServ, from);
04820         return RET_NOTARGET;
04821     }
04822 
04823     if (numargs < 3) {
04824         sSend(":%s NOTICE %s :%s is \2%s\2.", ChanServ, from, args[1],
04825               chan->flags & CHOLD ? "Closed" : "Not closed");
04826         return RET_OK;
04827     }
04828 
04829     if (!strcasecmp(args[2], "on") || !strcasecmp(args[2], "yes")
04830         || !strcasecmp(args[2], "close")) {
04831         sSend(":%s NOTICE %s :%s is now \2CLOSED\2.", ChanServ, from,
04832               args[1]);
04833         sSend(":%s GLOBOPS :%s closed channel %s.", ChanServ, from,
04834               args[1]);
04835         chan->flags |= CCLOSE;
04836         chanlog->log(nick, CS_CLOSE, chan->name, LOGF_ON);
04837 
04838         return RET_OK_DB;
04839     } else if (!strcasecmp(args[2], "off") || !strcasecmp(args[2], "no")) {
04840         sSend(":%s NOTICE %s :%s is now \2OPEN\2.", ChanServ, from,
04841               args[1]);
04842         chan->flags &= ~CCLOSE;
04843         chanlog->log(nick, CS_CLOSE, chan->name, LOGF_OFF);
04844 
04845         return RET_OK_DB;
04846     } else {
04847         sSend(":%s NOTICE %s :Unrecognized usage.", ChanServ, from);
04848         sSend(":%s NOTICE %s :CLOSE <#channel> [<on|off>]", ChanServ,
04849               from);
04850         return RET_OK_DB;
04851     }
04852 }
04853 
04854 /*--------------------------------------------------------------------*/
04855 
04864 CCMD(cs_hold)
04865 {
04866     char *from = nick->nick;
04867     RegChanList *chan;
04868 
04869     if (!isRoot(nick)) {
04870         sSend(":%s NOTICE %s :Access denied", ChanServ, from);
04871         return RET_NOPERM;
04872     }
04873 
04874     if (numargs < 2) {
04875         sSend(":%s NOTICE %s :Must supply at least one argument", ChanServ,
04876               from);
04877         return RET_SYNTAX;
04878     }
04879 
04880     chan = getRegChanData(args[1]);
04881     if (chan == NULL) {
04882         sSend(":%s NOTICE %s :Channel not found", ChanServ, from);
04883         return RET_NOTARGET;
04884     }
04885     if (numargs < 3) {
04886         sSend(":%s NOTICE %s :%s is \2%s\2.", ChanServ, from, args[1],
04887               chan->flags & CHOLD ? "Held" : "Not Held");
04888         return RET_OK;
04889     }
04890     if (!strcasecmp(args[2], "on") || !strcasecmp(args[2], "yes")) {
04891         sSend(":%s NOTICE %s :%s is now \2Held\2.", ChanServ, from,
04892               args[1]);
04893         chan->flags |= CHOLD;
04894         chanlog->log(nick, CS_HOLD, chan->name, LOGF_ON);
04895         return RET_OK_DB;
04896     } else if (!strcasecmp(args[2], "off") || !strcasecmp(args[2], "no")) {
04897         sSend(":%s NOTICE %s :%s is now \2Held\2.", ChanServ, from,
04898               args[1]);
04899         chan->flags &= ~CHOLD;
04900         chanlog->log(nick, CS_HOLD, chan->name, LOGF_OFF);
04901         return RET_OK_DB;
04902     } else {
04903         sSend(":%s NOTICE %s :Unrecognized usage.", ChanServ, from);
04904         sSend(":%s NOTICE %s :HOLD <#channel> <on|off>", ChanServ, from);
04905         return RET_SYNTAX;
04906     }
04907     return RET_OK;
04908 }
04909 
04910 /*--------------------------------------------------------------------*/
04911 
04934 CCMD(cs_mark)
04935 {
04936     char *from = nick->nick;
04937     RegChanList *chan;
04938 
04939     if (!isServop(nick)) {
04940         sSend(":%s NOTICE %s :Access denied", ChanServ, from);
04941         return RET_NOPERM;
04942     }
04943 
04944     if (numargs < 2) {
04945         sSend(":%s NOTICE %s :Must supply at least one argument", ChanServ,
04946               from);
04947         return RET_SYNTAX;
04948     }
04949 
04950     chan = getRegChanData(args[1]);
04951     if (chan == NULL) {
04952         sSend(":%s NOTICE %s :Channel not found", ChanServ, from);
04953         return RET_NOTARGET;
04954     }
04955     if (numargs < 3) {
04956         sSend(":%s NOTICE %s :%s is \2%s\2.", ChanServ, from, args[1],
04957               chan->flags & CMARK ? "Marked" : "Not Marked");
04958         return RET_OK;
04959     }
04960     if (!strcasecmp(args[2], "on") || !strcasecmp(args[2], "yes")) {
04961         if (chan->flags & CMARK) {
04962             if (chan->markby)
04963                 sSend(":%s NOTICE %s :%s has already been marked by %s.",
04964                       ChanServ, from, args[1], chan->markby);
04965             else
04966                 sSend(":%s NOTICE %s :%s has already been marked.",
04967                       ChanServ, from, args[1]);
04968             return RET_OK;
04969         }
04970         sSend(":%s NOTICE %s :%s is now \2Marked\2.", ChanServ, from,
04971               args[1]);
04972         chan->flags |= CMARK;
04973         if (chan->markby)
04974             FREE(chan->markby);
04975         chan->markby = strdup(nick->nick);
04976         chanlog->log(nick, CS_MARK, chan->name, LOGF_ON);
04977         return RET_OK_DB;
04978     } else if (!strcasecmp(args[2], "off") || !strcasecmp(args[2], "no")) {
04979         sSend(":%s NOTICE %s :%s is now \2Unmarked\2.", ChanServ, from,
04980               args[1]);
04981         chan->flags &= ~CMARK;
04982         chanlog->log(nick, CS_MARK, chan->name, LOGF_OFF);
04983         if (chan->markby)
04984             FREE(chan->markby);
04985         chan->markby = NULL;
04986         return RET_OK_DB;
04987     } else {
04988         sSend(":%s NOTICE %s :Unrecognized usage.", ChanServ, from);
04989         sSend(":%s NOTICE %s :MARK <#channel> <on|off>", ChanServ, from);
04990         return RET_SYNTAX;
04991     }
04992     return RET_OK;
04993 }
04994 
04995 /*--------------------------------------------------------------------*/
04996 
05003 CCMD(cs_clist)
05004 {
05005     char *from = nick->nick;
05006     char tmpch[IRCBUF] = "";
05007     ChanList *chan;
05008     cNickList *tmp;
05009     UserList *tnick;
05010     int c;
05011 
05012     if (numargs < 2) {
05013         sSend(":%s NOTICE %s :Must specify a channel name.", ChanServ,
05014               from);
05015         return RET_SYNTAX;
05016     }
05017 
05018     chan = getChanData(args[1]);
05019 
05020     if (!opFlagged(nick, OOPER) && (!chan || !chan->reg || (getChanOpId(chan->reg, from) < SOP))) {
05021         sSend(":%s NOTICE %s :Access denied", ChanServ, from);
05022         return RET_NOPERM;
05023     }
05024 
05025     tnick = getNickData(args[1]);
05026 
05027     if (chan == NULL) {
05028         if (!tnick || !isRoot(nick)) {
05029             sSend(":%s NOTICE %s :Channel not found", ChanServ, from);
05030             return RET_NOTARGET;
05031         }
05032 
05033         /* Another desync detection mechanism */
05034         else {
05035             cNickList *cu;
05036             int i;
05037 
05038             /* Note: This is online data not database data */
05039             sSend(":%s NOTICE %s :*** %s [%s@%s]", ChanServ, from,
05040                   tnick->nick, tnick->user, tnick->host);
05041             sSend(":%s NOTICE %s :ID: %ld, TS: %ld, LT: %ld", ChanServ,
05042                   from, tnick->idnum.getHashKey(), tnick->timestamp, tnick->floodlevel.GetLev());
05043             sSend(":%s NOTICE %s :OF: %d, FL: %d, CA: %d", ChanServ, from,
05044                   tnick->oflags, tnick->floodlevel.GetLev(), tnick->caccess);
05045 
05046             for (i = 0; i < NICKCHANHASHSIZE; i++) {
05047                 if (tnick->chan[i]) {
05048                     if ((cu = getChanUserData(tnick->chan[i], tnick))) {
05049                         if ((cu->op & CHANVOICE))
05050                             strcat(tmpch, "+");
05051                         if ((cu->op & CHANOP))
05052                             strcat(tmpch, "@");
05053                     }
05054                     strncat(tmpch, tnick->chan[i]->name, CHANLEN);
05055                     strcat(tmpch, "  ");
05056                     if (strlen(tmpch) > 80) {
05057                         sSend(":%s NOTICE %s :%s", ChanServ, from, tmpch);
05058                         tmpch[0] = '\0';
05059                     }
05060                 }
05061             }
05062             if (*tmpch != '\0')
05063                 sSend(":%s NOTICE %s :%s", ChanServ, from, tmpch);
05064             return RET_OK;
05065         }
05066     }
05067 
05068 
05069     if (!opFlagged(nick, OVERRIDE | OROOT))
05070         if (chan->modes & (PM_S | PM_I)) {
05071             for (tmp = chan->firstUser; tmp; tmp = tmp->next)
05072                 if (tmp->person == nick)
05073                     break;
05074             if (!tmp) {
05075                 if (!opFlagged(nick, OROOT))
05076                     sSend(":%s NOTICE %s :Channel not found", ChanServ,
05077                           from);
05078                 else {
05079                     sSend
05080                         (":%s NOTICE %s :That channel is secret (+s) or private (+p)",
05081                          ChanServ, from);
05082                     sSend
05083                         (":%s NOTICE %s :Use the %s OVERRIDE command to override.",
05084                          ChanServ, from, OperServ);
05085                 }
05086                 return RET_FAIL;
05087             }
05088         }
05089 
05090     //for (tmp = chan->firstUser; tmp; tmp = tmp->next) {
05091     //  sSend(":%s NOTICE %s :%s -> %s (%i)", ChanServ, from, chan->name,
05092     //            tmp->person->nick, tmp->op);
05093     //
05094     //}
05095 
05096     tmpch[0] = '\0';
05097     c = 0;
05098 
05099     sSend(":%s NOTICE %s :Channel membership for \2%s\2:", ChanServ, from, chan->name);
05100     for (tmp = chan->firstUser; tmp; tmp = tmp->next) {
05101         c += sprintf(tmpch+c, "%.1s%.1s%.1s%-18.30s",
05102                      *tmpch ? " " : "",
05103                      tmp->op & CHANVOICE ? "+" : "",
05104                      tmp->op & CHANOP ? "@" : "",
05105                      tmp->person->nick);
05106         if (c >= 80) {
05107             sSend(":%s NOTICE %s :%s", ChanServ, from, tmpch);
05108             c = 0;
05109             tmpch[0] = '\0';
05110         }   
05111     }
05112 
05113     if (tmpch[0] != '\0')
05114         sSend(":%s NOTICE %s :%s", ChanServ, from, tmpch);
05115     sSend(":%s NOTICE %s :End of channel member list.", ChanServ, from);
05116     return RET_OK;
05117 }
05118 
05119 /*--------------------------------------------------------------------*/
05120 
05126 CCMD(cs_whois)
05127 {
05128     char *from = nick->nick;
05129     UserList *tnick;
05130     char tmpch[(CHANLEN + 4) * NICKCHANHASHSIZE] = "";
05131     cNickList *c;
05132     int i;
05133 
05134     if (!opFlagged(nick, OOPER | OSERVOP)) {
05135         sSend(":%s NOTICE %s :Access denied", ChanServ, from);
05136         return RET_NOPERM;
05137     }
05138 
05139     if (numargs != 2) {
05140         sSend(":%s NOTICE %s :Must supply at least one argument", ChanServ,
05141               from);
05142         return RET_SYNTAX;
05143     }
05144 
05145     tnick = getNickData(args[1]);
05146 
05147     if (!tnick) {
05148         sSend(":%s NOTICE %s :Nick not found", ChanServ, from);
05149         return RET_NOTARGET;
05150     }
05151 
05152     sSend(":%s GLOBOPS :%s uses CWHOIS on %s", ChanServ, from, args[1]);
05153     operlog->log(nick, CS_WHOIS, fullhost1(tnick));
05154 
05155     sSend(":%s NOTICE %s :*** %s [%s@%s]", ChanServ, from, tnick->nick,
05156           tnick->user, tnick->host);
05157     for (i = 0; i < NICKCHANHASHSIZE; i++) {
05158         if (tnick->chan[i]) {
05159             if ((c = getChanUserData(tnick->chan[i], tnick))) {
05160                 if ((c->op & CHANVOICE))
05161                     strcat(tmpch, "+");
05162                 if ((c->op & CHANOP))
05163                     strcat(tmpch, "@");
05164             }
05165             strncat(tmpch, tnick->chan[i]->name, CHANLEN);
05166             strcat(tmpch, "  ");
05167             if (strlen(tmpch) > 80) {
05168                 sSend(":%s NOTICE %s :%s", ChanServ, from, tmpch);
05169                 tmpch[0] = '\0';
05170             }
05171         }
05172     }
05173     if (tmpch[0])
05174         sSend(":%s NOTICE %s :%s", ChanServ, from, tmpch);
05175     return RET_OK;
05176 }
05177 
05178 /*--------------------------------------------------------------------*/
05179 
05184 CCMD(cs_restrict)
05185 {
05186     char *from = nick->nick;
05187     RegChanList *regchan;
05188 
05189     if (numargs < 2) {
05190         sSend(":%s NOTICE %s :Too few arguments to RESTRICT command",
05191               ChanServ, from);
05192         return RET_SYNTAX;
05193     }
05194 
05195     regchan = getRegChanData(args[1]);
05196 
05197     if (!regchan) {
05198         sSend(":%s NOTICE %s :%s is not registered", ChanServ, from,
05199               args[1]);
05200         return RET_NOTARGET;
05201     }
05202 
05203     return cs_set_restrict(NULL, nick, regchan, args, numargs);
05204 }
05205 
05206 /*--------------------------------------------------------------------*/
05207 
05212 CCMD(cs_topiclock)
05213 {
05214     char *from = nick->nick;
05215     RegChanList *regchan;
05216 
05217     if (numargs < 2) {
05218         sSend(":%s NOTICE %s :Too few arguments to TOPICLOCK command",
05219               ChanServ, from);
05220         return RET_SYNTAX;
05221     }
05222 
05223     regchan = getRegChanData(args[1]);
05224 
05225     if (!regchan) {
05226         sSend(":%s NOTICE %s :%s is not registered", ChanServ, from,
05227               args[1]);
05228         return RET_NOTARGET;
05229     }
05230 
05231     return cs_set_topiclock(NULL, nick, regchan, args, numargs);
05232 }
05233 
05234 /*--------------------------------------------------------------------*/
05235 
05240 CCMD(cs_modelock)
05241 {
05242     char *from = nick->nick;
05243     RegChanList *regchan;
05244 
05245     if (numargs < 3) {
05246         sSend(":%s NOTICE %s :Too few arguments to MODELOCK command",
05247               ChanServ, from);
05248         sSend
05249             (":%s NOTICE %s :For usage information, type \2/CHANSERV HELP MLOCK\2",
05250              ChanServ, from);
05251         return RET_SYNTAX;
05252     }
05253 
05254     regchan = getRegChanData(args[1]);
05255 
05256     if (!regchan) {
05257         sSend(":%s NOTICE %s :%s is not registered", ChanServ, from,
05258               args[1]);
05259         return RET_NOTARGET;
05260     }
05261 
05262     if (MFOUNDER > getChanOpId(regchan, from)) {
05263         sSend(":%s NOTICE %s :Access denied", ChanServ, from);
05264         return RET_NOPERM;
05265     }
05266 
05267     return cs_set_mlock(NULL, nick, regchan, args, numargs);
05268 }
05269 
05270 /*--------------------------------------------------------------------*/
05271 
05277 #define CSO(q)  (((size_t) &((RegChanList *)0)->q))
05278 
05284 CCMD(cs_set)
05285 {
05286     char *from = nick->nick;
05287     RegChanList *chan;
05288     ChanList *tmp;
05289     cs_settbl_t *cmd;
05290 
05291     cs_settbl_t cs_setcmd[] = {
05292         /* string       function */
05293         {"opguard", cs_set_bool, FOUNDER, CSO(flags), "op guard",
05294          COPGUARD},
05295         {"protop", cs_set_bool, FOUNDER, CSO(flags), "protop", CPROTOP},
05296         {"keeptopic", cs_set_bool, FOUNDER, CSO(flags), "topic holding",
05297          CKTOPIC},
05298         {"ident", cs_set_bool, FOUNDER, CSO(flags), "ident", CIDENT},
05299         {"quiet", cs_set_bool, FOUNDER + 1, CSO(flags), "quiet changes",
05300          CQUIET},
05301         {"passw*d", cs_set_passwd, FOUNDER + 1},
05302         {"memo*", cs_set_memolvl, FOUNDER},
05303         {"desc*", cs_set_fixed, FOUNDER, CSO(desc), "description",
05304          CHANDESCBUF},
05305         {"founder", cs_set_founder, 0, 0},
05306         {"autogreet", cs_set_string, FOUNDER, CSO(autogreet), NULL, 255},
05307         {"url", cs_set_string, FOUNDER, CSO(url), NULL, 255},
05308         {"mlock", cs_set_mlock, FOUNDER, 0},
05309         {"restrict", cs_set_restrict, MFOUNDER, 0},
05310         {"topiclock", cs_set_topiclock, MFOUNDER, 0},
05311         {"encrypt", cs_set_encrypt, 0, 0},
05312         {NULL}
05313     };
05314 
05315     if (numargs < 2) {
05316         sSend
05317             (":%s NOTICE %s :You must specify arguments after the SET command",
05318              ChanServ, from);
05319         return RET_SYNTAX;
05320     }
05321 
05322     chan = getRegChanData(args[1]);
05323     tmp = getChanData(args[1]);
05324 
05325     if (chan == NULL) {
05326         sSend(":%s NOTICE %s :%s is not registered", ChanServ, from,
05327               args[1]);
05328         return RET_NOTARGET;
05329     }
05330 
05331     if (!isOper(nick) && (chan->flags & CFORCEXFER)) {
05332         sSend(":%s NOTICE %s :services for %s have been temporarily "
05333               "suspended.", ChanServ, from,
05334               args[1]);
05335         return RET_NOTARGET;
05336     }
05337 
05338     if (tmp && (tmp->reg != chan)) {    /* Sanity check */
05339         logDump(corelog,
05340                 "WARNING! tmp->reg != getRegChanData(tmp->name) (%s) (%s)",
05341                 tmp->name, chan->name);
05342         tmp->reg = chan;
05343     }
05344 
05345     if (isOper(nick) && opFlagged(nick, ODMOD | OVERRIDE)) {
05346         sSend(":%s GLOBOPS :%s using directmod on %s with set '%s'",
05347               ChanServ, nick->nick, chan->name, args[2]);
05348     }
05349 
05350     for (cmd = cs_setcmd; cmd->cmd != NULL; cmd++) {
05351         if (!match(cmd->cmd, args[2])) {
05352 
05353 
05354             if ((cmd->aclvl == (FOUNDER + 3))
05355                 && !opFlagged(nick, OOPER | ODMOD | OVERRIDE)) {
05356                 sSend(":%s NOTICE %s :Option not available.\r\n"
05357                       ":%s NOTICE %s :Please try /msg %s HELP", ChanServ,
05358                       from, ChanServ, from, ChanServ);
05359                 return RET_NOPERM;
05360             } else if ((cmd->aclvl == (FOUNDER + 2)) && !isRoot(nick))
05361                 continue;
05362             else if ((cmd->aclvl == (FOUNDER + 1))
05363                      && !opFlagged(nick, OOPER | ODMOD | OVERRIDE)) {
05364                 if (!isFounder(chan, nick)) {
05365                     sSend
05366                         (":%s NOTICE %s :You must identify as channel founder with the channel password to alter the value of that setting.",
05367                          ChanServ, from);
05368                     return RET_NOPERM;
05369                 }
05370             } else if (!opFlagged(nick, OOPER | ODMOD | OVERRIDE)
05371                        && (cmd->aclvl > getChanOpId(chan, from))) {
05372                 sSend(":%s NOTICE %s :Access denied.", ChanServ, from);
05373                 return RET_NOPERM;
05374             }
05375 
05376             return cmd->func(cmd, nick, chan, args + 1, numargs - 1);
05377         }
05378     }
05379 
05380     sSend(":%s NOTICE %s :Unknown SET item.\r\n"
05381           ":%s NOTICE %s :Please try /msg %s HELP", ChanServ, from,
05382           ChanServ, from, ChanServ);
05383     return RET_EFAULT;
05384 }
05385 
05389 cmd_return cs_set_string(cs_settbl_t * cmd, UserList * nick,
05390                          RegChanList * regchan, char **args, int numargs)
05391 {
05392     char *from = nick->nick, **str_target;
05393     char tmpbuf[IRCBUF + 1];
05394     int i;
05395 
05396     if (!regchan)
05397         return RET_OK;
05398     str_target = (char **)(((regchan->name) + (cmd->xoff)));
05399     *tmpbuf = '\0';
05400 
05401     if (numargs == 3 && !strcasecmp(args[2], "*")) {
05402         sSend(":%s NOTICE %s :The %s of %s is now unspecified.", ChanServ,
05403               from, cmd->optname ? cmd->optname : cmd->cmd, regchan->name);
05404         if (str_target)
05405             FREE((*str_target));
05406         (*str_target) = NULL;
05407         return RET_OK_DB;
05408     }
05409 
05410     if (numargs >= 3) {
05411         for (i = 2; i < numargs; i++) {
05412             if (strlen(tmpbuf) + strlen(args[i]) >= cmd->optlen)
05413                 break;
05414             strcat(tmpbuf, args[i]);
05415             if (i + 1 < numargs)
05416                 strcat(tmpbuf, " ");
05417         }
05418 
05419         if (i < numargs && !*tmpbuf) {
05420             sSend(":%s NOTICE %s :Text line too long (must be %lu or shorter).",
05421                  ChanServ, from, cmd->optlen);
05422             return RET_EFAULT;
05423         }
05424 
05425         if ((*str_target))
05426             FREE((*str_target));
05427         (*str_target) = NULL;
05428 
05429         (*str_target) = (char *)oalloc(strlen(tmpbuf) + 1);
05430         strcpy((*str_target), tmpbuf);
05431         sSend(":%s NOTICE %s :The %s of %s is now: %s", ChanServ, from,
05432               cmd->optname ? cmd->optname : cmd->cmd, regchan->name,
05433               (*str_target));
05434         return RET_OK_DB;
05435     }
05436 
05437     if (*str_target)
05438         sSend(":%s NOTICE %s :The %s of %s is: %s", ChanServ, from,
05439               cmd->optname ? cmd->optname : cmd->cmd, regchan->name,
05440               (*str_target));
05441     else
05442         sSend(":%s NOTICE %s :The %s of %s is empty.", ChanServ, from,
05443               cmd->optname ? cmd->optname : cmd->cmd, regchan->name);
05444     return RET_OK;
05445 }
05446 
05450 cmd_return cs_set_fixed(cs_settbl_t * cmd, UserList * nick,
05451                         RegChanList * regchan, char **args, int numargs)
05452 {
05453     char *from = nick->nick, *str_target;
05454     int i;
05455 
05456     if (!regchan || !cmd->optlen)
05457         return RET_FAIL;
05458     str_target = (char *)((regchan->name) + (cmd->xoff));
05459 
05460     if (numargs == 3 && !strcasecmp(args[2], "*") && (!cmd->optname || strcmp(cmd->optname, "description"))) {
05461         sSend(":%s NOTICE %s :The %s of %s is now unspecified.", ChanServ,
05462               from, cmd->optname ? cmd->optname : cmd->cmd, regchan->name);
05463         (*str_target) = '\0';
05464         return RET_OK_DB;
05465     }
05466 
05467     if (numargs >= 3) {
05468         (*str_target) = '\0';
05469 
05470         for (i = 2; i < numargs; i++) {
05471             if (strlen((str_target)) + strlen(args[i]) >= cmd->optlen)
05472                 break;
05473             strcat((str_target), args[i]);
05474             if (i + 1 < numargs)
05475                 strcat((str_target), " ");
05476         }
05477 
05478         if (i < numargs && !*str_target)
05479             sSend(":%s NOTICE %s :Text line too long (must be %lu or shorter).",
05480                  ChanServ, from, cmd->optlen);
05481         sSend(":%s NOTICE %s :The %s of %s is now: %s", ChanServ, from,
05482               cmd->optname ? cmd->optname : cmd->cmd, regchan->name,
05483               str_target);
05484         return RET_OK_DB;
05485     }
05486     sSend(":%s NOTICE %s :The %s of %s is: %s", ChanServ, from,
05487           cmd->optname ? cmd->optname : cmd->cmd, regchan->name,
05488           (str_target));
05489     return RET_OK;
05490 }
05491 
05495 cmd_return cs_set_bool(cs_settbl_t * cmd, UserList * nick,
05496                        RegChanList * regchan, char **args, int numargs)
05497 {
05498     long *long_target;
05499     char *from = nick->nick;
05500     ChanList *chan;
05501 
05502     if (!regchan || !cmd->optlen)
05503         return RET_FAIL;
05504     long_target = (long *)(((regchan->name) + (cmd->xoff)));
05505     chan = getChanData(regchan->name);
05506 
05507     if (numargs < 3) {
05508         sSend(":%s NOTICE %s :For %s, %s is currently %s.", ChanServ, from,
05509               regchan->name, cmd->optname ? cmd->optname : cmd->cmd,
05510               ((*long_target) & cmd->optlen) ? "ON" : "OFF");
05511         return RET_OK;
05512     }
05513 
05514     if (!strcasecmp(args[2], "ON")) {
05515         (*long_target) |= cmd->optlen;
05516         sSend(":%s NOTICE %s :For %s, %s is now \2on\2.", ChanServ, from,
05517               regchan->name, cmd->optname ? cmd->optname : cmd->cmd);
05518         if (chan)
05519             sendToChanOps(chan, "%s set %s on.", from,
05520                           cmd->optname ? cmd->optname : cmd->cmd);
05521         return RET_OK_DB;
05522     } else if (!strcasecmp(args[2], "OFF")) {
05523         (*long_target) &= ~cmd->optlen;
05524         sSend(":%s NOTICE %s :For %s, %s is now \2off\2.", ChanServ, from,
05525               regchan->name, cmd->optname ? cmd->optname : cmd->cmd);
05526         if (chan)
05527             sendToChanOps(chan, "%s set %s off.", from,
05528                           cmd->optname ? cmd->optname : cmd->cmd);
05529         return RET_OK_DB;
05530     } else {
05531         sSend(":%s NOTICE %s :For %s, %s is currently %s.", ChanServ, from,
05532               regchan->name, cmd->optname ? cmd->optname : cmd->cmd,
05533               ((*long_target) & cmd->optlen) ? "ON" : "OFF");
05534         return RET_OK;
05535     }
05536     return RET_OK;
05537 }
05538 
05542 cmd_return cs_set_restrict(cs_settbl_t * cmd, UserList * nick,
05543                            RegChanList * regchan, char **args, int numargs)
05544 {
05545     char *from = nick->nick;
05546     ChanList *tmpchan;
05547     int reslevel;
05548 
05549     if (regchan == NULL) {
05550         sSend(":%s NOTICE %s :%s is not registered", ChanServ, from,
05551               args[1]);
05552         return RET_NOTARGET;
05553     }
05554 
05555     tmpchan = getChanData(regchan->name);
05556 
05557     if (numargs < 3) {
05558         if (regchan->restrictlevel)
05559             sSend
05560                 (":%s NOTICE %s :Access to \2%s\2 currently restricted to %s.",
05561                  ChanServ, from, regchan->name,
05562                  opLevelName(regchan->restrictlevel, 2));
05563         else
05564             sSend
05565                 (":%s NOTICE %s :Restricted mode for \2%s\2 is currently \2OFF\2.",
05566                  ChanServ, from, regchan->name);
05567         return RET_SYNTAX;
05568     }
05569 
05570     if (MFOUNDER > getChanOp(regchan, from)) {
05571         sSend(":%s NOTICE %s :Access denied", ChanServ, from);
05572         return RET_NOPERM;
05573     }
05574 
05575     reslevel = opNameLevel(args[2]);
05576 
05577     if (reslevel == -1) {
05578         sSend(":%s NOTICE %s :No such level.", ChanServ, from);
05579         return RET_NOPERM;
05580     }
05581 
05582     if (reslevel > getChanOp(regchan, from)) {
05583         sSend(":%s NOTICE %s :Access denied", ChanServ, from);
05584         return RET_NOPERM;
05585     }
05586 
05587     regchan->restrictlevel = reslevel;
05588 
05589     if (tmpchan)
05590         sendToChanOps(tmpchan, "%s has set restriction to %i+ levels",
05591                       from, reslevel);
05592     if (reslevel)
05593         sSend(":%s NOTICE %s :%s is now restricted to %s level.", ChanServ,
05594               from, regchan->name, opLevelName(reslevel, 2));
05595     else
05596         sSend(":%s NOTICE %s :%s is no longer in restricted mode.",
05597               ChanServ, from, regchan->name);
05598     return RET_OK_DB;
05599 }
05600 
05604 cmd_return cs_set_topiclock(cs_settbl_t * cmd, UserList * nick,
05605                             RegChanList * regchan, char **args,
05606                             int numargs)
05607 {
05608     char *from = nick->nick;
05609     ChanList *tmpchan;
05610     int reslevel;
05611 
05612     if (regchan == NULL) {
05613         sSend(":%s NOTICE %s :%s is not registered", ChanServ, from,
05614               args[1]);
05615         return RET_NOTARGET;
05616     }
05617 
05618     tmpchan = getChanData(regchan->name);
05619 
05620     if (numargs < 3) {
05621         if (regchan->tlocklevel)
05622             sSend
05623                 (":%s NOTICE %s :Topic changing for \2%s\2 currently restricted to %s.",
05624                  ChanServ, from, regchan->name,
05625                  opLevelName(regchan->restrictlevel, 2));
05626         else
05627             sSend
05628                 (":%s NOTICE %s :The topic restriction for \2%s\2 is currently \2OFF\2.",
05629                  ChanServ, from, regchan->name);
05630         return RET_SYNTAX;
05631     }
05632 
05633     if (MFOUNDER > getChanOp(regchan, from)) {
05634         sSend(":%s NOTICE %s :Access denied", ChanServ, from);
05635         return RET_NOPERM;
05636     }
05637 
05638     reslevel = opNameLevel(args[2]);
05639 
05640     if (reslevel == -1) {
05641         sSend(":%s NOTICE %s :No such level.", ChanServ, from);
05642         return RET_NOPERM;
05643     }
05644 
05645     if (reslevel > getChanOp(regchan, from)) {
05646         sSend(":%s NOTICE %s :Access denied", ChanServ, from);
05647         return RET_NOPERM;
05648     }
05649 
05650     regchan->tlocklevel = reslevel;
05651 
05652     if (tmpchan)
05653         sendToChanOps(tmpchan, "%s has set topiclock to %i+ levels", from,
05654                       reslevel);
05655     if (reslevel)
05656         sSend(":%s NOTICE %s :Topic restriction for %s is now %s+.",
05657               ChanServ, from, regchan->name, opLevelName(reslevel, 2));
05658     else
05659         sSend(":%s NOTICE %s :Topic for %s is no longer restricted.",
05660               ChanServ, from, regchan->name);
05661     return RET_OK_DB;
05662 }
05663 
05667 cmd_return cs_set_encrypt(cs_settbl_t * cmd, UserList * nick,
05668                             RegChanList * regchan, char **args,
05669                             int numargs)
05670 {
05671     char *from = nick->nick;
05672     int do_enc = 0;
05673 
05674     if (numargs < 3) {
05675         sSend(":%s NOTICE %s :Password for %s is %scurrently encrypted.",
05676             ChanServ, from, regchan->name, (regchan->flags & CENCRYPT) ? "" : "not ");
05677         return RET_OK_DB;
05678     }
05679 
05680     if (!str_cmp(args[2], "ON") || !str_cmp(args[2], "ENCRYPT") ||
05681         !str_cmp(args[2], "YES"))
05682         do_enc = 1;
05683     else if (!str_cmp(args[2], "OFF") || !str_cmp(args[2], "UNENCRYPT") ||
05684          !str_cmp(args[2], "NO"))
05685         do_enc = 0;
05686     else {
05687         sSend(":%s NOTICE %s :Invalid setting.  Must be ON or OFF.",
05688               ChanServ, from);
05689     }
05690 
05691     if (numargs < 4) {
05692         sSend(":%s NOTICE %s :Password required to change this option.",
05693             ChanServ, from);
05694         return RET_OK_DB;
05695     }
05696 
05697     if (!Valid_pw(args[3], regchan->password, ChanGetEnc(regchan))) {
05698         PutReply(ChanServ, nick, ERR_BADPW, 0, 0, 0);
05699         if (BadPwChan(nick, regchan))
05700             return RET_KILLED;
05701         return RET_BADPW;
05702     }
05703     if (do_enc) {
05704         sSend(":%s NOTICE %s :Password for %s is now encrypted "
05705               "within services.", ChanServ, from, regchan->name);
05706         GoodPwChan(nick, regchan);
05707         regchan->flags |= CENCRYPT;
05708     }
05709     else {
05710         sSend(":%s NOTICE %s :Password for %s is no longer "
05711               "encrypted within services.",
05712             ChanServ, from, args[1]);
05713         regchan->flags &= ~CENCRYPT;
05714     }
05715 
05716     pw_enter_password(args[3], regchan->password, ChanGetEnc(regchan));
05717 
05718     return RET_OK_DB;
05719 }
05720 
05724 cmd_return cs_set_mlock(cs_settbl_t * cmd, UserList * nick,
05725                         RegChanList * regchan, char **args, int numargs)
05726 {
05727     struct
05728     {
05729         char character;
05730         int plus, minus;
05731     } mlock_table[] =
05732     {
05733         {
05734         'i', PM_I, MM_I}
05735         , {
05736         'm', PM_M, MM_M}
05737         , {
05738         'n', PM_N, MM_N}
05739         , {
05740         's', PM_S, MM_S}
05741         , {
05742         'p', PM_P, MM_P}
05743         , {
05744         't', PM_T, MM_T}
05745         , {
05746         'H', PM_H, MM_H}
05747         , {
05748         'c', PM_C, MM_C}
05749         , {
05750         '\0', 0, 0}
05751     };
05752 
05753     int onargs = 3;
05754     int on = 1;
05755     int reset = 0;
05756     u_int i, j, klen;
05757     char mode[20], parastr[IRCBUF * 2];
05758     char *from = nick->nick;
05759 
05760     ChanList *tmpchan;
05761 
05762     if (!regchan)
05763         return RET_FAIL;
05764     tmpchan = getChanData(regchan->name);
05765 
05766     if (regchan == NULL) {
05767         sSend(":%s NOTICE %s :%s is not registered", ChanServ, from,
05768               args[1]);
05769         return RET_FAIL;
05770     }
05771 
05772     if (numargs < 3) {
05773         makeModeLockStr(regchan, mode);
05774         sSend
05775             (":%s NOTICE %s :The modelock string for %s is currently \2%s\2",
05776              ChanServ, from, regchan->name, mode);
05777         return RET_OK;
05778     }
05779 
05780     if (MFOUNDER > getChanOp(regchan, from)) {
05781         sSend(":%s NOTICE %s :Access denied", ChanServ, from);
05782         return RET_NOPERM;
05783     }
05784 
05785     klen = strlen(args[2]);
05786 
05787     for (i = 0; i < klen; i++) {
05788         switch (args[2][i]) {
05789         default:
05790             for (j = 0; mlock_table[j].character; j++)
05791                 if (mlock_table[j].character == args[2][i])
05792                     break;
05793             if (mlock_table[j].character) {
05794                 if (on) {
05795                     regchan->mlock |= mlock_table[j].plus;
05796                     regchan->mlock &= ~mlock_table[j].minus;
05797                 } else {
05798                     regchan->mlock |= mlock_table[j].minus;
05799                     regchan->mlock &= ~mlock_table[j].plus;
05800                 }
05801             }
05802             break;
05803         case '=':
05804             if (!reset) {
05805                 regchan->mlock = 0;
05806                 reset = 1;
05807                 break;
05808             }
05809         case '+':
05810             on = 1;
05811             break;
05812         case '-':
05813             on = 0;
05814             break;
05815         case 'k':
05816             if (on) {
05817                 if (numargs < onargs + 1)
05818                     sSend(":%s NOTICE %s :+k not set, no key specified",
05819                           ChanServ, from);
05820                 else {
05821                     strncpyzt(regchan->key, args[onargs], KEYLEN-1);
05822                     regchan->mlock |= PM_K;
05823                     regchan->mlock &= ~MM_K;
05824                     onargs++;
05825                 }
05826             } else {
05827                 regchan->mlock |= MM_K;
05828                 regchan->mlock &= ~PM_K;
05829             }
05830             break;
05831         case 'l':
05832             if (on) {
05833                 if (numargs < onargs + 1)
05834                     sSend(":%s NOTICE %s :+l not set, no key specified",
05835                           ChanServ, from);
05836                 else {
05837                     regchan->limit = atoi(args[onargs]);
05838                     regchan->mlock |= PM_L;
05839                     regchan->mlock &= ~MM_L;
05840                     onargs++;
05841                 }
05842             } else {
05843                 regchan->mlock |= MM_L;
05844                 regchan->mlock &= ~PM_L;
05845             }
05846             break;
05847         }
05848     }
05849 
05850     makeModeLockStr(regchan, mode);
05851     sendToChanOps(getChanData(regchan->name), "%s set modelock %s", from,
05852                   mode);
05853     *parastr = '\0';
05854     if ((regchan->mlock & PM_L))
05855         sprintf(parastr + strlen(parastr), " %ld", regchan->limit);
05856     if ((regchan->mlock & PM_K) || (regchan->mlock & MM_K))
05857         sprintf(parastr + strlen(parastr), " %s", regchan->key
05858                 && *regchan->key ? regchan->key : "*");
05859     sSend(":%s MODE %s %s%s", ChanServ, regchan->name, mode, parastr);
05860 #ifdef IRCD_MLOCK
05861     sSend(":%s MLOCK %s %s%s", ChanServ, regchan->name, mode, parastr);
05862 #endif
05863 
05864     sSend(":%s NOTICE %s :Mode for %s is now locked to %s", ChanServ, from,
05865           regchan->name, mode);
05866     return RET_OK_DB;
05867 }
05868 
05872 /*ARGSUSED3*/
05873 cmd_return cs_set_passwd(cs_settbl_t * cmd, UserList * nick,
05874                          RegChanList * regchan, char **args, int numargs)
05875 {
05876     char *from = nick->nick;
05877     char pw_reason[IRCBUF];
05878 
05879     if (numargs < 3) {
05880         sSend
05881             (":%s NOTICE %s :Current password for %s cannot be shown for security reasons.",
05882              ChanServ, from, regchan->name);
05883         sSend
05884             (":%s NOTICE %s :To change the password, type \2/CHANSERV SET <CHANNEL> PASSWORD <NEWPASS>\2",
05885              ChanServ, from);
05886         sSend
05887             (":%s NOTICE %s :Or type \2/CHANSERV HELP SET PASSWORD\2 for more information",
05888              ChanServ, from);
05889         return RET_OK;
05890     }
05891 
05892     if (!isFounder(regchan, nick))
05893         sSend
05894             (":%s NOTICE %s :You must identify as channel founder to change the channel password",
05895              ChanServ, from);
05896     else {
05897 
05898                 if (isPasswordAcceptable(args[2], pw_reason) == 0) {
05899                     sSend(":%s NOTICE %s :Sorry, %s isn't a password that you can use.",
05900                         ChanServ, from, args[1]);
05901                     sSend(":%s NOTICE %s :%s", ChanServ, from, pw_reason);
05902                     return RET_EFAULT;
05903                 }
05904 
05907         if (4 > strlen(args[2]) || 15 < strlen(args[2])) {
05908             sSend
05909                 (":%s NOTICE %s :Please choose a password between 4 and 15 characters long",
05910                  ChanServ, from);
05911             return RET_EFAULT;
05912         } else {
05913             pw_enter_password(args[2], regchan->password, ChanGetEnc(regchan));
05914             clearChanIdent(regchan);
05915 
05916             sSend(":%s NOTICE %s :Password for %s is now %s", ChanServ,
05917                   from, regchan->name, args[2]);
05918             return RET_OK_DB;
05919         }
05920     }
05921     return RET_OK;
05922 }
05923 
05928 /*ARGSUSED3*/
05929 cmd_return cs_set_founder(cs_settbl_t * cmd, UserList * nick,
05930                           RegChanList * regchan, char **args, int numargs)
05931 {
05932     char *from = nick->nick;
05933     ChanList *chan = getChanData(regchan->name);
05934 
05935     if (numargs < 3) {
05936         const char *tmpName = regchan->founderId.getNick();
05937 
05938         if (tmpName == NULL)
05939             tmpName = "(none)";
05940 
05941         sSend(":%s NOTICE %s :Current founder of \2%s\2 is \2%s\2\2",
05942               ChanServ, from, regchan->name, tmpName);
05943         return RET_OK;
05944     }
05945 
05946     if (numargs < 3) {
05947         sSend(":%s NOTICE %s :To set yourself as the founder, you "
05948               "must specify the channel password.", ChanServ, from);
05949         return RET_SYNTAX;
05950     }
05951 
05952     if (!nick->reg) {
05953         sSend(":%s NOTICE %s :Your nickname, %s, is not registered.", ChanServ, from,
05954               from);
05955         return RET_FAIL;
05956     }
05957 
05958     if (Valid_pw(args[2], regchan->password, ChanGetEnc(regchan))) {
05959         RegNickList *founderInfo;
05960 
05961         founderInfo = regchan->founderId.getNickItem();
05962 
05963         if (founderInfo != NULL)
05964             founderInfo->chans--;
05965         regchan->founderId = nick->reg->regnum;
05966         nick->reg->chans++;
05967 
05968         if (chan)
05969             sendToChanOps(chan, "The founder of %s is now %s",
05970                           regchan->name, from);
05971         sSend(":%s NOTICE %s :You are now the founder of %s", ChanServ,
05972               from, regchan->name);
05973         GoodPwChan(nick, regchan);
05974 
05975         return RET_OK_DB;
05976     }
05977     if (chan)
05978         sendToChanOps(chan, "%s failed a SET FOUNDER attempt for %s", from,
05979                       chan->name);
05980     sSend(":%s NOTICE %s :Incorrect password", ChanServ, from);
05981     if (BadPwChan(nick, regchan))
05982         return RET_KILLED;
05983 
05984     return RET_BADPW;
05985 }
05986 
05990 /*ARGSUSED3*/
05991 cmd_return cs_set_memolvl(cs_settbl_t * cmd, UserList * nick,
05992                           RegChanList * regchan, char **args, int numargs)
05993 {
05994     char *from = nick->nick;
05995     int i;
05996 
05997     if (numargs < 3) {
05998         sSend(":%s NOTICE %s :Current memo level for \2%s\2 is \2%d\2.",
05999               ChanServ, from, regchan->name, regchan->memolevel);
06000         return RET_SYNTAX;
06001     }
06002 
06003     i = atoi(args[2]);
06004 
06005     if (!((i >= MAOP) && (i <= FOUNDER))) {
06006         sSend(":%s NOTICE %s :You need to specify a valid level.",
06007               ChanServ, from);
06008         return RET_SYNTAX;
06009     }
06010 
06011     regchan->memolevel = atoi(args[2]);
06012     sSend(":%s NOTICE %s :Channel memolevel is now %s", ChanServ, from,
06013           args[2]);
06014     return RET_OK_DB;
06015 }
06016 
06021 CCMD(cs_save)
06022 {
06023     char *from = nick->nick;
06024 
06025     if (!isRoot(nick)) {
06026         sSend(":%s NOTICE %s :Access denied.", ChanServ, from);
06027         return RET_NOPERM;
06028     }
06029 
06030 #ifdef GLOBOP_ON_SAVE
06031     sSend(":%s GLOBOPS :Saving database. (%s)", ChanServ, from);
06032 #else
06033     sSend(":%s PRIVMSG " LOGCHAN " :Saving database. (%s)", ChanServ,
06034           from);
06035 #endif
06036     saveChanData(firstRegChan);
06037     return RET_OK_DB;
06038 }
06039 
06040 /*--------------------------------------------------------------------*/
06041 /* Password retrievals */
06042 
06057 CCMD(cs_getpass)
06058 {
06059     char *from = nick->nick;
06060     const char *pwAuthKey;
06061     time_t doot = time(NULL);
06062     char buf[MAXBUF];
06063     int key_new, key_resend, key_transfer, i;
06064     RegNickList *regnick;
06065     RegChanList *chan;
06066     EmailMessage *email = NULL;
06067 
06068     key_new = key_resend = key_transfer = 0;
06069 
06070     if (numargs < 2) {
06071         sSend(":%s NOTICE %s :Must supply a channel name", ChanServ, from);
06072         return RET_SYNTAX;
06073     }
06074 
06079     if (numargs >= 3)
06080     {
06081         for(i = 2; i < numargs; i++)
06082         {
06083             if (*args[i] == '-')
06084             {
06085                 if (!strcmp(args[i], "-new"))
06086                     key_new = 1;
06087                 else if (!strcmp(args[i], "-resend"))
06088                     key_resend = 1;
06089                 else if (!strcmp(args[i], "-transfer") &&
06090                          opFlagged(nick, OGRP) &&
06091                      !is_sn_chan(args[1]))
06092                     key_transfer = 1;
06093                 else if (args[i][1] == '-')
06094                     break;
06095             }
06096             else
06097                 continue;
06098         }
06099     }
06100 
06101     if (key_resend && key_new)
06102         key_resend = key_new = 0;
06103 
06104     if (isOper(nick) == 0) {
06105         sSend(":%s NOTICE %s :You are not currently an IRC Operator",
06106               ChanServ, from);
06107         return RET_NOPERM;
06108     }
06109 
06110     chan = getRegChanData(args[1]);
06111 
06112     if (chan == NULL) {
06113         sSend(":%s NOTICE %s :%s is not registered", ChanServ, from,
06114               args[1]);
06115         return RET_NOTARGET;
06116     }
06117 
06118     regnick = chan->founderId.getNickItem();
06119 
06120     if (regnick == NULL) {
06121         sSend(":%s NOTICE %s :There is no founder on record for "
06122               "the channel %s.",
06123              ChanServ, from, args[1]);
06124         return RET_FAIL;
06125     }
06126 
06127     if (!key_transfer && (!regnick->email[0] || !strncasecmp(regnick->email, "(none)", 6))) {
06128         sSend
06129             (":%s NOTICE %s :The founder, %s, did not specify an e-mail address",
06130              NickServ, from, regnick->nick);
06131         return RET_FAIL;
06132     }
06133 
06134     if (!key_transfer)
06135     {
06136         email = new EmailMessage;
06137 
06138         if (!email) {
06139             sSend(":%s WALLOPS :cs_getpass: fatal error: out of memory",
06140                   ChanServ);
06141             abort();
06142             return RET_EFAULT;
06143         }
06144     }
06145 
06146     if (!key_resend && !key_new && chan->chpw_key)
06147     {
06148         sSend(":%s NOTICE %s :A password change key has already been "
06149               "issued for %s. Please specify -new or -resend.",
06150               ChanServ, from, chan->name);
06151         return RET_FAIL;
06152     }
06153     else if (key_transfer || (chan->flags & CFORCEXFER)) {
06154         if (!chan->chpw_key || !key_resend)
06155             chan->chpw_key = lrand48();
06156         if (!chan->chpw_key)
06157             chan->chpw_key = 1;
06158         pwAuthKey = GetAuthChKey("-forced-transfer-",
06159                      PrintPass(chan->password, ChanGetEnc(chan)),
06160                      chan->timereg,
06161                      chan->chpw_key);
06162         if (key_transfer)
06163             sSend(":%s GLOBOPS :%s used getpass (transfer) on %s", ChanServ, from, chan->name);
06164         else 
06165             sSend(":%s GLOBOPS :%s used getpass (getkey) on %s", ChanServ, from, chan->name);
06166         sSend(":%s NOTICE %s :The change auth key for %s is: %s",
06167               ChanServ, from, chan->name, pwAuthKey);
06168         operlog->log(nick, CSS_GETPASS_XFER, args[1]);
06169         chan->flags |= CFORCEXFER;
06170         clearChanIdent(chan);
06171 
06172         return RET_OK_DB;
06173     }
06174     else if ((chan->flags & CFORCEXFER) && !opFlagged(nick, OGRP | OOPER | OVERRIDE))
06175     {
06176         sSend(":%s NOTICE %s :This chan's pw key was marked ``channel transfer''",
06177               ChanServ, from);
06178         return RET_FAIL;
06179     }
06180 
06181 
06182     if (!key_resend || key_new)
06183         key_new = key_resend = 0;
06184 
06185     sSend(":%s GLOBOPS :%s used sendpass on %s", myname, from, chan->name);
06186     sSend
06187         (":%s NOTICE %s :The \2password change key\2 has been sent to the email address: "
06188          "%s", ChanServ, from, regnick->email);
06189     snprintf(buf, sizeof(buf), "%s Password Recovery <%s@%s>", ChanServ,
06190              ChanServ, NETWORK);
06191 
06192     if (!chan->chpw_key || !key_resend || key_new) {
06193         chan->chpw_key = lrand48();
06194     }
06195     if (!chan->chpw_key)
06196         chan->chpw_key = 1;
06197     pwAuthKey = GetAuthChKey(regnick->email, PrintPass(chan->password, ChanGetEnc(chan)), chan->timereg,
06198                              chan->chpw_key);
06199 
06200     email->from = buf;
06201     email->subject = "Password";
06202     email->to = regnick->email;
06203     sprintf(buf,
06204             "\nThe password change key for %.*s is %.*s\n"
06205             "\nType /CHANSERV SETPASS %.*s %.*s <password>\n",
06206             CHANLEN, chan->name,
06207             255, pwAuthKey,
06208             CHANLEN, chan->name,
06209             255, pwAuthKey
06210     );
06211     email->body = buf;
06212 
06213 
06214     email->body += "Or navigate to: "
06215         "http://astral.sorcery.net/~sortest/setpass.php?u_chan=";
06216     email->body += urlEncode(chan->name);
06217     email->body += "&u_ck=";
06218     email->body += urlEncode(pwAuthKey);
06219     email->body += "\nTo set a new channel password.\n\n";
06220 
06221     sprintf(buf,    "Retrieved by %.*s at %s",
06222             NICKLEN, from, ctime(&(doot)));
06223     email->body += buf;
06224     email->send();
06225     email->reset();
06226     operlog->log(nick, CS_GETPASS, args[1]);
06227 
06228     delete email;
06229 
06230     return RET_OK_DB;
06231 }
06232 
06233 /*--------------------------------------------------------------------*/
06234 /* Debug command: Get the value of the password struct */
06235 
06242 CCMD(cs_getrealpass)
06243 {
06244     char *from = nick->nick;
06245     RegChanList *chan;
06246 
06247 
06248     if (!isRoot(nick)) {
06249         sSend(":%s NOTICE %s :Access denied", ChanServ, from);
06250         return RET_NOPERM;
06251     }
06252 
06253     if (!opFlagged(nick, OROOT | OVERRIDE)) {
06254         sSend(":%s NOTICE %s :Restricted -- getrealpass is a debugging and administrative command not for password recovery.",
06255              ChanServ, from);
06256         return RET_NOPERM;
06257     }
06258 
06259     if (numargs != 2) {
06260         sSend(":%s NOTICE %s :Must supply a channel name", ChanServ, from);
06261         return RET_SYNTAX;
06262     }
06263 
06264     chan = getRegChanData(args[1]);
06265 
06266     if (chan == NULL) {
06267         sSend(":%s NOTICE %s :%s is not registered", ChanServ, from,
06268               args[1]);
06269         return RET_NOTARGET;
06270     }
06271 
06272     sSend(":%s GLOBOPS :%s used getrealpass on %s", ChanServ, from,
06273           chan->name);
06274     if (!(chan->flags & CENCRYPT))
06275         sSend(":%s NOTICE %s :The password for %s is %s", ChanServ, from,
06276               args[1], chan->password);
06277     else {
06278         u_char *p = toBase64(chan->password, 16);
06279 
06280         sSend(":%s NOTICE %s :The password for %s is encrypted: %s", ChanServ, from,
06281               args[1], md5_printable(chan->password));
06282         if (p) {
06283             sSend(":%s NOTICE %s :Base64 representation: $%s", ChanServ, from,
06284                  p);
06285             FREE(p);
06286         }
06287     }
06288     operlog->log(nick, CS_GETREALPASS, args[1]);
06289     return RET_OK;
06290 }
06291 
06292 /*--------------------------------------------------------------------*/
06293 
06305 CCMD(cs_invite)
06306 {
06307     char *from = nick->nick;
06308     int override = 0;
06309     RegChanList *chan;
06310 
06311     override = opFlagged(nick, OOPER | OVERRIDE) ? 1 : 0;
06312 
06313     if (numargs < 2) {
06314         sSend(":%s NOTICE %s :You must specify a channel.", ChanServ,
06315               from);
06316         return RET_SYNTAX;
06317     }
06318 
06319     chan = getRegChanData(args[1]);
06320 
06321     if (override && chan == NULL) {
06322         sSend(":%s INVITE %s %s", ChanServ, from, args[1]);
06323         if (override)
06324             sSend(":%s GLOBOPS :%s uses override invite (%s)", ChanServ,
06325                   from, args[1]);
06326         return RET_OK;
06327     }
06328 
06329     if (chan == NULL) {
06330         sSend(":%s NOTICE %s :%s isn't registered", ChanServ, from,
06331               args[1]);
06332         return RET_NOTARGET;
06333     }
06334 
06335     if ((getChanOp(chan, from) < chan->restrictlevel)) {
06336         sSend(":%s NOTICE %s :%s is currently restricted to chanop "
06337               "levels %d (%s+) and up.", ChanServ, from, chan->name,
06338               chan->restrictlevel, opLevelName(chan->restrictlevel, 0));
06339         return RET_NOPERM;
06340     }
06341 
06342     if (1 > getChanOp(chan, from) && !override) {
06343         sSend(":%s NOTICE %s :Access denied", ChanServ, from);
06344         return RET_NOPERM;
06345     } else {
06346         sSend(":%s NOTICE %s :Giving you an invitation for %s...",
06347               ChanServ, from, chan->name);
06348         sSend(":%s INVITE %s %s", ChanServ, from, chan->name);
06349 
06350         if (!getChanData(chan->name))
06351             sSend(":%s NOTICE %s :Error: %s is empty.", ChanServ, from,
06352                   chan->name);
06353 
06354         if (override && chan)
06355             sSend(":%s GLOBOPS :%s uses override invite (%s)", ChanServ,
06356                   from, chan->name);
06357     }
06358 
06359     return RET_OK;
06360 }
06361 
06362 /*--------------------------------------------------------------------*/
06363 
06374 CCMD(cs_unban)
06375 {
06376     char *from = nick->nick;
06377     RegChanList *chan;
06378     ChanList *tmp;
06379     cBanList *tmpban;
06380     cBanList *next;
06381     int a, domodes = 0;
06382     char mode[(USERLEN + HOSTLEN + HOSTLEN) * 6 + 5] = "";
06383     char userhost[NICKLEN + USERLEN + HOSTLEN];
06384     char userhostM[NICKLEN + USERLEN + HOSTLEN];
06385 
06386     tmp = getChanData(args[1]);
06387     chan = getRegChanData(args[1]);
06388 
06389     if (tmp == NULL || chan == NULL) {
06390         sSend(":%s NOTICE %s :The channel must be registered, and in use.",
06391               ChanServ, from);
06392         return RET_NOTARGET;
06393     }
06394     a = getChanOp(chan, from);
06395 
06396     if (a < AOP) {
06397         sSend(":%s NOTICE %s :You do not have sufficient access to %s",
06398               ChanServ, from, args[1]);
06399         if (a > 0)
06400             sSend
06401                 (":%s NOTICE %s :try \2/msg ChanServ INVITE %s\2 instead.",
06402                  ChanServ, from, args[1]);
06403         return RET_NOPERM;
06404     }
06405 
06406     if ((numargs < 3) || !strcasecmp(args[2], "me")
06407         || !strcasecmp(args[2], from)) {
06408         snprintf(userhost, sizeof(userhost), "%s!%s@%s", nick->nick,
06409                  nick->user, nick->host);
06410         snprintf(userhostM, sizeof(userhost), "%s!%s@%s", nick->nick,
06411                  nick->user, genHostMask(nick->host));
06412         for (tmpban = tmp->firstBan; tmpban; tmpban = next) {
06413             next = tmpban->next;
06414 
06415             if (match(tmpban->ban, userhost) == 0
06416                 || match(tmpban->ban, userhostM) == 0) {
06417                 strcat(mode, tmpban->ban);
06418                 strcat(mode, " ");
06419                 domodes++;
06420                 sSend(":%s NOTICE %s :You are no longer banned on %s (%s)",
06421                       ChanServ, from, args[1], tmpban->ban);
06422                 delChanBan(tmp, tmpban);
06423             }
06424             if (domodes > 0 && (domodes == 6 || next == NULL)) {
06425                 sSend(":%s NOTICE %s :Removed bans: %s", ChanServ, from,
06426                       mode);
06427                 sSend(":%s MODE %s -bbbbbb %s", ChanServ, args[1], mode);
06428                 domodes = 0;
06429                 mode[0] = 0;
06430             }
06431         }
06432     } else if (!strcasecmp(args[2], "all") || !strcasecmp(args[2], "*")) {
06433         if (a < MSOP)
06434             sSend(":%s NOTICE %s :Access denied", ChanServ, from);
06435         else {
06436             for (tmpban = tmp->firstBan; tmpban; tmpban = next) {
06437                 next = tmpban->next;
06438 
06439                 strcat(mode, tmpban->ban);
06440                 strcat(mode, " ");
06441                 domodes++;
06442                 if (domodes == 6 || next == NULL) {
06443                     sSend(":%s NOTICE %s :Removed bans: %s", ChanServ,
06444                           from, mode);
06445                     sSend(":%s MODE %s -bbbbbb %s", ChanServ, args[1],
06446                           mode);
06447                     domodes = 0;
06448                     mode[0] = 0;
06449                 }
06450                 delChanBan(tmp, tmpban);
06451 
06452             }
06453         }
06454     } else {
06455         if (a < MSOP) {
06456             sSend(":%s NOTICE %s :Access denied", ChanServ, from);
06457         } else {
06458             for (tmpban = tmp->firstBan; tmpban; tmpban = next) {
06459                 next = tmpban->next;
06460 
06461                 if (!match(args[2], tmpban->ban)) {
06462                     strcat(mode, tmpban->ban);
06463                     strcat(mode, " ");
06464                     domodes++;
06465                     delChanBan(tmp, tmpban);
06466                 }
06467                 if (domodes > 0 && (domodes == 6 || next == NULL)) {
06468                     sSend(":%s NOTICE %s :Removed bans: %s", ChanServ,
06469                           from, mode);
06470                     sSend(":%s MODE %s -bbbbbb %s", ChanServ, args[1],
06471                           mode);
06472                     domodes = 0;
06473                     mode[0] = 0;
06474                 }
06475             }
06476         }
06477     }
06478     return RET_OK;
06479 }
06480 
06481 /*--------------------------------------------------------------------*/
06482 
06494 CCMD(cs_list)
06495 {
06496     char *from = nick->nick;
06497     int i, x;
06498     RegChanList *reg;
06499     RegNickList *rnl = NULL;
06500     short byfounder = 0;
06501 
06502     i = x = 0;
06503 
06504     if (!isServop(nick) && !opFlagged(nick, OOPER | OLIST)) {
06505         sSend(":%s NOTICE %s :Permission denied.", ChanServ, from);
06506         return RET_NOPERM;
06507     }
06508 
06509     if (numargs < 2) {
06510         sSend(":%s NOTICE %s :You must specify a mask or -f", ChanServ,
06511               from);
06512         return RET_SYNTAX;
06513     }
06514 
06515     if (strncmp(args[1], "-f", 2) == 0)
06516         if (numargs < 3) {
06517             sSend(":%s NOTICE %s :You must specify a founder nick with -f",
06518                   ChanServ, from);
06519             return RET_SYNTAX;
06520         } else {
06521             byfounder = 1;
06522             rnl = getRegNickData(args[2]);
06523         }
06524 
06525     for (reg = firstRegChan; reg; reg = reg->next) {
06526         if (byfounder == 1) {
06527             if (rnl && rnl->regnum == reg->founderId) {
06528                 i++;
06529                 sSend(":%s NOTICE %s :%4i: %s %s (%s)", ChanServ, from, i,
06530                       reg->name, rnl->nick, reg->desc);
06531             }
06532         } else {
06533             if (!match(args[1], reg->name)) {
06534                 const char *tmpNick = reg->founderId.getNick();
06535 
06536                 if (tmpNick == NULL)
06537                     tmpNick = "(none)";
06538 
06539                 i++;
06540                 sSend(":%s NOTICE %s :%4i: %s %s (%s)", ChanServ, from, i,
06541                       reg->name, tmpNick, reg->desc);
06542             }
06543         }
06544         x++;
06545     }
06546 
06547     sSend(":%s NOTICE %s :%i/%i matched conditions (%s)", ChanServ, from,
06548           i, x, (byfounder == 1) ? args[2] : args[1]);
06549     if (byfounder == 1)
06550         sSend(":%s GLOBOPS :%s is listing founder nick %s (%d/%d matches)", ChanServ, from,
06551               args[2], i, x);
06552     else
06553         sSend(":%s GLOBOPS :%s is listing %s (%d/%d matches)", ChanServ, from, args[1], i, x);
06554     return RET_OK;
06555 }
06556 
06557 /*--------------------------------------------------------------------*/
06558 
06565 CCMD(cs_delete)
06566 {
06567     char *from = nick->nick;
06568     int i, x;
06569     RegChanList *reg;
06570     ChanList *chan;
06571 
06572     i = x = 0;
06573 
06574     if (!isServop(nick) && !opFlagged(nick, OOPER | OCBANDEL)) {
06575         sSend(":%s NOTICE %s :Permission denied.", ChanServ, from);
06576         return RET_NOPERM;
06577     }
06578 
06579     if (numargs < 2) {
06580         sSend(":%s NOTICE %s :You must specify a channel to delete.",
06581               ChanServ, from);
06582         return RET_SYNTAX;
06583     }
06584 
06585 
06586     if (is_sn_chan(args[1]) && !opFlagged(nick, OOPER | OSERVOP | OVERRIDE)) {
06587         sSend(":%s NOTICE %s :You can't delete that chan!", ChanServ,
06588               from);
06589         sSend(":%s GLOBOPS :%s tried to use DROP command on %s", ChanServ,
06590               from, args[1]);
06591         chanlog->log(nick, CS_DELETE, args[1], LOGF_FAIL | LOGF_OPER);
06592         return RET_OK_DB;
06593     }
06594 
06595 
06596     if (!(reg = getRegChanData(args[1]))) {
06597         sSend(":%s NOTICE %s :That channel is not registered.", ChanServ,
06598               from);
06599         return RET_NOTARGET;
06600     }
06601 
06602     if (addFlood(nick, 25))
06603         return RET_KILLED;
06604 
06605     if ((reg->flags & CHOLD) && !opFlagged(nick, OROOT)) {
06606         sSend
06607             (":%s NOTICE %s :That is a held channel.",
06608              ChanServ, from);
06609         sSend(":%s GLOBOPS :%s tried to use DROP command on %s", ChanServ,
06610               from, args[1]);
06611         chanlog->log(nick, CS_DELETE, args[1], LOGF_FAIL | LOGF_OPER);
06612         return RET_OK_DB;
06613     }
06614 
06615     sSend(":%s GLOBOPS :%s used DROP command on %s", ChanServ, from,
06616           args[1]);
06617 
06618     chanlog->log(nick, CS_DELETE, args[1], LOGF_OPER);
06619     if ((chan = getChanData(args[1])))
06620         chan->reg = NULL;
06621     delRegChan(reg);
06622     return RET_OK_DB;
06623 }
06624 
06625 /*--------------------------------------------------------------------*/
06626 
06634 CCMD(cs_log)
06635 {
06636     char *from = nick->nick;
06637     char log_str[IRCBUF + 1];
06638 
06639     if (addFlood(nick, 5))
06640         return RET_KILLED;
06641 
06642     if (isOper(nick) == 0) {
06643         sSend(":%s NOTICE %s :Access denied", ChanServ, from);
06644         return RET_NOPERM;
06645     }
06646 
06647     if (numargs < 2) {
06648         sSend(":%s NOTICE %s :Must supply at least one argument", ChanServ,
06649               from);
06650         return RET_SYNTAX;
06651     }
06652 
06653     bzero(log_str, IRCBUF);
06654     parse_str(args, numargs, 1, log_str, IRCBUF);
06655 
06656     if (strlen(log_str) < 2) {
06657         sSend(":%s NOTICE %s :Logline too short.", ChanServ, from);
06658         return RET_EFAULT;
06659     }
06660     if (strlen(log_str) >= (IRCBUF - 1)) {
06661         sSend(":%s NOTICE %s :Logline too long.", ChanServ, from);
06662         return RET_EFAULT;
06663     }
06664 
06665     sSend(":%s NOTICE %s :Ok, logged.", ChanServ, from);
06666     chanlog->log(nick, CS_LOG, NULL, 0, "%s", log_str);
06667     return RET_OK_DB;
06668 }
06669 
06670 /*--------------------------------------------------------------------*/
06671 
06679 CCMD(cs_setpass)
06680 {
06681     const char *from = nick->nick;
06682     const char *pwAuthChKey;
06683     RegChanList *rcl;
06684     RegNickList *rfl;
06685 
06686     if (numargs < 4)
06687     {
06688         sSend(":%s NOTICE %s :Syntax: SETPASS <channel> <code> <new password>",
06689             ChanServ, from);
06690         return RET_SYNTAX;
06691     }
06692 
06693     if ((rcl = getRegChanData(args[1])) == NULL)
06694     {
06695         sSend(":%s NOTICE %s :That channel is not registered.",
06696               NickServ, from);
06697         return RET_NOTARGET;
06698     }
06699 
06700     rfl = rcl->founderId.getNickItem();
06701 
06702     if (rfl && rcl->chpw_key) {
06703         if (rcl->flags & CFORCEXFER)
06704             pwAuthChKey = GetAuthChKey("-forced-transfer-", PrintPass(rcl->password, ChanGetEnc(rcl)),
06705                                      rcl->timereg, rcl->chpw_key);
06706         else
06707             pwAuthChKey = GetAuthChKey(rfl->email, PrintPass(rcl->password, ChanGetEnc(rcl)),
06708                                      rcl->timereg, rcl->chpw_key);
06709     }
06710     else
06711         pwAuthChKey = NULL;
06712 
06713     if (pwAuthChKey == NULL || strcmp(pwAuthChKey, args[2]))
06714     {
06715         sSend(":%s NOTICE %s :Access Denied.",
06716               ChanServ, from);
06717         return RET_FAIL;
06718     }
06719 
06720     if (strlen(args[3]) > PASSLEN)
06721     {
06722         sSend(":%s NOTICE %s :Specified password is too long, please "
06723               "choose a password of %d characters or less.",
06724               ChanServ, from, PASSLEN);
06725         return RET_EFAULT;
06726     }
06727 
06728 
06729     if (strcasecmp(args[1], args[3]) == 0 || strcasecmp(args[3], "password") == 0
06730         || strlen(args[3]) < 3)
06731     {
06732         sSend(":%s NOTICE %s :You cannot use \2%s\2 as your channel password.",
06733               ChanServ, from, args[3]);
06734         return RET_EFAULT;
06735     }
06736 
06737     sSend(":%s NOTICE %s :Ok.", ChanServ, from);
06738 
06739     rcl->flags |= CENCRYPT;
06740     rcl->chpw_key = 0;
06741     rcl->flags &= ~CFORCEXFER;
06742     pw_enter_password(args[3], rcl->password, ChanGetEnc(rcl));
06743     clearChanIdent(rcl);
06744 
06745     sSend(":%s NOTICE %s :The password for %s is now %s, do NOT forget it, we"
06746           " are not responsible for lost passwords.",
06747         ChanServ, from, rcl->name, args[3]);
06748 
06749     return RET_OK_DB;
06750 }
06751 
06755 NCMD(cs_setrealpass)
06756 {
06757     char* from = nick->nick;
06758     RegChanList* chn;
06759 
06760     if (!opFlagged(nick, OOPER | OGRP)) {
06761         sSend(":%s NOTICE %s :Permission denied -- this command is "
06762               "provided for PWops/GRPops.", ChanServ, from);
06763 
06764         return RET_NOPERM;
06765     }
06766 
06767     if (numargs < 2) {
06768         sSend(":%s NOTICE %s :Syntax Error: Channel name missing.",
06769             ChanServ, from);
06770         return RET_SYNTAX;
06771     }
06772 
06773     if (numargs < 3) {
06774         sSend(":%s NOTICE %s :Syntax Error: Password missing.",
06775             ChanServ, from);
06776         return RET_SYNTAX;
06777     }
06778 
06779     chn = getRegChanData(args[1]);
06780 
06781     if (chn == NULL) {
06782         sSend(":%s NOTICE %s :Setrealpass error: channel is not registered.",
06783             ChanServ, from);
06784         return RET_NOTARGET;
06785     }
06786 
06787     if ((!isRoot(nick) || !opFlagged(nick, OVERRIDE)) && is_sn_chan(args[1])) {
06788         sSend(":%s NOTICE %s :Permission denied: magic channel.",
06789             ChanServ, from);
06790         return RET_NOPERM;
06791     }
06792 
06793     pw_enter_password(args[2], chn->password, ChanGetEnc(chn));
06794     sSend(":%s NOTICE %s :New password installed.",
06795             ChanServ, from);
06796 
06797     return RET_OK_DB;
06798 }
06799 
06800 /*--------------------------------------------------------------------*/
06801 
06823 CCMD(cs_trigger)
06824 {
06825     // COMMAND_REQUIRE_REGNICK(nick);
06826     // COMMAND_REQUIRE_OPER(nick);
06827     // COMMAND_REQUIRE_PERMISSIONS(nick, OSERVOP);
06828     ChanTrigger* trg;
06829     char* from = nick->nick;
06830 
06831     if (!isOper(nick) || !isRoot(nick)) {
06832         PutReply(ChanServ, nick, ERR_NOACCESS, 0, 0, 0);
06833         return RET_NOPERM;
06834     }
06835 
06853     if (numargs == 1) {
06854         // Show system defaults
06855         sSend(":%s NOTICE %s :Default Values:", ChanServ, nick->nick);
06856         sSend(":%s NOTICE %s :MaxOps(%d), MaxAkicks(%d)", ChanServ, nick->nick, OpLimit, AkickLimit);
06857         return RET_OK;
06858     }
06859 
06860     if (numargs < 2) {
06861         sSend(":%s NOTICE %s :Syntax: TRIGGER [<channel> [<item> <value>]]", ChanServ, nick->nick);
06862         sSend(":%s NOTICE %s :        TRIGGER <channel> DEFAULTS", ChanServ, nick->nick);
06863         return RET_SYNTAX;
06864     }//$$ XXX
06865 
06866 
06867     if (!str_cmp(args[1], "LIST")) {
06868         long hashEnt;
06869         // List all triggers
06870 
06871         sSend(":%s NOTICE %s :%-30s %-3s %-3s", ChanServ, from, "Channel", "Ops", "Aks");
06872 
06873         for(hashEnt = 0; hashEnt < CHANTRIGHASHSIZE; hashEnt++)
06874             for(trg = LIST_FIRST(&ChanTrigHash[hashEnt]); trg;
06875                 trg = LIST_NEXT(trg, cn_lst))
06876             {
06877                 sSend(":%s NOTICE %s :%-30s %-3d %-3d", ChanServ,
06878                     from, trg->chan_name, DEFIFZERO(trg->op_trigger, OpLimit), DEFIFZERO(trg->ak_trigger, AkickLimit));
06879             }
06880         return RET_OK;
06881     }
06882 
06883     if (ValidChannelName(args[1]) == 0) { // Invalid channel name error
06884         PutReply(ChanServ, nick, ERR_CS_INVALIDCHAN_1ARG, args[1], 0, 0);
06885         return RET_EFAULT;
06886     }
06887 
06888     if (getRegChanData(args[1]) == 0) { // Channel not registered error
06889         PutReply(ChanServ, nick, ERR_CHANNOTREG_1ARG, args[1], 0, 0);
06890         return RET_NOTARGET;
06891     }
06892 
06893     trg = FindChannelTrigger(args[1]);
06894 
06895     if (numargs == 2) {
06896         // Show trigger information for channel
06897         sSend(":%s NOTICE %s :%s:", ChanServ, nick->nick, args[1]);
06898 
06899                 if (trg)
06900             sSend(":%s NOTICE %s :MaxOps(%d)  MaxAkicks(%d)", ChanServ, nick->nick,
06901                   DEFIFZERO(trg->op_trigger, OpLimit), DEFIFZERO(trg->ak_trigger, AkickLimit));
06902         else
06903             sSend(":%s NOTICE %s :MaxOps(%d)  MaxAkicks(%d)", ChanServ, nick->nick,
06904                   OpLimit, AkickLimit);
06905         return RET_OK;
06906     }
06907 
06908     {
06909     int k = -1;
06910 
06911     if (!str_cmp(args[2], "MaxOps"))
06912         k = 0;
06913     else if (!str_cmp(args[2], "MaxAkicks"))
06914         k = 1;
06915 
06916     if (k == -1 && (numargs != 4 || (numargs > 3 && args[3] && !isdigit(args[3][0])))) {
06917         if (!str_cmp(args[2], "DEFAULTS")) {
06918             // Delete the trigger info for this chan,
06919             // reverting it to default limits/state.
06920             // Log the change
06921             // Send a globops, maybe
06922 
06923             if (trg) {
06924                 DelChannelTrigger(trg);
06925                 FreeChannelTrigger(trg);
06926 
06927                 return RET_OK_DB;
06928             }
06929             return RET_NOTARGET;
06930         }
06931         // Syntax error, must specify trigger level
06932         return RET_SYNTAX;
06933     }
06934 
06935     if (k == -1) { // Error, no such trigger variable
06936         PutReply(ChanServ, nick, ERR_INVALID_TRIGVAR, 0, 0, 0);
06937         return RET_SYNTAX;
06938     }
06939 
06940     if (!trg) {
06941         trg = MakeChannelTrigger(args[1]);
06942         AddChannelTrigger(trg);
06943     }
06944 
06945     if (k == 0)
06946         trg->op_trigger = atoi(args[3]);
06947     if (k == 1)
06948         trg->ak_trigger = atoi(args[3]);
06949 
06950     // Add a trigger for the channel name if it doesn't exist,
06951     // make the appropriate changes
06952 
06953     // Log the change
06954     // Send globops
06955     return RET_OK_DB;
06956     }
06957 
06958     return RET_FAIL;
06959 }
06960 
06961 /*--------------------------------------------------------------------*/
06962 
06963 CCMD(cs_dmod)
06964 {
06965     RegChanList *chan;
06966         typedef enum { DMOD_UNDEFINED, DMOD_HELP, DMOD_TOPIC,
06967                        DMOD_FOUNDER } dModItem;
06968         dModItem tar = DMOD_UNDEFINED;
06969     char* from = nick->nick;
06970     int i;
06971 
06972     if (!isOper(nick) || !opFlagged(nick, ODMOD)) {
06973         PutReply(ChanServ, nick, ERR_NOACCESS, 0, 0, 0);
06974         return RET_NOPERM;
06975     }
06976 
06977         #define DMOD_KEY(nm, va) else if (strcmp(args[i], nm) == 0) {tar=(va); i++; break;}
06978 
06979     for(i = 1; i < numargs; i++) {
06980         if (args[i][0] != '-')
06981             break;
06982 
06983                 if (0);
06984         DMOD_KEY("-help", DMOD_HELP)
06985         DMOD_KEY("-topic", DMOD_TOPIC)
06986         DMOD_KEY("-founder", DMOD_FOUNDER)
06987     }
06988 
06989     if (tar == DMOD_UNDEFINED) {
06990         sSend(":%s NOTICE %s :An action is required.  See \2/cs help dmod\2  for more information.", ChanServ, from);
06991         return RET_SYNTAX;
06992     }
06993 
06994     if (i >= numargs) {
06995         sSend(":%s NOTICE %s :This command requires a channel.", ChanServ, from);
06996         return RET_SYNTAX;
06997     }
06998 
06999     if (i + 1 >= numargs) {
07000         sSend(":%s NOTICE %s :To what value?", ChanServ, from);
07001         return RET_SYNTAX;
07002     }
07003 
07004     chan = getRegChanData(args[i]);
07005     if (!chan) {
07006         sSend(":%s NOTICE %s :%s is not registered", ChanServ, from, args[1]);
07007         return RET_NOTARGET;
07008     }
07009 
07010     rshift_argv(args, i, numargs);
07011     numargs -= (i);
07012 
07013     switch( tar ) {
07014         case DMOD_FOUNDER :
07015             {
07016                 RegNickList* reg = getRegNickData(args[1]);
07017 
07018                 if (reg == NULL) {
07019                     PutReply(ChanServ, nick, ERR_NICKNOTREG_1ARG,
07020                         args[1], 0, 0);
07021                     return RET_NOTARGET;
07022                 }
07023 
07024                 chan->founderId = reg->regnum;
07025 
07026                 sSend(":%s NOTICE %s :%s now founder of %s.",
07027                       ChanServ, from, reg->nick, chan->name);
07028                 return RET_OK_DB;
07029             }
07030         case DMOD_TOPIC:
07031             {
07032                 char buf[TOPIC_MAX+1];
07033                 parse_str(args, numargs, 1, buf, TOPIC_MAX);
07034                 if (strcmp(buf, "*") == 0)
07035                     buf[0] = '\0';
07036                 if (chan->topic)
07037                     FREE(chan->topic);
07038                 chan->topic = static_cast<char *>(oalloc(strlen(buf)));
07039                 strcpy(chan->topic, buf);
07040                 strncpyzt(chan->tsetby, nick->nick, NICKLEN);
07041                 return RET_OK_DB;
07042             }
07043             break;
07044         default:;
07045             break;
07046     }
07047 
07048     return RET_EFAULT;
07049 }
07050 
07051 /*--------------------------------------------------------------------*/
07052 
07053 /* $Id: chanserv.c,v 1.3 2003/10/22 21:07:50 Mysid Exp $ */
07054 

Generated at Sat Oct 25 20:56:07 2003 for Services using Doxygen.
Services Copyr. 1996-2001 Chip Norkus, Max Byrd, Greg Poma, Michael Graff, James Hess, Dafydd James. All rights reserved See LICENSE for licensing information.