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