summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorWill Thompson <will.thompson@collabora.co.uk>2012-11-02 11:56:29 +0000
committerWill Thompson <will.thompson@collabora.co.uk>2012-11-02 11:56:29 +0000
commit27c1ffc5ebcfcb37ad958ef451ca2772bdc4ec01 (patch)
tree01963f027f44b83fa009f2b4189bc30d280a66fe /src
parentba22cc02a652a2ccfe6b59b113a87b2fe3761543 (diff)
parentc679fe4baaec1c29f7d0fbbed1f6b1d9c7d1266c (diff)
Merge branch 'ping'
Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=56589
Diffstat (limited to 'src')
-rw-r--r--src/idle-connection.c163
-rw-r--r--src/idle-connection.h2
-rw-r--r--src/idle-parser.c6
-rw-r--r--src/idle-parser.h4
-rw-r--r--src/idle-server-connection.c17
-rw-r--r--src/idle-server-connection.h1
6 files changed, 143 insertions, 50 deletions
diff --git a/src/idle-connection.c b/src/idle-connection.c
index d48564d..6a655b1 100644
--- a/src/idle-connection.c
+++ b/src/idle-connection.c
@@ -51,14 +51,14 @@
#include "extensions/extensions.h" /* Renaming */
+#define DEFAULT_KEEPALIVE_INTERVAL 30 /* sec */
+#define MISSED_KEEPALIVES_BEFORE_DISCONNECTING 3
+
/* From RFC 2813 :
* This in essence means that the client may send one (1) message every
* two (2) seconds without being adversely affected. Services MAY also
* be subject to this mechanism.
*/
-
-#define DEFAULT_KEEPALIVE_INTERVAL 30 /* sec */
-
#define MSG_QUEUE_TIMEOUT 2
static gboolean flush_queue_faster = FALSE;
@@ -140,16 +140,18 @@ enum {
LAST_PROPERTY_ENUM
};
-/* private structure */
-typedef struct _IdleConnectionPrivate IdleConnectionPrivate;
struct _IdleConnectionPrivate {
/*
* network connection
*/
-
IdleServerConnection *conn;
guint sconn_status;
+ /* When we sent a PING to the server which it hasn't PONGed for yet, or 0 if
+ * there isn't a PING outstanding.
+ */
+ gint64 ping_time;
+
/* IRC connection properties */
char *nickname;
char *server;
@@ -199,8 +201,6 @@ struct _IdleConnectionPrivate {
TpSimplePasswordManager *password_manager;
};
-#define IDLE_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), IDLE_TYPE_CONNECTION, IdleConnectionPrivate))
-
static void _iface_create_handle_repos(TpBaseConnection *self, TpHandleRepoIface **repos);
static GPtrArray *_iface_create_channel_managers(TpBaseConnection *self);
static gchar *_iface_get_unique_connection_name(TpBaseConnection *self);
@@ -213,6 +213,8 @@ static IdleParserHandlerResult _erroneous_nickname_handler(IdleParser *parser, I
static IdleParserHandlerResult _nick_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data);
static IdleParserHandlerResult _nickname_in_use_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data);
static IdleParserHandlerResult _ping_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data);
+static IdleParserHandlerResult _pong_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data);
+static IdleParserHandlerResult _unknown_command_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data);
static IdleParserHandlerResult _version_privmsg_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data);
static IdleParserHandlerResult _welcome_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data);
static IdleParserHandlerResult _whois_user_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data);
@@ -237,8 +239,9 @@ static void conn_aliasing_fill_contact_attributes (
GHashTable *attributes_hash);
static void idle_connection_init(IdleConnection *obj) {
- IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(obj);
+ IdleConnectionPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (obj, IDLE_TYPE_CONNECTION, IdleConnectionPrivate);
+ obj->priv = priv;
priv->sconn_status = SERVER_CONNECTION_STATE_NOT_CONNECTED;
priv->msg_queue = g_queue_new();
@@ -259,7 +262,8 @@ idle_connection_constructed (GObject *object)
}
static void idle_connection_set_property(GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) {
- IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(obj);
+ IdleConnection *self = IDLE_CONNECTION (obj);
+ IdleConnectionPrivate *priv = self->priv;
switch (prop_id) {
case PROP_NICKNAME:
@@ -320,7 +324,8 @@ static void idle_connection_set_property(GObject *obj, guint prop_id, const GVal
}
static void idle_connection_get_property(GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) {
- IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(obj);
+ IdleConnection *self = IDLE_CONNECTION (obj);
+ IdleConnectionPrivate *priv = self->priv;
switch (prop_id) {
case PROP_NICKNAME:
@@ -375,7 +380,7 @@ static void idle_connection_get_property(GObject *obj, guint prop_id, GValue *va
static void idle_connection_dispose (GObject *object) {
IdleConnection *self = IDLE_CONNECTION(object);
- IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(self);
+ IdleConnectionPrivate *priv = self->priv;
if (priv->dispose_has_run)
return;
@@ -408,7 +413,8 @@ static void idle_connection_dispose (GObject *object) {
}
static void idle_connection_finalize (GObject *object) {
- IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(object);
+ IdleConnection *self = IDLE_CONNECTION (object);
+ IdleConnectionPrivate *priv = self->priv;
IdleOutputPendingMsg *msg;
idle_contact_info_finalize(object);
@@ -509,8 +515,9 @@ static void idle_connection_class_init(IdleConnectionClass *klass) {
flush_queue_faster = TRUE;
}
-static GPtrArray *_iface_create_channel_managers(TpBaseConnection *self) {
- IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(self);
+static GPtrArray *_iface_create_channel_managers(TpBaseConnection *base) {
+ IdleConnection *self = IDLE_CONNECTION (base);
+ IdleConnectionPrivate *priv = self->priv;
GPtrArray *managers = g_ptr_array_sized_new(1);
GObject *manager;
@@ -520,7 +527,7 @@ static GPtrArray *_iface_create_channel_managers(TpBaseConnection *self) {
manager = g_object_new(IDLE_TYPE_MUC_MANAGER, "connection", self, NULL);
g_ptr_array_add(managers, manager);
- priv->password_manager = tp_simple_password_manager_new(self);
+ priv->password_manager = tp_simple_password_manager_new(base);
g_ptr_array_add(managers, priv->password_manager);
manager = g_object_new(IDLE_TYPE_ROOMLIST_MANAGER, "connection", self, NULL);
@@ -536,15 +543,17 @@ static void _iface_create_handle_repos(TpBaseConnection *self, TpHandleRepoIface
idle_handle_repos_init(repos);
}
-static gchar *_iface_get_unique_connection_name(TpBaseConnection *self) {
- IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(self);
+static gchar *_iface_get_unique_connection_name(TpBaseConnection *base) {
+ IdleConnection *self = IDLE_CONNECTION (base);
+ IdleConnectionPrivate *priv = self->priv;
return g_strdup_printf("%s@%s%p", priv->nickname, priv->server, self);
}
static gboolean _finish_shutdown_idle_func(gpointer data) {
TpBaseConnection *conn = TP_BASE_CONNECTION(data);
- IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+ IdleConnection *self = IDLE_CONNECTION(conn);
+ IdleConnectionPrivate *priv = self->priv;
if (priv->force_disconnect_id != 0) {
g_source_remove(priv->force_disconnect_id);
}
@@ -558,14 +567,16 @@ static gboolean
_force_disconnect (gpointer data)
{
IdleConnection *conn = IDLE_CONNECTION(data);
- IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
- idle_server_connection_disconnect_async(priv->conn, NULL, NULL, NULL);
+ IdleConnectionPrivate *priv = conn->priv;
+
+ IDLE_DEBUG("gave up waiting, forcibly disconnecting");
+ idle_server_connection_force_disconnect(priv->conn);
return FALSE;
}
static void _iface_disconnected(TpBaseConnection *self) {
IdleConnection *conn = IDLE_CONNECTION(self);
- IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+ IdleConnectionPrivate *priv = conn->priv;
/* we never got around to actually creating the connection
* iface object because we were still trying to connect, so
@@ -584,8 +595,9 @@ static void _iface_disconnected(TpBaseConnection *self) {
priv->force_disconnect_id = g_timeout_add_seconds(2, _force_disconnect, conn);
}
-static void _iface_shut_down(TpBaseConnection *self) {
- IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(self);
+static void _iface_shut_down(TpBaseConnection *base) {
+ IdleConnection *self = IDLE_CONNECTION (base);
+ IdleConnectionPrivate *priv = self->priv;
if (priv->quitting)
return;
@@ -594,7 +606,7 @@ static void _iface_shut_down(TpBaseConnection *self) {
* iface object because we were still trying to connect, so
* don't try to send any traffic down it */
if (priv->conn == NULL) {
- g_idle_add(_finish_shutdown_idle_func, self);;
+ g_idle_add(_finish_shutdown_idle_func, self);
} else {
idle_server_connection_disconnect_async(priv->conn, NULL, NULL, NULL);
}
@@ -628,7 +640,7 @@ static void _start_connecting_continue(IdleConnection *conn);
static void _password_prompt_cb(GObject *source, GAsyncResult *result, gpointer user_data) {
IdleConnection *conn = user_data;
TpBaseConnection *base_conn = TP_BASE_CONNECTION(conn);
- IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+ IdleConnectionPrivate *priv = conn->priv;
const GString *password;
GError *error = NULL;
@@ -655,7 +667,7 @@ static void _password_prompt_cb(GObject *source, GAsyncResult *result, gpointer
static gboolean _iface_start_connecting(TpBaseConnection *self, GError **error) {
IdleConnection *conn = IDLE_CONNECTION(self);
- IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+ IdleConnectionPrivate *priv = conn->priv;
g_assert(priv->nickname != NULL);
g_assert(priv->server != NULL);
@@ -679,7 +691,7 @@ static gboolean _iface_start_connecting(TpBaseConnection *self, GError **error)
static void _connection_connect_ready(GObject *source_object, GAsyncResult *res, gpointer user_data) {
IdleServerConnection *sconn = IDLE_SERVER_CONNECTION(source_object);
IdleConnection *conn = IDLE_CONNECTION(user_data);
- IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+ IdleConnectionPrivate *priv = conn->priv;
GError *error = NULL;
if (!idle_server_connection_connect_finish(sconn, res, &error)) {
@@ -701,6 +713,8 @@ static void _connection_connect_ready(GObject *source_object, GAsyncResult *res,
idle_parser_add_handler(conn->parser, IDLE_PARSER_NUMERIC_WHOISUSER, _whois_user_handler, conn);
idle_parser_add_handler(conn->parser, IDLE_PARSER_CMD_PING, _ping_handler, conn);
+ idle_parser_add_handler(conn->parser, IDLE_PARSER_PREFIXCMD_PONG, _pong_handler, conn);
+ idle_parser_add_handler(conn->parser, IDLE_PARSER_NUMERIC_UNKNOWNCOMMAND, _unknown_command_handler, conn);
idle_parser_add_handler_with_priority(conn->parser, IDLE_PARSER_PREFIXCMD_NICK, _nick_handler, conn, IDLE_PARSER_HANDLER_PRIORITY_FIRST);
idle_parser_add_handler(conn->parser, IDLE_PARSER_PREFIXCMD_PRIVMSG_USER, _version_privmsg_handler, conn);
@@ -709,7 +723,7 @@ static void _connection_connect_ready(GObject *source_object, GAsyncResult *res,
}
static void _start_connecting_continue(IdleConnection *conn) {
- IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+ IdleConnectionPrivate *priv = conn->priv;
IdleServerConnection *sconn;
if (tp_str_empty(priv->realname)) {
@@ -740,7 +754,7 @@ static void _start_connecting_continue(IdleConnection *conn) {
static gboolean keepalive_timeout_cb(gpointer user_data);
static void sconn_status_changed_cb(IdleServerConnection *sconn, IdleServerConnectionState state, IdleServerConnectionStateReason reason, IdleConnection *conn) {
- IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+ IdleConnectionPrivate *priv = conn->priv;
TpConnectionStatusReason tp_reason;
/* cancel scheduled forced disconnect since we are now disconnected */
@@ -804,17 +818,41 @@ static void sconn_received_cb(IdleServerConnection *sconn, gchar *raw_msg, IdleC
static gboolean keepalive_timeout_cb(gpointer user_data) {
IdleConnection *conn = IDLE_CONNECTION(user_data);
- IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+ IdleConnectionPrivate *priv = conn->priv;
gchar cmd[IRC_MSG_MAXLEN + 1];
- gint64 ping_time;
+ gint64 now;
- if (priv->sconn_status != SERVER_CONNECTION_STATE_CONNECTED) {
+ if (priv->sconn_status != SERVER_CONNECTION_STATE_CONNECTED ||
+ priv->quitting) {
priv->keepalive_timeout = 0;
return FALSE;
}
- ping_time = g_get_real_time();
- g_snprintf(cmd, IRC_MSG_MAXLEN + 1, "PING %" G_GINT64_FORMAT, ping_time);
+ now = g_get_real_time();
+
+ if (priv->ping_time != 0) {
+ gint64 seconds_since_ping = (now - priv->ping_time) / G_USEC_PER_SEC;
+ gint64 grace_period = priv->keepalive_interval * MISSED_KEEPALIVES_BEFORE_DISCONNECTING;
+
+ if (seconds_since_ping > grace_period) {
+ IDLE_DEBUG("haven't heard from the server in %" G_GINT64_FORMAT " seconds "
+ "(more than %u keepalive intervals)",
+ seconds_since_ping, MISSED_KEEPALIVES_BEFORE_DISCONNECTING);
+
+ idle_server_connection_force_disconnect(priv->conn);
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ if (priv->msg_queue->length > 0) {
+ /* No point in sending a PING if we're sending data anyway. */
+ return TRUE;
+ }
+
+ priv->ping_time = now;
+ g_snprintf(cmd, IRC_MSG_MAXLEN + 1, "PING %" G_GINT64_FORMAT, priv->ping_time);
_send_with_priority(conn, cmd, SERVER_CMD_MIN_PRIORITY);
return TRUE;
@@ -823,7 +861,7 @@ static gboolean keepalive_timeout_cb(gpointer user_data) {
static void _msg_queue_timeout_ready(GObject *source_object, GAsyncResult *res, gpointer user_data) {
IdleServerConnection *sconn = IDLE_SERVER_CONNECTION(source_object);
IdleConnection *conn = IDLE_CONNECTION (user_data);
- IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+ IdleConnectionPrivate *priv = conn->priv;
GError *error = NULL;
priv->msg_sending = FALSE;
@@ -839,7 +877,7 @@ static void _msg_queue_timeout_ready(GObject *source_object, GAsyncResult *res,
static gboolean msg_queue_timeout_cb(gpointer user_data) {
IdleConnection *conn = IDLE_CONNECTION(user_data);
- IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+ IdleConnectionPrivate *priv = conn->priv;
IdleOutputPendingMsg *output_msg;
IDLE_DEBUG("called");
@@ -872,7 +910,7 @@ static gboolean msg_queue_timeout_cb(gpointer user_data) {
static void
idle_connection_add_queue_timeout (IdleConnection *self)
{
- IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE (self);
+ IdleConnectionPrivate *priv = self->priv;
if (priv->msg_queue_timeout == 0)
{
@@ -896,7 +934,7 @@ idle_connection_add_queue_timeout (IdleConnection *self)
static void
idle_connection_clear_queue_timeout (IdleConnection *self)
{
- IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE (self);
+ IdleConnectionPrivate *priv = self->priv;
if (priv->msg_queue_timeout != 0)
{
@@ -909,7 +947,7 @@ idle_connection_clear_queue_timeout (IdleConnection *self)
* Queue a IRC command for sending, clipping it to IRC_MSG_MAXLEN bytes and appending the required <CR><LF> to it
*/
static void _send_with_priority(IdleConnection *conn, const gchar *msg, guint priority) {
- IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+ IdleConnectionPrivate *priv = conn->priv;
gchar cmd[IRC_MSG_MAXLEN + 3];
int len;
gchar *converted;
@@ -949,7 +987,7 @@ void idle_connection_send(IdleConnection *conn, const gchar *msg) {
gsize
idle_connection_get_max_message_length(IdleConnection *conn)
{
- IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+ IdleConnectionPrivate *priv = conn->priv;
if (priv->relay_prefix != NULL) {
/* server will add ':<relay_prefix> ' to all messages it relays on to
* other users. the +2 is for the initial : and the trailing space */
@@ -1058,6 +1096,35 @@ static IdleParserHandlerResult _ping_handler(IdleParser *parser, IdleParserMessa
return IDLE_PARSER_HANDLER_RESULT_HANDLED;
}
+static IdleParserHandlerResult _pong_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data) {
+ IdleConnection *conn = IDLE_CONNECTION(user_data);
+ IdleConnectionPrivate *priv = conn->priv;
+
+ /* We could compare ping_time to g_get_real_time() and give some indication
+ * of lag, if we were feeling enthusiastic.
+ */
+ priv->ping_time = 0;
+
+ return IDLE_PARSER_HANDLER_RESULT_HANDLED;
+}
+
+static IdleParserHandlerResult _unknown_command_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data) {
+ IdleConnection *conn = IDLE_CONNECTION(user_data);
+ IdleConnectionPrivate *priv = conn->priv;
+ const gchar *command = g_value_get_string(g_value_array_get_nth(args, 0));
+
+ if (!tp_strdiff(command, "PING")) {
+ IDLE_DEBUG("PING not supported, disabling keepalive.");
+ g_source_remove(priv->keepalive_timeout);
+ priv->keepalive_timeout = 0;
+ priv->ping_time = 0;
+
+ return IDLE_PARSER_HANDLER_RESULT_HANDLED;
+ }
+
+ return IDLE_PARSER_HANDLER_RESULT_NOT_HANDLED;
+}
+
static IdleParserHandlerResult _version_privmsg_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data) {
IdleConnection *conn = IDLE_CONNECTION(user_data);
const gchar *msg = g_value_get_string(g_value_array_get_nth(args, 2));
@@ -1094,7 +1161,7 @@ static IdleParserHandlerResult
_whois_user_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data)
{
IdleConnection *conn = IDLE_CONNECTION(user_data);
- IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+ IdleConnectionPrivate *priv = conn->priv;
/* message format: <nick> <user> <host> * :<real name> */
TpHandle handle = g_value_get_uint(g_value_array_get_nth(args, 0));
@@ -1122,7 +1189,7 @@ static void irc_handshakes(IdleConnection *conn) {
g_assert(conn != NULL);
g_assert(IDLE_IS_CONNECTION(conn));
- priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+ priv = conn->priv;
if ((priv->password != NULL) && (priv->password[0] != '\0')) {
g_snprintf(msg, IRC_MSG_MAXLEN + 1, "PASS %s", priv->password);
@@ -1141,7 +1208,7 @@ static void irc_handshakes(IdleConnection *conn) {
}
static void send_quit_request(IdleConnection *conn) {
- IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+ IdleConnectionPrivate *priv = conn->priv;
gchar cmd[IRC_MSG_MAXLEN + 1] = "QUIT";
if (priv->quit_message != NULL)
@@ -1172,7 +1239,7 @@ static void connection_disconnect_cb(IdleConnection *conn, TpConnectionStatusRea
static void
_queue_alias_changed(IdleConnection *conn, TpHandle handle, const gchar *alias) {
- IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+ IdleConnectionPrivate *priv = conn->priv;
if (!priv->queued_aliases_owners) {
TpHandleRepoIface *handles = tp_base_connection_get_handles(TP_BASE_CONNECTION(conn), TP_HANDLE_TYPE_CONTACT);
@@ -1217,7 +1284,7 @@ void idle_connection_canon_nick_receive(IdleConnection *conn, TpHandle handle, c
}
void idle_connection_emit_queued_aliases_changed(IdleConnection *conn) {
- IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
+ IdleConnectionPrivate *priv = conn->priv;
if (!priv->queued_aliases)
return;
@@ -1371,7 +1438,7 @@ static void idle_connection_set_aliases(TpSvcConnectionInterfaceAliasing *iface,
}
static gboolean idle_connection_hton(IdleConnection *obj, const gchar *input, gchar **output, GError **_error) {
- IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(obj);
+ IdleConnectionPrivate *priv = obj->priv;
GError *error = NULL;
gsize bytes_written;
gchar *ret;
@@ -1428,7 +1495,7 @@ idle_salvage_utf8 (gchar *supposed_utf8, gssize bytes)
static gchar *
idle_connection_ntoh(IdleConnection *obj, const gchar *input) {
- IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(obj);
+ IdleConnectionPrivate *priv = obj->priv;
GError *error = NULL;
gsize bytes_written;
gchar *ret;
diff --git a/src/idle-connection.h b/src/idle-connection.h
index c73e202..eb9c3f8 100644
--- a/src/idle-connection.h
+++ b/src/idle-connection.h
@@ -34,6 +34,7 @@ G_BEGIN_DECLS
typedef struct _IdleConnection IdleConnection;
typedef struct _IdleConnectionClass IdleConnectionClass;
+typedef struct _IdleConnectionPrivate IdleConnectionPrivate;
struct _IdleConnectionClass {
TpBaseConnectionClass parent_class;
@@ -45,6 +46,7 @@ struct _IdleConnection {
TpContactsMixin contacts;
IdleParser *parser;
GQueue *contact_info_requests;
+ IdleConnectionPrivate *priv;
};
GType idle_connection_get_type(void);
diff --git a/src/idle-parser.c b/src/idle-parser.c
index 5e7ede0..1699774 100644
--- a/src/idle-parser.c
+++ b/src/idle-parser.c
@@ -85,6 +85,7 @@ static const MessageSpec message_specs[] = {
{"NOTICE", "cIr:", IDLE_PARSER_PREFIXCMD_NOTICE_CHANNEL},
{"NOTICE", "cIc:", IDLE_PARSER_PREFIXCMD_NOTICE_USER},
{"PART", "cIr.", IDLE_PARSER_PREFIXCMD_PART},
+ {"PONG", "IIs:", IDLE_PARSER_PREFIXCMD_PONG},
{"PRIVMSG", "cIr:", IDLE_PARSER_PREFIXCMD_PRIVMSG_CHANNEL},
{"PRIVMSG", "cIc:", IDLE_PARSER_PREFIXCMD_PRIVMSG_USER},
{"QUIT", "cI.", IDLE_PARSER_PREFIXCMD_QUIT},
@@ -121,6 +122,7 @@ static const MessageSpec message_specs[] = {
{"317", "IIIcd", IDLE_PARSER_NUMERIC_WHOISIDLE},
{"322", "IIIrd.", IDLE_PARSER_NUMERIC_LIST},
{"323", "I", IDLE_PARSER_NUMERIC_LISTEND},
+ {"421", "IIIs:", IDLE_PARSER_NUMERIC_UNKNOWNCOMMAND},
{NULL, NULL, IDLE_PARSER_LAST_MESSAGE_CODE}
};
@@ -352,10 +354,10 @@ static void _parse_message(IdleParser *parser, const gchar *split_msg) {
for (int i = 0; i < IDLE_PARSER_LAST_MESSAGE_CODE; i++) {
const MessageSpec *spec = &(message_specs[i]);
- if ((split_msg[0] != ':') && (i <= IDLE_PARSER_CMD_PING)) {
+ if ((split_msg[0] != ':') && (i <= IDLE_PARSER_LAST_NON_PREFIX_CMD)) {
if (!g_ascii_strcasecmp(tokens[0], spec->str))
_parse_and_forward_one(parser, tokens, spec->code, spec->format);
- } else if (i >= IDLE_PARSER_PREFIXCMD_INVITE) {
+ } else if (i > IDLE_PARSER_LAST_NON_PREFIX_CMD) {
if (!g_ascii_strcasecmp(tokens[2], spec->str))
_parse_and_forward_one(parser, tokens, spec->code, spec->format);
}
diff --git a/src/idle-parser.h b/src/idle-parser.h
index fb68e3d..7ba333e 100644
--- a/src/idle-parser.h
+++ b/src/idle-parser.h
@@ -49,6 +49,8 @@ typedef enum {
IDLE_PARSER_CMD_ERROR = 0,
IDLE_PARSER_CMD_PING,
+ IDLE_PARSER_LAST_NON_PREFIX_CMD = IDLE_PARSER_CMD_PING,
+
IDLE_PARSER_PREFIXCMD_INVITE,
IDLE_PARSER_PREFIXCMD_JOIN,
IDLE_PARSER_PREFIXCMD_KICK,
@@ -58,6 +60,7 @@ typedef enum {
IDLE_PARSER_PREFIXCMD_NOTICE_CHANNEL,
IDLE_PARSER_PREFIXCMD_NOTICE_USER,
IDLE_PARSER_PREFIXCMD_PART,
+ IDLE_PARSER_PREFIXCMD_PONG,
IDLE_PARSER_PREFIXCMD_PRIVMSG_CHANNEL,
IDLE_PARSER_PREFIXCMD_PRIVMSG_USER,
IDLE_PARSER_PREFIXCMD_QUIT,
@@ -94,6 +97,7 @@ typedef enum {
IDLE_PARSER_NUMERIC_WHOISIDLE,
IDLE_PARSER_NUMERIC_LIST,
IDLE_PARSER_NUMERIC_LISTEND,
+ IDLE_PARSER_NUMERIC_UNKNOWNCOMMAND,
IDLE_PARSER_LAST_MESSAGE_CODE
} IdleParserMessageCode;
diff --git a/src/idle-server-connection.c b/src/idle-server-connection.c
index 64cb766..700b5f8 100644
--- a/src/idle-server-connection.c
+++ b/src/idle-server-connection.c
@@ -362,6 +362,23 @@ void idle_server_connection_disconnect_async(IdleServerConnection *conn, GCancel
user_data);
}
+void idle_server_connection_force_disconnect(IdleServerConnection *conn) {
+ /* Passing a cancelled cancellable to g_io_stream_close_async() stops us
+ * waiting for a TCP-level reply from the server which we already know
+ * has gone away. Quoth the docs for g_io_stream_close():
+ *
+ * Cancelling a close will still leave the stream closed, but some
+ * streams can use a faster close that doesn't block to e.g. check
+ * errors.
+ */
+ GCancellable *kill_me_now = g_cancellable_new();
+
+ g_cancellable_cancel(kill_me_now);
+ idle_server_connection_disconnect_full_async(conn,
+ SERVER_CONNECTION_STATE_REASON_ERROR, kill_me_now, NULL, NULL);
+ g_object_unref(kill_me_now);
+}
+
void idle_server_connection_disconnect_full_async(IdleServerConnection *conn, guint reason, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) {
IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(conn);
GSimpleAsyncResult *result;
diff --git a/src/idle-server-connection.h b/src/idle-server-connection.h
index 207fedc..8ce36ca 100644
--- a/src/idle-server-connection.h
+++ b/src/idle-server-connection.h
@@ -73,6 +73,7 @@ void idle_server_connection_connect_async(IdleServerConnection *conn, GCancellab
gboolean idle_server_connection_connect_finish(IdleServerConnection *conn, GAsyncResult *result, GError **error);
void idle_server_connection_disconnect_async(IdleServerConnection *conn, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
void idle_server_connection_disconnect_full_async(IdleServerConnection *conn, guint reason, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
+void idle_server_connection_force_disconnect(IdleServerConnection *conn);
gboolean idle_server_connection_disconnect_finish(IdleServerConnection *conn, GAsyncResult *result, GError **error);
void idle_server_connection_send_async(IdleServerConnection *conn, const gchar *cmd, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
gboolean idle_server_connection_send_finish(IdleServerConnection *conn, GAsyncResult *result, GError **error);