ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/modules/m_cap.c
Revision: 9857
Committed: Fri Jan 1 04:43:22 2021 UTC (5 years, 6 months ago) by michael
Content type: text/x-csrc
File size: 10627 byte(s)
Log Message:
- Bump copyright years

File Contents

# Content
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-2021 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$
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 typedef int (*bqcmp)(const void *, const void *);
40
41 static struct capabilities
42 {
43 unsigned int cap;
44 bool sticky; /**< Cap may not be cleared once set */
45 const char *name;
46 size_t name_len;
47 } capab_list[] = {
48 #define _CAP(cap, flags, name) \
49 { (cap), (flags), (name), sizeof(name) - 1 }
50 _CAP(CAP_UHNAMES, false, "userhost-in-names"),
51 _CAP(CAP_MULTI_PREFIX, false, "multi-prefix"),
52 _CAP(CAP_AWAY_NOTIFY, false, "away-notify"),
53 _CAP(CAP_EXTENDED_JOIN, false, "extended-join"),
54 _CAP(CAP_ACCOUNT_NOTIFY, false, "account-notify"),
55 _CAP(CAP_CAP_NOTIFY, false, "cap-notify"),
56 _CAP(CAP_INVITE_NOTIFY, false, "invite-notify"),
57 _CAP(CAP_CHGHOST, false, "chghost")
58 #undef _CAP
59 };
60
61 #define CAPAB_LIST_LEN (sizeof(capab_list) / sizeof(struct capabilities))
62
63 static int
64 capab_sort(const struct capabilities *cap1, const struct capabilities *cap2)
65 {
66 return strcasecmp(cap1->name, cap2->name);
67 }
68
69 static int
70 capab_search(const char *key, const struct capabilities *cap)
71 {
72 const char *rb = cap->name;
73
74 while (ToLower(*key) == ToLower(*rb)) /* Walk equivalent part of strings */
75 if (*key++ == '\0') /* Hit the end, all right... */
76 return 0;
77 else /* OK, let's move on... */
78 ++rb;
79
80 /*
81 * If the character they differ on happens to be a space, and it happens
82 * to be the same length as the capability name, then we've found a
83 * match; otherwise, return the difference of the two.
84 */
85 return (IsSpace(*key) && *rb == '\0') ? 0 : (ToLower(*key) - ToLower(*rb));
86 }
87
88 static struct capabilities *
89 find_cap(const char **caplist_p, int *neg_p)
90 {
91 const char *caplist = *caplist_p;
92 struct capabilities *cap = NULL;
93
94 *neg_p = 0; /* Clear negative flag... */
95
96 /* Next, find first non-whitespace character... */
97 while (*caplist && IsSpace(*caplist))
98 ++caplist;
99
100 /* We are now at the beginning of an element of the list; is it negative? */
101 if (*caplist == '-')
102 {
103 ++caplist; /* Yes; step past the flag... */
104 *neg_p = 1; /* Remember that it is negative... */
105 }
106
107 /* OK, now see if we can look up the capability... */
108 if (*caplist)
109 {
110 if (!(cap = bsearch(caplist, capab_list, CAPAB_LIST_LEN,
111 sizeof(struct capabilities),
112 (bqcmp)capab_search)))
113 {
114 /* Couldn't find the capability; advance to first whitespace character */
115 while (*caplist && !IsSpace(*caplist))
116 ++caplist;
117 }
118 else
119 caplist += cap->name_len; /* Advance to end of capability name */
120
121 /* Strip trailing spaces */
122 while (*caplist && IsSpace(*caplist))
123 ++caplist;
124 }
125
126 assert(caplist != *caplist_p || !*caplist); /* We *must* advance */
127
128 /* Move ahead in capability list string--or zero pointer if we hit end */
129 *caplist_p = *caplist ? caplist : NULL;
130
131 return cap; /* And return the capability (if any) */
132 }
133
134 /** Send a CAP \a subcmd list of capability changes to \a source_p.
135 * If more than one line is necessary, each line before the last has
136 * an added "*" parameter before that line's capability list.
137 * @param[in] source_p Client receiving capability list.
138 * @param[in] set Capabilities to show as set (with ack and sticky modifiers).
139 * @param[in] rem Capabalities to show as removed (with no other modifier).
140 * @param[in] subcmd Name of capability subcommand.
141 */
142 static void
143 send_caplist(struct Client *source_p,
144 const unsigned int *const set,
145 const unsigned int *const rem, const char *subcmd)
146 {
147 char capbuf[IRCD_BUFSIZE] = "", pfx[4];
148 char cmdbuf[IRCD_BUFSIZE] = "";
149 unsigned int i, loc, len, pfx_len, clen;
150
151 /* Set up the buffer for the final LS message... */
152 clen = snprintf(cmdbuf, sizeof(cmdbuf), ":%s CAP %s %s ", me.name,
153 source_p->name[0] ? source_p->name : "*", subcmd);
154
155 for (i = 0, loc = 0; i < CAPAB_LIST_LEN; ++i)
156 {
157 const struct capabilities *cap = &capab_list[i];
158
159 /*
160 * This is a little bit subtle, but just involves applying de
161 * Morgan's laws to the obvious check: We must display the
162 * capability if (and only if) it is set in \a rem or \a set, or
163 * if both are null.
164 */
165 if (!(rem && (*rem & cap->cap)) &&
166 !(set && (*set & cap->cap)) && (rem || set))
167 continue;
168
169 /* Build the prefix (space separator and any modifiers needed). */
170 pfx_len = 0;
171
172 if (loc)
173 pfx[pfx_len++] = ' ';
174 if (rem && (*rem & cap->cap))
175 pfx[pfx_len++] = '-';
176
177 pfx[pfx_len] = '\0';
178
179 len = cap->name_len + pfx_len; /* How much we'd add... */
180
181 if (sizeof(capbuf) < (clen + loc + len + 15))
182 {
183 /* Would add too much; must flush */
184 sendto_one(source_p, "%s* :%s", cmdbuf, capbuf);
185 capbuf[(loc = 0)] = '\0'; /* Re-terminate the buffer... */
186 }
187
188 loc += snprintf(capbuf + loc, sizeof(capbuf) - loc,
189 "%s%s", pfx, cap->name);
190 }
191
192 sendto_one(source_p, "%s:%s", cmdbuf, capbuf);
193 }
194
195 static void
196 cap_ls(struct Client *source_p, const char *arg)
197 {
198 if (IsUnknown(source_p)) /* Registration hasn't completed; suspend it... */
199 source_p->connection->registration |= REG_NEED_CAP;
200
201 if (arg && atoi(arg) >= 302)
202 {
203 source_p->connection->cap |= CAP_CAP_NOTIFY;
204 AddFlag(source_p, FLAGS_CAP302);
205 }
206
207 send_caplist(source_p, NULL, NULL, "LS"); /* Send list of capabilities */
208 }
209
210 static void
211 cap_req(struct Client *source_p, const char *arg)
212 {
213 unsigned int set = 0, rem = 0;
214 unsigned int cs = source_p->connection->cap; /* Enabled capabilities */
215 int neg = 0;
216
217 if (IsUnknown(source_p)) /* Registration hasn't completed; suspend it... */
218 source_p->connection->registration |= REG_NEED_CAP;
219
220 /* Walk through the capabilities list... */
221 for (const char *cl = arg; cl; )
222 {
223 /* Look up capability... */
224 const struct capabilities *cap = find_cap(&cl, &neg);
225 bool error = false;
226
227 if (cap == NULL)
228 error = true;
229 else if (neg && (cap->sticky == true))
230 error = true;
231 else if (neg && (cap->cap & CAP_CAP_NOTIFY) && HasFlag(source_p, FLAGS_CAP302))
232 error = true;
233
234 if (error == true)
235 {
236 sendto_one(source_p, ":%s CAP %s NAK :%s", me.name,
237 source_p->name[0] ? source_p->name : "*", arg);
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 else
249 {
250 rem &= ~cap->cap;
251 set |= cap->cap;
252 cs |= cap->cap;
253 }
254 }
255
256 /* Notify client of accepted changes and copy over results. */
257 send_caplist(source_p, &set, &rem, "ACK");
258
259 source_p->connection->cap = cs;
260 }
261
262 static void
263 cap_end(struct Client *source_p, const char *arg)
264 {
265 if (!IsUnknown(source_p)) /* Registration has completed... */
266 return; /* So just ignore the message... */
267
268 /* Capability negotiation is now done... */
269 source_p->connection->registration &= ~REG_NEED_CAP;
270
271 /* If client is now done... */
272 if (source_p->connection->registration == 0)
273 register_local_user(source_p);
274 }
275
276 static void
277 cap_list(struct Client *source_p, const char *arg)
278 {
279 /* Send the list of the client's capabilities */
280 send_caplist(source_p, &source_p->connection->cap, NULL, "LIST");
281 }
282
283 static struct subcmd
284 {
285 const char *cmd;
286 void (*proc)(struct Client *, const char *);
287 } cmdlist[] = {
288 { "END", cap_end },
289 { "LIST", cap_list },
290 { "LS", cap_ls },
291 { "REQ", cap_req }
292 };
293
294 static int
295 subcmd_search(const char *cmd, const struct subcmd *elem)
296 {
297 return strcasecmp(cmd, elem->cmd);
298 }
299
300 /*! \brief CAP command handler
301 *
302 * \param source_p Pointer to allocated Client struct from which the message
303 * originally comes from. This can be a local or remote client.
304 * \param parc Integer holding the number of supplied arguments.
305 * \param parv Argument vector where parv[0] .. parv[parc-1] are non-NULL
306 * pointers.
307 * \note Valid arguments for this command are:
308 * - parv[0] = command
309 * - parv[1] = CAP subcommand
310 * - parv[2] = space-separated list of capabilities
311 */
312 static void
313 m_cap(struct Client *source_p, int parc, char *parv[])
314 {
315 const char *subcmd = parv[1], *caplist = parv[2];
316 struct subcmd *cmd = NULL;
317
318 /* Find the subcommand handler */
319 if (!(cmd = bsearch(subcmd, cmdlist,
320 sizeof(cmdlist) / sizeof(struct subcmd),
321 sizeof(struct subcmd), (bqcmp)subcmd_search)))
322 {
323 sendto_one_numeric(source_p, &me, ERR_INVALIDCAPCMD, subcmd);
324 return;
325 }
326
327 /* Then execute it... */
328 cmd->proc(source_p, caplist);
329 }
330
331 static struct Message cap_msgtab =
332 {
333 .cmd = "CAP",
334 .handlers[UNREGISTERED_HANDLER] = { .handler = m_cap, .args_min = 2 },
335 .handlers[CLIENT_HANDLER] = { .handler = m_cap, .args_min = 2 },
336 .handlers[SERVER_HANDLER] = { .handler = m_ignore },
337 .handlers[ENCAP_HANDLER] = { .handler = m_ignore },
338 .handlers[OPER_HANDLER] = { .handler = m_cap, .args_min = 2 }
339 };
340
341 static void
342 module_init(void)
343 {
344 /* First, let's sort the array */
345 qsort(capab_list, CAPAB_LIST_LEN, sizeof(struct capabilities), (bqcmp)capab_sort);
346 mod_add_cmd(&cap_msgtab);
347 }
348
349 static void
350 module_exit(void)
351 {
352 mod_del_cmd(&cap_msgtab);
353 }
354
355 struct module module_entry =
356 {
357 .version = "$Revision$",
358 .modinit = module_init,
359 .modexit = module_exit,
360 .is_resident = true /* XXX for now until caps are completely modular */
361 };

Properties

Name Value
svn:eol-style native
svn:keywords Id Revision