ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/modules/m_cap.c
(Generate patch)

Comparing ircd-hybrid/trunk/modules/m_cap.c (file contents):
Revision 4792 by michael, Tue Oct 28 12:44:43 2014 UTC vs.
Revision 9799 by michael, Tue Dec 8 20:42:15 2020 UTC

# Line 2 | Line 2
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-2014 ircd-hybrid development team
5 > *  Copyright (c) 2006-2020 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
# Line 27 | Line 27
27  
28   #include "stdinc.h"
29   #include "client.h"
30 #include "hash.h"
30   #include "ircd.h"
31   #include "numeric.h"
32   #include "user.h"
# Line 37 | Line 36
36   #include "irc_string.h"
37  
38  
39 < #define CAPFL_HIDDEN    0x0001  /**< Do not advertize this capability */
40 < #define CAPFL_PROHIBIT  0x0002  /**< Client may not set this capability */
41 < #define CAPFL_PROTO     0x0004  /**< Cap must be acknowledged by client */
42 < #define CAPFL_STICKY    0x0008  /**< Cap may not be cleared once set */
39 > enum
40 > {
41 >  CAPFL_HIDDEN    = 1 << 0,  /**< Do not advertize this capability */
42 >  CAPFL_STICKY    = 1 << 1   /**< Cap may not be cleared once set */
43 > };
44  
45   typedef int (*bqcmp)(const void *, const void *);
46  
# Line 56 | Line 56 | static struct capabilities
56    _CAP(CAP_UHNAMES, 0, "userhost-in-names"),
57    _CAP(CAP_MULTI_PREFIX, 0, "multi-prefix"),
58    _CAP(CAP_AWAY_NOTIFY, 0, "away-notify"),
59 <  _CAP(CAP_EXTENDED_JOIN, 0, "extended-join")
59 >  _CAP(CAP_EXTENDED_JOIN, 0, "extended-join"),
60 >  _CAP(CAP_ACCOUNT_NOTIFY, 0, "account-notify"),
61 >  _CAP(CAP_INVITE_NOTIFY, 0, "invite-notify"),
62 >  _CAP(CAP_CHGHOST, 0, "chghost")
63   #undef _CAP
64   };
65  
# Line 77 | Line 80 | capab_search(const char *key, const stru
80      if (*key++ == '\0')  /* Hit the end, all right... */
81        return 0;
82      else  /* OK, let's move on... */
83 <      rb++;
83 >      ++rb;
84  
85    /*
86     * If the character they differ on happens to be a space, and it happens
# Line 141 | Line 144 | find_cap(const char **caplist_p, int *ne
144   * @param[in] rem Capabalities to show as removed (with no other modifier).
145   * @param[in] subcmd Name of capability subcommand.
146   */
147 < static int
148 < send_caplist(struct Client *source_p, unsigned int set,
149 <             unsigned int rem, const char *subcmd)
147 > static void
148 > send_caplist(struct Client *source_p,
149 >             const unsigned int *const set,
150 >             const unsigned int *const rem, const char *subcmd)
151   {
152 <  char capbuf[IRCD_BUFSIZE] = "", pfx[16];
152 >  char capbuf[IRCD_BUFSIZE] = "", pfx[4];
153    char cmdbuf[IRCD_BUFSIZE] = "";
154 <  unsigned int i, loc, len, flags, pfx_len, clen;
154 >  unsigned int i, loc, len, pfx_len, clen;
155  
156    /* Set up the buffer for the final LS message... */
157    clen = snprintf(cmdbuf, sizeof(capbuf), ":%s CAP %s %s ", me.name,
# Line 155 | Line 159 | send_caplist(struct Client *source_p, un
159  
160    for (i = 0, loc = 0; i < CAPAB_LIST_LEN; ++i)
161    {
162 <    flags = capab_list[i].flags;
162 >    const struct capabilities *cap = &capab_list[i];
163  
164      /*
165       * This is a little bit subtle, but just involves applying de
# Line 163 | Line 167 | send_caplist(struct Client *source_p, un
167       * capability if (and only if) it is set in \a rem or \a set, or
168       * if both are null and the capability is hidden.
169       */
170 <    if (!(rem && (rem & capab_list[i].cap)) &&
171 <        !(set && (set & capab_list[i].cap)) &&
172 <         (rem || set || (flags & CAPFL_HIDDEN)))
170 >    if (!(rem && (*rem & cap->cap)) &&
171 >        !(set && (*set & cap->cap)) &&
172 >         (rem || set || (cap->flags & CAPFL_HIDDEN)))
173        continue;
174  
175      /* Build the prefix (space separator and any modifiers needed). */
# Line 173 | Line 177 | send_caplist(struct Client *source_p, un
177  
178      if (loc)
179        pfx[pfx_len++] = ' ';
180 <    if (rem && (rem & capab_list[i].cap))
180 >    if (rem && (*rem & cap->cap))
181        pfx[pfx_len++] = '-';
178    else
179    {
180      if (flags & CAPFL_PROTO)
181        pfx[pfx_len++] = '~';
182      if (flags & CAPFL_STICKY)
183        pfx[pfx_len++] = '=';
184    }
182  
183      pfx[pfx_len] = '\0';
184  
185 <    len = capab_list[i].namelen + pfx_len;  /* How much we'd add... */
185 >    len = cap->namelen + pfx_len;  /* How much we'd add... */
186  
187      if (sizeof(capbuf) < (clen + loc + len + 15))
188      {
# Line 195 | Line 192 | send_caplist(struct Client *source_p, un
192      }
193  
194      loc += snprintf(capbuf + loc, sizeof(capbuf) - loc,
195 <                    "%s%s", pfx, capab_list[i].name);
195 >                    "%s%s", pfx, cap->name);
196    }
197  
198    sendto_one(source_p, "%s:%s", cmdbuf, capbuf);
202
203  return 0;  /* Convenience return */
199   }
200  
201 < static int
202 < cap_ls(struct Client *source_p, const char *caplist)
201 > static void
202 > cap_ls(struct Client *source_p, const char *arg)
203   {
204    if (IsUnknown(source_p))  /* Registration hasn't completed; suspend it... */
205      source_p->connection->registration |= REG_NEED_CAP;
206  
207 <  return send_caplist(source_p, 0, 0, "LS");  /* Send list of capabilities */
207 >  if (arg && atoi(arg) >= 302)
208 >    AddFlag(source_p, FLAGS_CAP302);
209 >
210 >  send_caplist(source_p, NULL, NULL, "LS");  /* Send list of capabilities */
211   }
212  
213 < static int
214 < cap_req(struct Client *source_p, const char *caplist)
213 > static void
214 > cap_req(struct Client *source_p, const char *arg)
215   {
218  const char *cl = caplist;
219  struct capabilities *cap = NULL;
216    unsigned int set = 0, rem = 0;
217 <  unsigned int cs = source_p->connection->cap_client; /* capability set */
222 <  unsigned int as = source_p->connection->cap_active; /* active set */
217 >  unsigned int cs = source_p->connection->cap;  /* Enabled capabilities */
218    int neg = 0;
219  
220    if (IsUnknown(source_p))  /* Registration hasn't completed; suspend it... */
221      source_p->connection->registration |= REG_NEED_CAP;
222  
223 <  while (cl) {  /* Walk through the capabilities list... */
224 <    if (!(cap = find_cap(&cl, &neg))  /* Look up capability... */
225 <        || (!neg && (cap->flags & CAPFL_PROHIBIT))  /* Is it prohibited? */
226 <        || (neg && (cap->flags & CAPFL_STICKY))) {  /* Is it sticky? */
223 >  /* Walk through the capabilities list... */
224 >  for (const char *cl = arg; cl; )
225 >  {
226 >    /* Look up capability... */
227 >    const struct capabilities *cap = find_cap(&cl, &neg);
228 >    if (cap == NULL ||
229 >        (neg && (cap->flags & CAPFL_STICKY)))
230 >    {
231        sendto_one(source_p, ":%s CAP %s NAK :%s", me.name,
232 <                 source_p->name[0] ? source_p->name : "*", caplist);
233 <      return 0;  /* Can't complete requested op... */
232 >                 source_p->name[0] ? source_p->name : "*", arg);
233 >      return;  /* Can't complete requested op... */
234      }
235  
236      if (neg)
# Line 240 | Line 239 | cap_req(struct Client *source_p, const c
239        rem |=  cap->cap;
240        set &= ~cap->cap;
241        cs  &= ~cap->cap;
243
244      if (!(cap->flags & CAPFL_PROTO))
245        as &= ~cap->cap;
242      }
243      else
244      {
245        rem &= ~cap->cap;
246        set |=  cap->cap;
247        cs  |=  cap->cap;
252
253      if (!(cap->flags & CAPFL_PROTO))
254        as |= cap->cap;
248      }
249    }
250  
251    /* Notify client of accepted changes and copy over results. */
252 <  send_caplist(source_p, set, rem, "ACK");
260 <
261 <  source_p->connection->cap_client = cs;
262 <  source_p->connection->cap_active = as;
252 >  send_caplist(source_p, &set, &rem, "ACK");
253  
254 <  return 0;
254 >  source_p->connection->cap = cs;
255   }
256  
257 < static int
258 < cap_ack(struct Client *source_p, const char *caplist)
269 < {
270 <  const char *cl = caplist;
271 <  struct capabilities *cap = NULL;
272 <  int neg = 0;
273 <
274 <  /*
275 <   * Coming from the client, this generally indicates that the client
276 <   * is using a new backwards-incompatible protocol feature. As such,
277 <   * it does not require further response from the server.
278 <   */
279 <  while (cl)
280 <  {
281 <    /* Walk through the capabilities list... */
282 <    if (!(cap = find_cap(&cl, &neg)) ||  /* Look up capability... */
283 <        (neg ? (source_p->connection->cap_active & cap->cap) :
284 <              !(source_p->connection->cap_active & cap->cap)))  /* uh... */
285 <      continue;
286 <
287 <    if (neg)  /* Set or clear the active capability... */
288 <      source_p->connection->cap_active &= ~cap->cap;
289 <    else
290 <      source_p->connection->cap_active |=  cap->cap;
291 <  }
292 <
293 <  return 0;
294 < }
295 <
296 < static int
297 < cap_clear(struct Client *source_p, const char *caplist)
298 < {
299 <  struct capabilities *cap = NULL;
300 <  unsigned int cleared = 0;
301 <
302 <  for (unsigned int ii = 0; ii < CAPAB_LIST_LEN; ++ii)
303 <  {
304 <    cap = &capab_list[ii];
305 <
306 <    /* Only clear active non-sticky capabilities. */
307 <    if (!(source_p->connection->cap_active & cap->cap) || (cap->flags & CAPFL_STICKY))
308 <      continue;
309 <
310 <    cleared |= cap->cap;
311 <    source_p->connection->cap_client &= ~cap->cap;
312 <
313 <    if (!(cap->flags & CAPFL_PROTO))
314 <      source_p->connection->cap_active &= ~cap->cap;
315 <  }
316 <
317 <  return send_caplist(source_p, 0, cleared, "ACK");
318 < }
319 <
320 < static int
321 < cap_end(struct Client *source_p, const char *caplist)
257 > static void
258 > cap_end(struct Client *source_p, const char *arg)
259   {
260    if (!IsUnknown(source_p))  /* Registration has completed... */
261 <    return 0;  /* So just ignore the message... */
261 >    return;  /* So just ignore the message... */
262  
263    /* Capability negotiation is now done... */
264    source_p->connection->registration &= ~REG_NEED_CAP;
265  
266    /* If client is now done... */
267 <  if (!source_p->connection->registration)
331 <  {
267 >  if (source_p->connection->registration == 0)
268      register_local_user(source_p);
333    return 0;
334  }
335
336  return 0;  /* Can't do registration yet... */
269   }
270  
271 < static int
272 < cap_list(struct Client *source_p, const char *caplist)
271 > static void
272 > cap_list(struct Client *source_p, const char *arg)
273   {
274    /* Send the list of the client's capabilities */
275 <  return send_caplist(source_p, source_p->connection->cap_client, 0, "LIST");
275 >  send_caplist(source_p, &source_p->connection->cap, NULL, "LIST");
276   }
277  
278   static struct subcmd
279   {
280    const char *cmd;
281 <  int (*proc)(struct Client *, const char *);
281 >  void (*proc)(struct Client *, const char *);
282   } cmdlist[] = {
283 <  { "ACK",   cap_ack   },
284 <  { "CLEAR", cap_clear },
285 <  { "END",   cap_end   },
286 <  { "LIST",  cap_list  },
355 <  { "LS",    cap_ls    },
356 <  { "NAK",   NULL      },
357 <  { "REQ",   cap_req   }
283 >  { "END",  cap_end   },
284 >  { "LIST", cap_list  },
285 >  { "LS",   cap_ls    },
286 >  { "REQ",  cap_req   }
287   };
288  
289   static int
# Line 375 | Line 304 | subcmd_search(const char *cmd, const str
304   *      - parv[1] = CAP subcommand
305   *      - parv[2] = space-separated list of capabilities
306   */
307 < static int
307 > static void
308   m_cap(struct Client *source_p, int parc, char *parv[])
309   {
310 <  const char *subcmd = NULL, *caplist = NULL;
310 >  const char *subcmd = parv[1], *caplist = parv[2];
311    struct subcmd *cmd = NULL;
312  
384  if (EmptyString(parv[1]))  /* A subcommand is required */
385    return 0;
386
387  subcmd = parv[1];
388
389  if (parc > 2)  /* A capability list was provided */
390    caplist = parv[2];
391
313    /* Find the subcommand handler */
314    if (!(cmd = bsearch(subcmd, cmdlist,
315                        sizeof(cmdlist) / sizeof(struct subcmd),
316                        sizeof(struct subcmd), (bqcmp)subcmd_search)))
317    {
318      sendto_one_numeric(source_p, &me, ERR_INVALIDCAPCMD, subcmd);
319 <    return 0;
319 >    return;
320    }
321  
322    /* Then execute it... */
323    if (cmd->proc)
324      (cmd->proc)(source_p, caplist);
404  return 0;
325   }
326  
327   static struct Message cap_msgtab =
328   {
329 <  "CAP", NULL, 0, 0, 2, MAXPARA, MFLG_SLOW, 0,
330 <  { m_cap, m_cap, m_ignore, m_ignore, m_cap, m_ignore }
329 >  .cmd = "CAP",
330 >  .handlers[UNREGISTERED_HANDLER] = { .handler = m_cap, .args_min = 2 },
331 >  .handlers[CLIENT_HANDLER] = { .handler = m_cap, .args_min = 2 },
332 >  .handlers[SERVER_HANDLER] = { .handler = m_ignore },
333 >  .handlers[ENCAP_HANDLER] = { .handler = m_ignore },
334 >  .handlers[OPER_HANDLER] = { .handler = m_cap, .args_min = 2 }
335   };
336  
337   static void
# Line 426 | Line 350 | module_exit(void)
350  
351   struct module module_entry =
352   {
429  .node    = { NULL, NULL, NULL },
430  .name    = NULL,
353    .version = "$Revision$",
432  .handle  = NULL,
354    .modinit = module_init,
355    .modexit = module_exit,
435  .flags   = 0
356   };

Diff Legend

Removed lines
+ Added lines
< Changed lines (old)
> Changed lines (new)