"Fossies" - the Fresh Open Source Software Archive

Member "ircd-hybrid-8.2.26/modules/m_cap.c" (31 May 2019, 12909 Bytes) of package /linux/privat/ircd-hybrid-8.2.26.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "m_cap.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 8.2.24_vs_8.2.25.

    1 /*
    2  *  ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
    3  *
    4  *  Copyright (c) 2004 Kevin L. Mitchell <klmitch@mit.edu>
    5  *  Copyright (c) 2006-2019 ircd-hybrid development team
    6  *
    7  *  This program is free software; you can redistribute it and/or modify
    8  *  it under the terms of the GNU General Public License as published by
    9  *  the Free Software Foundation; either version 2 of the License, or
   10  *  (at your option) any later version.
   11  *
   12  *  This program is distributed in the hope that it will be useful,
   13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   15  *  GNU General Public License for more details.
   16  *
   17  *  You should have received a copy of the GNU General Public License
   18  *  along with this program; if not, write to the Free Software
   19  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
   20  *  USA
   21  */
   22 
   23 /*! \file m_cap.c
   24  * \brief Includes required functions for processing the CAP command.
   25  * \version $Id: m_cap.c 8751 2019-01-01 11:06:50Z michael $
   26  */
   27 
   28 #include "stdinc.h"
   29 #include "client.h"
   30 #include "ircd.h"
   31 #include "numeric.h"
   32 #include "user.h"
   33 #include "send.h"
   34 #include "parse.h"
   35 #include "modules.h"
   36 #include "irc_string.h"
   37 
   38 
   39 enum
   40 {
   41   CAPFL_HIDDEN    = 1 << 0,  /**< Do not advertize this capability */
   42   CAPFL_PROHIBIT  = 1 << 1,  /**< Client may not set this capability */
   43   CAPFL_PROTO     = 1 << 2,  /**< Cap must be acknowledged by client */
   44   CAPFL_STICKY    = 1 << 3   /**< Cap may not be cleared once set */
   45 };
   46 
   47 typedef int (*bqcmp)(const void *, const void *);
   48 
   49 static struct capabilities
   50 {
   51   unsigned int cap;
   52   unsigned int flags;
   53   const char *name;
   54   size_t namelen;
   55 } capab_list[] = {
   56 #define _CAP(cap, flags, name)  \
   57     { (cap), (flags), (name), sizeof(name) - 1 }
   58   _CAP(CAP_UHNAMES, 0, "userhost-in-names"),
   59   _CAP(CAP_MULTI_PREFIX, 0, "multi-prefix"),
   60   _CAP(CAP_AWAY_NOTIFY, 0, "away-notify"),
   61   _CAP(CAP_EXTENDED_JOIN, 0, "extended-join"),
   62   _CAP(CAP_ACCOUNT_NOTIFY, 0, "account-notify"),
   63   _CAP(CAP_INVITE_NOTIFY, 0, "invite-notify"),
   64   _CAP(CAP_CHGHOST, 0, "chghost")
   65 #undef _CAP
   66 };
   67 
   68 #define CAPAB_LIST_LEN  (sizeof(capab_list) / sizeof(struct capabilities))
   69 
   70 static int
   71 capab_sort(const struct capabilities *cap1, const struct capabilities *cap2)
   72 {
   73   return strcasecmp(cap1->name, cap2->name);
   74 }
   75 
   76 static int
   77 capab_search(const char *key, const struct capabilities *cap)
   78 {
   79   const char *rb = cap->name;
   80 
   81   while (ToLower(*key) == ToLower(*rb))  /* Walk equivalent part of strings */
   82     if (*key++ == '\0')  /* Hit the end, all right... */
   83       return 0;
   84     else  /* OK, let's move on... */
   85       ++rb;
   86 
   87   /*
   88    * If the character they differ on happens to be a space, and it happens
   89    * to be the same length as the capability name, then we've found a
   90    * match; otherwise, return the difference of the two.
   91    */
   92   return (IsSpace(*key) && *rb == '\0') ? 0 : (ToLower(*key) - ToLower(*rb));
   93 }
   94 
   95 static struct capabilities *
   96 find_cap(const char **caplist_p, int *neg_p)
   97 {
   98   const char *caplist = *caplist_p;
   99   struct capabilities *cap = NULL;
  100 
  101   *neg_p = 0;  /* Clear negative flag... */
  102 
  103   /* Next, find first non-whitespace character... */
  104   while (*caplist && IsSpace(*caplist))
  105     ++caplist;
  106 
  107   /* We are now at the beginning of an element of the list; is it negative? */
  108   if (*caplist == '-')
  109   {
  110     ++caplist;  /* Yes; step past the flag... */
  111     *neg_p = 1;  /* Remember that it is negative... */
  112   }
  113 
  114   /* OK, now see if we can look up the capability... */
  115   if (*caplist)
  116   {
  117     if (!(cap = bsearch(caplist, capab_list, CAPAB_LIST_LEN,
  118                         sizeof(struct capabilities),
  119                         (bqcmp)capab_search)))
  120     {
  121       /* Couldn't find the capability; advance to first whitespace character */
  122       while (*caplist && !IsSpace(*caplist))
  123         ++caplist;
  124     }
  125     else
  126       caplist += cap->namelen;  /* Advance to end of capability name */
  127 
  128     /* Strip trailing spaces */
  129     while (*caplist && IsSpace(*caplist))
  130       ++caplist;
  131   }
  132 
  133   assert(caplist != *caplist_p || !*caplist);  /* We *must* advance */
  134 
  135   /* Move ahead in capability list string--or zero pointer if we hit end */
  136   *caplist_p = *caplist ? caplist : NULL;
  137 
  138   return cap;  /* And return the capability (if any) */
  139 }
  140 
  141 /** Send a CAP \a subcmd list of capability changes to \a source_p.
  142  * If more than one line is necessary, each line before the last has
  143  * an added "*" parameter before that line's capability list.
  144  * @param[in] source_p Client receiving capability list.
  145  * @param[in] set Capabilities to show as set (with ack and sticky modifiers).
  146  * @param[in] rem Capabalities to show as removed (with no other modifier).
  147  * @param[in] subcmd Name of capability subcommand.
  148  */
  149 static void
  150 send_caplist(struct Client *source_p,
  151              const unsigned int *const set,
  152              const unsigned int *const rem, const char *subcmd)
  153 {
  154   char capbuf[IRCD_BUFSIZE] = "", pfx[16];
  155   char cmdbuf[IRCD_BUFSIZE] = "";
  156   unsigned int i, loc, len, pfx_len, clen;
  157 
  158   /* Set up the buffer for the final LS message... */
  159   clen = snprintf(cmdbuf, sizeof(capbuf), ":%s CAP %s %s ", me.name,
  160                   source_p->name[0] ? source_p->name : "*", subcmd);
  161 
  162   for (i = 0, loc = 0; i < CAPAB_LIST_LEN; ++i)
  163   {
  164     const struct capabilities *cap = &capab_list[i];
  165 
  166     /*
  167      * This is a little bit subtle, but just involves applying de
  168      * Morgan's laws to the obvious check: We must display the
  169      * capability if (and only if) it is set in \a rem or \a set, or
  170      * if both are null and the capability is hidden.
  171      */
  172     if (!(rem && (*rem & cap->cap)) &&
  173         !(set && (*set & cap->cap)) &&
  174          (rem || set || (cap->flags & CAPFL_HIDDEN)))
  175       continue;
  176 
  177     /* Build the prefix (space separator and any modifiers needed). */
  178     pfx_len = 0;
  179 
  180     if (loc)
  181       pfx[pfx_len++] = ' ';
  182     if (rem && (*rem & cap->cap))
  183       pfx[pfx_len++] = '-';
  184     else
  185     {
  186       if (cap->flags & CAPFL_PROTO)
  187         pfx[pfx_len++] = '~';
  188       if (cap->flags & CAPFL_STICKY)
  189         pfx[pfx_len++] = '=';
  190     }
  191 
  192     pfx[pfx_len] = '\0';
  193 
  194     len = cap->namelen + pfx_len;  /* How much we'd add... */
  195 
  196     if (sizeof(capbuf) < (clen + loc + len + 15))
  197     {
  198       /* Would add too much; must flush */
  199       sendto_one(source_p, "%s* :%s", cmdbuf, capbuf);
  200       capbuf[(loc = 0)] = '\0';  /* Re-terminate the buffer... */
  201     }
  202 
  203     loc += snprintf(capbuf + loc, sizeof(capbuf) - loc,
  204                     "%s%s", pfx, cap->name);
  205   }
  206 
  207   sendto_one(source_p, "%s:%s", cmdbuf, capbuf);
  208 }
  209 
  210 static void
  211 cap_ls(struct Client *source_p, const char *caplist)
  212 {
  213   if (IsUnknown(source_p))  /* Registration hasn't completed; suspend it... */
  214     source_p->connection->registration |= REG_NEED_CAP;
  215 
  216   send_caplist(source_p, NULL, NULL, "LS");  /* Send list of capabilities */
  217 }
  218 
  219 static void
  220 cap_req(struct Client *source_p, const char *caplist)
  221 {
  222   const char *cl = caplist;
  223   struct capabilities *cap = NULL;
  224   unsigned int set = 0, rem = 0;
  225   unsigned int cs = source_p->connection->cap_client; /* capability set */
  226   unsigned int as = source_p->connection->cap_active; /* active set */
  227   int neg = 0;
  228 
  229   if (IsUnknown(source_p))  /* Registration hasn't completed; suspend it... */
  230     source_p->connection->registration |= REG_NEED_CAP;
  231 
  232   while (cl) {  /* Walk through the capabilities list... */
  233     if (!(cap = find_cap(&cl, &neg))  /* Look up capability... */
  234         || (!neg && (cap->flags & CAPFL_PROHIBIT))  /* Is it prohibited? */
  235         || (neg && (cap->flags & CAPFL_STICKY))) {  /* Is it sticky? */
  236       sendto_one(source_p, ":%s CAP %s NAK :%s", me.name,
  237                  source_p->name[0] ? source_p->name : "*", caplist);
  238       return;  /* Can't complete requested op... */
  239     }
  240 
  241     if (neg)
  242     {
  243       /* Set or clear the capability... */
  244       rem |=  cap->cap;
  245       set &= ~cap->cap;
  246       cs  &= ~cap->cap;
  247 
  248       if (!(cap->flags & CAPFL_PROTO))
  249         as &= ~cap->cap;
  250     }
  251     else
  252     {
  253       rem &= ~cap->cap;
  254       set |=  cap->cap;
  255       cs  |=  cap->cap;
  256 
  257       if (!(cap->flags & CAPFL_PROTO))
  258         as |= cap->cap;
  259     }
  260   }
  261 
  262   /* Notify client of accepted changes and copy over results. */
  263   send_caplist(source_p, &set, &rem, "ACK");
  264 
  265   source_p->connection->cap_client = cs;
  266   source_p->connection->cap_active = as;
  267 }
  268 
  269 static void
  270 cap_ack(struct Client *source_p, const char *caplist)
  271 {
  272   const char *cl = caplist;
  273   struct capabilities *cap = NULL;
  274   int neg = 0;
  275 
  276   /*
  277    * Coming from the client, this generally indicates that the client
  278    * is using a new backwards-incompatible protocol feature. As such,
  279    * it does not require further response from the server.
  280    */
  281   while (cl)
  282   {
  283     /* Walk through the capabilities list... */
  284     if (!(cap = find_cap(&cl, &neg)) ||  /* Look up capability... */
  285         (neg ? (source_p->connection->cap_client & cap->cap) :
  286               !(source_p->connection->cap_client & cap->cap)))  /* uh... */
  287       continue;
  288 
  289     /* Set or clear the active capability... */
  290     if (neg)
  291     {
  292       if (cap->flags & CAPFL_STICKY)
  293         continue;  /* but don't clear sticky capabilities */
  294 
  295       source_p->connection->cap_active &= ~cap->cap;
  296     }
  297     else
  298     {
  299       if (cap->flags & CAPFL_PROHIBIT)
  300         continue;  /* and don't set prohibited ones */
  301 
  302       source_p->connection->cap_active |=  cap->cap;
  303     }
  304   }
  305 }
  306 
  307 static void
  308 cap_clear(struct Client *source_p, const char *caplist)
  309 {
  310   unsigned int cleared = 0;
  311 
  312   for (unsigned int ii = 0; ii < CAPAB_LIST_LEN; ++ii)
  313   {
  314     const struct capabilities *cap = &capab_list[ii];
  315 
  316     /* Only clear active non-sticky capabilities. */
  317     if (!(source_p->connection->cap_client & cap->cap) || (cap->flags & CAPFL_STICKY))
  318       continue;
  319 
  320     cleared |= cap->cap;
  321     source_p->connection->cap_client &= ~cap->cap;
  322 
  323     if (!(cap->flags & CAPFL_PROTO))
  324       source_p->connection->cap_active &= ~cap->cap;
  325   }
  326 
  327   send_caplist(source_p, NULL, &cleared, "ACK");
  328 }
  329 
  330 static void
  331 cap_end(struct Client *source_p, const char *caplist)
  332 {
  333   if (!IsUnknown(source_p))  /* Registration has completed... */
  334     return;  /* So just ignore the message... */
  335 
  336   /* Capability negotiation is now done... */
  337   source_p->connection->registration &= ~REG_NEED_CAP;
  338 
  339   /* If client is now done... */
  340   if (source_p->connection->registration == 0)
  341     register_local_user(source_p);
  342 }
  343 
  344 static void
  345 cap_list(struct Client *source_p, const char *caplist)
  346 {
  347   /* Send the list of the client's capabilities */
  348   send_caplist(source_p, &source_p->connection->cap_client, NULL, "LIST");
  349 }
  350 
  351 static struct subcmd
  352 {
  353   const char *cmd;
  354   void (*proc)(struct Client *, const char *);
  355 } cmdlist[] = {
  356   { "ACK",   cap_ack   },
  357   { "CLEAR", cap_clear },
  358   { "END",   cap_end   },
  359   { "LIST",  cap_list  },
  360   { "LS",    cap_ls    },
  361   { "NAK",   NULL      },
  362   { "REQ",   cap_req   }
  363 };
  364 
  365 static int
  366 subcmd_search(const char *cmd, const struct subcmd *elem)
  367 {
  368   return strcasecmp(cmd, elem->cmd);
  369 }
  370 
  371 /*! \brief CAP command handler
  372  *
  373  * \param source_p Pointer to allocated Client struct from which the message
  374  *                 originally comes from.  This can be a local or remote client.
  375  * \param parc     Integer holding the number of supplied arguments.
  376  * \param parv     Argument vector where parv[0] .. parv[parc-1] are non-NULL
  377  *                 pointers.
  378  * \note Valid arguments for this command are:
  379  *      - parv[0] = command
  380  *      - parv[1] = CAP subcommand
  381  *      - parv[2] = space-separated list of capabilities
  382  */
  383 static int
  384 m_cap(struct Client *source_p, int parc, char *parv[])
  385 {
  386   const char *subcmd = NULL, *caplist = NULL;
  387   struct subcmd *cmd = NULL;
  388 
  389   if (EmptyString(parv[1]))  /* A subcommand is required */
  390     return 0;
  391 
  392   subcmd = parv[1];
  393 
  394   if (parc > 2)  /* A capability list was provided */
  395     caplist = parv[2];
  396 
  397   /* Find the subcommand handler */
  398   if (!(cmd = bsearch(subcmd, cmdlist,
  399                       sizeof(cmdlist) / sizeof(struct subcmd),
  400                       sizeof(struct subcmd), (bqcmp)subcmd_search)))
  401   {
  402     sendto_one_numeric(source_p, &me, ERR_INVALIDCAPCMD, subcmd);
  403     return 0;
  404   }
  405 
  406   /* Then execute it... */
  407   if (cmd->proc)
  408     (cmd->proc)(source_p, caplist);
  409   return 0;
  410 }
  411 
  412 static struct Message cap_msgtab =
  413 {
  414   .cmd = "CAP",
  415   .args_min = 2,
  416   .args_max = MAXPARA,
  417   .handlers[UNREGISTERED_HANDLER] = m_cap,
  418   .handlers[CLIENT_HANDLER] = m_cap,
  419   .handlers[SERVER_HANDLER] = m_ignore,
  420   .handlers[ENCAP_HANDLER] = m_ignore,
  421   .handlers[OPER_HANDLER] = m_cap
  422 };
  423 
  424 static void
  425 module_init(void)
  426 {
  427   /* First, let's sort the array */
  428   qsort(capab_list, CAPAB_LIST_LEN, sizeof(struct capabilities), (bqcmp)capab_sort);
  429   mod_add_cmd(&cap_msgtab);
  430 }
  431 
  432 static void
  433 module_exit(void)
  434 {
  435   mod_del_cmd(&cap_msgtab);
  436 }
  437 
  438 struct module module_entry =
  439 {
  440   .version = "$Revision: 8751 $",
  441   .modinit = module_init,
  442   .modexit = module_exit,
  443 };