Merge local development branch
[milk] / src / milk-auth.c
diff --git a/src/milk-auth.c b/src/milk-auth.c
new file mode 100644 (file)
index 0000000..96155c5
--- /dev/null
@@ -0,0 +1,396 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA  02110-1301  USA
+ *
+ * Authors: Travis Reitter <treitter@gmail.com>
+ */
+
+#include <config.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <hildon/hildon.h>
+
+#include <rtm-glib/rtm-error.h>
+#include <rtm-glib/rtm-glib.h>
+
+#include "milk-auth.h"
+#include "milk-dialogs.h"
+
+G_DEFINE_TYPE (MilkAuth, milk_auth, G_TYPE_OBJECT);
+
+/* less expensive than G_TYPE_INSTANCE_GET_PRIVATE */
+#define MILK_AUTH_PRIVATE(o) ((MILK_AUTH ((o)))->priv)
+
+#define RTM_API_KEY "81f5c6c904aeafbbc914d9845d250ea8"
+#define RTM_SHARED_SECRET "b08b15419378f913"
+
+struct _MilkAuthPrivate
+{
+        RtmGlib *rtm_glib;
+        char *api_key;
+        char *shared_secret;
+        char *frob;
+        MilkAuthState state;
+};
+
+enum {
+        PROP_API_KEY = 1,
+        PROP_SHARED_SECRET,
+        PROP_STATE,
+};
+
+static MilkAuth *default_auth = NULL;
+
+
+MilkAuthState
+milk_auth_get_state (MilkAuth *auth)
+{
+        g_return_val_if_fail (auth, MILK_AUTH_STATE_DISCONNECTED);
+        g_return_val_if_fail (MILK_IS_AUTH (auth),
+                        MILK_AUTH_STATE_DISCONNECTED);
+
+        return MILK_AUTH_PRIVATE (auth)->state;
+}
+
+static void
+milk_auth_get_property (GObject    *object,
+                        guint       property_id,
+                        GValue     *value,
+                        GParamSpec *pspec)
+{
+        MilkAuthPrivate *priv = MILK_AUTH_PRIVATE (object);
+
+        switch (property_id)
+        {
+                case PROP_API_KEY:
+                        g_value_set_string (value, priv->api_key);
+                break;
+
+                case PROP_SHARED_SECRET:
+                        g_value_set_string (value, priv->shared_secret);
+                break;
+
+                case PROP_STATE:
+                        g_value_set_int (value, priv->state);
+                break;
+
+                default:
+                        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id,
+                                        pspec);
+        }
+}
+
+static void
+milk_auth_set_property (GObject      *object,
+                        guint         property_id,
+                        const GValue *value,
+                        GParamSpec   *pspec)
+{
+        MilkAuthPrivate *priv;
+        MilkAuth *auth;
+
+        auth = MILK_AUTH (object);
+        priv = MILK_AUTH_PRIVATE (auth);
+
+        switch (property_id)
+        {
+                case PROP_API_KEY:
+                        priv->api_key = g_value_dup_string (value);
+                break;
+
+                case PROP_SHARED_SECRET:
+                        priv->shared_secret = g_value_dup_string (value);
+                break;
+
+                case PROP_STATE:
+                        priv->state = g_value_get_int (value);
+                break;
+
+                default:
+                        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id,
+                                        pspec);
+        }
+}
+
+static void
+auth_response_cb (GtkWidget *dialog,
+                  int        response,
+                  MilkAuth  *auth)
+
+{
+        MilkAuthPrivate *priv;
+        GError *error = NULL;
+        gchar *auth_token;
+        gchar *username;
+        MilkAuthState previous_state;
+
+        priv = MILK_AUTH_PRIVATE (auth);
+
+        previous_state = priv->state;
+        auth_token = rtm_glib_auth_get_token (priv->rtm_glib, priv->frob,
+                        &error);
+        if (error != NULL) {
+                g_warning ("%s", rtm_error_get_message (error));
+                goto auth_response_cb_error_OUT;
+        }
+
+        if (!rtm_glib_auth_check_token (priv->rtm_glib, auth_token, NULL)) {
+                g_warning ("auth_token not valid!\n");
+                goto auth_response_cb_error_OUT;
+        }
+        if (error != NULL) {
+                g_warning ("%s", rtm_error_get_message (error));
+                goto auth_response_cb_error_OUT;
+        }
+        username = rtm_glib_test_login (priv->rtm_glib, auth_token, &error);
+
+        g_free (auth_token);
+
+        if (error != NULL) {
+                g_warning ("%s", rtm_error_get_message (error));
+                goto auth_response_cb_error_OUT;
+        }
+
+        priv->state = MILK_AUTH_STATE_CONNECTED;
+        goto auth_response_cb_OUT;
+
+auth_response_cb_error_OUT:
+        priv->state = MILK_AUTH_STATE_DISCONNECTED;
+        /* FIXME: make it possible to re-try, etc. */
+        gtk_main_quit ();
+
+auth_response_cb_OUT:
+        if (priv->state != previous_state)
+                g_object_notify (G_OBJECT (auth), "state");
+
+        gtk_widget_destroy (dialog);
+}
+
+GList *
+milk_auth_get_tasks (MilkAuth    *auth,
+                     const char  *last_sync,
+                     GError     **error)
+{
+        MilkAuthPrivate *priv;
+        GList *rtm_tasks;
+
+        g_return_val_if_fail (auth, NULL);
+        g_return_val_if_fail (MILK_IS_AUTH (auth), NULL);
+        g_return_val_if_fail (
+                        milk_auth_get_state (auth) == MILK_AUTH_STATE_CONNECTED,
+                        NULL);
+
+        priv = MILK_AUTH_PRIVATE (auth);
+
+        /* FIXME: cache this */
+        rtm_tasks = rtm_glib_tasks_get_list (priv->rtm_glib, NULL, NULL,
+                        (char*) last_sync, error);
+
+        return rtm_tasks;
+}
+
+char*
+milk_auth_timeline_create (MilkAuth  *auth,
+                           GError   **error)
+{
+        MilkAuthPrivate *priv;
+
+        g_return_val_if_fail (MILK_IS_AUTH (auth), NULL);
+
+        priv = MILK_AUTH_PRIVATE (auth);
+
+        return rtm_glib_timelines_create (priv->rtm_glib, error);
+}
+
+RtmTask*
+milk_auth_task_add (MilkAuth    *auth,
+                    char        *timeline,
+                    const char  *name,
+                    GError     **error)
+{
+        MilkAuthPrivate *priv;
+
+        g_return_val_if_fail (MILK_IS_AUTH (auth), NULL);
+
+        priv = MILK_AUTH_PRIVATE (auth);
+
+        /* XXX: this uses Smart Add parsing; make this user-settable? */
+        /* FIXME: the cast to char* is actually a bug in the rtm-glib API */
+        return rtm_glib_tasks_add (priv->rtm_glib, timeline, (char*) name, NULL,
+                        TRUE, error);
+}
+
+char*
+milk_auth_task_complete (MilkAuth  *auth,
+                         char      *timeline,
+                         RtmTask   *task,
+                         GError   **error)
+{
+        MilkAuthPrivate *priv;
+
+        g_return_val_if_fail (MILK_IS_AUTH (auth), NULL);
+
+        priv = MILK_AUTH_PRIVATE (auth);
+
+        return rtm_glib_tasks_complete (priv->rtm_glib, timeline, task, error);
+}
+
+char*
+milk_auth_task_delete (MilkAuth  *auth,
+                       char      *timeline,
+                       RtmTask   *task,
+                       GError   **error)
+{
+        MilkAuthPrivate *priv;
+
+        g_return_val_if_fail (MILK_IS_AUTH (auth), NULL);
+
+        priv = MILK_AUTH_PRIVATE (auth);
+
+        return rtm_glib_tasks_delete (priv->rtm_glib, timeline, task, error);
+}
+
+void
+milk_auth_log_in (MilkAuth *auth)
+{
+        MilkAuthPrivate *priv;
+        GError *error = NULL;
+        gchar *url;
+        GtkDialog *dialog;
+
+        g_return_if_fail (auth);
+        g_return_if_fail (MILK_IS_AUTH (auth));
+
+        priv = MILK_AUTH_PRIVATE (auth);
+
+        if (rtm_glib_test_echo (priv->rtm_glib, &error)) {
+                g_print ("Test echo OK!\n");
+        } else {
+                g_print ("Test echo FAIL!\n");
+                return;
+        }
+        if (error != NULL) {
+                g_error ("%s", rtm_error_get_message (error));
+                return;
+        }
+
+        /* FIXME: relocate this */
+        if (priv->frob)
+                g_free (priv->frob);
+
+        priv->frob = rtm_glib_auth_get_frob (priv->rtm_glib, &error);
+        if (error != NULL) {
+                g_error ("%s", rtm_error_get_message (error));
+                return;
+        }
+        g_print ("Frob: %s\n", priv->frob);
+
+        url = rtm_glib_auth_get_login_url (priv->rtm_glib, priv->frob,
+                        "delete");
+        g_print ("URL: %s\n", url);
+
+        dialog = milk_dialogs_auth_prompt (NULL, url);
+
+        g_signal_connect (dialog, "response", G_CALLBACK (auth_response_cb),
+                        auth);
+}
+
+static void
+milk_auth_constructed (GObject *object)
+{
+        MilkAuthPrivate *priv = MILK_AUTH_PRIVATE (object);
+
+        priv->rtm_glib = rtm_glib_new (priv->api_key, priv->shared_secret);
+}
+
+static void
+milk_auth_finalize (GObject *object)
+{
+        MilkAuthPrivate *priv = MILK_AUTH_PRIVATE (object);
+
+        g_object_unref (priv->rtm_glib);
+
+        g_free (priv->api_key);
+        g_free (priv->shared_secret);
+        g_free (priv->frob);
+}
+
+static void
+milk_auth_class_init (MilkAuthClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+        g_type_class_add_private (klass, sizeof (MilkAuthPrivate));
+
+        object_class->get_property = milk_auth_get_property;
+        object_class->set_property = milk_auth_set_property;
+        object_class->constructed = milk_auth_constructed;
+        object_class->finalize = milk_auth_finalize;
+
+        /* FIXME: make these read-only */
+        g_object_class_install_property
+                (object_class,
+                 PROP_API_KEY,
+                 g_param_spec_string
+                         ("api-key",
+                          "API authentication key",
+                          "Milk's API authentication key.",
+                          RTM_API_KEY,
+                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                          G_PARAM_STATIC_STRINGS));
+
+        g_object_class_install_property
+                (object_class,
+                 PROP_SHARED_SECRET,
+                 g_param_spec_string
+                         ("shared-secret",
+                          "Shared secret",
+                          "Milk's shared secret with the server.",
+                          RTM_SHARED_SECRET,
+                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                          G_PARAM_STATIC_STRINGS));
+
+        g_object_class_install_property
+                (object_class,
+                 PROP_STATE,
+                 g_param_spec_int
+                         ("state",
+                          "Authentication state",
+                          "Authentication/connection state with the server.",
+                          0, (NUM_MILK_AUTH_STATES-1),
+                          MILK_AUTH_STATE_DISCONNECTED,
+                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                          G_PARAM_STATIC_STRINGS));
+}
+
+static void
+milk_auth_init (MilkAuth *self)
+{
+        MilkAuthPrivate *priv;
+
+        self->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (
+                        self, MILK_TYPE_AUTH, MilkAuthPrivate);
+}
+
+MilkAuth*
+milk_auth_get_default ()
+{
+        if (!default_auth)
+                default_auth = g_object_new (MILK_TYPE_AUTH, NULL);
+
+        return default_auth;
+}