]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/irc-cap.c
irc-cap.c: Update source code documentation
[ngircd-alex.git] / src / ngircd / irc-cap.c
1 /*
2  * ngIRCd -- The Next Generation IRC Daemon
3  * Copyright (c)2001-2013 Alexander Barton (alex@barton.de) and Contributors.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * Please read the file COPYING, README and AUTHORS for more information.
10  */
11
12 #include "portab.h"
13
14 /**
15  * @file
16  * Handler for IRC capability ("CAP") commands
17  */
18
19 #include "imp.h"
20 #include <assert.h>
21 #include <string.h>
22
23 #include "defines.h"
24 #include "conn.h"
25 #include "channel.h"
26 #include "client-cap.h"
27 #include "irc-write.h"
28 #include "log.h"
29 #include "login.h"
30 #include "messages.h"
31 #include "parse.h"
32
33 #include "exp.h"
34 #include "irc-cap.h"
35
36 /* Local functions */
37
38 /**
39  * Set CAP negotiation status and mark client as "supports capabilities".
40  *
41  * @param Client The client to handle.
42  */
43 static void
44 Set_CAP_Negotiation(CLIENT *Client)
45 {
46         assert(Client != NULL);
47
48         if (Client_Type(Client) != CLIENT_USER)
49                 Client_CapAdd(Client, CLIENT_CAP_PENDING);
50         Client_CapAdd(Client, CLIENT_CAP_SUPPORTED);
51 }
52
53 /**
54  * Parse capability string and return numeric flag value.
55  *
56  * @param Args The string containing space-separated capability names.
57  * @return Changed capability flags or 0 on error.
58  */
59 static int
60 Parse_CAP(int Capabilities, char *Args)
61 {
62         static char tmp[COMMAND_LEN];
63         char *ptr;
64
65         assert(Args != NULL);
66
67         strlcpy(tmp, Args, sizeof(tmp));
68
69         ptr = strtok(tmp, " ");
70         while (ptr) {
71                 if (*ptr == '-') {
72                         /* drop capabilities */
73                         ptr++;
74                         if (strcmp(ptr, "multi-prefix") == 0)
75                                 Capabilities &= ~CLIENT_CAP_MULTI_PREFIX;
76                         else
77                                 return -1;
78                 } else {
79                         /* request capabilities */
80                         if (strcmp(ptr, "multi-prefix") == 0)
81                                 Capabilities |= CLIENT_CAP_MULTI_PREFIX;
82                         else
83                                 return -1;
84                 }
85                 ptr = strtok(NULL, " ");
86         }
87
88         return Capabilities;
89 }
90
91 /**
92  * Return textual representation of capability flags.
93  *
94  * Please note: this function returns a pointer to a global buffer and
95  * therefore isn't thread safe!
96  *
97  * @param Capabilities Capability flags (bitmask).
98  * @return Pointer to textual representation.
99  */
100 static char *
101 Get_CAP_String(int Capabilities)
102 {
103         static char txt[COMMAND_LEN];
104
105         txt[0] = '\0';
106
107         if (Capabilities & CLIENT_CAP_MULTI_PREFIX)
108                 strlcat(txt, "multi-prefix ", sizeof(txt));
109
110         return txt;
111 }
112
113 /**
114  * Handler for the IRCv3 sub-command "CAP LS".
115  *
116  * @param Client The client from which this command has been received.
117  * @param Arg Command argument or NULL.
118  * @return CONNECTED or DISCONNECTED.
119  */
120 static bool
121 Handle_CAP_LS(CLIENT *Client, UNUSED char *Arg)
122 {
123         assert(Client != NULL);
124
125         Set_CAP_Negotiation(Client);
126
127         return IRC_WriteStrClient(Client,
128                                   "CAP %s LS :multi-prefix",
129                                   Client_ID(Client));
130 }
131
132 /**
133  * Handler for the IRCv3 sub-command "CAP LIST".
134  *
135  * @param Client The client from which this command has been received.
136  * @param Arg Command argument or NULL.
137  * @return CONNECTED or DISCONNECTED.
138  */
139 static bool
140 Handle_CAP_LIST(CLIENT *Client, UNUSED char *Arg)
141 {
142         assert(Client != NULL);
143
144         return IRC_WriteStrClient(Client, "CAP %s LIST :%s", Client_ID(Client),
145                                   Get_CAP_String(Client_Cap(Client)));
146 }
147
148 /**
149  * Handler for the IRCv3 sub-command "CAP REQ".
150  *
151  * @param Client The client from which this command has been received.
152  * @param Arg Command argument.
153  * @return CONNECTED or DISCONNECTED.
154  */
155 static bool
156 Handle_CAP_REQ(CLIENT *Client, char *Arg)
157 {
158         int new_cap;
159
160         assert(Client != NULL);
161         assert(Arg != NULL);
162
163         Set_CAP_Negotiation(Client);
164
165         new_cap = Parse_CAP(Client_Cap(Client), Arg);
166
167         if (new_cap < 0)
168                 return IRC_WriteStrClient(Client, "CAP %s NAK :%s",
169                                           Client_ID(Client), Arg);
170
171         Client_CapSet(Client, new_cap);
172         return IRC_WriteStrClient(Client, "CAP %s ACK :%s",
173                                   Client_ID(Client), Arg);
174 }
175
176 /**
177  * Handler for the IRCv3 sub-command "CAP ACK".
178  *
179  * @param Client The client from which this command has been received.
180  * @param Arg Command argument.
181  * @return CONNECTED or DISCONNECTED.
182  */
183 static bool
184 Handle_CAP_ACK(UNUSED CLIENT *Client, UNUSED char *Arg)
185 {
186         assert(Client != NULL);
187         assert(Arg != NULL);
188
189         return CONNECTED;
190 }
191
192 /**
193  * Handler for the IRCv3 sub-command "CAP CLEAR".
194  *
195  * @param Client The client from which this command has been received.
196  * @return CONNECTED or DISCONNECTED.
197  */
198 static bool
199 Handle_CAP_CLEAR(CLIENT *Client)
200 {
201         int cap_old;
202
203         assert(Client != NULL);
204
205         cap_old = Client_Cap(Client);
206         if (cap_old & CLIENT_CAP_MULTI_PREFIX)
207                 Client_CapDel(Client, CLIENT_CAP_MULTI_PREFIX);
208
209         return IRC_WriteStrClient(Client, "CAP %s ACK :%s", Client_ID(Client),
210                                   Get_CAP_String(cap_old));
211 }
212
213 /**
214  * Handler for the IRCv3 sub-command "CAP END".
215  *
216  * @param Client The client from which this command has been received.
217  * @return CONNECTED or DISCONNECTED.
218  */
219 static bool
220 Handle_CAP_END(CLIENT *Client)
221 {
222         assert(Client != NULL);
223
224         if (Client_Type(Client) != CLIENT_USER) {
225                 /* User is still logging in ... */
226                 Client_CapDel(Client, CLIENT_CAP_PENDING);
227
228                 if (Client_Type(Client) == CLIENT_WAITCAPEND) {
229                         /* Only "CAP END" was missing: log in! */
230                         return Login_User(Client);
231                 }
232         }
233
234         return CONNECTED;
235 }
236
237 /* Global functions */
238
239 /**
240  * Handler for the IRCv3 command "CAP".
241  *
242  * @param Client The client from which this command has been received.
243  * @param Req Request structure with prefix and all parameters.
244  * @return CONNECTED or DISCONNECTED.
245  */
246 GLOBAL bool
247 IRC_CAP(CLIENT *Client, REQUEST *Req)
248 {
249         assert(Client != NULL);
250         assert(Req != NULL);
251
252         /* Bad number of prameters? */
253         if (Req->argc < 1 || Req->argc > 2)
254                 return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
255                                           Client_ID(Client), Req->command);
256
257         LogDebug("Got \"%s %s\" command from \"%s\" ...",
258                  Req->command, Req->argv[0], Client_ID(Client));
259
260         if (Req->argc == 1) {
261                 if (strcasecmp(Req->argv[0], "CLEAR") == 0)
262                         return Handle_CAP_CLEAR(Client);
263                 if (strcasecmp(Req->argv[0], "END") == 0)
264                         return Handle_CAP_END(Client);
265         }
266         if (Req->argc >= 1 && Req->argc <= 2) {
267                 if (strcasecmp(Req->argv[0], "LS") == 0)
268                         return Handle_CAP_LS(Client, Req->argv[1]);
269                 if (strcasecmp(Req->argv[0], "LIST") == 0)
270                         return Handle_CAP_LIST(Client, Req->argv[1]);
271         }
272         if (Req->argc == 2) {
273                 if (strcasecmp(Req->argv[0], "REQ") == 0)
274                         return Handle_CAP_REQ(Client, Req->argv[1]);
275                 if (strcasecmp(Req->argv[0], "ACK") == 0)
276                         return Handle_CAP_ACK(Client, Req->argv[1]);
277         }
278
279         return IRC_WriteStrClient(Client, ERR_INVALIDCAP_MSG,
280                                   Client_ID(Client), Req->argv[0]);
281 }
282
283 /* -eof- */