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