handle.c (17296B)
1 /* 2 * src/handle.c from hirc 3 * 4 * Copyright (c) 2021-2022 hhvn <dev@hhvn.uk> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 * 18 */ 19 20 #include <stdio.h> 21 #include <ctype.h> 22 #include <string.h> 23 #include <stdlib.h> 24 #include "hirc.h" 25 #include "data/handlers.h" 26 27 HANDLER( 28 handle_PING) { 29 assert_warn(param_len(msg->params) >= 2,); 30 31 serv_write(server, Sched_now, "PONG :%s\r\n", *(msg->params+1)); 32 } 33 34 HANDLER( 35 handle_PONG) { 36 int len; 37 38 assert_warn((len = param_len(msg->params)) >= 2,); 39 40 /* RFC1459 says that PONG should have a list of daemons, 41 * but that's not how PONG seems to work in modern IRC. 42 * Therefore, consider the last parameter as the "message" */ 43 if (strcmp_n(*(msg->params + len - 1), expect_get(server, Expect_pong)) == 0) { 44 hist_addp(server->history, msg, Activity_status, HIST_DFL); 45 expect_set(server, Expect_pong, NULL); 46 } 47 } 48 49 HANDLER( 50 handle_JOIN) { 51 struct Channel *chan; 52 struct Nick *nick; 53 char *target; 54 55 assert_warn(msg->from && param_len(msg->params) >= 2,); 56 57 target = *(msg->params+1); 58 if ((chan = chan_get(&server->channels, target, -1)) == NULL) 59 chan = chan_add(server, &server->channels, target, 0); 60 chan_setold(chan, 0); 61 62 nick = msg->from; 63 if (nick_get(&chan->nicks, nick->nick) == NULL) 64 nick_add(&chan->nicks, msg->from->prefix, ' ', server); 65 66 hist_addp(server->history, msg, Activity_status, HIST_LOG); 67 hist_addp(chan->history, msg, Activity_status, HIST_DFL); 68 69 if (nick_isself(nick)) { 70 if (strcmp_n(target, expect_get(server, Expect_join)) == 0) 71 ui_select(server, chan); 72 else 73 windows[Win_buflist].refresh = 1; 74 expect_set(server, Expect_join, NULL); 75 } else if (selected.channel == chan) { 76 windows[Win_nicklist].refresh = 1; 77 } 78 } 79 80 HANDLER( 81 handle_PART) { 82 struct Channel *chan; 83 struct Nick *nick; 84 char *target; 85 86 assert_warn(msg->from && param_len(msg->params) >= 2,); 87 88 target = *(msg->params+1); 89 if ((chan = chan_get(&server->channels, target, -1)) == NULL) 90 return; 91 92 nick = msg->from; 93 if (nick_isself(nick)) { 94 chan_setold(chan, 1); 95 nick_free_list(&chan->nicks); 96 if (chan == selected.channel && strcmp_n(target, expect_get(server, Expect_part)) == 0) { 97 ui_select(selected.server, NULL); 98 expect_set(server, Expect_part, NULL); 99 } 100 windows[Win_buflist].refresh = 1; 101 } else { 102 nick_remove(&chan->nicks, nick->nick); 103 if (chan == selected.channel) 104 windows[Win_nicklist].refresh = 1; 105 } 106 107 hist_addp(server->history, msg, Activity_status, HIST_LOG); 108 hist_addp(chan->history, msg, Activity_status, HIST_DFL); 109 } 110 111 HANDLER( 112 handle_KICK) { 113 struct Channel *chan; 114 struct Nick *nick; 115 char *target; 116 117 assert_warn(msg->from && param_len(msg->params) >= 3,); 118 119 target = *(msg->params+1); 120 if ((chan = chan_get(&server->channels, target, -1)) == NULL) 121 chan = chan_add(server, &server->channels, target, 0); 122 123 nick = nick_create(*(msg->params+2), ' ', server); 124 if (nick_isself(nick)) { 125 chan_setold(chan, 1); 126 nick_free_list(&chan->nicks); 127 if (chan == selected.channel) 128 ui_select(selected.server, NULL); 129 windows[Win_buflist].refresh = 1; 130 } else { 131 nick_remove(&chan->nicks, nick->nick); 132 if (chan == selected.channel) 133 windows[Win_nicklist].refresh = 1; 134 } 135 136 hist_addp(server->history, msg, Activity_status, HIST_LOG); 137 hist_addp(chan->history, msg, Activity_status, HIST_DFL); 138 nick_free(nick); 139 } 140 141 HANDLER( 142 handle_ERROR) { 143 char *lowered, *p; 144 int recon = 1; 145 146 if (param_len(msg->params) > 1) { 147 lowered = estrdup(*(msg->params+1)); 148 for (p = lowered; *p; p++) 149 *p = tolower(*p); 150 if (strstr(lowered, "unauthorized") || 151 strstr(lowered, "invalid") || 152 strstr(lowered, "kill") || 153 strstr(lowered, "ban") || 154 strstr(lowered, "kline") || 155 strstr(lowered, "gline") || 156 strstr(lowered, "k-line") || 157 strstr(lowered, "g-line")) 158 recon = 0; 159 pfree(&lowered); 160 } 161 162 serv_disconnect(server, recon, NULL); 163 hist_addp(server->history, msg, Activity_status, HIST_DFL); 164 } 165 166 HANDLER( 167 handle_QUIT) { 168 struct Channel *chan; 169 struct Nick *nick; 170 171 assert_warn(msg->from && param_len(msg->params) >= 1,); 172 173 nick = msg->from; 174 if (nick_isself(nick)) { 175 serv_disconnect(server, 0, NULL); 176 } 177 178 hist_addp(server->history, msg, Activity_status, HIST_LOG); 179 for (chan = server->channels; chan; chan = chan->next) { 180 if (nick_get(&chan->nicks, nick->nick) != NULL) { 181 nick_remove(&chan->nicks, nick->nick); 182 hist_addp(chan->history, msg, Activity_status, HIST_DFL); 183 if (chan == selected.channel) 184 windows[Win_nicklist].refresh = 1; 185 } 186 } 187 } 188 189 HANDLER( 190 handle_MODE) { 191 struct Channel *chan; 192 193 assert_warn(msg->from && param_len(msg->params) >= 3,); 194 195 if (serv_ischannel(server, *(msg->params+1))) { 196 if ((chan = chan_get(&server->channels, *(msg->params+1), -1)) == NULL) 197 chan = chan_add(server, &server->channels, *(msg->params+1), 0); 198 199 expect_set(server, Expect_nosuchnick, NULL); 200 hist_addp(server->history, msg, Activity_status, HIST_LOG); 201 hist_addp(chan->history, msg, Activity_status, HIST_DFL); 202 serv_write(server, Sched_now, "MODE %s\r\n", chan->name); /* Get full mode via RPL_CHANNELMODEIS 203 * instead of concatenating manually */ 204 serv_write(server, Sched_now, "NAMES %s\r\n", chan->name); /* Also get updated priviledges */ 205 } else { 206 hist_addp(server->history, msg, Activity_status, HIST_DFL); 207 } 208 } 209 210 HANDLER( 211 handle_PRIVMSG) { 212 int act_direct = Activity_hilight, act_regular = Activity_message, act; 213 struct Channel *chan; 214 struct Nick *nick; 215 char *target; 216 217 assert_warn(msg->from && param_len(msg->params) >= 3,); 218 219 if (strcmp(*msg->params, "NOTICE") == 0) 220 act_direct = act_regular = Activity_notice; 221 222 target = *(msg->params + 1); 223 nick = msg->from; 224 if (strchr(nick->nick, '.')) { 225 /* it's a server */ 226 hist_addp(server->history, msg, Activity_status, HIST_DFL); 227 } else if (strcmp_n(target, server->self->nick) == 0) { 228 /* it's messaging me */ 229 if ((chan = chan_get(&server->queries, nick->nick, -1)) == NULL) 230 chan = chan_add(server, &server->queries, nick->nick, 1); 231 chan_setold(chan, 0); 232 233 hist_addp(chan->history, msg, act_direct, HIST_DFL); 234 } else if (nick_isself(nick) && !strchr("#&!+", *target)) { 235 /* i'm messaging someone */ 236 if ((chan = chan_get(&server->queries, target, -1)) == NULL) 237 chan = chan_add(server, &server->queries, target, 1); 238 chan_setold(chan, 0); 239 240 hist_addp(chan->history, msg, act_regular, HIST_DFL); 241 } else { 242 /* message to a channel */ 243 if ((chan = chan_get(&server->channels, target, -1)) == NULL) 244 chan = chan_add(server, &server->channels, target, 0); 245 246 if (strstr(*(msg->params+2), server->self->nick)) 247 act = act_direct; 248 else 249 act = act_regular; 250 hist_addp(chan->history, msg, act, HIST_DFL); 251 } 252 } 253 254 HANDLER( 255 handle_INVITE) { 256 struct Channel *query; 257 258 assert_warn(msg->from && param_len(msg->params) >= 3,); 259 260 if ((query = chan_get(&server->queries, msg->from->nick, -1)) != NULL) 261 hist_addp(query->history, msg, Activity_status, HIST_DFL); 262 else 263 hist_addp(server->history, msg, Activity_status, HIST_DFL); 264 } 265 266 HANDLER( 267 handle_RPL_ISUPPORT) { 268 char *key, *value; 269 char **params = msg->params; 270 271 hist_addp(server->history, msg, Activity_status, HIST_DFL); 272 assert_warn(param_len(msg->params) >= 4,); 273 274 params += 2; 275 276 /* skip the last param ".... :are supported by this server" */ 277 for (; *params && *(params+1); params++) { 278 key = estrdup(*params); 279 if ((value = strchr(key, '=')) != NULL) { 280 *value = '\0'; 281 if (*(value+1)) 282 value++; 283 else 284 value = NULL; 285 } 286 287 support_set(server, key, value); 288 pfree(&key); 289 } 290 } 291 292 HANDLER( 293 handle_RPL_AWAY) { 294 struct Channel *query; 295 296 if ((query = chan_get(&server->queries, *(msg->params+2), -1)) != NULL) { 297 hist_addp(query->history, msg, Activity_status, HIST_DFL); 298 hist_addp(server->history, msg, Activity_status, HIST_LOG); 299 } else { 300 hist_addp(server->history, msg, Activity_status, HIST_DFL); 301 } 302 } 303 304 HANDLER( 305 handle_RPL_CHANNELMODEIS) { 306 struct Channel *chan; 307 308 assert_warn(param_len(msg->params) >= 4,); 309 310 if ((chan = chan_get(&server->channels, *(msg->params+2), -1)) == NULL) 311 chan = chan_add(server, &server->channels, *(msg->params+2), 0); 312 313 pfree(&chan->mode); 314 chan->mode = estrdup(*(msg->params+3)); 315 316 hist_addp(server->history, msg, Activity_status, HIST_LOG); 317 if (expect_get(server, Expect_channelmodeis)) { 318 hist_addp(chan->history, msg, Activity_status, HIST_DFL); 319 expect_set(server, Expect_channelmodeis, NULL); 320 } else { 321 hist_addp(chan->history, msg, Activity_status, HIST_LOG); 322 } 323 } 324 325 HANDLER( 326 handle_RPL_INVITING) { 327 struct Channel *chan; 328 329 assert_warn(param_len(msg->params) >= 4,); 330 331 if ((chan = chan_get(&server->channels, *(msg->params+3), -1)) == NULL) 332 chan = chan_add(server, &server->channels, *(msg->params+3), 0); 333 334 hist_addp(chan->history, msg, Activity_status, HIST_DFL|HIST_SELF); 335 } 336 337 HANDLER( 338 handle_RPL_NAMREPLY) { 339 struct Channel *chan; 340 struct Nick *oldnick; 341 char **params = msg->params; 342 char *nick, priv, *target; 343 char **nicks, **nicksref; 344 char *supportedprivs; 345 346 assert_warn(param_len(params) >= 5,); 347 348 hist_addp(server->history, msg, Activity_status, HIST_LOG); 349 350 params += 3; 351 target = *params; 352 353 if ((chan = chan_get(&server->channels, target, -1)) == NULL) 354 chan = chan_add(server, &server->channels, target, 0); 355 356 if (strcmp_n(target, expect_get(server, Expect_names)) == 0) 357 hist_addp(chan->history, msg, Activity_status, HIST_DFL); 358 else 359 hist_addp(chan->history, msg, Activity_status, HIST_LOG); 360 361 params++; 362 supportedprivs = strchr(support_get(server, "PREFIX"), ')'); 363 if (supportedprivs == NULL || supportedprivs[0] == '\0') 364 supportedprivs = ""; 365 else 366 supportedprivs++; 367 368 nicksref = nicks = param_create(*params); 369 for (; *nicks && **nicks; nicks++) { 370 priv = ' '; 371 nick = *nicks; 372 if (strchr(supportedprivs, **nicks)) { 373 priv = **nicks; 374 while (strchr(supportedprivs, *nick)) 375 nick++; 376 } 377 if ((oldnick = nick_get(&chan->nicks, nick)) == NULL) 378 nick_add(&chan->nicks, nick, priv, server); 379 else 380 oldnick->priv = priv; 381 } 382 383 if (selected.channel == chan) 384 windows[Win_nicklist].refresh = 1; 385 param_free(nicksref); 386 } 387 388 HANDLER( 389 handle_RPL_ENDOFNAMES) { 390 char *target; 391 392 hist_addp(server->history, msg, Activity_status, HIST_LOG); 393 assert_warn(param_len(msg->params) >= 3,); 394 395 target = *(msg->params+2); 396 if (strcmp_n(target, expect_get(server, Expect_names)) == 0) 397 expect_set(server, Expect_names, NULL); 398 } 399 400 HANDLER( 401 handle_ERR_NOSUCHNICK) { 402 char *expectation; 403 struct Channel *chan = NULL; 404 405 if ((expectation = expect_get(server, Expect_nosuchnick)) != NULL) { 406 chan = chan_get(&server->channels, expectation, -1); 407 expect_set(server, Expect_nosuchnick, NULL); 408 } 409 410 hist_addp(chan ? chan->history : server->history, msg, Activity_error, HIST_DFL|HIST_SERR); 411 } 412 413 HANDLER( 414 handle_ERR_NICKNAMEINUSE) { 415 char nick[64]; /* should be limited to 9 chars, but newer servers *shrug*/ 416 struct Nick *nnick; 417 418 hist_addp(server->history, msg, Activity_status, HIST_DFL); 419 420 if (expect_get(server, Expect_nicknameinuse) == NULL) { 421 snprintf(nick, sizeof(nick), "%s_", server->self->nick); 422 nnick = nick_create(nick, ' ', server); 423 nick_free(server->self); 424 server->self = nnick; 425 server->self->self = 1; 426 serv_write(server, Sched_now, "NICK %s\r\n", nick); 427 } else { 428 expect_set(server, Expect_nicknameinuse, NULL); 429 } 430 } 431 432 HANDLER( 433 handle_NICK) { 434 struct Nick *nick, *chnick; 435 struct Channel *chan; 436 char prefix[128]; 437 char *newnick; 438 char priv; 439 440 assert_warn(msg->from && *msg->params && *(msg->params+1),); 441 442 nick = msg->from; 443 hist_addp(server->history, msg, Activity_status, msg->from->self ? HIST_DFL : HIST_LOG); 444 newnick = *(msg->params+1); 445 446 if (strcmp_n(nick->nick, newnick) == 0) 447 return; 448 449 if (nick_isself(nick)) { 450 nick_free(server->self); 451 server->self = nick_create(newnick, ' ', server); 452 server->self->self = 1; 453 expect_set(server, Expect_nicknameinuse, NULL); 454 } 455 456 for (chan = server->channels; chan; chan = chan->next) { 457 if ((chnick = nick_get(&chan->nicks, nick->nick)) != NULL) { 458 snprintf(prefix, sizeof(prefix), ":%s!%s@%s", 459 newnick, chnick->ident, chnick->host); 460 priv = chnick->priv; 461 nick_remove(&chan->nicks, nick->nick); 462 nick_add(&chan->nicks, prefix, priv, server); 463 hist_addp(chan->history, msg, Activity_status, HIST_DFL); 464 if (selected.channel == chan) 465 windows[Win_nicklist].refresh = 1; 466 } 467 } 468 } 469 470 HANDLER( 471 handle_TOPIC) { 472 struct Channel *chan; 473 474 assert_warn(param_len(msg->params) >= 3 && msg->from,); 475 476 if ((chan = chan_get(&server->channels, *(msg->params+1), -1)) != NULL) { 477 hist_addp(chan->history, msg, Activity_status, HIST_DFL); 478 pfree(&chan->topic); 479 chan->topic = *(msg->params+2) ? estrdup(*(msg->params+2)) : NULL; 480 } 481 } 482 483 HANDLER( 484 handle_RPL_NOTOPIC) { 485 struct Channel *chan; 486 char *target; 487 488 assert_warn(param_len(msg->params) >= 4,); 489 490 target = *(msg->params+2); 491 492 hist_addp(server->history, msg, Activity_status, HIST_LOG); 493 if ((chan = chan_get(&server->channels, target, -1)) == NULL) 494 return; 495 496 if (strcmp_n(target, expect_get(server, Expect_topic)) == 0) { 497 hist_addp(chan->history, msg, Activity_status, HIST_DFL); 498 expect_set(server, Expect_topic, NULL); 499 } else { 500 hist_addp(chan->history, msg, Activity_status, HIST_LOG); 501 } 502 } 503 504 HANDLER( 505 handle_RPL_TOPIC) { 506 struct Channel *chan; 507 char *target, *topic; 508 509 assert_warn(param_len(msg->params) >= 4,); 510 511 hist_addp(server->history, msg, Activity_status, HIST_LOG); 512 513 target = *(msg->params+2); 514 topic = *(msg->params+3); 515 516 if ((chan = chan_get(&server->channels, target, -1)) == NULL) 517 return; 518 519 pfree(&chan->topic); 520 chan->topic = topic ? estrdup(topic) : NULL; 521 522 if (strcmp_n(target, expect_get(server, Expect_topic)) == 0) { 523 hist_addp(chan->history, msg, Activity_status, HIST_DFL); 524 expect_set(server, Expect_topic, NULL); 525 expect_set(server, Expect_topicwhotime, target); 526 } else { 527 hist_addp(chan->history, msg, Activity_status, HIST_LOG); 528 } 529 } 530 531 HANDLER( 532 handle_RPL_TOPICWHOTIME) { 533 struct Channel *chan; 534 char *target; 535 536 assert_warn(param_len(msg->params) >= 5,); 537 538 hist_addp(server->history, msg, Activity_status, HIST_LOG); 539 540 target = *(msg->params+2); 541 542 if ((chan = chan_get(&server->channels, target, -1)) == NULL) 543 return; 544 545 if (strcmp_n(target, expect_get(server, Expect_topicwhotime)) == 0) { 546 hist_addp(chan->history, msg, Activity_status, HIST_DFL); 547 expect_set(server, Expect_topicwhotime, NULL); 548 } else { 549 hist_addp(chan->history, msg, Activity_status, HIST_LOG); 550 } 551 } 552 553 HANDLER( 554 handle_RPL_WELCOME) { 555 if (server->status != ConnStatus_connected) { 556 /* XXX: unify this with RPL_ENDOFMOTD */ 557 server->status = ConnStatus_connected; 558 serv_auto_send(server); 559 schedule_send(server, Sched_connected); 560 } 561 hist_addp(server->history, msg, Activity_status, HIST_DFL); 562 windows[Win_buflist].refresh = 1; 563 } 564 565 HANDLER( 566 handle_RPL_MOTD) { 567 char *text; 568 569 if (config_getl("motd.removedash")) { 570 text = msg->raw; 571 if (*text == ':') 572 text++; 573 if ((text = strchr(text, ':'))) { 574 text++; 575 if (strncmp(text, "- ", CONSTLEN("- ")) == 0) 576 memmove(text, text + 2, strlen(text + 2) + 1); 577 else if (strncmp(text, "-", CONSTLEN("-")) == 0) 578 memmove(text, text + 1, strlen(text + 1) + 1); 579 } 580 } 581 hist_addp(server->history, msg, Activity_status, HIST_DFL); 582 } 583 584 HANDLER( 585 handle_RPL_ENDOFMOTD) { 586 /* If server doesn't support RPL_WELCOME, use RPL_ENDOFMOTD to set status */ 587 if (server->status != ConnStatus_connected) { 588 server->status = ConnStatus_connected; 589 serv_auto_send(server); 590 schedule_send(server, Sched_connected); 591 } 592 hist_addp(server->history, msg, Activity_status, HIST_DFL); 593 windows[Win_buflist].refresh = 1; 594 } 595 596 void 597 handle_logonly(struct Server *server, struct History *msg) { 598 hist_addp(server->history, msg, Activity_status, HIST_LOG); 599 } 600 601 void 602 handle(struct Server *server, char *msg) { 603 struct History *hist; 604 time_t timestamp; 605 char **params; 606 char *cmd; 607 int i; 608 609 timestamp = time(NULL); 610 params = param_create(msg); 611 if (!*params) { 612 pfree(¶ms); 613 return; 614 } 615 616 if (**params == ':' || **params == '|') 617 cmd = *(params + 1); 618 else 619 cmd = *(params); 620 621 for (i=0; cmd && handlers[i].cmd; i++) { 622 if (strcmp(handlers[i].cmd, cmd) == 0) { 623 if (handlers[i].func) { 624 /* histinfo set to the server's history 625 * currently, but not actually appended */ 626 hist = hist_create(server->history, NULL, msg, 0, timestamp, 0); 627 handlers[i].func(server, hist); 628 hist_free(hist); 629 } 630 /* NULL handlers will stop a message being added to server->history */ 631 goto end; 632 } 633 } 634 635 /* add it to server->history if there is no handler */ 636 if (*cmd == '4' && *cmd == '5') 637 hist_add(server->history, msg, Activity_error, timestamp, HIST_DFL|HIST_SERR); 638 else 639 hist_add(server->history, msg, Activity_status, timestamp, HIST_DFL); 640 641 end: 642 param_free(params); 643 }