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