001 #define IRCD_USERMODE_v 0x1
002 #define IRCD_USERMODE_h 0x2
003 #define IRCD_USERMODE_o 0x4
004 #define IRCD_USERMODE_a 0x8
005 #define IRCD_USERMODE_q 0x10
006 
007 #define ICRD_NULL_NICK "AnonNoUse"
008 
009 I64 registry_tmp;
010 
011 
012 class IrcClient
013 {
014 IrcClient *prev;
015 IrcClient *next;
016 I64 s;
017 U8 *attempted_nick;
018 U8 *nick;
019 U8 *username;
020 U8 *realname;
021 U8 *server;
022 U8 *host;
023 U32 ip;
024 U8 *vhost;
025 CFifoI64 *msgs;
026 I64 idle;
027 I64 limit;
028 CTask *task;
029 CDate last_connect;
030 Bool disconnected,pad[7];
031 I64 caps;
032 U8 *account; //See Accounts.HC
033 U8 password[4096];
034 U8 *client_tags; 
035 I64 missed_pings;
036 Bool logged_in;
037 Bool nick_set;
038 };
039 
040 class IrcUser
041 {
042 IrcUser *prev;
043 IrcUser *next;
044 IrcClient *client;
045 U64 flags;
046 };
047 
048 class IrcChannel
049 {
050 IrcChannel *prev;
051 IrcChannel *next;
052 U8 *name;
053 U8 *topic;
054 IrcUser *users;
055 CFifoI64 *backlog;
056 I64 msg_cnt; //Used for msg_id
057 I64 password_hash;
058 Bool is_secret;
059 };
060 
061 extern U0 IrcParseCommand(IrcClient *client, U8 *str);
062 extern U0 IrcChannelsQuit(IrcClient *client, U8 *msg=NULL);
063 
064 IrcClient *client_head = CAlloc(sizeof(IrcClient),gihon_task);
065 IrcChannel *channel_head = CAlloc(sizeof(IrcChannel),gihon_task);
066 
067 U0 IrcClientAdd(IrcClient *client)
068 {
069   IrcClient *clients = client_head;
070   while (clients->next)
071     {
072     clients = clients->next;
073   }
074   client->prev = clients;
075   clients->next = client;
076   ircd_clients_total++;
077 }
078 #include "Account.HC";
079 U0 IrcClientDel(IrcClient *client)
080 {
081   IrcChannelsQuit(client);
082   IrcClientAccountLogout(client);
083   U8 *tmp;
084   CTask *task = client->task;
085   IrcClient *prev = client->prev;
086   IrcClient *next = client->next;
087   if(prev) prev->next = next;
088   if(next) next->prev = prev;
089   if (task && task != Fs)
090     Kill(task,FALSE);
091   close(client->s);
092   while(FifoI64Rem(client->msgs,&tmp))
093     Free(tmp);
094   FifoI64Del(client->msgs); //TODO Free
095   Free(client);
096   ircd_clients_total--;
097 }
098 
099 IrcClient *IrcGetClientByNick(U8 *nick)
100 {
101   IrcClient *client = client_head->next;
102   while (client)
103     {
104     if (client->nick&&!StrCmp(client->nick, nick)) return client;
105     client = client->next;
106   }
107   return NULL;
108 }
109 
110 U0 IrcClientSetNick(IrcClient *client, U8 *nick)
111 {
112   I64 i;
113   U8 *buf = CAlloc(4096);
114   IrcClient *chk_client = client_head->next;
115   Free(client->attempted_nick);
116   client->attempted_nick=StrNew(nick,gihon_task);
117   // check if in use, owned, forbidden, etc..
118   while (chk_client)
119     {
120     if (chk_client->nick&&!StrICmp(chk_client->nick, nick))
121       {
122       StrPrint(buf, ":%s 433 %s %s :Nickname is already in use.\r\n", ircd_hostname, client->username,
123             nick);
124       GihonFifoIns(client->msgs, StrNew(buf));
125       Free(buf);
126       return;
127     }
128     chk_client = chk_client->next;
129   }
130   for (i=0; i<service_cnt; i++)
131     {
132     if (service_nick[i]&&!StrICmp(service_nick[i], nick))
133       {
134       StrPrint(buf, ":%s 432 %s %s :Invalid nickname: Reserved for Services\r\n", ircd_hostname, client->username,
135             nick);
136       GihonFifoIns(client->msgs, StrNew(buf));
137       Free(buf);
138       return;
139     }
140   }
141 
142   for(i=0;nick[i];i++) {
143     if(!Bt(char_bmp_alpha_numeric,nick[i])&&nick[i]!='_') {
144       StrPrint(buf, ":%s 432 %s %s :Must use alhpa-numeric chars for nickname\r\n", ircd_hostname, client->username,
145             nick);
146       GihonFifoIns(client->msgs, StrNew(buf));
147       Free(buf);
148       return;
149     }      
150   }
151 
152   client->nick_set=TRUE;
153   if(!StrCmp(client->nick,ICRD_NULL_NICK))
154     StrPrint(buf, ":%s!%s@%s NICK %s\r\n",client->username,client->username,client->host,nick);
155   else
156     StrPrint(buf, ":%s!%s@%s NICK %s\r\n",client->nick,client->username,client->host,nick);
157   GihonFifoIns(client->msgs, StrNew(buf));
158   Free(client->nick);
159   client->nick=StrNew(nick,gihon_task);
160   Free(buf);
161 }
162 
163 
164 #include "Account.HC";
165 U0 IrcClientSetUser(IrcClient *client, U8 *username, U8 *host, U8 *server, U8 *realname)
166 {
167 // check user params
168   U8 *tmp,*tmp2;
169   CIrcAccount *acnt;
170   client->username = StrNew(username,gihon_task);
171   client->host = StrNew(host,gihon_task);
172   client->realname = StrNew(realname,gihon_task);
173   client->server = StrNew(server,gihon_task);
174 }
175 
176 U0 IrcClientMotd(IrcClient *client)
177 {
178   I64 i;
179   U8 *buf = CAlloc(4096);
180   StrPrint(buf, ":%s 375 %s :-\r\n", ircd_hostname, client->username);
181   GihonFifoIns(client->msgs, StrNew(buf));
182   for (i=0; i<motd_line_cnt; i++)
183     {
184     StrPrint(buf, ":%s 372 %s :%s\r\n", ircd_hostname, client->username, motd_lines[i]);
185     GihonFifoIns(client->msgs, StrNew(buf));
186   }
187   StrPrint(buf, ":%s 376 %s :>\r\n", ircd_hostname, client->username);
188   GihonFifoIns(client->msgs, StrNew(buf));
189   Free(buf);
190 }
191 
192 U0 IrcClientNotice(IrcClient *client, U8 *msg)
193 {
194   U8 *buf = CAlloc(4096);
195   StrPrint(buf, ":%s NOTICE Auth :%s\r\n", ircd_hostname, msg);
196   GihonFifoIns(client->msgs, StrNew(buf));
197   Free(buf);
198 }
199 
200 U0 IrcClientJoin(U8 *dst, IrcClient *tx_client)
201 {
202   U8 *buf = CAlloc(4096);
203   IrcClient *rx_client = client_head->next;
204   IrcChannel *rx_channel = channel_head->next;
205   IrcUser *rx_user;
206   while (rx_channel)
207     {
208     if (!StrCmp(rx_channel->name, dst))
209       { //PRIVMSG to channel
210       rx_user = rx_channel->users->next;
211       while (rx_user)
212         {
213         rx_client = rx_user->client;
214         StrCpy(buf,"");
215         CatPrint(buf, ":%s!%s@%s JOIN :%s\r\n", tx_client->nick, tx_client->username,
216               tx_client->host, dst);
217         GihonFifoIns(rx_client->msgs, StrNew(buf));
218         rx_user = rx_user->next;
219       }
220       return;
221     }
222     rx_channel = rx_channel->next;
223   }
224   Free(buf);
225 }
226 
227 U0 IrcClientNames(IrcClient *client, U8 *dst)
228 {
229   U8 *buf = CAlloc(4096);
230   U8 *mode = CAlloc(16);
231   IrcChannel *channel = channel_head->next;
232   IrcUser *user;
233   StrPrint(buf, ":%s 353 %s = %s :", ircd_hostname, client->username, dst);
234   while (channel)
235     {
236     if (!StrCmp(channel->name, dst))
237       {
238       user = channel->users->next;
239       while (user)
240         {
241         StrPrint(mode, "");
242         if (user->flags & IRCD_USERMODE_q) StrPrint(mode, "@");
243         else if (user->flags & IRCD_USERMODE_o) StrPrint(mode, "@");
244         else if (user->flags & IRCD_USERMODE_h) StrPrint(mode, "\%");
245         else if (user->flags & IRCD_USERMODE_v) StrPrint(mode, "+");
246         StrPrint(buf+StrLen(buf), "%s%s ", mode, user->client->nick);
247         user = user->next;
248       }
249       StrPrint(buf+StrLen(buf), "\r\n");
250       GihonFifoIns(client->msgs, StrNew(buf));
251       StrPrint(buf, ":%s 366 %s %s :End of /NAMES list.\r\n", ircd_hostname, client->username, dst);
252       GihonFifoIns(client->msgs, StrNew(buf));
253       Free(mode);
254       Free(buf);
255       return;
256     }
257     channel = channel->next;
258   }
259   Free(mode);
260   Free(buf);
261 }
262 
263 U0 IrcClientTopic(IrcClient *client, U8 *dst)
264 {
265   U8 *buf = CAlloc(4096);
266   IrcChannel *channel = channel_head->next;
267   while (channel)
268     {
269     if (!StrCmp(channel->name, dst))
270       {
271       if (StrLen(channel->topic))
272         {
273         StrPrint(buf, ":%s 332 %s %s :%s\r\n", ircd_hostname, client->username, dst, channel->topic);
274         GihonFifoIns(client->msgs, StrNew(buf));
275       }
276       Free(buf);
277       return;
278     }
279     channel = channel->next;
280   }
281   Free(buf);
282 }
283 
284 U0 IrcClientPart(U8 *dst, IrcClient *tx_client, U8 *msg=NULL)
285 {
286   U8 *buf = CAlloc(4096);
287   IrcClient *rx_client = client_head->next;
288   IrcChannel *rx_channel = channel_head->next;
289   IrcUser *rx_user;
290   while (rx_channel)
291     {
292     if (!StrCmp(rx_channel->name, dst))
293       { //PRIVMSG to channel
294       rx_user = rx_channel->users->next;
295       while (rx_user)
296         {
297         rx_client = rx_user->client;
298         if (msg)
299           {
300           StrPrint(buf, ":%s!%s@%s PART %s :%s\r\n", tx_client->nick, tx_client->username,
301                 tx_client->host, dst, msg);
302         }
303         else
304           {
305           StrPrint(buf, ":%s!%s@%s PART %s\r\n", tx_client->nick, tx_client->username,
306                 tx_client->host, dst);
307         }
308         GihonFifoIns(rx_client->msgs, StrNew(buf));
309         rx_user = rx_user->next;
310       }
311       return;
312     }
313     rx_channel = rx_channel->next;
314   }
315   Free(buf);
316 }
317 
318 U0 IrcClientPing(IrcClient *client, U8 *msg)
319 {
320   U8 *buf = CAlloc(4096);
321   StrPrint(buf, ":%s PONG %s :%s\r\n", ircd_hostname, ircd_hostname, msg);
322   GihonFifoIns(client->msgs, StrNew(buf));
323   Free(buf);
324 }
325 Bool IrcClientCanTalkOnChannel(IrcChannel *chan,IrcClient *cl) {
326   IrcUser *user=chan->users->next;
327   while(user) {
328     if(user->flags&IRCD_USERMODE_v&&user->client==cl)
329       return TRUE;
330     user=user->next;
331   }
332   return FALSE;
333 }
334 extern Bool IsDccCommand(IrcClient *cl,U8 *to,U8 *msg);
335 //Used for TAGMSG and PRIVMSG
336 U0 IrcClient_SendOut(IrcClient *tx_client, U8* type="PRIVMSG",U8 *dst, U8 *msg)
337 {
338   if(dst&&IsDccCommand(tx_client,dst,msg))
339     return;
340   if(dst[0]=='#') {
341 //Public channel,be sure to censor poo poo words
342     msg=BadWordFilter(msg);
343   } else
344     msg=StrNew(msg);
345   U8 *buf = CAlloc(4096) ,*tmp;
346   CFifoI64 *new_fifo;
347   Bool wants_space;
348   IrcClient *rx_client = client_head->next;
349   while (rx_client)
350     {
351     if (!StrCmp(rx_client->nick, dst))
352       { //PRIVMSG to nick
353       StrCpy(buf,"");
354       wants_space=FALSE;
355       if(wants_space) CatPrint(buf," ");
356       if(!StrICmp(type,"TAGMSG")) //Don't include ':'
357         CatPrint(buf, ":%s!%s@%s %s %s\r\n", tx_client->nick, tx_client->username,
358               tx_client->host, type, dst);
359       else 
360         CatPrint(buf, ":%s!%s@%s %s %s :%s\r\n", tx_client->nick, tx_client->username,
361               tx_client->host, type, dst, msg);
362       GihonFifoIns(rx_client->msgs, StrNew(buf));
363       Free(buf);
364 //Echo to self
365       buf=MStrPrint("<<me>> %s",msg);
366       IrcAddPrivMsgToUserMail(rx_client,tx_client->nick,buf);
367       Free(buf);
368       IrcAddPrivMsgToUserMail(tx_client,dst,msg);
369       Free(msg);
370       return;
371     }
372     rx_client = rx_client->next;
373   }
374   IrcChannel *rx_channel = channel_head->next;
375   IrcUser *rx_user;
376   while (rx_channel)
377     {
378     if (!StrCmp(rx_channel->name, dst))
379       { //PRIVMSG to channel
380       if(!IrcClientCanTalkOnChannel(rx_channel,tx_client)) {
381         StrPrint(buf, ":%s 404 %s %s :You cant talk on this channel.\r\n",
382             ircd_hostname, tx_client->username, rx_channel->name);
383         GihonFifoIns(tx_client->msgs, StrNew(buf));
384         Free(buf);
385         Free(msg);
386         return;
387       }
388       rx_user = rx_channel->users->next;
389       IrcAddToBacklog(rx_channel,tx_client->nick,msg);
390       while (rx_user)
391         {
392         rx_client = rx_user->client;
393         if (rx_client!=tx_client)
394           {
395           StrCpy(buf,"");
396           wants_space=FALSE;
397           if(wants_space) CatPrint(buf," ");
398           if(!StrICmp(type,"TAGMSG")) //Don't include ':'
399             CatPrint(buf, ":%s!%s@%s %s %s\r\n", tx_client->nick, tx_client->username,
400                   tx_client->host, type, dst);
401           else 
402             CatPrint(buf, ":%s!%s@%s %s %s :%s\r\n", tx_client->nick, tx_client->username,
403                   tx_client->host, type, dst, msg);
404           GihonFifoIns(rx_client->msgs, StrNew(buf));
405         }
406         rx_user = rx_user->next;
407       }
408       Free(buf);
409       Free(msg);
410       return;
411     }
412     rx_channel = rx_channel->next;
413   }
414 //No matches,try sending to user mail
415   IrcAddPrivMsgToUserMail(tx_client,dst,msg);
416   Free(buf);
417   buf=MStrPrint("<<me>> %s",msg);
418   IrcClient dummy;
419   MemSet(&dummy,0,sizeof(IrcClient));
420   dummy.nick=dst;
421   dummy.username=dst;
422   IrcAddPrivMsgToUserMail(&dummy,tx_client->nick,buf);
423   Free(buf);
424   Free(msg);
425 }
426 U0 IrcClientPrivMsg(IrcClient *tx_client,U8 *dst, U8 *msg) {
427   return IrcClient_SendOut(tx_client,"PRIVMSG",dst,msg);
428 }
429 U0 IrcClientTagMsg(IrcClient *tx_client, U8 *dst) {
430   return IrcClient_SendOut(tx_client,"TAGMSG",dst,"");
431 }
432 
433 U0 IrcClientQuit(U8 *dst, IrcClient *tx_client, U8 *msg=NULL)
434 {
435   U8 *buf = CAlloc(4096);
436   IrcClient *rx_client = client_head->next;
437   IrcChannel *rx_channel = channel_head->next;
438   IrcUser *rx_user;
439   while (rx_channel)
440     {
441     if (rx_channel->name&&!StrCmp(rx_channel->name, dst))
442       { //PRIVMSG to channel
443       rx_user = rx_channel->users->next;
444       while (rx_user)
445         {
446         rx_client = rx_user->client;
447         if (msg)
448           {
449           StrPrint(buf, ":%s!%s@%s QUIT :%s\r\n", tx_client->nick, tx_client->username,
450                 tx_client->host, msg);
451         }
452         else
453           {
454           StrPrint(buf, ":%s!%s@%s QUIT\r\n", tx_client->nick, tx_client->username,
455                 tx_client->host);
456         }
457         GihonFifoIns(rx_client->msgs, StrNew(buf));
458         rx_user = rx_user->next;
459       }
460       return;
461     }
462     rx_channel = rx_channel->next;
463   }
464   Free(buf);
465 }
466 
467 U0 IrcClientWho(IrcClient *client, U8 *dst)
468 {
469   U8 *buf = CAlloc(4096);
470   IrcChannel *channel = channel_head->next;
471   IrcUser *user;
472   while (channel)
473     {
474     if (!StrCmp(channel->name, dst))
475       {
476       user = channel->users->next;
477       while (user)
478         {
479         StrPrint(buf, ":%s 352 %s %s %s %s * %s H :0 %s\r\n", ircd_hostname, client->username, dst,
480               user->client->username, user->client->host, user->client->nick,
481               user->client->realname);
482         GihonFifoIns(client->msgs, StrNew(buf));
483         user = user->next;
484       }
485       StrPrint(buf, ":%s 315 %s %s :End of /WHO list.\r\n", ircd_hostname, client->username, dst);
486       GihonFifoIns(client->msgs, StrNew(buf));
487       Free(buf);
488       return;
489     }
490     channel = channel->next;
491   }
492   Free(buf);
493 }
494 I64 RecieveLn(IrcClient *client,U8 *buf,I64 max,I64) {
495   I64 idx=0;
496   I64 s = client->s;
497   idx=recvLine(s,buf,max,0);
498   return idx;
499 }
500 U0 IrcClientRxHandler(I64 s)
501 {
502   IrcClient *client = CAlloc(sizeof(IrcClient),gihon_task);
503   client->task = Fs;
504   client->s = s;
505   client->ip = 0;
506   client->limit = IRCD_LIMIT_MAX;
507   client->nick=StrNew(ICRD_NULL_NICK,gihon_task);
508   client->msgs = FifoI64New(IRCD_TXFIFO_SIZE,gihon_task);
509   client->idle = __GetTicks;
510   IrcClientAdd(client);
511 
512 //Allow client to connect first
513   IrcClientNotice(client, "Welcome to Gihon IRC Server!");
514   IrcClientNotice(client, "This server is running Gihon, an IRCd for TempleOS");
515   I64 err = NULL;
516   U8 *rxbuf = CAlloc(IRCD_RXBUF_SIZE);
517   while (err > -1 && !client->disconnected)
518     {
519     err = RecieveLn(client, rxbuf, IRCD_RXBUF_SIZE, 0);
520 //AdamLog(rxbuf);
521     //AdamLog("\n");
522     client->limit--;
523     if (client->limit)
524       {
525       StrUtil(rxbuf,SUF_REM_LEADING|SUF_REM_TRAILING);
526       IrcParseCommand(client, rxbuf);
527     }
528     else
529       {
530       err = -1;
531     }
532     Sleep(10);
533   }
534   client->disconnected = TRUE;
535   Free(rxbuf);
536   IrcClientDel(client);
537 }
538 
539 U0 IrcClientTxHandler()
540 {
541   I64 sec = NULL;
542   CDateStruct ds;
543   U8 *buf = CAlloc(4096);
544   I64 msg = NULL;
545   IrcClient *client;
546   while (1)
547     {
548 st_disconnect:
549     client = client_head->next;
550     while (client)
551       {
552       if (client->disconnected)
553         {
554 discon:
555         client->disconnected = TRUE;
556       }
557       client = client->next;
558     }
559 
560     if (sec != ds.sec)
561       {
562       client = client_head->next;
563       while (client)
564         {
565         client->limit = Min(IRCD_LIMIT_MAX, client->limit+1);
566         client = client->next;
567       }
568       sec = ds.sec;
569     }
570 
571     client = client_head->next;
572     while (client)
573       {
574 
575       if (client->idle+IRCD_PING_INTERVAL <= __GetTicks)
576         {
577         StrPrint(buf, "PING :%s\r\n", ircd_hostname);
578         GihonFifoIns(client->msgs, StrNew(buf));
579         if(client->missed_pings>=3)
580           client->disconnected = TRUE;
581         client->missed_pings++;
582         client->idle = IRCD_PING_INTERVAL + __GetTicks;
583       }
584       while (FifoI64Cnt(client->msgs))
585         {
586         FifoI64Rem(client->msgs, &msg);
587         send(client->s, msg, StrLen(msg),0);
588         Free(msg);
589       }
590       client = client->next;
591     }
592     Date2Struct(&ds, Now);
593     Sleep(10);
594   }
595 }