]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/inspircd.cpp
41e0897b7ff306f1395f8b1590cde9e9ca4e76cc
[user/henk/code/inspircd.git] / src / inspircd.cpp
1 /* ---------------------------------------------------------------------
2  * 
3  *            +------------------------------------+
4  *            | Inspire Internet Relay Chat Daemon |
5  *            +------------------------------------+
6  *
7  *       InspIRCd is copyright (C) 2002-2006 ChatSpike-Dev.
8  *                           E-mail:
9  *                    <brain@chatspike.net>
10  *                    <Craig@chatspike.net>
11  *     
12  *  Written by Craig Edwards, Craig McLure, and others.
13  *  This program is free but copyrighted software; you can redistribute
14  *  it and/or modify it under the terms of the GNU General Public
15  *  License as published by the Free Software Foundation, version 2
16  *  (two) ONLY.
17  *
18  *  This program is distributed in the hope that it will be useful,
19  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
20  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  *  GNU General Public License for more details.
22  *
23  *  You should have received a copy of the GNU General Public License
24  *  along with this program; if not, write to the Free Software
25  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26  *
27  * ---------------------------------------------------------------------
28  */
29
30 #include "inspircd.h"
31 #include "configreader.h"
32 #include <signal.h>
33 #include <time.h>
34 #include <string>
35 #include <exception>
36 #include <stdexcept>
37 #include <new>
38 #include <map>
39 #include <sstream>
40 #include <fstream>
41 #include <vector>
42 #include <deque>
43 #include "modules.h"
44 #include "mode.h"
45 #include "xline.h"
46 #include "socketengine.h"
47 #include "inspircd_se_config.h"
48 #include "socket.h"
49 #include "typedefs.h"
50 #include "command_parse.h"
51
52 using irc::sockets::NonBlocking;
53 using irc::sockets::insp_ntoa;
54 using irc::sockets::insp_inaddr;
55 using irc::sockets::insp_sockaddr;
56
57 char lowermap[255];
58
59 InspIRCd* SI = NULL;
60
61 void InspIRCd::AddServerName(const std::string &servername)
62 {
63         this->Log(DEBUG,"Adding server name: %s",servername.c_str());
64         
65         if(find(servernames.begin(), servernames.end(), servername) == servernames.end())
66                 servernames.push_back(servername); /* Wasn't already there. */
67 }
68
69 const char* InspIRCd::FindServerNamePtr(const std::string &servername)
70 {
71         servernamelist::iterator iter = find(servernames.begin(), servernames.end(), servername);
72         
73         if(iter == servernames.end())
74         {               
75                 AddServerName(servername);
76                 iter = --servernames.end();
77         }
78
79         return iter->c_str();
80 }
81
82 bool InspIRCd::FindServerName(const std::string &servername)
83 {
84         return (find(servernames.begin(), servernames.end(), servername) != servernames.end());
85 }
86
87 void InspIRCd::Exit(int status)
88 {
89         exit (status);
90 }
91
92 void InspIRCd::Start()
93 {
94         printf("\033[1;32mInspire Internet Relay Chat Server, compiled %s at %s\n",__DATE__,__TIME__);
95         printf("(C) ChatSpike Development team.\033[0m\n\n");
96         printf("Developers:\t\t\033[1;32mBrain, FrostyCoolSlug, w00t, Om, Special\033[0m\n");
97         printf("Others:\t\t\t\033[1;32mSee /INFO Output\033[0m\n");
98         printf("Name concept:\t\t\033[1;32mLord_Zathras\033[0m\n\n");
99 }
100
101 void InspIRCd::Rehash(int status)
102 {
103         SI->WriteOpers("Rehashing config file %s due to SIGHUP",ServerConfig::CleanFilename(CONFIG_FILE));
104         fclose(SI->Config->log_file);
105         SI->OpenLog(NULL,0);
106         SI->Config->Read(false,NULL);
107         FOREACH_MOD_I(SI,I_OnRehash,OnRehash(""));
108 }
109
110 void InspIRCd::SetSignals(bool SEGVHandler)
111 {
112         signal (SIGALRM, SIG_IGN);
113         signal (SIGHUP, InspIRCd::Rehash);
114         signal (SIGPIPE, SIG_IGN);
115         signal (SIGTERM, InspIRCd::Exit);
116 }
117
118 bool InspIRCd::DaemonSeed()
119 {
120         int childpid;
121         if ((childpid = fork ()) < 0)
122                 return (ERROR);
123         else if (childpid > 0)
124         {
125                 /* We wait a few seconds here, so that the shell prompt doesnt come back over the output */
126                 sleep(6);
127                 exit (0);
128         }
129         setsid ();
130         umask (007);
131         printf("InspIRCd Process ID: \033[1;32m%lu\033[0m\n",(unsigned long)getpid());
132
133         rlimit rl;
134         if (getrlimit(RLIMIT_CORE, &rl) == -1)
135         {
136                 this->Log(DEFAULT,"Failed to getrlimit()!");
137                 return false;
138         }
139         else
140         {
141                 rl.rlim_cur = rl.rlim_max;
142                 if (setrlimit(RLIMIT_CORE, &rl) == -1)
143                         this->Log(DEFAULT,"setrlimit() failed, cannot increase coredump size.");
144         }
145   
146         return true;
147 }
148
149 void InspIRCd::WritePID(const std::string &filename)
150 {
151         std::ofstream outfile(filename.c_str());
152         if (outfile.is_open())
153         {
154                 outfile << getpid();
155                 outfile.close();
156         }
157         else
158         {
159                 printf("Failed to write PID-file '%s', exiting.\n",filename.c_str());
160                 this->Log(DEFAULT,"Failed to write PID-file '%s', exiting.",filename.c_str());
161                 Exit(0);
162         }
163 }
164
165 std::string InspIRCd::GetRevision()
166 {
167         return REVISION;
168 }
169
170 InspIRCd::InspIRCd(int argc, char** argv)
171         : ModCount(-1), duration_m(60), duration_h(60*60), duration_d(60*60*24), duration_w(60*60*24*7), duration_y(60*60*24*365)
172 {
173         bool SEGVHandler = false;
174
175         modules.resize(255);
176         factory.resize(255);
177         
178         this->Config = new ServerConfig(this);
179         this->Start();
180         this->module_sockets.clear();
181         this->TIME = this->OLDTIME = this->startup_time = time(NULL);
182         srand(this->TIME);
183         this->Log(DEBUG,"*** InspIRCd starting up!");
184         if (!ServerConfig::FileExists(CONFIG_FILE))
185         {
186                 printf("ERROR: Cannot open config file: %s\nExiting...\n",CONFIG_FILE);
187                 this->Log(DEFAULT,"main: no config");
188                 printf("ERROR: Your config file is missing, this IRCd will self destruct in 10 seconds!\n");
189                 Exit(ERROR);
190         }
191         *this->LogFileName = 0;
192         if (argc > 1) {
193                 for (int i = 1; i < argc; i++)
194                 {
195                         if (!strcmp(argv[i],"-nofork"))
196                         {
197                                 Config->nofork = true;
198                         }
199                         else if(!strcmp(argv[i],"-debug"))
200                         {
201                                 Config->forcedebug = true;
202                         }
203                         else if(!strcmp(argv[i],"-nolog"))
204                         {
205                                 Config->writelog = false;
206                         }
207                         else if (!strcmp(argv[i],"-wait"))
208                         {
209                                 sleep(6);
210                         }
211                         else if (!strcmp(argv[i],"-nolimit"))
212                         {
213                                 printf("WARNING: The `-nolimit' option is deprecated, and now on by default. This behaviour may change in the future.\n");
214                         }
215                         else if (!strcmp(argv[i],"-notraceback"))
216                         {
217                                 SEGVHandler = false;
218                         }
219                         else if (!strcmp(argv[i],"-logfile"))
220                         {
221                                 if (argc > i+1)
222                                 {
223                                         strlcpy(LogFileName,argv[i+1],MAXBUF);
224                                         printf("LOG: Setting logfile to %s\n",LogFileName);
225                                 }
226                                 else
227                                 {
228                                         printf("ERROR: The -logfile parameter must be followed by a log file name and path.\n");
229                                         Exit(ERROR);
230                                 }
231                                 i++;
232                         }
233                         else
234                         {
235                                 printf("Usage: %s [-nofork] [-nolog] [-debug] [-wait] [-nolimit] [-notraceback] [-logfile <filename>]\n",argv[0]);
236                                 Exit(ERROR);
237                         }
238                 }
239         }
240
241         strlcpy(Config->MyExecutable,argv[0],MAXBUF);
242
243         this->MakeLowerMap();
244
245         OpenLog(argv, argc);
246         this->stats = new serverstats();
247         this->Parser = new CommandParser(this);
248         this->Timers = new TimerManager();
249         this->XLines = new XLineManager(this);
250         Config->ClearStack();
251         Config->Read(true, NULL);
252         this->CheckRoot();
253         this->Modes = new ModeParser(this);
254         this->AddServerName(Config->ServerName);        
255         CheckDie();
256         InitializeDisabledCommands(Config->DisabledCommands, this);
257         stats->BoundPortCount = BindPorts(true);
258
259         for(int t = 0; t < 255; t++)
260                 Config->global_implementation[t] = 0;
261
262         memset(&Config->implement_lists,0,sizeof(Config->implement_lists));
263
264         printf("\n");
265         this->SetSignals(SEGVHandler);
266         if (!Config->nofork)
267         {
268                 if (!this->DaemonSeed())
269                 {
270                         printf("ERROR: could not go into daemon mode. Shutting down.\n");
271                         Exit(ERROR);
272                 }
273         }
274
275         /* Because of limitations in kqueue on freebsd, we must fork BEFORE we
276          * initialize the socket engine.
277          */
278         SocketEngineFactory* SEF = new SocketEngineFactory();
279         SE = SEF->Create(this);
280         delete SEF;
281
282         this->Res = new DNS(this);
283
284         this->Log(DEBUG,"RES: %08x",this->Res);
285
286         this->LoadAllModules();
287
288         /* Just in case no modules were loaded - fix for bug #101 */
289         this->BuildISupport();
290
291         if (!stats->BoundPortCount)
292         {
293                 printf("\nERROR: I couldn't bind any ports! Are you sure you didn't start InspIRCd twice?\n");
294                 Exit(ERROR);
295         }
296
297         /* Add the listening sockets used for client inbound connections
298          * to the socket engine
299          */
300         this->Log(DEBUG,"%d listeners",stats->BoundPortCount);
301         for (unsigned long count = 0; count < stats->BoundPortCount; count++)
302         {
303                 this->Log(DEBUG,"Add listener: %d",Config->openSockfd[count]);
304                 if (!SE->AddFd(Config->openSockfd[count]))
305                 {
306                         printf("\nEH? Could not add listener to socketengine. You screwed up, aborting.\n");
307                         Exit(ERROR);
308                 }
309         }
310
311         if (!Config->nofork)
312         {
313                 fclose(stdout);
314                 fclose(stderr);
315                 fclose(stdin);
316         }
317
318         printf("\nInspIRCd is now running!\n");
319
320         this->WritePID(Config->PID);
321 }
322
323 std::string InspIRCd::GetVersionString()
324 {
325         char versiondata[MAXBUF];
326         char dnsengine[] = "singlethread-object";
327         if (*Config->CustomVersion)
328         {
329                 snprintf(versiondata,MAXBUF,"%s %s :%s",VERSION,Config->ServerName,Config->CustomVersion);
330         }
331         else
332         {
333                 snprintf(versiondata,MAXBUF,"%s %s :%s [FLAGS=%lu,%s,%s]",VERSION,Config->ServerName,SYSTEM,(unsigned long)OPTIMISATION,SE->GetName().c_str(),dnsengine);
334         }
335         return versiondata;
336 }
337
338 char* InspIRCd::ModuleError()
339 {
340         return MODERR;
341 }
342
343 void InspIRCd::EraseFactory(int j)
344 {
345         int v = 0;
346         for (std::vector<ircd_module*>::iterator t = factory.begin(); t != factory.end(); t++)
347         {
348                 if (v == j)
349                 {
350                         factory.erase(t);
351                         factory.push_back(NULL);
352                         return;
353                 }
354                 v++;
355         }
356 }
357
358 void InspIRCd::EraseModule(int j)
359 {
360         int v1 = 0;
361         for (ModuleList::iterator m = modules.begin(); m!= modules.end(); m++)
362         {
363                 if (v1 == j)
364                 {
365                         DELETE(*m);
366                         modules.erase(m);
367                         modules.push_back(NULL);
368                         break;
369                 }
370                 v1++;
371         }
372         int v2 = 0;
373         for (std::vector<std::string>::iterator v = Config->module_names.begin(); v != Config->module_names.end(); v++)
374         {
375                 if (v2 == j)
376                 {
377                        Config->module_names.erase(v);
378                        break;
379                 }
380                 v2++;
381         }
382
383 }
384
385 void InspIRCd::MoveTo(std::string modulename,int slot)
386 {
387         unsigned int v2 = 256;
388         for (unsigned int v = 0; v < Config->module_names.size(); v++)
389         {
390                 if (Config->module_names[v] == modulename)
391                 {
392                         // found an instance, swap it with the item at the end
393                         v2 = v;
394                         break;
395                 }
396         }
397         if ((v2 != (unsigned int)slot) && (v2 < 256))
398         {
399                 // Swap the module names over
400                 Config->module_names[v2] = Config->module_names[slot];
401                 Config->module_names[slot] = modulename;
402                 // now swap the module factories
403                 ircd_module* temp = factory[v2];
404                 factory[v2] = factory[slot];
405                 factory[slot] = temp;
406                 // now swap the module objects
407                 Module* temp_module = modules[v2];
408                 modules[v2] = modules[slot];
409                 modules[slot] = temp_module;
410                 // now swap the implement lists (we dont
411                 // need to swap the global or recount it)
412                 for (int n = 0; n < 255; n++)
413                 {
414                         char x = Config->implement_lists[v2][n];
415                         Config->implement_lists[v2][n] = Config->implement_lists[slot][n];
416                         Config->implement_lists[slot][n] = x;
417                 }
418         }
419         else
420         {
421                 this->Log(DEBUG,"Move of %s to slot failed!",modulename.c_str());
422         }
423 }
424
425 void InspIRCd::MoveAfter(std::string modulename, std::string after)
426 {
427         for (unsigned int v = 0; v < Config->module_names.size(); v++)
428         {
429                 if (Config->module_names[v] == after)
430                 {
431                         MoveTo(modulename, v);
432                         return;
433                 }
434         }
435 }
436
437 void InspIRCd::MoveBefore(std::string modulename, std::string before)
438 {
439         for (unsigned int v = 0; v < Config->module_names.size(); v++)
440         {
441                 if (Config->module_names[v] == before)
442                 {
443                         if (v > 0)
444                         {
445                                 MoveTo(modulename, v-1);
446                         }
447                         else
448                         {
449                                 MoveTo(modulename, v);
450                         }
451                         return;
452                 }
453         }
454 }
455
456 void InspIRCd::MoveToFirst(std::string modulename)
457 {
458         MoveTo(modulename,0);
459 }
460
461 void InspIRCd::MoveToLast(std::string modulename)
462 {
463         MoveTo(modulename,this->GetModuleCount());
464 }
465
466 void InspIRCd::BuildISupport()
467 {
468         // the neatest way to construct the initial 005 numeric, considering the number of configure constants to go in it...
469         std::stringstream v;
470         v << "WALLCHOPS WALLVOICES MODES=" << MAXMODES << " CHANTYPES=# PREFIX=(ohv)@%+ MAP MAXCHANNELS=" << MAXCHANS << " MAXBANS=60 VBANLIST NICKLEN=" << NICKMAX-1;
471         v << " CASEMAPPING=rfc1459 STATUSMSG=@%+ CHARSET=ascii TOPICLEN=" << MAXTOPIC << " KICKLEN=" << MAXKICK << " MAXTARGETS=" << Config->MaxTargets << " AWAYLEN=";
472         v << MAXAWAY << " CHANMODES=b,k,l,psmnti FNC NETWORK=" << Config->Network << " MAXPARA=32";
473         Config->data005 = v.str();
474         FOREACH_MOD_I(this,I_On005Numeric,On005Numeric(Config->data005));
475 }
476
477 bool InspIRCd::UnloadModule(const char* filename)
478 {
479         std::string filename_str = filename;
480         for (unsigned int j = 0; j != Config->module_names.size(); j++)
481         {
482                 if (Config->module_names[j] == filename_str)
483                 {
484                         if (modules[j]->GetVersion().Flags & VF_STATIC)
485                         {
486                                 this->Log(DEFAULT,"Failed to unload STATIC module %s",filename);
487                                 snprintf(MODERR,MAXBUF,"Module not unloadable (marked static)");
488                                 return false;
489                         }
490                         /* Give the module a chance to tidy out all its metadata */
491                         for (chan_hash::iterator c = this->chanlist.begin(); c != this->chanlist.end(); c++)
492                         {
493                                 modules[j]->OnCleanup(TYPE_CHANNEL,c->second);
494                         }
495                         for (user_hash::iterator u = this->clientlist.begin(); u != this->clientlist.end(); u++)
496                         {
497                                 modules[j]->OnCleanup(TYPE_USER,u->second);
498                         }
499
500                         FOREACH_MOD_I(this,I_OnUnloadModule,OnUnloadModule(modules[j],Config->module_names[j]));
501
502                         for(int t = 0; t < 255; t++)
503                         {
504                                 Config->global_implementation[t] -= Config->implement_lists[j][t];
505                         }
506
507                         /* We have to renumber implement_lists after unload because the module numbers change!
508                          */
509                         for(int j2 = j; j2 < 254; j2++)
510                         {
511                                 for(int t = 0; t < 255; t++)
512                                 {
513                                         Config->implement_lists[j2][t] = Config->implement_lists[j2+1][t];
514                                 }
515                         }
516
517                         // found the module
518                         this->Log(DEBUG,"Removing dependent commands...");
519                         Parser->RemoveCommands(filename);
520                         this->Log(DEBUG,"Deleting module...");
521                         this->EraseModule(j);
522                         this->Log(DEBUG,"Erasing module entry...");
523                         this->EraseFactory(j);
524                         this->Log(DEFAULT,"Module %s unloaded",filename);
525                         this->ModCount--;
526                         BuildISupport();
527                         return true;
528                 }
529         }
530         this->Log(DEFAULT,"Module %s is not loaded, cannot unload it!",filename);
531         snprintf(MODERR,MAXBUF,"Module not loaded");
532         return false;
533 }
534
535 bool InspIRCd::LoadModule(const char* filename)
536 {
537         char modfile[MAXBUF];
538 #ifdef STATIC_LINK
539         strlcpy(modfile,filename,MAXBUF);
540 #else
541         snprintf(modfile,MAXBUF,"%s/%s",Config->ModPath,filename);
542 #endif
543         std::string filename_str = filename;
544 #ifndef STATIC_LINK
545 #ifndef IS_CYGWIN
546         if (!ServerConfig::DirValid(modfile))
547         {
548                 this->Log(DEFAULT,"Module %s is not within the modules directory.",modfile);
549                 snprintf(MODERR,MAXBUF,"Module %s is not within the modules directory.",modfile);
550                 return false;
551         }
552 #endif
553 #endif
554         this->Log(DEBUG,"Loading module: %s",modfile);
555 #ifndef STATIC_LINK
556         if (ServerConfig::FileExists(modfile))
557         {
558 #endif
559                 for (unsigned int j = 0; j < Config->module_names.size(); j++)
560                 {
561                         if (Config->module_names[j] == filename_str)
562                         {
563                                 this->Log(DEFAULT,"Module %s is already loaded, cannot load a module twice!",modfile);
564                                 snprintf(MODERR,MAXBUF,"Module already loaded");
565                                 return false;
566                         }
567                 }
568                 try
569                 {
570                         ircd_module* a = new ircd_module(this, modfile);
571                         factory[this->ModCount+1] = a;
572                         if (factory[this->ModCount+1]->LastError())
573                         {
574                                 this->Log(DEFAULT,"Unable to load %s: %s",modfile,factory[this->ModCount+1]->LastError());
575                                 snprintf(MODERR,MAXBUF,"Loader/Linker error: %s",factory[this->ModCount+1]->LastError());
576                                 return false;
577                         }
578                         if ((long)factory[this->ModCount+1]->factory != -1)
579                         {
580                                 Module* m = factory[this->ModCount+1]->factory->CreateModule(this);
581                                 modules[this->ModCount+1] = m;
582                                 /* save the module and the module's classfactory, if
583                                  * this isnt done, random crashes can occur :/ */
584                                 Config->module_names.push_back(filename);
585
586                                 char* x = &Config->implement_lists[this->ModCount+1][0];
587                                 for(int t = 0; t < 255; t++)
588                                         x[t] = 0;
589
590                                 modules[this->ModCount+1]->Implements(x);
591
592                                 for(int t = 0; t < 255; t++)
593                                         Config->global_implementation[t] += Config->implement_lists[this->ModCount+1][t];
594                         }
595                         else
596                         {
597                                 this->Log(DEFAULT,"Unable to load %s",modfile);
598                                 snprintf(MODERR,MAXBUF,"Factory function failed: Probably missing init_module() entrypoint.");
599                                 return false;
600                         }
601                 }
602                 catch (ModuleException& modexcept)
603                 {
604                         this->Log(DEFAULT,"Unable to load %s: ",modfile,modexcept.GetReason());
605                         snprintf(MODERR,MAXBUF,"Factory function threw an exception: %s",modexcept.GetReason());
606                         return false;
607                 }
608 #ifndef STATIC_LINK
609         }
610         else
611         {
612                 this->Log(DEFAULT,"InspIRCd: startup: Module Not Found %s",modfile);
613                 snprintf(MODERR,MAXBUF,"Module file could not be found");
614                 return false;
615         }
616 #endif
617         this->ModCount++;
618         FOREACH_MOD_I(this,I_OnLoadModule,OnLoadModule(modules[this->ModCount],filename_str));
619         // now work out which modules, if any, want to move to the back of the queue,
620         // and if they do, move them there.
621         std::vector<std::string> put_to_back;
622         std::vector<std::string> put_to_front;
623         std::map<std::string,std::string> put_before;
624         std::map<std::string,std::string> put_after;
625         for (unsigned int j = 0; j < Config->module_names.size(); j++)
626         {
627                 if (modules[j]->Prioritize() == PRIORITY_LAST)
628                 {
629                         put_to_back.push_back(Config->module_names[j]);
630                 }
631                 else if (modules[j]->Prioritize() == PRIORITY_FIRST)
632                 {
633                         put_to_front.push_back(Config->module_names[j]);
634                 }
635                 else if ((modules[j]->Prioritize() & 0xFF) == PRIORITY_BEFORE)
636                 {
637                         put_before[Config->module_names[j]] = Config->module_names[modules[j]->Prioritize() >> 8];
638                 }
639                 else if ((modules[j]->Prioritize() & 0xFF) == PRIORITY_AFTER)
640                 {
641                         put_after[Config->module_names[j]] = Config->module_names[modules[j]->Prioritize() >> 8];
642                 }
643         }
644         for (unsigned int j = 0; j < put_to_back.size(); j++)
645         {
646                 MoveToLast(put_to_back[j]);
647         }
648         for (unsigned int j = 0; j < put_to_front.size(); j++)
649         {
650                 MoveToFirst(put_to_front[j]);
651         }
652         for (std::map<std::string,std::string>::iterator j = put_before.begin(); j != put_before.end(); j++)
653         {
654                 MoveBefore(j->first,j->second);
655         }
656         for (std::map<std::string,std::string>::iterator j = put_after.begin(); j != put_after.end(); j++)
657         {
658                 MoveAfter(j->first,j->second);
659         }
660         BuildISupport();
661         return true;
662 }
663
664 void InspIRCd::DoOneIteration(bool process_module_sockets)
665 {
666         EventHandler* activefds[MAX_DESCRIPTORS];
667         unsigned int numberactive;
668
669         /* time() seems to be a pretty expensive syscall, so avoid calling it too much.
670          * Once per loop iteration is pleanty.
671          */
672         OLDTIME = TIME;
673         TIME = time(NULL);
674
675         /* Run background module timers every few seconds
676          * (the docs say modules shouldnt rely on accurate
677          * timing using this event, so we dont have to
678          * time this exactly).
679          */
680         if (TIME != OLDTIME)
681         {
682                 if (TIME < OLDTIME)
683                         WriteOpers("*** \002EH?!\002 -- Time is flowing BACKWARDS in this dimension! Clock drifted backwards %d secs.",abs(OLDTIME-TIME));
684                 if ((TIME % 3600) == 0)
685                 {
686                         irc::whowas::MaintainWhoWas(TIME);
687                 }
688                 Timers->TickTimers(TIME);
689                 if (process_module_sockets)
690                         this->DoSocketTimeouts(TIME);
691                 this->DoBackgroundUserStuff(TIME);
692
693                 if ((TIME % 5) == 0)
694                 {
695                         XLines->expire_lines();
696                         FOREACH_MOD_I(this,I_OnBackgroundTimer,OnBackgroundTimer(TIME));
697                         Timers->TickMissedTimers(TIME);
698                 }
699         }
700          
701         /* Call the socket engine to wait on the active
702          * file descriptors. The socket engine has everything's
703          * descriptors in its list... dns, modules, users,
704          * servers... so its nice and easy, just one call.
705          */
706         if (!(numberactive = SE->Wait(activefds)))
707                 return;
708
709         /**
710          * Now process each of the fd's. For users, we have a fast
711          * lookup table which can find a user by file descriptor, so
712          * processing them by fd isnt expensive. If we have a lot of
713          * listening ports or module sockets though, things could get
714          * ugly.
715          */
716         this->Log(DEBUG,"There are %d fd's to process.",numberactive);
717
718         for (unsigned int activefd = 0; activefd < numberactive; activefd++)
719         {
720                 this->Log(DEBUG,"Handle %s event on fd %d",activefds[activefd]->Readable() ? "read" : "write", activefds[activefd]->GetFd());
721                 activefds[activefd]->HandleEvent(activefds[activefd]->Readable() ? EVENT_READ : EVENT_WRITE);
722         }
723 }
724
725 bool InspIRCd::IsIdent(const char* n)
726 {
727         if (!n || !*n)
728                 return false;
729
730         for (char* i = (char*)n; *i; i++)
731         {
732                 if ((*i >= 'A') && (*i <= '}'))
733                 {
734                         continue;
735                 }
736                 if (((*i >= '0') && (*i <= '9')) || (*i == '-') || (*i == '.'))
737                 {
738                         continue;
739                 }
740                 return false;
741         }
742         return true;
743 }
744
745
746 bool InspIRCd::IsNick(const char* n)
747 {
748         if (!n || !*n)
749                 return false;
750
751         int p = 0; 
752         for (char* i = (char*)n; *i; i++, p++)
753         {
754                 /* "A"-"}" can occur anywhere in a nickname */
755                 if ((*i >= 'A') && (*i <= '}'))
756                 {
757                         continue;
758                 }
759                 /* "0"-"9", "-" can occur anywhere BUT the first char of a nickname */
760                 if ((((*i >= '0') && (*i <= '9')) || (*i == '-')) && (i > n))
761                 {
762                         continue;
763                 }
764                 /* invalid character! abort */
765                 return false;
766         }
767         return (p < NICKMAX - 1);
768 }
769
770 int InspIRCd::Run()
771 {
772         while (true)
773         {
774                 DoOneIteration(true);
775         }
776         /* This is never reached -- we hope! */
777         return 0;
778 }
779
780 /**********************************************************************************/
781
782 /**
783  * An ircd in four lines! bwahahaha. ahahahahaha. ahahah *cough*.
784  */
785
786 int main(int argc, char** argv)
787 {
788         SI = new InspIRCd(argc, argv);
789         SI->Run();
790         delete SI;
791         return 0;
792 }
793
794 /* this returns true when all modules are satisfied that the user should be allowed onto the irc server
795  * (until this returns true, a user will block in the waiting state, waiting to connect up to the
796  * registration timeout maximum seconds)
797  */
798 bool InspIRCd::AllModulesReportReady(userrec* user)
799 {
800         if (!Config->global_implementation[I_OnCheckReady])
801                 return true;
802
803         for (int i = 0; i <= this->GetModuleCount(); i++)
804         {
805                 if (Config->implement_lists[i][I_OnCheckReady])
806                 {
807                         int res = modules[i]->OnCheckReady(user);
808                         if (!res)
809                                 return false;
810                 }
811         }
812         return true;
813 }
814
815 int InspIRCd::GetModuleCount()
816 {
817         return this->ModCount;
818 }
819
820 time_t InspIRCd::Time()
821 {
822         return TIME;
823 }
824