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