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