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 }