irctk.c (59879B)
1 /* irctk -- an IRC toolkit 2 * Copyright (C) Antoine Amarilli 2010-2018 3 * Licence: GPLv3, see README and COPYING */ 4 5 #include <argp.h> 6 #include <assert.h> 7 #include <errno.h> 8 #include <pthread.h> 9 #include <stdarg.h> 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <string.h> 13 #include <sys/time.h> 14 #include <unistd.h> 15 16 #include <libircclient.h> 17 #include <libirc_rfcnumeric.h> 18 19 #define E_SESSION 1 20 #define E_CONNECT 2 21 #define E_THREAD 3 22 #define E_PASSWORD 4 23 #define E_SERVER_ERROR 5 24 #define E_BADNAME 6 25 #define E_BADLINE 7 26 #define E_LIBIRCCLIENT_VERSION 7 27 28 enum do_say_type {DO_SAY_MSG, DO_SAY_NOTICE}; 29 30 enum default_destinations {DEFAULT_FIRST, DEFAULT_LAST_IN, DEFAULT_LAST_OUT, DEFAULT_ALL}; 31 enum event_tos {NOTHING, COMMAND, MESSAGE}; 32 enum track {NO, YES, UNIQUE}; 33 34 #define MAX_LINE_LEN 4096 35 #define MAX_CHAN_LEN 4096 36 #define MAX_CHANS_LEN 4096 37 #define MAX_NICK_LEN 4096 38 #define STDIN_BUFFER_SIZE 4096 39 40 #define INITIAL_CHAN_LIST_SIZE 2 41 42 // non-standard error for the hybrid ircd 43 #define ERR_BADCHANNAME 479 44 45 const char *argp_program_bug_address = "<a3nm@a3nm.net>"; 46 47 static char doc[] = "irctk -- an IRC toolkit"; 48 static char args_doc[] = "[NICK[:PASS]@]SERVER[:PORT] [CHANNEL[:PASS]]..."; 49 50 enum arg_types {STANDARD, SSL, TIMING, MISC, CHANNEL_SELECTION, BOT_OPTIONS, 51 TRACKING, PARSING, DISPLAY_SELECTION, NAME}; 52 53 static struct argp_option options[] = { 54 55 {"verbose", 'v', 0, 0, 56 "Produce debug output on stderr", 57 STANDARD}, 58 {"quiet", 'q', 0, 0, 59 "Don't display errors on stderr, fail silently", 60 STANDARD}, 61 62 {"ssl", 's', 0, 0, 63 "Use SSL", 64 SSL}, 65 {"no-ssl", 'S', 0, 0, 66 "Do not use SSL (default)", 67 SSL}, 68 {"check-certificate", 'k', 0, 0, 69 "Require a valid SSL certificate (default)", 70 SSL}, 71 {"no-check-certificate", 'K', 0, 0, 72 "Accept self-signed, invalid (and man-in-the-middle) certificates", 73 SSL}, 74 75 {"interval", 'i', "secs", 0, 76 "Wait delay between posted messages (default: 1)", 77 TIMING}, 78 {"interval-end", 'I', "secs", 0, 79 "Wait delay between sending the last message and disconnecting" 80 " (default: 2)", 81 TIMING}, 82 {"retry-after", 'y', "secs", 0, 83 "Wait delay before trying to reconnect to the server (default: 20)", 84 TIMING}, 85 {"retry-factor", 'Y', "secs", 0, 86 "Retry delay multiplication factor after each failed attempt (default: 2)", 87 TIMING}, 88 89 {"join", 'j', 0, 0, 90 "Automatically join and re-join channels as needed (default)", MISC }, 91 {"no-join", 'J', 0, 0, 92 "Never join other channels than those specified on the command-line, don't" 93 " re-join", MISC }, 94 95 {"default-always-first", '0', 0, 0, 96 "Post messages to the first specified channel by default", 97 CHANNEL_SELECTION}, 98 {"default-last-active", 'a', 0, 0, 99 "Post messages to the last active channel by default (default)", 100 CHANNEL_SELECTION}, 101 {"default-last-posted", 'p', 0, 0, 102 "Post messages to the last posted channel by default", 103 CHANNEL_SELECTION}, 104 {"default-all", 'l', 0, 0, 105 "Post messages to all channels specified on the command line by default", 106 CHANNEL_SELECTION}, 107 {"show-inferred", 'P', 0, 0, 108 "When inferring a next chan with --default-last-posted, display it on" 109 " stderr", 110 CHANNEL_SELECTION}, 111 112 {"reply", 'r', 0, 0, 113 "When inferring a destination channel with --default-last-active*," 114 " also infer a destination nick, and prefix lines with this nick", 115 BOT_OPTIONS}, 116 {"filter", 'f', 0, 0, 117 "Only keep messages directly addressed to us (in private channel or with" 118 " lines prefixed by our nick)", 119 BOT_OPTIONS }, 120 {"filter-prune", 'F', 0, 0, 121 "Only give the message body without the channel, nick or address (implies -f, useful" 122 " with -r)", 123 BOT_OPTIONS }, 124 125 {"no-track-renames", 'T', 0, 0, 126 "When sending to a user, do not track them if they changes nick (default)", 127 TRACKING }, 128 {"track-renames", 't', 0, 0, 129 "When sending to a user, track them if they changes nick", 130 TRACKING }, 131 {"unique-names", 'u', 0, 0, 132 "Track nick changes and expose unique names for users", 133 TRACKING }, 134 135 {"event-to-nothing", 'n', 0, 0, 136 "Ignore server events (default).", 137 PARSING}, 138 {"event-to-command", 'c', 0, 0, 139 "Translate server events to messages with \"/quit\", \"/me\"...", 140 PARSING}, 141 {"event-to-message", 'm', 0, 0, 142 "Translate server events to a human-readable description", 143 PARSING}, 144 145 {"command-to-event", 'e', 0, 0, 146 "Interpret messages starting with \"/quit\", \"/me\", etc. as commands" 147 " unless escaped (default)", 148 DISPLAY_SELECTION, }, 149 {"no-command-to-event", 'E', 0, 0, 150 "Don't generate events from messages with \"/quit\", \"/me\"...", 151 DISPLAY_SELECTION, }, 152 {"with-host", 'w', 0, 0, 153 "Keep the host info in pseudos", 154 DISPLAY_SELECTION}, 155 {"own", 'o', 0, 0, 156 "Include own messages in output", 157 DISPLAY_SELECTION}, 158 159 {"username", 'U', "name", 0, 160 "Unix username (default: same as nick, only keeping lowercase letters)", 161 NAME}, 162 {"realname", 'R', "name", 0, 163 "Full name (default: same as nick)", 164 NAME}, 165 {"force-nick", 'N', 0, 0, 166 "Abort when the requested nick is unavailable or invalid; don't pick" 167 " another one automatically", 168 NAME}, 169 170 { 0 } 171 }; 172 173 174 /* The structure to store program settings */ 175 struct arguments 176 { 177 pthread_mutex_t mutex; 178 179 char nick[MAX_NICK_LEN]; 180 char hostname[MAX_NICK_LEN]; 181 char *raw_server; // before parsing SSL 182 char *server; 183 int port; 184 char *password; 185 char **channels; 186 int n_channels; // TODO2 refactor this with fifos 187 188 int ssl; 189 int ssl_check; 190 191 int verbosity; 192 193 int interval; 194 int interval_after; 195 int retry_after; 196 int retry_factor; 197 198 enum default_destinations default_destination; 199 int show_inferred; 200 201 int with_host; 202 int own; 203 204 enum event_tos event_to; 205 int command_to_event; 206 enum track track; 207 int join; 208 209 char username[MAX_NICK_LEN]; 210 char realname[MAX_NICK_LEN]; 211 char username_set; 212 char realname_set; 213 int force_nick; 214 215 int command; 216 char * raw_dest; 217 char ready; 218 char last_chan_in[MAX_CHAN_LEN]; 219 char last_nick_in[MAX_NICK_LEN]; 220 char last_chans_out[MAX_CHANS_LEN]; 221 int show_nick_prefix; 222 int filter; 223 int prune; 224 }; 225 226 typedef struct arguments irc_ctx_t; 227 228 struct arguments args; 229 230 /* The initial argument values */ 231 void initialize_args() 232 { 233 assert(!pthread_mutex_init(&(args.mutex), NULL)); 234 235 strncpy(args.nick, "irctk", MAX_NICK_LEN-1); 236 strncpy(args.hostname, "!~UNKNOWN@UNKNOWN", MAX_NICK_LEN-1); 237 args.raw_server = NULL; 238 args.server = NULL; 239 args.password = NULL; 240 args.port = 6667; 241 args.channels = (char**) NULL; 242 args.n_channels = 0; 243 244 args.ssl = 0; 245 args.ssl_check = 1; 246 247 args.verbosity = 0; 248 249 args.interval = 1000000; 250 args.interval_after = 2000000; 251 args.retry_after = 20; 252 args.retry_factor = 2; 253 254 args.default_destination = DEFAULT_LAST_IN; 255 args.show_inferred = 0; 256 257 args.with_host = 0; 258 args.own = 0; 259 260 args.event_to = NOTHING; 261 args.command_to_event = 1; 262 args.track = NO; 263 args.join = 1; 264 265 strncpy(args.username, "irctk", MAX_NICK_LEN-1); 266 strncpy(args.realname, "irctk", MAX_NICK_LEN-1); 267 args.username_set = 0; 268 args.realname_set = 0; 269 args.force_nick = 0; 270 271 args.filter = 0; 272 args.prune = 0; 273 args.last_chan_in[0] = 0; 274 args.last_nick_in[0] = 0; 275 args.last_chans_out[0] = 0; 276 } 277 278 /* set the username, removing bad characters */ 279 void set_username(struct arguments *arguments, char *name) { 280 int opos = 0, ipos = 0; 281 while (name[ipos]) { 282 // section 2.3.1 of RFC 2812, definition of "user" 283 if (name[ipos] != 0x0A && name[ipos] != 0x0D 284 && name[ipos] != 0x20 && name[ipos] != 0x40) 285 arguments->username[opos++] = name[ipos]; 286 ipos++; 287 } 288 arguments->username[opos] = 0; 289 if (!opos) { 290 // there were no valid characters to copy, revert to default 291 strncpy(arguments->username, "irctk", MAX_NICK_LEN-1); 292 } 293 } 294 295 /* set our nick, and names if not already set */ 296 void set_nick_and_names(struct arguments *arguments) 297 { 298 strncpy(arguments->nick, arguments->raw_dest, MAX_NICK_LEN-1); 299 if (!args.username_set) 300 set_username(arguments, arguments->raw_dest); 301 if (!args.realname_set) 302 strncpy(arguments->realname, arguments->raw_dest, MAX_NICK_LEN-1); 303 } 304 305 306 /* The command-line argument parsing function for argp */ 307 static error_t parse_opt (int key, char *arg, struct argp_state *state) 308 { 309 struct arguments *arguments = state->input; 310 int i; 311 312 if (key != ARGP_KEY_ARG && key != ARGP_KEY_END && (! arguments->channels)) { 313 switch (key) { 314 case 'v': 315 arguments->verbosity = 1; 316 break; 317 case 'q': 318 arguments->verbosity = -1; 319 break; 320 case 'S': 321 arguments->ssl = 0; 322 break; 323 case 's': 324 arguments->ssl = 1; 325 break; 326 case 'K': 327 arguments->ssl_check = 0; 328 break; 329 case 'k': 330 arguments->ssl_check = 1; 331 break; 332 case 'n': 333 arguments->event_to = NOTHING; 334 break; 335 case 'c': 336 arguments->event_to = COMMAND; 337 break; 338 case 'm': 339 arguments->event_to = MESSAGE; 340 break; 341 case 'E': 342 arguments->command_to_event = 0; 343 break; 344 case 'e': 345 arguments->command_to_event = 1; 346 break; 347 case 'i': 348 arguments->interval = (int) (atof(arg) * 1000000.); 349 break; 350 case 'I': 351 arguments->interval_after = (int) (atof(arg) * 1000000.); 352 break; 353 case 'y': 354 arguments->retry_after = (int) (atof(arg)); 355 break; 356 case 'Y': 357 arguments->retry_factor = (int) (atof(arg)); 358 break; 359 case 'U': 360 arguments->username_set = 1; 361 set_username(arguments, arg); 362 break; 363 case 'R': 364 arguments->realname_set = 1; 365 strncpy(arguments->realname, arg, MAX_NICK_LEN-1); 366 break; 367 case 'o': 368 arguments->own = 1; 369 break; 370 case 'w': 371 arguments->with_host = 1; 372 break; 373 case 'r': 374 arguments->show_nick_prefix = 1; 375 break; 376 case '0': 377 arguments->default_destination = DEFAULT_FIRST; 378 break; 379 case 'f': 380 arguments->filter = 1; 381 break; 382 case 'F': 383 arguments->filter = 1; 384 arguments->prune = 1; 385 break; 386 case 'J': 387 arguments->join = 0; 388 break; 389 case 'j': 390 arguments->join = 1; 391 break; 392 case 'T': 393 arguments->track = NO; 394 break; 395 case 't': 396 arguments->track = YES; 397 break; 398 case 'u': 399 arguments->track = UNIQUE; 400 break; 401 case 'a': 402 arguments->default_destination = DEFAULT_LAST_IN; 403 break; 404 case 'p': 405 arguments->default_destination = DEFAULT_LAST_OUT; 406 break; 407 case 'l': 408 arguments->default_destination = DEFAULT_ALL; 409 break; 410 case 'P': 411 arguments->show_inferred = 1; 412 break; 413 case 'N': 414 arguments->force_nick = 1; 415 break; 416 417 case ARGP_KEY_NO_ARGS: 418 argp_usage (state); 419 420 default: 421 return ARGP_ERR_UNKNOWN; 422 } 423 } else { 424 switch(key) { 425 case ARGP_KEY_ARG: 426 if (state->arg_num == 0) { 427 i = -1; 428 arguments->raw_dest = arg; 429 arguments->raw_server = arg; 430 431 int saw_colon = 0; 432 int saw_at = 0; 433 434 while (arguments->raw_dest[++i]) { 435 if (arguments->raw_dest[i] == ':' && !saw_colon) { 436 arguments->raw_dest[i] = 0; 437 saw_colon = i; 438 } 439 if (arguments->raw_dest[i] == '@') { 440 arguments->raw_dest[i] = 0; 441 saw_at = i; 442 arguments->raw_server = arguments->raw_dest + saw_at + 1; 443 set_nick_and_names(arguments); 444 if (saw_colon) { 445 arguments->password = arguments->raw_dest + saw_colon + 1; 446 saw_colon = 0; 447 } 448 } 449 } 450 if (saw_colon) { 451 // we must have a server, so the last thing seen behind an initial 452 // colon must be the port 453 arguments->port = atoi(arguments->raw_dest + saw_colon + 1); 454 } 455 } 456 else { 457 if (arguments->channels == NULL) { 458 arguments->channels = state->argv + (state->next - 1); 459 } 460 arguments->n_channels++; 461 } 462 break; 463 464 case ARGP_KEY_END: 465 if (state->arg_num < 1 || strlen(arguments->raw_server) == 0 466 || strlen(arguments->nick) == 0 || arguments->port == 0) 467 argp_usage (state); 468 break; 469 } 470 } 471 return 0; 472 } 473 474 /* argp structure */ 475 static struct argp argp = { options, parse_opt, args_doc, doc }; 476 477 478 /* Emit a message with a verbosity level, and optionally terminate. 479 * Verbosity is >=1 for debug, 0 for info, -n for error n-1 */ 480 void message(int verbosity, const char *err, va_list ap) 481 { 482 if (args.verbosity >= verbosity) { 483 fprintf(stderr, "%s ", verbosity > 0 ? "irctk: [debug]" : 484 verbosity >= 0 ? "irctk:" : "irctk: [error]"); 485 vfprintf(stderr, err, ap); 486 } 487 if (verbosity < 0) 488 exit(1-verbosity); 489 } 490 491 void info(const char *err, ...) 492 { 493 va_list ap; 494 va_start(ap, err); 495 return message(0, err, ap); 496 } 497 498 void debug(const char *err, ...) 499 { 500 va_list ap; 501 va_start(ap, err); 502 return message(1, err, ap); 503 } 504 505 void die(int val, const char *err, ...) 506 { 507 va_list ap; 508 va_start(ap, err); 509 return message(-val - 1, err, ap); 510 } 511 512 int mprintf(const char *fmt, ...) 513 { 514 va_list ap; 515 va_start(ap, fmt); 516 int ret; 517 ret = vprintf(fmt, ap); 518 fflush(stdout); 519 return ret; 520 } 521 522 523 typedef struct line { 524 char *line; 525 char *full_line; 526 int next_dep_fifoidx; 527 int next_dep_lineidx; 528 char is_head; 529 char is_sendable; 530 } line; 531 532 typedef struct action { 533 char *line; 534 char *full_line; 535 char *destination; 536 } action; 537 538 typedef struct fifo { 539 int hd; 540 int tl; 541 line queue[STDIN_BUFFER_SIZE]; 542 } fifo; 543 544 typedef struct fifo_ctrl { 545 pthread_cond_t empty; 546 pthread_cond_t full; 547 pthread_mutex_t mutex; 548 } fifo_ctrl; 549 550 typedef struct fifo_set { 551 int n; 552 int allocated; 553 fifo_ctrl ctrl; 554 fifo *fifos; 555 char **chans; 556 char **surface_name; 557 char *was_pushed; 558 } fifo_set; 559 560 void init_fifo_ctrl(fifo_ctrl *c) { 561 assert(!pthread_cond_init(&(c->empty), NULL)); 562 assert(!pthread_cond_init(&(c->full), NULL)); 563 assert(!pthread_mutex_init(&(c->mutex), NULL)); 564 } 565 566 void init_fifo_set (fifo_set *s) { 567 s->n = 0; 568 s->allocated = INITIAL_CHAN_LIST_SIZE; 569 s->chans = malloc(s->allocated * sizeof(char*)); 570 assert(s->chans); 571 s->fifos = malloc(s->allocated * sizeof(fifo)); 572 assert(s->fifos); 573 s->was_pushed = malloc(s->allocated * sizeof(char)); 574 assert(s->was_pushed); 575 s->surface_name = malloc(s->allocated * sizeof(char*)); 576 assert(s->surface_name); 577 init_fifo_ctrl(&(s->ctrl)); 578 } 579 580 void destroy_fifo_ctrl(fifo_ctrl *f) { 581 pthread_cond_destroy(&f->empty); 582 pthread_cond_destroy(&f->full); 583 pthread_mutex_destroy(&f->mutex); 584 } 585 586 void destroy_fifo_set (fifo_set *s) { 587 int i; 588 free(s->fifos); 589 for (i = 0; i < s->n ; i++) 590 free(s->chans[i]); 591 free(s->chans); 592 free(s->was_pushed); 593 for (i = 0; i < s->n ; i++) 594 free(s->surface_name[i]); 595 free(s->surface_name); 596 destroy_fifo_ctrl(&(s->ctrl)); 597 } 598 599 void init_fifo(fifo *f) { 600 f->hd = 0; 601 f->tl = 0; 602 } 603 604 int size(fifo *f) { 605 return (f->tl - f->hd + STDIN_BUFFER_SIZE) % STDIN_BUFFER_SIZE; 606 } 607 int full(fifo *f) { 608 return size(f) == STDIN_BUFFER_SIZE - 1; 609 } 610 int full_fifo_set(fifo_set *s) { 611 // fifo_set is full if one fifo is full 612 int i; 613 for (i=0; i<s->n; i++) 614 if (full(&(s->fifos[i]))) 615 return 1; 616 return 0; 617 } 618 int empty(fifo *f) { 619 return size(f) == 0; 620 } 621 int empty_fifo_set(fifo_set *s) { 622 // fifo_set is empty if all fifos are empty 623 int i; 624 for (i=0; i<s->n; i++) 625 if (!empty(&(s->fifos[i]))) 626 return 0; 627 return 1; 628 } 629 630 void realloc_chans(fifo_set *s) { 631 debug("realloc_chans()\n"); 632 s->allocated *= 2; 633 s->chans = realloc(s->chans, s->allocated*sizeof(char*)); 634 assert(s->chans); 635 s->was_pushed = realloc(s->was_pushed, s->allocated*sizeof(char*)); 636 assert(s->was_pushed); 637 s->fifos = realloc(s->fifos, s->allocated*sizeof(fifo)); 638 assert(s->fifos); 639 s->surface_name = realloc(s->surface_name, s->allocated*sizeof(char*)); 640 assert(s->surface_name); 641 } 642 643 int push_chan_alias(fifo_set *s, const char *chan, const char *alias) { 644 if (s->n == s->allocated) 645 realloc_chans(s); 646 s->chans[s->n] = malloc(MAX_CHANS_LEN * sizeof(chan)); 647 assert(s->chans[s->n]); 648 strncpy(s->chans[s->n], chan, MAX_CHAN_LEN-1); 649 init_fifo(&s->fifos[s->n]); 650 s->was_pushed[s->n] = 0; 651 s->surface_name[s->n] = malloc(MAX_NICK_LEN * sizeof(chan)); 652 assert(s->surface_name[s->n]); 653 strncpy(s->surface_name[s->n], alias, MAX_NICK_LEN-1); 654 return (s->n)++; 655 } 656 657 int push_chan(fifo_set *s, const char *chan) { 658 return push_chan_alias(s, chan, chan); 659 } 660 661 int scan_collection(fifo_set *s, const char **collection, const char *chan) { 662 int i; 663 for (i=0; i<s->n; i++) { 664 if (!strcmp(collection[i], chan)) { 665 return i; 666 } 667 } 668 return -1; 669 } 670 671 int index_of_chan_get(fifo_set *s, const char *chan) { 672 return scan_collection(s, (const char **) s->chans, chan); 673 } 674 675 int index_of_chan_surface(fifo_set *s, const char *chan) { 676 int result = scan_collection(s, (const char **) s->surface_name, chan); 677 if (result >= 0) { 678 return result; 679 } else { 680 return push_chan(s, chan); 681 } 682 } 683 684 int index_of_chan(fifo_set *s, const char *chan) { 685 int result = index_of_chan_get(s, chan); 686 if (result >= 0) { 687 return result; 688 } else { 689 return push_chan(s, chan); 690 } 691 } 692 693 int rename_user(fifo_set *s, const char *from, const char *to) { 694 int pos; 695 debug("rename_user %s -> %s\n", from, to); 696 pthread_mutex_lock(&s->ctrl.mutex); 697 pos = scan_collection(s, (const char **) s->surface_name, from); 698 assert(pos >= 0); 699 strncpy(s->surface_name[pos], to, MAX_NICK_LEN-1); 700 pthread_mutex_unlock(&s->ctrl.mutex); 701 return pos; 702 } 703 704 int surface_nick_exists(fifo_set *s, const char *nick) { 705 int result = 0; 706 pthread_mutex_lock(&s->ctrl.mutex); 707 if (scan_collection(s, (const char **) s->surface_name, nick) >= 0) 708 result = 1; 709 pthread_mutex_unlock(&s->ctrl.mutex); 710 return result; 711 } 712 713 int nick_exists(fifo_set *s, const char *nick) { 714 int result = 0; 715 if (scan_collection(s, (const char **) s->chans, nick) >= 0 || 716 scan_collection(s, (const char **) s->surface_name, nick) >= 0) 717 result = 1; 718 return result; 719 } 720 721 int register_nick(fifo_set *s, const char *nick) { 722 int result; 723 pthread_mutex_lock(&s->ctrl.mutex); 724 // we assume that nick is not a current surface nick 725 if (scan_collection(s, (const char **) s->chans, nick) < 0) { 726 result = push_chan(s, nick); 727 } else { 728 // nick is already taken, generate a fresh id 729 int suffix = 2; 730 char new[MAX_NICK_LEN], num[MAX_NICK_LEN]; 731 while (1) { 732 strncpy(new, nick, MAX_NICK_LEN/2); 733 sprintf(num, "%d", suffix++); 734 strncat(new, num, MAX_NICK_LEN/2-1); 735 debug("Trying to register under %s\n", new); 736 if (!nick_exists(s, new)) { 737 break; 738 } 739 } 740 debug("Register under %s %s\n", new, nick); 741 result = push_chan_alias(s, new, nick); 742 } 743 pthread_mutex_unlock(&s->ctrl.mutex); 744 return result; 745 } 746 747 void push_one(fifo_set *s, char *fl, char *dest, char *l, 748 int next_dep_fifoidx, int next_dep_lineidx, 749 int *fifoidx, int *lineidx) { 750 int c; 751 if (args.track != UNIQUE) 752 c = index_of_chan_surface(s, dest); 753 else 754 c = index_of_chan(s, dest); 755 fifo *f = &(s->fifos[c]); 756 if (s->was_pushed[c]) 757 return; // already pushed 758 debug("PUSH_ONE \"%s\" (%p), nextdep %d %d ioc %d position %d\n", 759 l, l, next_dep_fifoidx, next_dep_lineidx, c, f->tl); 760 s->was_pushed[c] = 1; 761 f->queue[f->tl].line = l; 762 f->queue[f->tl].full_line = fl; 763 f->queue[f->tl].next_dep_fifoidx = next_dep_fifoidx; 764 f->queue[f->tl].next_dep_lineidx = next_dep_lineidx; 765 f->queue[f->tl].is_head = 0; 766 f->queue[f->tl].is_sendable = 0; 767 *fifoidx = c; 768 *lineidx = f->tl; 769 debug("pushed one, returning next dep %d %d\n", *fifoidx, *lineidx); 770 f->tl++; 771 f->tl %= STDIN_BUFFER_SIZE; 772 } 773 774 void set_last_idxes(fifo_set *s, char *chan, int fifoidx, int lineidx) { 775 int c = index_of_chan(s, chan); 776 fifo *f = &(s->fifos[c]); 777 f->queue[(f->tl - 1 + STDIN_BUFFER_SIZE) % STDIN_BUFFER_SIZE].next_dep_fifoidx = fifoidx; 778 f->queue[(f->tl - 1 + STDIN_BUFFER_SIZE) % STDIN_BUFFER_SIZE].next_dep_lineidx = lineidx; 779 } 780 781 void reset_chan_marks(fifo_set *s) { 782 int i; 783 for (i=0; i<s->n; i++) 784 s->was_pushed[i] = 0; 785 } 786 787 void pre_push(fifo_set *s) { 788 pthread_mutex_lock(&s->ctrl.mutex); 789 while (full_fifo_set(s)) 790 pthread_cond_wait(&s->ctrl.full, &s->ctrl.mutex); 791 // now fifo_set isn't full 792 } 793 794 void post_push(fifo_set *s, int was_empty) { 795 if (was_empty) { 796 debug("signal for empty\n"); 797 pthread_cond_signal(&s->ctrl.empty); 798 } 799 pthread_mutex_unlock(&s->ctrl.mutex); 800 } 801 802 void push_fifo_set(fifo_set *s, char *fl, char *dests, char *l) { 803 debug("push_fifo_set full_line \"%s\" (%p) line \"%s\" (%p) dests %s\n", 804 fl, fl, l, l, dests); 805 int was_empty; 806 807 pre_push(s); 808 int i = 0; 809 int cont = 1; 810 char *last_dest, *first_dest = 0; 811 int first_fifoidx = -1, first_lineidx = -1; 812 int fifoidx = 0, lineidx = 0, nfifoidx = 0, nlineidx = 0; 813 814 last_dest = dests; 815 816 reset_chan_marks(s); 817 was_empty = empty_fifo_set(s); 818 while (cont) { 819 if (dests[i] == ',' || !dests[i]) { 820 if (!dests[i]) 821 cont = 0; 822 if (i) 823 dests[i] = 0; 824 debug("will push one \"%s\"\n", l); 825 // only put the full line for *one* occurrence 826 push_one(s, first_fifoidx < 0?fl:NULL, last_dest, l, fifoidx, lineidx, &nfifoidx, &nlineidx); 827 fifoidx = nfifoidx; 828 lineidx = nlineidx; 829 if (first_fifoidx < 0) { 830 first_fifoidx = fifoidx; 831 first_lineidx = lineidx; 832 first_dest = last_dest; 833 } 834 last_dest = dests + i + 1; 835 } 836 i++; 837 } 838 839 set_last_idxes(s, first_dest, fifoidx, lineidx); 840 841 post_push(s, was_empty); 842 } 843 844 /* TODO2 refactor this */ 845 void push_fifo_set_all(fifo_set *s, char *fl, char **dests, int ndests, char *l) { 846 pre_push(s); 847 int was_empty; 848 int i = 0; 849 int fifoidx = 0, lineidx = 0, nfifoidx = 0, nlineidx = 0; 850 851 assert(ndests > 0); 852 reset_chan_marks(s); 853 was_empty = empty_fifo_set(s); 854 for (i=0; i<ndests; i++) { 855 push_one(s, i?NULL:fl, dests[i], l, fifoidx, lineidx, &nfifoidx, &nlineidx); 856 fifoidx = nfifoidx; 857 lineidx = nlineidx; 858 } 859 860 set_last_idxes(s, dests[0], fifoidx, lineidx); 861 862 post_push(s, was_empty); 863 } 864 865 void mark_head(fifo *f) { 866 f->queue[f->hd].is_head = 1; 867 f->queue[f->hd].is_sendable = 0; 868 } 869 870 void mark_heads(fifo_set *s) { 871 int i; 872 for (i=0; i<s->n; i++) 873 mark_head(&s->fifos[i]); 874 } 875 876 int mark_sendable_line(fifo_set *s, line *l) { 877 if (!l->is_head) 878 return 0; 879 if (l->is_sendable) 880 return 1; 881 l->is_sendable = 1; 882 line* ptr = &(s->fifos[l->next_dep_fifoidx].queue[l->next_dep_lineidx]); 883 return mark_sendable_line(s, ptr); 884 } 885 int mark_sendable(fifo_set *s, fifo *f) { 886 if (empty(f)) 887 return 1; 888 return mark_sendable_line(s, &f->queue[f->hd]); 889 } 890 void unmark_sendable_line(fifo_set *s, line *l) { 891 if (!l->is_sendable) 892 return; 893 l->is_sendable = 0; 894 line* ptr = &(s->fifos[l->next_dep_fifoidx].queue[l->next_dep_lineidx]); 895 unmark_sendable_line(s, ptr); 896 } 897 void unmark_sendable(fifo_set *s, fifo *f) { 898 unmark_sendable_line(s, &f->queue[f->hd]); 899 } 900 void pop_if_sendable(fifo_set *s, int c, int *n_result, action *result) { 901 fifo *f = &s->fifos[c]; 902 if (f->queue[f->hd].is_sendable && f->queue[f->hd].line) { 903 result[*n_result].line = f->queue[f->hd].line; 904 result[*n_result].full_line = f->queue[f->hd].full_line; 905 result[*n_result].destination = s->surface_name[c]; 906 (*n_result)++; 907 f->hd++; 908 f->hd %= STDIN_BUFFER_SIZE; 909 } else { 910 // don't pop sentinel 911 debug("don't pop channel %d\n", c); 912 } 913 } 914 915 int pop_fifo_set(fifo_set *s, action **result) { 916 int n_result = 0; 917 int was_full; 918 int i; 919 pthread_mutex_lock(&s->ctrl.mutex); 920 while (empty_fifo_set(s)) { 921 debug("wait because empty\n"); 922 pthread_cond_wait(&s->ctrl.empty, &s->ctrl.mutex); 923 } 924 // now fifo isn't empty 925 debug("now fifo isn't empty\n"); 926 was_full = full_fifo_set(s); 927 mark_heads(s); 928 for (i=0; i<s->n; i++) { 929 debug("doing fifo for chan %s\n", s->chans[i]); 930 debug("fifo hd %d tl %d\n", s->fifos[i].hd, s->fifos[i].tl); 931 if (!mark_sendable(s, &s->fifos[i])) 932 unmark_sendable(s, &s->fifos[i]); 933 } 934 // freeing result is responsibility of callee 935 *result = malloc(s->n * sizeof(line)); 936 assert(*result); 937 for (i=0; i<s->n; i++) 938 pop_if_sendable(s, i, &n_result, *result); 939 if (!n_result) { 940 // only thing left is sentinel, return sentinel 941 debug("only thing to return is sentinel\n"); 942 n_result = 1; 943 result[0]->line = NULL; 944 result[0]->full_line = NULL; 945 } 946 debug("prepared %d results to return\n", n_result); 947 if (was_full) 948 pthread_cond_signal(&s->ctrl.full); 949 pthread_mutex_unlock(&s->ctrl.mutex); 950 return n_result; 951 } 952 953 // fifo for input 954 // we want to read lines at the time we get them even if we are delaying writes 955 // on the irc channel 956 fifo_set fifos; 957 958 959 /* Our main chan is either the first CLI chan or our own chan if there are no 960 * CLI chans */ 961 char* first_chan() 962 { 963 if (args.n_channels) 964 return args.channels[0]; 965 else return args.nick; 966 } 967 968 const char *input_nick(const char *nick) { 969 int c; 970 const char *result; 971 if (args.track == NO) 972 return nick; 973 pthread_mutex_lock(&fifos.ctrl.mutex); 974 c = scan_collection(&fifos, (const char **) fifos.surface_name, nick); 975 if (c < 0) 976 result = nick; // dest changed nick, but we're not supposed to track that 977 else 978 result = fifos.chans[c]; 979 pthread_mutex_unlock(&fifos.ctrl.mutex); 980 return result; 981 } 982 983 const char *output_nick(const char *nick) { 984 int c; 985 char *result; 986 if (args.track != UNIQUE) 987 return nick; 988 pthread_mutex_lock(&fifos.ctrl.mutex); 989 c = scan_collection(&fifos, (const char **) fifos.surface_name, nick); 990 assert(c >= 0); 991 result = fifos.chans[c]; 992 pthread_mutex_unlock(&fifos.ctrl.mutex); 993 return result; 994 } 995 996 #define MATCH_CMD(str, cmd) \ 997 ( strncmp((str) + 1, (cmd " "), sizeof(cmd) / sizeof(char) ) \ 998 ? NULL : (str) + sizeof(cmd) / sizeof(char) + 1 ) 999 1000 #define MATCH_CMD0(str, cmd) \ 1001 ( strncmp((str) + 1, (cmd), sizeof(cmd) / sizeof(char) - 1) \ 1002 ? NULL : (str) + sizeof(cmd) / sizeof(char) ) 1003 1004 #define IS_END(ptr) ( !(ptr) || (ptr) == '\n' ) 1005 1006 /* return value should be freed by callee */ 1007 char* consume_word(char **from) 1008 { 1009 const char *start; 1010 char *result; 1011 int size = 0; 1012 while (**from == ' ') 1013 ++*from; 1014 start = *from; 1015 while (**from != ' ' && !IS_END(**from)) { 1016 ++*from; 1017 ++size; 1018 } 1019 while (**from == ' ') 1020 ++*from; 1021 result = malloc((size + 1) * sizeof(char)); 1022 assert(result); 1023 strncpy(result, start, size); 1024 result[size] = 0; 1025 return result; 1026 } 1027 1028 int do_me(irc_session_t *s, char *chan, char *password, char *line) { 1029 /* TODO2 only join channels we haven't joined yet */ 1030 int ret; 1031 int oldend; 1032 char oldchar; 1033 if (args.join && chan[0] == '#') 1034 irc_cmd_join(s, chan, password); 1035 if (args.own) { 1036 if (args.event_to == COMMAND) 1037 mprintf("[%s] <%s%s> /me %s", chan, 1038 output_nick(args.nick), 1039 args.with_host ? args.hostname : "", 1040 line); 1041 if (args.event_to == MESSAGE) 1042 mprintf("[%s] <%s%s> * %s%s %s", chan, 1043 output_nick(args.nick), 1044 args.with_host ? args.hostname : "", 1045 output_nick(args.nick), 1046 args.with_host ? args.hostname : "", 1047 line); 1048 } 1049 1050 /* kludge: replace trailing '\n's by '\0's temporarily to avoid problem with 1051 * the '\01' of ACTION falling on a different line */ 1052 oldend = strlen(line); 1053 while (oldend >= 0 && (line[oldend] == '\n' || line[oldend] == 0)) 1054 oldend--; 1055 oldend++; 1056 // oldend is now the last index that is >= 0 and line[oldend] is '\0' or '\n' 1057 oldchar = line[oldend]; 1058 line[oldend] = 0; 1059 ret = irc_cmd_me(s, chan, line); 1060 line[oldend] = oldchar; 1061 return ret; 1062 } 1063 1064 int do_say(irc_session_t *s, char *chan, char *password, char *line, enum do_say_type type) { 1065 /* TODO2 only join channels we haven't joined yet */ 1066 if (args.join && chan[0] == '#') 1067 irc_cmd_join(s, chan, password); 1068 if (args.own) { 1069 mprintf("[%s] <%s%s> %s", chan, 1070 output_nick(args.nick), 1071 args.with_host ? args.hostname : "", 1072 line); 1073 } 1074 1075 switch (type) { 1076 case DO_SAY_MSG: 1077 return irc_cmd_msg(s, chan, line); 1078 1079 case DO_SAY_NOTICE: 1080 return irc_cmd_notice(s, chan, line); 1081 } 1082 1083 abort(); // unreachable 1084 } 1085 1086 /* If chan is suffixed by a password, modify it and return the modified offset */ 1087 int get_password(char *chan) { 1088 int pos = 0; 1089 while (chan[pos] != ':' && chan[pos]) 1090 pos++; 1091 if (chan[pos]) { 1092 chan[pos] = 0; 1093 return pos; 1094 } 1095 return -1; 1096 } 1097 1098 void revert_password(char *chan, int pos) { 1099 if (pos < 0) return; 1100 chan[pos] = ':'; 1101 } 1102 1103 1104 int my_irc_cmd_oper(irc_session_t *s, char *user, char *pass) 1105 { 1106 return irc_send_raw(s, "OPER %s %s\n", user, pass); 1107 } 1108 1109 1110 /* Send a message or do a command */ 1111 /* Return value is 0 if OK, >0 if error, -1 if correctly exited */ 1112 int do_cmd_msg(irc_session_t *s, char *chan, char *line) 1113 { 1114 int rsl = 0; 1115 int pw_pos = get_password(chan); 1116 char *password = chan + pw_pos + 1; 1117 1118 debug("do_cmd_msg %s %s\n", chan, line); 1119 1120 /* TODO2 ask the server to notify, if possible */ 1121 /* TODO2 find a way to get real host name (part after !) */ 1122 1123 if (line[0] == '/' && args.command_to_event) { 1124 char *arg; 1125 if ((arg = MATCH_CMD(line, "nick"))) { 1126 rsl = irc_cmd_nick (s, arg); 1127 } else if ((arg = MATCH_CMD(line, "mode"))) { 1128 rsl = irc_cmd_channel_mode (s, chan, arg); 1129 } else if ((arg = MATCH_CMD0(line, "part"))) { 1130 if (!*arg || *arg == '\n') { 1131 rsl = irc_cmd_part(s, chan); 1132 } else if (*arg == ' ') { 1133 rsl = irc_cmd_part(s, arg + 1); 1134 } else { 1135 info("Unrecognized command: %s", line); 1136 } 1137 } else if ((arg = MATCH_CMD0(line, "join"))) { 1138 char *to_join = NULL; 1139 // we must revert temporarily so that we can join 1140 revert_password(chan, pw_pos); 1141 if (!*arg || *arg == '\n') { 1142 to_join = chan; 1143 } else if (*arg == ' ') { 1144 to_join = arg + 1; 1145 } 1146 if (to_join && to_join[0] == '#') { 1147 if (args.join) { 1148 // now, get the right index 1149 int pw_pos2 = get_password(to_join); 1150 rsl = irc_cmd_join(s, to_join, pw_pos2 >= 0 ? to_join + pw_pos2 + 1 : NULL); 1151 // and revert 1152 revert_password(to_join, pw_pos); 1153 } else { 1154 info("Will not join because --no-auto-join was set\n"); 1155 } 1156 } else { 1157 info("Unrecognized command: %s", line); 1158 } 1159 // and we go back to the previous state 1160 pw_pos = get_password(chan); 1161 } else if ((arg = MATCH_CMD(line, "topic"))) { 1162 rsl = irc_cmd_topic(s, chan, arg); 1163 } else if ((arg = MATCH_CMD(line, "quote"))) { 1164 rsl = irc_send_raw(s, "%s", arg); 1165 } else if ((arg = MATCH_CMD0(line, "quit"))) { 1166 int ok = 1; 1167 if (*arg == ' ') { 1168 rsl = irc_cmd_quit(s, arg + 1); 1169 } else if (!*arg || *arg == '\n') { 1170 rsl = irc_cmd_quit(s, NULL); 1171 } else { 1172 info("Unrecognized command: %s", line); 1173 ok = 0; 1174 } 1175 if (!rsl && ok) 1176 rsl = -1; 1177 } else if ((arg = MATCH_CMD(line, "invite"))) { 1178 char *inick = consume_word(&arg); 1179 if (!IS_END(*arg)) { 1180 rsl = irc_cmd_invite(s, inick, arg); 1181 } else { 1182 rsl = irc_cmd_invite(s, inick, chan); 1183 } 1184 free(inick); 1185 } else if ((arg = MATCH_CMD(line, "oper"))) { 1186 char *ouser = consume_word(&arg); 1187 if (!IS_END(*arg)) { 1188 rsl = my_irc_cmd_oper(s, ouser, arg); 1189 } else { 1190 rsl = my_irc_cmd_oper(s, ouser, ""); 1191 } 1192 free(ouser); 1193 } else if ((arg = MATCH_CMD(line, "kick"))) { 1194 char *knick = consume_word(&arg); 1195 rsl = irc_cmd_kick(s, knick, chan, arg); 1196 free(knick); 1197 } else if ((arg = MATCH_CMD0(line, "me"))) { 1198 if (*arg == ' ') { 1199 rsl = do_me(s, chan, password, arg + 1); 1200 } else if (!*arg || *arg == '\n') { 1201 rsl = do_me(s, chan, password, arg); 1202 } else { 1203 info("Unrecognized command: %s", line); 1204 } 1205 } else if ((arg = MATCH_CMD0(line, "say"))) { 1206 // just a way to escape messages starting by '/' 1207 if (*arg == ' ') { 1208 rsl = do_say(s, chan, password, arg + 1, DO_SAY_MSG); 1209 } else if (!*arg || *arg == '\n') { 1210 rsl = do_say(s, chan, password, arg, DO_SAY_MSG); 1211 } else { 1212 info("Unrecognized command: %s", line); 1213 } 1214 } else if ((arg = MATCH_CMD0(line, "notice"))) { 1215 // same, but for notices 1216 if (*arg == ' ') { 1217 rsl = do_say(s, chan, password, arg + 1, DO_SAY_NOTICE); 1218 } else if (!*arg || *arg == '\n') { 1219 rsl = do_say(s, chan, password, arg, DO_SAY_NOTICE); 1220 } else { 1221 info("Unrecognized command: %s", line); 1222 } 1223 } else if (line[1] == ' ') { 1224 // another way, compatible with irssi: "/ /message" 1225 rsl = do_say(s, chan, password, line + 2, DO_SAY_MSG); 1226 } else { 1227 info("Unrecognized or malformed command: %s", line); 1228 } 1229 // TODO2: names, list, topic, usermode, whois, raw... 1230 // with correct user tracking! 1231 } else { 1232 /* TODO2 only join channels we haven't joined yet */ 1233 if (args.join && chan[0] == '#') { 1234 rsl = irc_cmd_join(s, chan, password); 1235 } 1236 1237 if (args.track != NO) { 1238 char lline[MAX_LINE_LEN + MAX_NICK_LEN + 2]; 1239 int seppos = -1; 1240 int i; 1241 for (i=0; i<strlen(line); i++) { 1242 if (line[i] == ',' || line[i] == ':') { 1243 seppos = i; 1244 break; 1245 } 1246 if (line[i] == ' ') 1247 break; 1248 } 1249 if (seppos == -1) { 1250 rsl = do_say(s, chan, password, line, DO_SAY_MSG); 1251 } else { 1252 char sep; 1253 sep = line[seppos]; 1254 line[seppos] = 0; 1255 pthread_mutex_lock(&fifos.ctrl.mutex); 1256 int c = scan_collection(&fifos, (const char **) fifos.chans, line); 1257 if (c < 0) 1258 strncpy(lline, line, MAX_NICK_LEN-1); 1259 else 1260 strncpy(lline, fifos.surface_name[c], MAX_NICK_LEN-1); 1261 pthread_mutex_unlock(&fifos.ctrl.mutex); 1262 line[seppos] = sep; 1263 strncat(lline, line + seppos, MAX_LINE_LEN-1); 1264 rsl = do_say(s, chan, password, lline, DO_SAY_MSG); 1265 } 1266 } else { 1267 rsl = do_say(s, chan, password, line, DO_SAY_MSG); 1268 } 1269 } 1270 1271 revert_password(chan, pw_pos); 1272 1273 if (rsl) 1274 return rsl; 1275 1276 return 0; 1277 } 1278 1279 // Handle a command request which might not specify a destination 1280 void cmd_msg(char *full_line, char *target, char *line) 1281 { 1282 char *msg; 1283 1284 debug("cmd_msg %s \"%s\" (%p), last_in %s\n", target, line, line, args.last_chan_in); 1285 1286 if (args.track != NO) { 1287 char real_nick[MAX_NICK_LEN]; 1288 int seppos = -1; 1289 int i; 1290 for (i=0; i<strlen(line); i++) { 1291 if (line[i] == ',' || line[i] == ':') { 1292 seppos = i; 1293 break; 1294 } 1295 if (line[i] == ' ') 1296 break; 1297 } 1298 if (seppos >= 0) { 1299 char sep; 1300 sep = line[seppos]; 1301 line[seppos] = 0; 1302 pthread_mutex_lock(&fifos.ctrl.mutex); 1303 int c = scan_collection(&fifos, (const char **) fifos.surface_name, line); 1304 if (c >= 0) 1305 strncpy(real_nick, fifos.chans[c], MAX_NICK_LEN); 1306 pthread_mutex_unlock(&fifos.ctrl.mutex); 1307 line[seppos] = sep; 1308 if (c >= 0) { 1309 // replace surface name by real name 1310 // we must move temporarily the line out of the way 1311 debug("i will allocate %d bytes and copy %s\n", (strlen(line) + 1) * sizeof(char), line); 1312 char* old_line = malloc((strlen(line) + 1) * sizeof(char)); 1313 assert(old_line); 1314 strcpy(old_line, line); 1315 1316 // the size of the line needs to change 1317 // first, count the part before line starts 1318 // then count the length of the line plus \0 byte 1319 // then adjust: the size of the real_nick 1320 // minus seppos, the size of the old nick 1321 int new_size = (line-full_line) + strlen(line) + 1 + (strlen(real_nick) - seppos); 1322 int target_offset = target ? (target - full_line) : (-1); 1323 int line_offset = line - full_line; 1324 debug("-1 my full line is: %s\n", full_line); 1325 char *nfull_line = realloc(full_line, new_size*sizeof(char)); 1326 assert(nfull_line); 1327 full_line = nfull_line; 1328 debug("resized to: %d\n", new_size*sizeof(char)); 1329 debug("0 my full line is: %s\n", full_line); 1330 if (target) 1331 target = full_line + target_offset; 1332 line = full_line + line_offset; 1333 1334 // now do the copy 1335 debug("1 my full line is: %s\n", full_line); 1336 strcpy(line, real_nick); 1337 debug("2 my full line is: %s\n", full_line); 1338 strcat(line, old_line + seppos); 1339 debug("3 my full line is: %s\n", full_line); 1340 1341 free(old_line); 1342 1343 debug("now my full line is: %s\n", full_line); 1344 debug("now my target is: %s\n", target); 1345 debug("now my line is: %s\n", line); 1346 } 1347 } 1348 } 1349 1350 /* Manage the fact that target may be "" */ 1351 if (!target) { 1352 debug("have to infer destination\n"); 1353 switch(args.default_destination) { 1354 case DEFAULT_FIRST: 1355 push_fifo_set(&fifos, full_line, first_chan(), line); 1356 return; 1357 1358 case DEFAULT_LAST_IN: 1359 if (args.show_nick_prefix && args.last_nick_in[0] 1360 && args.last_chan_in[0] == '#') { 1361 if (line[0] != '\n') { 1362 msg = malloc((2*MAX_LINE_LEN + 2)* sizeof(char)); 1363 assert(msg); 1364 msg[0] = 0; 1365 if (args.track == YES) { 1366 strncat((char*) msg, input_nick(args.last_nick_in), 1367 MAX_LINE_LEN-1); 1368 } else { 1369 strncat((char*) msg, args.last_nick_in, MAX_LINE_LEN-1); 1370 } 1371 strcat((char*) msg, ": "); 1372 strncat((char*) msg, line, MAX_LINE_LEN-1); 1373 free(full_line); 1374 full_line = msg; 1375 line = msg; 1376 } else { 1377 args.last_nick_in[0] = 0; 1378 } 1379 } 1380 debug("last_chan_in is %s\n", args.last_chan_in); 1381 return push_fifo_set(&fifos, full_line, 1382 args.last_chan_in[0] ? args.last_chan_in : first_chan(), line); 1383 1384 case DEFAULT_LAST_OUT: 1385 return push_fifo_set(&fifos, full_line, 1386 args.last_chans_out[0] ? args.last_chans_out : first_chan(), line); 1387 1388 case DEFAULT_ALL: 1389 //TODO2: ugly, could have generated the comma-separated string 1390 //like for last_chans_out! 1391 if (args.n_channels) 1392 return push_fifo_set_all(&fifos, full_line, args.channels, args.n_channels, line); 1393 else 1394 return push_fifo_set(&fifos, full_line, args.nick, line); // fallback 1395 1396 default: 1397 return; // won't happen 1398 } 1399 } else { 1400 push_fifo_set(&fifos, full_line, target, line); 1401 } 1402 } 1403 1404 void saw_user(const char *nick) { 1405 if (!nick) return; 1406 if (!surface_nick_exists(&fifos, nick)) { 1407 // unknown user, register them 1408 debug("Register this new user\n"); 1409 register_nick(&fifos, nick); 1410 } 1411 } 1412 1413 // Manage an event 1414 void manage_event (irc_session_t *session, const char *event, const char *origin, 1415 const char **params, unsigned int count) 1416 { 1417 int rsl = 0; 1418 char temp[MAX_NICK_LEN+1]; 1419 1420 saw_user(origin); 1421 if (origin) { 1422 debug("irc_target_get_nick(%s, %d)\n", origin, MAX_NICK_LEN); 1423 irc_target_get_nick(origin, temp, MAX_NICK_LEN); 1424 } else { 1425 temp[0] = 0; 1426 } 1427 if (!strcmp(temp, args.nick)) { 1428 // it is us, store our hostname 1429 irc_target_get_host(origin, args.hostname, MAX_NICK_LEN); 1430 } 1431 1432 if (!strcmp(event, "ERROR")) { 1433 if (count == 1) { 1434 die(E_SERVER_ERROR, "Error from server: %s\n", params[0]); 1435 } else { 1436 die(E_SERVER_ERROR, "Unspecified error from server\n"); 1437 } 1438 } 1439 1440 irc_ctx_t * ctx; 1441 switch(atoi(event)) { 1442 case LIBIRC_RFC_ERR_ERRONEUSNICKNAME: 1443 if (args.force_nick) { 1444 die(E_BADNAME, "Could not set nick\n"); 1445 } else { 1446 if (!((irc_ctx_t *) irc_get_ctx(session))->ready) { 1447 info("Could not set nick, reverting to \"irctk\"\n"); 1448 strncpy(args.nick, "irctk", MAX_NICK_LEN-1); 1449 rsl = irc_cmd_nick(session, "irctk"); 1450 } else { 1451 info("Could not set nick, ignoring\n"); 1452 rsl = 0; 1453 } 1454 } 1455 break; 1456 case LIBIRC_RFC_ERR_NICKCOLLISION: 1457 case LIBIRC_RFC_ERR_NICKNAMEINUSE: 1458 if (args.force_nick) { 1459 die(E_BADNAME, "Nickname already in use\n"); 1460 } else { 1461 int len = strlen(args.nick); 1462 if (len >= MAX_NICK_LEN-1) { 1463 die(E_BADNAME, "No available nickname found\n"); 1464 } else { 1465 args.nick[len] = '_'; 1466 args.nick[len+1] = 0; 1467 // no need to rename self because the surface_name of ourself points 1468 // to args.nick 1469 rsl = irc_cmd_nick(session, args.nick); 1470 info("Requested nick is taken, try %s\n", args.nick); 1471 } 1472 } 1473 break ; 1474 case LIBIRC_RFC_ERR_PASSWDMISMATCH: 1475 ctx = (irc_ctx_t *) irc_get_ctx (session); 1476 if (ctx->ready) { 1477 // this could be about a wrong password for /oper, so make it non-fatal 1478 info("Invalid or missing password.\n"); 1479 } else { 1480 // assuming this is about trying to connect to the server 1481 die(E_PASSWORD, "Invalid or missing password when connecting to server.\n"); 1482 } 1483 break; 1484 case LIBIRC_RFC_ERR_BADCHANNELKEY: 1485 info("Cannot join %s: bad password.\n", params[1]); 1486 break; 1487 case LIBIRC_RFC_ERR_NOSUCHCHANNEL: 1488 info("Cannot join %s: no such channel.\n", params[1]); 1489 break ; 1490 case LIBIRC_RFC_ERR_TOOMANYCHANNELS: 1491 info("Cannot join %s: you have joined too many channels.\n", params[1]); 1492 break ; 1493 case LIBIRC_RFC_ERR_INVITEONLYCHAN: 1494 info("Cannot join %s: channel is invite-only.\n", params[1]); 1495 break ; 1496 case LIBIRC_RFC_ERR_NOTONCHANNEL: 1497 info("Cannot perform action in %s: not on channel.\n", params[1]); 1498 break ; 1499 case LIBIRC_RFC_ERR_CHANOPRIVSNEEDED: 1500 info("Cannot perform action in %s: you're not channel operator.\n", params[1]); 1501 break ; 1502 case LIBIRC_RFC_ERR_CANNOTSENDTOCHAN: 1503 info("Cannot send to %s.\n", params[1]); 1504 break ; 1505 case ERR_BADCHANNAME: 1506 info("Could not join %s.\n", params[1]); 1507 break ; 1508 case LIBIRC_RFC_ERR_USERONCHANNEL: 1509 info("Cannot invite %s to %s: %s is already on %s.\n", params[1], params[2], params[1], params[2]); 1510 break ; 1511 case LIBIRC_RFC_ERR_NOSUCHNICK: 1512 info("No such nick/channel: %s\n", params[1]); 1513 break ; 1514 default: 1515 if (!strcmp(event, "INVITE")) { 1516 if (args.event_to == COMMAND) 1517 mprintf("[%s] <%s> /invite %s %s\n", params[1], output_nick(origin), params[0], params[1]); 1518 if (args.event_to == MESSAGE) 1519 mprintf("[%s] -!- %s invited %s to join %s\n", params[1], output_nick(origin), params[0], params[1]); 1520 } 1521 } 1522 1523 switch (count ) { 1524 case 2: 1525 debug("%s %s: %s %s\n", origin, event, params[0], params[1]); 1526 break; 1527 case 1: 1528 debug("%s %s: %s\n", origin, event, params[0]); 1529 break; 1530 default: 1531 debug("Event \"%s\", origin: \"%s\", params: %d\n", event, 1532 origin ? origin : "NULL", count); 1533 } 1534 if (rsl > 0) { 1535 int error = irc_errno(session); 1536 info("(%d) %s when managing event: %s\n", 1537 error, irc_strerror(error), event); 1538 } 1539 } 1540 1541 // Manage a mode event 1542 void manage_mode_event (irc_session_t *session, const char *event, const char *origin, 1543 const char **params, unsigned int count) 1544 { 1545 manage_event (session, event, origin, params, count); 1546 1547 // now, specific processing 1548 if (args.event_to == COMMAND) 1549 mprintf("[%s] <%s> /mode %s %s\n", params[0], output_nick(origin), params[1], params[0]); 1550 if (args.event_to == MESSAGE) 1551 mprintf("[%s] -!- %s mode %s on %s\n", params[0], output_nick(origin), params[1], params[0]); 1552 } 1553 1554 // Manage a umode event 1555 void manage_umode_event (irc_session_t *session, const char *event, const char *origin, 1556 const char **params, unsigned int count) 1557 { 1558 manage_event (session, event, origin, params, count); 1559 1560 // now, specific processing 1561 if (args.event_to == COMMAND) 1562 mprintf("[%s] <%s> /mode %s %s\n", args.nick, output_nick(origin), params[0], args.nick); 1563 if (args.event_to == MESSAGE) 1564 mprintf("[%s] -!- %s mode %s on %s\n", args.nick, output_nick(origin), params[0], args.nick); 1565 } 1566 1567 // Handle a nick event 1568 void event_nick (irc_session_t *session, const char *event, const char *origin, 1569 const char **params, unsigned int count) 1570 { 1571 debug("[] <%s> /nick %s\n", origin, params[0]); 1572 saw_user(origin); 1573 rename_user(&fifos, origin, params[0]); 1574 if (!strcmp(origin, args.nick)) 1575 strncpy(args.nick, params[0], MAX_NICK_LEN-1); 1576 if (args.event_to == COMMAND) 1577 mprintf("[] <%s> /nick %s\n", output_nick(origin), params[0]); 1578 if (args.event_to == MESSAGE) 1579 mprintf("[] -!- %s is now known as %s\n", output_nick(origin), params[0]); 1580 } 1581 1582 // Handle a join event 1583 void event_join (irc_session_t * session, const char *event, const char *origin, 1584 const char ** params, unsigned int count) 1585 { 1586 manage_event(session, event, origin, params, count); 1587 debug("[%s] <%s> /join %s\n", params[0], origin, params[0]); 1588 saw_user(params[0]); 1589 1590 if (args.event_to == COMMAND) 1591 mprintf("[%s] <%s> /join %s\n", params[0], output_nick(origin), params[0]); 1592 if (args.event_to == MESSAGE) 1593 mprintf("[%s] -!- %s has joined %s\n", params[0], output_nick(origin), params[0]); 1594 } 1595 1596 // Handle a part event 1597 void event_part (irc_session_t *session, const char *event, const char *origin, 1598 const char **params, unsigned int count) 1599 { 1600 saw_user(origin); 1601 if (args.event_to == COMMAND) 1602 mprintf("[%s] <%s> /part %s\n", params[0], output_nick(origin), params[0]); 1603 if (args.event_to == MESSAGE) 1604 mprintf("[%s] -!- %s has left %s (%s)\n", params[0], output_nick(origin), params[0], 1605 params[1]); 1606 } 1607 1608 // Handle a topic event 1609 void event_topic (irc_session_t *session, const char *event, const char *origin, 1610 const char **params, unsigned int count) 1611 { 1612 saw_user(origin); 1613 if (args.event_to == COMMAND) 1614 mprintf("[%s] <%s> /topic %s\n", params[0], output_nick(origin), params[1]); 1615 if (args.event_to == MESSAGE) 1616 mprintf("[%s] -!- %s changed the topic of %s to %s\n", params[0], output_nick(origin), 1617 params[0], params[1]); 1618 } 1619 1620 // Handle a quit event 1621 void event_quit (irc_session_t *session, const char *event, const char *origin, 1622 const char **params, unsigned int count) 1623 { 1624 saw_user(origin); 1625 if (args.event_to == COMMAND) 1626 mprintf("[] <%s> /quit %s\n", output_nick(origin), params[0]); 1627 if (args.event_to == MESSAGE) 1628 mprintf("[] -!- %s has quit (%s)\n", output_nick(origin), params[0]); 1629 } 1630 1631 // Handle an action 1632 void event_ctcp_action (irc_session_t *session, const char *event, 1633 const char *origin, const char **params, unsigned int count) 1634 { 1635 saw_user(origin); 1636 if (args.event_to == COMMAND) 1637 mprintf("[%s] <%s> /me %s\n", params[0], output_nick(origin), params[1]); 1638 if (args.event_to == MESSAGE) 1639 mprintf("[%s] *** %s %s\n", params[0], output_nick(origin), params[1]); 1640 } 1641 1642 // Handle a kick event 1643 void event_kick (irc_session_t *session, const char *event, const char *origin, 1644 const char **params, unsigned int count) 1645 { 1646 saw_user(origin); 1647 saw_user(params[1]); 1648 if (args.event_to == COMMAND) 1649 mprintf("[%s] <%s> /kick %s %s\n", params[0], output_nick(origin), params[1], params[2]); 1650 if (args.event_to == MESSAGE) 1651 mprintf("[%s] -!- %s was kicked from %s by %s (%s)\n", params[0], output_nick(params[1]), 1652 params[0], output_nick(origin), params[2]); 1653 } 1654 1655 // Handle a connect event 1656 void event_connect (irc_session_t *session, const char *event, const char *origin, 1657 const char **params, unsigned int count) 1658 { 1659 saw_user(origin); 1660 irc_ctx_t * ctx = (irc_ctx_t *) irc_get_ctx (session); 1661 int i; 1662 1663 manage_event(session, event, origin, params, count); 1664 1665 pthread_mutex_lock(&args.mutex); 1666 ctx->ready = 1; 1667 pthread_mutex_unlock(&args.mutex); 1668 debug("Connected!\n"); 1669 1670 for (i = 0; i < ctx->n_channels; i++) { 1671 if (ctx->channels[i][0] != '#') 1672 continue; // not a regular channel 1673 debug("Attempt to join %s\n", ctx->channels[i]); 1674 int pw_pos = get_password(ctx->channels[i]); 1675 irc_cmd_join (session, ctx->channels[i], ctx->channels[i] + pw_pos + 1); 1676 revert_password(ctx->channels[i], pw_pos); 1677 } 1678 } 1679 1680 // Handle a channel event 1681 void event_channel (irc_session_t *session, const char *event, 1682 const char *origin, const char **params, unsigned int count) 1683 { 1684 saw_user(origin); 1685 int i = 0, ok = 1, ok2; 1686 const char* pruned; 1687 1688 if (count != 2) return; 1689 1690 if (args.filter) 1691 if (strcmp(params[0], args.nick)) { 1692 int len = strlen(args.nick); 1693 int offset = 0; 1694 while (params[1][offset] == ' ') 1695 offset++; 1696 for (i=0; i<len; i++) 1697 if (args.nick[i] != params[1][offset + i] || !params[1][offset + i]) { 1698 ok = 0; 1699 break; 1700 } 1701 if (ok) { 1702 // check the nick ends here 1703 char next = params[1][offset + len]; 1704 if (next != ':' && next != ' ' && next != ',') 1705 ok = 0; // nick continues 1706 } 1707 } 1708 1709 if (ok) { 1710 if (!args.prune) { 1711 mprintf ("[%s] <", params[0]); 1712 if (!origin) 1713 mprintf("someone"); 1714 else 1715 /* not robust, and useless since we use STRIPNICKS */ 1716 //while(origin[i++] != '!') putchar(origin[i-1]); 1717 mprintf("%s", output_nick(origin)); 1718 mprintf("> "); 1719 if (params[1][0] == '/' && args.event_to == COMMAND) 1720 mprintf("/ "); // escape slashes 1721 mprintf("%s\n", params[1] ); 1722 } else { 1723 pruned = params[1]; 1724 while (pruned[0] == ' ') pruned++; 1725 ok2 = 1; 1726 while(pruned[0] != ':' && pruned[0] != ',' && pruned[0] != ' ') { 1727 if (!pruned[0] || pruned[0] == ' ') { 1728 ok2 = 0; 1729 break; 1730 } 1731 pruned++; 1732 } 1733 if (params[0][0] != '#') { 1734 // must be a private message 1735 ok2 = 0; 1736 } 1737 1738 if (ok2) { 1739 pruned++; // skip ':' 1740 while(pruned[0] == ' ') 1741 pruned++; // skip spaces 1742 } 1743 else pruned = params[1]; // no address 1744 1745 mprintf("%s\n", pruned); 1746 } 1747 // if addressed in our private chan, reply on the sender's priv chan 1748 if (strcmp(params[0], args.nick)) 1749 strncpy(args.last_chan_in, params[0], MAX_CHAN_LEN-1); 1750 else strncpy(args.last_chan_in, origin?origin:"someone", MAX_CHAN_LEN-1); 1751 // if UNIQUE, store the uid, else store the surface 1752 if (args.track == UNIQUE) { 1753 strncpy(args.last_nick_in, origin?output_nick(origin):"someone", 1754 MAX_NICK_LEN-1); 1755 } else { 1756 strncpy(args.last_nick_in, origin?origin:"someone", MAX_NICK_LEN-1); 1757 } 1758 } 1759 } 1760 1761 // Handle a privmsg 1762 void event_privmsg (irc_session_t *session, const char *event, 1763 const char *origin, const char **params, unsigned int count) 1764 { 1765 saw_user(origin); 1766 manage_event(session, event, origin, params, count); 1767 1768 event_channel (session, event, origin, params, count); 1769 } 1770 1771 // Handle a numeric event 1772 void event_numeric (irc_session_t *session, unsigned int event, 1773 const char *origin, const char **params, unsigned int count) 1774 { 1775 saw_user(origin); 1776 char buf[24]; 1777 sprintf (buf, "%d", event); 1778 1779 manage_event(session, buf, origin, params, count); 1780 } 1781 1782 1783 irc_session_t* do_connect() 1784 { 1785 irc_session_t *s; 1786 irc_callbacks_t callbacks; 1787 memset (&callbacks, 0, sizeof(callbacks)); 1788 args.ready = 0 ; 1789 callbacks.event_connect = event_connect; 1790 callbacks.event_nick = event_nick; 1791 callbacks.event_quit = event_quit; 1792 callbacks.event_join = event_join; 1793 callbacks.event_part = event_part; 1794 callbacks.event_mode = manage_mode_event; 1795 callbacks.event_umode = manage_umode_event; 1796 callbacks.event_topic = event_topic; 1797 callbacks.event_kick = event_kick; 1798 callbacks.event_channel = event_channel; 1799 callbacks.event_privmsg = event_privmsg; 1800 callbacks.event_notice = manage_event; 1801 callbacks.event_invite = manage_event; 1802 callbacks.event_ctcp_rep = manage_event; 1803 callbacks.event_ctcp_action = event_ctcp_action; 1804 callbacks.event_unknown = manage_event; 1805 callbacks.event_numeric = event_numeric; 1806 callbacks.event_dcc_chat_req = NULL; 1807 callbacks.event_dcc_send_req = NULL; 1808 1809 s = irc_create_session (&callbacks); 1810 1811 if (!s) 1812 die(E_SESSION, "Could not create session\n"); 1813 1814 if (!args.with_host) 1815 irc_option_set (s, LIBIRC_OPTION_STRIPNICKS); 1816 if(!args.ssl_check) 1817 irc_option_set (s, LIBIRC_OPTION_SSL_NO_VERIFY); 1818 irc_set_ctx (s, &args); 1819 1820 debug("Connecting to %s port %d with nick %s and password %s...\n", 1821 args.server, args.port, args.nick, args.password); 1822 1823 if (irc_connect(s, args.server, args.port, args.password, args.nick, 1824 args.username, args.realname)) { 1825 info("Could not connect: %s\n", irc_strerror (irc_errno(s))); 1826 if (irc_errno(s) == LIBIRC_ERR_SSL_NOT_SUPPORTED) 1827 info("(Did you ./configure --enable-ssl when building libircclient?)\n"); 1828 exit(E_CONNECT); 1829 } 1830 1831 return s; 1832 } 1833 1834 // The IRC thread entry point 1835 static void* irc_thread (void *arg) 1836 { 1837 irc_session_t * sp = (irc_session_t *) arg; 1838 int ret; 1839 irc_run(sp); 1840 ret = irc_errno(sp); 1841 pthread_mutex_lock(&args.mutex); 1842 if (args.ready == 0) { 1843 // we hadn't even connected yet 1844 // TODO2 maybe not all errors are fatal? 1845 if (ret) 1846 info("Connection failed: %s\n", irc_strerror(ret)); 1847 else 1848 // TODO2 this seems to happen when a port is blocked 1849 // can we notice the error elsewhere? 1850 info("Connection failed (no reason specified)\n"); 1851 if (ret == LIBIRC_ERR_CONNECT_SSL_FAILED) 1852 info("(Maybe try without --ssl?)\n"); 1853 if (ret == LIBIRC_ERR_SSL_CERT_VERIFY_FAILED) 1854 info("(Try with --no-check-certificate if security is not an issue?)\n"); 1855 } else { 1856 info("Connection interrupted: %s\n", irc_strerror(ret)); 1857 } 1858 args.ready = -ret; 1859 pthread_mutex_unlock(&args.mutex); 1860 return 0; 1861 } 1862 1863 // The fifo_in thread entry point 1864 static void* fifo_in_thread (void *arg) { 1865 char *line = NULL; 1866 int res; 1867 size_t size; 1868 1869 while ((res = getline(&line, (size_t*) &size, stdin)) != -1) { 1870 debug("[thread_in] READS %s\n", line); 1871 debug("someone will have to free %p\n", line); 1872 if (line[0] == '[') { 1873 int i=0; 1874 char *msg; 1875 char *target = line+1; 1876 while (line[i] != ']' && line[i]) 1877 i++; 1878 if (!line[i]) 1879 die(E_BADLINE, "Malformed address prefix\n"); 1880 msg = line + i + 1; 1881 if (msg[0] == ' ') 1882 msg++; 1883 if (i) 1884 line[i] = 0; 1885 debug("[thread_in] calling with line target msg %p %p %p\n", line, target, msg); 1886 strncpy(args.last_chans_out, target, MAX_CHANS_LEN-1); 1887 cmd_msg(line, target, msg); 1888 } else { 1889 /* No target specified, we attempt the default */ 1890 debug("[thread_in] calling with line target msg %p %p %p\n", line, NULL, line); 1891 cmd_msg(line, NULL, line); 1892 } 1893 1894 // the popper will free the line 1895 line = NULL; 1896 } 1897 // sentinel 1898 debug("[thread_in] pushed sentinel\n"); 1899 push_fifo_set(&fifos, NULL, "", NULL); 1900 free(line); // which wasn't used 1901 return 0; 1902 } 1903 1904 1905 // Start the IRC thread and monitor stdin 1906 int start (int max_wait) 1907 { 1908 irc_session_t * s; 1909 pthread_t tid_irc; 1910 int i; 1911 int waiting = 0; 1912 int cont = 1; 1913 action *results; 1914 int n_results; 1915 int rsl, error; 1916 1917 s = do_connect(); 1918 1919 debug("Connection request successful!\n"); 1920 1921 debug("Starting threads...\n"); 1922 if (pthread_create (&tid_irc, 0, irc_thread, (void*) s) != 0) 1923 die(E_THREAD, "Could not create thread: %s\n", strerror(errno)); 1924 debug("Threads started!\n"); 1925 1926 if (args.show_inferred && args.default_destination == DEFAULT_LAST_OUT) 1927 fprintf(stderr, "[%s] ", args.last_chans_out); 1928 1929 //gettimeofday(&tp1, NULL); 1930 1931 // TODO do things nicely with a cond raised by a connection error, connection 1932 // success, or timeout 1933 waiting = 0; 1934 usleep(200000); 1935 while (1) { 1936 int val; 1937 pthread_mutex_lock(&args.mutex); 1938 val = args.ready; 1939 pthread_mutex_unlock(&args.mutex); 1940 if (val) 1941 break; 1942 debug("Waiting for the connection to be ready...\n"); 1943 debug("waiting (%d)\n", waiting); 1944 sleep(1); 1945 waiting++; 1946 if (waiting >= max_wait) { 1947 int new_max_wait = max_wait * args.retry_factor; 1948 info("Connection timed out after %d seconds, retrying for %d seconds...\n", 1949 max_wait, new_max_wait); 1950 irc_disconnect(s); 1951 args.ready = 0; 1952 sleep(1); 1953 return start(new_max_wait); 1954 } 1955 } 1956 1957 if (args.ready > 0) { 1958 1959 debug("[main] Starting loop\n"); 1960 1961 irc_ctx_t * ctx = (irc_ctx_t *) irc_get_ctx (s); 1962 for (i = 0; i < (args.join ? fifos.n : ctx->n_channels); i++) { 1963 char *chan = args.join ? fifos.chans[i] : ctx->channels[i]; 1964 if (chan[0] != '#') 1965 continue; // not a regular channel 1966 debug("[reboot] rejoining %s\n",chan); 1967 int pw_pos = get_password(chan); 1968 irc_cmd_join (s, chan, chan + pw_pos + 1); 1969 rsl = irc_cmd_join(s, chan, pw_pos >= 0 ? chan + pw_pos + 1 : NULL); 1970 revert_password(chan, pw_pos); 1971 } 1972 1973 while (cont) { 1974 n_results = pop_fifo_set(&fifos, &results); 1975 debug("[main] I popped %d results\n", n_results); 1976 for (i = 0; i < n_results; i++) { 1977 if (!results[i].line) { 1978 debug("[main] Sentinel seen, we will exit\n"); 1979 cont = 0; // sentinel 1980 } else { 1981 debug("[main] manage line %s %s, pointer %p\n", 1982 results[i].destination, results[i].line, results[i].full_line ); 1983 rsl = do_cmd_msg(s, results[i].destination, results[i].line); 1984 if (rsl > 0) { 1985 error = irc_errno(s); 1986 info("(%d) %s when interpreting: %s", 1987 error, irc_strerror(error), results[i].line); 1988 } 1989 if (rsl < 0) { 1990 // successful exit 1991 cont = 0; 1992 } 1993 } 1994 } 1995 // now, we free stuff 1996 for (i = 0; i < n_results; i++) 1997 if (results[i].full_line) 1998 free(results[i].full_line); 1999 free(results); 2000 debug("[main] I managed my lines\n"); 2001 int val; 2002 pthread_mutex_lock(&args.mutex); 2003 val = args.ready; 2004 pthread_mutex_unlock(&args.mutex); 2005 if (cont && val <= 0) { 2006 info("Connection lost, reconnecting...\n"); 2007 irc_disconnect(s); 2008 sleep(1); 2009 return start(max_wait); 2010 } 2011 2012 if (args.show_inferred && args.default_destination == DEFAULT_LAST_OUT) 2013 fprintf(stderr, "[%s] ", args.last_chans_out); 2014 2015 if (cont) 2016 usleep(args.interval); 2017 2018 debug("[main] endloop\n"); 2019 } 2020 debug("[main] exiting\n"); 2021 2022 usleep(args.interval_after); 2023 } 2024 2025 irc_disconnect(s); 2026 2027 free(args.server); 2028 pthread_mutex_destroy(&args.mutex); 2029 2030 return 0; 2031 } 2032 2033 void ssl_rename() { 2034 args.server = malloc((strlen(args.raw_server)+2) * sizeof(char*)); 2035 assert(args.server); 2036 if (args.ssl) { 2037 // this is libircclient's convention, I don't really like it 2038 args.server[0] = '#'; 2039 args.server[1] = 0; 2040 } else { 2041 args.server[0] = 0; 2042 } 2043 strcat(args.server, args.raw_server); 2044 } 2045 2046 2047 int main (int argc, char **argv) 2048 { 2049 // check libircclient version 2050 unsigned int high, low; 2051 irc_get_version(&high, &low); 2052 if (high < 1 || (high == 1 && low < 8)) { 2053 die(E_LIBIRCCLIENT_VERSION, 2054 "Need libircclient >= 1.8 but library version is %d.%d\n", 2055 high, low); 2056 } 2057 2058 // initialize the default option values 2059 initialize_args(); 2060 2061 // parse command line arguments to set option values 2062 argp_parse (&argp, argc, argv, 0, 0, &args); 2063 2064 // setup SSL 2065 ssl_rename(); 2066 2067 // initialize the fifo 2068 init_fifo_set(&fifos); 2069 saw_user(args.nick); 2070 2071 // start stdin thread 2072 pthread_t tid_in ; 2073 if (pthread_create (&tid_in, 0, fifo_in_thread, (void*) NULL) != 0) 2074 die(E_THREAD, "Could not create thread: %s\n", strerror(errno)); 2075 2076 2077 2078 //return test_fifo_set(); 2079 2080 // start trying to connect with the initial retry interval 2081 return start(args.retry_after); 2082 } 2083