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