2 * This file is part of sharing-plugin-gallery2
4 * Copyright (C) 2009 Heikki Kallasjoki. All rights reserved.
5 * Copyright (C) 2008-2009 Nokia Corporation. All rights reserved.
7 * This code is licensed under a MIT-style license, that can be
8 * found in the file called "COPYING" in the root directory.
16 #include <sharing-http.h>
21 static gchar* url_encode (const gchar* source);
25 * @con: Connection to use
26 * @urlbase: Base URL to the Gallery 2 site
27 * @username: User name
29 * @cookies: Output parameter for any cookies set.
30 * @auth: Output paremeter for the authentication token.
32 * Logs in to the Gallery 2 service.
34 * Returns: Validation result.
36 SharingPluginInterfaceAccountValidateResult
37 gallery2_login (ConIcConnection* con,
38 const gchar* urlbase, const gchar* username, const gchar* password,
39 GHashTable** cookies, gchar** auth)
41 SharingPluginInterfaceAccountValidateResult ret = SHARING_ACCOUNT_VALIDATE_SUCCESS;
43 SharingHTTP* http = sharing_http_new ();
48 /* Do the login request */
50 SharingHTTPRunResponse res = 0;
53 gchar* euser = url_encode (username);
54 gchar* epass = url_encode (password);
56 gchar* url = g_strdup_printf("%s/main.php?g2_controller=remote:GalleryRemote&"
57 "g2_form[cmd]=login&g2_form[protocol_version]=2.0&"
58 "g2_form[uname]=%s&g2_form[password]=%s",
59 urlbase, euser, epass);
64 sharing_http_set_connection (http, con);
65 res = sharing_http_run (http, url);
70 /* Parse the response */
72 if (res == SHARING_HTTP_RUNRES_SUCCESS && sharing_http_get_res_code (http) == 200)
74 /* Split response into lines */
79 gsize content_len = 0;
80 const gchar* content = sharing_http_get_res_content (http, &content_len);
81 gchar* c = g_strndup (content, content_len); /* force \0 termination */
82 lines = g_strsplit_set (c, "\r\n", 0);
86 /* Process the lines */
88 *cookies = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
90 gboolean in_body = FALSE;
92 for (gchar** p = lines; *p; p++)
98 if (g_ascii_strncasecmp (line, "Set-Cookie:", 11) == 0)
100 /* Extract the key=value part of the cookie */
105 gchar* c = g_strchug (line+11); /* start of cookie data */
107 char* end = strchr (c, ';');
110 data = g_strsplit (c, "=", 2);
113 if (!data[0] || !data[1])
115 /* Bad Set-Cookie: header, ignore */
120 /* Insert into our table */
122 g_hash_table_replace (*cookies, data[0], data[1]);
123 g_free (data); /* not g_strfreev! strings still used in "cookies" */
128 if (g_str_has_prefix (line, "#__GR2PROTO__"))
136 /* Split key=value into fields */
138 gchar* value = strchr (line, '=');
139 if (!value) continue;
143 /* Process the necessary parts */
145 if (strcmp (line, "status") == 0 && strcmp (value, "0") != 0)
147 ULOG_ERR_L ("Gallery 2 login auth failed\n");
148 ret = SHARING_ACCOUNT_VALIDATE_FAILED;
152 if (strcmp (line, "auth_token") == 0)
154 *auth = g_strdup (value);
164 ULOG_ERR_L ("Gallery 2 login connection failed\n");
165 ret = SHARING_ACCOUNT_VALIDATE_ERROR_CONNECTION;
168 if (ret != SHARING_ACCOUNT_VALIDATE_SUCCESS)
170 if (*cookies) g_hash_table_unref (*cookies);
171 if (*auth) g_free (auth);
176 sharing_http_unref (http);
181 * gallery2_lookup_album:
182 * @con: Connection to use
183 * @urlbase: Base URL to the Gallery 2 site
184 * @albumpath: Slash-separated path to album
185 * @album: Output parameter to hold the album ID
186 * @cookies: Cookies from gallery2_login.
187 * @auth: Authentication token from gallery2_login.
189 * Retrieves the album id based on an album path.
191 SharingPluginInterfaceAccountValidateResult
192 gallery2_lookup_album (ConIcConnection* con,
193 const gchar* urlbase, const gchar* albumpath, gchar** album,
194 GHashTable* cookies, gchar* auth)
196 SharingPluginInterfaceAccountValidateResult ret = SHARING_ACCOUNT_VALIDATE_ERROR_UNKNOWN;
198 SharingHTTP* http = sharing_http_new ();
200 /* Prepare and send the request */
202 gchar* url = g_strdup_printf("%s/main.php?g2_controller=remote:GalleryRemote%s%s&"
203 "g2_form[cmd]=fetch-albums-prune&g2_form[protocol_version]=2.2&g2_form[no_perms]=yes",
205 auth ? "&g2_authToken=" : "", auth ? auth : "");
211 g_hash_table_iter_init (&iter, cookies);
212 while (g_hash_table_iter_next (&iter, &key, &value))
214 gchar* hdr = g_strdup_printf("Cookie: %s=%s", (gchar*)key, (gchar*)value);
215 sharing_http_add_req_header_line (http, hdr);
220 sharing_http_set_connection (http, con);
221 SharingHTTPRunResponse res = sharing_http_run (http, url);
226 /* Parse the response into an album map. */
228 GHashTable* album_names = 0; /* map string (display-name) -> GSList [ string (url-name) ] */
229 GHashTable* album_parent = 0; /* map string (url-name) -> string (url-name) */
230 gchar* album_root = 0; /* root album url-name */
231 gboolean valid = FALSE; /* true if the maps are usable */
232 char** lines = 0; /* raw data (response lines) */
234 if (res == SHARING_HTTP_RUNRES_SUCCESS && sharing_http_get_res_code (http) == 200)
237 gsize content_len = 0;
238 const gchar* content = sharing_http_get_res_body (http, &content_len);
239 gchar* c = g_strndup (content, content_len); /* force \0 termination */
240 lines = g_strsplit_set (c, "\r\n", 0);
244 gboolean in_body = FALSE;
245 gchar* current_ref_num = 0;
246 gchar* current_url_name = 0;
248 for (gchar** p = lines; *p; p++)
254 if (g_str_has_prefix (line, "#__GR2PROTO__"))
256 album_names = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)g_slist_free);
257 album_parent = g_hash_table_new (g_str_hash, g_str_equal);
263 gchar* value = strchr (line, '=');
264 if (!value) continue;
268 if (strcmp (line, "status") == 0)
270 if (strcmp (value, "0") == 0)
274 else if (g_str_has_prefix (line, "album.name."))
276 current_ref_num = line + 11;
277 current_url_name = value;
279 else if (g_str_has_prefix (line, "album.title."))
281 if (!current_ref_num || strcmp (current_ref_num, line + 12) != 0)
283 GSList* others = g_hash_table_lookup (album_names, value);
284 if (others) g_hash_table_steal (album_names, value);
285 g_hash_table_insert (album_names, value, g_slist_prepend (others, current_url_name));
287 else if (g_str_has_prefix (line, "album.parent."))
289 if (!current_ref_num || strcmp (current_ref_num, line + 13) != 0)
291 g_hash_table_insert (album_parent, current_url_name, value);
292 if (strcmp (value, "0") == 0)
293 album_root = current_url_name;
298 ret = SHARING_ACCOUNT_VALIDATE_ERROR_CONNECTION;
300 sharing_http_unref (http);
302 /* Find the album we are interested in. */
306 if (album_names && album_parent && album_root && valid)
308 gchar* current_album = album_root;
309 gboolean seen_parents = FALSE; /* for the root special case */
310 gboolean found_final = FALSE; /* to make sure the last real component is found */
312 gchar** components = g_strsplit (albumpath, "/", 0);
314 for (gchar** p = components; *p; p++)
316 if (!**p) continue; /* ignore empty path components */
317 found_final = FALSE; /* this component needs to be found */
318 GSList* candidates = g_hash_table_lookup (album_names, *p);
319 /* bad case: no candidates at all */
320 if (!candidates) break;
321 /* special case 1: if only one candidate and no unseen parents, choose that */
322 if (!seen_parents && !g_slist_next (candidates))
325 current_album = candidates->data;
328 /* general case: find a candidate with an acceptable parent */
331 gchar* parent = g_hash_table_lookup (album_parent, candidates->data);
332 /* suitable parents: current_album, or (if no specified parents) null or 0 (explicit root case) */
333 if ((parent && strcmp (parent, current_album) == 0)
334 || (!seen_parents && (!parent || strcmp (parent, "0") == 0)))
337 current_album = candidates->data;
340 candidates = g_slist_next (candidates); /* try next */
344 g_strfreev (components);
348 *album = g_strdup(current_album);
349 ret = SHARING_ACCOUNT_VALIDATE_SUCCESS;
353 if (album_names) g_hash_table_unref (album_names);
354 if (album_parent) g_hash_table_unref (album_parent);
360 /* gallery2_send callback helper declarations */
362 struct gallery2_send_record
364 SharingTransfer* transfer;
365 gdouble progress_start;
366 gdouble progress_end;
371 gboolean gallery2_send_callback (SharingHTTP* http, guint64 bytes_sent, gpointer user_data);
375 * @con: Connection to use
376 * @transfer: Sharing transfer object
377 * @progress_start: Initial state of progress meter
378 * @progress_end: Desired final state of progress meter
379 * @dms: Dead man's switch
380 * @media: Media item to send
381 * @urlbase: Base URL to the Gallery 2 site
382 * @album: Album ID from gallery2_lookup_album.
383 * @cookies: Cookies from gallery2_login.
384 * @auth: Authentication token from gallery2_login.
386 * Sends a media item to a Gallery 2 service.
388 * Returns: Send result.
390 SharingPluginInterfaceSendResult
391 gallery2_send (ConIcConnection* con,
392 SharingTransfer* transfer, gdouble progress_start, gdouble progress_end, gboolean *dms,
393 SharingEntryMedia* media,
394 const gchar* urlbase, const gchar* album, GHashTable* cookies, gchar* auth)
396 struct gallery2_send_record rec = {
397 .transfer = transfer,
398 .progress_start = progress_start,
399 .progress_end = progress_end,
404 SharingPluginInterfaceSendResult ret = SHARING_SEND_SUCCESS;
406 SharingHTTP* http = sharing_http_new ();
408 /* Prepare and send the request */
410 gchar* media_title = sharing_entry_media_get_title (media);
411 gchar* media_mime = sharing_entry_media_get_mime (media);
412 gchar* media_filename = sharing_entry_media_get_filename (media);
414 const gchar* desc = sharing_entry_media_get_desc (media);
416 const gchar* title = media_title;
417 if (!title || !*title) title = media_filename;
418 if (!title || !*title) title = "(unknown)";
423 gchar* edesc = (desc && *desc ? url_encode (desc) : 0);
424 gchar* etitle = url_encode (title);
425 url = g_strdup_printf("%s/main.php?g2_controller=remote:GalleryRemote%s%s&"
426 "g2_form[cmd]=add-item&g2_form[protocol_version]=2.0&"
427 "g2_form[set_albumName]=%s&g2_form[caption]=%s"
430 auth ? "&g2_authToken=" : "", auth ? auth : "",
432 edesc ? "&g2_form[extrafield.Summary]=" : "", edesc ? edesc : "",
433 edesc ? "&g2_form[extrafield.Description]=" : "", edesc ? edesc : "");
442 g_hash_table_iter_init (&iter, cookies);
443 while (g_hash_table_iter_next (&iter, &key, &value))
445 gchar* hdr = g_strdup_printf("Cookie: %s=%s", (gchar*)key, (gchar*)value);
446 sharing_http_add_req_header_line (http, hdr);
451 sharing_http_add_req_multipart_file_with_filename (http,
453 sharing_entry_media_get_localpath (media),
454 media_mime ? media_mime : "image/jpeg",
455 media_filename ? media_filename : "unknown.jpg");
457 g_free (media_title);
459 g_free (media_filename);
461 media_title = media_mime = media_filename = 0;
463 rec.media_bytes = sharing_entry_media_get_size (media);
464 sharing_http_set_progress_callback (http, gallery2_send_callback, &rec);
467 sharing_http_set_connection (http, con);
468 SharingHTTPRunResponse res = sharing_http_run (http, url);
474 /* Parse the response */
476 if (res == SHARING_HTTP_RUNRES_SUCCESS && sharing_http_get_res_code (http) == 200)
481 gsize content_len = 0;
482 const gchar* content = sharing_http_get_res_body (http, &content_len);
483 gchar* c = g_strndup (content, content_len); /* force \0 termination */
484 lines = g_strsplit_set (c, "\r\n", 0);
488 gboolean in_body = FALSE;
489 ret = SHARING_SEND_ERROR_UNKNOWN;
491 for (gchar** p = lines; *p; p++)
497 if (g_str_has_prefix (line, "#__GR2PROTO__"))
502 gchar* value = strchr (line, '=');
503 if (!value) continue;
507 if (strcmp (line, "status") == 0)
509 if (strcmp (value, "0") == 0)
510 ret = SHARING_SEND_SUCCESS;
517 else if (res == SHARING_HTTP_RUNRES_CANCELLED)
518 ret = SHARING_SEND_CANCELLED;
520 ret = SHARING_SEND_ERROR_CONNECTION;
522 sharing_http_unref (http);
525 sharing_transfer_set_progress (transfer, progress_end);
530 /* gallery2_send callback implementation */
532 gboolean gallery2_send_callback (SharingHTTP* http, guint64 bytes_sent, gpointer user_data)
534 struct gallery2_send_record* rec = user_data;
536 if (!sharing_transfer_continue (rec->transfer))
541 gdouble progress = (rec->progress_start + rec->progress_end) / 2.0;
543 if (rec->media_bytes)
545 if (bytes_sent >= rec->media_bytes)
546 progress = rec->progress_end;
548 progress = rec->progress_start + (bytes_sent / (gdouble)rec->media_bytes) * (rec->progress_end - rec->progress_start);
551 sharing_transfer_set_progress (rec->transfer, progress);
556 /* Helper implementations */
558 static gchar url_encode_hex[16] = {
559 '0', '1', '2', '3', '4', '5', '6', '7',
560 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
563 static gchar* url_encode (const gchar* source)
570 /* Count new string length */
572 for (s = source; *s; s++)
575 if (!((*s >= '0' && *s <= '9') || (*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z')))
579 /* Build encoded string */
581 dest = g_malloc (dest_len + 1);
583 for (s = source, d = dest; *s; s++)
585 if ((*s >= '0' && *s <= '9') || (*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z'))
592 *d++ = url_encode_hex[(*s >> 4) & 0xf];
593 *d++ = url_encode_hex[*s & 0xf];