ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/modules/m_gline.c
Revision: 1028
Committed: Sun Nov 8 13:03:38 2009 UTC (14 years, 4 months ago) by michael
Content type: text/x-csrc
File size: 21164 byte(s)
Log Message:
- move ircd-hybrid-7.2 to trunk

File Contents

# Content
1 /*
2 * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd).
3 *
4 * Copyright (C) 2002 by the past and present ircd coders, and others.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 * USA
20 */
21
22 /*! \file m_gline.c
23 * \brief Includes required functions for processing the GLINE command.
24 * \version $Id$
25 */
26
27 #include "stdinc.h"
28 #include "list.h"
29 #include "handlers.h"
30 #include "s_gline.h"
31 #include "channel.h"
32 #include "client.h"
33 #include "common.h"
34 #include "irc_string.h"
35 #include "sprintf_irc.h"
36 #include "ircd.h"
37 #include "hostmask.h"
38 #include "numeric.h"
39 #include "s_bsd.h"
40 #include "s_conf.h"
41 #include "s_misc.h"
42 #include "send.h"
43 #include "msg.h"
44 #include "fileio.h"
45 #include "s_serv.h"
46 #include "hash.h"
47 #include "parse.h"
48 #include "modules.h"
49 #include "s_log.h"
50
51 #define GLINE_NOT_PLACED 0
52 #define GLINE_ALREADY_VOTED -1
53 #define GLINE_PLACED 1
54
55
56 static void me_gline(struct Client *, struct Client *, int, char *[]);
57 static void ms_gline(struct Client *, struct Client *, int, char *[]);
58 static void mo_gline(struct Client *, struct Client *, int, char *[]);
59 static void me_gungline(struct Client *, struct Client *, int, char *[]);
60 static void mo_gungline(struct Client *, struct Client *, int, char *[]);
61
62 /*
63 * gline enforces 3 parameters to force operator to give a reason
64 * a gline is not valid with "No reason"
65 * -db
66 */
67 struct Message gline_msgtab = {
68 "GLINE", 0, 0, 3, 0, MFLG_SLOW, 0,
69 { m_unregistered, m_not_oper, ms_gline, me_gline, mo_gline, m_ignore }
70 };
71
72 struct Message ungline_msgtab = {
73 "GUNGLINE", 0, 0, 3, 0, MFLG_SLOW, 0,
74 { m_unregistered, m_not_oper, m_ignore, me_gungline, mo_gungline, m_ignore }
75 };
76
77
78 void
79 _modinit(void)
80 {
81 mod_add_cmd(&gline_msgtab);
82 mod_add_cmd(&ungline_msgtab);
83 add_capability("GLN", CAP_GLN, 1);
84 }
85
86 void
87 _moddeinit(void)
88 {
89 mod_del_cmd(&gline_msgtab);
90 mod_del_cmd(&ungline_msgtab);
91 delete_capability("GLN");
92 }
93
94 const char *_version = "$Revision$";
95
96 /*! \brief Adds a GLINE to the configuration subsystem.
97 *
98 * \param source_p Operator requesting gline
99 * \param user Username covered by the gline
100 * \param host Hostname covered by the gline
101 * \param reason Reason for the gline
102 */
103 static void
104 set_local_gline(const struct Client *source_p, const char *user,
105 const char *host, const char *reason)
106 {
107 char buffer[IRCD_BUFSIZE];
108 struct ConfItem *conf;
109 struct AccessItem *aconf;
110
111
112 conf = make_conf_item(GLINE_TYPE);
113 aconf = map_to_conf(conf);
114
115 snprintf(buffer, sizeof(buffer), "%s (%s)", reason, smalldate(CurrentTime));
116 DupString(aconf->reason, buffer);
117 DupString(aconf->user, user);
118 DupString(aconf->host, host);
119
120 aconf->hold = CurrentTime + ConfigFileEntry.gline_time;
121 add_temp_line(conf);
122
123 sendto_realops_flags(UMODE_ALL, L_ALL,
124 "%s added G-Line for [%s@%s] [%s]",
125 get_oper_name(source_p),
126 aconf->user, aconf->host, aconf->reason);
127 ilog(L_TRACE, "%s added G-Line for [%s@%s] [%s]",
128 get_oper_name(source_p), aconf->user, aconf->host, aconf->reason);
129 log_oper_action(LOG_GLINE_TYPE, source_p, "[%s@%s] [%s]\n",
130 aconf->user, aconf->host, aconf->reason);
131 /* Now, activate gline against current online clients */
132 rehashed_klines = 1;
133 }
134
135 /*! \brief Removes a GLINE from the configuration subsystem.
136 *
137 * \param user Username covered by the gline
138 * \param host Hostname covered by the gline
139 */
140 static int
141 remove_gline_match(const char *user, const char *host)
142 {
143 struct AccessItem *aconf;
144 dlink_node *ptr = NULL;
145 struct irc_ssaddr addr, caddr;
146 int nm_t, cnm_t, bits, cbits;
147
148 nm_t = parse_netmask(host, &addr, &bits);
149
150 DLINK_FOREACH(ptr, temporary_glines.head)
151 {
152 aconf = map_to_conf(ptr->data);
153 cnm_t = parse_netmask(aconf->host, &caddr, &cbits);
154
155 if (cnm_t != nm_t || irccmp(user, aconf->user))
156 continue;
157
158 if ((nm_t == HM_HOST && !irccmp(aconf->host, host)) ||
159 (nm_t == HM_IPV4 && bits == cbits && match_ipv4(&addr, &caddr, bits))
160 #ifdef IPV6
161 || (nm_t == HM_IPV6 && bits == cbits && match_ipv6(&addr, &caddr, bits))
162 #endif
163 )
164 {
165 dlinkDelete(ptr, &temporary_glines);
166 delete_one_address_conf(aconf->host, aconf);
167 return 1;
168 }
169 }
170
171 return 0;
172 }
173
174 /*! \brief This function is called once a majority of opers have agreed on a
175 * GLINE/GUNGLINE, and it can be placed. The information about an
176 * operator being passed to us happens to be the operator who pushed us
177 * over the "majority" level needed. See check_majority() for more
178 * information.
179 *
180 * \param source_p Operator requesting gline
181 * \param user Username covered by the gline
182 * \param host Hostname covered by the gline
183 * \param reason Reason for the gline
184 * \param type Valid values are either GLINE_PENDING_ADD_TYPE, or
185 * GLINE_PENDING_DEL_TYPE
186 */
187 static void
188 add_new_majority(const struct Client *source_p, const char *user,
189 const char *host, const char *reason, const unsigned int type)
190 {
191 struct gline_pending *pending = MyMalloc(sizeof(struct gline_pending));
192
193 strlcpy(pending->vote_1.oper_nick, source_p->name, sizeof(pending->vote_1.oper_nick));
194 strlcpy(pending->vote_1.oper_user, source_p->username, sizeof(pending->vote_1.oper_user));
195 strlcpy(pending->vote_1.oper_host, source_p->host, sizeof(pending->vote_1.oper_host));
196 strlcpy(pending->vote_1.oper_server, source_p->servptr->name, sizeof(pending->vote_1.oper_server));
197
198 strlcpy(pending->user, user, sizeof(pending->user));
199 strlcpy(pending->host, host, sizeof(pending->host));
200 strlcpy(pending->vote_1.reason, reason, sizeof(pending->vote_1.reason));
201
202 pending->last_gline_time = CurrentTime;
203 pending->vote_1.time_request = CurrentTime;
204
205 dlinkAdd(pending, &pending->node, &pending_glines[type]);
206 }
207
208 /*! \brief See if there is a majority agreement on a GLINE on the given user.
209 * There must be at least 3 different opers agreeing on this
210 * GLINE/GUNGLINE
211 *
212 * \param source_p Operator requesting gline
213 * \param user Username covered by the gline
214 * \param host Hostname covered by the gline
215 * \param reason Reason for the gline
216 * \param type Valid values are either GLINE_PENDING_ADD_TYPE, or
217 * GLINE_PENDING_DEL_TYPE
218 *
219 * \return
220 * - GLINE_ALREADY_VOTED returned if oper/server has already voted
221 * - GLINE_PLACED returned if this triggers a gline
222 * - GLINE_NOT_PLACED returned if not triggered
223 */
224 static int
225 check_majority(const struct Client *source_p, const char *user,
226 const char *host, const char *reason, const int type)
227 {
228 dlink_node *dn_ptr = NULL;
229
230 cleanup_glines(NULL);
231
232 /* if its already glined, why bother? :) -- fl_ */
233 if ((type == GLINE_PENDING_ADD_TYPE) && find_is_glined(host, user))
234 return GLINE_NOT_PLACED;
235
236 DLINK_FOREACH(dn_ptr, pending_glines[type].head)
237 {
238 struct gline_pending *gp_ptr = dn_ptr->data;
239
240 if (irccmp(gp_ptr->user, user) ||
241 irccmp(gp_ptr->host, host))
242 continue;
243
244 if ((!irccmp(gp_ptr->vote_1.oper_user, source_p->username) &&
245 !irccmp(gp_ptr->vote_1.oper_host, source_p->host)) ||
246 !irccmp(gp_ptr->vote_1.oper_server, source_p->servptr->name))
247 return GLINE_ALREADY_VOTED;
248
249 if (gp_ptr->vote_2.oper_user[0] != '\0')
250 {
251 /* if two other opers on two different servers have voted yes */
252 if ((!irccmp(gp_ptr->vote_2.oper_user, source_p->username) &&
253 !irccmp(gp_ptr->vote_2.oper_host, source_p->host)) ||
254 !irccmp(gp_ptr->vote_2.oper_server, source_p->servptr->name))
255 return GLINE_ALREADY_VOTED;
256
257 if (type == GLINE_PENDING_DEL_TYPE)
258 {
259 if (remove_gline_match(user, host))
260 {
261 sendto_realops_flags(UMODE_ALL, L_ALL,
262 "%s has removed the G-Line for: [%s@%s]",
263 get_oper_name(source_p), user, host);
264 ilog(L_NOTICE, "%s removed G-Line for [%s@%s]",
265 get_oper_name(source_p), user, host);
266 }
267 }
268 else
269 /* trigger the gline using the original reason --fl */
270 set_local_gline(source_p, user, host, gp_ptr->vote_1.reason);
271
272 cleanup_glines(gp_ptr);
273 return GLINE_PLACED;
274 }
275
276 strlcpy(gp_ptr->vote_2.oper_nick, source_p->name,
277 sizeof(gp_ptr->vote_2.oper_nick));
278 strlcpy(gp_ptr->vote_2.oper_user, source_p->username,
279 sizeof(gp_ptr->vote_2.oper_user));
280 strlcpy(gp_ptr->vote_2.oper_host, source_p->host,
281 sizeof(gp_ptr->vote_2.oper_host));
282 strlcpy(gp_ptr->vote_2.reason, reason,
283 sizeof(gp_ptr->vote_2.reason));
284 strlcpy(gp_ptr->vote_2.oper_server, source_p->servptr->name,
285 sizeof(gp_ptr->vote_2.oper_server));
286 gp_ptr->last_gline_time = CurrentTime;
287 gp_ptr->vote_2.time_request = CurrentTime;
288 return GLINE_NOT_PLACED;
289 }
290
291 /*
292 * Didn't find this user@host gline in pending gline list
293 * so add it.
294 */
295 add_new_majority(source_p, user, host, reason, type);
296 return GLINE_NOT_PLACED;
297 }
298
299 static void
300 do_sgline(struct Client *source_p, int parc, char *parv[], int prop)
301 {
302 const char *reason = NULL; /* reason for "victims" demise */
303 char *user = NULL;
304 char *host = NULL; /* user and host of GLINE "victim" */
305 int var_offset = 0, logged = 0;
306 dlink_node *ptr;
307 struct Client *target_p = NULL;
308
309 switch (parc)
310 {
311 case 4: /* hyb-7 style */
312 if (!IsClient(source_p))
313 return;
314 break;
315 case 8: /* hyb-6 style */
316 if (!IsServer(source_p))
317 return;
318 target_p = find_person(source_p->from, parv[1]);
319 if (target_p == NULL || target_p->servptr != source_p)
320 return;
321 source_p = target_p, var_offset = 4;
322 break;
323 default:
324 return;
325 }
326
327 assert(source_p->servptr != NULL);
328
329 user = parv[++var_offset];
330 host = parv[++var_offset];
331 reason = parv[++var_offset];
332
333 var_offset = 0;
334
335 DLINK_FOREACH(ptr, gdeny_items.head)
336 {
337 struct ConfItem *conf = ptr->data;
338 struct AccessItem *aconf = map_to_conf(conf);
339
340 if (match(conf->name, source_p->servptr->name) &&
341 match(aconf->user, source_p->username) &&
342 match(aconf->host, source_p->host))
343 {
344 var_offset = aconf->flags;
345 break;
346 }
347 }
348
349 if (prop && !(var_offset & GDENY_BLOCK))
350 {
351 sendto_server(source_p->from, NULL, CAP_GLN, NOCAPS,
352 ":%s GLINE %s %s :%s",
353 source_p->name, user, host, reason);
354 /* hyb-6 version to the rest */
355 sendto_server(source_p->from, NULL, NOCAPS, CAP_GLN,
356 ":%s GLINE %s %s %s %s %s %s :%s",
357 source_p->servptr->name,
358 source_p->name, source_p->username, source_p->host,
359 source_p->servptr->name,
360 user, host, reason);
361 }
362 else if (ConfigFileEntry.gline_logging & GDENY_BLOCK && ServerInfo.hub)
363 {
364 sendto_realops_flags(UMODE_ALL, L_ALL, "Blocked G-Line %s requested on [%s@%s] [%s]",
365 get_oper_name(source_p), user, host, reason);
366 ilog(L_TRACE, "Blocked G-Line %s requested on [%s@%s] [%s]",
367 get_oper_name(source_p), user, host, reason);
368 logged = 1;
369 }
370
371 if (var_offset & GDENY_REJECT)
372 {
373 if (ConfigFileEntry.gline_logging & GDENY_REJECT && !logged)
374 {
375 sendto_realops_flags(UMODE_ALL, L_ALL, "Rejected G-Line %s requested on [%s@%s] [%s]",
376 get_oper_name(source_p), user, host, reason);
377 ilog(L_TRACE, "Rejected G-Line %s requested on [%s@%s] [%s]",
378 get_oper_name(source_p), user, host, reason);
379 }
380
381 return;
382 }
383
384 if (ConfigFileEntry.glines)
385 {
386 if (!valid_wild_card(source_p, YES, 2, user, host))
387 return;
388
389 if (IsClient(source_p))
390 {
391 const char *p = NULL;
392
393 if ((p = strchr(host, '/')))
394 {
395 int bitlen = strtol(++p, NULL, 10);
396 int min_bitlen = strchr(host, ':') ? ConfigFileEntry.gline_min_cidr6 :
397 ConfigFileEntry.gline_min_cidr;
398
399 if (bitlen < min_bitlen)
400 {
401 sendto_realops_flags(UMODE_ALL, L_ALL, "%s!%s@%s on %s is requesting "
402 "a GLINE with a CIDR mask < %d for [%s@%s] [%s]",
403 source_p->name, source_p->username, source_p->host,
404 source_p->servptr->name, min_bitlen, user, host, reason);
405 return;
406 }
407 }
408 }
409
410 /* If at least 3 opers agree this user should be G lined then do it */
411 if (check_majority(source_p, user, host, reason, GLINE_PENDING_ADD_TYPE) ==
412 GLINE_ALREADY_VOTED)
413 {
414 sendto_realops_flags(UMODE_ALL, L_ALL, "oper or server has already voted");
415 return;
416 }
417
418 sendto_realops_flags(UMODE_ALL, L_ALL,
419 "%s requesting G-Line for [%s@%s] [%s]",
420 get_oper_name(source_p),
421 user, host, reason);
422 ilog(L_TRACE, "#gline for %s@%s [%s] requested by %s",
423 user, host, reason, get_oper_name(source_p));
424 }
425 }
426
427
428 /*! \brief GLINE command handler (called by operators)
429 *
430 * \param client_p Pointer to allocated Client struct with physical connection
431 * to this server, i.e. with an open socket connected.
432 * \param source_p Pointer to allocated Client struct from which the message
433 * originally comes from. This can be a local or remote client.
434 * \param parc Integer holding the number of supplied arguments.
435 * \param parv Argument vector where parv[0] .. parv[parc-1] are non-NULL
436 * pointers.
437 * \note Valid arguments for this command are:
438 * - parv[0] = sender prefix
439 * - parv[1] = user\@host mask
440 * - parv[2] = reason
441 */
442 static void
443 mo_gline(struct Client *client_p, struct Client *source_p,
444 int parc, char *parv[])
445 {
446 char *user = NULL;
447 char *host = NULL;
448 char *reason = NULL;
449 char *p;
450
451 if (!ConfigFileEntry.glines)
452 {
453 sendto_one(source_p, ":%s NOTICE %s :GLINE disabled",
454 me.name, source_p->name);
455 return;
456 }
457
458 if (!IsOperGline(source_p))
459 {
460 sendto_one(source_p, form_str(ERR_NOPRIVS),
461 me.name, source_p->name, "gline");
462 return;
463 }
464
465 if (parse_aline("GLINE", source_p, parc, parv, AWILD,
466 &user, &host, NULL, NULL, &reason) < 0)
467 return;
468
469 if ((p = strchr(host, '/')) != NULL)
470 {
471 int bitlen = strtol(++p, NULL, 10);
472 int min_bitlen = strchr(host, ':') ? ConfigFileEntry.gline_min_cidr6 :
473 ConfigFileEntry.gline_min_cidr;
474 if (bitlen < min_bitlen)
475 {
476 sendto_one(source_p, ":%s NOTICE %s :Cannot set G-Lines with CIDR length < %d",
477 me.name, source_p->name, min_bitlen);
478 return;
479 }
480 }
481
482 /* If at least 3 opers agree this user should be G lined then do it */
483 if (check_majority(source_p, user, host, reason, GLINE_PENDING_ADD_TYPE) ==
484 GLINE_ALREADY_VOTED)
485 {
486 sendto_one(source_p,
487 ":%s NOTICE %s :This server or oper has already voted",
488 me.name, source_p->name);
489 return;
490 }
491
492 /*
493 * call these two functions first so the 'requesting' notice always comes
494 * before the 'has triggered' notice. -bill
495 */
496 sendto_realops_flags(UMODE_ALL, L_ALL,
497 "%s requesting G-Line for [%s@%s] [%s]",
498 get_oper_name(source_p),
499 user, host, reason);
500 ilog(L_TRACE, "#gline for %s@%s [%s] requested by %s!%s@%s",
501 user, host, reason, source_p->name, source_p->username,
502 source_p->host);
503
504 /* 4 param version for hyb-7 servers */
505 sendto_server(NULL, NULL, CAP_GLN|CAP_TS6, NOCAPS,
506 ":%s GLINE %s %s :%s",
507 ID(source_p), user, host, reason);
508 sendto_server(NULL, NULL, CAP_GLN, CAP_TS6,
509 ":%s GLINE %s %s :%s",
510 source_p->name, user, host, reason);
511
512 /* 8 param for hyb-6 */
513 sendto_server(NULL, NULL, CAP_TS6, CAP_GLN,
514 ":%s GLINE %s %s %s %s %s %s :%s",
515 ID(&me),
516 ID(source_p), source_p->username,
517 source_p->host, source_p->servptr->name, user, host,
518 reason);
519 sendto_server(NULL, NULL, NOCAPS, CAP_GLN|CAP_TS6,
520 ":%s GLINE %s %s %s %s %s %s :%s",
521 me.name, source_p->name, source_p->username,
522 source_p->host, source_p->servptr->name, user, host,
523 reason);
524 }
525
526 /* ms_gline()
527 * me_gline()
528 * do_sgline()
529 *
530 * inputs - The usual for a m_ function
531 * output -
532 * side effects -
533 *
534 * Place a G line if 3 opers agree on the identical user@host
535 *
536 * Allow this server to pass along GLINE if received and
537 * GLINES is not defined.
538 *
539 * ENCAP'd GLINES are propagated by encap code.
540 */
541
542 static void
543 ms_gline(struct Client *client_p, struct Client *source_p,
544 int parc, char *parv[])
545 {
546 do_sgline(source_p, parc, parv, 1);
547 }
548
549 static void
550 me_gline(struct Client *client_p, struct Client *source_p,
551 int parc, char *parv[])
552 {
553 do_sgline(source_p, parc, parv, 0);
554 }
555
556 static void
557 do_sungline(struct Client *source_p, const char *user,
558 const char *host, const char *reason, int prop)
559 {
560 assert(source_p->servptr != NULL);
561
562 sendto_realops_flags(UMODE_ALL, L_ALL,
563 "%s requesting UNG-Line for [%s@%s] [%s]",
564 get_oper_name(source_p), user, host, reason);
565 ilog(L_TRACE, "#ungline for %s@%s [%s] requested by %s",
566 user, host, reason, get_oper_name(source_p));
567
568 /* If at least 3 opers agree this user should be un G lined then do it */
569 if (check_majority(source_p, user, host, reason, GLINE_PENDING_DEL_TYPE) ==
570 GLINE_ALREADY_VOTED)
571 sendto_realops_flags(UMODE_ALL, L_ALL, "oper or server has already voted");
572
573 if (prop)
574 {
575 sendto_server(source_p->from, NULL, CAP_ENCAP|CAP_TS6, NOCAPS,
576 ":%s ENCAP * GUNGLINE %s %s :%s",
577 ID(source_p), user, host, reason);
578 sendto_server(source_p->from, NULL, CAP_ENCAP, CAP_TS6,
579 ":%s ENCAP * GUNGLINE %s %s :%s",
580 source_p->name, user, host, reason);
581 }
582 }
583
584 /*! \brief GUNGLINE command handler (called in response to an encapsulated
585 * GUNGLINE command)
586 *
587 * \param client_p Pointer to allocated Client struct with physical connection
588 * to this server, i.e. with an open socket connected.
589 * \param source_p Pointer to allocated Client struct from which the message
590 * originally comes from. This can be a local or remote client.
591 * \param parc Integer holding the number of supplied arguments.
592 * \param parv Argument vector where parv[0] .. parv[parc-1] are non-NULL
593 * pointers.
594 * \note Valid arguments for this command are:
595 * - parv[0] = sender prefix
596 * - parv[1] = username
597 * - parv[2] = hostname
598 * - parv[3] = reason
599 */
600 static void
601 me_gungline(struct Client *client_p, struct Client *source_p,
602 int parc, char *parv[])
603 {
604 if (ConfigFileEntry.glines)
605 do_sungline(source_p, parv[1], parv[2], parv[3], 0);
606 }
607
608 /*! \brief GUNGLINE command handler (called by operators)
609 *
610 * \param client_p Pointer to allocated Client struct with physical connection
611 * to this server, i.e. with an open socket connected.
612 * \param source_p Pointer to allocated Client struct from which the message
613 * originally comes from. This can be a local or remote client.
614 * \param parc Integer holding the number of supplied arguments.
615 * \param parv Argument vector where parv[0] .. parv[parc-1] are non-NULL
616 * pointers.
617 * \note Valid arguments for this command are:
618 * - parv[0] = sender prefix
619 * - parv[1] = user\@host mask
620 * - parv[2] = reason
621 */
622 static void
623 mo_gungline(struct Client *client_p, struct Client *source_p,
624 int parc, char *parv[])
625 {
626 char *user = NULL;
627 char *host = NULL;
628 char *reason = NULL;
629
630 if (!ConfigFileEntry.glines)
631 {
632 sendto_one(source_p, ":%s NOTICE %s :GUNGLINE disabled",
633 me.name, source_p->name);
634 return;
635 }
636
637 if (!IsOperGline(source_p))
638 {
639 sendto_one(source_p, form_str(ERR_NOPRIVS),
640 me.name, source_p->name, "gungline");
641 return;
642 }
643
644 if (parse_aline("GUNGLINE", source_p, parc, parv, 0, &user,
645 &host, NULL, NULL, &reason) < 0)
646 return;
647
648 do_sungline(source_p, user, host, reason, 1);
649 }

Properties

Name Value
svn:eol-style native
svn:keywords Id Revision