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