Rework check for number of parameters
[ngircd-alex.git] / src / ngircd / irc-oper.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  * IRC operator commands
17  */
18
19 #include "imp.h"
20 #include <assert.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <signal.h>
25
26 #include "ngircd.h"
27 #include "conn-func.h"
28 #include "conf.h"
29 #include "channel.h"
30 #include "class.h"
31 #include "irc-macros.h"
32 #include "irc-write.h"
33 #include "log.h"
34 #include "match.h"
35 #include "messages.h"
36 #include "parse.h"
37 #include "op.h"
38
39 #include <exp.h>
40 #include "irc-oper.h"
41
42 /**
43  * Handle invalid received OPER command.
44  * Log OPER attempt and send error message to client.
45  */
46 static bool
47 Bad_OperPass(CLIENT *Client, char *errtoken, char *errmsg)
48 {
49         Log(LOG_WARNING, "Got invalid OPER from \"%s\": \"%s\" -- %s",
50             Client_Mask(Client), errtoken, errmsg);
51         return IRC_WriteErrClient(Client, ERR_PASSWDMISMATCH_MSG,
52                                   Client_ID(Client));
53 } /* Bad_OperPass */
54
55 /**
56  * Handler for the IRC "OPER" command.
57  *
58  * @param Client The client from which this command has been received.
59  * @param Req Request structure with prefix and all parameters.
60  * @return CONNECTED or DISCONNECTED.
61  */
62 GLOBAL bool
63 IRC_OPER( CLIENT *Client, REQUEST *Req )
64 {
65         struct Conf_Oper *op;
66         size_t len, i;
67
68         assert( Client != NULL );
69         assert( Req != NULL );
70
71         len = array_length(&Conf_Opers, sizeof(*op));
72         op = array_start(&Conf_Opers);
73         for (i = 0; i < len && strcmp(op[i].name, Req->argv[0]); i++)
74                 ;
75         if (i >= len)
76                 return Bad_OperPass(Client, Req->argv[0], "not configured");
77
78         if (strcmp(op[i].pwd, Req->argv[1]) != 0)
79                 return Bad_OperPass(Client, op[i].name, "bad password");
80
81         if (op[i].mask && (!Match(op[i].mask, Client_Mask(Client))))
82                 return Bad_OperPass(Client, op[i].mask, "hostmask check failed");
83
84         if (!Client_HasMode(Client, 'o')) {
85                 Client_ModeAdd(Client, 'o');
86                 if (!IRC_WriteStrClient(Client, "MODE %s :+o",
87                                         Client_ID(Client)))
88                         return DISCONNECTED;
89                 IRC_WriteStrServersPrefix(NULL, Client, "MODE %s :+o",
90                                           Client_ID(Client));
91         }
92
93         if (!Client_OperByMe(Client))
94                 Log(LOG_NOTICE|LOG_snotice,
95                     "Got valid OPER for \"%s\" from \"%s\", user is an IRC operator now.",
96                     Req->argv[0], Client_Mask(Client));
97
98         Client_SetOperByMe(Client, true);
99         return IRC_WriteStrClient(Client, RPL_YOUREOPER_MSG, Client_ID(Client));
100 } /* IRC_OPER */
101
102 /**
103  * Handler for the IRC "DIE" command.
104  *
105  * @param Client The client from which this command has been received.
106  * @param Req Request structure with prefix and all parameters.
107  * @return CONNECTED or DISCONNECTED.
108  */
109 GLOBAL bool
110 IRC_DIE(CLIENT * Client, REQUEST * Req)
111 {
112         /* Shut down server */
113
114         CONN_ID c;
115         CLIENT *cl;
116
117         assert(Client != NULL);
118         assert(Req != NULL);
119
120         if (!Op_Check(Client, Req))
121                 return Op_NoPrivileges(Client, Req);
122
123         /* Is a message given? */
124         if (Req->argc > 0) {
125                 c = Conn_First();
126                 while (c != NONE) {
127                         cl = Conn_GetClient(c);
128                         if (Client_Type(cl) == CLIENT_USER)
129                                 IRC_WriteStrClient(cl, "NOTICE %s :%s",
130                                                 Client_ID(cl), Req->argv[0]);
131                         c = Conn_Next(c);
132                 }
133         }
134
135         Log(LOG_NOTICE | LOG_snotice, "Got DIE command from \"%s\" ...",
136             Client_Mask(Client));
137         NGIRCd_SignalQuit = true;
138
139         return CONNECTED;
140 } /* IRC_DIE */
141
142 /**
143  * Handler for the IRC "REHASH" command.
144  *
145  * @param Client The client from which this command has been received.
146  * @param Req Request structure with prefix and all parameters.
147  * @return CONNECTED or DISCONNECTED.
148  */
149 GLOBAL bool
150 IRC_REHASH( CLIENT *Client, REQUEST *Req )
151 {
152         /* Reload configuration file */
153
154         assert( Client != NULL );
155         assert( Req != NULL );
156
157         if (!Op_Check(Client, Req))
158                 return Op_NoPrivileges(Client, Req);
159
160         Log(LOG_NOTICE|LOG_snotice, "Got REHASH command from \"%s\" ...",
161             Client_Mask(Client));
162         IRC_WriteStrClient(Client, RPL_REHASHING_MSG, Client_ID(Client));
163
164         raise(SIGHUP);
165
166         return CONNECTED;
167 } /* IRC_REHASH */
168
169 /**
170  * Handler for the IRC "RESTART" command.
171  *
172  * @param Client The client from which this command has been received.
173  * @param Req Request structure with prefix and all parameters.
174  * @return CONNECTED or DISCONNECTED.
175  */
176 GLOBAL bool
177 IRC_RESTART( CLIENT *Client, REQUEST *Req )
178 {
179         /* Restart IRC server (fork a new process) */
180
181         assert( Client != NULL );
182         assert( Req != NULL );
183
184         if (!Op_Check(Client, Req))
185                 return Op_NoPrivileges(Client, Req);
186
187         Log(LOG_NOTICE|LOG_snotice, "Got RESTART command from \"%s\" ...",
188             Client_Mask(Client));
189         NGIRCd_SignalRestart = true;
190
191         return CONNECTED;
192 } /* IRC_RESTART */
193
194 /**
195  * Handler for the IRC "CONNECT" command.
196  *
197  * @param Client The client from which this command has been received.
198  * @param Req Request structure with prefix and all parameters.
199  * @return CONNECTED or DISCONNECTED.
200  */
201 GLOBAL bool
202 IRC_CONNECT(CLIENT * Client, REQUEST * Req)
203 {
204         CLIENT *from, *target;
205
206         assert(Client != NULL);
207         assert(Req != NULL);
208
209         /* Bad number of parameters? */
210         if (Req->argc != 1 && Req->argc != 2 && Req->argc != 3 &&
211             Req->argc != 5 && Req->argc != 6)
212                 return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
213                                           Client_ID(Client), Req->command);
214
215         /* Invalid port number? */
216         if ((Req->argc > 1) && atoi(Req->argv[1]) < 1)
217                 return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
218                                           Client_ID(Client), Req->command);
219
220         if (Client_Type(Client) != CLIENT_SERVER
221             && !Client_HasMode(Client, 'o'))
222                 return Op_NoPrivileges(Client, Req);
223
224         from = Client;
225         target = Client_ThisServer();
226
227         if (Req->argc == 3 || Req->argc == 6) {
228                 /* This CONNECT has a target parameter */
229                 if (Client_Type(Client) == CLIENT_SERVER && Req->prefix)
230                         from = Client_Search(Req->prefix);
231                 if (! from)
232                         return IRC_WriteErrClient(Client, ERR_NOSUCHNICK_MSG,
233                                                   Client_ID(Client), Req->prefix);
234
235                 target = (Req->argc == 3) ? Client_Search(Req->argv[2])
236                                           : Client_Search(Req->argv[5]);
237                 if (! target || Client_Type(target) != CLIENT_SERVER)
238                         return IRC_WriteErrClient(from, ERR_NOSUCHSERVER_MSG,
239                                                   Client_ID(from), Req->argv[0]);
240         }
241
242         if (target != Client_ThisServer()) {
243                 /* Forward CONNECT command ... */
244                 if (Req->argc == 3)
245                         IRC_WriteStrClientPrefix(target, from,
246                                  "CONNECT %s %s :%s", Req->argv[0],
247                                  Req->argv[1], Req->argv[2]);
248                 else
249                         IRC_WriteStrClientPrefix(target, from,
250                                 "CONNECT %s %s %s %s %s :%s", Req->argv[0],
251                                 Req->argv[1], Req->argv[2], Req->argv[3],
252                                 Req->argv[4], Req->argv[5]);
253                 return CONNECTED;
254         }
255
256         if (!Op_Check(from, Req))
257                 return Op_NoPrivileges(Client, Req);
258
259         switch (Req->argc) {
260         case 1:
261                 if (!Conf_EnablePassiveServer(Req->argv[0]))
262                         return IRC_WriteErrClient(from, ERR_NOSUCHSERVER_MSG,
263                                                   Client_ID(from),
264                                                   Req->argv[0]);
265                 break;
266         case 2:
267         case 3:
268                 /* Connect configured server */
269                 if (!Conf_EnableServer
270                     (Req->argv[0], (UINT16) atoi(Req->argv[1])))
271                         return IRC_WriteErrClient(from, ERR_NOSUCHSERVER_MSG,
272                                                   Client_ID(from),
273                                                   Req->argv[0]);
274                 break;
275         default:
276                 /* Add server */
277                 if (!Conf_AddServer
278                     (Req->argv[0], (UINT16) atoi(Req->argv[1]), Req->argv[2],
279                      Req->argv[3], Req->argv[4]))
280                         return IRC_WriteErrClient(from, ERR_NOSUCHSERVER_MSG,
281                                                   Client_ID(from),
282                                                   Req->argv[0]);
283         }
284
285         Log(LOG_NOTICE | LOG_snotice,
286             "Got CONNECT command from \"%s\" for \"%s\".", Client_Mask(from),
287             Req->argv[0]);
288         IRC_SendWallops(Client_ThisServer(), Client_ThisServer(),
289                         "Received CONNECT %s from %s",
290                         Req->argv[0], Client_ID(from));
291
292         return CONNECTED;
293 } /* IRC_CONNECT */
294
295 /**
296  * Handler for the IRC "DISCONNECT" command.
297  *
298  * This command is not specified in the IRC RFCs, it is an extension
299  * of ngIRCd: it shuts down and disables a configured server connection.
300  *
301  * @param Client The client from which this command has been received.
302  * @param Req Request structure with prefix and all parameters.
303  * @return CONNECTED or DISCONNECTED.
304  */
305 GLOBAL bool
306 IRC_DISCONNECT(CLIENT * Client, REQUEST * Req)
307 {
308         CONN_ID my_conn;
309
310         assert(Client != NULL);
311         assert(Req != NULL);
312
313         if (!Op_Check(Client, Req))
314                 return Op_NoPrivileges(Client, Req);
315
316         IRC_SendWallops(Client_ThisServer(), Client_ThisServer(),
317                         "Received DISCONNECT %s from %s",
318                         Req->argv[0], Client_ID(Client));
319
320         Log(LOG_NOTICE | LOG_snotice,
321             "Got DISCONNECT command from \"%s\" for \"%s\".",
322             Client_Mask(Client), Req->argv[0]);
323
324         /* Save ID of this connection */
325         my_conn = Client_Conn(Client);
326
327         /* Disconnect configured server */
328         if (!Conf_DisableServer(Req->argv[0]))
329                 return IRC_WriteErrClient(Client, ERR_NOSUCHSERVER_MSG,
330                                           Client_ID(Client), Req->argv[0]);
331
332         /* Are we still connected or were we killed, too? */
333         if (Conn_GetClient(my_conn))
334                 return CONNECTED;
335         else
336                 return DISCONNECTED;
337 } /* IRC_DISCONNECT */
338
339 /**
340  * Handler for the IRC "WALLOPS" command.
341  *
342  * @param Client The client from which this command has been received.
343  * @param Req Request structure with prefix and all parameters.
344  * @return CONNECTED or DISCONNECTED.
345  */
346 GLOBAL bool
347 IRC_WALLOPS( CLIENT *Client, REQUEST *Req )
348 {
349         CLIENT *from;
350
351         assert( Client != NULL );
352         assert( Req != NULL );
353
354         switch (Client_Type(Client)) {
355         case CLIENT_USER:
356                 if (!Client_OperByMe(Client))
357                         return IRC_WriteErrClient(Client, ERR_NOPRIVILEGES_MSG,
358                                                   Client_ID(Client));
359                 from = Client;
360                 break;
361         case CLIENT_SERVER:
362                 from = Client_Search(Req->prefix);
363                 break;
364         default:
365                 return CONNECTED;
366         }
367
368         if (!from)
369                 return IRC_WriteErrClient(Client, ERR_NOSUCHNICK_MSG,
370                                           Client_ID(Client), Req->prefix);
371
372         IRC_SendWallops(Client, from, "%s", Req->argv[0]);
373         return CONNECTED;
374 } /* IRC_WALLOPS */
375
376 /**
377  * Handle <?>LINE commands (GLINE, KLINE).
378  *
379  * @param Client The client from which this command has been received.
380  * @param Req Request structure with prefix and all parameters.
381  * @return CONNECTED or DISCONNECTED.
382  */
383 GLOBAL bool
384 IRC_xLINE(CLIENT *Client, REQUEST *Req)
385 {
386         CLIENT *from;
387         int class;
388         char class_c;
389
390         assert(Client != NULL);
391         assert(Req != NULL);
392
393         /* Bad number of parameters? */
394         if (Req->argc != 1 && Req->argc != 3)
395                 return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
396                                           Client_ID(Client), Req->command);
397
398         from = Op_Check(Client, Req);
399         if (!from)
400                 return Op_NoPrivileges(Client, Req);
401
402         switch(Req->command[0]) {
403                 case 'g':
404                 case 'G':
405                         class = CLASS_GLINE; class_c = 'G';
406                         break;
407                 case 'k':
408                 case 'K':
409                         class = CLASS_KLINE; class_c = 'K';
410                         break;
411                 default:
412                         Log(LOG_CRIT,
413                             "IRC_xLINE() called for unknown line: %c!? Ignored.",
414                             Req->command[0]);
415                         return CONNECTED;
416         }
417
418         if (Req->argc == 1) {
419                 /* Delete mask from list */
420                 Class_DeleteMask(class, Req->argv[0]);
421                 Log(LOG_NOTICE|LOG_snotice,
422                     "\"%s\" deleted \"%s\" from %c-Line list.",
423                     Client_Mask(from), Req->argv[0], class_c);
424                 if (class == CLASS_GLINE) {
425                         /* Inform other servers */
426                         IRC_WriteStrServersPrefix(Client, from, "%s %s",
427                                                   Req->command, Req->argv[0]);
428
429                 }
430         } else {
431                 /* Add new mask to list */
432                 if (Class_AddMask(class, Req->argv[0],
433                                   time(NULL) + atol(Req->argv[1]),
434                                   Req->argv[2])) {
435                         Log(LOG_NOTICE|LOG_snotice,
436                             "\"%s\" added \"%s\" to %c-Line list: \"%s\" (%ld seconds).",
437                             Client_Mask(from), Req->argv[0], class_c,
438                             Req->argv[2], atol(Req->argv[1]));
439                         if (class == CLASS_GLINE) {
440                                 /* Inform other servers */
441                                 IRC_WriteStrServersPrefix(Client, from,
442                                                 "%s %s %s :%s", Req->command,
443                                                 Req->argv[0], Req->argv[1],
444                                                 Req->argv[2]);
445                         }
446                 }
447         }
448
449         return CONNECTED;
450 }
451
452 /* -eof- */