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