Author: Kev <klmitch@mit.edu>
[ircu2.10.12-pk.git] / ircd / m_gline.c
1 /*
2  * IRC - Internet Relay Chat, ircd/m_gline.c
3  * Copyright (C) 1990 Jarkko Oikarinen and
4  *                    University of Oulu, Computing Center
5  *
6  * See file AUTHORS in IRC package for additional names of
7  * the programmers.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 1, or (at your option)
12  * any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  *
23  * $Id$
24  */
25
26 /*
27  * m_functions execute protocol messages on this server:
28  *
29  *    cptr    is always NON-NULL, pointing to a *LOCAL* client
30  *            structure (with an open socket connected!). This
31  *            identifies the physical socket where the message
32  *            originated (or which caused the m_function to be
33  *            executed--some m_functions may call others...).
34  *
35  *    sptr    is the source of the message, defined by the
36  *            prefix part of the message if present. If not
37  *            or prefix not found, then sptr==cptr.
38  *
39  *            (!IsServer(cptr)) => (cptr == sptr), because
40  *            prefixes are taken *only* from servers...
41  *
42  *            (IsServer(cptr))
43  *                    (sptr == cptr) => the message didn't
44  *                    have the prefix.
45  *
46  *                    (sptr != cptr && IsServer(sptr) means
47  *                    the prefix specified servername. (?)
48  *
49  *                    (sptr != cptr && !IsServer(sptr) means
50  *                    that message originated from a remote
51  *                    user (not local).
52  *
53  *            combining
54  *
55  *            (!IsServer(sptr)) means that, sptr can safely
56  *            taken as defining the target structure of the
57  *            message in this server.
58  *
59  *    *Always* true (if 'parse' and others are working correct):
60  *
61  *    1)      sptr->from == cptr  (note: cptr->from == cptr)
62  *
63  *    2)      MyConnect(sptr) <=> sptr == cptr (e.g. sptr
64  *            *cannot* be a local connection, unless it's
65  *            actually cptr!). [MyConnect(x) should probably
66  *            be defined as (x == x->from) --msa ]
67  *
68  *    parc    number of variable parameter strings (if zero,
69  *            parv is allowed to be NULL)
70  *
71  *    parv    a NULL terminated list of parameter pointers,
72  *
73  *                    parv[0], sender (prefix string), if not present
74  *                            this points to an empty string.
75  *                    parv[1]...parv[parc-1]
76  *                            pointers to additional parameters
77  *                    parv[parc] == NULL, *always*
78  *
79  *            note:   it is guaranteed that parv[0]..parv[parc-1] are all
80  *                    non-NULL pointers.
81  */
82 #include "config.h"
83
84 #include "client.h"
85 #include "gline.h"
86 #include "hash.h"
87 #include "ircd.h"
88 #include "ircd_features.h"
89 #include "ircd_log.h"
90 #include "ircd_reply.h"
91 #include "ircd_string.h"
92 #include "match.h"
93 #include "msg.h"
94 #include "numeric.h"
95 #include "numnicks.h"
96 #include "s_conf.h"
97 #include "s_misc.h"
98 #include "send.h"
99
100 /* #include <assert.h> -- Now using assert in ircd_log.h */
101 #include <stdlib.h>
102 #include <string.h>
103
104 /*
105  * ms_gline - server message handler
106  *
107  * parv[0] = Sender prefix
108  * parv[1] = Target: server numeric
109  * parv[2] = (+|-)<G-line mask>
110  * parv[3] = G-line lifetime
111  *
112  * From Uworld:
113  *
114  * parv[4] = Comment
115  *
116  * From somewhere else:
117  *
118  * parv[4] = Last modification time
119  * parv[5] = Comment
120  *
121  */
122 int
123 ms_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
124 {
125   struct Client *acptr = 0;
126   struct Gline *agline = 0;
127   unsigned int flags = 0;
128   enum GlineAction action = GLINE_MODIFY;
129   time_t expire_off = 0, lastmod = 0, lifetime = 0;
130   char *mask = parv[2], *target = parv[1], *reason = "No reason", *tmp = 0;
131
132   if (parc < 3)
133     return need_more_params(sptr, "GLINE");
134
135   if (*mask == '!') {
136     mask++;
137     flags |= GLINE_OPERFORCE; /* assume oper had WIDE_GLINE */
138   } else if (IsServer(sptr))
139     flags |= GLINE_FORCE;
140
141   switch (*mask) { /* handle +, -, <, and > */
142   case '+': /* activate the G-line */
143     action = GLINE_ACTIVATE;
144     mask++;
145     break;
146
147   case '-': /* deactivate the G-line */
148     action = GLINE_DEACTIVATE;
149     mask++;
150     break;
151
152   case '>': /* locally activate the G-line */
153     action = GLINE_LOCAL_ACTIVATE;
154     mask++;
155     break;
156
157   case '<': /* locally deactivate the G-line */
158     action = GLINE_LOCAL_DEACTIVATE;
159     mask++;
160     break;
161   }
162
163   /* Now, let's figure out if it's a local or global G-line */
164   if (action == GLINE_LOCAL_ACTIVATE || action == GLINE_LOCAL_DEACTIVATE ||
165       (target[0] == '*' && target[1] == '\0'))
166     flags |= GLINE_GLOBAL;
167   else {
168     if (!(acptr = FindNServer(target)))
169       return 0; /* no such server, jump out */
170
171     flags |= GLINE_LOCAL;
172   }
173
174   /* Next, try to find the G-line... */
175   if ((flags & GLINE_GLOBAL) || IsMe(acptr)) /* don't bother if it's not me! */
176     agline = gline_find(mask, flags | GLINE_ANY | GLINE_EXACT);
177
178   /* We now have all the pieces to tell us what we've got; let's
179    * put it all together and convert the rest of the arguments.
180    */
181
182   /* Handle the local G-lines first... */
183   if (flags & GLINE_LOCAL) {
184     assert(acptr);
185
186     /* normalize the action, first */
187     if (action == GLINE_LOCAL_ACTIVATE || action == GLINE_MODIFY)
188       action = GLINE_ACTIVATE;
189     else if (action == GLINE_LOCAL_DEACTIVATE)
190       action = GLINE_DEACTIVATE;
191
192     if (action == GLINE_ACTIVATE) { /* get expiration and reason */
193       if (parc < 5) /* check parameter count... */
194         return need_more_params(sptr, "GLINE");
195
196       expire_off = atoi(parv[3]); /* get expiration... */
197       reason = parv[parc - 1]; /* and reason */
198
199       if (IsMe(acptr)) {
200         /* XXX and create the local G-line */
201         sendwallto_group_butone(&me, WALL_DESYNCH, NULL,
202                                 "I would create a local G-line here; target "
203                                 "%s, mask %s, operforce %s, action %s, "
204                                 "expire %Tu, reason: %s", target, mask,
205                                 flags & GLINE_OPERFORCE ? "YES" : "NO",
206                                 action == GLINE_ACTIVATE ? "+" : "-",
207                                 expire_off, reason);
208       }
209     } else if (IsMe(acptr)) { /* destroying a local G-line */
210       /* XXX destroy the G-line */;
211       sendwallto_group_butone(&me, WALL_DESYNCH, NULL,
212                               "I would destroy a local G-line here; target "
213                               "%s, mask %s, operforce %s, action %s", target,
214                               mask, flags & GLINE_OPERFORCE ? "YES" : "NO",
215                               action == GLINE_ACTIVATE ? "+" : "-");
216     }
217
218     /* OK, we've converted arguments; if it's not for us, forward */
219     /* UPDATE NOTE: Once all servers are updated to u2.10.12.11, the
220      * format string in this sendcmdto_one() may be updated to omit
221      * <lastmod> for GLINE_ACTIVATE and to omit <expire>, <lastmod>,
222      * and <reason> for GLINE_DEACTIVATE.
223      */
224     if (!IsMe(acptr)) {
225       sendwallto_group_butone(&me, WALL_DESYNCH, NULL,
226                               "I am forwarding a local G-line to a remote "
227                               "server; target %s, mask %s, operforce %s, "
228                               "action %s, expire %Tu, lastmod %Tu, reason: %s",
229                               target, mask,
230                               flags & GLINE_OPERFORCE ? "YES" : "NO",
231                               action == GLINE_ACTIVATE ? "+" :  "-",
232                               expire_off, CurrentTime, reason);
233       sendcmdto_one(sptr, CMD_GLINE, acptr, "%C %s%c%s %Tu %Tu :%s",
234                     acptr, flags & GLINE_OPERFORCE ? "!" : "",
235                     action == GLINE_ACTIVATE ? '+' : '-', mask, expire_off,
236                     CurrentTime, reason);
237     }
238
239     return 0; /* all done */
240   }
241
242   /* can't modify a G-line that doesn't exist, so remap to activate */
243   if (!agline && action == GLINE_MODIFY)
244     action = GLINE_ACTIVATE;
245
246   /* OK, let's figure out what other parameters we may have... */
247   switch (action) {
248   case GLINE_LOCAL_ACTIVATE: /* locally activating a G-line */
249   case GLINE_LOCAL_DEACTIVATE: /* locally deactivating a G-line */
250     break; /* no additional parameters to manipulate */
251
252   case GLINE_ACTIVATE: /* activating a G-line */
253   case GLINE_DEACTIVATE: /* deactivating a G-line */
254     /* in either of these cases, we have at least a lastmod parameter */
255     if (parc < 4)
256       return need_more_params(sptr, "GLINE");
257     else if (parc == 4) /* lastmod only form... */
258       lastmod = atoi(parv[3]);
259     /*FALLTHROUGH*/
260   case GLINE_MODIFY: /* modifying a G-line */
261     /* convert expire and lastmod, look for lifetime and reason */
262     if (parc > 4) { /* protect against fall-through from 4-param form */
263       if (parc < 5)
264         return need_more_params(sptr, "GLINE");
265
266       expire_off = atoi(parv[3]); /* convert expiration and lastmod */
267       lastmod = atoi(parv[4]);
268
269       if (parc > 6) { /* no question, have a lifetime and reason */
270         lifetime = atoi(parv[5]);
271         reason = parv[parc - 1];
272       } else if (parc == 6) { /* either a lifetime or a reason */
273         if (!agline || /* gline creation, has to be the reason */
274             /* trial-convert as lifetime, and if it doesn't fully convert,
275              * it must be the reason */
276             ((lifetime = strtoul(parv[5], &tmp, 10)) && !*tmp)) {
277           lifetime = 0;
278           reason = parv[5];
279         }
280       }
281     }
282   }
283
284   sendwallto_group_butone(&me, WALL_DESYNCH, NULL,
285                           "I have a global G-line I would act upon now; "
286                           "target %s, mask %s, operforce %s, action %s, "
287                           "expire %Tu, lastmod %Tu, lifetime %Tu, "
288                           "reason: %s; gline %s!",
289                           target, mask, flags & GLINE_OPERFORCE ? "YES" : "NO",
290                           action == GLINE_ACTIVATE ? "+" :
291                           (action == GLINE_DEACTIVATE ? "-" :
292                            (action == GLINE_LOCAL_ACTIVATE ? ">" :
293                             (action == GLINE_LOCAL_DEACTIVATE ? "<" :
294                              "(MODIFY)"))), expire_off, lastmod, lifetime,
295                           reason, agline ? "EXISTS" : "does not exist");
296
297   /* OK, at this point, we have converted all available parameters.
298    * Let's actually do the action!
299    */
300   if (agline)
301     /* XXX modify the G-line */;
302
303   /* XXX create the G-line */return 0;
304
305
306
307
308
309 /*   if ((parc == 3 && *mask == '-') || parc == 5) */
310 /*   { */
311 /*     if (!find_conf_byhost(cli_confs(cptr), cli_name(sptr), CONF_UWORLD)) */
312 /*       return need_more_params(sptr, "GLINE"); */
313
314 /*     flags |= GLINE_FORCE; */
315 /*   } */
316 /*   else if (parc > 5) */
317 /*     lastmod = atoi(parv[4]); */
318 /*   else */
319 /*     return need_more_params(sptr, "GLINE"); */
320
321 /*   if (parc > 4) */
322 /*     reason = parv[parc - 1]; */
323
324 /*   if (IsServer(sptr)) */
325 /*     flags |= GLINE_FORCE; */
326
327 /*   if (!(target[0] == '*' && target[1] == '\0')) { */
328 /*     if (!(acptr = FindNServer(target))) */
329 /*       return 0; /\* no such server *\/ */
330
331 /*     if (!IsMe(acptr)) { /\* manually propagate *\/ */
332 /*       if (!lastmod) */
333 /*      sendcmdto_one(sptr, CMD_GLINE, acptr, */
334 /*                    (parc == 3) ? "%C %s" : "%C %s %s :%s", acptr, mask, */
335 /*                    parv[3], reason); */
336 /*       else */
337 /*      sendcmdto_one(sptr, CMD_GLINE, acptr, "%C %s%s %s %s :%s", acptr, */
338 /*                    flags & GLINE_OPERFORCE ? "!" : "", mask, parv[3], */
339 /*                    parv[4], reason); */
340
341 /*       return 0; */
342 /*     } */
343
344 /*     flags |= GLINE_LOCAL; */
345 /*   } */
346
347 /*   if (*mask == '-') */
348 /*     mask++; */
349 /*   else if (*mask == '+') { */
350 /*     flags |= GLINE_ACTIVE; */
351 /*     mask++; */
352 /*   } else */
353 /*     flags |= GLINE_ACTIVE; */
354
355 /*   expire_off = parc < 5 ? 0 : atoi(parv[3]); */
356
357 /*   agline = gline_find(mask, GLINE_ANY | GLINE_EXACT); */
358
359 /*   if (agline) { */
360 /*     if (GlineIsLocal(agline) && !(flags & GLINE_LOCAL)) /\* global over local *\/ */
361 /*       gline_free(agline); */
362 /*     else if (!lastmod && ((flags & GLINE_ACTIVE) == GlineIsRemActive(agline))) */
363 /*       return gline_propagate(cptr, sptr, agline); */
364 /*     else if (!lastmod || GlineLastMod(agline) < lastmod) { /\* new mod *\/ */
365 /*       if (flags & GLINE_ACTIVE) */
366 /*      return gline_activate(cptr, sptr, agline, lastmod, flags); */
367 /*       else */
368 /*      return gline_deactivate(cptr, sptr, agline, lastmod, flags); */
369 /*     } else if (GlineLastMod(agline) == lastmod || IsBurstOrBurstAck(cptr)) */
370 /*       return 0; */
371 /*     else */
372 /*       return gline_resend(cptr, agline); /\* other server desynched WRT gline *\/ */
373 /*   } else if (parc == 3 && !(flags & GLINE_ACTIVE)) { */
374 /*     /\* U-lined server removing a G-line we don't have; propagate the removal */
375 /*      * anyway. */
376 /*      *\/ */
377 /*     if (!(flags & GLINE_LOCAL)) */
378 /*       sendcmdto_serv_butone(sptr, CMD_GLINE, cptr, "* -%s", mask); */
379 /*     return 0; */
380 /*   } else if (parc < 5) */
381 /*     return need_more_params(sptr, "GLINE"); */
382
383 /*   return gline_add(cptr, sptr, mask, reason, expire_off, lastmod, flags); */
384 }
385
386 /*
387  * mo_gline - oper message handler
388  *
389  * parv[0] = Sender prefix
390  * parv[1] = [[+|-]<G-line mask>]
391  *
392  * Local (to me) style:
393  *
394  * parv[2] = [Expiration offset]
395  * parv[3] = [Comment]
396  *
397  * Global (or remote local) style:
398  *
399  * parv[2] = [target]
400  * parv[3] = [Expiration offset]
401  * parv[4] = [Comment]
402  *
403  */
404 int
405 mo_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
406 {
407   struct Client *acptr = 0;
408   struct Gline *agline;
409   unsigned int flags = 0;
410   time_t expire_off;
411   char *mask = parv[1], *target = 0, *reason;
412
413   if (parc < 2)
414     return gline_list(sptr, 0);
415
416   if (*mask == '!') {
417     mask++;
418
419     if (HasPriv(sptr, PRIV_WIDE_GLINE))
420       flags |= GLINE_OPERFORCE;
421   }
422
423   if (*mask == '+') {
424     flags |= GLINE_ACTIVE;
425     mask++;
426
427   } else if (*mask == '-')
428     mask++;
429   else
430     return gline_list(sptr, mask);
431
432   if (parc == 4) {
433     expire_off = atoi(parv[2]);
434     reason = parv[3];
435     flags |= GLINE_LOCAL;
436   } else if (parc > 4) {
437     target = parv[2];
438     expire_off = atoi(parv[3]);
439     reason = parv[4];
440   } else
441     return need_more_params(sptr, "GLINE");
442
443   if (target)
444   {
445     if (!(target[0] == '*' && target[1] == '\0'))
446     {
447       if (!(acptr = find_match_server(target)))
448         return send_reply(sptr, ERR_NOSUCHSERVER, target);
449
450       /* manually propagate, since we don't set it */
451       if (!IsMe(acptr))
452       {
453         if (!feature_bool(FEAT_CONFIG_OPERCMDS))
454           return send_reply(sptr, ERR_DISABLED, "GLINE");
455
456         if (!HasPriv(sptr, PRIV_GLINE))
457           return send_reply(sptr, ERR_NOPRIVILEGES);
458
459         sendcmdto_one(sptr, CMD_GLINE, acptr, "%C %s%c%s %s %Tu :%s", acptr,
460                       flags & GLINE_OPERFORCE ? "!" : "",
461                       flags & GLINE_ACTIVE ? '+' : '-', mask, parv[3],
462                       TStime(), reason);
463         return 0;
464       }
465       flags |= GLINE_LOCAL;
466     }
467   }
468
469   if (!(flags & GLINE_LOCAL) && !feature_bool(FEAT_CONFIG_OPERCMDS))
470     return send_reply(sptr, ERR_DISABLED, "GLINE");
471
472   if (!HasPriv(sptr, (flags & GLINE_LOCAL ? PRIV_LOCAL_GLINE : PRIV_GLINE)))
473     return send_reply(sptr, ERR_NOPRIVILEGES);
474
475   agline = gline_find(mask, GLINE_ANY | GLINE_EXACT);
476
477   if (agline) {
478     if (GlineIsLocal(agline) && !(flags & GLINE_LOCAL)) /* global over local */
479       gline_free(agline);
480     else {
481       if (!GlineLastMod(agline)) /* force mods to Uworld-set G-lines local */
482         flags |= GLINE_LOCAL;
483
484       if (flags & GLINE_ACTIVE)
485         return gline_activate(cptr, sptr, agline,
486                               GlineLastMod(agline) ? TStime() : 0, flags);
487       else
488         return gline_deactivate(cptr, sptr, agline,
489                                 GlineLastMod(agline) ? TStime() : 0, flags);
490     }
491   }
492
493   return gline_add(cptr, sptr, mask, reason, expire_off, TStime(), flags);
494 }
495
496 /*
497  * m_gline - user message handler
498  *
499  * parv[0] = Sender prefix
500  * parv[1] = [<server name>]
501  *
502  */
503 int
504 m_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
505 {
506   if (parc < 2)
507     return send_reply(sptr, ERR_NOSUCHGLINE, "");
508
509   return gline_list(sptr, parv[1]);
510 }