/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Pix
*
* Copyright (C) 2010 Free Software Foundation, Inc.
*
* 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, see .
*/
#include
#include
#include
#include
#include "flickr-account.h"
#include "flickr-consumer.h"
#include "flickr-photo.h"
#include "flickr-photoset.h"
#include "flickr-service.h"
#define IMAGES_PER_PAGE 500
#define RESPONSE_FORMAT "rest"
enum {
PROP_0,
PROP_SERVER
};
typedef struct {
FlickrPrivacy privacy_level;
FlickrSafety safety_level;
gboolean hidden;
int max_width;
int max_height;
GList *file_list;
GCancellable *cancellable;
GAsyncReadyCallback callback;
gpointer user_data;
GList *current;
goffset total_size;
goffset uploaded_size;
goffset wrote_body_data_size;
int n_files;
int uploaded_files;
GList *ids;
} PostPhotosData;
static void
post_photos_data_free (PostPhotosData *post_photos)
{
if (post_photos == NULL)
return;
_g_string_list_free (post_photos->ids);
_g_object_unref (post_photos->cancellable);
_g_object_list_unref (post_photos->file_list);
g_free (post_photos);
}
typedef struct {
FlickrPhotoset *photoset;
GList *photo_ids;
GCancellable *cancellable;
GAsyncReadyCallback callback;
gpointer user_data;
int n_files;
GList *current;
int n_current;
} AddPhotosData;
static void
add_photos_data_free (AddPhotosData *add_photos)
{
if (add_photos == NULL)
return;
_g_object_unref (add_photos->photoset);
_g_string_list_free (add_photos->photo_ids);
_g_object_unref (add_photos->cancellable);
g_free (add_photos);
}
/* -- flickr_service -- */
struct _FlickrServicePrivate {
PostPhotosData *post_photos;
AddPhotosData *add_photos;
FlickrServer *server;
OAuthConsumer *consumer;
GChecksum *checksum;
char *frob;
};
G_DEFINE_TYPE_WITH_CODE (FlickrService,
flickr_service,
OAUTH_TYPE_SERVICE,
G_ADD_PRIVATE (FlickrService))
static void
flickr_service_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
FlickrService *self;
self = FLICKR_SERVICE (object);
switch (property_id) {
case PROP_SERVER:
self->priv->server = g_value_get_pointer (value);
self->priv->consumer = oauth_consumer_copy (&flickr_consumer);
self->priv->consumer->request_token_url = self->priv->server->request_token_url;
self->priv->consumer->access_token_url = self->priv->server->access_token_url;
self->priv->consumer->consumer_key = self->priv->server->consumer_key;
self->priv->consumer->consumer_secret = self->priv->server->consumer_secret;
g_object_set (self, "consumer", self->priv->consumer, NULL);
break;
default:
break;
}
}
static void
flickr_service_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
FlickrService *self;
self = FLICKR_SERVICE (object);
switch (property_id) {
case PROP_SERVER:
g_value_set_pointer (value, self->priv->server);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
flickr_service_finalize (GObject *object)
{
FlickrService *self;
self = FLICKR_SERVICE (object);
post_photos_data_free (self->priv->post_photos);
add_photos_data_free (self->priv->add_photos);
oauth_consumer_free (self->priv->consumer);
g_checksum_free (self->priv->checksum);
g_free (self->priv->frob);
G_OBJECT_CLASS (flickr_service_parent_class)->finalize (object);
}
/* -- flickr_service_old_auth_get_frob -- */
static void
flickr_service_old_auth_add_api_sig (FlickrService *self,
GHashTable *data_set)
{
GList *keys;
GList *scan;
g_hash_table_insert (data_set, "api_key", (gpointer) self->priv->server->consumer_key);
if (oauth_service_get_token (OAUTH_SERVICE (self)) != NULL)
g_hash_table_insert (data_set, "auth_token", (gpointer) oauth_service_get_token (OAUTH_SERVICE (self)));
g_checksum_reset (self->priv->checksum);
g_checksum_update (self->priv->checksum, (guchar *) self->priv->server->consumer_secret, -1);
keys = g_hash_table_get_keys (data_set);
keys = g_list_sort (keys, (GCompareFunc) strcmp);
for (scan = keys; scan; scan = scan->next) {
char *key = scan->data;
g_checksum_update (self->priv->checksum, (guchar *) key, -1);
g_checksum_update (self->priv->checksum, g_hash_table_lookup (data_set, key), -1);
}
g_hash_table_insert (data_set, "api_sig", (gpointer) g_checksum_get_string (self->priv->checksum));
g_list_free (keys);
}
static void
flickr_service_old_auth_get_frob_ready_cb (SoupSession *session,
SoupMessage *msg,
gpointer user_data)
{
FlickrService *self = user_data;
GTask *task;
SoupBuffer *body;
DomDocument *doc = NULL;
GError *error = NULL;
g_free (self->priv->frob);
self->priv->frob = NULL;
task = _web_service_get_task (WEB_SERVICE (self));
body = soup_message_body_flatten (msg->response_body);
if (flickr_utils_parse_response (body, &doc, &error)) {
DomElement *root;
DomElement *child;
root = DOM_ELEMENT (doc)->first_child;
for (child = root->first_child; child; child = child->next_sibling)
if (g_strcmp0 (child->tag_name, "frob") == 0)
self->priv->frob = g_strdup (dom_element_get_inner_text (child));
if (self->priv->frob == NULL)
g_task_return_error (task, g_error_new_literal (WEB_SERVICE_ERROR, WEB_SERVICE_ERROR_GENERIC, _("Unknown error")));
else
g_task_return_boolean (task, TRUE);
g_object_unref (doc);
}
else
g_task_return_error (task, error);
soup_buffer_free (body);
}
static void
flickr_service_old_auth_get_frob (FlickrService *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GHashTable *data_set;
SoupMessage *msg;
oauth_service_set_token (OAUTH_SERVICE (self), NULL);
data_set = g_hash_table_new (g_str_hash, g_str_equal);
g_hash_table_insert (data_set, "method", "flickr.auth.getFrob");
flickr_service_old_auth_add_api_sig (self, data_set);
msg = soup_form_request_new_from_hash ("GET", self->priv->server->rest_url, data_set);
_web_service_send_message (WEB_SERVICE (self),
msg,
cancellable,
callback,
user_data,
flickr_service_old_auth_get_frob,
flickr_service_old_auth_get_frob_ready_cb,
self);
g_hash_table_destroy (data_set);
}
static gboolean
flickr_service_old_auth_get_frob_finish (FlickrService *self,
GAsyncResult *result,
GError **error)
{
return g_task_propagate_boolean (G_TASK (result), error);
}
/* -- flickr_service_old_auth_get_token -- */
static void
flickr_service_old_auth_get_token_ready_cb (SoupSession *session,
SoupMessage *msg,
gpointer user_data)
{
FlickrService *self = user_data;
GTask *task;
SoupBuffer *body;
DomDocument *doc = NULL;
GError *error = NULL;
task = _web_service_get_task (WEB_SERVICE (self));
body = soup_message_body_flatten (msg->response_body);
if (flickr_utils_parse_response (body, &doc, &error)) {
DomElement *response;
DomElement *auth;
const char *token;
token = NULL;
response = DOM_ELEMENT (doc)->first_child;
for (auth = response->first_child; auth; auth = auth->next_sibling) {
if (g_strcmp0 (auth->tag_name, "auth") == 0) {
DomElement *node;
for (node = auth->first_child; node; node = node->next_sibling) {
if (g_strcmp0 (node->tag_name, "token") == 0) {
token = dom_element_get_inner_text (node);
oauth_service_set_token (OAUTH_SERVICE (self), token);
break;
}
}
for (node = auth->first_child; node; node = node->next_sibling) {
if (g_strcmp0 (node->tag_name, "user") == 0) {
FlickrAccount *account;
account = g_object_new (FLICKR_TYPE_ACCOUNT,
"id", dom_element_get_attribute (node, "nsid"),
"username", dom_element_get_attribute (node, "username"),
"name", dom_element_get_attribute (node, "fullname"),
"token", token,
NULL);
g_task_return_pointer (task, account, g_object_unref);
break;
}
}
}
}
if (token == NULL)
g_task_return_error (task, g_error_new_literal (WEB_SERVICE_ERROR, WEB_SERVICE_ERROR_GENERIC, _("Unknown error")));
g_object_unref (doc);
}
else
g_task_return_error (task, error);
soup_buffer_free (body);
}
static void
flickr_service_old_auth_get_token (FlickrService *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GHashTable *data_set;
SoupMessage *msg;
oauth_service_set_token (OAUTH_SERVICE (self), NULL);
data_set = g_hash_table_new (g_str_hash, g_str_equal);
g_hash_table_insert (data_set, "method", "flickr.auth.getToken");
g_hash_table_insert (data_set, "frob", self->priv->frob);
flickr_service_old_auth_add_api_sig (self, data_set);
msg = soup_form_request_new_from_hash ("GET", self->priv->server->rest_url, data_set);
_web_service_send_message (WEB_SERVICE (self),
msg,
cancellable,
callback,
user_data,
flickr_service_old_auth_get_token,
flickr_service_old_auth_get_token_ready_cb,
self);
g_hash_table_destroy (data_set);
}
static OAuthAccount *
flickr_service_old_auth_get_token_finish (FlickrService *self,
GAsyncResult *result,
GError **error)
{
return g_task_propagate_pointer (G_TASK (result), error);
}
static char *
old_auth_get_access_type_name (WebAuthorization access_type)
{
char *name = NULL;
switch (access_type) {
case WEB_AUTHORIZATION_READ:
name = "read";
break;
case WEB_AUTHORIZATION_WRITE:
name = "write";
break;
}
return name;
}
static char *
flickr_service_old_auth_get_login_link (FlickrService *self,
WebAuthorization access_type)
{
GHashTable *data_set;
GString *link;
GList *keys;
GList *scan;
g_return_val_if_fail (self->priv->frob != NULL, NULL);
data_set = g_hash_table_new (g_str_hash, g_str_equal);
g_hash_table_insert (data_set, "frob", self->priv->frob);
g_hash_table_insert (data_set, "perms", old_auth_get_access_type_name (access_type));
flickr_service_old_auth_add_api_sig (self, data_set);
link = g_string_new (self->priv->server->authorization_url);
g_string_append (link, "?");
keys = g_hash_table_get_keys (data_set);
for (scan = keys; scan; scan = scan->next) {
char *key = scan->data;
if (scan != keys)
g_string_append (link, "&");
g_string_append (link, key);
g_string_append (link, "=");
g_string_append (link, g_hash_table_lookup (data_set, key));
}
g_list_free (keys);
g_hash_table_destroy (data_set);
return g_string_free (link, FALSE);
}
/* -- flickr_service_ask_authorization -- */
#define _RESPONSE_CONTINUE 1
#define _RESPONSE_AUTHORIZE 2
static void
old_auth_token_ready_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
FlickrService *self = user_data;
GError *error = NULL;
OAuthAccount *account;
account = flickr_service_old_auth_get_token_finish (self, res, &error);
if (account == NULL) {
gtk_dialog_response (GTK_DIALOG (_web_service_get_auth_dialog (WEB_SERVICE (self))), GTK_RESPONSE_CANCEL);
gth_task_completed (GTH_TASK (self), error);
return;
}
web_service_set_current_account (WEB_SERVICE (self), account);
gtk_dialog_response (GTK_DIALOG (_web_service_get_auth_dialog (WEB_SERVICE (self))), GTK_RESPONSE_OK);
g_object_unref (account);
}
static void
old_authorization_complete (FlickrService *self)
{
GtkWidget *dialog;
char *text;
char *secondary_text;
dialog = _web_service_get_auth_dialog (WEB_SERVICE (self));
gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), _RESPONSE_AUTHORIZE, FALSE);
gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), _RESPONSE_CONTINUE, TRUE);
text = g_strdup_printf (_("Return to this window when you have finished the authorization process on %s"), self->priv->server->display_name);
secondary_text = g_strdup (_("Once you’re done, click the “Continue” button below."));
g_object_set (dialog, "text", text, "secondary-text", secondary_text, NULL);
gtk_window_present (GTK_WINDOW (dialog));
g_free (secondary_text);
g_free (text);
}
static void
old_authorization_dialog_response_cb (GtkDialog *dialog,
int response_id,
gpointer user_data)
{
FlickrService *self = user_data;
switch (response_id) {
case _RESPONSE_AUTHORIZE:
{
char *url;
GError *error = NULL;
url = flickr_service_old_auth_get_login_link (self, WEB_AUTHORIZATION_WRITE);
if (gtk_show_uri_on_window (GTK_WINDOW (dialog), url, GDK_CURRENT_TIME, &error))
old_authorization_complete (self);
else
gth_task_completed (GTH_TASK (self), error);
g_free (url);
}
break;
case _RESPONSE_CONTINUE:
gtk_widget_hide (GTK_WIDGET (dialog));
gth_task_dialog (GTH_TASK (self), FALSE, NULL);
flickr_service_old_auth_get_token (self,
gth_task_get_cancellable (GTH_TASK (self)),
old_auth_token_ready_cb,
self);
break;
default:
break;
}
}
static void
old_auth_frob_ready_cb (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
FlickrService *self = user_data;
GError *error = NULL;
GtkWidget *dialog;
char *text;
char *secondary_text;
if (! flickr_service_old_auth_get_frob_finish (self, result, &error)) {
gth_task_completed (GTH_TASK (self), error);
return;
}
dialog = gtk_message_dialog_new (NULL,
GTK_DIALOG_MODAL,
GTK_MESSAGE_OTHER,
GTK_BUTTONS_NONE,
NULL);
gtk_dialog_add_buttons (GTK_DIALOG (dialog),
_GTK_LABEL_CANCEL, GTK_RESPONSE_CANCEL,
_("C_ontinue"), _RESPONSE_CONTINUE,
_("_Authorize…"), _RESPONSE_AUTHORIZE,
NULL);
text = g_strdup_printf (_("Pix requires your authorization to upload the photos to %s"), self->priv->server->display_name);
secondary_text = g_strdup_printf (_("Click “Authorize” to open your web browser and authorize pix to upload photos to %s. When you’re finished, return to this window to complete the authorization."), self->priv->server->display_name);
g_object_set (dialog, "text", text, "secondary-text", secondary_text, NULL);
gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), _RESPONSE_AUTHORIZE, TRUE);
gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), _RESPONSE_CONTINUE, FALSE);
g_signal_connect (dialog,
"response",
G_CALLBACK (old_authorization_dialog_response_cb),
self);
_web_service_set_auth_dialog (WEB_SERVICE (self), GTK_DIALOG (dialog));
gtk_window_present (GTK_WINDOW (dialog));
g_free (secondary_text);
g_free (text);
}
static void
flickr_service_ask_authorization (WebService *base)
{
FlickrService *self = FLICKR_SERVICE (base);
if (self->priv->server->new_authentication) {
WEB_SERVICE_CLASS (flickr_service_parent_class)->ask_authorization (base);
return;
}
/* old authentication process, still used by 23hq.com */
flickr_service_old_auth_get_frob (self,
gth_task_get_cancellable (GTH_TASK (self)),
old_auth_frob_ready_cb,
self);
}
static void
flickr_service_add_signature (FlickrService *self,
const char *method,
const char *url,
GHashTable *parameters)
{
if (self->priv->server->new_authentication)
oauth_service_add_signature (OAUTH_SERVICE (self), method, url, parameters);
else
flickr_service_old_auth_add_api_sig (self, parameters);
}
/* -- flickr_service_get_user_info -- */
static void
get_user_info_ready_cb (SoupSession *session,
SoupMessage *msg,
gpointer user_data)
{
FlickrService *self = user_data;
GTask *task;
SoupBuffer *body;
DomDocument *doc = NULL;
GError *error = NULL;
task = _web_service_get_task (WEB_SERVICE (self));
if (msg->status_code != 200) {
g_task_return_new_error (task,
SOUP_HTTP_ERROR,
msg->status_code,
"%s",
soup_status_get_phrase (msg->status_code));
return;
}
body = soup_message_body_flatten (msg->response_body);
if (flickr_utils_parse_response (body, &doc, &error)) {
OAuthAccount *account;
DomElement *response;
DomElement *node;
gboolean success = FALSE;
account = _g_object_ref (web_service_get_current_account (WEB_SERVICE (self)));
if (account == NULL)
account = g_object_new (FLICKR_TYPE_ACCOUNT,
"token", oauth_service_get_token (OAUTH_SERVICE (self)),
"token-secret", oauth_service_get_token_secret (OAUTH_SERVICE (self)),
NULL);
response = DOM_ELEMENT (doc)->first_child;
for (node = response->first_child; node; node = node->next_sibling) {
if (g_strcmp0 (node->tag_name, "user") == 0) {
success = TRUE;
flickr_account_load_extra_data (FLICKR_ACCOUNT (account), node);
g_task_return_pointer (task, g_object_ref (account), (GDestroyNotify) g_object_unref);
}
}
if (! success)
g_task_return_error (task, g_error_new_literal (WEB_SERVICE_ERROR, WEB_SERVICE_ERROR_GENERIC, _("Unknown error")));
g_object_unref (account);
g_object_unref (doc);
}
else
g_task_return_error (task, error);
soup_buffer_free (body);
}
static void
flickr_service_get_user_info (WebService *base,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
FlickrService *self = FLICKR_SERVICE (base);
OAuthAccount *account;
GHashTable *data_set;
SoupMessage *msg;
account = web_service_get_current_account (WEB_SERVICE (self));
if (account != NULL) {
oauth_service_set_token (OAUTH_SERVICE (self), account->token);
oauth_service_set_token_secret (OAUTH_SERVICE (self), account->token_secret);
}
data_set = g_hash_table_new (g_str_hash, g_str_equal);
g_hash_table_insert (data_set, "format", RESPONSE_FORMAT);
g_hash_table_insert (data_set, "method", "flickr.people.getUploadStatus");
flickr_service_add_signature (self, "GET", self->priv->server->rest_url, data_set);
msg = soup_form_request_new_from_hash ("GET", self->priv->server->rest_url, data_set);
_web_service_send_message (WEB_SERVICE (self),
msg,
cancellable,
callback,
user_data,
flickr_service_get_user_info,
get_user_info_ready_cb,
self);
g_hash_table_destroy (data_set);
}
static void
flickr_service_class_init (FlickrServiceClass *klass)
{
GObjectClass *object_class;
WebServiceClass *service_class;
object_class = (GObjectClass*) klass;
object_class->set_property = flickr_service_set_property;
object_class->get_property = flickr_service_get_property;
object_class->finalize = flickr_service_finalize;
service_class = (WebServiceClass*) klass;
service_class->ask_authorization = flickr_service_ask_authorization;
service_class->get_user_info = flickr_service_get_user_info;
/* properties */
g_object_class_install_property (object_class,
PROP_SERVER,
g_param_spec_pointer ("server",
"Server",
"",
G_PARAM_READWRITE));
}
static void
flickr_service_init (FlickrService *self)
{
self->priv = flickr_service_get_instance_private (self);
self->priv->post_photos = NULL;
self->priv->add_photos = NULL;
self->priv->server = NULL;
self->priv->checksum = g_checksum_new (G_CHECKSUM_MD5);
self->priv->frob = NULL;
}
FlickrService *
flickr_service_new (FlickrServer *server,
GCancellable *cancellable,
GtkWidget *browser,
GtkWidget *dialog)
{
g_return_val_if_fail (server != NULL, NULL);
return g_object_new (FLICKR_TYPE_SERVICE,
"service-name", server->name,
"service-address", server->url,
"service-protocol", server->protocol,
"account-type", FLICKR_TYPE_ACCOUNT,
"cancellable", cancellable,
"browser", browser,
"dialog", dialog,
"server", server,
NULL);
}
FlickrServer *
flickr_service_get_server (FlickrService *self)
{
return self->priv->server;
}
/* -- flickr_service_list_photosets -- */
static void
list_photosets_ready_cb (SoupSession *session,
SoupMessage *msg,
gpointer user_data)
{
FlickrService *self = user_data;
GTask *task;
SoupBuffer *body;
DomDocument *doc = NULL;
GError *error = NULL;
task = _web_service_get_task (WEB_SERVICE (self));
if (msg->status_code != 200) {
g_task_return_new_error (task,
SOUP_HTTP_ERROR,
msg->status_code,
"%s",
soup_status_get_phrase (msg->status_code));
return;
}
body = soup_message_body_flatten (msg->response_body);
if (flickr_utils_parse_response (body, &doc, &error)) {
DomElement *response;
DomElement *node;
GList *photosets = NULL;
response = DOM_ELEMENT (doc)->first_child;
for (node = response->first_child; node; node = node->next_sibling) {
if (g_strcmp0 (node->tag_name, "photosets") == 0) {
DomElement *child;
for (child = node->first_child; child; child = child->next_sibling) {
if (g_strcmp0 (child->tag_name, "photoset") == 0) {
FlickrPhotoset *photoset;
photoset = flickr_photoset_new ();
dom_domizable_load_from_element (DOM_DOMIZABLE (photoset), child);
photosets = g_list_prepend (photosets, photoset);
}
}
}
}
photosets = g_list_reverse (photosets);
g_task_return_pointer (task, photosets, (GDestroyNotify) _g_object_list_unref);
g_object_unref (doc);
}
else
g_task_return_error (task, error);
soup_buffer_free (body);
}
void
flickr_service_list_photosets (FlickrService *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GHashTable *data_set;
SoupMessage *msg;
gth_task_progress (GTH_TASK (self), _("Getting the album list"), NULL, TRUE, 0.0);
data_set = g_hash_table_new (g_str_hash, g_str_equal);
g_hash_table_insert (data_set, "format", RESPONSE_FORMAT);
g_hash_table_insert (data_set, "method", "flickr.photosets.getList");
flickr_service_add_signature (self, "GET", self->priv->server->rest_url, data_set);
msg = soup_form_request_new_from_hash ("GET", self->priv->server->rest_url, data_set);
_web_service_send_message (WEB_SERVICE (self),
msg,
cancellable,
callback,
user_data,
flickr_service_list_photosets,
list_photosets_ready_cb,
self);
g_hash_table_destroy (data_set);
}
GList *
flickr_service_list_photosets_finish (FlickrService *service,
GAsyncResult *result,
GError **error)
{
return g_task_propagate_pointer (G_TASK (result), error);
}
/* -- flickr_service_create_photoset_finish -- */
static void
create_photoset_ready_cb (SoupSession *session,
SoupMessage *msg,
gpointer user_data)
{
FlickrService *self = user_data;
GTask *task;
SoupBuffer *body;
DomDocument *doc = NULL;
GError *error = NULL;
task = _web_service_get_task (WEB_SERVICE (self));
if (msg->status_code != 200) {
g_task_return_new_error (task,
SOUP_HTTP_ERROR,
msg->status_code,
"%s",
soup_status_get_phrase (msg->status_code));
return;
}
body = soup_message_body_flatten (msg->response_body);
if (flickr_utils_parse_response (body, &doc, &error)) {
DomElement *response;
DomElement *node;
FlickrPhotoset *photoset = NULL;
response = DOM_ELEMENT (doc)->first_child;
for (node = response->first_child; node; node = node->next_sibling) {
if (g_strcmp0 (node->tag_name, "photoset") == 0) {
photoset = flickr_photoset_new ();
dom_domizable_load_from_element (DOM_DOMIZABLE (photoset), node);
g_task_return_pointer (task, photoset, (GDestroyNotify) g_object_unref);
}
}
if (photoset == NULL)
g_task_return_error (task, g_error_new_literal (WEB_SERVICE_ERROR, WEB_SERVICE_ERROR_GENERIC, _("Unknown error")));
g_object_unref (doc);
}
else
g_task_return_error (task, error);
soup_buffer_free (body);
}
void
flickr_service_create_photoset (FlickrService *self,
FlickrPhotoset *photoset,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GHashTable *data_set;
SoupMessage *msg;
g_return_if_fail (photoset != NULL);
g_return_if_fail (photoset->primary != NULL);
gth_task_progress (GTH_TASK (self), _("Creating the new album"), NULL, TRUE, 0.0);
data_set = g_hash_table_new (g_str_hash, g_str_equal);
g_hash_table_insert (data_set, "format", RESPONSE_FORMAT);
g_hash_table_insert (data_set, "method", "flickr.photosets.create");
g_hash_table_insert (data_set, "title", photoset->title);
g_hash_table_insert (data_set, "primary_photo_id", photoset->primary);
flickr_service_add_signature (self, "GET", self->priv->server->rest_url, data_set);
msg = soup_form_request_new_from_hash ("GET", self->priv->server->rest_url, data_set);
_web_service_send_message (WEB_SERVICE (self),
msg,
cancellable,
callback,
user_data,
flickr_service_create_photoset,
create_photoset_ready_cb,
self);
g_hash_table_destroy (data_set);
}
FlickrPhotoset *
flickr_service_create_photoset_finish (FlickrService *self,
GAsyncResult *result,
GError **error)
{
return g_task_propagate_pointer (G_TASK (result), error);
}
/* -- flickr_service_add_photos_to_set -- */
static void
add_photos_to_set_done (FlickrService *self,
GError *error)
{
GTask *task;
task = _web_service_get_task (WEB_SERVICE (self));
if (task == NULL)
task = g_task_new (G_OBJECT (self),
NULL,
self->priv->add_photos->callback,
self->priv->add_photos->user_data);
if (error == NULL)
g_task_return_boolean (task, TRUE);
else
g_task_return_error (task, g_error_copy (error));
}
static void add_current_photo_to_set (FlickrService *self);
static void
add_next_photo_to_set (FlickrService *self)
{
self->priv->add_photos->current = self->priv->add_photos->current->next;
self->priv->add_photos->n_current += 1;
add_current_photo_to_set (self);
}
static void
add_current_photo_to_set_ready_cb (SoupSession *session,
SoupMessage *msg,
gpointer user_data)
{
FlickrService *self = user_data;
GTask *task;
SoupBuffer *body;
DomDocument *doc = NULL;
GError *error = NULL;
task = _web_service_get_task (WEB_SERVICE (self));
if (msg->status_code != 200) {
g_task_return_new_error (task,
SOUP_HTTP_ERROR,
msg->status_code,
"%s",
soup_status_get_phrase (msg->status_code));
return;
}
body = soup_message_body_flatten (msg->response_body);
if (! flickr_utils_parse_response (body, &doc, &error)) {
soup_buffer_free (body);
add_photos_to_set_done (self, error);
return;
}
g_object_unref (doc);
soup_buffer_free (body);
add_next_photo_to_set (self);
}
static void
add_current_photo_to_set (FlickrService *self)
{
char *photo_id;
GHashTable *data_set;
SoupMessage *msg;
if (self->priv->add_photos->current == NULL) {
add_photos_to_set_done (self, NULL);
return;
}
gth_task_progress (GTH_TASK (self),
_("Creating the new album"),
"",
FALSE,
(double) self->priv->add_photos->n_current / (self->priv->add_photos->n_files + 1));
photo_id = self->priv->add_photos->current->data;
if (g_strcmp0 (photo_id, self->priv->add_photos->photoset->primary) == 0) {
add_next_photo_to_set (self);
return;
}
data_set = g_hash_table_new (g_str_hash, g_str_equal);
g_hash_table_insert (data_set, "format", RESPONSE_FORMAT);
g_hash_table_insert (data_set, "method", "flickr.photosets.addPhoto");
g_hash_table_insert (data_set, "photoset_id", self->priv->add_photos->photoset->id);
g_hash_table_insert (data_set, "photo_id", photo_id);
flickr_service_add_signature (self, "POST", self->priv->server->rest_url, data_set);
msg = soup_form_request_new_from_hash ("POST", self->priv->server->rest_url, data_set);
_web_service_send_message (WEB_SERVICE (self),
msg,
self->priv->add_photos->cancellable,
self->priv->add_photos->callback,
self->priv->add_photos->user_data,
flickr_service_add_photos_to_set,
add_current_photo_to_set_ready_cb,
self);
g_hash_table_destroy (data_set);
}
void
flickr_service_add_photos_to_set (FlickrService *self,
FlickrPhotoset *photoset,
GList *photo_ids,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
gth_task_progress (GTH_TASK (self), _("Creating the new album"), NULL, TRUE, 0.0);
add_photos_data_free (self->priv->add_photos);
self->priv->add_photos = g_new0 (AddPhotosData, 1);
self->priv->add_photos->photoset = _g_object_ref (photoset);
self->priv->add_photos->photo_ids = _g_string_list_dup (photo_ids);
self->priv->add_photos->cancellable = _g_object_ref (cancellable);
self->priv->add_photos->callback = callback;
self->priv->add_photos->user_data = user_data;
self->priv->add_photos->n_files = g_list_length (self->priv->add_photos->photo_ids);
self->priv->add_photos->current = self->priv->add_photos->photo_ids;
self->priv->add_photos->n_current = 1;
_web_service_reset_task (WEB_SERVICE (self));
add_current_photo_to_set (self);
}
gboolean
flickr_service_add_photos_to_set_finish (FlickrService *self,
GAsyncResult *result,
GError **error)
{
return g_task_propagate_boolean (G_TASK (result), error);
}
/* -- flickr_service_post_photos -- */
static void
post_photos_done (FlickrService *self,
GError *error)
{
GTask *task;
task = _web_service_get_task (WEB_SERVICE (self));
if (error == NULL) {
self->priv->post_photos->ids = g_list_reverse (self->priv->post_photos->ids);
g_task_return_pointer (task, self->priv->post_photos->ids, (GDestroyNotify) _g_string_list_free);
self->priv->post_photos->ids = NULL;
}
else {
if (self->priv->post_photos->current != NULL) {
GthFileData *file_data = self->priv->post_photos->current->data;
char *msg;
msg = g_strdup_printf (_("Could not upload “%s”: %s"), g_file_info_get_display_name (file_data->info), error->message);
g_free (error->message);
error->message = msg;
}
g_task_return_error (task, error);
}
}
static void flickr_service_post_current_file (FlickrService *self);
static void
post_photo_ready_cb (SoupSession *session,
SoupMessage *msg,
gpointer user_data)
{
FlickrService *self = user_data;
SoupBuffer *body;
DomDocument *doc = NULL;
GError *error = NULL;
GthFileData *file_data;
if (msg->status_code != 200) {
GError *error;
error = g_error_new_literal (SOUP_HTTP_ERROR, msg->status_code, soup_status_get_phrase (msg->status_code));
post_photos_done (self, error);
g_error_free (error);
return;
}
body = soup_message_body_flatten (msg->response_body);
if (flickr_utils_parse_response (body, &doc, &error)) {
DomElement *response;
DomElement *node;
/* save the file id */
response = DOM_ELEMENT (doc)->first_child;
for (node = response->first_child; node; node = node->next_sibling) {
if (g_strcmp0 (node->tag_name, "photoid") == 0) {
const char *id;
id = dom_element_get_inner_text (node);
self->priv->post_photos->ids = g_list_prepend (self->priv->post_photos->ids, g_strdup (id));
}
}
g_object_unref (doc);
}
else {
soup_buffer_free (body);
post_photos_done (self, error);
return;
}
soup_buffer_free (body);
file_data = self->priv->post_photos->current->data;
self->priv->post_photos->uploaded_size += g_file_info_get_size (file_data->info);
self->priv->post_photos->current = self->priv->post_photos->current->next;
flickr_service_post_current_file (self);
}
static char *
get_safety_value (FlickrSafety safety_level)
{
char *value = NULL;
switch (safety_level) {
case FLICKR_SAFETY_SAFE:
value = "1";
break;
case FLICKR_SAFETY_MODERATE:
value = "2";
break;
case FLICKR_SAFETY_RESTRICTED:
value = "3";
break;
}
return value;
}
static void
upload_photo_wrote_body_data_cb (SoupMessage *msg,
SoupBuffer *chunk,
gpointer user_data)
{
FlickrService *self = user_data;
GthFileData *file_data;
char *details;
double current_file_fraction;
if (self->priv->post_photos->current == NULL)
return;
self->priv->post_photos->wrote_body_data_size += chunk->length;
if (self->priv->post_photos->wrote_body_data_size > msg->request_body->length)
return;
file_data = self->priv->post_photos->current->data;
/* Translators: %s is a filename */
details = g_strdup_printf (_("Uploading “%s”"), g_file_info_get_display_name (file_data->info));
current_file_fraction = (double) self->priv->post_photos->wrote_body_data_size / msg->request_body->length;
gth_task_progress (GTH_TASK (self),
NULL,
details,
FALSE,
(double) (self->priv->post_photos->uploaded_size + (g_file_info_get_size (file_data->info) * current_file_fraction)) / self->priv->post_photos->total_size);
g_free (details);
}
static void
post_photo_file_buffer_ready_cb (void **buffer,
gsize count,
GError *error,
gpointer user_data)
{
FlickrService *self = user_data;
GthFileData *file_data;
SoupMultipart *multipart;
char *uri;
SoupBuffer *body;
void *resized_buffer;
gsize resized_count;
SoupMessage *msg;
if (error != NULL) {
post_photos_done (self, error);
return;
}
file_data = self->priv->post_photos->current->data;
multipart = soup_multipart_new ("multipart/form-data");
/* the metadata part */
{
GHashTable *data_set;
char *title;
char *description;
char *tags;
GObject *metadata;
GList *keys;
GList *scan;
data_set = g_hash_table_new (g_str_hash, g_str_equal);
g_hash_table_insert (data_set, "format", RESPONSE_FORMAT);
title = gth_file_data_get_attribute_as_string (file_data, "general::title");
if (title != NULL)
g_hash_table_insert (data_set, "title", title);
description = gth_file_data_get_attribute_as_string (file_data, "general::description");
if (description != NULL)
g_hash_table_insert (data_set, "description", description);
tags = NULL;
metadata = g_file_info_get_attribute_object (file_data->info, "general::tags");
if (metadata != NULL)
tags = gth_string_list_join (GTH_STRING_LIST (gth_metadata_get_string_list (GTH_METADATA (metadata))), " ");
if (tags != NULL)
g_hash_table_insert (data_set, "tags", tags);
g_hash_table_insert (data_set, "is_public", (self->priv->post_photos->privacy_level == FLICKR_PRIVACY_PUBLIC) ? "1" : "0");
g_hash_table_insert (data_set, "is_friend", ((self->priv->post_photos->privacy_level == FLICKR_PRIVACY_FRIENDS) || (self->priv->post_photos->privacy_level == FLICKR_PRIVACY_FRIENDS_FAMILY)) ? "1" : "0");
g_hash_table_insert (data_set, "is_family", ((self->priv->post_photos->privacy_level == FLICKR_PRIVACY_FAMILY) || (self->priv->post_photos->privacy_level == FLICKR_PRIVACY_FRIENDS_FAMILY)) ? "1" : "0");
g_hash_table_insert (data_set, "safety_level", get_safety_value (self->priv->post_photos->safety_level));
g_hash_table_insert (data_set, "hidden", self->priv->post_photos->hidden ? "2" : "1");
flickr_service_add_signature (self, "POST", self->priv->server->upload_url, data_set);
keys = g_hash_table_get_keys (data_set);
for (scan = keys; scan; scan = scan->next) {
char *key = scan->data;
soup_multipart_append_form_string (multipart, key, g_hash_table_lookup (data_set, key));
}
g_free (tags);
g_list_free (keys);
g_free (description);
g_free (title);
g_hash_table_unref (data_set);
}
/* the file part */
if (_g_buffer_resize_image (*buffer,
count,
file_data,
self->priv->post_photos->max_width,
self->priv->post_photos->max_height,
&resized_buffer,
&resized_count,
self->priv->post_photos->cancellable,
&error))
{
body = soup_buffer_new (SOUP_MEMORY_TAKE, resized_buffer, resized_count);
}
else if (error == NULL) {
body = soup_buffer_new (SOUP_MEMORY_TEMPORARY, *buffer, count);
}
else {
soup_multipart_free (multipart);
post_photos_done (self, error);
return;
}
uri = g_file_get_uri (file_data->file);
soup_multipart_append_form_file (multipart,
"photo",
uri,
gth_file_data_get_mime_type (file_data),
body);
soup_buffer_free (body);
g_free (uri);
/* send the file */
self->priv->post_photos->wrote_body_data_size = 0;
msg = soup_form_request_new_from_multipart (self->priv->server->upload_url, multipart);
g_signal_connect (msg,
"wrote-body-data",
(GCallback) upload_photo_wrote_body_data_cb,
self);
_web_service_send_message (WEB_SERVICE (self),
msg,
self->priv->post_photos->cancellable,
self->priv->post_photos->callback,
self->priv->post_photos->user_data,
flickr_service_post_photos,
post_photo_ready_cb,
self);
soup_multipart_free (multipart);
}
static void
flickr_service_post_current_file (FlickrService *self)
{
GthFileData *file_data;
if (self->priv->post_photos->current == NULL) {
post_photos_done (self, NULL);
return;
}
file_data = self->priv->post_photos->current->data;
_g_file_load_async (file_data->file,
G_PRIORITY_DEFAULT,
self->priv->post_photos->cancellable,
post_photo_file_buffer_ready_cb,
self);
}
static void
post_photos_info_ready_cb (GList *files,
GError *error,
gpointer user_data)
{
FlickrService *self = user_data;
GList *scan;
if (error != NULL) {
post_photos_done (self, error);
return;
}
self->priv->post_photos->file_list = _g_object_list_ref (files);
for (scan = self->priv->post_photos->file_list; scan; scan = scan->next) {
GthFileData *file_data = scan->data;
self->priv->post_photos->total_size += g_file_info_get_size (file_data->info);
self->priv->post_photos->n_files += 1;
}
self->priv->post_photos->current = self->priv->post_photos->file_list;
flickr_service_post_current_file (self);
}
void
flickr_service_post_photos (FlickrService *self,
FlickrPrivacy privacy_level,
FlickrSafety safety_level,
gboolean hidden,
int max_width,
int max_height,
GList *file_list, /* GFile list */
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
gth_task_progress (GTH_TASK (self),
_("Uploading the files to the server"),
"",
TRUE,
0.0);
post_photos_data_free (self->priv->post_photos);
self->priv->post_photos = g_new0 (PostPhotosData, 1);
self->priv->post_photos->privacy_level = privacy_level;
self->priv->post_photos->safety_level = safety_level;
self->priv->post_photos->hidden = hidden;
self->priv->post_photos->max_width = max_width;
self->priv->post_photos->max_height = max_height;
self->priv->post_photos->cancellable = _g_object_ref (cancellable);
self->priv->post_photos->callback = callback;
self->priv->post_photos->user_data = user_data;
self->priv->post_photos->total_size = 0;
self->priv->post_photos->n_files = 0;
_g_query_all_metadata_async (file_list,
GTH_LIST_DEFAULT,
"*",
self->priv->post_photos->cancellable,
post_photos_info_ready_cb,
self);
}
GList *
flickr_service_post_photos_finish (FlickrService *self,
GAsyncResult *result,
GError **error)
{
return g_task_propagate_pointer (G_TASK (result), error);
}
/* -- flickr_service_list_photos -- */
typedef struct {
FlickrService *self;
FlickrPhotoset *photoset;
char *extras;
GCancellable *cancellable;
GAsyncReadyCallback callback;
gpointer user_data;
GList *photos;
int position;
} FlickrListPhotosData;
static void
flickr_list_photos_data_free (FlickrListPhotosData *data)
{
_g_object_unref (data->self);
_g_object_unref (data->photoset);
g_free (data->extras);
_g_object_unref (data->cancellable);
g_free (data);
}
static void
flickr_service_list_photoset_page (FlickrListPhotosData *data,
int page);
static void
flickr_service_list_photoset_paged_ready_cb (SoupSession *session,
SoupMessage *msg,
gpointer user_data)
{
FlickrListPhotosData *data = user_data;
FlickrService *self = data->self;
GTask *task;
SoupBuffer *body;
DomDocument *doc = NULL;
GError *error = NULL;
task = _web_service_get_task (WEB_SERVICE (self));
if (msg->status_code != 200) {
g_task_return_new_error (task,
SOUP_HTTP_ERROR,
msg->status_code,
"%s",
soup_status_get_phrase (msg->status_code));
flickr_list_photos_data_free (data);
return;
}
body = soup_message_body_flatten (msg->response_body);
if (flickr_utils_parse_response (body, &doc, &error)) {
DomElement *response;
DomElement *node;
int pages = 0;
int page = 0;
response = DOM_ELEMENT (doc)->first_child;
for (node = response->first_child; node; node = node->next_sibling) {
if (g_strcmp0 (node->tag_name, "photoset") == 0) {
DomElement *child;
for (child = node->first_child; child; child = child->next_sibling) {
if (g_strcmp0 (child->tag_name, "photo") == 0) {
FlickrPhoto *photo;
photo = flickr_photo_new (self->priv->server);
dom_domizable_load_from_element (DOM_DOMIZABLE (photo), child);
photo->position = data->position++;
data->photos = g_list_prepend (data->photos, photo);
}
}
pages = dom_element_get_attribute_as_int (node, "pages");
page = dom_element_get_attribute_as_int (node, "page");
}
}
if (page > pages) {
g_task_return_new_error (task,
SOUP_HTTP_ERROR,
0,
"%s",
"Invalid data");
flickr_list_photos_data_free (data);
}
else if (page < pages) {
/* read the next page */
flickr_service_list_photoset_page (data, page + 1);
}
else { /* page == pages */
data->photos = g_list_reverse (data->photos);
g_task_return_pointer (task,
_g_object_list_ref (data->photos),
(GDestroyNotify) _g_object_list_unref);
flickr_list_photos_data_free (data);
}
g_object_unref (doc);
}
else
g_task_return_error (task, error);
soup_buffer_free (body);
}
static void
flickr_service_list_photoset_page (FlickrListPhotosData *data,
int n_page)
{
FlickrService *self = data->self;
GHashTable *data_set;
char *page = NULL;
char *per_page = NULL;
SoupMessage *msg;
g_return_if_fail (data->photoset != NULL);
gth_task_progress (GTH_TASK (self),
_("Getting the photo list"),
NULL,
TRUE,
0.0);
data_set = g_hash_table_new (g_str_hash, g_str_equal);
g_hash_table_insert (data_set, "format", RESPONSE_FORMAT);
g_hash_table_insert (data_set, "method", "flickr.photosets.getPhotos");
g_hash_table_insert (data_set, "photoset_id", data->photoset->id);
if (data->extras != NULL)
g_hash_table_insert (data_set, "extras", (char *) data->extras);
if (n_page > 0) {
page = g_strdup_printf ("%d", IMAGES_PER_PAGE);
g_hash_table_insert (data_set, "per_page", page);
per_page = g_strdup_printf ("%d", n_page);
g_hash_table_insert (data_set, "page", per_page);
}
flickr_service_add_signature (self, "GET", self->priv->server->rest_url, data_set);
msg = soup_form_request_new_from_hash ("GET", self->priv->server->rest_url, data_set);
_web_service_send_message (WEB_SERVICE (self),
msg,
data->cancellable,
data->callback,
data->user_data,
flickr_service_list_photos,
flickr_service_list_photoset_paged_ready_cb,
data);
g_free (per_page);
g_free (page);
g_hash_table_destroy (data_set);
}
void
flickr_service_list_photos (FlickrService *self,
FlickrPhotoset *photoset,
const char *extras,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
FlickrListPhotosData *data;
data = g_new0 (FlickrListPhotosData, 1);
data->self = _g_object_ref (self);
data->photoset = _g_object_ref (photoset);
data->extras = g_strdup (extras);
data->cancellable = _g_object_ref (cancellable);
data->callback = callback;
data->user_data = user_data;
data->photos = NULL;
data->position = 0;
flickr_service_list_photoset_page (data, 1);
}
GList *
flickr_service_list_photos_finish (FlickrService *self,
GAsyncResult *result,
GError **error)
{
return g_task_propagate_pointer (G_TASK (result), error);
}