001 I64 Str2UserFlags(U8 *str) {
002   I64 ret=0;
003   if(!str) return ret;
004   if(StrOcc(str,'v')) ret|=IRCD_USERMODE_v;
005   if(StrOcc(str,'q')) ret|=IRCD_USERMODE_q;
006   if(StrOcc(str,'a')) ret|=IRCD_USERMODE_a;
007   if(StrOcc(str,'o')) ret|=IRCD_USERMODE_o;
008   if(StrOcc(str,'h')) ret|=IRCD_USERMODE_h;
009   return ret;
010 }
011 U8 *Flags2UserModeStr(I64 flags) {
012   U8 *ret=MAlloc(16);
013   StrCpy(ret,"");
014   if(flags&IRCD_USERMODE_v) CatPrint(ret,"v");
015   if(flags&IRCD_USERMODE_q) CatPrint(ret,"q");
016   if(flags&IRCD_USERMODE_o) CatPrint(ret,"o");
017   if(flags&IRCD_USERMODE_a) CatPrint(ret,"a");
018   if(flags&IRCD_USERMODE_h) CatPrint(ret,"h");
019   return ret;
020 }
021 
022 
023 
024 U0 IrcChannelAdd(U8 *name)
025 {
026   IrcChannel *channel = CAlloc(sizeof(IrcChannel),gihon_task);
027   channel->name = StrNew(name,gihon_task);
028   channel->topic = CAlloc(4096,gihon_task);
029   channel->users = CAlloc(sizeof(IrcUser),gihon_task);
030   channel->backlog=FifoI64New(BACKLOG_FIFO_SZ,gihon_task);
031   IrcChannel *channels = channel_head;
032   while (channels->next)
033   {
034     channels = channels->next;
035   }
036   channel->prev = channels;
037   channels->next = channel;
038   ircd_chans_total++;
039 }
040 
041 U0 IrcChannelDel(IrcChannel *channel)
042 {
043   U8 *msg;
044   IrcChannel *prev = channel->prev;
045   IrcChannel *next = channel->next;
046   if(prev) prev->next = next;
047   if(next) next->prev = prev;
048   while(FifoI64Rem(channel->backlog,&msg))
049     Free(msg);
050   FifoI64Del(channel->backlog);
051   Free(channel->name);
052   Free(channel);
053   ircd_chans_total--;
054 }
055 
056 U0 IrcList(IrcClient *client) {
057   IrcChannel *channel = channel_head;
058   IrcUser *u;
059   U8 buf[4096];
060   I64 users;
061   GihonFifoIns(client->msgs, MStrPrint(":%s 321 %s Channel :Users  Name\n",ircd_hostname,client->nick));
062   while (channel)
063     {
064     if (channel->name) {
065       u=channel->users;
066       users=0;
067       while(u&&(u=u->next)) 
068         users++;
069       StrPrint(buf,":%s 322 %s %s %d :'%s'\n",ircd_hostname,client->nick,channel->name,users,channel->topic);
070       GihonFifoIns(client->msgs, StrNew(buf));
071     }
072     channel = channel->next;
073   }  
074   GihonFifoIns(client->msgs, MStrPrint(":%s 323 %s :End of /LIST\n",ircd_hostname,client->nick));
075 }
076 
077 IrcChannel *IrcGetChanByName(U8 *channame)
078 {
079   IrcChannel *channel = channel_head;
080   while (channel)
081     {
082     if (channel->name&&!StrCmp(channel->name, channame)) return channel;
083     channel = channel->next;
084   }
085   return NULL;
086 }
087 
088 U0 IrcChannelUserAdd(IrcChannel *channel, IrcClient *client, U64 flags=NULL)
089 {
090   IrcUser *user = CAlloc(sizeof(IrcUser),gihon_task);
091   IrcUser *users = channel->users;
092   while(users->next) users=users->next;
093   user->client = client;
094   user->flags = flags;
095   user->prev = users;
096   users->next = user;
097 }
098 
099 Bool IrcChannelUserDel(IrcChannel *channel, IrcClient *client)
100 {
101   Bool removed=FALSE;
102   IrcUser *users = channel->users->next;
103   IrcUser *prev = NULL;
104   IrcUser *next = NULL;
105 re_enter:
106   while (users)
107     {
108     if (users->client==client)
109       {
110       prev=users->prev;
111       next=users->next;
112       if(prev) prev->next = next;
113       if(next) next->prev = prev;
114       Free(users);
115       users=next;
116       removed=TRUE;
117 //Maybe there are multiple references to a user in a channel
118       goto re_enter;
119     }
120     users = users->next;
121   }
122   return removed;
123 }
124 
125 U0 IrcChannelTopic(U8 *channame, IrcClient *client, U8 *topic)
126 {
127   U8 *buf = CAlloc(4096);
128   IrcChannel *channel = IrcGetChanByName(channame);
129   IrcUser *user;
130   IrcUser *users;
131   if (channel)
132     {
133     user = channel->users->next;
134     while (user)
135       {
136       if (user->client==client)
137         {
138         if (user->flags >= IRCD_USERMODE_o&&client->logged_in)
139           {
140           StrPrint(channel->topic, topic);
141           IrcSaveChannelTopic(channel,topic);
142           users = channel->users->next;
143           while (users)
144             {
145             StrPrint(buf, ":%s!%s@%s TOPIC %s :%s\r\n", client->nick, client->username,
146                   client->host, channame, channel->topic);
147             GihonFifoIns(users->client->msgs, StrNew(buf));
148             users = users->next;
149           }
150         }
151         else
152           {
153           StrPrint(buf, ":%s 482 %s %s :You do not have access to change the topic on this channel\r\n",
154                 ircd_hostname, client->username, channame);
155           GihonFifoIns(client->msgs, StrNew(buf));
156         }
157         Free(buf);
158         return;
159       }
160       user = user->next;
161     }
162   }
163   Free(buf);
164 }
165 
166 #include "MessageTag.HC";
167 CDate IrcPrsTimeTag(U8 *str,CDateStruct *ds=NULL) {
168   U8 *buf=IrcParseMessageTag(str,"time");
169   CDateStruct _ds;
170   if(!ds) ds=&_ds;
171   MemSet(ds,0,sizeof CDateStruct);
172   if(!buf) return 0;
173   I64 yr,mon,day,hr,mn,sc,s100;
174   StrScan(buf,"%d-%d-%dT%d:%d:%d.%dZ",&yr,&mon,&day,
175         &hr,&mn,&sc,&s100
176         );
177   ds->year=yr;
178   ds->mon=mon;
179   ds->day_of_mon=day;
180   ds->hour=hr;
181   ds->min=mn;
182   ds->sec=sc;
183   ds->sec100=s100;
184   Free(buf);
185   return Struct2Date(ds);
186 }
187 I64 DateCmp(CDate a,CDate b) {
188   if(a.date>b.date) return 1;
189   if(a.date<b.date) return -1;
190   if(a.time>b.time) return 1;
191   if(a.time<b.time) return -1;
192   return 0;
193 }
194 U0 IrcBacklog(IrcClient *to,U8 *name) {
195   IrcChannel *channel = channel_head->next;
196   CFifoI64 *shalow;
197   U8 *tmp,buf[4096],who[4096],msg[4096],time_tag[4096];
198   U8 *msg_ptr=msg,*who_ptr=who,*time_tag_ptr=time_tag;
199   while(channel) {
200     if(channel->name&&!StrCmp(channel->name,name)) {
201       shalow=MAllocIdent(channel->backlog);
202       while(FifoI64Rem(shalow,&tmp)) {
203         StrScan(tmp," %s:%s",&who_ptr,&msg_ptr);
204         StrPrint(buf, ":%s!%s@%s PRIVMSG %s :%s\r\n", who, to->username,to->host, name, msg);
205         GihonFifoIns(to->msgs, StrNew(buf));
206       }
207       Free(shalow);
208       break;
209     }
210     channel=channel->next;
211   }
212 }
213 
214 
215 Bool IrcChannelJoin(U8 *_channame, IrcClient *client,U8 *password=NULL)
216 {
217   Bool ret=TRUE;
218   U64 flags  = IRCD_USERMODE_v;
219   IrcChannel *channel;
220   IrcUser *users;
221   U8 *orig = StrNew(_channame),*channame,*time_code,*user_flags_str;
222   _channame = orig;
223   while (*_channame) {
224     channame = _channame;
225     if (StrFirstOcc(channame,",")) {
226       _channame=StrFirstOcc(channame,",")+1;
227       _channame[-1]=0;
228     } else
229       _channame += StrLen(_channame);
230     if (channame[0]==':') channame++;  // Fix for Revolution IRC client?
231     channel = IrcGetChanByName(channame);
232     if (!channel)
233       {
234         if(channame[0]!='#') {
235          GihonFifoIns(client->msgs,MStrPrint(":%s 403 %s %s :Channel names need to start with '#'.\r\n",
236               ircd_hostname, client->username, channame));
237          return; 
238         }
239         user_flags_str=IrcGetChannelUserMode(channame,client->nick);
240         if(!user_flags_str) {
241           flags=IRCD_USERMODE_q|IRCD_USERMODE_o|IRCD_USERMODE_v;
242           IrcSetChannelUserMode(channame,client->nick,"oqv");
243         } else {
244           flags=Str2UserFlags(user_flags_str);
245           Free(user_flags_str);
246         }
247         IrcChannelAdd(channame);
248         channel = IrcGetChanByName(channame);
249     }
250     users=channel->users->next;
251     while(users) {
252       if(users->client==client)
253         goto already_in_chan;
254       users=users->next;
255     }
256     if (channel)
257       {
258       if(channel->password_hash) {
259         if(!password)
260           goto bad_pass;
261         if(HashStr(password)!=channel->password_hash)
262           goto bad_pass;
263       } else if(password) {
264 bad_pass:
265         ret=FALSE;
266          GihonFifoIns(client->msgs,MStrPrint(":%s 475 %s %s :Invalid channel password.\r\n",
267               ircd_hostname, client->username, channame));
268         goto already_in_chan;
269       }
270       user_flags_str=IrcGetChannelUserMode(channame,client->nick);
271       if(user_flags_str)
272         flags=Str2UserFlags(user_flags_str);
273       else
274         flags=IRCD_USERMODE_v; //TODO default flags for poo poo
275       Free(user_flags_str);
276       IrcChannelUserAdd(channel, client, flags);
277       IrcClientJoin(channame, client);
278       IrcBacklog(client,channame);
279     }
280 already_in_chan:;
281   }
282   Free(orig);
283   return ret;
284 }
285 
286 U0 IrcChannelKick(U8 *channame, IrcClient *client, U8 *nick, U8 *reason=NULL)
287 {
288   U8 *buf = CAlloc(4096);
289   IrcChannel *channel = IrcGetChanByName(channame);
290   IrcClient *kick_client = IrcGetClientByNick(nick);
291   IrcUser *user;
292   IrcUser *users;
293   if (channel && kick_client)
294     {
295     user = channel->users->next;
296     while (user)
297       {
298       if (user->client==client)
299         {
300         if (user->flags >= IRCD_USERMODE_h)
301           {
302           users = channel->users->next;
303           while (users)
304             {
305             StrPrint(buf, ":%s!%s@%s KICK %s %s :%s\r\n", client->nick, client->username,
306                   client->host, channame, nick, reason);
307             GihonFifoIns(users->client->msgs, StrNew(buf));
308             users = users->next;
309           }
310           IrcChannelUserDel(channel, kick_client);
311         }
312         else
313           {
314           StrPrint(buf, ":%s 482 %s %s :You must be a channel half-operator\r\n",
315                 ircd_hostname, client->username, channame);
316           GihonFifoIns(client->msgs, StrNew(buf));
317         }
318         Free(buf);
319         return;
320       }
321       user = user->next;
322     }
323   }
324   Free(buf);
325 }
326 
327 U0 IrcChannelMode(U8 *channame, IrcClient *client, U8 *mode, U8 *nick=NULL)
328 {
329   U64 res = 0;
330   Bool set = FALSE;
331   U8 *buf = CAlloc(4096),*flags_str;
332   IrcChannel *channel = IrcGetChanByName(channame);
333   IrcClient *mode_client = NULL;
334   IrcUser *user;
335   IrcUser *users;
336 
337   if(!channel) return;
338 
339   if(!client->logged_in) {
340     StrPrint(buf, ":%s 482 %s %s :You must logged in to use MODE.\r\n",
341           ircd_hostname, client->username, channame);
342     GihonFifoIns(client->msgs, StrNew(buf));
343     Free(buf);
344     return;
345   }
346 
347   if(!StrCmp("-k",mode)) {
348     user = channel->users->next;
349     while (user) {
350       if (user->client==client) {
351         if(user->flags>=IRCD_USERMODE_o) {
352           IrcSetChannelPassword(channel,NULL);
353           goto fin;
354         }
355       }
356       user=user->next;
357     }
358     StrPrint(buf, ":%s 482 %s %s :You have to be an operator to set remove a password.\r\n",
359           ircd_hostname, client->username, channame);
360     GihonFifoIns(client->msgs, StrNew(buf));
361     goto fin;
362   } else if(!StrCmp("+k",mode)) {
363     user = channel->users->next;
364     while (user) {
365       if (user->client==client) {
366         if(user->flags>=IRCD_USERMODE_o) {
367           IrcSetChannelPassword(channel,nick);
368           goto fin;
369         }
370       }
371       user=user->next;
372     }
373     StrPrint(buf, ":%s 482 %s %s :Only operators can set channel passwords.\r\n",
374           ircd_hostname, client->username, channame);
375     GihonFifoIns(client->msgs, StrNew(buf));
376     goto fin;
377   }
378 
379   if (nick)
380     { // Set user mode
381     mode_client = IrcGetClientByNick(nick);
382     if (!mode_client)
383       {
384 // nick does not exist?
385       Free(buf);
386       return;
387     }
388     else
389       {
390       user = channel->users->next;
391       while (user)
392         {
393         if (user->client==client)
394           {
395           if ((!StrCmp("-v", mode) || !StrCmp("+v", mode)))
396             {
397             set = TRUE;
398             if (user->flags < IRCD_USERMODE_h)
399               {
400               res = IRCD_USERMODE_h;
401             }
402           }
403           if ((!StrCmp("-h", mode) || !StrCmp("+h", mode)))
404             {
405             set = TRUE;
406             if (user->flags < IRCD_USERMODE_o)
407               {
408               res = IRCD_USERMODE_o;
409             }
410           }
411           if ((!StrCmp("-o", mode) || !StrCmp("+o", mode)))
412             {
413             set = TRUE;
414             if (user->flags < IRCD_USERMODE_q)
415               {
416               res = IRCD_USERMODE_q;
417             }
418           }
419           if (set)
420             {
421             if (!res)
422               {
423               users = channel->users->next;
424               while (users)
425                 {
426                 if (users->client==mode_client)
427                   {
428                   if (mode[0]=='-')
429                     {
430                     if (user->flags >= users->flags)
431                       {
432                       switch (mode[1])
433                         {
434                         case 'v':
435                           users->flags&=~IRCD_USERMODE_v;
436                           break;
437 //Removing an pridvledge will remove "parent privledges".
438                         case 'h':
439                           users->flags&=~IRCD_USERMODE_h;
440                         case 'o':
441                           users->flags&=~IRCD_USERMODE_o;
442                         case 'a':
443                           users->flags&=~IRCD_USERMODE_a;
444                         case 'q':
445                           users->flags&=~IRCD_USERMODE_q;
446                         default:
447                           break;
448                       }
449                     }
450                   }
451                   if (mode[0]=='+')
452                     {
453                     switch (mode[1])
454                       {
455                       case 'v':
456                         users->flags|=IRCD_USERMODE_v;
457                         break;
458                       case 'h':
459                         users->flags|=IRCD_USERMODE_h;
460                         break;
461                       case 'o':
462                         users->flags|=IRCD_USERMODE_o;
463                         break;
464                       case 'a':
465                         users->flags|=IRCD_USERMODE_a;
466                         break;
467                       case 'q':
468                         users->flags|=IRCD_USERMODE_q;
469                         break;
470                       default:
471                         break;
472                     }
473                   }
474                 }
475                 flags_str=Flags2UserModeStr(users->flags);
476                 IrcSetChannelUserMode(channame,users->client->nick,flags_str);
477                 Free(flags_str);
478                 StrPrint(buf, ":%s!%s@%s MODE %s %s %s\r\n", client->nick, client->username,client->host, channame, mode, nick);
479                 GihonFifoIns(users->client->msgs, StrNew(buf));
480                 users = users->next;
481               }
482             }
483             else
484               {
485               switch (res)
486                 {
487                 case IRCD_USERMODE_h:
488                   StrPrint(buf, ":%s 482 %s %s :You must have channel halfop access or above to set channel mode #\r\n",
489                         ircd_hostname, client->username, channame);
490                   buf[StrLen(buf)-3] = mode[1];
491                   GihonFifoIns(client->msgs, StrNew(buf));
492                   break;
493                 case IRCD_USERMODE_o:
494                   StrPrint(buf, ":%s 482 %s %s :You must have channel op access or above to set channel mode #\r\n",
495                         ircd_hostname, client->username, channame);
496                   buf[StrLen(buf)-3] = mode[1];
497                   GihonFifoIns(client->msgs, StrNew(buf));
498                   break;
499                 case IRCD_USERMODE_q:
500                   StrPrint(buf, ":%s 482 %s %s :You must be the channel owner to set channel mode #\r\n",
501                         ircd_hostname, client->username, channame);
502                   buf[StrLen(buf)-3] = mode[1];
503                   GihonFifoIns(client->msgs, StrNew(buf));
504                   break;
505                 default:
506                   break;
507               }
508             }
509           }
510           Free(buf);
511           return;
512         }
513         user = user->next;
514       }
515     }
516   }
517   else
518     { // TODO: Set channel mode
519     if (channel)
520       {
521 
522     }
523 
524   }
525 fin:
526   Free(buf);
527 }
528 
529 U0 IrcChannelPart(U8 *channame, IrcClient *client, U8 *msg=NULL)
530 {
531   IrcChannel *channel = IrcGetChanByName(channame);
532   if (channel)
533     {
534     if(IrcChannelUserDel(channel, client));
535       IrcClientPart(channame, client, msg);
536   }
537 }
538 
539 U0 IrcChannelsQuit(IrcClient *client, U8 *msg=NULL)
540 {
541   IrcChannel *channel = channel_head->next;
542   while (channel)
543     {
544     if(IrcChannelUserDel(channel, client))
545       IrcClientQuit(channel->name, client, msg);
546     channel = channel->next;
547   }
548 }