diff options
author | Will Thompson <will.thompson@collabora.co.uk> | 2012-11-02 11:56:29 +0000 |
---|---|---|
committer | Will Thompson <will.thompson@collabora.co.uk> | 2012-11-02 11:56:29 +0000 |
commit | 27c1ffc5ebcfcb37ad958ef451ca2772bdc4ec01 (patch) | |
tree | 01963f027f44b83fa009f2b4189bc30d280a66fe /src | |
parent | ba22cc02a652a2ccfe6b59b113a87b2fe3761543 (diff) | |
parent | c679fe4baaec1c29f7d0fbbed1f6b1d9c7d1266c (diff) |
Merge branch 'ping'
Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=56589
Diffstat (limited to 'src')
-rw-r--r-- | src/idle-connection.c | 163 | ||||
-rw-r--r-- | src/idle-connection.h | 2 | ||||
-rw-r--r-- | src/idle-parser.c | 6 | ||||
-rw-r--r-- | src/idle-parser.h | 4 | ||||
-rw-r--r-- | src/idle-server-connection.c | 17 | ||||
-rw-r--r-- | src/idle-server-connection.h | 1 |
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); |