added gnutls backend and moved backend code into new files
[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_debug.h"
98 #include "s_misc.h"
99 #include "send.h"
100
101 /* #include <assert.h> -- Now using assert in ircd_log.h */
102 #include <stdlib.h>
103 #include <string.h>
104
105 #define PASTWATCH       157680000       /* number of seconds in 5 years */
106
107 /*
108  * If the expiration value, interpreted as an absolute timestamp, is
109  * more recent than 5 years in the past, we interpret it as an
110  * absolute timestamp; otherwise, we assume it's relative and convert
111  * it to an absolute timestamp.  Either way, the output of this macro
112  * is an absolute timestamp--not guaranteed to be a *valid* timestamp,
113  * but you can't have everything in a macro ;)
114  */
115 #define abs_expire(exp)                                                 \
116   ((exp) >= TStime() - PASTWATCH ? (exp) : (exp) + TStime())
117
118 /*
119  * ms_gline - server message handler
120  *
121  * parv[0] = Sender prefix
122  * parv[1] = Target: server numeric
123  * parv[2] = (+|-)<G-line mask>
124  *
125  * For other parameters, see doc/readme.gline.
126  */
127 int
128 ms_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
129 {
130   struct Client *acptr = 0;
131   struct Gline *agline = 0;
132   unsigned int flags = 0;
133   enum GlineAction action = GLINE_MODIFY;
134   time_t expire = 0, lastmod = 0, lifetime = 0;
135   char *mask = parv[2], *target = parv[1], *reason = "No reason", *tmp = 0;
136
137   if (parc < 3)
138     return need_more_params(sptr, "GLINE");
139
140   if (IsServer(sptr))
141     flags |= GLINE_FORCE;
142
143   if (*mask == '!') {
144     mask++;
145     flags |= GLINE_OPERFORCE; /* assume oper had WIDE_GLINE */
146   }
147
148   switch (*mask) { /* handle +, -, <, and > */
149   case '+': /* activate the G-line */
150     action = GLINE_ACTIVATE;
151     mask++;
152     break;
153
154   case '-': /* deactivate the G-line */
155     action = GLINE_DEACTIVATE;
156     mask++;
157     break;
158
159   case '>': /* locally activate the G-line */
160     action = GLINE_LOCAL_ACTIVATE;
161     mask++;
162     break;
163
164   case '<': /* locally deactivate the G-line */
165     action = GLINE_LOCAL_DEACTIVATE;
166     mask++;
167     break;
168   }
169
170   /* Is there no mask left? */
171   if (mask[0] == '\0')
172     return need_more_params(sptr, "GLINE");
173
174   /* Now, let's figure out if it's a local or global G-line */
175   if (action == GLINE_LOCAL_ACTIVATE || action == GLINE_LOCAL_DEACTIVATE ||
176       (target[0] == '*' && target[1] == '\0'))
177     flags |= GLINE_GLOBAL;
178   else
179     flags |= GLINE_LOCAL;
180
181   /* now figure out if we need to resolve a server */
182   if ((action == GLINE_LOCAL_ACTIVATE || action == GLINE_LOCAL_DEACTIVATE ||
183        (flags & GLINE_LOCAL)) && !(acptr = FindNServer(target)))
184     return 0; /* no such server, jump out */
185
186   /* If it's a local activate/deactivate and server isn't me, propagate it */
187   if ((action == GLINE_LOCAL_ACTIVATE || action == GLINE_LOCAL_DEACTIVATE) &&
188       !IsMe(acptr)) {
189     Debug((DEBUG_DEBUG, "I am forwarding a local change to a global gline "
190            "to a remote server; target %s, mask %s, operforce %s, action %c",
191            target, mask, flags & GLINE_OPERFORCE ? "YES" : "NO",
192            action == GLINE_LOCAL_ACTIVATE ? '>' : '<'));
193
194     sendcmdto_one(sptr, CMD_GLINE, acptr, "%C %s%c%s", acptr,
195                   flags & GLINE_OPERFORCE ? "!" : "",
196                   action == GLINE_LOCAL_ACTIVATE ? '>' : '<', mask);
197
198     return 0; /* all done */
199   }
200
201   /* Next, try to find the G-line... */
202   if ((flags & GLINE_GLOBAL) || IsMe(acptr)) /* don't bother if it's not me! */
203     agline = gline_find(mask, flags | GLINE_ANY | GLINE_EXACT);
204
205   /* We now have all the pieces to tell us what we've got; let's put
206    * it all together and convert the rest of the arguments.
207    */
208
209   /* Handle the local G-lines first... */
210   if (flags & GLINE_LOCAL) {
211     assert(acptr);
212
213     /* normalize the action, first */
214     if (action == GLINE_LOCAL_ACTIVATE || action == GLINE_MODIFY)
215       action = GLINE_ACTIVATE;
216     else if (action == GLINE_LOCAL_DEACTIVATE)
217       action = GLINE_DEACTIVATE;
218
219     if (action == GLINE_ACTIVATE) { /* get expiration and reason */
220       if (parc < 5) /* check parameter count... */
221         return need_more_params(sptr, "GLINE");
222
223       expire = atoi(parv[3]); /* get expiration... */
224       expire = abs_expire(expire); /* convert to absolute... */
225       reason = parv[parc - 1]; /* and reason */
226
227       if (IsMe(acptr)) {
228         if (agline) /* G-line already exists, so let's ignore it... */
229           return 0;
230
231         /* OK, create the local G-line */
232         Debug((DEBUG_DEBUG, "I am creating a local G-line here; target %s, "
233                "mask %s, operforce %s, action %s, expire %Tu, reason: %s",
234                target, mask, flags & GLINE_OPERFORCE ? "YES" : "NO",
235                action == GLINE_ACTIVATE ? "+" : "-", expire, reason));
236
237         return gline_add(cptr, sptr, mask, reason, expire, lastmod,
238                          lifetime, flags | GLINE_ACTIVE);
239       }
240     } else if (IsMe(acptr)) { /* destroying a local G-line */
241       if (!agline) /* G-line doesn't exist, so let's complain... */
242         return send_reply(sptr, ERR_NOSUCHGLINE, mask);
243
244       /* Let's now destroy the G-line */;
245       Debug((DEBUG_DEBUG, "I am destroying a local G-line here; target %s, "
246              "mask %s, operforce %s, action %s", target, mask,
247              flags & GLINE_OPERFORCE ? "YES" : "NO",
248              action == GLINE_ACTIVATE ? "+" : "-"));
249
250       return gline_destroy(cptr, sptr, agline);
251     }
252
253     /* OK, we've converted arguments; if it's not for us, forward */
254     /* UPDATE NOTE: Once all servers are updated to u2.10.12.11, the
255      * format string in this sendcmdto_one() may be updated to omit
256      * <lastmod> for GLINE_ACTIVATE and to omit <expire>, <lastmod>,
257      * and <reason> for GLINE_DEACTIVATE.
258      */
259     assert(!IsMe(acptr));
260
261     Debug((DEBUG_DEBUG, "I am forwarding a local G-line to a remote server; "
262            "target %s, mask %s, operforce %s, action %c, expire %Tu, "
263            "lastmod %Tu, reason: %s", target, mask,
264            flags & GLINE_OPERFORCE ? "YES" : "NO",
265            action == GLINE_ACTIVATE ? '+' :  '-', expire, TStime(),
266            reason));
267
268     sendcmdto_one(sptr, CMD_GLINE, acptr, "%C %s%c%s %Tu %Tu :%s",
269                   acptr, flags & GLINE_OPERFORCE ? "!" : "",
270                   action == GLINE_ACTIVATE ? '+' : '-', mask,
271                   expire - TStime(), TStime(), reason);
272
273     return 0; /* all done */
274   }
275
276   /* can't modify a G-line that doesn't exist, so remap to activate */
277   if (!agline && action == GLINE_MODIFY)
278     action = GLINE_ACTIVATE;
279
280   /* OK, let's figure out what other parameters we may have... */
281   switch (action) {
282   case GLINE_LOCAL_ACTIVATE: /* locally activating a G-line */
283   case GLINE_LOCAL_DEACTIVATE: /* locally deactivating a G-line */
284     if (!agline) /* no G-line to locally activate or deactivate? */
285       return send_reply(sptr, ERR_NOSUCHGLINE, mask);
286     lastmod = agline->gl_lastmod;
287     break; /* no additional parameters to manipulate */
288
289   case GLINE_ACTIVATE: /* activating a G-line */
290   case GLINE_DEACTIVATE: /* deactivating a G-line */
291     /* in either of these cases, we have at least a lastmod parameter */
292     if (parc < 4)
293       return need_more_params(sptr, "GLINE");
294     else if (parc == 4) /* lastmod only form... */
295       lastmod = atoi(parv[3]);
296     /*FALLTHROUGH*/
297   case GLINE_MODIFY: /* modifying a G-line */
298     /* convert expire and lastmod, look for lifetime and reason */
299     if (parc > 4) { /* protect against fall-through from 4-param form */
300       expire = atoi(parv[3]); /* convert expiration and lastmod */
301       expire = abs_expire(expire);
302       lastmod = atoi(parv[4]);
303
304       flags |= GLINE_EXPIRE; /* we have an expiration time update */
305
306       if (parc > 6) { /* no question, have a lifetime and reason */
307         lifetime = atoi(parv[5]);
308         reason = parv[parc - 1];
309
310         flags |= GLINE_LIFETIME | GLINE_REASON;
311       } else if (parc == 6) { /* either a lifetime or a reason */
312         if (!agline || /* gline creation, has to be the reason */
313             /* trial-convert as lifetime, and if it doesn't fully convert,
314              * it must be the reason */
315             (!(lifetime = strtoul(parv[5], &tmp, 10)) && !*tmp)) {
316           lifetime = 0;
317           reason = parv[5];
318
319           flags |= GLINE_REASON; /* have a reason update */
320         } else if (lifetime)
321           flags |= GLINE_LIFETIME; /* have a lifetime update */
322       }
323     }
324   }
325
326   if (!lastmod) /* must have a lastmod parameter by now */
327     return need_more_params(sptr, "GLINE");
328
329   Debug((DEBUG_DEBUG, "I have a global G-line I am acting upon now; "
330          "target %s, mask %s, operforce %s, action %s, expire %Tu, "
331          "lastmod %Tu, lifetime %Tu, reason: %s; gline %s!  (fields "
332          "present: %s %s %s)", target, mask,
333          flags & GLINE_OPERFORCE ? "YES" : "NO",
334          action == GLINE_ACTIVATE ? "+" :
335          (action == GLINE_DEACTIVATE ? "-" :
336           (action == GLINE_LOCAL_ACTIVATE ? ">" :
337            (action == GLINE_LOCAL_DEACTIVATE ? "<" : "(MODIFY)"))),
338          expire, lastmod, lifetime, reason,
339          agline ? "EXISTS" : "does not exist",
340          flags & GLINE_EXPIRE ? "expire" : "",
341          flags & GLINE_LIFETIME ? "lifetime" : "",
342          flags & GLINE_REASON ? "reason" : ""));
343
344   /* OK, at this point, we have converted all available parameters.
345    * Let's actually do the action!
346    */
347   if (agline)
348     return gline_modify(cptr, sptr, agline, action, reason, expire,
349                         lastmod, lifetime, flags);
350
351   assert(action != GLINE_LOCAL_ACTIVATE);
352   assert(action != GLINE_LOCAL_DEACTIVATE);
353   assert(action != GLINE_MODIFY);
354
355   if (!expire) { /* Cannot *add* a G-line we don't have, but try hard */
356     Debug((DEBUG_DEBUG, "Propagating G-line %s for G-line we don't have",
357            action == GLINE_ACTIVATE ? "activation" : "deactivation"));
358
359     /* propagate the G-line, even though we don't have it */
360     sendcmdto_serv_butone(sptr, CMD_GLINE, cptr, "* %c%s %Tu",
361                           action == GLINE_ACTIVATE ? '+' : '-',
362                           mask, lastmod);
363
364     return 0;
365   }
366
367   return gline_add(cptr, sptr, mask, reason, expire, lastmod, lifetime,
368                    flags | ((action == GLINE_ACTIVATE) ? GLINE_ACTIVE : 0));
369 }
370
371 /*
372  * mo_gline - oper message handler
373  *
374  * parv[0] = Sender prefix
375  * parv[1] = [[+|-]<G-line mask>]
376  *
377  * For other parameters, see doc/readme.gline.
378  */
379 int
380 mo_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
381 {
382   struct Client *acptr = 0;
383   struct Gline *agline = 0;
384   unsigned int flags = 0;
385   enum GlineAction action = GLINE_MODIFY;
386   time_t expire = 0;
387   char *mask = parv[1], *target = 0, *reason = 0, *end;
388
389   if (parc < 2)
390     return gline_list(sptr, 0);
391
392   if (*mask == '!') {
393     mask++;
394
395     if (HasPriv(sptr, PRIV_WIDE_GLINE))
396       flags |= GLINE_OPERFORCE;
397   }
398
399   switch (*mask) { /* handle +, -, <, and > */
400   case '+': /* activate the G-line */
401     action = GLINE_ACTIVATE;
402     mask++;
403     break;
404
405   case '-': /* deactivate the G-line */
406     action = GLINE_DEACTIVATE;
407     mask++;
408     break;
409
410   case '>': /* locally activate the G-line */
411     action = GLINE_LOCAL_ACTIVATE;
412     mask++;
413     break;
414
415   case '<': /* locally deactivate the G-line */
416     action = GLINE_LOCAL_DEACTIVATE;
417     mask++;
418     break;
419   }
420
421   /* OK, let's figure out the parameters... */
422   switch (action) {
423   case GLINE_MODIFY: /* no specific action on the G-line... */
424     if (parc == 2) /* user wants a listing of a specific G-line */
425       return gline_list(sptr, mask);
426     else if (parc < 4) /* must have target and expire, minimum */
427       return need_more_params(sptr, "GLINE");
428
429     target = parv[2]; /* get the target... */
430     expire = strtol(parv[3], &end, 10) + TStime(); /* and the expiration */
431     if (*end != '\0')
432       return send_reply(sptr, SND_EXPLICIT | ERR_BADEXPIRE, "%s :Bad expire time", parv[3]);
433
434     flags |= GLINE_EXPIRE; /* remember that we got an expire time */
435
436     if (parc > 4) { /* also got a reason... */
437       reason = parv[parc - 1];
438       flags |= GLINE_REASON;
439     }
440
441     /* target is not global, interpolate action and require reason */
442     if (target[0] != '*' || target[1] != '\0') {
443       if (!reason) /* have to have a reason for this */
444         return need_more_params(sptr, "GLINE");
445
446       action = GLINE_ACTIVATE;
447     }
448     break;
449
450   case GLINE_LOCAL_ACTIVATE: /* locally activate a G-line */
451   case GLINE_LOCAL_DEACTIVATE: /* locally deactivate a G-line */
452     if (parc > 2) { /* if target is available, pick it */
453       target = parv[2];
454       if (target[0] == '*' && target[1] == '\0')
455         return send_reply(sptr, ERR_NOSUCHSERVER, target);
456     }
457     break;
458
459   case GLINE_ACTIVATE: /* activating/adding a G-line */
460   case GLINE_DEACTIVATE: /* deactivating/removing a G-line */
461     if (parc < 3)
462       return need_more_params(sptr, "GLINE");
463
464     if (parc > 3) {
465       /* get expiration and target */
466       reason = parv[parc - 1];
467       expire = strtol(parv[parc - 2], &end, 10) + TStime();
468       if (*end != '\0')
469         return send_reply(sptr, SND_EXPLICIT | ERR_BADEXPIRE, "%s :Bad expire time", parv[parc - 2]);
470
471       flags |= GLINE_EXPIRE | GLINE_REASON; /* remember that we got 'em */
472
473       if (parc > 4) /* also have a target! */
474         target = parv[2];
475     } else {
476       target = parv[2]; /* target has to be present, and has to be '*' */
477
478       if (target[0] != '*' || target[1] != '\0')
479         return need_more_params(sptr, "GLINE");
480     }
481     break;
482   }
483
484   /* Is there no mask left? */
485   if (mask[0] == '\0')
486     return need_more_params(sptr, "GLINE");
487
488   /* Now let's figure out which is the target server */
489   if (!target) /* no target, has to be me... */
490     acptr = &me;
491   /* if it's not '*', look up the server */
492   else if ((target[0] != '*' || target[1] != '\0') &&
493            !(acptr = find_match_server(target)))
494     return send_reply(sptr, ERR_NOSUCHSERVER, target);
495
496   /* Now, is the G-line local or global? */
497   if (action == GLINE_LOCAL_ACTIVATE || action == GLINE_LOCAL_DEACTIVATE ||
498       !acptr)
499     flags |= GLINE_GLOBAL;
500   else /* it's some form of local G-line */
501     flags |= GLINE_LOCAL;
502
503   /* If it's a local activate/deactivate and server isn't me, propagate it */
504   if ((action == GLINE_LOCAL_ACTIVATE || action == GLINE_LOCAL_DEACTIVATE) &&
505       !IsMe(acptr)) {
506     /* check for permissions... */
507     if (!feature_bool(FEAT_CONFIG_OPERCMDS))
508       return send_reply(sptr, ERR_DISABLED, "GLINE");
509     else if (!HasPriv(sptr, PRIV_GLINE))
510       return send_reply(sptr, ERR_NOPRIVILEGES);
511
512     Debug((DEBUG_DEBUG, "I am forwarding a local change to a global gline "
513            "to a remote server; target %s, mask %s, operforce %s, action %c",
514            cli_name(acptr), mask, flags & GLINE_OPERFORCE ? "YES" : "NO",
515            action == GLINE_LOCAL_ACTIVATE ? '>' : '<'));
516
517     sendcmdto_one(sptr, CMD_GLINE, acptr, "%C %s%c%s", acptr,
518                   flags & GLINE_OPERFORCE ? "!" : "",
519                   action == GLINE_LOCAL_ACTIVATE ? '>' : '<', mask);
520
521     return 0; /* all done */
522   }
523
524   /* Next, try to find the G-line... */
525   if ((flags & GLINE_GLOBAL) || IsMe(acptr)) /* don't bother if it's not me! */
526     agline = gline_find(mask, flags | GLINE_ANY | GLINE_EXACT);
527
528   /* We now have all the pieces to tell us what we've got; let's put
529    * it all together and convert the rest of the arguments.
530    */
531
532   /* Handle the local G-lines first... */
533   if (flags & GLINE_LOCAL) {
534     assert(acptr);
535
536     /* normalize the action, first */
537     if (action == GLINE_LOCAL_ACTIVATE || action == GLINE_MODIFY)
538       action = GLINE_ACTIVATE;
539     else if (action == GLINE_LOCAL_DEACTIVATE)
540       action = GLINE_DEACTIVATE;
541
542     /* If it's not for us, forward */
543     /* UPDATE NOTE: Once all servers are updated to u2.10.12.11, the
544      * format string in this sendcmdto_one() may be updated to omit
545      * <lastmod> for GLINE_ACTIVATE and to omit <expire>, <lastmod>,
546      * and <reason> for GLINE_DEACTIVATE.
547      */
548
549     if (!IsMe(acptr)) {
550       /* check for permissions... */
551       if (!feature_bool(FEAT_CONFIG_OPERCMDS))
552         return send_reply(sptr, ERR_DISABLED, "GLINE");
553       else if (!HasPriv(sptr, PRIV_GLINE))
554         return send_reply(sptr, ERR_NOPRIVILEGES);
555
556       Debug((DEBUG_DEBUG, "I am forwarding a local G-line to a remote "
557              "server; target %s, mask %s, operforce %s, action %c, "
558              "expire %Tu, reason %s", target, mask,
559              flags & GLINE_OPERFORCE ? "YES" : "NO",
560              action == GLINE_ACTIVATE ? '+' : '-', expire, reason));
561
562       sendcmdto_one(sptr, CMD_GLINE, acptr, "%C %s%c%s %Tu %Tu :%s",
563                     acptr, flags & GLINE_OPERFORCE ? "!" : "",
564                     action == GLINE_ACTIVATE ? '+' : '-', mask,
565                     expire - TStime(), TStime(), reason);
566
567       return 0; /* all done */
568     }
569
570     /* check local G-line permissions... */
571     if (!HasPriv(sptr, PRIV_LOCAL_GLINE))
572       return send_reply(sptr, ERR_NOPRIVILEGES);
573
574     /* let's handle activation... */
575     if (action == GLINE_ACTIVATE) {
576       if (agline) /* G-line already exists, so let's ignore it... */
577         return 0;
578
579       /* OK, create the local G-line */
580       Debug((DEBUG_DEBUG, "I am creating a local G-line here; target %s, "
581              "mask %s, operforce %s, action  %s, expire %Tu, reason: %s",
582              target, mask, flags & GLINE_OPERFORCE ? "YES" : "NO",
583              action == GLINE_ACTIVATE ? "+" : "-", expire, reason));
584
585       return gline_add(cptr, sptr, mask, reason, expire, 0, 0,
586                        flags | GLINE_ACTIVE);
587     } else { /* OK, it's a deactivation/destruction */
588       if (!agline) /* G-line doesn't exist, so let's complain... */
589         return send_reply(sptr, ERR_NOSUCHGLINE, mask);
590
591       /* Let's now destroy the G-line */
592       Debug((DEBUG_DEBUG, "I am destroying a local G-line here; target %s, "
593              "mask %s, operforce %s, action %s", target, mask,
594              flags & GLINE_OPERFORCE ? "YES" : "NO",
595              action == GLINE_ACTIVATE ? "+" : "-"));
596
597       return gline_destroy(cptr, sptr, agline);
598     }
599   }
600
601   /* can't modify a G-line that doesn't exist...
602    * (and if we are creating a new one, we need a reason and expiration)
603    */
604   if (!agline &&
605       (action == GLINE_MODIFY || action == GLINE_LOCAL_ACTIVATE ||
606        action == GLINE_LOCAL_DEACTIVATE || !reason || !expire))
607     return send_reply(sptr, ERR_NOSUCHGLINE, mask);
608
609   /* check for G-line permissions... */
610   if (action == GLINE_LOCAL_ACTIVATE || action == GLINE_LOCAL_DEACTIVATE) {
611     /* only need local privileges for locally-limited status changes */
612     if (!HasPriv(sptr, PRIV_LOCAL_GLINE))
613       return send_reply(sptr, ERR_NOPRIVILEGES);
614   } else { /* global privileges required */
615     if (!feature_bool(FEAT_CONFIG_OPERCMDS))
616       return send_reply(sptr, ERR_DISABLED, "GLINE");
617     else if (!HasPriv(sptr, PRIV_GLINE))
618       return send_reply(sptr, ERR_NOPRIVILEGES);
619   }
620
621   Debug((DEBUG_DEBUG, "I have a global G-line I am acting upon now; "
622          "target %s, mask %s, operforce %s, action %s, expire %Tu, "
623          "reason: %s; gline %s!  (fields present: %s %s)", target, 
624          mask, flags & GLINE_OPERFORCE ? "YES" : "NO",
625          action == GLINE_ACTIVATE ? "+" :
626          (action == GLINE_DEACTIVATE ? "-" :
627           (action == GLINE_LOCAL_ACTIVATE ? ">" :
628            (action == GLINE_LOCAL_DEACTIVATE ? "<" : "(MODIFY)"))),
629          expire, reason, agline ? "EXISTS" : "does not exist",
630          flags & GLINE_EXPIRE ? "expire" : "",
631          flags & GLINE_REASON ? "reason" : ""));
632
633   if (agline) /* modifying an existing G-line */
634     return gline_modify(cptr, sptr, agline, action, reason, expire,
635                         TStime(), 0, flags);
636
637   assert(action != GLINE_LOCAL_ACTIVATE);
638   assert(action != GLINE_LOCAL_DEACTIVATE);
639   assert(action != GLINE_MODIFY);
640
641   /* create a new G-line */
642   return gline_add(cptr, sptr, mask, reason, expire, TStime(), 0,
643                    flags | ((action == GLINE_ACTIVATE) ? GLINE_ACTIVE : 0));
644 }
645
646 /*
647  * m_gline - user message handler
648  *
649  * parv[0] = Sender prefix
650  * parv[1] = [<server name>]
651  *
652  */
653 int
654 m_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
655 {
656   if (parc < 2)
657     return send_reply(sptr, ERR_NOSUCHGLINE, "");
658
659   return gline_list(sptr, parv[1]);
660 }