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