| 44 static const char *irc_blist_icon(PurpleAccount *a, PurpleBuddy *b); |
45 static const char *irc_blist_icon(PurpleAccount *a, PurpleBuddy *b); |
| 45 static GList *irc_status_types(PurpleAccount *account); |
46 static GList *irc_status_types(PurpleAccount *account); |
| 46 static GList *irc_get_actions(PurpleConnection *gc); |
47 static GList *irc_get_actions(PurpleConnection *gc); |
| 47 /* static GList *irc_chat_info(PurpleConnection *gc); */ |
48 /* static GList *irc_chat_info(PurpleConnection *gc); */ |
| 48 static void irc_login(PurpleAccount *account); |
49 static void irc_login(PurpleAccount *account); |
| 49 static void irc_login_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond); |
50 static void irc_login_cb(GObject *source, GAsyncResult *res, gpointer user_data); |
| 50 static void irc_login_cb(gpointer data, gint source, const gchar *error_message); |
|
| 51 static void irc_ssl_connect_failure(PurpleSslConnection *gsc, PurpleSslErrorType error, gpointer data); |
|
| 52 static void irc_close(PurpleConnection *gc); |
51 static void irc_close(PurpleConnection *gc); |
| 53 static int irc_im_send(PurpleConnection *gc, PurpleMessage *msg); |
52 static int irc_im_send(PurpleConnection *gc, PurpleMessage *msg); |
| 54 static int irc_chat_send(PurpleConnection *gc, int id, PurpleMessage *msg); |
53 static int irc_chat_send(PurpleConnection *gc, int id, PurpleMessage *msg); |
| 55 static void irc_chat_join (PurpleConnection *gc, GHashTable *data); |
54 static void irc_chat_join (PurpleConnection *gc, GHashTable *data); |
| 56 static void irc_input_cb(gpointer data, gint source, PurpleInputCondition cond); |
55 static void irc_read_input(struct irc_conn *irc); |
| 57 static void irc_input_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond); |
|
| 58 |
56 |
| 59 static guint irc_nick_hash(const char *nick); |
57 static guint irc_nick_hash(const char *nick); |
| 60 static gboolean irc_nick_equal(const char *nick1, const char *nick2); |
58 static gboolean irc_nick_equal(const char *nick1, const char *nick2); |
| 61 static void irc_buddy_free(struct irc_buddy *ib); |
59 static void irc_buddy_free(struct irc_buddy *ib); |
| 62 |
60 |
| 109 irc_send_len(irc, buf, len); |
94 irc_send_len(irc, buf, len); |
| 110 return len; |
95 return len; |
| 111 } |
96 } |
| 112 |
97 |
| 113 static void |
98 static void |
| 114 irc_send_cb(gpointer data, gint source, PurpleInputCondition cond) |
99 irc_flush_cb(GObject *source, GAsyncResult *res, gpointer data) |
| 115 { |
100 { |
| 116 struct irc_conn *irc = data; |
101 PurpleConnection *gc = data; |
| 117 int ret, writelen; |
102 gboolean result; |
| 118 const gchar *buffer = NULL; |
103 GError *error = NULL; |
| 119 |
104 |
| 120 writelen = purple_circular_buffer_get_max_read(irc->outbuf); |
105 result = g_output_stream_flush_finish(G_OUTPUT_STREAM(source), |
| 121 |
106 res, &error); |
| 122 if (writelen == 0) { |
107 |
| 123 purple_input_remove(irc->writeh); |
108 if (!result) { |
| 124 irc->writeh = 0; |
109 purple_connection_g_error(gc, error, |
| 125 return; |
110 _("Lost connection with server: %s")); |
| 126 } |
111 g_clear_error(&error); |
| 127 |
112 return; |
| 128 buffer = purple_circular_buffer_get_output(irc->outbuf); |
113 } |
| 129 |
|
| 130 ret = do_send(irc, buffer, writelen); |
|
| 131 |
|
| 132 if (ret < 0 && errno == EAGAIN) |
|
| 133 return; |
|
| 134 else if (ret <= 0) { |
|
| 135 PurpleConnection *gc = purple_account_get_connection(irc->account); |
|
| 136 gchar *tmp = g_strdup_printf(_("Lost connection with server: %s"), |
|
| 137 g_strerror(errno)); |
|
| 138 purple_connection_error (gc, |
|
| 139 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); |
|
| 140 g_free(tmp); |
|
| 141 return; |
|
| 142 } |
|
| 143 |
|
| 144 purple_circular_buffer_mark_read(irc->outbuf, ret); |
|
| 145 |
|
| 146 #if 0 |
|
| 147 /* We *could* try to write more if we wrote it all */ |
|
| 148 if (ret == write_len) { |
|
| 149 irc_send_cb(data, source, cond); |
|
| 150 } |
|
| 151 #endif |
|
| 152 } |
114 } |
| 153 |
115 |
| 154 int irc_send(struct irc_conn *irc, const char *buf) |
116 int irc_send(struct irc_conn *irc, const char *buf) |
| 155 { |
117 { |
| 156 return irc_send_len(irc, buf, strlen(buf)); |
118 return irc_send_len(irc, buf, strlen(buf)); |
| 157 } |
119 } |
| 158 |
120 |
| 159 int irc_send_len(struct irc_conn *irc, const char *buf, int buflen) |
121 int irc_send_len(struct irc_conn *irc, const char *buf, int buflen) |
| 160 { |
122 { |
| 161 int ret; |
|
| 162 char *tosend= g_strdup(buf); |
123 char *tosend= g_strdup(buf); |
| |
124 GBytes *data; |
| 163 |
125 |
| 164 purple_signal_emit(_irc_protocol, "irc-sending-text", purple_account_get_connection(irc->account), &tosend); |
126 purple_signal_emit(_irc_protocol, "irc-sending-text", purple_account_get_connection(irc->account), &tosend); |
| 165 |
127 |
| 166 if (tosend == NULL) |
128 if (tosend == NULL) |
| 167 return 0; |
129 return 0; |
| 168 |
130 |
| 169 /* If we're not buffering writes, try to send immediately */ |
131 data = g_bytes_new_take(tosend, strlen(tosend)); |
| 170 if (!irc->writeh) |
132 purple_queued_output_stream_push_bytes(irc->output, data); |
| 171 ret = do_send(irc, tosend, buflen); |
133 g_bytes_unref(data); |
| 172 else { |
134 |
| 173 ret = -1; |
135 if (!g_output_stream_has_pending(G_OUTPUT_STREAM(irc->output))) { |
| 174 errno = EAGAIN; |
136 /* Connection idle. Flush data. */ |
| 175 } |
137 g_output_stream_flush_async(G_OUTPUT_STREAM(irc->output), |
| 176 |
138 G_PRIORITY_DEFAULT, irc->cancellable, |
| 177 /* purple_debug(PURPLE_DEBUG_MISC, "irc", "sent%s: %s", |
139 irc_flush_cb, |
| 178 irc->gsc ? " (ssl)" : "", tosend); */ |
140 purple_account_get_connection(irc->account)); |
| 179 if (ret <= 0 && errno != EAGAIN) { |
141 } |
| 180 PurpleConnection *gc = purple_account_get_connection(irc->account); |
142 |
| 181 gchar *tmp = g_strdup_printf(_("Lost connection with server: %s"), |
143 return strlen(tosend); |
| 182 g_strerror(errno)); |
|
| 183 purple_connection_error (gc, |
|
| 184 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); |
|
| 185 g_free(tmp); |
|
| 186 } else if (ret < buflen) { |
|
| 187 if (ret < 0) |
|
| 188 ret = 0; |
|
| 189 if (!irc->writeh) |
|
| 190 irc->writeh = purple_input_add( |
|
| 191 irc->gsc ? irc->gsc->fd : irc->fd, |
|
| 192 PURPLE_INPUT_WRITE, irc_send_cb, irc); |
|
| 193 purple_circular_buffer_append(irc->outbuf, tosend + ret, |
|
| 194 buflen - ret); |
|
| 195 } |
|
| 196 g_free(tosend); |
|
| 197 return ret; |
|
| 198 } |
144 } |
| 199 |
145 |
| 200 /* XXX I don't like messing directly with these buddies */ |
146 /* XXX I don't like messing directly with these buddies */ |
| 201 gboolean irc_blist_timeout(struct irc_conn *irc) |
147 gboolean irc_blist_timeout(struct irc_conn *irc) |
| 202 { |
148 { |
| 367 irc->msgs = g_hash_table_new(g_str_hash, g_str_equal); |
314 irc->msgs = g_hash_table_new(g_str_hash, g_str_equal); |
| 368 irc_msg_table_build(irc); |
315 irc_msg_table_build(irc); |
| 369 |
316 |
| 370 purple_connection_update_progress(gc, _("Connecting"), 1, 2); |
317 purple_connection_update_progress(gc, _("Connecting"), 1, 2); |
| 371 |
318 |
| |
319 if ((resolver = purple_proxy_get_proxy_resolver(account)) == NULL) { |
| |
320 /* Invalid proxy settings */ |
| |
321 purple_connection_error (gc, |
| |
322 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, |
| |
323 _("Unable to connect")); |
| |
324 return; |
| |
325 } |
| |
326 |
| |
327 client = g_socket_client_new(); |
| |
328 g_socket_client_set_proxy_resolver(client, resolver); |
| |
329 g_object_unref(resolver); |
| |
330 |
| |
331 /* Optionally use TLS if it's set in the account settings */ |
| 372 if (purple_account_get_bool(account, "ssl", FALSE)) { |
332 if (purple_account_get_bool(account, "ssl", FALSE)) { |
| 373 irc->gsc = purple_ssl_connect(account, irc->server, |
333 g_socket_client_set_tls(client, TRUE); |
| 374 purple_account_get_int(account, "port", IRC_DEFAULT_SSL_PORT), |
334 purple_tls_certificate_attach_to_socket_client(client); |
| 375 irc_login_cb_ssl, irc_ssl_connect_failure, gc); |
335 } |
| 376 } |
336 |
| 377 |
337 g_socket_client_connect_to_host_async(client, irc->server, |
| 378 if (!irc->gsc) { |
338 purple_account_get_int(account, "port", |
| 379 |
339 g_socket_client_get_tls(client) ? |
| 380 if (purple_proxy_connect(gc, account, irc->server, |
340 IRC_DEFAULT_SSL_PORT : |
| 381 purple_account_get_int(account, "port", IRC_DEFAULT_PORT), |
341 IRC_DEFAULT_PORT), |
| 382 irc_login_cb, gc) == NULL) |
342 irc->cancellable, irc_login_cb, gc); |
| 383 { |
343 g_object_unref(client); |
| 384 purple_connection_error (gc, |
|
| 385 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, |
|
| 386 _("Unable to connect")); |
|
| 387 return; |
|
| 388 } |
|
| 389 } |
|
| 390 } |
344 } |
| 391 |
345 |
| 392 static gboolean do_login(PurpleConnection *gc) { |
346 static gboolean do_login(PurpleConnection *gc) { |
| 393 char *buf, *tmp = NULL; |
347 char *buf, *tmp = NULL; |
| 394 char *server; |
348 char *server; |
| 456 irc->recv_time = time(NULL); |
410 irc->recv_time = time(NULL); |
| 457 |
411 |
| 458 return TRUE; |
412 return TRUE; |
| 459 } |
413 } |
| 460 |
414 |
| 461 static void irc_login_cb_ssl(gpointer data, PurpleSslConnection *gsc, |
415 static void |
| 462 PurpleInputCondition cond) |
416 irc_login_cb(GObject *source, GAsyncResult *res, gpointer user_data) |
| 463 { |
417 { |
| 464 PurpleConnection *gc = data; |
418 PurpleConnection *gc = user_data; |
| |
419 GSocketConnection *conn; |
| |
420 GError *error = NULL; |
| |
421 struct irc_conn *irc; |
| |
422 |
| |
423 conn = g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source), |
| |
424 res, &error); |
| |
425 |
| |
426 if (conn == NULL) { |
| |
427 purple_connection_g_error(gc, error, |
| |
428 _("Unable to connect: %s")); |
| |
429 g_clear_error(&error); |
| |
430 return; |
| |
431 } |
| |
432 |
| |
433 irc = purple_connection_get_protocol_data(gc); |
| |
434 irc->conn = conn; |
| |
435 irc->output = purple_queued_output_stream_new( |
| |
436 g_io_stream_get_output_stream(G_IO_STREAM(irc->conn))); |
| 465 |
437 |
| 466 if (do_login(gc)) { |
438 if (do_login(gc)) { |
| 467 purple_ssl_input_add(gsc, irc_input_cb_ssl, gc); |
439 irc->input = g_data_input_stream_new( |
| 468 } |
440 g_io_stream_get_input_stream( |
| 469 } |
441 G_IO_STREAM(irc->conn))); |
| 470 |
442 irc_read_input(irc); |
| 471 static void irc_login_cb(gpointer data, gint source, const gchar *error_message) |
443 } |
| 472 { |
|
| 473 PurpleConnection *gc = data; |
|
| 474 struct irc_conn *irc = purple_connection_get_protocol_data(gc); |
|
| 475 |
|
| 476 if (source < 0) { |
|
| 477 gchar *tmp = g_strdup_printf(_("Unable to connect: %s"), |
|
| 478 error_message); |
|
| 479 purple_connection_error (gc, |
|
| 480 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); |
|
| 481 g_free(tmp); |
|
| 482 return; |
|
| 483 } |
|
| 484 |
|
| 485 irc->fd = source; |
|
| 486 |
|
| 487 if (do_login(gc)) { |
|
| 488 irc->inpa = purple_input_add(irc->fd, PURPLE_INPUT_READ, irc_input_cb, gc); |
|
| 489 } |
|
| 490 } |
|
| 491 |
|
| 492 static void |
|
| 493 irc_ssl_connect_failure(PurpleSslConnection *gsc, PurpleSslErrorType error, |
|
| 494 gpointer data) |
|
| 495 { |
|
| 496 PurpleConnection *gc = data; |
|
| 497 struct irc_conn *irc = purple_connection_get_protocol_data(gc); |
|
| 498 |
|
| 499 irc->gsc = NULL; |
|
| 500 |
|
| 501 purple_connection_ssl_error (gc, error); |
|
| 502 } |
444 } |
| 503 |
445 |
| 504 static void irc_close(PurpleConnection *gc) |
446 static void irc_close(PurpleConnection *gc) |
| 505 { |
447 { |
| 506 struct irc_conn *irc = purple_connection_get_protocol_data(gc); |
448 struct irc_conn *irc = purple_connection_get_protocol_data(gc); |
| 507 |
449 |
| 508 if (irc == NULL) |
450 if (irc == NULL) |
| 509 return; |
451 return; |
| 510 |
452 |
| 511 if (irc->gsc || (irc->fd >= 0)) |
453 if (irc->conn != NULL) |
| 512 irc_cmd_quit(irc, "quit", NULL, NULL); |
454 irc_cmd_quit(irc, "quit", NULL, NULL); |
| 513 |
455 |
| 514 if (irc->inpa) { |
456 if (irc->cancellable != NULL) { |
| 515 purple_input_remove(irc->inpa); |
457 g_cancellable_cancel(irc->cancellable); |
| 516 irc->inpa = 0; |
458 g_clear_object(&irc->cancellable); |
| 517 } |
459 } |
| 518 |
460 |
| 519 g_free(irc->inbuf); |
461 g_clear_object(&irc->input); |
| 520 if (irc->gsc) { |
462 g_clear_object(&irc->output); |
| 521 purple_ssl_close(irc->gsc); |
463 |
| 522 } else if (irc->fd >= 0) { |
464 if (irc->conn != NULL) { |
| 523 close(irc->fd); |
465 GError *error = NULL; |
| |
466 |
| |
467 if (!g_io_stream_close(G_IO_STREAM(irc->conn), NULL, &error)) { |
| |
468 purple_debug_warning("irc", |
| |
469 "Error closing connection: %s", |
| |
470 error->message); |
| |
471 g_clear_error(&error); |
| |
472 } |
| |
473 |
| |
474 g_clear_object(&irc->conn); |
| 524 } |
475 } |
| 525 if (irc->timer) |
476 if (irc->timer) |
| 526 purple_timeout_remove(irc->timer); |
477 purple_timeout_remove(irc->timer); |
| 527 g_hash_table_destroy(irc->cmds); |
478 g_hash_table_destroy(irc->cmds); |
| 528 g_hash_table_destroy(irc->msgs); |
479 g_hash_table_destroy(irc->msgs); |
| 529 g_hash_table_destroy(irc->buddies); |
480 g_hash_table_destroy(irc->buddies); |
| 530 if (irc->motd) |
481 if (irc->motd) |
| 531 g_string_free(irc->motd, TRUE); |
482 g_string_free(irc->motd, TRUE); |
| 532 g_free(irc->server); |
483 g_free(irc->server); |
| 533 |
|
| 534 if (irc->writeh) |
|
| 535 purple_input_remove(irc->writeh); |
|
| 536 |
|
| 537 g_object_unref(G_OBJECT(irc->outbuf)); |
|
| 538 |
484 |
| 539 g_free(irc->mode_chars); |
485 g_free(irc->mode_chars); |
| 540 g_free(irc->reqnick); |
486 g_free(irc->reqnick); |
| 541 |
487 |
| 542 #ifdef HAVE_CYRUS_SASL |
488 #ifdef HAVE_CYRUS_SASL |
| 638 if (ib && --ib->ref == 0) { |
584 if (ib && --ib->ref == 0) { |
| 639 g_hash_table_remove(irc->buddies, purple_buddy_get_name(buddy)); |
585 g_hash_table_remove(irc->buddies, purple_buddy_get_name(buddy)); |
| 640 } |
586 } |
| 641 } |
587 } |
| 642 |
588 |
| 643 static void read_input(struct irc_conn *irc, int len) |
589 static void |
| 644 { |
590 irc_read_input_cb(GObject *source, GAsyncResult *res, gpointer data) |
| 645 PurpleConnection *connection = purple_account_get_connection(irc->account); |
591 { |
| 646 char *cur, *end; |
|
| 647 |
|
| 648 purple_connection_update_last_received(connection); |
|
| 649 irc->inbufused += len; |
|
| 650 irc->inbuf[irc->inbufused] = '\0'; |
|
| 651 |
|
| 652 cur = irc->inbuf; |
|
| 653 |
|
| 654 /* This is a hack to work around the fact that marv gets messages |
|
| 655 * with null bytes in them while using some weird irc server at work |
|
| 656 */ |
|
| 657 while ((cur < (irc->inbuf + irc->inbufused)) && !*cur) |
|
| 658 cur++; |
|
| 659 |
|
| 660 while (cur < irc->inbuf + irc->inbufused && |
|
| 661 ((end = strstr(cur, "\r\n")) || (end = strstr(cur, "\n")))) { |
|
| 662 int step = (*end == '\r' ? 2 : 1); |
|
| 663 *end = '\0'; |
|
| 664 irc_parse_msg(irc, cur); |
|
| 665 cur = end + step; |
|
| 666 } |
|
| 667 if (cur != irc->inbuf + irc->inbufused) { /* leftover */ |
|
| 668 irc->inbufused -= (cur - irc->inbuf); |
|
| 669 memmove(irc->inbuf, cur, irc->inbufused); |
|
| 670 } else { |
|
| 671 irc->inbufused = 0; |
|
| 672 } |
|
| 673 } |
|
| 674 |
|
| 675 static void irc_input_cb_ssl(gpointer data, PurpleSslConnection *gsc, |
|
| 676 PurpleInputCondition cond) |
|
| 677 { |
|
| 678 |
|
| 679 PurpleConnection *gc = data; |
592 PurpleConnection *gc = data; |
| 680 struct irc_conn *irc = purple_connection_get_protocol_data(gc); |
593 struct irc_conn *irc; |
| 681 int len; |
594 gchar *line; |
| 682 |
595 gsize len; |
| 683 if(!g_list_find(purple_connections_get_all(), gc)) { |
596 gsize start = 0; |
| 684 purple_ssl_close(gsc); |
597 GError *error = NULL; |
| 685 return; |
598 |
| 686 } |
599 line = g_data_input_stream_read_line_finish( |
| 687 |
600 G_DATA_INPUT_STREAM(source), res, &len, &error); |
| 688 if (irc->inbuflen < irc->inbufused + IRC_INITIAL_BUFSIZE) { |
601 |
| 689 irc->inbuflen += IRC_INITIAL_BUFSIZE; |
602 if (line == NULL && error != NULL) { |
| 690 irc->inbuf = g_realloc(irc->inbuf, irc->inbuflen); |
603 purple_connection_g_error(gc, error, |
| 691 } |
604 _("Lost connection with server: %s")); |
| 692 |
605 g_clear_error(&error); |
| 693 len = purple_ssl_read(gsc, irc->inbuf + irc->inbufused, IRC_INITIAL_BUFSIZE - 1); |
606 return; |
| 694 |
607 } else if (line == NULL) { |
| 695 if (len < 0 && errno == EAGAIN) { |
|
| 696 /* Try again later */ |
|
| 697 return; |
|
| 698 } else if (len < 0) { |
|
| 699 gchar *tmp = g_strdup_printf(_("Lost connection with server: %s"), |
|
| 700 g_strerror(errno)); |
|
| 701 purple_connection_error (gc, |
|
| 702 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); |
|
| 703 g_free(tmp); |
|
| 704 return; |
|
| 705 } else if (len == 0) { |
|
| 706 purple_connection_error (gc, |
608 purple_connection_error (gc, |
| 707 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, |
609 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, |
| 708 _("Server closed the connection")); |
610 _("Server closed the connection")); |
| 709 return; |
611 return; |
| 710 } |
612 } |
| 711 |
613 |
| 712 read_input(irc, len); |
614 irc = purple_connection_get_protocol_data(gc); |
| 713 } |
615 |
| 714 |
616 purple_connection_update_last_received(gc); |
| 715 static void irc_input_cb(gpointer data, gint source, PurpleInputCondition cond) |
617 |
| 716 { |
618 if (len > 0 && line[len - 1] == '\r') |
| 717 PurpleConnection *gc = data; |
619 line[len - 1] = '\0'; |
| 718 struct irc_conn *irc = purple_connection_get_protocol_data(gc); |
620 |
| 719 int len; |
621 /* This is a hack to work around the fact that marv gets messages |
| 720 |
622 * with null bytes in them while using some weird irc server at work |
| 721 if (irc->inbuflen < irc->inbufused + IRC_INITIAL_BUFSIZE) { |
623 */ |
| 722 irc->inbuflen += IRC_INITIAL_BUFSIZE; |
624 while (start < len && line[start] == '\0') |
| 723 irc->inbuf = g_realloc(irc->inbuf, irc->inbuflen); |
625 ++start; |
| 724 } |
626 |
| 725 |
627 if (len - start > 0) |
| 726 len = read(irc->fd, irc->inbuf + irc->inbufused, IRC_INITIAL_BUFSIZE - 1); |
628 irc_parse_msg(irc, line + start); |
| 727 if (len < 0 && errno == EAGAIN) { |
629 |
| 728 return; |
630 g_free(line); |
| 729 } else if (len < 0) { |
631 |
| 730 gchar *tmp = g_strdup_printf(_("Lost connection with server: %s"), |
632 irc_read_input(irc); |
| 731 g_strerror(errno)); |
633 } |
| 732 purple_connection_error (gc, |
634 |
| 733 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); |
635 static void |
| 734 g_free(tmp); |
636 irc_read_input(struct irc_conn *irc) |
| 735 return; |
637 { |
| 736 } else if (len == 0) { |
638 PurpleConnection *gc = purple_account_get_connection(irc->account); |
| 737 purple_connection_error (gc, |
639 |
| 738 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, |
640 g_data_input_stream_read_line_async(irc->input, |
| 739 _("Server closed the connection")); |
641 G_PRIORITY_DEFAULT, irc->cancellable, |
| 740 return; |
642 irc_read_input_cb, gc); |
| 741 } |
|
| 742 |
|
| 743 read_input(irc, len); |
|
| 744 } |
643 } |
| 745 |
644 |
| 746 static void irc_chat_join (PurpleConnection *gc, GHashTable *data) |
645 static void irc_chat_join (PurpleConnection *gc, GHashTable *data) |
| 747 { |
646 { |
| 748 struct irc_conn *irc = purple_connection_get_protocol_data(gc); |
647 struct irc_conn *irc = purple_connection_get_protocol_data(gc); |