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