main.c (26468B)
1 /* 2 * This work is dedicated to the public domain. 3 * See COPYING file for more information. 4 */ 5 6 #include <errno.h> 7 #include <execinfo.h> 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <string.h> 11 #include <signal.h> 12 #include <time.h> 13 #include <unistd.h> 14 15 #ifdef __gnu_linux__ 16 #include <sys/epoll.h> 17 #else 18 #include <sys/event.h> 19 #endif 20 21 #define LOG_LEVEL 1 22 23 #include "htable.h" 24 #include "util.c" 25 26 #define CLONE_COOLDOWN 1 27 #define CLONE_ADDEND 10 28 #define RECONN_TIME 10 29 30 #define FD_ADDEND 100 31 #define NET_ADDEND 10 32 #define USER_ADDEND 100 33 34 #define BUFSIZE 1024 35 #define NICK_LEN 16 36 #define HANDLE_EVENTS 10 /* no. of events to handle at a time */ 37 38 #define EV_READ 1 39 #define EV_WRITE 2 40 41 enum { 42 IDLE = 0, 43 RESET, 44 CLONING, 45 EXIT 46 }; 47 48 struct fd_data { 49 char *user; /* nick */ 50 int netid; /* net index */ 51 int suffix; /* suffix count */ 52 int ready; /* joined */ 53 }; 54 55 struct fd_ref { 56 char *user; /* nick */ 57 int netid; /* net index */ 58 struct fd_ref *next; /* next node */ 59 }; 60 61 struct network { 62 int fd; /* fd */ 63 char *name; /* name */ 64 char *symb; /* symbol */ 65 char *host; /* host */ 66 char *port; /* port */ 67 char *chan; /* channel */ 68 }; 69 70 static time_t ntime = -1; /* next timeout */ 71 static int state = IDLE; /* program state */ 72 static int fifofd; /* fifo fd */ 73 static int break_evloop; /* break event loop */ 74 static char *fifopath; /* fifo path */ 75 static char msg[BUFSIZE]; /* message buffer */ 76 77 #ifdef __gnu_linux__ 78 static int epfd; /* epoll instance */ 79 #else 80 static int kqfd; /* kqueue instance */ 81 #endif 82 83 static struct network *networks; /* networks array */ 84 static int netlen; /* array length */ 85 static int netcap; /* array capacity */ 86 static struct fd_data **fdtodata; /* fd -> data pointer */ 87 static int fdcap; /* fdtodata capacity */ 88 static struct htable *usertofds; /* user -> array of fds of clones 89 indexed according to networks array */ 90 91 static struct fd_ref *reconn_list_head, /* re-connection queue */ 92 *reconn_list_tail; 93 94 /* functions prototype */ 95 void fd_register(int, int); 96 void fifo_read(void); 97 time_t event_timeout(void); 98 void fd_write(int); 99 void fd_read(int); 100 void net_add(char *, char *, char *, char *, char *); 101 void net_del(int, char *, int); 102 void user_add(char *, int, int); 103 void user_del(char *, char *); 104 void reconn_list_add(char *, int); 105 void reconn_list_del(char *, int); 106 int fd_new(int, char *); 107 void fd_del(int, char *, int); 108 void fd_reconn(int, char *); 109 int *clone_get_fds(char *, int); 110 void nick_add_symb(char *, int); 111 void privmsg_update(char *, char *, int, int); 112 void print_table(void); 113 void print_htable(void); 114 void print_users(void); 115 void print_reconn_list(void); 116 void print_border(void); 117 ssize_t writeall(int, char *); 118 119 int 120 main(int argc, char *argv[]) 121 { 122 #ifdef __gnu_linux__ 123 struct epoll_event epevs[HANDLE_EVENTS]; 124 #else 125 struct kevent kevs[HANDLE_EVENTS]; 126 struct timespec tmsp; 127 #endif 128 int i, fd, nev; /* no. of returned events to handle */ 129 time_t timeout; 130 131 /* set stdout to unbufferd */ 132 setvbuf(stdout, NULL, _IONBF, 0); 133 134 /* handle arguments */ 135 if (argc != 2) 136 fatal("usage: ticl fifo"); 137 else 138 fifopath = argv[1]; 139 140 /* init global variables */ 141 netcap = NET_ADDEND; 142 fdcap = FD_ADDEND; 143 networks = emalloc((size_t)netcap * sizeof(struct network)); 144 fdtodata = emalloc((size_t)fdcap * sizeof(struct fd_data *)); 145 usertofds = htcreate(hash_str, (key_cmp_fn *)strcmp, free, free, 146 USER_ADDEND); 147 148 #ifdef __gnu_linux__ 149 if ((epfd = epoll_create1(0)) == -1) 150 fatal("epoll_create1:"); 151 #else 152 if ((kqfd = kqueue()) == -1) 153 fatal("kqueue:"); 154 #endif 155 fifofd = fifo_open(fifopath); 156 fd_register(fifofd, EV_READ); 157 158 /* event loop */ 159 while (state != EXIT) { 160 if (ntime == -1) { 161 timeout = -1; 162 } else { 163 timeout = ntime - time(NULL); 164 if (timeout < 0) 165 timeout = 0; 166 } 167 #ifdef __gnu_linux__ 168 nev = epoll_wait(epfd, epevs, HANDLE_EVENTS, (int)timeout * 1000); 169 if (nev == -1) 170 fatal("epoll_wait:"); 171 #else 172 tmsp.tv_sec = timeout; 173 tmsp.tv_nsec = 0; 174 nev = kevent(kqfd, NULL, 0, kevs, HANDLE_EVENTS, timeout < 0 ? NULL : &tmsp); 175 if (nev == -1) 176 fatal("kevent:"); 177 #endif 178 else if (nev == 0) { 179 ntime = event_timeout(); 180 continue; 181 } 182 183 for (i = 0; i < nev; i++) { 184 #ifdef __gnu_linux__ 185 fd = epevs[i].data.fd; 186 #else 187 fd = (int)kevs[i].ident; 188 #endif 189 if (fd == fifofd) { 190 fifo_read(); 191 break; 192 #ifdef __gnu_linux__ 193 } else if (epevs[i].events & EPOLLOUT) { 194 #else 195 } else if (kevs[i].filter == EVFILT_WRITE) { 196 #endif 197 fd_write(fd); 198 #ifdef __gnu_linux__ 199 } else if (epevs[i].events & EPOLLIN) { 200 #else 201 } else if (kevs[i].filter == EVFILT_READ) { 202 #endif 203 fd_read(fd); 204 } else { 205 fatal("unknown event"); 206 } 207 208 if (reconn_list_head != NULL && ntime == -1) 209 ntime = time(0) + RECONN_TIME; 210 if (break_evloop) { 211 break_evloop = FALSE; 212 break; 213 } 214 } 215 } 216 217 /* 218 * delete and free all the networks with their users 219 * - delete in reverse order to prevent swapping. 220 */ 221 snprintf(msg, sizeof(msg), "QUIT :relay shutting down\r\n"); 222 for (i = netlen-1; i >= 0; i--) 223 net_del(i, msg, FALSE); 224 225 free(networks); 226 free(fdtodata); 227 htdestroy(usertofds); 228 return 0; 229 } 230 231 void 232 fd_register(int fd, int mode) 233 { 234 #ifdef __gnu_linux__ 235 struct epoll_event ev; 236 237 if (mode == EV_READ) 238 ev.events = EPOLLIN; 239 else if (mode == EV_WRITE) 240 ev.events = EPOLLOUT; 241 else 242 fatal("unknown event mode"); 243 244 ev.data.fd = fd; 245 if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) == -1) 246 fatal("epoll_ctl:"); 247 #else 248 struct kevent ev; 249 int filter; 250 251 if (mode == EV_READ) 252 filter = EVFILT_READ; 253 else if (mode == EV_WRITE) 254 filter = EVFILT_WRITE; 255 else 256 fatal("unknown event mode"); 257 258 EV_SET(&ev, fd, filter, EV_ADD, 0, 0, 0); 259 if (kevent(kqfd, &ev, 1, NULL, 0, NULL) == -1) 260 fatal("kevent:"); 261 #endif 262 } 263 264 void 265 fifo_read(void) 266 { 267 char buffer[BUFSIZE]; 268 char *buf; 269 char *cmd; 270 ssize_t n; 271 272 n = readline(fifofd, buffer, sizeof(buffer)); 273 if (n == -1) { 274 fatal("read:"); 275 } else if (n == 0) { 276 /* reopen fifo again */ 277 close(fifofd); 278 fifofd = fifo_open(fifopath); 279 fd_register(fifofd, EV_READ); 280 return; 281 } 282 if (*buffer == '\0') 283 return; 284 285 buf = buffer; 286 cmd = split(&buf, ' '); 287 if (strcmp(cmd, "netadd") == 0) { 288 char *name = split(&buf, ' '); 289 char *symb = split(&buf, ' '); 290 char *host = split(&buf, ' '); 291 char *port = split(&buf, ' '); 292 char *chan = buf; 293 if (!*name || !*symb || !*host || !*port || !*chan) 294 warnf("usage: netadd <name> <symbol> <host> <port> <channel>"); 295 else 296 net_add(name, symb, host, port, chan); 297 } else if (strcmp(cmd, "netdel") == 0) { 298 char *name = buf; 299 if (!*name) { 300 warnf("usage: netdel <name>"); 301 } else { 302 int i; 303 for (i = 0; i < netlen; i++) { 304 if (strcmp(name, networks[i].name) == 0) { 305 snprintf(msg, sizeof(msg), "QUIT :netdel: %s\r\n", networks[i].name); 306 net_del(i, msg, FALSE); 307 return; 308 } 309 } 310 warnf("%s: network doesn't exist", name); 311 } 312 } else if (strcmp(cmd, "print") == 0) { 313 print_table(); 314 } else if (strcmp(cmd, "htable") == 0) { 315 print_htable(); 316 } else if (strcmp(cmd, "users") == 0) { 317 print_users(); 318 } else if (strcmp(cmd, "reconn") == 0) { 319 print_reconn_list(); 320 } else if (strcmp(cmd, "exit") == 0) { 321 state = EXIT; 322 } else { 323 warnf("%s is not a command", cmd); 324 } 325 } 326 327 time_t 328 event_timeout(void) 329 { 330 static struct htiter it; 331 332 int i, j, *fds; 333 char *user; 334 335 if (state == IDLE) { 336 struct fd_ref *tmp; 337 debug(1, "Reconnecting"); 338 while(reconn_list_head != NULL) { 339 user = reconn_list_head->user; 340 i = reconn_list_head->netid; 341 342 if (user == NULL) { 343 networks[i].fd = fd_new(i, user); 344 } else { 345 if ((fds = htsearch(usertofds, user)) == NULL) 346 fatal("%s: user doesn't exist", user); 347 if (fds[i] == -3 && 348 networks[i].fd > 0 && 349 fdtodata[networks[i].fd]->ready) 350 fds[i] = fd_new(i, user); 351 } 352 tmp = reconn_list_head; 353 reconn_list_head = reconn_list_head->next; 354 free(tmp); 355 } 356 reconn_list_tail = reconn_list_head; 357 return -1; 358 } else if (state == RESET) { 359 state = CLONING; 360 htiter_init(&it); 361 } 362 debug(1, "."); 363 364 j = 0; 365 while (htiterate(usertofds, &it)) { 366 user = (char *)it.node->key; 367 fds = (int *)it.node->val; 368 for (i = 0; i < netlen; i++) { 369 if (fds[i] == 0 && networks[i].fd > 0 && 370 fdtodata[networks[i].fd]->ready) 371 fds[i] = fd_new(i, user); 372 } 373 j++; 374 if (j >= CLONE_ADDEND) 375 return time(NULL) + CLONE_COOLDOWN; 376 } 377 state = IDLE; 378 return -1; 379 } 380 381 void 382 fd_write(int fd) 383 { 384 #ifdef __gnu_linux__ 385 struct epoll_event ev; 386 ev.events = EPOLLIN; 387 ev.data.fd = fd; 388 if (epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev) == -1) 389 fatal("epoll_ctl:"); 390 #else 391 struct kevent ev; 392 EV_SET(&ev, fd, EVFILT_WRITE, EV_DISABLE, 0, 0, 0); 393 if (kevent(kqfd, &ev, 1, NULL, 0, NULL) == -1) 394 fatal("kevent:"); 395 fd_register(fd, EV_READ); 396 #endif 397 if (fdtodata[fd]->user == NULL) { /* watcher */ 398 snprintf(msg, sizeof(msg), "NICK watcher\r\nUSER watcher 0 * :watcher\r\n"); 399 writeall(fd, msg); 400 } else { /* user */ 401 snprintf(msg, sizeof(msg), "NICK %s\r\nUSER user 0 * :user\r\n", fdtodata[fd]->user); 402 writeall(fd, msg); 403 } 404 } 405 406 void 407 fd_read(int fd) 408 { 409 char buffer [BUFSIZE]; 410 char backup [BUFSIZE]; 411 char wnick [NICK_LEN]; /* watcher nick */ 412 char *buf; 413 char *cmd; 414 char *nick; 415 int i, netid; 416 ssize_t n; 417 418 netid = fdtodata[fd]->netid; 419 420 n = readline(fd, buffer, sizeof(buffer)); 421 if (n == -1) { 422 warnf("%d: read:", fd); 423 snprintf(msg, sizeof(msg), "QUIT :read failed\r\n"); 424 fd_reconn(fd, msg); 425 return; 426 } else if (n == 0) { 427 warnf("%d: read: connection closed", fd); 428 snprintf(msg, sizeof(msg), "QUIT :connection closed\r\n"); 429 fd_reconn(fd, msg); 430 return; 431 } 432 if (*buffer == '\0') 433 return; 434 435 /* clone the buffer */ 436 strlcpy(backup, buffer, sizeof(backup)); 437 buf = buffer; 438 439 /* set watcher nick */ 440 strlcpy(wnick, "watcher", sizeof(wnick)); 441 for (i = 0; i < fdtodata[fd]->suffix; i++) 442 strcat(wnick, "_"); 443 444 /* first column */ 445 cmd = split(&buf, ' '); 446 if (strcmp(cmd, "NOTICE") == 0) { /* ignore */ 447 return; 448 } else if (strcmp(cmd, "ERROR") == 0) { 449 warnf("%d: %s", fd, backup); 450 snprintf(msg, sizeof(msg), "QUIT :ERROR\r\n"); 451 fd_reconn(fd, msg); 452 return; 453 } else if (strcmp(cmd, "PING") == 0) { 454 snprintf(msg, sizeof(msg), "PONG %s\r\n", buf); 455 writeall(fd, msg); 456 return; 457 } 458 /* strip nick from first column */ 459 nick = split(&cmd, '!'); 460 if (nick[0] == ':') 461 nick++; 462 463 /* second column */ 464 cmd = split(&buf, ' '); 465 466 /* ignore all the info messages */ 467 if ((strcmp(cmd, "002") == 0) || 468 (strcmp(cmd, "003") == 0) || 469 (strcmp(cmd, "004") == 0) || 470 (strcmp(cmd, "005") == 0) || 471 (strcmp(cmd, "251") == 0) || 472 (strcmp(cmd, "252") == 0) || 473 (strcmp(cmd, "253") == 0) || /* unknown connection(s) */ 474 (strcmp(cmd, "254") == 0) || 475 (strcmp(cmd, "255") == 0) || 476 (strcmp(cmd, "265") == 0) || 477 (strcmp(cmd, "266") == 0) || 478 (strcmp(cmd, "250") == 0) || 479 (strcmp(cmd, "332") == 0) || 480 (strcmp(cmd, "333") == 0) || 481 (strcmp(cmd, "375") == 0) || 482 (strcmp(cmd, "372") == 0) || 483 (strcmp(cmd, "376") == 0) || 484 (strcmp(cmd, "396") == 0) || 485 (strcmp(cmd, "366") == 0) || 486 (strcmp(cmd, "MODE") == 0) || 487 (strcmp(cmd, "TOPIC") == 0) || 488 (strcmp(cmd, "NOTICE") == 0)) { 489 return; 490 } else if (strcmp(cmd, "432") == 0) { /* Erroneous Nickname */ 491 fatal("%d: %s", fd, backup); 492 } else if (strcmp(cmd, "433") == 0) { /* Nickname already in use */ 493 split(&buf, ' '); 494 nick = split(&buf, ' '); 495 strcat(nick, "_"); 496 fdtodata[fd]->suffix++; 497 if (strlen(nick) > NICK_LEN) { 498 warnf("%s: nickname too long", nick); 499 snprintf(msg, sizeof(msg), "QUIT :%s: nickname too long\r\n", nick); 500 if (fdtodata[fd]->user == NULL) 501 net_del(netid, msg, FALSE); 502 else 503 fd_del(fd, msg, FALSE); 504 return; 505 } else { 506 snprintf(msg, sizeof(msg), "NICK %s\r\n", nick); 507 writeall(fd, msg); 508 } 509 return; 510 } else if (strcmp(cmd, "001") == 0) { 511 snprintf(msg, sizeof(msg), "JOIN %s\r\n", networks[netid].chan); 512 writeall(fd, msg); 513 return; 514 } else if (strcmp(cmd, "PRIVMSG") == 0) { 515 char privmsg[BUFSIZE] = ""; 516 int *fds; 517 518 if (fdtodata[fd]->user == NULL) { /* if watcher */ 519 nick_add_symb(nick, netid); 520 if ((fds = htsearch(usertofds, nick)) == NULL) 521 return; 522 523 split(&buf, ':'); /* set buf to msg */ 524 for (i = 0; i < netlen; i++) { 525 printf("%s\n", buf); 526 if (fds[i] > 0) { 527 privmsg_update(privmsg, buf, netid, fds[i]); 528 snprintf(msg, sizeof(msg), "PRIVMSG %s :%s\r\n", networks[i].chan, privmsg); 529 writeall(fds[i], msg); 530 } 531 } 532 } else { 533 char *netsymb; 534 char *user = split(&buf, ' '); 535 536 /* ignore messages from channel (it is handled by watcher) */ 537 if (user[0] == '#' || user[0] == '&') 538 return; 539 540 nick_add_symb(nick, netid); 541 if ((fds = htsearch(usertofds, nick)) == NULL) 542 return; 543 544 /* split user nick and network symbol */ 545 *strrchr(user, ']') = '\0'; 546 netsymb = strrchr(user, '['); 547 *netsymb++ = '\0'; 548 549 /* get the network index */ 550 for (i = 0; i < netlen; i++) { 551 if (strcmp(netsymb, networks[i].symb) == 0) 552 break; 553 } 554 555 split(&buf, ':'); /* set buf to msg */ 556 privmsg_update(privmsg, buf, netid, fds[i]); 557 snprintf(msg, sizeof(msg), "PRIVMSG %s :%s\r\n", user, privmsg); 558 writeall(fds[i], msg); 559 } 560 return; 561 } 562 else if (strcmp(cmd, "353") == 0) { 563 fdtodata[fd]->ready = TRUE; 564 /* FALLBACK */ 565 } 566 567 /* from now, the messages are handled by watcher */ 568 if (fdtodata[fd]->user != NULL) /* if clone */ 569 return; 570 571 if (strcmp(cmd, "353") == 0) { 572 char *nick; 573 split(&buf, ':'); 574 state = RESET; 575 ntime = 0; 576 577 /* then add all new users */ 578 while (*(nick = split(&buf, ' ')) != '\0') { 579 if (*nick == '@' || 580 *nick == '&' || 581 *nick == '~' || 582 *nick == '%' || 583 *nick == '+' || 584 *nick == '\\') 585 nick++; 586 if (strcmp(nick, wnick) != 0) 587 user_add(nick, netid, FALSE); 588 } 589 return; 590 } else if (strcmp(cmd, "JOIN") == 0) { 591 /* if real user */ 592 if ((strcmp(nick, wnick) != 0) && 593 (clone_get_fds(nick, netid) == NULL)) { 594 if (state != IDLE) 595 warnf("%s: ignored (network cloning)", nick); 596 else 597 user_add(nick, netid, TRUE); 598 } 599 return; 600 } else if ((strcmp(cmd, "QUIT") == 0) || (strcmp(cmd, "PART") == 0)) { 601 split(&buf, ':'); /* ignore ':' and assign QUIT/PART msg to buf */ 602 snprintf(msg, sizeof(msg), "QUIT :%s\r\n", buf); 603 nick_add_symb(nick, netid); 604 if (htsearch(usertofds, nick) != NULL) 605 user_del(nick, msg); 606 return; 607 } else if (strcmp(cmd, "NICK") == 0) { 608 int *fds, i; 609 char *newnick; 610 611 nick_add_symb(nick, netid); 612 if ((fds = htsearch(usertofds, nick)) == NULL) 613 return; 614 615 /* set buf to new nick */ 616 split(&buf, ':'); 617 /* allocate a new nick with net symbol and replace the old one */ 618 newnick = emalloc((strlen(buf) + strlen(networks[netid].symb) + 2 + 1) * sizeof(char)); 619 sprintf(newnick, "%s[%s]", buf, networks[netid].symb); 620 snprintf(msg, sizeof(msg), "NICK %s\r\n", newnick); 621 for (i = 0; i < netlen; i++) { 622 if (fds[i] > 0) { 623 fdtodata[fds[i]]->user = newnick; 624 fdtodata[fds[i]]->suffix = 0; 625 writeall(fds[i], msg); 626 } else if (fds[i] == -3) { 627 reconn_list_del(nick, i); 628 reconn_list_add(newnick, i); 629 } 630 } 631 htmodkey(usertofds, nick, newnick); 632 return; 633 } else if (strcmp(cmd, "KICK") == 0) { 634 /* :<user_who_kicked>!~username@host KICK <channel> <user_who_is_being_kicked> :<kick_msg> */ 635 int *fds; 636 char *user; 637 638 split(&buf, ' '); /* channel name */ 639 user = split(&buf, ' '); /* user who is being kicked */ 640 split(&buf, ':'); /* ignore ':' and store reason to buf */ 641 642 /* if watcher is being kicked, delete the network */ 643 if (strcmp(user, wnick) == 0) { 644 snprintf(msg, sizeof(msg), "QUIT :netdel: %s (%s is kicked by %s)\r\n", 645 networks[netid].name, wnick, nick); 646 fd_reconn(fd, msg); 647 return; 648 } 649 650 /* if message is from a real user, delete the user */ 651 if ((fds = clone_get_fds(user, netid)) == NULL) { 652 snprintf(msg, sizeof(msg), "QUIT :userdel: %s (kicked by %s)\r\n", user, nick); 653 nick_add_symb(user, netid); 654 user_del(user, msg); 655 return; 656 } 657 658 /* delete the kicked clone */ 659 snprintf(msg, sizeof(msg), "QUIT :kicked by %s\r\n", nick); 660 fd_reconn(fds[netid], msg); 661 662 /* get the original user netid */ 663 for (i = 0; i < netlen; i++) { 664 if (fds[i] == -1) 665 break; 666 } 667 /* send notice in the channel through watcher */ 668 if (networks[i].fd > 0) { 669 snprintf(msg, sizeof(msg), "PRIVMSG %s :%s is kicked by %s [%s]\r\n", 670 networks[i].chan, user, nick, buf); 671 writeall(networks[i].fd, msg); 672 } 673 return; 674 } 675 warnf("%d: %s", fd, backup); 676 return; 677 } 678 679 void 680 net_add(char *name, char *symb, char *host, char *port, char *chan) 681 { 682 struct network *n; 683 int i; 684 685 for (i = 0; i < netlen; i++) { 686 if (strcmp(networks[i].name, name) == 0) { 687 warnf("%s: network name already exists", name); 688 return; 689 } 690 if (strcmp(networks[i].symb, symb) == 0) { 691 warnf("%s: network symbol already exists", symb); 692 return; 693 } 694 if ((strcmp(networks[i].host, host) == 0) && 695 (strcmp(networks[i].port, port) == 0) && 696 (strcmp(networks[i].chan, chan) == 0)) { 697 warnf("%s:%s%s: network configuration already exists", 698 host, port, chan); 699 return; 700 } 701 } 702 703 /* if full, resize the network and user fds */ 704 if (netlen == netcap) { 705 struct htiter it; 706 htiter_init(&it); 707 708 networks = erealloc(networks, (size_t)(netcap + NET_ADDEND) * 709 sizeof(struct network)); 710 while (htiterate(usertofds, &it)) { 711 it.node->val = erealloc(it.node->val, 712 (size_t)(netcap + NET_ADDEND) * 713 sizeof(int)); 714 /* zero out the extended array */ 715 for (i = netcap; i < netcap + NET_ADDEND; i++) 716 ((int *)it.node->val)[i] = 0; 717 } 718 netcap += NET_ADDEND; 719 } 720 721 /* add a network */ 722 n = &networks[netlen]; 723 n->name = strdup(name); 724 n->symb = strdup(symb); 725 n->host = strdup(host); 726 n->port = strdup(port); 727 n->chan = strdup(chan); 728 n->fd = fd_new(netlen, NULL); 729 netlen++; 730 } 731 732 void 733 net_del(int netid, char *msg, int reconnect) 734 { 735 int *fds; 736 char *user; 737 struct network *n; 738 struct htiter lit, it; /* last, current iterator */ 739 740 n = &networks[netid]; 741 htiter_init(&it); 742 htiter_init(&lit); 743 while (htiterate(usertofds, &it)) { 744 user = (char *)it.node->key; 745 fds = (int *)it.node->val; 746 if (fds[netid] == -1) { 747 user_del(it.node->key, msg); 748 it = lit; /* this node is deleted */ 749 } else { 750 if (fds[netid] > 0) 751 fd_del(fds[netid], msg, FALSE); 752 else if (fds[netid] == -3) 753 reconn_list_del(user, netid); 754 755 if (!reconnect) { 756 if(netid != netlen-1) { 757 fds[netid] = fds[netlen-1]; 758 if (fds[netid] > 0) { 759 fdtodata[fds[netid]]->netid = netid; 760 } else if (fds[netid] == -3) { 761 reconn_list_del(user, netlen-1); 762 reconn_list_add(user, netid); 763 } 764 } 765 fds[netlen-1] = 0; 766 } else { 767 fds[netid] = 0; 768 } 769 } 770 lit = it; 771 } 772 773 if (n->fd > 0) 774 fd_del(n->fd, msg, reconnect); 775 else if (n->fd == -3) 776 reconn_list_del(NULL, netid); 777 778 if (!reconnect) { 779 free(n->name); 780 free(n->symb); 781 free(n->host); 782 free(n->port); 783 free(n->chan); 784 /* swap */ 785 if(netid != netlen-1) { 786 networks[netid] = networks[netlen-1]; 787 if (networks[netid].fd > 0) { 788 fdtodata[n->fd]->netid = netid; 789 } else if (networks[netid].fd == -3) { 790 reconn_list_del(NULL, netlen-1); 791 reconn_list_add(NULL, netid); 792 } 793 } 794 netlen--; 795 } 796 } 797 798 void 799 user_add(char *unick, int netid, int clone) 800 { 801 size_t len; 802 char *nick; 803 int *fds; 804 805 len = strlen(unick) + strlen(networks[netid].symb) + 2 + 1; 806 if (len-1 > NICK_LEN) { 807 warnf("%s[%s]: nickname too long", unick, networks[netid].symb); 808 return; 809 } 810 811 /* resize hash table if storage is low */ 812 if ((usertofds->cap - usertofds->len) < USER_ADDEND) 813 usertofds = htresize(usertofds, usertofds->cap + USER_ADDEND); 814 815 /* allocate a new user */ 816 nick = emalloc(len * sizeof(char)); 817 fds = ecalloc((size_t)netcap, sizeof(int)); 818 sprintf(nick, "%s[%s]", unick, networks[netid].symb); 819 fds[netid] = -1; 820 821 if (clone) { 822 int i; 823 for (i = 0; i < netlen; i++) { 824 if (fds[i] == 0 && networks[i].fd > 0 && 825 fdtodata[networks[i].fd]->ready) 826 fds[i] = fd_new(i, nick); 827 } 828 } 829 830 if (htinsert(usertofds, nick, fds) == -1) 831 fatal("%s: user already exists", nick); 832 } 833 834 void 835 user_del(char *user, char *msg) 836 { 837 int i, *fds; 838 839 if ((fds = htsearch(usertofds, user)) == NULL) 840 fatal("%s: user doesn't exist", user); 841 for (i = 0; i < netlen; i++) { 842 if (fds[i] > 0) { 843 fd_del(fds[i], msg, FALSE); 844 } else if (fds[i] == -3) { 845 reconn_list_del(user, i); 846 } 847 } 848 htremove(usertofds, user); 849 } 850 851 void 852 reconn_list_add(char *user, int netid) 853 { 854 struct fd_ref *node; 855 856 node = emalloc(sizeof(struct fd_ref)); 857 node->user = user; 858 node->netid = netid; 859 node->next = NULL; 860 if (reconn_list_tail == NULL) { 861 reconn_list_head = reconn_list_tail = node; 862 } else { 863 reconn_list_tail->next = node; 864 reconn_list_tail = reconn_list_tail->next; 865 } 866 } 867 868 void 869 reconn_list_del(char *user, int netid) 870 { 871 struct fd_ref *n, *pn; /* current and previous node */ 872 873 n = reconn_list_head; 874 pn = NULL; 875 while (n != NULL) { 876 if (n->netid == netid && 877 ((user == NULL && n->user == NULL) || 878 (user != NULL && n->user != NULL && 879 (strcmp(n->user, user) == 0)))) { 880 if (n == reconn_list_head && n == reconn_list_tail) 881 reconn_list_head = reconn_list_tail = NULL; 882 else if (n == reconn_list_head) 883 reconn_list_head = n->next; 884 else if (n == reconn_list_tail) 885 reconn_list_tail = pn; 886 else 887 pn->next = n->next; 888 free(n); 889 return; 890 } 891 pn = n; 892 n = n->next; 893 } 894 fatal("%d:%s: failed to find in re-connection list", netid, user); 895 } 896 897 int 898 fd_new(int netid, char *user) 899 { 900 int fd; 901 struct network *n; 902 struct fd_data *data; 903 904 n = &networks[netid]; 905 fd = dial(n->host, n->port); 906 if (fd == -1) { 907 warnf("%s:%s: failed to connect", n->host, n->port); 908 return -2; 909 } 910 fd_register(fd, EV_WRITE); 911 912 if (user == NULL) 913 debug(1, "%d: netadd: %s[%s]", fd, n->name, n->symb); 914 else 915 debug(1, "%d: add[%s]: %s", fd, n->symb, user); 916 917 if (fd+1 > fdcap) { 918 fdcap *= 2; 919 fdtodata = erealloc(fdtodata, (size_t)fdcap * sizeof(struct fd_data *)); 920 } 921 922 data = emalloc(1 * sizeof(struct fd_data)); 923 data->netid = netid; 924 data->user = user; 925 data->suffix = 0; 926 data->ready = FALSE; 927 928 fdtodata[fd] = data; 929 930 return fd; 931 } 932 933 void 934 fd_del(int fd, char *msg, int reconnection) 935 { 936 char *user; 937 int netid; 938 int *fds; 939 940 user = fdtodata[fd]->user; 941 netid = fdtodata[fd]->netid; 942 943 if (user == NULL) { 944 debug(1, "%d: netdel: %s[%s]", fd, networks[netid].name, networks[netid].symb); 945 networks[netid].fd = reconnection ? -3 : -2; 946 } else { 947 debug(1, "%d: del[%s]: %s", fd, networks[netid].symb, user); 948 if ((fds = htsearch(usertofds, user)) == NULL) 949 fatal("%s: user doesn't exist", user); 950 fds[netid] = reconnection ? -3 : -2; 951 } 952 953 if (fdtodata[fd]->ready) 954 writeall(fd, msg); 955 close(fd); 956 957 if (reconnection) 958 reconn_list_add(user, netid); 959 free(fdtodata[fd]); 960 fdtodata[fd] = NULL; 961 break_evloop = TRUE; 962 } 963 964 void 965 fd_reconn(int fd, char *msg) 966 { 967 if (fdtodata[fd]->user == NULL) 968 net_del(fdtodata[fd]->netid, msg, TRUE); 969 else 970 fd_del(fd, msg, TRUE); 971 } 972 973 int * 974 clone_get_fds(char *nick, int netid) 975 { 976 unsigned int s; 977 int *fds = NULL; 978 979 /* count suffix */ 980 for (s = 0; nick[strlen(nick)-s-1] == '_'; s++); 981 /* remove suffix */ 982 if (s > 0) 983 nick[strlen(nick)-s] = '\0'; 984 985 fds = htsearch(usertofds, nick); 986 /* if match but suffix doesn't match */ 987 if ((fds != NULL) && (fdtodata[fds[netid]]->suffix != (int)s)) 988 fds = NULL; 989 990 /* add suffix back */ 991 if (s > 0) 992 nick[strlen(nick)] = '_'; 993 994 return fds; 995 } 996 997 void 998 nick_add_symb(char *nick, int netid) 999 { 1000 strcat(nick, "["); 1001 strcat(nick, networks[netid].symb); 1002 strcat(nick, "]"); 1003 } 1004 1005 /* 1006 * trim all the nicknames to original nick 1007 * src will be destructed 1008 */ 1009 void 1010 privmsg_update(char *dst, char *src, int netid, int fd) 1011 { 1012 char d; /* delimiter */ 1013 char *n; 1014 int i, *fds; 1015 1016 while (src != NULL) { 1017 n = strpbrk(src, " :;,<>@&~%+\\"); 1018 if (n == NULL) { 1019 d = '\0'; 1020 } else { 1021 d = *n; 1022 *n = '\0'; 1023 n++; 1024 } 1025 1026 /* check if the word is nick */ 1027 if ((fds = clone_get_fds(src, netid)) != NULL) { 1028 int netid2 = fdtodata[fd]->netid; 1029 *strrchr(src, '[') = '\0'; 1030 src[strlen(src)] = '['; 1031 if (fds[netid2] > 0) { 1032 nick_add_symb(dst, netid2); 1033 for (i = 0; i < fdtodata[fds[netid2]]->suffix; i++) 1034 strcat(dst, "_"); 1035 } 1036 } else { 1037 strcat(dst, src); 1038 } 1039 1040 strncat(dst, &d, 1); 1041 src = n; 1042 } 1043 } 1044 1045 void 1046 print_table(void) 1047 { 1048 int i, *fds, diff, tabs; 1049 char *nick; 1050 struct htiter it; 1051 1052 if (netlen == 0) 1053 return; 1054 1055 print_border(); 1056 /* print networks */ 1057 printf("Networks\t\t"); 1058 for (i = 0; i < netlen; i++) { 1059 printf("%s->%d", networks[i].symb, networks[i].fd); 1060 /* print suffix */ 1061 if (networks[i].fd > 0 && fdtodata[networks[i].fd]->suffix > 0) 1062 printf("(%d)\t", fdtodata[networks[i].fd]->suffix); 1063 else 1064 printf("\t\t"); 1065 } 1066 printf("\n"); 1067 1068 htiter_init(&it); 1069 while (htiterate(usertofds, &it)) { 1070 fds = (int *)it.node->val; 1071 nick = (char *)it.node->key; 1072 /* print tabbed user nick */ 1073 printf("%s", nick); 1074 diff = 24 - (int)strlen(nick); 1075 tabs = ((diff / 8) + (diff % 8 > 0)); 1076 printf("%.*s", tabs, "\t\t\t"); 1077 /* print tabbed fds */ 1078 for (i = 0; i < netlen; i++) { 1079 printf("%d", fds[i]); 1080 /* print suffix */ 1081 if ((fds[i] > 0) && (fdtodata[fds[i]]->suffix > 0)) 1082 printf("(%d)", fdtodata[fds[i]]->suffix); 1083 printf("\t\t"); 1084 } 1085 printf("\n"); 1086 } 1087 print_border(); 1088 } 1089 1090 void 1091 print_htable(void) 1092 { 1093 struct htiter it; 1094 int index = -1; 1095 1096 print_border(); 1097 htiter_init(&it); 1098 while (htiterate(usertofds, &it)) { 1099 if (index != (int)it.index) { 1100 /* ignore first new line */ 1101 if (index != -1) 1102 printf("\n"); 1103 printf("%d", it.index-1); 1104 index = (int)it.index; 1105 } 1106 printf(" -> %s", (char *)it.node->key); 1107 } 1108 printf("\n"); 1109 print_border(); 1110 } 1111 1112 void 1113 print_users(void) 1114 { 1115 struct htiter it; 1116 int i = 0; 1117 1118 print_border(); 1119 htiter_init(&it); 1120 while (htiterate(usertofds, &it)) 1121 printf("%d: %s\n", i++, (char *)it.node->key); 1122 print_border(); 1123 } 1124 1125 void 1126 print_reconn_list(void) 1127 { 1128 struct fd_ref *n; 1129 1130 print_border(); 1131 n = reconn_list_head; 1132 while(n != NULL) { 1133 printf("%d: %s\n", n->netid, n->user); 1134 n = n->next; 1135 } 1136 print_border(); 1137 } 1138 1139 void 1140 print_border(void) 1141 { 1142 int i; 1143 for (i = 0; i < 64; i++) 1144 printf("-"); 1145 printf("\n"); 1146 } 1147 1148 ssize_t 1149 writeall(int fd, char *buf) 1150 { 1151 ssize_t left, sent, n; 1152 left = (ssize_t)strlen(buf); 1153 sent = 0; 1154 while (sent < left) { 1155 if ((n = write(fd, buf+sent, (size_t)left)) == -1) { 1156 warnf("%d: write:", fd); 1157 snprintf(msg, sizeof(msg), "QUIT :write failed\r\n"); 1158 fd_reconn(fd, msg); 1159 return -1; 1160 } 1161 sent += n; 1162 left -= n; 1163 } 1164 return sent; 1165 }