6 * Copyright (C) 2003, Robbert Haarman <purple@inglorion.net>
7 * Copyright (C) 2003, 2012 Ethan Blanton <elb@pidgin.im>
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"
29 #include "buddylist.h"
30 #include "conversation.h"
35 #include "purple-gio.h"
41 #define PING_TIMEOUT 60
43 static void irc_ison_buddy_init(char *name
, struct irc_buddy
*ib
, GList
**list
);
45 static const char *irc_blist_icon(PurpleAccount
*a
, PurpleBuddy
*b
);
46 static GList
*irc_status_types(PurpleAccount
*account
);
47 static GList
*irc_get_actions(PurpleConnection
*gc
);
48 /* static GList *irc_chat_info(PurpleConnection *gc); */
49 static void irc_login(PurpleAccount
*account
);
50 static void irc_login_cb(GObject
*source
, GAsyncResult
*res
, gpointer user_data
);
51 static void irc_close(PurpleConnection
*gc
);
52 static int irc_im_send(PurpleConnection
*gc
, PurpleMessage
*msg
);
53 static int irc_chat_send(PurpleConnection
*gc
, int id
, PurpleMessage
*msg
);
54 static void irc_chat_join (PurpleConnection
*gc
, GHashTable
*data
);
55 static void irc_read_input(struct irc_conn
*irc
);
57 static guint
irc_nick_hash(const char *nick
);
58 static gboolean
irc_nick_equal(const char *nick1
, const char *nick2
);
59 static void irc_buddy_free(struct irc_buddy
*ib
);
61 PurpleProtocol
*_irc_protocol
= NULL
;
63 static void irc_view_motd(PurpleProtocolAction
*action
)
65 PurpleConnection
*gc
= action
->connection
;
69 if (gc
== NULL
|| purple_connection_get_protocol_data(gc
) == NULL
) {
70 purple_debug(PURPLE_DEBUG_ERROR
, "irc", "got MOTD request for NULL gc\n");
73 irc
= purple_connection_get_protocol_data(gc
);
74 if (irc
->motd
== NULL
) {
75 purple_notify_error(gc
, _("Error displaying MOTD"),
76 _("No MOTD available"),
77 _("There is no MOTD associated with this connection."),
78 purple_request_cpar_from_connection(gc
));
81 title
= g_strdup_printf(_("MOTD for %s"), irc
->server
);
82 body
= g_strdup_printf("<span style=\"font-family: monospace;\">%s</span>", irc
->motd
->str
);
83 purple_notify_formatted(gc
, title
, title
, NULL
, body
, NULL
, NULL
);
88 static int irc_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
90 struct irc_conn
*irc
= purple_connection_get_protocol_data(gc
);
94 irc_send_len(irc
, buf
, len
);
99 irc_flush_cb(GObject
*source
, GAsyncResult
*res
, gpointer data
)
101 PurpleConnection
*gc
= data
;
103 GError
*error
= NULL
;
105 result
= g_output_stream_flush_finish(G_OUTPUT_STREAM(source
),
109 g_prefix_error(&error
, _("Lost connection with server: "));
110 purple_connection_take_error(gc
, error
);
115 int irc_send(struct irc_conn
*irc
, const char *buf
)
117 return irc_send_len(irc
, buf
, strlen(buf
));
120 int irc_send_len(struct irc_conn
*irc
, const char *buf
, int buflen
)
122 char *tosend
= g_strdup(buf
);
126 purple_signal_emit(_irc_protocol
, "irc-sending-text", purple_account_get_connection(irc
->account
), &tosend
);
131 len
= strlen(tosend
);
132 data
= g_bytes_new_take(tosend
, len
);
133 purple_queued_output_stream_push_bytes(irc
->output
, data
);
136 if (!g_output_stream_has_pending(G_OUTPUT_STREAM(irc
->output
))) {
137 /* Connection idle. Flush data. */
138 g_output_stream_flush_async(G_OUTPUT_STREAM(irc
->output
),
139 G_PRIORITY_DEFAULT
, irc
->cancellable
,
141 purple_account_get_connection(irc
->account
));
147 /* XXX I don't like messing directly with these buddies */
148 gboolean
irc_blist_timeout(struct irc_conn
*irc
)
150 if (irc
->ison_outstanding
) {
154 g_hash_table_foreach(irc
->buddies
, (GHFunc
)irc_ison_buddy_init
,
155 (gpointer
*)&irc
->buddies_outstanding
);
157 irc_buddy_query(irc
);
162 void irc_buddy_query(struct irc_conn
*irc
)
166 struct irc_buddy
*ib
;
169 string
= g_string_sized_new(512);
171 while ((lp
= g_list_first(irc
->buddies_outstanding
))) {
172 ib
= (struct irc_buddy
*)lp
->data
;
173 if (string
->len
+ strlen(ib
->name
) + 1 > 450)
175 g_string_append_printf(string
, "%s ", ib
->name
);
176 ib
->new_online_status
= FALSE
;
177 irc
->buddies_outstanding
= g_list_delete_link(irc
->buddies_outstanding
, lp
);
181 buf
= irc_format(irc
, "vn", "ISON", string
->str
);
184 irc
->ison_outstanding
= TRUE
;
186 irc
->ison_outstanding
= FALSE
;
188 g_string_free(string
, TRUE
);
191 static void irc_ison_buddy_init(char *name
, struct irc_buddy
*ib
, GList
**list
)
193 *list
= g_list_append(*list
, ib
);
197 static void irc_ison_one(struct irc_conn
*irc
, struct irc_buddy
*ib
)
201 if (irc
->buddies_outstanding
!= NULL
) {
202 irc
->buddies_outstanding
= g_list_append(irc
->buddies_outstanding
, ib
);
206 ib
->new_online_status
= FALSE
;
207 buf
= irc_format(irc
, "vn", "ISON", ib
->name
);
213 static const char *irc_blist_icon(PurpleAccount
*a
, PurpleBuddy
*b
)
218 static GList
*irc_status_types(PurpleAccount
*account
)
220 PurpleStatusType
*type
;
223 type
= purple_status_type_new(PURPLE_STATUS_AVAILABLE
, NULL
, NULL
, TRUE
);
224 types
= g_list_append(types
, type
);
226 type
= purple_status_type_new_with_attrs(
227 PURPLE_STATUS_AWAY
, NULL
, NULL
, TRUE
, TRUE
, FALSE
,
228 "message", _("Message"), purple_value_new(G_TYPE_STRING
),
230 types
= g_list_append(types
, type
);
232 type
= purple_status_type_new(PURPLE_STATUS_OFFLINE
, NULL
, NULL
, TRUE
);
233 types
= g_list_append(types
, type
);
238 static GList
*irc_get_actions(PurpleConnection
*gc
)
241 PurpleProtocolAction
*act
= NULL
;
243 act
= purple_protocol_action_new(_("View MOTD"), irc_view_motd
);
244 list
= g_list_append(list
, act
);
249 static GList
*irc_chat_join_info(PurpleConnection
*gc
)
252 PurpleProtocolChatEntry
*pce
;
254 pce
= g_new0(PurpleProtocolChatEntry
, 1);
255 pce
->label
= _("_Channel:");
256 pce
->identifier
= "channel";
257 pce
->required
= TRUE
;
258 m
= g_list_append(m
, pce
);
260 pce
= g_new0(PurpleProtocolChatEntry
, 1);
261 pce
->label
= _("_Password:");
262 pce
->identifier
= "password";
264 m
= g_list_append(m
, pce
);
269 static GHashTable
*irc_chat_info_defaults(PurpleConnection
*gc
, const char *chat_name
)
271 GHashTable
*defaults
;
273 defaults
= g_hash_table_new_full(g_str_hash
, g_str_equal
, NULL
, g_free
);
275 if (chat_name
!= NULL
)
276 g_hash_table_insert(defaults
, "channel", g_strdup(chat_name
));
281 static void irc_login(PurpleAccount
*account
)
283 PurpleConnection
*gc
;
284 struct irc_conn
*irc
;
286 const char *username
= purple_account_get_username(account
);
287 GSocketClient
*client
;
288 GError
*error
= NULL
;
290 gc
= purple_account_get_connection(account
);
291 purple_connection_set_flags(gc
, PURPLE_CONNECTION_FLAG_NO_NEWLINES
|
292 PURPLE_CONNECTION_FLAG_NO_IMAGES
);
294 if (strpbrk(username
, " \t\v\r\n") != NULL
) {
295 purple_connection_take_error(gc
, g_error_new_literal(
296 PURPLE_CONNECTION_ERROR
,
297 PURPLE_CONNECTION_ERROR_INVALID_SETTINGS
,
298 _("IRC nick and server may not contain whitespace")));
302 irc
= g_new0(struct irc_conn
, 1);
303 purple_connection_set_protocol_data(gc
, irc
);
304 irc
->account
= account
;
305 irc
->cancellable
= g_cancellable_new();
307 userparts
= g_strsplit(username
, "@", 2);
308 purple_connection_set_display_name(gc
, userparts
[0]);
309 irc
->server
= g_strdup(userparts
[1]);
310 g_strfreev(userparts
);
312 irc
->buddies
= g_hash_table_new_full((GHashFunc
)irc_nick_hash
, (GEqualFunc
)irc_nick_equal
,
313 NULL
, (GDestroyNotify
)irc_buddy_free
);
314 irc
->cmds
= g_hash_table_new(g_str_hash
, g_str_equal
);
315 irc_cmd_table_build(irc
);
316 irc
->msgs
= g_hash_table_new(g_str_hash
, g_str_equal
);
317 irc_msg_table_build(irc
);
319 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
321 client
= purple_gio_socket_client_new(account
, &error
);
323 if (client
== NULL
) {
324 purple_connection_take_error(gc
, error
);
328 /* Optionally use TLS if it's set in the account settings */
329 g_socket_client_set_tls(client
,
330 purple_account_get_bool(account
, "ssl", FALSE
));
332 g_socket_client_connect_to_host_async(client
, irc
->server
,
333 purple_account_get_int(account
, "port",
334 g_socket_client_get_tls(client
) ?
335 IRC_DEFAULT_SSL_PORT
:
337 irc
->cancellable
, irc_login_cb
, gc
);
338 g_object_unref(client
);
341 static gboolean
do_login(PurpleConnection
*gc
) {
342 char *buf
, *tmp
= NULL
;
344 const char *nickname
, *identname
, *realname
;
345 struct irc_conn
*irc
= purple_connection_get_protocol_data(gc
);
346 const char *pass
= purple_connection_get_password(gc
);
347 #ifdef HAVE_CYRUS_SASL
348 const gboolean use_sasl
= purple_account_get_bool(irc
->account
, "sasl", FALSE
);
352 #ifdef HAVE_CYRUS_SASL
354 buf
= irc_format(irc
, "vv:", "CAP", "REQ", "sasl");
355 else /* intended to fall through */
357 buf
= irc_format(irc
, "v:", "PASS", pass
);
358 if (irc_send(irc
, buf
) < 0) {
365 realname
= purple_account_get_string(irc
->account
, "realname", "");
366 identname
= purple_account_get_string(irc
->account
, "username", "");
368 if (identname
== NULL
|| *identname
== '\0') {
369 identname
= g_get_user_name();
372 if (identname
!= NULL
&& strchr(identname
, ' ') != NULL
) {
373 tmp
= g_strdup(identname
);
374 while ((buf
= strchr(tmp
, ' ')) != NULL
) {
379 if (*irc
->server
== ':') {
380 /* Same as hostname, above. */
381 server
= g_strdup_printf("0%s", irc
->server
);
383 server
= g_strdup(irc
->server
);
386 buf
= irc_format(irc
, "vvvv:", "USER", tmp
? tmp
: identname
, "*", server
,
387 *realname
== '\0' ? IRC_DEFAULT_ALIAS
: realname
);
390 if (irc_send(irc
, buf
) < 0) {
395 nickname
= purple_connection_get_display_name(gc
);
396 buf
= irc_format(irc
, "vn", "NICK", nickname
);
397 irc
->reqnick
= g_strdup(nickname
);
398 irc
->nickused
= FALSE
;
399 if (irc_send(irc
, buf
) < 0) {
405 irc
->recv_time
= time(NULL
);
411 irc_login_cb(GObject
*source
, GAsyncResult
*res
, gpointer user_data
)
413 PurpleConnection
*gc
= user_data
;
414 GSocketConnection
*conn
;
415 GError
*error
= NULL
;
416 struct irc_conn
*irc
;
418 conn
= g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source
),
422 g_prefix_error(&error
, _("Unable to connect: "));
423 purple_connection_take_error(gc
, error
);
427 irc
= purple_connection_get_protocol_data(gc
);
429 irc
->output
= purple_queued_output_stream_new(
430 g_io_stream_get_output_stream(G_IO_STREAM(irc
->conn
)));
433 irc
->input
= g_data_input_stream_new(
434 g_io_stream_get_input_stream(
435 G_IO_STREAM(irc
->conn
)));
440 static void irc_close(PurpleConnection
*gc
)
442 struct irc_conn
*irc
= purple_connection_get_protocol_data(gc
);
447 if (irc
->conn
!= NULL
)
448 irc_cmd_quit(irc
, "quit", NULL
, NULL
);
450 if (irc
->cancellable
!= NULL
) {
451 g_cancellable_cancel(irc
->cancellable
);
452 g_clear_object(&irc
->cancellable
);
455 if (irc
->conn
!= NULL
) {
456 purple_gio_graceful_close(G_IO_STREAM(irc
->conn
),
457 G_INPUT_STREAM(irc
->input
),
458 G_OUTPUT_STREAM(irc
->output
));
461 g_clear_object(&irc
->input
);
462 g_clear_object(&irc
->output
);
463 g_clear_object(&irc
->conn
);
466 purple_timeout_remove(irc
->timer
);
467 g_hash_table_destroy(irc
->cmds
);
468 g_hash_table_destroy(irc
->msgs
);
469 g_hash_table_destroy(irc
->buddies
);
471 g_string_free(irc
->motd
, TRUE
);
474 g_free(irc
->mode_chars
);
475 g_free(irc
->reqnick
);
477 #ifdef HAVE_CYRUS_SASL
478 if (irc
->sasl_conn
) {
479 sasl_dispose(&irc
->sasl_conn
);
480 irc
->sasl_conn
= NULL
;
482 g_free(irc
->sasl_cb
);
484 g_string_free(irc
->sasl_mechs
, TRUE
);
491 static int irc_im_send(PurpleConnection
*gc
, PurpleMessage
*msg
)
493 struct irc_conn
*irc
= purple_connection_get_protocol_data(gc
);
497 args
[0] = irc_nick_skip_mode(irc
, purple_message_get_recipient(msg
));
499 purple_markup_html_to_xhtml(purple_message_get_contents(msg
),
503 irc_cmd_privmsg(irc
, "msg", NULL
, args
);
508 static void irc_get_info(PurpleConnection
*gc
, const char *who
)
510 struct irc_conn
*irc
= purple_connection_get_protocol_data(gc
);
514 irc_cmd_whois(irc
, "whois", NULL
, args
);
517 static void irc_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
519 PurpleConnection
*gc
= purple_account_get_connection(account
);
520 struct irc_conn
*irc
;
522 const char *status_id
= purple_status_get_id(status
);
524 g_return_if_fail(gc
!= NULL
);
525 irc
= purple_connection_get_protocol_data(gc
);
527 if (!purple_status_is_active(status
))
532 if (!strcmp(status_id
, "away")) {
533 args
[0] = purple_status_get_attr_string(status
, "message");
534 if ((args
[0] == NULL
) || (*args
[0] == '\0'))
536 irc_cmd_away(irc
, "away", NULL
, args
);
537 } else if (!strcmp(status_id
, "available")) {
538 irc_cmd_away(irc
, "back", NULL
, args
);
542 static void irc_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
, const char *message
)
544 struct irc_conn
*irc
= purple_connection_get_protocol_data(gc
);
545 struct irc_buddy
*ib
;
546 const char *bname
= purple_buddy_get_name(buddy
);
548 ib
= g_hash_table_lookup(irc
->buddies
, bname
);
551 purple_protocol_got_user_status(irc
->account
, bname
,
552 ib
->online
? "available" : "offline", NULL
);
554 ib
= g_new0(struct irc_buddy
, 1);
555 ib
->name
= g_strdup(bname
);
557 g_hash_table_replace(irc
->buddies
, ib
->name
, ib
);
560 /* if the timer isn't set, this is during signon, so we don't want to flood
561 * ourself off with ISON's, so we don't, but after that we want to know when
562 * someone's online asap */
564 irc_ison_one(irc
, ib
);
567 static void irc_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
569 struct irc_conn
*irc
= purple_connection_get_protocol_data(gc
);
570 struct irc_buddy
*ib
;
572 ib
= g_hash_table_lookup(irc
->buddies
, purple_buddy_get_name(buddy
));
573 if (ib
&& --ib
->ref
== 0) {
574 g_hash_table_remove(irc
->buddies
, purple_buddy_get_name(buddy
));
579 irc_read_input_cb(GObject
*source
, GAsyncResult
*res
, gpointer data
)
581 PurpleConnection
*gc
= data
;
582 struct irc_conn
*irc
;
586 GError
*error
= NULL
;
588 line
= g_data_input_stream_read_line_finish(
589 G_DATA_INPUT_STREAM(source
), res
, &len
, &error
);
591 if (line
== NULL
&& error
!= NULL
) {
592 g_prefix_error(&error
, _("Lost connection with server: "));
593 purple_connection_take_error(gc
, error
);
595 } else if (line
== NULL
) {
596 purple_connection_take_error(gc
, g_error_new_literal(
597 PURPLE_CONNECTION_ERROR
,
598 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
599 _("Server closed the connection")));
603 irc
= purple_connection_get_protocol_data(gc
);
605 purple_connection_update_last_received(gc
);
607 if (len
> 0 && line
[len
- 1] == '\r')
608 line
[len
- 1] = '\0';
610 /* This is a hack to work around the fact that marv gets messages
611 * with null bytes in them while using some weird irc server at work
613 while (start
< len
&& line
[start
] == '\0')
617 irc_parse_msg(irc
, line
+ start
);
625 irc_read_input(struct irc_conn
*irc
)
627 PurpleConnection
*gc
= purple_account_get_connection(irc
->account
);
629 g_data_input_stream_read_line_async(irc
->input
,
630 G_PRIORITY_DEFAULT
, irc
->cancellable
,
631 irc_read_input_cb
, gc
);
634 static void irc_chat_join (PurpleConnection
*gc
, GHashTable
*data
)
636 struct irc_conn
*irc
= purple_connection_get_protocol_data(gc
);
639 args
[0] = g_hash_table_lookup(data
, "channel");
640 args
[1] = g_hash_table_lookup(data
, "password");
641 irc_cmd_join(irc
, "join", NULL
, args
);
644 static char *irc_get_chat_name(GHashTable
*data
) {
645 return g_strdup(g_hash_table_lookup(data
, "channel"));
648 static void irc_chat_invite(PurpleConnection
*gc
, int id
, const char *message
, const char *name
)
650 struct irc_conn
*irc
= purple_connection_get_protocol_data(gc
);
651 PurpleConversation
*convo
= PURPLE_CONVERSATION(purple_conversations_find_chat(gc
, id
));
655 purple_debug(PURPLE_DEBUG_ERROR
, "irc", "Got chat invite request for bogus chat\n");
659 args
[1] = purple_conversation_get_name(convo
);
660 irc_cmd_invite(irc
, "invite", purple_conversation_get_name(convo
), args
);
664 static void irc_chat_leave (PurpleConnection
*gc
, int id
)
666 struct irc_conn
*irc
= purple_connection_get_protocol_data(gc
);
667 PurpleConversation
*convo
= PURPLE_CONVERSATION(purple_conversations_find_chat(gc
, id
));
673 args
[0] = purple_conversation_get_name(convo
);
675 irc_cmd_part(irc
, "part", purple_conversation_get_name(convo
), args
);
676 purple_serv_got_chat_left(gc
, id
);
679 static int irc_chat_send(PurpleConnection
*gc
, int id
, PurpleMessage
*msg
)
681 struct irc_conn
*irc
= purple_connection_get_protocol_data(gc
);
682 PurpleConversation
*convo
= PURPLE_CONVERSATION(purple_conversations_find_chat(gc
, id
));
687 purple_debug(PURPLE_DEBUG_ERROR
, "irc", "chat send on nonexistent chat\n");
692 return irc_parse_cmd(irc
, convo
->name
, what
+ 1);
695 purple_markup_html_to_xhtml(purple_message_get_contents(msg
), NULL
, &tmp
);
696 args
[0] = purple_conversation_get_name(convo
);
699 irc_cmd_privmsg(irc
, "msg", NULL
, args
);
702 purple_serv_got_chat_in(gc
, id
, purple_connection_get_display_name(gc
),
703 purple_message_get_flags(msg
),
704 purple_message_get_contents(msg
), time(NULL
));
709 static guint
irc_nick_hash(const char *nick
)
714 lc
= g_utf8_strdown(nick
, -1);
715 bucket
= g_str_hash(lc
);
721 static gboolean
irc_nick_equal(const char *nick1
, const char *nick2
)
723 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
726 static void irc_buddy_free(struct irc_buddy
*ib
)
732 static void irc_chat_set_topic(PurpleConnection
*gc
, int id
, const char *topic
)
735 const char *name
= NULL
;
736 struct irc_conn
*irc
;
738 irc
= purple_connection_get_protocol_data(gc
);
739 name
= purple_conversation_get_name(PURPLE_CONVERSATION(
740 purple_conversations_find_chat(gc
, id
)));
745 buf
= irc_format(irc
, "vt:", "TOPIC", name
, topic
);
750 static PurpleRoomlist
*irc_roomlist_get_list(PurpleConnection
*gc
)
752 struct irc_conn
*irc
;
753 GList
*fields
= NULL
;
754 PurpleRoomlistField
*f
;
757 irc
= purple_connection_get_protocol_data(gc
);
760 g_object_unref(irc
->roomlist
);
762 irc
->roomlist
= purple_roomlist_new(purple_connection_get_account(gc
));
764 f
= purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING
, "", "channel", TRUE
);
765 fields
= g_list_append(fields
, f
);
767 f
= purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_INT
, _("Users"), "users", FALSE
);
768 fields
= g_list_append(fields
, f
);
770 f
= purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING
, _("Topic"), "topic", FALSE
);
771 fields
= g_list_append(fields
, f
);
773 purple_roomlist_set_fields(irc
->roomlist
, fields
);
775 buf
= irc_format(irc
, "v", "LIST");
779 return irc
->roomlist
;
782 static void irc_roomlist_cancel(PurpleRoomlist
*list
)
784 PurpleAccount
*account
= purple_roomlist_get_account(list
);
785 PurpleConnection
*gc
= purple_account_get_connection(account
);
786 struct irc_conn
*irc
;
791 irc
= purple_connection_get_protocol_data(gc
);
793 purple_roomlist_set_in_progress(list
, FALSE
);
795 if (irc
->roomlist
== list
) {
796 irc
->roomlist
= NULL
;
797 g_object_unref(list
);
801 static void irc_keepalive(PurpleConnection
*gc
)
803 struct irc_conn
*irc
= purple_connection_get_protocol_data(gc
);
804 if ((time(NULL
) - irc
->recv_time
) > PING_TIMEOUT
)
805 irc_cmd_ping(irc
, NULL
, NULL
, NULL
);
809 irc_get_max_message_size(PurpleConversation
*conv
)
811 /* TODO: this static value is got from pidgin-otr, but it depends on
812 * some factors, for example IRC channel name. */
817 irc_protocol_init(PurpleProtocol
*protocol
)
819 PurpleAccountUserSplit
*split
;
820 PurpleAccountOption
*option
;
822 protocol
->id
= "prpl-irc";
823 protocol
->name
= "IRC";
824 protocol
->options
= OPT_PROTO_CHAT_TOPIC
| OPT_PROTO_PASSWORD_OPTIONAL
|
825 OPT_PROTO_SLASH_COMMANDS_NATIVE
;
827 split
= purple_account_user_split_new(_("Server"), IRC_DEFAULT_SERVER
, '@');
828 protocol
->user_splits
= g_list_append(protocol
->user_splits
, split
);
830 option
= purple_account_option_int_new(_("Port"), "port", IRC_DEFAULT_PORT
);
831 protocol
->account_options
= g_list_append(protocol
->account_options
, option
);
833 option
= purple_account_option_string_new(_("Encodings"), "encoding", IRC_DEFAULT_CHARSET
);
834 protocol
->account_options
= g_list_append(protocol
->account_options
, option
);
836 option
= purple_account_option_bool_new(_("Auto-detect incoming UTF-8"), "autodetect_utf8", IRC_DEFAULT_AUTODETECT
);
837 protocol
->account_options
= g_list_append(protocol
->account_options
, option
);
839 option
= purple_account_option_string_new(_("Ident name"), "username", "");
840 protocol
->account_options
= g_list_append(protocol
->account_options
, option
);
842 option
= purple_account_option_string_new(_("Real name"), "realname", "");
843 protocol
->account_options
= g_list_append(protocol
->account_options
, option
);
846 option = purple_account_option_string_new(_("Quit message"), "quitmsg", IRC_DEFAULT_QUIT);
847 protocol->account_options = g_list_append(protocol->account_options, option);
850 option
= purple_account_option_bool_new(_("Use SSL"), "ssl", FALSE
);
851 protocol
->account_options
= g_list_append(protocol
->account_options
, option
);
853 #ifdef HAVE_CYRUS_SASL
854 option
= purple_account_option_bool_new(_("Authenticate with SASL"), "sasl", FALSE
);
855 protocol
->account_options
= g_list_append(protocol
->account_options
, option
);
857 option
= purple_account_option_bool_new(
858 _("Allow plaintext SASL auth over unencrypted connection"),
859 "auth_plain_in_clear", FALSE
);
860 protocol
->account_options
= g_list_append(protocol
->account_options
, option
);
865 irc_protocol_class_init(PurpleProtocolClass
*klass
)
867 klass
->login
= irc_login
;
868 klass
->close
= irc_close
;
869 klass
->status_types
= irc_status_types
;
870 klass
->list_icon
= irc_blist_icon
;
874 irc_protocol_client_iface_init(PurpleProtocolClientIface
*client_iface
)
876 client_iface
->get_actions
= irc_get_actions
;
877 client_iface
->normalize
= purple_normalize_nocase
;
878 client_iface
->get_max_message_size
= irc_get_max_message_size
;
882 irc_protocol_server_iface_init(PurpleProtocolServerIface
*server_iface
)
884 server_iface
->set_status
= irc_set_status
;
885 server_iface
->get_info
= irc_get_info
;
886 server_iface
->add_buddy
= irc_add_buddy
;
887 server_iface
->remove_buddy
= irc_remove_buddy
;
888 server_iface
->keepalive
= irc_keepalive
;
889 server_iface
->send_raw
= irc_send_raw
;
893 irc_protocol_im_iface_init(PurpleProtocolIMIface
*im_iface
)
895 im_iface
->send
= irc_im_send
;
899 irc_protocol_chat_iface_init(PurpleProtocolChatIface
*chat_iface
)
901 chat_iface
->info
= irc_chat_join_info
;
902 chat_iface
->info_defaults
= irc_chat_info_defaults
;
903 chat_iface
->join
= irc_chat_join
;
904 chat_iface
->get_name
= irc_get_chat_name
;
905 chat_iface
->invite
= irc_chat_invite
;
906 chat_iface
->leave
= irc_chat_leave
;
907 chat_iface
->send
= irc_chat_send
;
908 chat_iface
->set_topic
= irc_chat_set_topic
;
912 irc_protocol_roomlist_iface_init(PurpleProtocolRoomlistIface
*roomlist_iface
)
914 roomlist_iface
->get_list
= irc_roomlist_get_list
;
915 roomlist_iface
->cancel
= irc_roomlist_cancel
;
919 irc_protocol_xfer_iface_init(PurpleProtocolXferIface
*xfer_iface
)
921 xfer_iface
->send
= irc_dccsend_send_file
;
922 xfer_iface
->new_xfer
= irc_dccsend_new_xfer
;
925 PURPLE_DEFINE_TYPE_EXTENDED(
926 IRCProtocol
, irc_protocol
, PURPLE_TYPE_PROTOCOL
, 0,
928 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE
,
929 irc_protocol_client_iface_init
)
931 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE
,
932 irc_protocol_server_iface_init
)
934 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE
,
935 irc_protocol_im_iface_init
)
937 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT_IFACE
,
938 irc_protocol_chat_iface_init
)
940 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_ROOMLIST_IFACE
,
941 irc_protocol_roomlist_iface_init
)
943 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_XFER_IFACE
,
944 irc_protocol_xfer_iface_init
)
947 static PurplePluginInfo
*
948 plugin_query(GError
**error
)
950 return purple_plugin_info_new(
952 "name", "IRC Protocol",
953 "version", DISPLAY_VERSION
,
954 "category", N_("Protocol"),
955 "summary", N_("IRC Protocol Plugin"),
956 "description", N_("The IRC Protocol Plugin that Sucks Less"),
957 "website", PURPLE_WEBSITE
,
958 "abi-version", PURPLE_ABI_VERSION
,
959 "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL
|
960 PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD
,
966 plugin_load(PurplePlugin
*plugin
, GError
**error
)
968 irc_protocol_register_type(plugin
);
970 _irc_protocol
= purple_protocols_add(IRC_TYPE_PROTOCOL
, error
);
974 purple_prefs_remove("/plugins/prpl/irc/quitmsg");
975 purple_prefs_remove("/plugins/prpl/irc");
977 irc_register_commands();
979 purple_signal_register(_irc_protocol
, "irc-sending-text",
980 purple_marshal_VOID__POINTER_POINTER
, G_TYPE_NONE
, 2,
981 PURPLE_TYPE_CONNECTION
,
982 G_TYPE_POINTER
); /* pointer to a string */
983 purple_signal_register(_irc_protocol
, "irc-receiving-text",
984 purple_marshal_VOID__POINTER_POINTER
, G_TYPE_NONE
, 2,
985 PURPLE_TYPE_CONNECTION
,
986 G_TYPE_POINTER
); /* pointer to a string */
992 plugin_unload(PurplePlugin
*plugin
, GError
**error
)
994 irc_unregister_commands();
996 if (!purple_protocols_remove(_irc_protocol
, error
))
1002 PURPLE_PLUGIN_INIT(irc
, plugin_query
, plugin_load
, plugin_unload
);