2 * IRC - Internet Relay Chat, ircd/m_cap.c
3 * Copyright (C) 2004 Kevin L. Mitchell <klmitch@mit.edu>
5 * See file AUTHORS in IRC package for additional names of
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 1, or (at your option)
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 * m_functions execute protocol messages on this server:
28 * cptr is always NON-NULL, pointing to a *LOCAL* client
29 * structure (with an open socket connected!). This
30 * identifies the physical socket where the message
31 * originated (or which caused the m_function to be
32 * executed--some m_functions may call others...).
34 * sptr is the source of the message, defined by the
35 * prefix part of the message if present. If not
36 * or prefix not found, then sptr==cptr.
38 * (!IsServer(cptr)) => (cptr == sptr), because
39 * prefixes are taken *only* from servers...
42 * (sptr == cptr) => the message didn't
45 * (sptr != cptr && IsServer(sptr) means
46 * the prefix specified servername. (?)
48 * (sptr != cptr && !IsServer(sptr) means
49 * that message originated from a remote
54 * (!IsServer(sptr)) means that, sptr can safely
55 * taken as defining the target structure of the
56 * message in this server.
58 * *Always* true (if 'parse' and others are working correct):
60 * 1) sptr->from == cptr (note: cptr->from == cptr)
62 * 2) MyConnect(sptr) <=> sptr == cptr (e.g. sptr
63 * *cannot* be a local connection, unless it's
64 * actually cptr!). [MyConnect(x) should probably
65 * be defined as (x == x->from) --msa ]
67 * parc number of variable parameter strings (if zero,
68 * parv is allowed to be NULL)
70 * parv a NULL terminated list of parameter pointers,
72 * parv[0], sender (prefix string), if not present
73 * this points to an empty string.
74 * parv[1]...parv[parc-1]
75 * pointers to additional parameters
76 * parv[parc] == NULL, *always*
78 * note: it is guaranteed that parv[0]..parv[parc-1] are all
85 #include "ircd_chattr.h"
87 #include "ircd_reply.h"
88 #include "ircd_snprintf.h"
89 #include "ircd_string.h"
98 typedef int (*bqcmp)(const void *, const void *);
100 static struct capabilities {
107 #define _CAP(cap, flags, name) \
108 { CAP_ ## cap, #cap, (flags), (name), sizeof(name) - 1 }
113 #define CAPAB_LIST_LEN (sizeof(capab_list) / sizeof(struct capabilities))
115 static struct CapSet clean_set; /* guaranteed to be all zeros (right?) */
118 capab_sort(const struct capabilities *cap1, const struct capabilities *cap2)
120 return ircd_strcmp(cap1->name, cap2->name);
124 capab_search(const char *key, const struct capabilities *cap)
126 const char *rb = cap->name;
127 while (ToLower(*key) == ToLower(*rb)) /* walk equivalent part of strings */
128 if (!*key++) /* hit the end, all right... */
130 else /* OK, let's move on... */
133 /* If the character they differ on happens to be a space, and it happens
134 * to be the same length as the capability name, then we've found a
135 * match; otherwise, return the difference of the two.
137 return (IsSpace(*key) && !*rb) ? 0 : (ToLower(*key) - ToLower(*rb));
140 static struct capabilities *
141 find_cap(const char **caplist_p, int *neg_p)
143 static int inited = 0;
144 const char *caplist = *caplist_p;
145 struct capabilities *cap = 0;
147 *neg_p = 0; /* clear negative flag... */
149 if (!inited) { /* First, let's sort the array... */
150 qsort(capab_list, CAPAB_LIST_LEN, sizeof(struct capabilities),
152 inited++; /* remember that we've done this step... */
155 /* Next, find first non-whitespace character... */
156 while (*caplist && IsSpace(*caplist))
159 /* We are now at the beginning of an element of the list; is it negative? */
160 if (*caplist == '-') {
161 caplist++; /* yes; step past the flag... */
162 *neg_p = 1; /* remember that it is negative... */
165 /* OK, now see if we can look up the capability... */
167 if (!(cap = (struct capabilities *)bsearch(caplist, capab_list,
169 sizeof(struct capabilities),
170 (bqcmp)capab_search))) {
171 /* Couldn't find the capability; advance to first whitespace character */
172 while (*caplist && !IsSpace(*caplist))
175 caplist += cap->namelen; /* advance to end of capability name */
178 assert(caplist != *caplist_p || !*caplist); /* we *must* advance */
180 /* move ahead in capability list string--or zero pointer if we hit end */
181 *caplist_p = *caplist ? caplist : 0;
183 return cap; /* and return the capability (if any) */
187 send_caplist(struct Client *sptr, const struct CapSet *cs)
189 char capbuf[BUFSIZE] = "";
193 /* set up the buffer for the LSL message... */
194 mb = msgq_make(sptr, "%:#C " MSG_CAP " LSL :", &me);
196 for (i = 0, loc = 0; i < CAPAB_LIST_LEN; i++) {
197 if (cs ? !CapHas(cs, capab_list[i].cap) :
198 (capab_list[i].flags & CAPFL_HIDDEN))
199 continue; /* not including this capability in the list */
201 len = capab_list[i].namelen + (loc != 0); /* how much we'd add... */
203 if (msgq_bufleft(mb) < loc + len) { /* would add too much; must flush */
204 sendcmdto_one(&me, CMD_CAP, sptr, "LS :%s", capbuf);
205 capbuf[(loc = 0)] = '\0'; /* re-terminate the buffer... */
208 loc += ircd_snprintf(0, capbuf + loc, sizeof(capbuf) - loc, "%s%s",
209 loc ? " " : "", capab_list[i].name);
212 msgq_append(0, mb, "%s", capbuf); /* append capabilities to the LSL cmd */
213 send_buffer(sptr, mb, 0); /* send them out... */
214 msgq_clean(mb); /* and release the buffer */
216 return 0; /* convenience return */
220 cap_empty(struct Client *sptr, const char *caplist)
222 if (IsUnknown(sptr)) /* registration hasn't completed; suspend it... */
223 cli_unreg(sptr) |= CLIREG_CAP;
225 return send_caplist(sptr, 0); /* send list of capabilities */
229 cap_req(struct Client *sptr, const char *caplist)
231 const char *cl = caplist;
232 struct capabilities *cap;
233 struct CapSet cs = *cli_capab(sptr); /* capability set */
234 struct CapSet as = *cli_active(sptr); /* active set */
237 if (IsUnknown(sptr)) /* registration hasn't completed; suspend it... */
238 cli_unreg(sptr) |= CLIREG_CAP;
240 while (cl) { /* walk through the capabilities list... */
241 if (!(cap = find_cap(&cl, &neg)) || /* look up capability... */
242 (!neg && (cap->flags & CAPFL_PROHIBIT))) { /* is it prohibited? */
243 sendcmdto_one(&me, CMD_CAP, sptr, "NAK :%s", caplist);
244 return 0; /* can't complete requested op... */
247 if (neg) { /* set or clear the capability... */
248 CapClr(&cs, cap->cap);
249 if (!(cap->flags & CAPFL_PROTO))
250 CapClr(&as, cap->cap);
252 CapSet(&cs, cap->cap);
253 if (!(cap->flags & CAPFL_PROTO))
254 CapSet(&as, cap->cap);
258 sendcmdto_one(&me, CMD_CAP, sptr, "ACK :%s", caplist);
260 *cli_capab(sptr) = cs; /* copy the completed results */
261 *cli_active(sptr) = as;
267 cap_ack(struct Client *sptr, const char *caplist)
269 const char *cl = caplist;
270 struct capabilities *cap;
273 while (cl) { /* walk through the capabilities list... */
274 if (!(cap = find_cap(&cl, &neg)) || /* look up capability... */
275 (neg ? HasCap(sptr, cap->cap) : !HasCap(sptr, cap->cap))) /* uh... */
278 if (neg) /* set or clear the active capability... */
279 CapClr(cli_active(sptr), cap->cap);
281 CapSet(cli_active(sptr), cap->cap);
288 cap_clear(struct Client *sptr, const char *caplist)
290 sendcmdto_one(&me, CMD_CAP, sptr, "CLEAR"); /* Reply... */
292 *cli_capab(sptr) = clean_set; /* then clear! */
298 cap_end(struct Client *sptr, const char *caplist)
300 if (!IsUnknown(sptr)) /* registration has completed... */
301 return 0; /* so just ignore the message... */
303 cli_unreg(sptr) &= ~CLIREG_CAP; /* capability negotiation is now done... */
305 if (!cli_unreg(sptr)) /* if client is now done... */
306 return register_user(sptr, sptr, cli_name(sptr), cli_user(sptr)->username);
308 return 0; /* Can't do registration yet... */
312 cap_list(struct Client *sptr, const char *caplist)
314 /* Send the list of the client's capabilities */
315 return send_caplist(sptr, cli_capab(sptr));
318 static struct subcmd {
320 int (*proc)(struct Client *sptr, const char *caplist);
324 { "CLEAR", cap_clear },
326 { "LIST", cap_list },
334 subcmd_search(const char *cmd, const struct subcmd *elem)
336 return ircd_strcmp(cmd, elem->cmd);
340 * m_cap - user message handler
342 * parv[0] = Send prefix
346 * parv[1] = [<subcommand>]
347 * parv[2] = [<capab list>]
351 m_cap(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
353 char *subcmd = "", *caplist = 0;
356 if (parc > 1 && parv[1]) /* a subcommand was provided */
358 if (parc > 2) /* a capability list was provided */
361 /* find the subcommand handler */
362 if (!(cmd = (struct subcmd *)bsearch(subcmd, cmdlist,
363 sizeof(cmdlist) / sizeof(struct subcmd),
364 sizeof(struct subcmd),
365 (bqcmp)subcmd_search)))
366 return send_reply(sptr, ERR_UNKNOWNCAPCMD, subcmd);
368 /* then execute it... */
369 return cmd->proc ? (cmd->proc)(sptr, caplist) : 0;