]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/xline.cpp
Fixed bug #85
[user/henk/code/inspircd.git] / src / xline.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  Inspire is copyright (C) 2002-2004 ChatSpike-Dev.
6  *                       E-mail:
7  *                <brain@chatspike.net>
8  *                <Craig@chatspike.net>
9  *     
10  * Written by Craig Edwards, Craig McLure, and others.
11  * This program is free but copyrighted software; see
12  *            the file COPYING for details.
13  *
14  * ---------------------------------------------------
15  */
16
17 using namespace std;
18
19 #include "inspircd_config.h"
20 #include "inspircd.h"
21 #include "inspircd_io.h"
22 #include "inspircd_util.h"
23 #include <unistd.h>
24 #include <fcntl.h>
25 #include <sys/errno.h>
26 #include <time.h>
27 #include <string>
28 #ifdef GCC3
29 #include <ext/hash_map>
30 #else
31 #include <hash_map>
32 #endif
33 #include <map>
34 #include <sstream>
35 #include <vector>
36 #include <deque>
37 #include "users.h"
38 #include "ctables.h"
39 #include "globals.h"
40 #include "modules.h"
41 #include "dynamic.h"
42 #include "wildcard.h"
43 #include "message.h"
44 #include "commands.h"
45 #include "xline.h"
46 #include "inspstring.h"
47 #include "helperfuncs.h"
48 #include "hashcomp.h"
49
50 extern int MODCOUNT;
51 extern std::vector<Module*> modules;
52 extern std::vector<ircd_module*> factory;
53
54 extern int LogLevel;
55 extern char ServerName[MAXBUF];
56 extern char Network[MAXBUF];
57 extern char ServerDesc[MAXBUF];
58 extern char AdminName[MAXBUF];
59 extern char AdminEmail[MAXBUF];
60 extern char AdminNick[MAXBUF];
61 extern char diepass[MAXBUF];
62 extern char restartpass[MAXBUF];
63 extern char motd[MAXBUF];
64 extern char rules[MAXBUF];
65 extern char list[MAXBUF];
66 extern char PrefixQuit[MAXBUF];
67 extern char DieValue[MAXBUF];
68
69 extern int debugging;
70 extern int WHOWAS_STALE;
71 extern int WHOWAS_MAX;
72 extern int DieDelay;
73 extern time_t startup_time;
74 extern int NetBufferSize;
75 extern time_t nb_start;
76
77 extern std::vector<std::string> module_names;
78
79 extern int boundPortCount;
80 extern int portCount;
81
82 extern int ports[MAXSOCKS];
83
84 extern std::stringstream config_f;
85
86 extern FILE *log_file;
87
88 typedef nspace::hash_map<std::string, userrec*, nspace::hash<string>, irc::StrHashComp> user_hash;
89 typedef nspace::hash_map<std::string, chanrec*, nspace::hash<string>, irc::StrHashComp> chan_hash;
90 typedef nspace::hash_map<in_addr,string*, nspace::hash<in_addr>, irc::InAddr_HashComp> address_cache;
91 typedef nspace::hash_map<std::string, WhoWasUser*, nspace::hash<string>, irc::StrHashComp> whowas_hash;
92 typedef std::deque<command_t> command_table;
93
94
95 extern user_hash clientlist;
96 extern chan_hash chanlist;
97 extern whowas_hash whowas;
98 extern command_table cmdlist;
99 extern file_cache MOTD;
100 extern file_cache RULES;
101 extern address_cache IP;
102
103 /* Version two, now with optimized expiry!
104  *
105  * Because the old way was horrendously slow, the new way of expiring xlines is very
106  * very efficient. I have improved the efficiency of the algorithm in two ways:
107  *
108  * (1) There are now two lists of items for each linetype. One list holds temporary
109  *     items, and the other list holds permenant items (ones which will expire).
110  *     Items which are on the permenant list are NEVER checked at all by the
111  *     expire_lines() function.
112  * (2) The temporary xline lists are always kept in strict numerical order, keyed by 
113  *     current time + duration. This means that the line which is due to expire the
114  *     soonest is always pointed at by vector::begin(), so a simple while loop can
115  *     very efficiently, very quickly and above all SAFELY pick off the first few
116  *     items in the vector which need zapping.
117  *
118  *     -- Brain
119  */
120
121
122
123 extern time_t TIME;
124
125 /* Lists for temporary lines with an expiry time */
126
127 std::vector<KLine> klines;
128 std::vector<GLine> glines;
129 std::vector<ZLine> zlines;
130 std::vector<QLine> qlines;
131 std::vector<ELine> elines;
132
133 /* Seperate lists for perm XLines that isnt checked by expiry functions */
134
135 std::vector<KLine> pklines;
136 std::vector<GLine> pglines;
137 std::vector<ZLine> pzlines;
138 std::vector<QLine> pqlines;
139 std::vector<ELine> pelines;
140
141
142 bool GSortComparison ( const GLine one, const GLine two );
143 bool ZSortComparison ( const ZLine one, const ZLine two );
144 bool ESortComparison ( const ELine one, const ELine two );
145 bool QSortComparison ( const QLine one, const QLine two );
146 bool KSortComparison ( const KLine one, const KLine two );
147
148 // Reads the default bans from the config file.
149 // only a very small number of bans are defined
150 // this way these days, such as qlines against 
151 // services nicks, etc.
152
153 void read_xline_defaults()
154 {
155         char ipmask[MAXBUF];
156         char nick[MAXBUF];
157         char host[MAXBUF];
158         char reason[MAXBUF];
159
160         for (int i = 0; i < ConfValueEnum("badip",&config_f); i++)
161         {
162                 ConfValue("badip","ipmask",i,ipmask,&config_f);
163                 ConfValue("badip","reason",i,reason,&config_f);
164                 add_zline(0,"<Config>",reason,ipmask);
165                 log(DEBUG,"Read Z line (badip tag): ipmask=%s reason=%s",ipmask,reason);
166         }
167         
168         for (int i = 0; i < ConfValueEnum("badnick",&config_f); i++)
169         {
170                 ConfValue("badnick","nick",i,nick,&config_f);
171                 ConfValue("badnick","reason",i,reason,&config_f);
172                 add_qline(0,"<Config>",reason,nick);
173                 log(DEBUG,"Read Q line (badnick tag): nick=%s reason=%s",nick,reason);
174         }
175         
176         for (int i = 0; i < ConfValueEnum("badhost",&config_f); i++)
177         {
178                 ConfValue("badhost","host",i,host,&config_f);
179                 ConfValue("badhost","reason",i,reason,&config_f);
180                 add_kline(0,"<Config>",reason,host);
181                 log(DEBUG,"Read K line (badhost tag): host=%s reason=%s",host,reason);
182         }
183         for (int i = 0; i < ConfValueEnum("exception",&config_f); i++)
184         {
185                 ConfValue("exception","host",i,host,&config_f);
186                 ConfValue("exception","reason",i,reason,&config_f);
187                 add_eline(0,"<Config>",reason,host);
188                 log(DEBUG,"Read E line (exception tag): host=%s reason=%s",host,reason);
189         }
190 }
191
192 // adds a g:line
193
194 void add_gline(long duration, const char* source,const char* reason,const char* hostmask)
195 {
196         del_gline(hostmask);
197         GLine item;
198         item.duration = duration;
199         strlcpy(item.hostmask,hostmask,199);
200         strlcpy(item.reason,reason,MAXBUF);
201         strlcpy(item.source,source,255);
202         item.n_matches = 0;
203         item.set_time = TIME;
204         if (duration)
205         {
206                 glines.push_back(item);
207                 sort(glines.begin(), glines.end(),GSortComparison);
208         }
209         else
210         {
211                 pglines.push_back(item);
212         }
213 }
214
215 // adds an e:line (exception to bans)
216
217 void add_eline(long duration, const char* source, const char* reason, const char* hostmask)
218 {
219         del_eline(hostmask);
220         ELine item;
221         item.duration = duration;
222         strlcpy(item.hostmask,hostmask,199);
223         strlcpy(item.reason,reason,MAXBUF);
224         strlcpy(item.source,source,255);
225         item.n_matches = 0;
226         item.set_time = TIME;
227         if (duration)
228         {
229                 elines.push_back(item);
230                 sort(elines.begin(), elines.end(),ESortComparison);
231         }
232         else
233         {
234                 pelines.push_back(item);
235         }
236 }
237
238 // adds a q:line
239
240 void add_qline(long duration, const char* source, const char* reason, const char* nickname)
241 {
242         del_qline(nickname);
243         QLine item;
244         item.duration = duration;
245         strlcpy(item.nick,nickname,63);
246         strlcpy(item.reason,reason,MAXBUF);
247         strlcpy(item.source,source,255);
248         item.n_matches = 0;
249         item.is_global = false;
250         item.set_time = TIME;
251         if (duration)
252         {
253                 qlines.push_back(item);
254                 sort(qlines.begin(), qlines.end(),QSortComparison);
255         }
256         else
257         {
258                 pqlines.push_back(item);
259         }
260 }
261
262 // adds a z:line
263
264 void add_zline(long duration, const char* source, const char* reason, const char* ipaddr)
265 {
266         del_zline(ipaddr);
267         ZLine item;
268         item.duration = duration;
269         if (strchr(ipaddr,'@'))
270         {
271                 while (*ipaddr != '@')
272                         ipaddr++;
273                 ipaddr++;
274         }
275         strlcpy(item.ipaddr,ipaddr,39);
276         strlcpy(item.reason,reason,MAXBUF);
277         strlcpy(item.source,source,255);
278         item.n_matches = 0;
279         item.is_global = false;
280         item.set_time = TIME;
281         if (duration)
282         {
283                 zlines.push_back(item);
284                 sort(zlines.begin(), zlines.end(),ZSortComparison);
285         }
286         else
287         {
288                 pzlines.push_back(item);
289         }
290 }
291
292 // adds a k:line
293
294 void add_kline(long duration, const char* source, const char* reason, const char* hostmask)
295 {
296         del_kline(hostmask);
297         KLine item;
298         item.duration = duration;
299         strlcpy(item.hostmask,hostmask,200);
300         strlcpy(item.reason,reason,MAXBUF);
301         strlcpy(item.source,source,255);
302         item.n_matches = 0;
303         item.set_time = TIME;
304         if (duration)
305         {
306                 klines.push_back(item);
307                 sort(klines.begin(), klines.end(),KSortComparison);
308         }
309         else
310         {
311                 pklines.push_back(item);
312         }
313 }
314
315 // deletes a g:line, returns true if the line existed and was removed
316
317 bool del_gline(const char* hostmask)
318 {
319         for (std::vector<GLine>::iterator i = glines.begin(); i != glines.end(); i++)
320         {
321                 if (!strcasecmp(hostmask,i->hostmask))
322                 {
323                         glines.erase(i);
324                         return true;
325                 }
326         }
327         for (std::vector<GLine>::iterator i = pglines.begin(); i != pglines.end(); i++)
328         {
329                 if (!strcasecmp(hostmask,i->hostmask))
330                 {
331                         pglines.erase(i);
332                         return true;
333                 }
334         }
335         return false;
336 }
337
338 // deletes a e:line, returns true if the line existed and was removed
339
340 bool del_eline(const char* hostmask)
341 {
342         for (std::vector<ELine>::iterator i = elines.begin(); i != elines.end(); i++)
343         {
344                 if (!strcasecmp(hostmask,i->hostmask))
345                 {
346                         elines.erase(i);
347                         return true;
348                 }
349         }
350         for (std::vector<ELine>::iterator i = pelines.begin(); i != pelines.end(); i++)
351         {
352                 if (!strcasecmp(hostmask,i->hostmask))
353                 {
354                         pelines.erase(i);
355                         return true;
356                 }
357         }
358         return false;
359 }
360
361 // deletes a q:line, returns true if the line existed and was removed
362
363 bool del_qline(const char* nickname)
364 {
365         for (std::vector<QLine>::iterator i = qlines.begin(); i != qlines.end(); i++)
366         {
367                 if (!strcasecmp(nickname,i->nick))
368                 {
369                         qlines.erase(i);
370                         return true;
371                 }
372         }
373         for (std::vector<QLine>::iterator i = pqlines.begin(); i != pqlines.end(); i++)
374         {
375                 if (!strcasecmp(nickname,i->nick))
376                 {
377                         pqlines.erase(i);
378                         return true;
379                 }
380         }
381         return false;
382 }
383
384 bool qline_make_global(const char* nickname)
385 {
386         for (std::vector<QLine>::iterator i = qlines.begin(); i != qlines.end(); i++)
387         {
388                 if (!strcasecmp(nickname,i->nick))
389                 {
390                         i->is_global = true;
391                         return true;
392                 }
393         }
394         return false;
395 }
396
397 bool zline_make_global(const char* ipaddr)
398 {
399         for (std::vector<ZLine>::iterator i = zlines.begin(); i != zlines.end(); i++)
400         {
401                 if (!strcasecmp(ipaddr,i->ipaddr))
402                 {
403                         i->is_global = true;
404                         return true;
405                 }
406         }
407         return false;
408 }
409
410 // deletes a z:line, returns true if the line existed and was removed
411
412 bool del_zline(const char* ipaddr)
413 {
414         for (std::vector<ZLine>::iterator i = zlines.begin(); i != zlines.end(); i++)
415         {
416                 if (!strcasecmp(ipaddr,i->ipaddr))
417                 {
418                         zlines.erase(i);
419                         return true;
420                 }
421         }
422         for (std::vector<ZLine>::iterator i = pzlines.begin(); i != pzlines.end(); i++)
423         {
424                 if (!strcasecmp(ipaddr,i->ipaddr))
425                 {
426                         pzlines.erase(i);
427                         return true;
428                 }
429         }
430         return false;
431 }
432
433 // deletes a k:line, returns true if the line existed and was removed
434
435 bool del_kline(const char* hostmask)
436 {
437         for (std::vector<KLine>::iterator i = klines.begin(); i != klines.end(); i++)
438         {
439                 if (!strcasecmp(hostmask,i->hostmask))
440                 {
441                         klines.erase(i);
442                         return true;
443                 }
444         }
445         for (std::vector<KLine>::iterator i = pklines.begin(); i != pklines.end(); i++)
446         {
447                 if (!strcasecmp(hostmask,i->hostmask))
448                 {
449                         pklines.erase(i);
450                         return true;
451                 }
452         }
453         return false;
454 }
455
456 // returns a pointer to the reason if a nickname matches a qline, NULL if it didnt match
457
458 char* matches_qline(const char* nick)
459 {
460         if (qlines.empty())
461                 return NULL;
462         for (std::vector<QLine>::iterator i = qlines.begin(); i != qlines.end(); i++)
463                 if (match(nick,i->nick))
464                         return i->reason;
465         for (std::vector<QLine>::iterator i = pqlines.begin(); i != pqlines.end(); i++)
466                 if (match(nick,i->nick))
467                         return i->reason;
468         return NULL;
469 }
470
471 // returns a pointer to the reason if a host matches a gline, NULL if it didnt match
472
473 char* matches_gline(const char* host)
474 {
475         if (glines.empty())
476                 return NULL;
477         for (std::vector<GLine>::iterator i = glines.begin(); i != glines.end(); i++)
478                 if (match(host,i->hostmask))
479                         return i->reason;
480         for (std::vector<GLine>::iterator i = pglines.begin(); i != pglines.end(); i++)
481                 if (match(host,i->hostmask))
482                         return i->reason;
483         return NULL;
484 }
485
486 char* matches_exception(const char* host)
487 {
488         if (elines.empty())
489                 return NULL;
490         char host2[MAXBUF];
491         snprintf(host2,MAXBUF,"*@%s",host);
492         for (std::vector<ELine>::iterator i = elines.begin(); i != elines.end(); i++)
493                 if ((match(host,i->hostmask)) || (match(host2,i->hostmask)))
494                         return i->reason;
495         for (std::vector<ELine>::iterator i = pelines.begin(); i != pelines.end(); i++)
496                 if ((match(host,i->hostmask)) || (match(host2,i->hostmask)))
497                         return i->reason;
498         return NULL;
499 }
500
501
502 void gline_set_creation_time(char* host, time_t create_time)
503 {
504         for (std::vector<GLine>::iterator i = glines.begin(); i != glines.end(); i++)
505         {
506                 if (!strcasecmp(host,i->hostmask))
507                 {
508                         i->set_time = create_time;
509                         return;
510                 }
511         }
512         for (std::vector<GLine>::iterator i = pglines.begin(); i != pglines.end(); i++)
513         {
514                 if (!strcasecmp(host,i->hostmask))
515                 {
516                         i->set_time = create_time;
517                         return;
518                 }
519         }
520         return ;        
521 }
522
523 void eline_set_creation_time(char* host, time_t create_time)
524 {
525         for (std::vector<ELine>::iterator i = elines.begin(); i != elines.end(); i++)
526         {
527                 if (!strcasecmp(host,i->hostmask))
528                 {
529                         i->set_time = create_time;
530                         return;
531                 }
532         }
533         for (std::vector<ELine>::iterator i = pelines.begin(); i != pelines.end(); i++) 
534         {
535                 if (!strcasecmp(host,i->hostmask))
536                 {
537                         i->set_time = create_time;
538                         return;
539                 }
540         }
541         return;
542 }
543
544 void qline_set_creation_time(char* nick, time_t create_time)
545 {
546         for (std::vector<QLine>::iterator i = qlines.begin(); i != qlines.end(); i++)
547         {
548                 if (!strcasecmp(nick,i->nick))
549                 {
550                         i->set_time = create_time;
551                         return;
552                 }
553         }
554         for (std::vector<QLine>::iterator i = pqlines.begin(); i != pqlines.end(); i++)
555         {
556                 if (!strcasecmp(nick,i->nick))
557                 {
558                         i->set_time = create_time;
559                         return;
560                 }
561         }
562         return;
563 }
564
565 void zline_set_creation_time(char* ip, time_t create_time)
566 {
567         for (std::vector<ZLine>::iterator i = zlines.begin(); i != zlines.end(); i++)
568         {
569                 if (!strcasecmp(ip,i->ipaddr))
570                 {
571                         i->set_time = create_time;
572                         return;
573                 }
574         }
575         for (std::vector<ZLine>::iterator i = pzlines.begin(); i != pzlines.end(); i++)
576         {
577                 if (!strcasecmp(ip,i->ipaddr))
578                 {
579                         i->set_time = create_time;
580                         return;
581                 }
582         }
583         return;
584 }
585
586 // returns a pointer to the reason if an ip address matches a zline, NULL if it didnt match
587
588 char* matches_zline(const char* ipaddr)
589 {
590         if (zlines.empty())
591                 return NULL;
592         for (std::vector<ZLine>::iterator i = zlines.begin(); i != zlines.end(); i++)
593                 if (match(ipaddr,i->ipaddr))
594                         return i->reason;
595         for (std::vector<ZLine>::iterator i = pzlines.begin(); i != pzlines.end(); i++)
596                 if (match(ipaddr,i->ipaddr))
597                         return i->reason;
598         return NULL;
599 }
600
601 // returns a pointer to the reason if a host matches a kline, NULL if it didnt match
602
603 char* matches_kline(const char* host)
604 {
605         if (klines.empty())
606                 return NULL;
607         for (std::vector<KLine>::iterator i = klines.begin(); i != klines.end(); i++)
608                 if (match(host,i->hostmask))
609                         return i->reason;
610         for (std::vector<KLine>::iterator i = pklines.begin(); i != pklines.end(); i++)
611                 if (match(host,i->hostmask))
612                         return i->reason;
613         return NULL;
614 }
615
616 bool GSortComparison ( const GLine one, const GLine two )
617 {
618         return (one.duration + one.set_time) < (two.duration + two.set_time);
619 }
620
621 bool ESortComparison ( const ELine one, const ELine two )
622 {
623         return (one.duration + one.set_time) < (two.duration + two.set_time);
624 }
625
626 bool ZSortComparison ( const ZLine one, const ZLine two )
627 {
628         return (one.duration + one.set_time) < (two.duration + two.set_time);
629 }
630
631 bool KSortComparison ( const KLine one, const KLine two )
632 {
633         return (one.duration + one.set_time) < (two.duration + two.set_time);
634 }
635
636 bool QSortComparison ( const QLine one, const QLine two )
637 {
638         return (one.duration + one.set_time) < (two.duration + two.set_time);
639 }
640
641 // removes lines that have expired
642
643 void expire_lines()
644 {
645         time_t current = TIME;
646
647         /* Because we now store all our XLines in sorted order using (i->duration + i->set_time) as a key, this
648          * means that to expire the XLines we just need to do a while, picking off the top few until there are
649          * none left at the head of the queue that are after the current time.
650          */
651
652         while ((glines.size()) && (current > (glines.begin()->duration + glines.begin()->set_time)))
653         {
654                 std::vector<GLine>::iterator i = glines.begin();
655                 WriteOpers("Expiring timed G-Line %s (set by %s %d seconds ago)",i->hostmask,i->source,i->duration);
656                 glines.erase(i);
657         }
658
659         while ((elines.size()) && (current > (elines.begin()->duration + elines.begin()->set_time)))
660         {
661                 std::vector<ELine>::iterator i = elines.begin();
662                 WriteOpers("Expiring timed E-Line %s (set by %s %d seconds ago)",i->hostmask,i->source,i->duration);
663                 elines.erase(i);
664         }
665
666         while ((zlines.size()) && (current > (zlines.begin()->duration + zlines.begin()->set_time)))
667         {
668                 std::vector<ZLine>::iterator i = zlines.begin();
669                 WriteOpers("Expiring timed Z-Line %s (set by %s %d seconds ago)",i->ipaddr,i->source,i->duration);
670                 zlines.erase(i);
671         }
672
673         while ((klines.size()) && (current > (klines.begin()->duration + klines.begin()->set_time)))
674         {
675                 std::vector<KLine>::iterator i = klines.begin();
676                 WriteOpers("Expiring timed K-Line %s (set by %s %d seconds ago)",i->hostmask,i->source,i->duration);
677                 klines.erase(i);
678         }
679
680         while ((qlines.size()) && (current > (qlines.begin()->duration + qlines.begin()->set_time)))
681         {
682                 std::vector<QLine>::iterator i = qlines.begin();
683                 WriteOpers("Expiring timed Q-Line %s (set by %s %d seconds ago)",i->nick,i->source,i->duration);
684                 qlines.erase(i);
685         }
686         
687 }
688
689 // applies lines, removing clients and changing nicks etc as applicable
690
691 void apply_lines()
692 {
693         bool go_again = true;
694         char reason[MAXBUF];
695         char host[MAXBUF];
696         
697         if ((!glines.size()) && (!klines.size()) && (!zlines.size()) && (!qlines.size()))
698                 return;
699         
700         while (go_again)
701         {
702                 go_again = false;
703                 for (user_hash::const_iterator u = clientlist.begin(); u != clientlist.end(); u++)
704                 {
705                         if (!strcasecmp(u->second->server,ServerName))
706                         {
707                                 snprintf(host,MAXBUF,"%s@%s",u->second->ident,u->second->host);
708                                 if (elines.size())
709                                 {
710                                         // ignore people matching exempts
711                                         if (matches_exception(host))
712                                                 continue;
713                                 }
714                                 if (glines.size() || pglines.size())
715                                 {
716                                         char* check = matches_gline(host);
717                                         if (check)
718                                         {
719                                                 WriteOpers("*** User %s matches G-Line: %s",u->second->registered == 7 ? u->second->nick:"<unknown>",check);
720                                                 snprintf(reason,MAXBUF,"G-Lined: %s",check);
721                                                 kill_link(u->second,reason);
722                                                 go_again = true;
723                                                 break;
724                                         }
725                                 }
726                                 if (klines.size() || pklines.size())
727                                 {
728                                         char* check = matches_kline(host);
729                                         if (check)
730                                         {
731                                                 WriteOpers("*** User %s matches K-Line: %s",u->second->registered == 7 ? u->second->nick:"<unknown>",check);
732                                                 snprintf(reason,MAXBUF,"K-Lined: %s",check);
733                                                 kill_link(u->second,reason);
734                                                 go_again = true;
735                                                 break;
736                                         }
737                                 }
738                                 if (qlines.size() || pqlines.size())
739                                 {
740                                         char* check = matches_qline(u->second->nick);
741                                         if (check)
742                                         {
743                                                 snprintf(reason,MAXBUF,"Matched Q-Lined nick: %s",check);
744                                                 WriteOpers("*** Q-Lined nickname %s from %s: %s",u->second->registered == 7 ? u->second->nick:"<unknown>",u->second->host,check);
745                                                 kill_link(u->second,reason);
746                                                 go_again = true;
747                                                 break;
748                                         }
749                                 }
750                                 if (zlines.size() || pzlines.size())
751                                 {
752                                         char* check = matches_zline(u->second->ip);
753                                         if (check)
754                                         {
755                                                 snprintf(reason,MAXBUF,"Z-Lined: %s",check);
756                                                 WriteOpers("*** User %s matches Z-Line: %s",u->second->registered == 7 ? u->second->nick:"<unknown>",u->second->host,check);
757                                                 kill_link(u->second,reason);
758                                                 go_again = true;
759                                                 break;
760                                         }
761                                 }
762                         }
763                 }
764         }
765 }
766
767 void stats_k(userrec* user)
768 {
769         for (std::vector<KLine>::iterator i = klines.begin(); i != klines.end(); i++)
770                 WriteServ(user->fd,"216 %s :%s %d %d %s %s",user->nick,i->hostmask,i->set_time,i->duration,i->source,i->reason);
771         for (std::vector<KLine>::iterator i = pklines.begin(); i != pklines.end(); i++)
772                 WriteServ(user->fd,"216 %s :%s %d %d %s %s",user->nick,i->hostmask,i->set_time,i->duration,i->source,i->reason);
773 }
774
775 void stats_g(userrec* user)
776 {
777         for (std::vector<GLine>::iterator i = glines.begin(); i != glines.end(); i++)
778                 WriteServ(user->fd,"223 %s :%s %d %d %s %s",user->nick,i->hostmask,i->set_time,i->duration,i->source,i->reason);
779         for (std::vector<GLine>::iterator i = pglines.begin(); i != pglines.end(); i++)
780                 WriteServ(user->fd,"223 %s :%s %d %d %s %s",user->nick,i->hostmask,i->set_time,i->duration,i->source,i->reason);
781 }
782
783 void stats_q(userrec* user)
784 {
785         for (std::vector<QLine>::iterator i = qlines.begin(); i != qlines.end(); i++)
786                 WriteServ(user->fd,"217 %s :%s %d %d %s %s",user->nick,i->nick,i->set_time,i->duration,i->source,i->reason);
787         for (std::vector<QLine>::iterator i = pqlines.begin(); i != pqlines.end(); i++)
788                 WriteServ(user->fd,"217 %s :%s %d %d %s %s",user->nick,i->nick,i->set_time,i->duration,i->source,i->reason);
789 }
790
791 void stats_z(userrec* user)
792 {
793         for (std::vector<ZLine>::iterator i = zlines.begin(); i != zlines.end(); i++)
794                 WriteServ(user->fd,"223 %s :%s %d %d %s %s",user->nick,i->ipaddr,i->set_time,i->duration,i->source,i->reason);
795         for (std::vector<ZLine>::iterator i = pzlines.begin(); i != pzlines.end(); i++)
796                 WriteServ(user->fd,"223 %s :%s %d %d %s %s",user->nick,i->ipaddr,i->set_time,i->duration,i->source,i->reason);
797 }
798
799 void stats_e(userrec* user)
800 {
801         for (std::vector<ELine>::iterator i = elines.begin(); i != elines.end(); i++)
802                 WriteServ(user->fd,"223 %s :%s %d %d %s %s",user->nick,i->hostmask,i->set_time,i->duration,i->source,i->reason);
803         for (std::vector<ELine>::iterator i = pelines.begin(); i != pelines.end(); i++)
804                 WriteServ(user->fd,"223 %s :%s %d %d %s %s",user->nick,i->hostmask,i->set_time,i->duration,i->source,i->reason);
805 }