]>
jfr.im git - solanum.git/blob - modules/m_cap.c
3 * Copyright (C) 2005 Lee Hardy <lee@leeh.co.uk>
4 * Copyright (C) 2005 ircd-ratbox development team
5 * Copyright (C) 2016 William Pitcock <nenolod@dereferenced.org>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
11 * 1.Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 * 2.Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3.The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
23 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
28 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
47 typedef int (*bqcmp
)(const void *, const void *);
49 static int m_cap(struct MsgBuf
*, struct Client
*, struct Client
*, int, const char **);
51 struct Message cap_msgtab
= {
53 {{m_cap
, 2}, {m_cap
, 2}, mg_ignore
, mg_ignore
, mg_ignore
, {m_cap
, 2}}
56 mapi_clist_av1 cap_clist
[] = { &cap_msgtab
, NULL
};
57 DECLARE_MODULE_AV2(cap
, NULL
, NULL
, cap_clist
, NULL
, NULL
, NULL
, NULL
, NULL
);
59 #define IsCapableEntry(c, e) IsCapable(c, 1 << (e)->value)
60 #define HasCapabilityFlag(c, f) (c->ownerdata != NULL && (((struct ClientCapability *)c->ownerdata)->flags & (f)) == f)
63 clicap_visible(struct Client
*client_p
, const struct CapabilityEntry
*cap
)
65 struct ClientCapability
*clicap
;
67 /* orphaned caps shouldn't be visible */
68 if (cap
->flags
& CAP_ORPHANED
)
71 if (cap
->ownerdata
== NULL
)
74 clicap
= cap
->ownerdata
;
75 if (clicap
->visible
== NULL
)
78 return clicap
->visible(client_p
);
82 * Used iteratively over a buffer, extracts individual cap tokens.
84 * Inputs: buffer to start iterating over (NULL to iterate over existing buf)
85 * int pointer to whether the cap token is negated
86 * int pointer to whether we finish with success
87 * Ouputs: Cap entry if found, NULL otherwise.
89 static struct CapabilityEntry
*
90 clicap_find(const char *data
, int *negate
, int *finished
)
92 static char buf
[BUFSIZE
];
94 struct CapabilityEntry
*cap
;
101 rb_strlcpy(buf
, data
, sizeof(buf
));
108 /* skip any whitespace */
109 while(*p
&& IsSpace(*p
))
123 /* someone sent a '-' without a parameter.. */
128 if((s
= strchr(p
, ' ')))
131 if((cap
= capability_find(cli_capindex
, p
)) != NULL
)
143 * Generates a list of capabilities.
145 * Inputs: client to send to, subcmd to send,
146 * flags to match against: 0 to do none, -1 if client has no flags,
147 * int to whether we are doing CAP CLEAR
151 clicap_generate(struct Client
*source_p
, const char *subcmd
, int flags
, int clear
)
153 char buf
[BUFSIZE
] = { 0 };
154 char capbuf
[BUFSIZE
] = { 0 };
157 struct CapabilityEntry
*entry
;
158 struct DictionaryIter iter
;
160 mlen
= snprintf(buf
, sizeof buf
, ":%s CAP %s %s",
162 EmptyString(source_p
->name
) ? "*" : source_p
->name
,
165 /* shortcut, nothing to do */
168 sendto_one(source_p
, "%s :", buf
);
172 DICTIONARY_FOREACH(entry
, &iter
, cli_capindex
->cap_dict
)
175 struct ClientCapability
*clicap
= entry
->ownerdata
;
176 const char *data
= NULL
;
180 if(!IsCapableEntry(source_p
, entry
))
182 /* they are capable of this, check sticky */
183 else if(clear
&& HasCapabilityFlag(entry
, CLICAP_FLAGS_STICKY
))
187 if (!clicap_visible(source_p
, entry
))
190 caplen
= strlen(entry
->cap
);
191 if (!flags
&& (source_p
->flags
& FLAGS_CLICAP_DATA
) && clicap
!= NULL
&& clicap
->data
!= NULL
)
192 data
= clicap
->data(source_p
);
195 caplen
+= strlen(data
) + 1;
197 /* \r\n\0, possible "-~=", space, " *" */
198 if(buflen
+ mlen
>= BUFSIZE
- 10)
200 /* remove our trailing space -- if buflen == mlen
201 * here, we didnt even succeed in adding one.
203 capbuf
[buflen
] = '\0';
205 sendto_one(source_p
, "%s * :%s", buf
, capbuf
);
208 memset(capbuf
, 0, sizeof capbuf
);
211 buflen
= rb_snprintf_append(capbuf
, sizeof capbuf
, "%s%s%s%s ",
212 clear
? "-" : "", entry
->cap
, data
!= NULL
? "=" : "", data
!= NULL
? data
: "");
215 /* remove trailing space */
216 capbuf
[buflen
] = '\0';
218 sendto_one(source_p
, "%s :%s", buf
, capbuf
);
222 cap_ack(struct Client
*source_p
, const char *arg
)
224 struct CapabilityEntry
*cap
;
225 int capadd
= 0, capdel
= 0;
226 int finished
= 0, negate
;
231 for(cap
= clicap_find(arg
, &negate
, &finished
); cap
;
232 cap
= clicap_find(NULL
, &negate
, &finished
))
234 /* sent an ACK for something they havent REQd */
235 if(!IsCapableEntry(source_p
, cap
))
240 /* dont let them ack something sticky off */
241 if(HasCapabilityFlag(cap
, CLICAP_FLAGS_STICKY
))
244 capdel
|= (1 << cap
->value
);
247 capadd
|= (1 << cap
->value
);
250 source_p
->localClient
->caps
|= capadd
;
251 source_p
->localClient
->caps
&= ~capdel
;
255 cap_clear(struct Client
*source_p
, const char *arg
)
257 clicap_generate(source_p
, "ACK",
258 source_p
->localClient
->caps
? source_p
->localClient
->caps
: -1, 1);
260 source_p
->localClient
->caps
= 0;
264 cap_end(struct Client
*source_p
, const char *arg
)
266 if(IsRegistered(source_p
))
269 source_p
->flags
&= ~FLAGS_CLICAP
;
271 if(source_p
->name
[0] && source_p
->flags
& FLAGS_SENTUSER
)
273 register_local_user(source_p
, source_p
);
278 cap_list(struct Client
*source_p
, const char *arg
)
280 /* list of what theyre currently using */
281 clicap_generate(source_p
, "LIST",
282 source_p
->localClient
->caps
? source_p
->localClient
->caps
: -1, 0);
286 cap_ls(struct Client
*source_p
, const char *arg
)
288 if(!IsRegistered(source_p
))
289 source_p
->flags
|= FLAGS_CLICAP
;
291 if (arg
!= NULL
&& !strcmp(arg
, "302"))
293 source_p
->flags
|= FLAGS_CLICAP_DATA
;
294 source_p
->localClient
->caps
|= CLICAP_CAP_NOTIFY
;
297 /* list of what we support */
298 clicap_generate(source_p
, "LS", 0, 0);
302 cap_req(struct Client
*source_p
, const char *arg
)
305 char pbuf
[2][BUFSIZE
];
306 struct CapabilityEntry
*cap
;
309 int capadd
= 0, capdel
= 0;
310 int finished
= 0, negate
;
312 if(!IsRegistered(source_p
))
313 source_p
->flags
|= FLAGS_CLICAP
;
318 buflen
= snprintf(buf
, sizeof(buf
), ":%s CAP %s ACK",
319 me
.name
, EmptyString(source_p
->name
) ? "*" : source_p
->name
);
324 for(cap
= clicap_find(arg
, &negate
, &finished
); cap
;
325 cap
= clicap_find(NULL
, &negate
, &finished
))
327 size_t namelen
= strlen(cap
->cap
);
329 /* filled the first array, but cant send it in case the
330 * request fails. one REQ should never fill more than two
333 if(buflen
+ plen
+ namelen
+ 6 >= BUFSIZE
)
342 if(HasCapabilityFlag(cap
, CLICAP_FLAGS_STICKY
))
348 strcat(pbuf
[i
], "-");
351 capdel
|= (1 << cap
->value
);
355 if (!clicap_visible(source_p
, cap
))
361 capadd
|= (1 << cap
->value
);
364 /* XXX this probably should exclude REQACK'd caps from capadd/capdel, but keep old behaviour for now */
365 if(HasCapabilityFlag(cap
, CLICAP_FLAGS_REQACK
))
367 strcat(pbuf
[i
], "~");
371 strcat(pbuf
[i
], cap
->cap
);
373 strcat(pbuf
[i
], " ");
374 plen
+= (namelen
+ 1);
380 sendto_one(source_p
, ":%s CAP %s NAK :%s",
381 me
.name
, EmptyString(source_p
->name
) ? "*" : source_p
->name
, arg
);
387 sendto_one(source_p
, "%s * :%s", buf
, pbuf
[0]);
388 sendto_one(source_p
, "%s :%s", buf
, pbuf
[1]);
391 sendto_one(source_p
, "%s :%s", buf
, pbuf
[0]);
393 source_p
->localClient
->caps
|= capadd
;
394 source_p
->localClient
->caps
&= ~capdel
;
397 static struct clicap_cmd
400 void (*func
)(struct Client
*source_p
, const char *arg
);
401 } clicap_cmdlist
[] = {
402 /* This list *MUST* be in alphabetical order */
404 { "CLEAR", cap_clear
},
406 { "LIST", cap_list
},
412 clicap_cmd_search(const char *command
, struct clicap_cmd
*entry
)
414 return irccmp(command
, entry
->cmd
);
418 m_cap(struct MsgBuf
*msgbuf_p
, struct Client
*client_p
, struct Client
*source_p
, int parc
, const char *parv
[])
420 struct clicap_cmd
*cmd
;
422 if(!(cmd
= bsearch(parv
[1], clicap_cmdlist
,
423 sizeof(clicap_cmdlist
) / sizeof(struct clicap_cmd
),
424 sizeof(struct clicap_cmd
), (bqcmp
) clicap_cmd_search
)))
426 sendto_one(source_p
, form_str(ERR_INVALIDCAPCMD
),
427 me
.name
, EmptyString(source_p
->name
) ? "*" : source_p
->name
,
432 (cmd
->func
)(source_p
, parv
[2]);