6 * Copyright (C) 2003, Robbert Haarman <purple@inglorion.net>
7 * Copyright (C) 2003, Ethan Blanton <eblanton@cs.purdue.edu>
8 * Copyright (C) 2000-2003, Rob Flynn <rob@tgflinux.com>
9 * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
28 #include "accountopt.h"
30 #include "conversation.h"
40 #define PING_TIMEOUT 60
42 static void irc_ison_buddy_init(char *name
, struct irc_buddy
*ib
, GList
**list
);
44 static void irc_who_channel(PurpleConversation
*conv
, struct irc_conn
*irc
);
46 static const char *irc_blist_icon(PurpleAccount
*a
, PurpleBuddy
*b
);
47 static GList
*irc_status_types(PurpleAccount
*account
);
48 static GList
*irc_actions(PurplePlugin
*plugin
, gpointer context
);
49 /* static GList *irc_chat_info(PurpleConnection *gc); */
50 static void irc_login(PurpleAccount
*account
);
51 static void irc_login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
);
52 static void irc_login_cb(gpointer data
, gint source
, const gchar
*error_message
);
53 static void irc_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
, gpointer data
);
54 static void irc_close(PurpleConnection
*gc
);
55 static int irc_im_send(PurpleConnection
*gc
, const char *who
, const char *what
, PurpleMessageFlags flags
);
56 static int irc_chat_send(PurpleConnection
*gc
, int id
, const char *what
, PurpleMessageFlags flags
);
57 static void irc_chat_join (PurpleConnection
*gc
, GHashTable
*data
);
58 static void irc_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
);
59 static void irc_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
);
61 static guint
irc_nick_hash(const char *nick
);
62 static gboolean
irc_nick_equal(const char *nick1
, const char *nick2
);
63 static void irc_buddy_free(struct irc_buddy
*ib
);
65 PurplePlugin
*_irc_plugin
= NULL
;
67 static void irc_view_motd(PurplePluginAction
*action
)
69 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
73 if (gc
== NULL
|| gc
->proto_data
== NULL
) {
74 purple_debug(PURPLE_DEBUG_ERROR
, "irc", "got MOTD request for NULL gc\n");
78 if (irc
->motd
== NULL
) {
79 purple_notify_error(gc
, _("Error displaying MOTD"), _("No MOTD available"),
80 _("There is no MOTD associated with this connection."));
83 title
= g_strdup_printf(_("MOTD for %s"), irc
->server
);
84 purple_notify_formatted(gc
, title
, title
, NULL
, irc
->motd
->str
, NULL
, NULL
);
88 static int do_send(struct irc_conn
*irc
, const char *buf
, gsize len
)
93 ret
= purple_ssl_write(irc
->gsc
, buf
, len
);
95 ret
= write(irc
->fd
, buf
, len
);
101 static int irc_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
103 struct irc_conn
*irc
= (struct irc_conn
*)gc
->proto_data
;
104 return do_send(irc
, buf
, len
);
108 irc_send_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
110 struct irc_conn
*irc
= data
;
113 writelen
= purple_circ_buffer_get_max_read(irc
->outbuf
);
116 purple_input_remove(irc
->writeh
);
121 ret
= do_send(irc
, irc
->outbuf
->outptr
, writelen
);
123 if (ret
< 0 && errno
== EAGAIN
)
126 PurpleConnection
*gc
= purple_account_get_connection(irc
->account
);
127 gchar
*tmp
= g_strdup_printf(_("Lost connection with server: %s"),
129 purple_connection_error_reason (gc
,
130 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
, tmp
);
135 purple_circ_buffer_mark_read(irc
->outbuf
, ret
);
138 /* We *could* try to write more if we wrote it all */
139 if (ret
== write_len
) {
140 irc_send_cb(data
, source
, cond
);
145 int irc_send(struct irc_conn
*irc
, const char *buf
)
148 char *tosend
= g_strdup(buf
);
150 purple_signal_emit(_irc_plugin
, "irc-sending-text", purple_account_get_connection(irc
->account
), &tosend
);
154 buflen
= strlen(tosend
);
157 /* If we're not buffering writes, try to send immediately */
159 ret
= do_send(irc
, tosend
, buflen
);
165 /* purple_debug(PURPLE_DEBUG_MISC, "irc", "sent%s: %s",
166 irc->gsc ? " (ssl)" : "", tosend); */
167 if (ret
<= 0 && errno
!= EAGAIN
) {
168 PurpleConnection
*gc
= purple_account_get_connection(irc
->account
);
169 gchar
*tmp
= g_strdup_printf(_("Lost connection with server: %s"),
171 purple_connection_error_reason (gc
,
172 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
, tmp
);
174 } else if (ret
< buflen
) {
178 irc
->writeh
= purple_input_add(
179 irc
->gsc
? irc
->gsc
->fd
: irc
->fd
,
180 PURPLE_INPUT_WRITE
, irc_send_cb
, irc
);
181 purple_circ_buffer_append(irc
->outbuf
, tosend
+ ret
,
188 /* XXX I don't like messing directly with these buddies */
189 gboolean
irc_blist_timeout(struct irc_conn
*irc
)
191 if (irc
->ison_outstanding
) {
195 g_hash_table_foreach(irc
->buddies
, (GHFunc
)irc_ison_buddy_init
,
196 (gpointer
*)&irc
->buddies_outstanding
);
198 irc_buddy_query(irc
);
203 void irc_buddy_query(struct irc_conn
*irc
)
207 struct irc_buddy
*ib
;
210 string
= g_string_sized_new(512);
212 while ((lp
= g_list_first(irc
->buddies_outstanding
))) {
213 ib
= (struct irc_buddy
*)lp
->data
;
214 if (string
->len
+ strlen(ib
->name
) + 1 > 450)
216 g_string_append_printf(string
, "%s ", ib
->name
);
217 ib
->new_online_status
= FALSE
;
218 irc
->buddies_outstanding
= g_list_remove_link(irc
->buddies_outstanding
, lp
);
222 buf
= irc_format(irc
, "vn", "ISON", string
->str
);
225 irc
->ison_outstanding
= TRUE
;
227 irc
->ison_outstanding
= FALSE
;
229 g_string_free(string
, TRUE
);
232 static void irc_ison_buddy_init(char *name
, struct irc_buddy
*ib
, GList
**list
)
234 *list
= g_list_append(*list
, ib
);
238 gboolean
irc_who_channel_timeout(struct irc_conn
*irc
)
240 // WHO all of our channels.
241 g_list_foreach(purple_get_conversations(), (GFunc
)irc_who_channel
, (gpointer
)irc
);
246 static void irc_who_channel(PurpleConversation
*conv
, struct irc_conn
*irc
)
248 if (purple_conversation_get_account(conv
) == irc
->account
&& purple_conversation_get_type(conv
) == PURPLE_CONV_TYPE_CHAT
) {
249 char *buf
= irc_format(irc
, "vc", "WHO", purple_conversation_get_name(conv
));
251 purple_debug(PURPLE_DEBUG_INFO
, "irc", "Performing periodic who on %s", purple_conversation_get_name(conv
));
257 static void irc_ison_one(struct irc_conn
*irc
, struct irc_buddy
*ib
)
261 if (irc
->buddies_outstanding
!= NULL
) {
262 irc
->buddies_outstanding
= g_list_append(irc
->buddies_outstanding
, ib
);
266 ib
->new_online_status
= FALSE
;
267 buf
= irc_format(irc
, "vn", "ISON", ib
->name
);
273 static const char *irc_blist_icon(PurpleAccount
*a
, PurpleBuddy
*b
)
278 static GList
*irc_status_types(PurpleAccount
*account
)
280 PurpleStatusType
*type
;
283 type
= purple_status_type_new(PURPLE_STATUS_AVAILABLE
, NULL
, NULL
, TRUE
);
284 types
= g_list_append(types
, type
);
286 type
= purple_status_type_new_with_attrs(
287 PURPLE_STATUS_AWAY
, NULL
, NULL
, TRUE
, TRUE
, FALSE
,
288 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
290 types
= g_list_append(types
, type
);
292 type
= purple_status_type_new(PURPLE_STATUS_OFFLINE
, NULL
, NULL
, TRUE
);
293 types
= g_list_append(types
, type
);
298 static GList
*irc_actions(PurplePlugin
*plugin
, gpointer context
)
301 PurplePluginAction
*act
= NULL
;
303 act
= purple_plugin_action_new(_("View MOTD"), irc_view_motd
);
304 list
= g_list_append(list
, act
);
309 static GList
*irc_chat_join_info(PurpleConnection
*gc
)
312 struct proto_chat_entry
*pce
;
314 pce
= g_new0(struct proto_chat_entry
, 1);
315 pce
->label
= _("_Channel:");
316 pce
->identifier
= "channel";
317 pce
->required
= TRUE
;
318 m
= g_list_append(m
, pce
);
320 pce
= g_new0(struct proto_chat_entry
, 1);
321 pce
->label
= _("_Password:");
322 pce
->identifier
= "password";
324 m
= g_list_append(m
, pce
);
329 static GHashTable
*irc_chat_info_defaults(PurpleConnection
*gc
, const char *chat_name
)
331 GHashTable
*defaults
;
333 defaults
= g_hash_table_new_full(g_str_hash
, g_str_equal
, NULL
, g_free
);
335 if (chat_name
!= NULL
)
336 g_hash_table_insert(defaults
, "channel", g_strdup(chat_name
));
341 static void irc_login(PurpleAccount
*account
)
343 PurpleConnection
*gc
;
344 struct irc_conn
*irc
;
346 const char *username
= purple_account_get_username(account
);
348 gc
= purple_account_get_connection(account
);
349 gc
->flags
|= PURPLE_CONNECTION_NO_NEWLINES
;
351 if (strpbrk(username
, " \t\v\r\n") != NULL
) {
352 purple_connection_error_reason (gc
,
353 PURPLE_CONNECTION_ERROR_INVALID_SETTINGS
,
354 _("IRC nick and server may not contain whitespace"));
358 gc
->proto_data
= irc
= g_new0(struct irc_conn
, 1);
360 irc
->account
= account
;
361 irc
->outbuf
= purple_circ_buffer_new(512);
363 userparts
= g_strsplit(username
, "@", 2);
364 purple_connection_set_display_name(gc
, userparts
[0]);
365 irc
->server
= g_strdup(userparts
[1]);
366 g_strfreev(userparts
);
368 irc
->buddies
= g_hash_table_new_full((GHashFunc
)irc_nick_hash
, (GEqualFunc
)irc_nick_equal
,
369 NULL
, (GDestroyNotify
)irc_buddy_free
);
370 irc
->cmds
= g_hash_table_new(g_str_hash
, g_str_equal
);
371 irc_cmd_table_build(irc
);
372 irc
->msgs
= g_hash_table_new(g_str_hash
, g_str_equal
);
373 irc_msg_table_build(irc
);
375 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
377 if (purple_account_get_bool(account
, "ssl", FALSE
)) {
378 if (purple_ssl_is_supported()) {
379 irc
->gsc
= purple_ssl_connect(account
, irc
->server
,
380 purple_account_get_int(account
, "port", IRC_DEFAULT_SSL_PORT
),
381 irc_login_cb_ssl
, irc_ssl_connect_failure
, gc
);
383 purple_connection_error_reason (gc
,
384 PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT
,
385 _("SSL support unavailable"));
392 if (purple_proxy_connect(gc
, account
, irc
->server
,
393 purple_account_get_int(account
, "port", IRC_DEFAULT_PORT
),
394 irc_login_cb
, gc
) == NULL
)
396 purple_connection_error_reason (gc
,
397 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
398 _("Unable to connect"));
404 static gboolean
do_login(PurpleConnection
*gc
) {
405 char *buf
, *tmp
= NULL
;
407 const char *username
, *realname
;
408 struct irc_conn
*irc
= gc
->proto_data
;
409 const char *pass
= purple_connection_get_password(gc
);
412 buf
= irc_format(irc
, "v:", "PASS", pass
);
413 if (irc_send(irc
, buf
) < 0) {
420 realname
= purple_account_get_string(irc
->account
, "realname", "");
421 username
= purple_account_get_string(irc
->account
, "username", "");
423 if (username
== NULL
|| *username
== '\0') {
424 username
= g_get_user_name();
427 if (username
!= NULL
&& strchr(username
, ' ') != NULL
) {
428 tmp
= g_strdup(username
);
429 while ((buf
= strchr(tmp
, ' ')) != NULL
) {
434 if (*irc
->server
== ':') {
435 /* Same as hostname, above. */
436 server
= g_strdup_printf("0%s", irc
->server
);
438 server
= g_strdup(irc
->server
);
441 buf
= irc_format(irc
, "vvvv:", "USER", tmp
? tmp
: username
, "*", server
,
442 strlen(realname
) ? realname
: IRC_DEFAULT_ALIAS
);
445 if (irc_send(irc
, buf
) < 0) {
450 username
= purple_connection_get_display_name(gc
);
451 buf
= irc_format(irc
, "vn", "NICK", username
);
452 irc
->reqnick
= g_strdup(username
);
453 irc
->nickused
= FALSE
;
454 if (irc_send(irc
, buf
) < 0) {
460 irc
->recv_time
= time(NULL
);
465 static void irc_login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
466 PurpleInputCondition cond
)
468 PurpleConnection
*gc
= data
;
471 purple_ssl_input_add(gsc
, irc_input_cb_ssl
, gc
);
475 static void irc_login_cb(gpointer data
, gint source
, const gchar
*error_message
)
477 PurpleConnection
*gc
= data
;
478 struct irc_conn
*irc
= gc
->proto_data
;
481 gchar
*tmp
= g_strdup_printf(_("Unable to connect: %s"),
483 purple_connection_error_reason (gc
,
484 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
, tmp
);
492 gc
->inpa
= purple_input_add(irc
->fd
, PURPLE_INPUT_READ
, irc_input_cb
, gc
);
497 irc_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
500 PurpleConnection
*gc
= data
;
501 struct irc_conn
*irc
= gc
->proto_data
;
505 purple_connection_ssl_error (gc
, error
);
508 static void irc_close(PurpleConnection
*gc
)
510 struct irc_conn
*irc
= gc
->proto_data
;
515 if (irc
->gsc
|| (irc
->fd
>= 0))
516 irc_cmd_quit(irc
, "quit", NULL
, NULL
);
519 purple_input_remove(gc
->inpa
);
523 purple_ssl_close(irc
->gsc
);
524 } else if (irc
->fd
>= 0) {
528 purple_timeout_remove(irc
->timer
);
529 if (irc
->who_channel_timer
)
530 purple_timeout_remove(irc
->who_channel_timer
);
531 g_hash_table_destroy(irc
->cmds
);
532 g_hash_table_destroy(irc
->msgs
);
533 g_hash_table_destroy(irc
->buddies
);
535 g_string_free(irc
->motd
, TRUE
);
539 purple_input_remove(irc
->writeh
);
541 purple_circ_buffer_destroy(irc
->outbuf
);
543 g_free(irc
->mode_chars
);
544 g_free(irc
->reqnick
);
549 static int irc_im_send(PurpleConnection
*gc
, const char *who
, const char *what
, PurpleMessageFlags flags
)
551 struct irc_conn
*irc
= gc
->proto_data
;
555 args
[0] = irc_nick_skip_mode(irc
, who
);
557 purple_markup_html_to_xhtml(what
, NULL
, &plain
);
560 irc_cmd_privmsg(irc
, "msg", NULL
, args
);
565 static void irc_get_info(PurpleConnection
*gc
, const char *who
)
567 struct irc_conn
*irc
= gc
->proto_data
;
571 irc_cmd_whois(irc
, "whois", NULL
, args
);
574 static void irc_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
576 PurpleConnection
*gc
= purple_account_get_connection(account
);
577 struct irc_conn
*irc
;
579 const char *status_id
= purple_status_get_id(status
);
581 g_return_if_fail(gc
!= NULL
);
582 irc
= gc
->proto_data
;
584 if (!purple_status_is_active(status
))
589 if (!strcmp(status_id
, "away")) {
590 args
[0] = purple_status_get_attr_string(status
, "message");
591 if ((args
[0] == NULL
) || (*args
[0] == '\0'))
593 irc_cmd_away(irc
, "away", NULL
, args
);
594 } else if (!strcmp(status_id
, "available")) {
595 irc_cmd_away(irc
, "back", NULL
, args
);
599 static void irc_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
601 struct irc_conn
*irc
= (struct irc_conn
*)gc
->proto_data
;
602 struct irc_buddy
*ib
;
603 const char *bname
= purple_buddy_get_name(buddy
);
605 ib
= g_hash_table_lookup(irc
->buddies
, bname
);
608 purple_prpl_got_user_status(irc
->account
, bname
,
609 ib
->online
? "available" : "offline", NULL
);
611 ib
= g_new0(struct irc_buddy
, 1);
612 ib
->name
= g_strdup(bname
);
614 g_hash_table_replace(irc
->buddies
, ib
->name
, ib
);
617 /* if the timer isn't set, this is during signon, so we don't want to flood
618 * ourself off with ISON's, so we don't, but after that we want to know when
619 * someone's online asap */
621 irc_ison_one(irc
, ib
);
624 static void irc_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
626 struct irc_conn
*irc
= (struct irc_conn
*)gc
->proto_data
;
627 struct irc_buddy
*ib
;
629 ib
= g_hash_table_lookup(irc
->buddies
, purple_buddy_get_name(buddy
));
630 if (ib
&& --ib
->ref
== 0) {
631 g_hash_table_remove(irc
->buddies
, purple_buddy_get_name(buddy
));
635 static void read_input(struct irc_conn
*irc
, int len
)
639 irc
->account
->gc
->last_received
= time(NULL
);
640 irc
->inbufused
+= len
;
641 irc
->inbuf
[irc
->inbufused
] = '\0';
645 /* This is a hack to work around the fact that marv gets messages
646 * with null bytes in them while using some weird irc server at work
648 while ((cur
< (irc
->inbuf
+ irc
->inbufused
)) && !*cur
)
651 while (cur
< irc
->inbuf
+ irc
->inbufused
&&
652 ((end
= strstr(cur
, "\r\n")) || (end
= strstr(cur
, "\n")))) {
653 int step
= (*end
== '\r' ? 2 : 1);
655 irc_parse_msg(irc
, cur
);
658 if (cur
!= irc
->inbuf
+ irc
->inbufused
) { /* leftover */
659 irc
->inbufused
-= (cur
- irc
->inbuf
);
660 memmove(irc
->inbuf
, cur
, irc
->inbufused
);
666 static void irc_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
667 PurpleInputCondition cond
)
670 PurpleConnection
*gc
= data
;
671 struct irc_conn
*irc
= gc
->proto_data
;
674 if(!g_list_find(purple_connections_get_all(), gc
)) {
675 purple_ssl_close(gsc
);
679 if (irc
->inbuflen
< irc
->inbufused
+ IRC_INITIAL_BUFSIZE
) {
680 irc
->inbuflen
+= IRC_INITIAL_BUFSIZE
;
681 irc
->inbuf
= g_realloc(irc
->inbuf
, irc
->inbuflen
);
684 len
= purple_ssl_read(gsc
, irc
->inbuf
+ irc
->inbufused
, IRC_INITIAL_BUFSIZE
- 1);
686 if (len
< 0 && errno
== EAGAIN
) {
687 /* Try again later */
689 } else if (len
< 0) {
690 gchar
*tmp
= g_strdup_printf(_("Lost connection with server: %s"),
692 purple_connection_error_reason (gc
,
693 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
, tmp
);
696 } else if (len
== 0) {
697 purple_connection_error_reason (gc
,
698 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
699 _("Server closed the connection"));
703 read_input(irc
, len
);
706 static void irc_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
708 PurpleConnection
*gc
= data
;
709 struct irc_conn
*irc
= gc
->proto_data
;
712 if (irc
->inbuflen
< irc
->inbufused
+ IRC_INITIAL_BUFSIZE
) {
713 irc
->inbuflen
+= IRC_INITIAL_BUFSIZE
;
714 irc
->inbuf
= g_realloc(irc
->inbuf
, irc
->inbuflen
);
717 len
= read(irc
->fd
, irc
->inbuf
+ irc
->inbufused
, IRC_INITIAL_BUFSIZE
- 1);
718 if (len
< 0 && errno
== EAGAIN
) {
720 } else if (len
< 0) {
721 gchar
*tmp
= g_strdup_printf(_("Lost connection with server: %s"),
723 purple_connection_error_reason (gc
,
724 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
, tmp
);
727 } else if (len
== 0) {
728 purple_connection_error_reason (gc
,
729 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
730 _("Server closed the connection"));
734 read_input(irc
, len
);
737 static void irc_chat_join (PurpleConnection
*gc
, GHashTable
*data
)
739 struct irc_conn
*irc
= gc
->proto_data
;
742 args
[0] = g_hash_table_lookup(data
, "channel");
743 args
[1] = g_hash_table_lookup(data
, "password");
744 irc_cmd_join(irc
, "join", NULL
, args
);
747 static char *irc_get_chat_name(GHashTable
*data
) {
748 return g_strdup(g_hash_table_lookup(data
, "channel"));
751 static void irc_chat_invite(PurpleConnection
*gc
, int id
, const char *message
, const char *name
)
753 struct irc_conn
*irc
= gc
->proto_data
;
754 PurpleConversation
*convo
= purple_find_chat(gc
, id
);
758 purple_debug(PURPLE_DEBUG_ERROR
, "irc", "Got chat invite request for bogus chat\n");
762 args
[1] = purple_conversation_get_name(convo
);
763 irc_cmd_invite(irc
, "invite", purple_conversation_get_name(convo
), args
);
767 static void irc_chat_leave (PurpleConnection
*gc
, int id
)
769 struct irc_conn
*irc
= gc
->proto_data
;
770 PurpleConversation
*convo
= purple_find_chat(gc
, id
);
776 args
[0] = purple_conversation_get_name(convo
);
778 irc_cmd_part(irc
, "part", purple_conversation_get_name(convo
), args
);
779 serv_got_chat_left(gc
, id
);
782 static int irc_chat_send(PurpleConnection
*gc
, int id
, const char *what
, PurpleMessageFlags flags
)
784 struct irc_conn
*irc
= gc
->proto_data
;
785 PurpleConversation
*convo
= purple_find_chat(gc
, id
);
790 purple_debug(PURPLE_DEBUG_ERROR
, "irc", "chat send on nonexistent chat\n");
795 return irc_parse_cmd(irc
, convo
->name
, what
+ 1);
798 purple_markup_html_to_xhtml(what
, NULL
, &tmp
);
799 args
[0] = convo
->name
;
802 irc_cmd_privmsg(irc
, "msg", NULL
, args
);
804 serv_got_chat_in(gc
, id
, purple_connection_get_display_name(gc
), flags
, what
, time(NULL
));
809 static guint
irc_nick_hash(const char *nick
)
814 lc
= g_utf8_strdown(nick
, -1);
815 bucket
= g_str_hash(lc
);
821 static gboolean
irc_nick_equal(const char *nick1
, const char *nick2
)
823 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
826 static void irc_buddy_free(struct irc_buddy
*ib
)
832 static void irc_chat_set_topic(PurpleConnection
*gc
, int id
, const char *topic
)
835 const char *name
= NULL
;
836 struct irc_conn
*irc
;
838 irc
= gc
->proto_data
;
839 name
= purple_conversation_get_name(purple_find_chat(gc
, id
));
844 buf
= irc_format(irc
, "vt:", "TOPIC", name
, topic
);
849 static PurpleRoomlist
*irc_roomlist_get_list(PurpleConnection
*gc
)
851 struct irc_conn
*irc
;
852 GList
*fields
= NULL
;
853 PurpleRoomlistField
*f
;
856 irc
= gc
->proto_data
;
859 purple_roomlist_unref(irc
->roomlist
);
861 irc
->roomlist
= purple_roomlist_new(purple_connection_get_account(gc
));
863 f
= purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING
, "", "channel", TRUE
);
864 fields
= g_list_append(fields
, f
);
866 f
= purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_INT
, _("Users"), "users", FALSE
);
867 fields
= g_list_append(fields
, f
);
869 f
= purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING
, _("Topic"), "topic", FALSE
);
870 fields
= g_list_append(fields
, f
);
872 purple_roomlist_set_fields(irc
->roomlist
, fields
);
874 buf
= irc_format(irc
, "v", "LIST");
878 return irc
->roomlist
;
881 static void irc_roomlist_cancel(PurpleRoomlist
*list
)
883 PurpleConnection
*gc
= purple_account_get_connection(list
->account
);
884 struct irc_conn
*irc
;
889 irc
= gc
->proto_data
;
891 purple_roomlist_set_in_progress(list
, FALSE
);
893 if (irc
->roomlist
== list
) {
894 irc
->roomlist
= NULL
;
895 purple_roomlist_unref(list
);
899 static void irc_keepalive(PurpleConnection
*gc
)
901 struct irc_conn
*irc
= gc
->proto_data
;
902 if ((time(NULL
) - irc
->recv_time
) > PING_TIMEOUT
)
903 irc_cmd_ping(irc
, NULL
, NULL
, NULL
);
906 static PurplePluginProtocolInfo prpl_info
=
908 OPT_PROTO_CHAT_TOPIC
| OPT_PROTO_PASSWORD_OPTIONAL
|
909 OPT_PROTO_SLASH_COMMANDS_NATIVE
,
910 NULL
, /* user_splits */
911 NULL
, /* protocol_options */
912 NO_BUDDY_ICONS
, /* icon_spec */
913 irc_blist_icon
, /* list_icon */
914 NULL
, /* list_emblems */
915 NULL
, /* status_text */
916 NULL
, /* tooltip_text */
917 irc_status_types
, /* away_states */
918 NULL
, /* blist_node_menu */
919 irc_chat_join_info
, /* chat_info */
920 irc_chat_info_defaults
, /* chat_info_defaults */
921 irc_login
, /* login */
922 irc_close
, /* close */
923 irc_im_send
, /* send_im */
925 NULL
, /* send_typing */
926 irc_get_info
, /* get_info */
927 irc_set_status
, /* set_status */
929 NULL
, /* change_passwd */
930 irc_add_buddy
, /* add_buddy */
931 NULL
, /* add_buddies */
932 irc_remove_buddy
, /* remove_buddy */
933 NULL
, /* remove_buddies */
934 NULL
, /* add_permit */
936 NULL
, /* rem_permit */
938 NULL
, /* set_permit_deny */
939 irc_chat_join
, /* join_chat */
940 NULL
, /* reject_chat */
941 irc_get_chat_name
, /* get_chat_name */
942 irc_chat_invite
, /* chat_invite */
943 irc_chat_leave
, /* chat_leave */
944 NULL
, /* chat_whisper */
945 irc_chat_send
, /* chat_send */
946 irc_keepalive
, /* keepalive */
947 NULL
, /* register_user */
948 NULL
, /* get_cb_info */
949 NULL
, /* get_cb_away */
950 NULL
, /* alias_buddy */
951 NULL
, /* group_buddy */
952 NULL
, /* rename_group */
953 NULL
, /* buddy_free */
954 NULL
, /* convo_closed */
955 purple_normalize_nocase
, /* normalize */
956 NULL
, /* set_buddy_icon */
957 NULL
, /* remove_group */
958 NULL
, /* get_cb_real_name */
959 irc_chat_set_topic
, /* set_chat_topic */
960 NULL
, /* find_blist_chat */
961 irc_roomlist_get_list
, /* roomlist_get_list */
962 irc_roomlist_cancel
, /* roomlist_cancel */
963 NULL
, /* roomlist_expand_category */
964 NULL
, /* can_receive_file */
965 irc_dccsend_send_file
, /* send_file */
966 irc_dccsend_new_xfer
, /* new_xfer */
967 NULL
, /* offline_message */
968 NULL
, /* whiteboard_prpl_ops */
969 irc_send_raw
, /* send_raw */
970 NULL
, /* roomlist_room_serialize */
971 NULL
, /* unregister_user */
972 NULL
, /* send_attention */
973 NULL
, /* get_attention_types */
974 sizeof(PurplePluginProtocolInfo
), /* struct_size */
975 NULL
, /* get_account_text_table */
976 NULL
, /* initiate_media */
977 NULL
, /* get_media_caps */
978 NULL
, /* get_moods */
979 NULL
, /* set_public_alias */
980 NULL
, /* get_public_alias */
981 NULL
, /* add_buddy_with_invite */
982 NULL
/* add_buddies_with_invite */
985 static gboolean
load_plugin (PurplePlugin
*plugin
) {
987 purple_signal_register(plugin
, "irc-sending-text",
988 purple_marshal_VOID__POINTER_POINTER
, NULL
, 2,
989 purple_value_new(PURPLE_TYPE_SUBTYPE
, PURPLE_SUBTYPE_CONNECTION
),
990 purple_value_new_outgoing(PURPLE_TYPE_STRING
));
991 purple_signal_register(plugin
, "irc-receiving-text",
992 purple_marshal_VOID__POINTER_POINTER
, NULL
, 2,
993 purple_value_new(PURPLE_TYPE_SUBTYPE
, PURPLE_SUBTYPE_CONNECTION
),
994 purple_value_new_outgoing(PURPLE_TYPE_STRING
));
999 static PurplePluginInfo info
=
1001 PURPLE_PLUGIN_MAGIC
,
1002 PURPLE_MAJOR_VERSION
,
1003 PURPLE_MINOR_VERSION
,
1004 PURPLE_PLUGIN_PROTOCOL
, /**< type */
1005 NULL
, /**< ui_requirement */
1007 NULL
, /**< dependencies */
1008 PURPLE_PRIORITY_DEFAULT
, /**< priority */
1010 "prpl-irc", /**< id */
1012 DISPLAY_VERSION
, /**< version */
1013 N_("IRC Protocol Plugin"), /** summary */
1014 N_("The IRC Protocol Plugin that Sucks Less"), /** description */
1015 NULL
, /**< author */
1016 PURPLE_WEBSITE
, /**< homepage */
1018 load_plugin
, /**< load */
1019 NULL
, /**< unload */
1020 NULL
, /**< destroy */
1022 NULL
, /**< ui_info */
1023 &prpl_info
, /**< extra_info */
1024 NULL
, /**< prefs_info */
1034 static void _init_plugin(PurplePlugin
*plugin
)
1036 PurpleAccountUserSplit
*split
;
1037 PurpleAccountOption
*option
;
1039 split
= purple_account_user_split_new(_("Server"), IRC_DEFAULT_SERVER
, '@');
1040 prpl_info
.user_splits
= g_list_append(prpl_info
.user_splits
, split
);
1042 option
= purple_account_option_int_new(_("Port"), "port", IRC_DEFAULT_PORT
);
1043 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
1045 option
= purple_account_option_string_new(_("Encodings"), "encoding", IRC_DEFAULT_CHARSET
);
1046 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
1048 option
= purple_account_option_bool_new(_("Auto-detect incoming UTF-8"), "autodetect_utf8", IRC_DEFAULT_AUTODETECT
);
1049 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
1051 option
= purple_account_option_string_new(_("Username"), "username", "");
1052 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
1054 option
= purple_account_option_string_new(_("Real name"), "realname", "");
1055 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
1058 option = purple_account_option_string_new(_("Quit message"), "quitmsg", IRC_DEFAULT_QUIT);
1059 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
1062 option
= purple_account_option_bool_new(_("Use SSL"), "ssl", FALSE
);
1063 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
1065 _irc_plugin
= plugin
;
1067 purple_prefs_remove("/plugins/prpl/irc/quitmsg");
1068 purple_prefs_remove("/plugins/prpl/irc");
1070 irc_register_commands();
1073 PURPLE_INIT_PLUGIN(irc
, _init_plugin
, info
);