]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/helperfuncs.cpp
c1a53b908614115108184a90ce6a342e6111fe10
[user/henk/code/inspircd.git] / src / helperfuncs.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 <unistd.h>
23 #include <fcntl.h>
24 #include <sys/errno.h>
25 #include <time.h>
26 #include <string>
27 #ifdef GCC3
28 #include <ext/hash_map>
29 #else
30 #include <hash_map>
31 #endif
32 #include <sstream>
33 #include <vector>
34 #include <deque>
35 #include <stdarg.h>
36 #include "connection.h"
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 "mode.h"
45 #include "xline.h"
46 #include "commands.h"
47 #include "inspstring.h"
48 #include "helperfuncs.h"
49 #include "hashcomp.h"
50 #include "typedefs.h"
51
52 extern int MODCOUNT;
53 extern std::vector<Module*> modules;
54 extern ServerConfig *Config;
55 extern InspIRCd* ServerInstance;
56 extern time_t TIME;
57 extern char lowermap[255];
58 static char list[MAXBUF];
59 extern userrec* fd_ref_table[65536];
60 static char already_sent[65536];
61 extern std::vector<userrec*> all_opers;
62 extern user_hash clientlist;
63 extern chan_hash chanlist;
64
65 void log(int level,char *text, ...)
66 {
67         va_list argsPtr;
68         struct tm * timeinfo;
69         if (level < Config->LogLevel)
70                 return;
71         char textbuffer[MAXBUF];
72         timeinfo = localtime(&TIME);
73
74         if (Config->log_file)
75         {
76                 char b[MAXBUF];
77                 va_start (argsPtr, text);
78                 vsnprintf(textbuffer, MAXBUF, text, argsPtr);
79                 va_end(argsPtr);
80                 strlcpy(b,asctime(timeinfo),MAXBUF);
81                 b[24] = ':';    // we know this is the end of the time string
82                 if (Config->log_file)
83                         fprintf(Config->log_file,"%s %s\n",b,textbuffer);
84                 if (Config->nofork)
85                 {
86                         // nofork enabled? display it on terminal too
87                         printf("%s %s\n",b,textbuffer);
88                 }
89         }
90 }
91
92 void readfile(file_cache &F, const char* fname)
93 {
94         FILE* file;
95         char linebuf[MAXBUF];
96
97         log(DEBUG,"readfile: loading %s",fname);
98         F.clear();
99         file =  fopen(fname,"r");
100         if (file)
101         {
102                 while (!feof(file))
103                 {
104                         fgets(linebuf,sizeof(linebuf),file);
105                         linebuf[strlen(linebuf)-1]='\0';
106                         if (!*linebuf)
107                         {
108                                 strcpy(linebuf,"  ");
109                         }
110                         if (!feof(file))
111                         {
112                                 F.push_back(linebuf);
113                         }
114                 }
115                 fclose(file);
116         }
117         else
118         {
119                 log(DEBUG,"readfile: failed to load file: %s",fname);
120         }
121         log(DEBUG,"readfile: loaded %s, %lu lines",fname,(unsigned long)F.size());
122 }
123
124 void Write(int sock,char *text, ...)
125 {
126         if (sock < 0)
127                 return;
128         if (!text)
129         {
130                 log(DEFAULT,"*** BUG *** Write was given an invalid parameter");
131                 return;
132         }
133         log(DEBUG,"Write: Normal");
134         va_list argsPtr;
135         char textbuffer[MAXBUF],tb[MAXBUF];
136         va_start (argsPtr, text);
137         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
138         va_end(argsPtr);
139         int bytes = snprintf(tb,MAXBUF,"%s\r\n",textbuffer);
140         chop(tb);
141         if (fd_ref_table[sock])
142         {
143                 if (Config->GetIOHook(fd_ref_table[sock]->port))
144                 {
145                         log(DEBUG,"Write: IO Hooked");
146                         Config->GetIOHook(fd_ref_table[sock]->port)->OnRawSocketWrite(sock,tb,bytes);
147                 }
148                 else
149                 {
150                         fd_ref_table[sock]->AddWriteBuf(tb);
151                 }
152                 ServerInstance->stats->statsSent += bytes;
153         }
154         else log(DEFAULT,"ERROR! attempted write to a user with no fd_ref_table entry!!!");
155 }
156
157 /* write a server formatted numeric response to a single socket */
158
159 void WriteServ(int sock, char* text, ...)
160 {
161         if (sock < 0)
162                 return;
163         if (!text)
164         {
165                 log(DEFAULT,"*** BUG *** WriteServ was given an invalid parameter");
166                 return;
167         }
168         log(DEBUG,"WriteServ: normal");
169         va_list argsPtr;
170         va_start (argsPtr, text);
171         char textbuffer[MAXBUF],tb[MAXBUF];
172         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
173         va_end(argsPtr);
174         int bytes = snprintf(tb,MAXBUF,":%s %s\r\n",Config->ServerName,textbuffer);
175         chop(tb);
176         if (fd_ref_table[sock])
177         {
178                 if (Config->GetIOHook(fd_ref_table[sock]->port))
179                 {
180                         log(DEBUG,"WriteServ: IO Hooked");
181                         Config->GetIOHook(fd_ref_table[sock]->port)->OnRawSocketWrite(sock,tb,bytes);
182                 }
183                 else
184                 {
185                         fd_ref_table[sock]->AddWriteBuf(tb);
186                 }
187                 ServerInstance->stats->statsSent += bytes;
188         }
189         else log(DEFAULT,"ERROR! attempted write to a user with no fd_ref_table entry!!!");
190 }
191
192 /* write text from an originating user to originating user */
193
194 void WriteFrom(int sock, userrec *user,char* text, ...)
195 {
196         if (sock < 0)
197                 return;
198         if ((!text) || (!user))
199         {
200                 log(DEFAULT,"*** BUG *** WriteFrom was given an invalid parameter");
201                 return;
202         }
203         log(DEBUG,"WriteFrom: normal");
204         va_list argsPtr;
205         va_start (argsPtr, text);
206         char textbuffer[MAXBUF],tb[MAXBUF];
207         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
208         va_end(argsPtr);
209         int bytes = snprintf(tb,MAXBUF,":%s!%s@%s %s\r\n",user->nick,user->ident,user->dhost,textbuffer);
210         chop(tb);
211         if (fd_ref_table[sock])
212         {
213                 if (Config->GetIOHook(fd_ref_table[sock]->port))
214                 {
215                         log(DEBUG,"WriteFrom: IO hooked");
216                         Config->GetIOHook(fd_ref_table[sock]->port)->OnRawSocketWrite(sock,tb,bytes);
217                 }
218                 else
219                 {
220                         fd_ref_table[sock]->AddWriteBuf(tb);
221                 }
222                 ServerInstance->stats->statsSent += bytes;
223         }
224         else log(DEFAULT,"ERROR! attempted write to a user with no fd_ref_table entry!!!");
225 }
226
227 /* write text to an destination user from a source user (e.g. user privmsg) */
228
229 void WriteTo(userrec *source, userrec *dest,char *data, ...)
230 {
231         if ((!dest) || (!data))
232         {
233                 log(DEFAULT,"*** BUG *** WriteTo was given an invalid parameter");
234                 return;
235         }
236         if (dest->fd == FD_MAGIC_NUMBER)
237                 return;
238         char textbuffer[MAXBUF],tb[MAXBUF];
239         va_list argsPtr;
240         va_start (argsPtr, data);
241         vsnprintf(textbuffer, MAXBUF, data, argsPtr);
242         va_end(argsPtr);
243         chop(tb);
244
245         // if no source given send it from the server.
246         if (!source)
247         {
248                 WriteServ(dest->fd,":%s %s",Config->ServerName,textbuffer);
249         }
250         else
251         {
252                 WriteFrom(dest->fd,source,"%s",textbuffer);
253         }
254 }
255
256 /* write formatted text from a source user to all users on a channel
257  * including the sender (NOT for privmsg, notice etc!) */
258
259 void WriteChannel(chanrec* Ptr, userrec* user, char* text, ...)
260 {
261         if ((!Ptr) || (!user) || (!text))
262         {
263                 log(DEFAULT,"*** BUG *** WriteChannel was given an invalid parameter");
264                 return;
265         }
266         char textbuffer[MAXBUF];
267         va_list argsPtr;
268         va_start (argsPtr, text);
269         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
270         va_end(argsPtr);
271
272         std::vector<char*> *ulist = Ptr->GetUsers();
273         unsigned int x = ulist->size();
274         for (unsigned int j = 0; j < x; j++)
275         {
276                 char* o = (*ulist)[j];
277                 userrec* otheruser = (userrec*)o;
278                 if (otheruser->fd != FD_MAGIC_NUMBER)
279                         WriteTo(user,otheruser,"%s",textbuffer);
280         }
281 }
282
283 /* write formatted text from a source user to all users on a channel
284  * including the sender (NOT for privmsg, notice etc!) doesnt send to
285  * users on remote servers */
286
287 void WriteChannelLocal(chanrec* Ptr, userrec* user, char* text, ...)
288 {
289         if ((!Ptr) || (!text))
290         {
291                 log(DEFAULT,"*** BUG *** WriteChannel was given an invalid parameter");
292                 return;
293         }
294         char textbuffer[MAXBUF];
295         va_list argsPtr;
296         va_start (argsPtr, text);
297         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
298         va_end(argsPtr);
299
300         std::vector<char*> *ulist = Ptr->GetUsers();
301         unsigned int x = ulist->size();
302         for (unsigned int j = 0; j < x; j++)
303         {
304                 char* o = (*ulist)[j];
305                 userrec* otheruser = (userrec*)o;
306                 if ((otheruser->fd != FD_MAGIC_NUMBER) && (otheruser->fd != -1) && (otheruser != user))
307                 {
308                         if (!user)
309                         {
310                                 WriteServ(otheruser->fd,"%s",textbuffer);
311                         }
312                         else
313                         {
314                                 WriteTo(user,otheruser,"%s",textbuffer);
315                         }
316                 }
317         }
318 }
319
320 void WriteChannelWithServ(char* ServName, chanrec* Ptr, char* text, ...)
321 {
322         if ((!Ptr) || (!text))
323         {
324                 log(DEFAULT,"*** BUG *** WriteChannelWithServ was given an invalid parameter");
325                 return;
326         }
327         char textbuffer[MAXBUF];
328         va_list argsPtr;
329         va_start (argsPtr, text);
330         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
331         va_end(argsPtr);
332
333
334         std::vector<char*> *ulist = Ptr->GetUsers();
335         unsigned int x = ulist->size();
336         for (unsigned int j = 0; j < x; j++)
337         {
338                 char* o = (*ulist)[j];
339                 userrec* otheruser = (userrec*)o;
340                 if (otheruser->fd != FD_MAGIC_NUMBER)
341                         WriteServ(otheruser->fd,"%s",textbuffer);
342         }
343 }
344
345 /* write formatted text from a source user to all users on a channel except
346  * for the sender (for privmsg etc) */
347
348 void ChanExceptSender(chanrec* Ptr, userrec* user, char* text, ...)
349 {
350         if ((!Ptr) || (!user) || (!text))
351         {
352                 log(DEFAULT,"*** BUG *** ChanExceptSender was given an invalid parameter");
353                 return;
354         }
355         char textbuffer[MAXBUF];
356         va_list argsPtr;
357         va_start (argsPtr, text);
358         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
359         va_end(argsPtr);
360
361         std::vector<char*> *ulist = Ptr->GetUsers();
362         unsigned int x = ulist->size();
363         for (unsigned int j = 0; j < x; j++)
364         {
365                 char* o = (*ulist)[j];
366                 userrec* otheruser = (userrec*)o;
367                 if ((otheruser->fd != FD_MAGIC_NUMBER) && (user != otheruser))
368                         WriteFrom(otheruser->fd,user,"%s",textbuffer);
369         }
370 }
371
372 std::string GetServerDescription(char* servername)
373 {
374         std::string description = "";
375         FOREACH_MOD OnGetServerDescription(servername,description);
376         if (description != "")
377         {
378                 return description;
379         }
380         else
381         {
382                 return Config->ServerDesc; // not a remote server that can be found, it must be me.
383         }
384 }
385
386 /* write a formatted string to all users who share at least one common
387  * channel, including the source user e.g. for use in NICK */
388
389 void WriteCommon(userrec *u, char* text, ...)
390 {
391         if (!u)
392         {
393                 log(DEFAULT,"*** BUG *** WriteCommon was given an invalid parameter");
394                 return;
395         }
396
397         if (u->registered != 7) {
398                 log(DEFAULT,"*** BUG *** WriteCommon on an unregistered user");
399                 return;
400         }
401
402         char textbuffer[MAXBUF];
403         va_list argsPtr;
404         va_start (argsPtr, text);
405         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
406         va_end(argsPtr);
407
408         // FIX: Stops a message going to the same person more than once
409         memset(&already_sent,0,MAXCLIENTS);
410
411         bool sent_to_at_least_one = false;
412
413         unsigned int y = u->chans.size();
414         for (unsigned int i = 0; i < y; i++)
415         {
416                 if (u->chans[i].channel)
417                 {
418                         std::vector<char*> *ulist = u->chans[i].channel->GetUsers();
419                         unsigned int x = ulist->size();
420                         for (unsigned int j = 0; j < x; j++)
421                         {
422                                 char* o = (*ulist)[j];
423                                 userrec* otheruser = (userrec*)o;
424                                 if ((otheruser->fd > -1) && (!already_sent[otheruser->fd]))
425                                 {
426                                         already_sent[otheruser->fd] = 1;
427                                         WriteFrom(otheruser->fd,u,"%s",textbuffer);
428                                         sent_to_at_least_one = true;
429                                 }
430                         }
431                 }
432         }
433         // if the user was not in any channels, no users will receive the text. Make sure the user
434         // receives their OWN message for WriteCommon
435         if (!sent_to_at_least_one)
436         {
437                 WriteFrom(u->fd,u,"%s",textbuffer);
438         }
439 }
440
441 /* write a formatted string to all users who share at least one common
442  * channel, NOT including the source user e.g. for use in QUIT */
443
444 void WriteCommonExcept(userrec *u, char* text, ...)
445 {
446         if (!u)
447         {
448                 log(DEFAULT,"*** BUG *** WriteCommon was given an invalid parameter");
449                 return;
450         }
451
452         if (u->registered != 7) {
453                 log(DEFAULT,"*** BUG *** WriteCommon on an unregistered user");
454                 return;
455         }
456
457         char textbuffer[MAXBUF];
458         va_list argsPtr;
459         va_start (argsPtr, text);
460         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
461         va_end(argsPtr);
462
463         memset(&already_sent,0,MAXCLIENTS);
464
465         unsigned int y = u->chans.size();
466         for (unsigned int i = 0; i < y; i++)
467         {
468                 if (u->chans[i].channel)
469                 {
470                         std::vector<char*> *ulist = u->chans[i].channel->GetUsers();
471                         unsigned int x = ulist->size();
472                         for (unsigned int j = 0; j < x; j++)
473                         {
474                                 char* o = (*ulist)[j];
475                                 userrec* otheruser = (userrec*)o;
476                                 if (u != otheruser)
477                                 {
478                                         if ((otheruser->fd > -1) && (!already_sent[otheruser->fd]))
479                                         {
480                                                 already_sent[otheruser->fd] = 1;
481                                                 WriteFrom(otheruser->fd,u,"%s",textbuffer);
482                                         }
483                                 }
484                         }
485                 }
486         }
487 }
488
489 void WriteOpers(char* text, ...)
490 {
491         if (!text)
492         {
493                 log(DEFAULT,"*** BUG *** WriteOpers was given an invalid parameter");
494                 return;
495         }
496
497         char textbuffer[MAXBUF];
498         va_list argsPtr;
499         va_start (argsPtr, text);
500         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
501         va_end(argsPtr);
502
503         for (std::vector<userrec*>::iterator i = all_opers.begin(); i != all_opers.end(); i++)
504         {
505                 userrec* a = *i;
506                 if ((a) && (a->fd != FD_MAGIC_NUMBER))
507                 {
508                         if (strchr(a->modes,'s'))
509                         {
510                                 // send server notices to all with +s
511                                 WriteServ(a->fd,"NOTICE %s :%s",a->nick,textbuffer);
512                         }
513                 }
514         }
515 }
516
517 void ServerNoticeAll(char* text, ...)
518 {
519         if (!text)
520                 return;
521
522         char textbuffer[MAXBUF];
523         va_list argsPtr;
524         va_start (argsPtr, text);
525         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
526         va_end(argsPtr);
527
528         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
529         {
530                 if ((i->second) && (i->second->fd != FD_MAGIC_NUMBER))
531                 {
532                         WriteServ(i->second->fd,"NOTICE $%s :%s",Config->ServerName,textbuffer);
533                 }
534         }
535 }
536
537 void ServerPrivmsgAll(char* text, ...)
538 {
539         if (!text)
540                 return;
541
542         char textbuffer[MAXBUF];
543         va_list argsPtr;
544         va_start (argsPtr, text);
545         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
546         va_end(argsPtr);
547
548         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
549         {
550                 if ((i->second) && (i->second->fd != FD_MAGIC_NUMBER))
551                 {
552                         WriteServ(i->second->fd,"PRIVMSG $%s :%s",Config->ServerName,textbuffer);
553                 }
554         }
555 }
556
557 void WriteMode(const char* modes, int flags, const char* text, ...)
558 {
559         if ((!text) || (!modes) || (!flags))
560         {
561                 log(DEFAULT,"*** BUG *** WriteMode was given an invalid parameter");
562                 return;
563         }
564
565         char textbuffer[MAXBUF];
566         va_list argsPtr;
567         va_start (argsPtr, text);
568         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
569         va_end(argsPtr);
570         int modelen = strlen(modes);
571
572         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
573         {
574                 if ((i->second) && (i->second->fd != FD_MAGIC_NUMBER))
575                 {
576                         bool send_to_user = false;
577
578                         if (flags == WM_AND)
579                         {
580                                 send_to_user = true;
581                                 for (int n = 0; n < modelen; n++)
582                                 {
583                                         if (!hasumode(i->second,modes[n]))
584                                         {
585                                                 send_to_user = false;
586                                                 break;
587                                         }
588                                 }
589                         }
590                         else if (flags == WM_OR)
591                         {
592                                 send_to_user = false;
593                                 for (int n = 0; n < modelen; n++)
594                                 {
595                                         if (hasumode(i->second,modes[n]))
596                                         {
597                                                 send_to_user = true;
598                                                 break;
599                                         }
600                                 }
601                         }
602
603                         if (send_to_user)
604                         {
605                                 WriteServ(i->second->fd,"NOTICE %s :%s",i->second->nick,textbuffer);
606                         }
607                 }
608         }
609 }
610
611 void NoticeAll(userrec *source, bool local_only, char* text, ...)
612 {
613         if ((!text) || (!source))
614         {
615                 log(DEFAULT,"*** BUG *** NoticeAll was given an invalid parameter");
616                 return;
617         }
618
619         char textbuffer[MAXBUF];
620         va_list argsPtr;
621         va_start (argsPtr, text);
622         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
623         va_end(argsPtr);
624
625         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
626         {
627                 if ((i->second) && (i->second->fd != FD_MAGIC_NUMBER))
628                 {
629                         WriteFrom(i->second->fd,source,"NOTICE $* :%s",textbuffer);
630                 }
631         }
632
633 }
634
635
636 void WriteWallOps(userrec *source, bool local_only, char* text, ...)
637 {
638         if ((!text) || (!source))
639         {
640                 log(DEFAULT,"*** BUG *** WriteOpers was given an invalid parameter");
641                 return;
642         }
643
644         char textbuffer[MAXBUF];
645         va_list argsPtr;
646         va_start (argsPtr, text);
647         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
648         va_end(argsPtr);
649
650         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
651         {
652                 if ((i->second) && (i->second->fd != FD_MAGIC_NUMBER))
653                 {
654                         if (strchr(i->second->modes,'w'))
655                         {
656                                 WriteTo(source,i->second,"WALLOPS :%s",textbuffer);
657                         }
658                 }
659         }
660 }
661
662 /* convert a string to lowercase. Note following special circumstances
663  * taken from RFC 1459. Many "official" server branches still hold to this
664  * rule so i will too;
665  *
666  *  Because of IRC's scandanavian origin, the characters {}| are
667  *  considered to be the lower case equivalents of the characters []\,
668  *  respectively. This is a critical issue when determining the
669  *  equivalence of two nicknames.
670  */
671
672 void strlower(char *n)
673 {
674         if (n)
675         {
676                 for (char* t = n; *t; t++)
677                         *t = lowermap[(unsigned)*t];
678         }
679 }
680
681 /* Find a user record by nickname and return a pointer to it */
682
683 userrec* Find(std::string nick)
684 {
685         user_hash::iterator iter = clientlist.find(nick);
686
687         if (iter == clientlist.end())
688                 /* Couldn't find it */
689                 return NULL;
690
691         return iter->second;
692 }
693
694 /* find a channel record by channel name and return a pointer to it */
695
696 chanrec* FindChan(const char* chan)
697 {
698         if (!chan)
699         {
700                 log(DEFAULT,"*** BUG *** Findchan was given an invalid parameter");
701                 return NULL;
702         }
703
704         chan_hash::iterator iter = chanlist.find(chan);
705
706         if (iter == chanlist.end())
707                 /* Couldn't find it */
708                 return NULL;
709
710         return iter->second;
711 }
712
713
714 long GetMaxBans(char* name)
715 {
716         char CM[MAXBUF];
717         for (int count = 0; count < Config->ConfValueEnum("banlist",&Config->config_f); count++)
718         {
719                 Config->ConfValue("banlist","chan",count,CM,&Config->config_f);
720                 if (match(name,CM))
721                 {
722                         Config->ConfValue("banlist","limit",count,CM,&Config->config_f);
723                         return atoi(CM);
724                 }
725         }
726         return 64;
727 }
728
729 void purge_empty_chans(userrec* u)
730 {
731
732         int purge = 0;
733
734         // firstly decrement the count on each channel
735         for (unsigned int f = 0; f < u->chans.size(); f++)
736         {
737                 if (u->chans[f].channel)
738                 {
739                         u->chans[f].channel->DelUser((char*)u);
740                 }
741         }
742
743         for (unsigned int i = 0; i < u->chans.size(); i++)
744         {
745                 if (u->chans[i].channel)
746                 {
747                         if (!usercount(u->chans[i].channel))
748                         {
749                                 chan_hash::iterator i2 = chanlist.find(u->chans[i].channel->name);
750                                 /* kill the record */
751                                 if (i2 != chanlist.end())
752                                 {
753                                         log(DEBUG,"del_channel: destroyed: %s",i2->second->name);
754                                         if (i2->second)
755                                                 delete i2->second;
756                                         chanlist.erase(i2);
757                                         purge++;
758                                         u->chans[i].channel = NULL;
759                                 }
760                         }
761                         else
762                         {
763                                 log(DEBUG,"skipped purge for %s",u->chans[i].channel->name);
764                         }
765                 }
766         }
767         log(DEBUG,"completed channel purge, killed %lu",(unsigned long)purge);
768
769         DeleteOper(u);
770 }
771
772
773 char* chanmodes(chanrec *chan)
774 {
775         static char scratch[MAXBUF];
776         static char sparam[MAXBUF];
777         char* offset = scratch;
778
779         if (!chan)
780         {
781                 log(DEFAULT,"*** BUG *** chanmodes was given an invalid parameter");
782                 *scratch = '\0';
783                 return scratch;
784         }
785
786         *scratch = '\0';
787         *sparam = '\0';
788         if (chan->binarymodes & CM_NOEXTERNAL)
789                 *offset++ = 'n';
790         if (chan->binarymodes & CM_TOPICLOCK)
791                 *offset++ = 't';
792         if (*chan->key)
793                 *offset++ = 'k';
794         if (chan->limit)
795                 *offset++ = 'l';
796         if (chan->binarymodes & CM_INVITEONLY)
797                 *offset++ = 'i';
798         if (chan->binarymodes & CM_MODERATED)
799                 *offset++ = 'm';
800         if (chan->binarymodes & CM_SECRET)
801                 *offset++ = 's';
802         if (chan->binarymodes & CM_PRIVATE)
803                 *offset++ = 'p';
804         if (*chan->key)
805                 snprintf(sparam,MAXBUF," %s",chan->key);
806         if (chan->limit)
807         {
808                 char foo[24];
809                 sprintf(foo," %lu",(unsigned long)chan->limit);
810                 strlcat(sparam,foo,MAXBUF);
811         }
812         if (*chan->custom_modes)
813         {
814                 for (char* t = chan->custom_modes; *t; t++)
815                         *offset++ = *t;
816                 for (int z = 0; chan->custom_modes[z]; z++)
817                 {
818                         std::string extparam = chan->GetModeParameter(chan->custom_modes[z]);
819                         if (extparam != "")
820                         {
821                                 strlcat(sparam," ",MAXBUF);
822                                 strlcat(sparam,extparam.c_str(),MAXBUF);
823                         }
824                 }
825         }
826         /* Null terminate scratch */
827         *offset = '\0';
828         strlcat(scratch,sparam,MAXMODES);
829         return scratch;
830 }
831
832
833 /* compile a userlist of a channel into a string, each nick seperated by
834  * spaces and op, voice etc status shown as @ and + */
835
836 void userlist(userrec *user,chanrec *c)
837 {
838         if ((!c) || (!user))
839         {
840                 log(DEFAULT,"*** BUG *** userlist was given an invalid parameter");
841                 return;
842         }
843
844         snprintf(list,MAXBUF,"353 %s = %s :", user->nick, c->name);
845
846         std::vector<char*> *ulist = c->GetUsers();
847         for (unsigned int i = 0; i < ulist->size(); i++)
848         {
849                 char* o = (*ulist)[i];
850                 userrec* otheruser = (userrec*)o;
851                 if ((!has_channel(user,c)) && (strchr(otheruser->modes,'i')))
852                 {
853                         /* user is +i, and source not on the channel, does not show
854                          * nick in NAMES list */
855                         continue;
856                 }
857                 strlcat(list,cmode(otheruser,c),MAXBUF);
858                 strlcat(list,otheruser->nick,MAXBUF);
859                 strlcat(list," ",MAXBUF);
860                 if (strlen(list)>(480-NICKMAX))
861                 {
862                         /* list overflowed into
863                          * multiple numerics */
864                         WriteServ(user->fd,"%s",list);
865                         snprintf(list,MAXBUF,"353 %s = %s :", user->nick, c->name);
866                 }
867         }
868         /* if whats left in the list isnt empty, send it */
869         if (list[strlen(list)-1] != ':')
870         {
871                 WriteServ(user->fd,"%s",list);
872         }
873 }
874
875 /* return a count of the users on a specific channel accounting for
876  * invisible users who won't increase the count. e.g. for /LIST */
877
878 int usercount_i(chanrec *c)
879 {
880         int count = 0;
881
882         if (!c)
883         {
884                 log(DEFAULT,"*** BUG *** usercount_i was given an invalid parameter");
885                 return 0;
886         }
887
888         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
889         {
890                 if (i->second)
891                 {
892                         if (has_channel(i->second,c))
893                         {
894                                 if (isnick(i->second->nick))
895                                 {
896                                         if ((!has_channel(i->second,c)) && (strchr(i->second->modes,'i')))
897                                         {
898                                                 /* user is +i, and source not on the channel, does not show
899                                                  * nick in NAMES list */
900                                                 continue;
901                                         }
902                                         count++;
903                                 }
904                         }
905                 }
906         }
907         log(DEBUG,"usercount_i: %s %lu",c->name,(unsigned long)count);
908         return count;
909 }
910
911
912 int usercount(chanrec *c)
913 {
914         if (!c)
915         {
916                 log(DEFAULT,"*** BUG *** usercount was given an invalid parameter");
917                 return 0;
918         }
919         int count = c->GetUserCounter();
920         log(DEBUG,"usercount: %s %lu",c->name,(unsigned long)count);
921         return count;
922 }
923
924
925 // looks up a users password for their connection class (<ALLOW>/<DENY> tags)
926
927 char* Passwd(userrec *user)
928 {
929         for (ClassVector::iterator i = Config->Classes.begin(); i != Config->Classes.end(); i++)
930         {
931                 if ((i->type == CC_ALLOW) && match(user->host,i->host))
932                 {
933                         return i->pass;
934                 }
935         }
936         return "";
937 }
938
939 bool IsDenied(userrec *user)
940 {
941         for (ClassVector::iterator i = Config->Classes.begin(); i != Config->Classes.end(); i++)
942         {
943                 if ((i->type == CC_DENY) && match(user->host,i->host))
944                 {
945                         return true;
946                 }
947         }
948         return false;
949 }
950
951
952
953
954 /* sends out an error notice to all connected clients (not to be used
955  * lightly!) */
956
957 void send_error(char *s)
958 {
959         log(DEBUG,"send_error: %s",s);
960         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
961         {
962                 if (IS_LOCAL(i->second))
963                 {
964                         if (i->second->registered == 7)
965                         {
966                                 WriteServ(i->second->fd,"NOTICE %s :%s",i->second->nick,s);
967                         }
968                         else
969                         {
970                                 // fix - unregistered connections receive ERROR, not NOTICE
971                                 Write(i->second->fd,"ERROR :%s",s);
972                         }
973                 }
974         }
975 }
976
977 void Error(int status)
978 {
979         signal (SIGALRM, SIG_IGN);
980         signal (SIGPIPE, SIG_IGN);
981         signal (SIGTERM, SIG_IGN);
982         signal (SIGABRT, SIG_IGN);
983         signal (SIGSEGV, SIG_IGN);
984         signal (SIGURG, SIG_IGN);
985         signal (SIGKILL, SIG_IGN);
986         log(DEFAULT,"*** fell down a pothole in the road to perfection ***");
987         send_error("Error! Segmentation fault! save meeeeeeeeeeeeee *splat!*");
988         Exit(status);
989 }
990
991 // this function counts all users connected, wether they are registered or NOT.
992 int usercnt(void)
993 {
994         return clientlist.size();
995 }
996
997 // this counts only registered users, so that the percentages in /MAP don't mess up when users are sitting in an unregistered state
998 int registered_usercount(void)
999 {
1000         int c = 0;
1001         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
1002         {
1003                 if (i->second->registered == 7) c++;
1004         }
1005         return c;
1006 }
1007
1008 int usercount_invisible(void)
1009 {
1010         int c = 0;
1011         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
1012         {
1013                 if ((isnick(i->second->nick)) && (strchr(i->second->modes,'i'))) c++;
1014         }
1015         return c;
1016 }
1017
1018 int usercount_opers(void)
1019 {
1020         int c = 0;
1021         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
1022         {
1023                 if ((isnick(i->second->nick)) && (strchr(i->second->modes,'o'))) c++;
1024         }
1025         return c;
1026 }
1027
1028 int usercount_unknown(void)
1029 {
1030         int c = 0;
1031
1032         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
1033         {
1034                 if ((i->second->fd > -1) && (i->second->registered != 7))
1035                         c++;
1036         }
1037         return c;
1038 }
1039
1040 long chancount(void)
1041 {
1042         return chanlist.size();
1043 }
1044
1045 long local_count()
1046 {
1047         int c = 0;
1048         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
1049         {
1050                 if ((isnick(i->second->nick)) && (i->second->fd > -1)) c++;
1051         }
1052         return c;
1053 }
1054
1055 void ShowMOTD(userrec *user)
1056 {
1057         static char mbuf[MAXBUF];
1058         static char crud[MAXBUF];
1059         std::string WholeMOTD = "";
1060         if (!Config->MOTD.size())
1061         {
1062                 WriteServ(user->fd,"422 %s :Message of the day file is missing.",user->nick);
1063                 return;
1064         }
1065         snprintf(crud,MAXBUF,":%s 372 %s :- ", Config->ServerName, user->nick);
1066         snprintf(mbuf,MAXBUF,":%s 375 %s :- %s message of the day\r\n", Config->ServerName, user->nick, Config->ServerName);
1067         WholeMOTD = WholeMOTD + mbuf;
1068         for (unsigned int i = 0; i < Config->MOTD.size(); i++)
1069                 WholeMOTD = WholeMOTD + std::string(crud) + Config->MOTD[i].c_str() + std::string("\r\n");
1070         snprintf(mbuf,MAXBUF,":%s 376 %s :End of message of the day.\r\n", Config->ServerName, user->nick);
1071         WholeMOTD = WholeMOTD + mbuf;
1072         // only one write operation
1073         if (Config->GetIOHook(user->port))
1074         {
1075                 Config->GetIOHook(user->port)->OnRawSocketWrite(user->fd,(char*)WholeMOTD.c_str(),WholeMOTD.length());
1076         }
1077         else
1078         {
1079                 user->AddWriteBuf(WholeMOTD);
1080         }
1081         ServerInstance->stats->statsSent += WholeMOTD.length();
1082 }
1083
1084 void ShowRULES(userrec *user)
1085 {
1086         if (!Config->RULES.size())
1087         {
1088                 WriteServ(user->fd,"NOTICE %s :Rules file is missing.",user->nick);
1089                 return;
1090         }
1091         WriteServ(user->fd,"NOTICE %s :%s rules",user->nick,Config->ServerName);
1092         for (unsigned int i = 0; i < Config->RULES.size(); i++)
1093                 WriteServ(user->fd,"NOTICE %s :%s",user->nick,Config->RULES[i].c_str());
1094         WriteServ(user->fd,"NOTICE %s :End of %s rules.",user->nick,Config->ServerName);
1095 }
1096
1097 // this returns 1 when all modules are satisfied that the user should be allowed onto the irc server
1098 // (until this returns true, a user will block in the waiting state, waiting to connect up to the
1099 // registration timeout maximum seconds)
1100 bool AllModulesReportReady(userrec* user)
1101 {
1102         for (int i = 0; i <= MODCOUNT; i++)
1103         {
1104                 int res = modules[i]->OnCheckReady(user);
1105                 if (!res)
1106                         return false;
1107         }
1108         return true;
1109 }
1110
1111 bool DirValid(char* dirandfile)
1112 {
1113         char work[MAXBUF];
1114         char buffer[MAXBUF], otherdir[MAXBUF];
1115         strlcpy(work,dirandfile,MAXBUF);
1116         int p = strlen(work);
1117         // we just want the dir
1118         while (*work)
1119         {
1120                 if (work[p] == '/')
1121                 {
1122                         work[p] = '\0';
1123                         break;
1124                 }
1125                 work[p--] = '\0';
1126         }
1127         // Get the current working directory
1128         if( getcwd( buffer, MAXBUF ) == NULL )
1129                 return false;
1130         chdir(work);
1131         if( getcwd( otherdir, MAXBUF ) == NULL )
1132                 return false;
1133         chdir(buffer);
1134         if (strlen(otherdir) >= strlen(work))
1135         {
1136                 otherdir[strlen(work)] = '\0';
1137                 if (!strcmp(otherdir,work))
1138                 {
1139                         return true;
1140                 }
1141                 return false;
1142         }
1143         else return false;
1144 }
1145
1146 std::string GetFullProgDir(char** argv, int argc)
1147 {
1148         char work[MAXBUF];
1149         char buffer[MAXBUF], otherdir[MAXBUF];
1150         strlcpy(work,argv[0],MAXBUF);
1151         int p = strlen(work);
1152         // we just want the dir
1153         while (*work)
1154         {
1155                 if (work[p] == '/')
1156                 {
1157                         work[p] = '\0';
1158                         break;
1159                 }
1160                 work[p--] = '\0';
1161         }
1162         // Get the current working directory
1163         if( getcwd( buffer, MAXBUF ) == NULL )
1164                 return "";
1165         chdir(work);
1166         if( getcwd( otherdir, MAXBUF ) == NULL )
1167                 return "";
1168         chdir(buffer);
1169         return otherdir;
1170 }
1171