loudmouth/lm-feature-ping.c
author Mikael Hallendal <micke@imendio.com>
Wed, 08 Oct 2008 12:34:50 +0200
changeset 515 fb0f1adfa271
parent 509 497afdad39a5
child 516 4dd3aa6b83e5
permissions -rw-r--r--
Change the emacs format string to use space instead of tab for indentation

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
/*
 * Copyright (C) 2008 Imendio AB
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include <config.h>

#include "lm-connection.h"
#include "lm-debug.h"
#include "lm-internals.h"
#include "lm-marshal.h"
#include "lm-misc.h"

#include "lm-feature-ping.h"

#define XMPP_NS_PING "urn:xmpp:ping"

#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), LM_TYPE_FEATURE_PING, LmFeaturePingPriv))

typedef struct LmFeaturePingPriv LmFeaturePingPriv;
struct LmFeaturePingPriv {
        LmConnection *connection;
	guint         keep_alive_rate;
	GSource      *keep_alive_source;
	guint         keep_alive_counter;
};

static void     feature_ping_finalize            (GObject           *object);
static void     feature_ping_get_property        (GObject           *object,
                                                  guint              param_id,
                                                  GValue            *value,
                                                  GParamSpec        *pspec);
static void     feature_ping_set_property        (GObject           *object,
                                                  guint              param_id,
                                                  const GValue      *value,
                                                  GParamSpec        *pspec);

static LmHandlerResult
feature_ping_keep_alive_reply                    (LmMessageHandler *handler,
                                                  LmConnection     *connection,
                                                  LmMessage        *m,
                                                  gpointer          user_data);
static gboolean feature_ping_send_keep_alive     (LmFeaturePing    *fp);

G_DEFINE_TYPE (LmFeaturePing, lm_feature_ping, G_TYPE_OBJECT)

enum {
	PROP_0,
        PROP_CONNECTION,
        PROP_RATE
};

enum {
        TIMED_OUT,
	LAST_SIGNAL
};

static guint signals[LAST_SIGNAL] = { 0 };

static void
lm_feature_ping_class_init (LmFeaturePingClass *class)
{
	GObjectClass *object_class = G_OBJECT_CLASS (class);

	object_class->finalize     = feature_ping_finalize;
	object_class->get_property = feature_ping_get_property;
	object_class->set_property = feature_ping_set_property;

        g_object_class_install_property (object_class,
                                         PROP_CONNECTION,
                                         g_param_spec_pointer ("connection",
                                                               "Connection",
                                                               "The LmConnection to use",
                                                               G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));

        g_object_class_install_property (object_class,
					 PROP_RATE,
					 g_param_spec_uint ("rate",
                                                            "Timeout Rate",
                                                            "Keep alive rate in seconds",
                                                            0, G_MAXUINT,
                                                            0,
                                                            G_PARAM_READWRITE));

        signals[TIMED_OUT] = 
		g_signal_new ("timed-out",
			      G_OBJECT_CLASS_TYPE (object_class),
			      G_SIGNAL_RUN_LAST,
			      0,
			      NULL, NULL,
			      lm_marshal_VOID__VOID,
			      G_TYPE_NONE, 0);
	
	g_type_class_add_private (object_class, sizeof (LmFeaturePingPriv));
}

static void
lm_feature_ping_init (LmFeaturePing *feature_ping)
{
	LmFeaturePingPriv *priv;

	priv = GET_PRIV (feature_ping);

}

static void
feature_ping_finalize (GObject *object)
{
	LmFeaturePingPriv *priv;

	priv = GET_PRIV (object);

	(G_OBJECT_CLASS (lm_feature_ping_parent_class)->finalize) (object);
}

static void
feature_ping_get_property (GObject    *object,
                           guint       param_id,
                           GValue     *value,
                           GParamSpec *pspec)
{
	LmFeaturePingPriv *priv;

	priv = GET_PRIV (object);

	switch (param_id) {
	case PROP_RATE:
		g_value_set_uint (value, priv->keep_alive_rate);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
		break;
	};
}

static void
feature_ping_set_property (GObject      *object,
                           guint         param_id,
                           const GValue *value,
                           GParamSpec   *pspec)
{
	LmFeaturePingPriv *priv;

	priv = GET_PRIV (object);

	switch (param_id) {
        case PROP_CONNECTION:
                priv->connection = g_value_get_pointer (value);
                break;
        case PROP_RATE:
		priv->keep_alive_rate = g_value_get_uint (value);
                /* Restart the pings */
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
		break;
	};
}

static LmHandlerResult
feature_ping_keep_alive_reply (LmMessageHandler *handler,
                               LmConnection     *connection,
                               LmMessage        *m,
                               gpointer          user_data)
{
        LmFeaturePingPriv *priv;

        priv = GET_PRIV (user_data);

        priv->keep_alive_counter = 0;
	
        return LM_HANDLER_RESULT_REMOVE_MESSAGE;
}

static gboolean
feature_ping_send_keep_alive (LmFeaturePing *fp)
{
        LmFeaturePingPriv *priv;
	LmMessage         *ping;
        LmMessageNode     *ping_node;
	LmMessageHandler  *keep_alive_handler;
        gchar             *server;

        priv = GET_PRIV (fp);

        priv->keep_alive_counter++;
	if (priv->keep_alive_counter > 3) {
                g_signal_emit (fp, signals[TIMED_OUT], 0);
                /* FIXME */
#if 0 /* Should be moved to signal callback in LmConnection */
                connection_do_close (connection);
		connection_signal_disconnect (connection,
					      LM_DISCONNECT_REASON_PING_TIME_OUT);
#endif

	}

        server = _lm_connection_get_server (priv->connection);
	
        ping = lm_message_new_with_sub_type (server,
					     LM_MESSAGE_TYPE_IQ,
					     LM_MESSAGE_SUB_TYPE_GET);

	ping_node = lm_message_node_add_child (ping->node, "ping", NULL);

	lm_message_node_set_attribute (ping_node, "xmlns", XMPP_NS_PING);

	keep_alive_handler =
		lm_message_handler_new (feature_ping_keep_alive_reply,
                                        fp,
				        FALSE);

        if (!lm_connection_send_with_reply (priv->connection,
					    ping,
					    keep_alive_handler,
					    NULL)) {
                lm_verbose ("Error while sending XMPP ping!\n");
	}

	lm_message_handler_unref (keep_alive_handler);
	lm_message_unref (ping);
	g_free (server);

	return TRUE;
}


void
lm_feature_ping_start (LmFeaturePing *fp)
{
        LmFeaturePingPriv *priv;

        g_return_if_fail (LM_IS_FEATURE_PING (fp));

        priv = GET_PRIV (fp);

        if (priv->keep_alive_source) {
                lm_feature_ping_stop (fp);
	}

        if (priv->keep_alive_rate > 0) {
                priv->keep_alive_counter = 0;
                priv->keep_alive_source =
                        lm_misc_add_timeout (_lm_connection_get_context (priv->connection),
					     priv->keep_alive_rate * 1000,
					     (GSourceFunc) feature_ping_send_keep_alive,
                                             fp);
	}
}

void
lm_feature_ping_stop (LmFeaturePing *fp)
{
        LmFeaturePingPriv *priv;

        g_return_if_fail (LM_IS_FEATURE_PING (fp));

        priv = GET_PRIV (fp);

        if (priv->keep_alive_source) {
		g_source_destroy (priv->keep_alive_source);
	}

        priv->keep_alive_source = NULL;
}