+/*
+ * This file is part of sharing-plugin-gallery2
+ *
+ * Copyright (C) 2009 Heikki Kallasjoki. All rights reserved.
+ * Copyright (C) 2008-2009 Nokia Corporation. All rights reserved.
+ *
+ * This code is licensed under a MIT-style license, that can be
+ * found in the file called "COPYING" in the root directory.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <glib.h>
+#include <osso-log.h>
+#include <sharing-http.h>
+#include "gallery2.h"
+
+/**
+ * gallery2_login:
+ * @con: Connection to use
+ * @urlbase: Base URL to the Gallery 2 site
+ * @username: User name
+ * @password: Password
+ * @cookies: Output parameter for any cookies set.
+ * @auth: Output paremeter for the authentication token.
+ *
+ * Logs in to the Gallery 2 service.
+ *
+ * Returns: Validation result.
+ */
+SharingPluginInterfaceAccountValidateResult
+gallery2_login (ConIcConnection* con,
+ const gchar* urlbase, const gchar* username, const gchar* password,
+ GHashTable** cookies, gchar** auth)
+{
+ SharingPluginInterfaceAccountValidateResult ret = SHARING_ACCOUNT_VALIDATE_SUCCESS;
+
+ SharingHTTP* http = sharing_http_new ();
+
+ *cookies = 0;
+ *auth = 0;
+
+ /* Do the login request */
+
+ gchar* url = g_strdup_printf("%s/main.php?g2_controller=remote:GalleryRemote&"
+ "g2_form[cmd]=login&g2_form[protocol_version]=2.0&"
+ "g2_form[uname]=%s&g2_form[password]=%s",
+ urlbase, username, password);
+
+ sharing_http_set_connection (http, con);
+ SharingHTTPRunResponse res = sharing_http_run (http, url);
+
+ g_free (url);
+ url = 0;
+
+ /* Parse the response */
+
+ if (res == SHARING_HTTP_RUNRES_SUCCESS && sharing_http_get_res_code (http) == 200)
+ {
+ /* Split response into lines */
+
+ gchar** lines = 0;
+
+ {
+ gsize content_len = 0;
+ const gchar* content = sharing_http_get_res_content (http, &content_len);
+ gchar* c = g_strndup (content, content_len); /* force \0 termination */
+ lines = g_strsplit_set (c, "\r\n", 0);
+ g_free (c);
+ }
+
+ /* Process the lines */
+
+ *cookies = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+ gboolean in_body = FALSE;
+
+ for (gchar** p = lines; *p; p++)
+ {
+ gchar* line = *p;
+
+ if (!in_body)
+ {
+ if (g_ascii_strncasecmp (line, "Set-Cookie:", 11) == 0)
+ {
+ /* Extract the key=value part of the cookie */
+
+ gchar** data = 0;
+
+ {
+ gchar* c = g_strchug (line+11); /* start of cookie data */
+
+ char* end = strchr (c, ';');
+ if (end) *end = 0;
+
+ data = g_strsplit (c, "=", 2);
+ }
+
+ if (!data[0] || !data[1])
+ {
+ /* Bad Set-Cookie: header, ignore */
+ g_strfreev (data);
+ continue;
+ }
+
+ /* Insert into our table */
+
+ g_hash_table_replace (*cookies, data[0], data[1]);
+ g_free (data); /* not g_strfreev! strings still used in "cookies" */
+
+ continue;
+ }
+
+ if (g_str_has_prefix (line, "#__GR2PROTO__"))
+ {
+ in_body = TRUE;
+ continue;
+ }
+ }
+ else
+ {
+ /* Split key=value into fields */
+
+ gchar* value = strchr (line, '=');
+ if (!value) continue;
+ *value = 0;
+ value++;
+
+ /* Process the necessary parts */
+
+ if (strcmp (line, "status") == 0 && strcmp (value, "0") != 0)
+ {
+ ULOG_ERR_L ("Gallery 2 login auth failed\n");
+ ret = SHARING_ACCOUNT_VALIDATE_FAILED;
+ break;
+ }
+
+ if (strcmp (line, "auth_token") == 0)
+ {
+ *auth = g_strdup (value);
+ continue;
+ }
+ }
+ }
+
+ g_strfreev (lines);
+ }
+ else
+ {
+ ULOG_ERR_L ("Gallery 2 login connection failed\n");
+ ret = SHARING_ACCOUNT_VALIDATE_ERROR_CONNECTION;
+ }
+
+ if (ret != SHARING_ACCOUNT_VALIDATE_SUCCESS)
+ {
+ if (*cookies) g_hash_table_unref (*cookies);
+ if (*auth) g_free (auth);
+ *cookies = 0;
+ *auth = 0;
+ }
+
+ sharing_http_unref (http);
+ return ret;
+}
+
+/**
+ * gallery2_lookup_album:
+ * @con: Connection to use
+ * @urlbase: Base URL to the Gallery 2 site
+ * @albumpath: Slash-separated path to album
+ * @album: Output parameter to hold the album ID
+ * @cookies: Cookies from gallery2_login.
+ * @auth: Authentication token from gallery2_login.
+ *
+ * Retrieves the album id based on an album path.
+ */
+SharingPluginInterfaceAccountValidateResult
+gallery2_lookup_album (ConIcConnection* con,
+ const gchar* urlbase, const gchar* albumpath, gchar** album,
+ GHashTable* cookies, gchar* auth)
+{
+ SharingPluginInterfaceAccountValidateResult ret = SHARING_ACCOUNT_VALIDATE_ERROR_UNKNOWN;
+
+ SharingHTTP* http = sharing_http_new ();
+
+ /* Prepare and send the request */
+
+ gchar* url = g_strdup_printf("%s/main.php?g2_controller=remote:GalleryRemote%s%s&"
+ "g2_form[cmd]=fetch-albums-prune&g2_form[protocol_version]=2.2&g2_form[no_perms]=yes",
+ urlbase,
+ auth ? "&g2_authToken=" : "", auth ? auth : "");
+
+ if (cookies)
+ {
+ GHashTableIter iter;
+ gpointer key, value;
+ g_hash_table_iter_init (&iter, cookies);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ gchar* hdr = g_strdup_printf("Cookie: %s=%s", (gchar*)key, (gchar*)value);
+ sharing_http_add_req_header_line (http, hdr);
+ g_free (hdr);
+ }
+ }
+
+ sharing_http_set_connection (http, con);
+ SharingHTTPRunResponse res = sharing_http_run (http, url);
+
+ g_free (url);
+ url = 0;
+
+ /* Parse the response into an album map. */
+
+ GHashTable* album_names = 0; /* map string (display-name) -> GSList [ string (url-name) ] */
+ GHashTable* album_parent = 0; /* map string (url-name) -> string (url-name) */
+ gchar* album_root = 0; /* root album url-name */
+ gboolean valid = FALSE; /* true if the maps are usable */
+ char** lines = 0; /* raw data (response lines) */
+
+ if (res == SHARING_HTTP_RUNRES_SUCCESS && sharing_http_get_res_code (http) == 200)
+ {
+ {
+ gsize content_len = 0;
+ const gchar* content = sharing_http_get_res_body (http, &content_len);
+ gchar* c = g_strndup (content, content_len); /* force \0 termination */
+ lines = g_strsplit_set (c, "\r\n", 0);
+ g_free (c);
+ }
+
+ gboolean in_body = FALSE;
+ gchar* current_ref_num = 0;
+ gchar* current_url_name = 0;
+
+ for (gchar** p = lines; *p; p++)
+ {
+ gchar* line = *p;
+
+ if (!in_body)
+ {
+ if (g_str_has_prefix (line, "#__GR2PROTO__"))
+ {
+ album_names = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)g_slist_free);
+ album_parent = g_hash_table_new (g_str_hash, g_str_equal);
+ in_body = TRUE;
+ }
+ continue;
+ }
+
+ gchar* value = strchr (line, '=');
+ if (!value) continue;
+ *value = 0;
+ value++;
+
+ if (strcmp (line, "status") == 0)
+ {
+ if (strcmp (value, "0") == 0)
+ valid = TRUE;
+ continue;
+ }
+ else if (g_str_has_prefix (line, "album.name."))
+ {
+ current_ref_num = line + 11;
+ current_url_name = value;
+ }
+ else if (g_str_has_prefix (line, "album.title."))
+ {
+ if (!current_ref_num || strcmp (current_ref_num, line + 12) != 0)
+ continue;
+ GSList* others = g_hash_table_lookup (album_names, value);
+ if (others) g_hash_table_steal (album_names, value);
+ g_hash_table_insert (album_names, value, g_slist_prepend (others, current_url_name));
+ }
+ else if (g_str_has_prefix (line, "album.parent."))
+ {
+ if (!current_ref_num || strcmp (current_ref_num, line + 13) != 0)
+ continue;
+ g_hash_table_insert (album_parent, current_url_name, value);
+ if (strcmp (value, "0") == 0)
+ album_root = current_url_name;
+ }
+ }
+ }
+ else
+ ret = SHARING_ACCOUNT_VALIDATE_ERROR_CONNECTION;
+
+ sharing_http_unref (http);
+
+ /* Find the album we are interested in. */
+
+ *album = 0;
+
+ if (album_names && album_parent && album_root && valid)
+ {
+ gchar* current_album = album_root;
+ gboolean seen_parents = FALSE; /* for the root special case */
+ gboolean found_final = FALSE; /* to make sure the last real component is found */
+
+ gchar** components = g_strsplit (albumpath, "/", 0);
+
+ for (gchar** p = components; *p; p++)
+ {
+ if (!**p) continue; /* ignore empty path components */
+ found_final = FALSE; /* this component needs to be found */
+ GSList* candidates = g_hash_table_lookup (album_names, *p);
+ /* bad case: no candidates at all */
+ if (!candidates) break;
+ /* special case 1: if only one candidate and no unseen parents, choose that */
+ if (!seen_parents && !g_slist_next (candidates))
+ {
+ found_final = TRUE;
+ current_album = candidates->data;
+ continue;
+ }
+ /* general case: find a candidate with an acceptable parent */
+ while (candidates)
+ {
+ gchar* parent = g_hash_table_lookup (album_parent, candidates->data);
+ /* suitable parents: current_album, or (if no specified parents) null or 0 (explicit root case) */
+ if ((parent && strcmp (parent, current_album) == 0)
+ || (!seen_parents && (!parent || strcmp (parent, "0") == 0)))
+ {
+ found_final = TRUE;
+ current_album = candidates->data;
+ break;
+ }
+ candidates = g_slist_next (candidates); /* try next */
+ }
+ }
+
+ g_strfreev (components);
+
+ if (found_final)
+ {
+ *album = g_strdup(current_album);
+ ret = SHARING_ACCOUNT_VALIDATE_SUCCESS;
+ }
+ }
+
+ if (album_names) g_hash_table_unref (album_names);
+ if (album_parent) g_hash_table_unref (album_parent);
+ g_strfreev (lines);
+
+ return ret;
+}
+
+/* gallery2_send callback helper declarations */
+
+struct gallery2_send_record
+{
+ SharingTransfer* transfer;
+ gdouble progress_start;
+ gdouble progress_end;
+ guint64 media_bytes;
+ gboolean* dms;
+};
+
+gboolean gallery2_send_callback (SharingHTTP* http, guint64 bytes_sent, gpointer user_data);
+
+/**
+ * gallery2_send:
+ * @con: Connection to use
+ * @transfer: Sharing transfer object
+ * @progress_start: Initial state of progress meter
+ * @progress_end: Desired final state of progress meter
+ * @dms: Dead man's switch
+ * @media: Media item to send
+ * @urlbase: Base URL to the Gallery 2 site
+ * @album: Album ID from gallery2_lookup_album.
+ * @cookies: Cookies from gallery2_login.
+ * @auth: Authentication token from gallery2_login.
+ *
+ * Sends a media item to a Gallery 2 service.
+ *
+ * Returns: Send result.
+ */
+SharingPluginInterfaceSendResult
+gallery2_send (ConIcConnection* con,
+ SharingTransfer* transfer, gdouble progress_start, gdouble progress_end, gboolean *dms,
+ SharingEntryMedia* media,
+ const gchar* urlbase, const gchar* album, GHashTable* cookies, gchar* auth)
+{
+ struct gallery2_send_record rec = {
+ .transfer = transfer,
+ .progress_start = progress_start,
+ .progress_end = progress_end,
+ .media_bytes = 0,
+ .dms = dms
+ };
+
+ SharingPluginInterfaceSendResult ret = SHARING_SEND_SUCCESS;
+
+ SharingHTTP* http = sharing_http_new ();
+
+ /* Prepare and send the request */
+
+ /* gchar* album = "1652"; */ /* TODO: get from UI/login */
+
+ gchar* media_title = sharing_entry_media_get_title (media);
+ gchar* media_mime = sharing_entry_media_get_mime (media);
+ gchar* media_filename = sharing_entry_media_get_filename (media);
+
+ const gchar* desc = sharing_entry_media_get_desc (media);
+
+ gchar* url = g_strdup_printf("%s/main.php?g2_controller=remote:GalleryRemote%s%s&"
+ "g2_form[cmd]=add-item&g2_form[protocol_version]=2.0&"
+ "g2_form[set_albumName]=%s&g2_form[caption]=%s"
+ "%s%s%s%s",
+ urlbase,
+ auth ? "&g2_authToken=" : "", auth ? auth : "",
+ album, media_title,
+ desc ? "&g2_form[extrafield.Summary]=" : "", desc ? desc : "",
+ desc ? "&g2_form[extrafield.Description]=" : "", desc ? desc : "");
+
+ if (cookies)
+ {
+ GHashTableIter iter;
+ gpointer key, value;
+ g_hash_table_iter_init (&iter, cookies);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ gchar* hdr = g_strdup_printf("Cookie: %s=%s", (gchar*)key, (gchar*)value);
+ sharing_http_add_req_header_line (http, hdr);
+ g_free (hdr);
+ }
+ }
+
+ sharing_http_add_req_multipart_file_with_filename (http,
+ "g2_userfile",
+ sharing_entry_media_get_localpath (media),
+ media_mime ? media_mime : "image/jpeg",
+ media_filename ? media_filename : "unknown.jpg");
+
+ g_free (media_title);
+ g_free (media_mime);
+ g_free (media_filename);
+
+ media_title = media_mime = media_filename = 0;
+
+ rec.media_bytes = sharing_entry_media_get_size (media);
+ sharing_http_set_progress_callback (http, gallery2_send_callback, &rec);
+
+ *dms = FALSE;
+ sharing_http_set_connection (http, con);
+ SharingHTTPRunResponse res = sharing_http_run (http, url);
+ *dms = FALSE;
+
+ g_free (url);
+ url = 0;
+
+ /* Parse the response */
+
+ if (res == SHARING_HTTP_RUNRES_SUCCESS && sharing_http_get_res_code (http) == 200)
+ {
+ gchar** lines = 0;
+
+ {
+ gsize content_len = 0;
+ const gchar* content = sharing_http_get_res_body (http, &content_len);
+ gchar* c = g_strndup (content, content_len); /* force \0 termination */
+ lines = g_strsplit_set (c, "\r\n", 0);
+ g_free (c);
+ }
+
+ gboolean in_body = FALSE;
+ ret = SHARING_SEND_ERROR_UNKNOWN;
+
+ for (gchar** p = lines; *p; p++)
+ {
+ gchar* line = *p;
+
+ if (!in_body)
+ {
+ if (g_str_has_prefix (line, "#__GR2PROTO__"))
+ in_body = TRUE;
+ continue;
+ }
+
+ gchar* value = strchr (line, '=');
+ if (!value) continue;
+ *value = 0;
+ value++;
+
+ if (strcmp (line, "status") == 0)
+ {
+ if (strcmp (value, "0") == 0)
+ ret = SHARING_SEND_SUCCESS;
+ break;
+ }
+ }
+
+ g_strfreev (lines);
+ }
+ else if (res == SHARING_HTTP_RUNRES_CANCELLED)
+ ret = SHARING_SEND_CANCELLED;
+ else
+ ret = SHARING_SEND_ERROR_CONNECTION;
+
+ sharing_http_unref (http);
+
+ *dms = FALSE;
+ sharing_transfer_set_progress (transfer, progress_end);
+
+ return ret;
+}
+
+/* gallery2_send callback implementation */
+
+gboolean gallery2_send_callback (SharingHTTP* http, guint64 bytes_sent, gpointer user_data)
+{
+ struct gallery2_send_record* rec = user_data;
+
+ if (!sharing_transfer_continue (rec->transfer))
+ return FALSE;
+
+ *rec->dms = FALSE;
+
+ gdouble progress = (rec->progress_start + rec->progress_end) / 2.0;
+
+ if (rec->media_bytes)
+ {
+ if (bytes_sent >= rec->media_bytes)
+ progress = rec->progress_end;
+ else
+ progress = rec->progress_start + (bytes_sent / (gdouble)rec->media_bytes) * (rec->progress_end - rec->progress_start);
+ }
+
+ sharing_transfer_set_progress (rec->transfer, progress);
+
+ return TRUE;
+}