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