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