1 /*
2     This file is part of darktable,
3     Copyright (C) 2018-2021 darktable developers.
4 
5     darktable is free software: you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation, either version 3 of the License, or
8     (at your option) any later version.
9 
10     darktable is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with darktable.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #include "bauhaus/bauhaus.h"
20 #include "common/darktable.h"
21 #include "common/file_location.h"
22 #include "common/image.h"
23 #include "common/image_cache.h"
24 #include "common/imageio.h"
25 #include "common/imageio_module.h"
26 #include "common/metadata.h"
27 #include "common/pwstorage/pwstorage.h"
28 #include "common/tags.h"
29 #include "common/curl_tools.h"
30 #include "control/conf.h"
31 #include "control/control.h"
32 #include "dtgtk/button.h"
33 #include "gui/gtk.h"
34 #include "imageio/storage/imageio_storage_api.h"
35 #include <curl/curl.h>
36 #include <json-glib/json-glib.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <inttypes.h>
41 
42 DT_MODULE(1)
43 
44 #define piwigo_EXTRA_VERBOSE FALSE
45 
46 #define MAX_ALBUM_NAME_SIZE 100
47 
48 typedef struct _piwigo_api_context_t
49 {
50   /// curl context
51   CURL *curl_ctx;
52   JsonParser *json_parser;
53   JsonObject *response;
54   gboolean authenticated;
55   gchar *cookie_file;
56   gchar *url;
57 
58   gchar *server;
59   gchar *username;
60   gchar *password;
61   gboolean error_occured;
62 } _piwigo_api_context_t;
63 
64 typedef struct _piwigo_album_t
65 {
66   int64_t id;
67   char name[MAX_ALBUM_NAME_SIZE];
68   char label[MAX_ALBUM_NAME_SIZE];
69   int64_t size;
70 } _piwigo_album_t;
71 
72 typedef struct _piwigo_account_t
73 {
74   gchar *server;
75   gchar *username;
76   gchar *password;
77 } _piwigo_account_t;
78 
79 typedef struct dt_storage_piwigo_gui_data_t
80 {
81   GtkLabel *status_label;
82   GtkEntry *server_entry;
83   GtkEntry *user_entry, *pwd_entry, *new_album_entry;
84   GtkBox *create_box;                               // Create album options...
85   GtkWidget *permission_list;
86   GtkWidget *album_list, *parent_album_list;
87   GtkWidget *account_list;
88 
89   GList *albums;
90   GList *accounts;
91 
92   /** Current Piwigo context for the gui */
93   _piwigo_api_context_t *api;
94 } dt_storage_piwigo_gui_data_t;
95 
96 typedef struct _curl_args_t
97 {
98   char name[100];
99   char value[512];
100 } _curl_args_t;
101 
102 typedef struct dt_storage_piwigo_params_t
103 {
104   _piwigo_api_context_t *api;
105   int64_t album_id;
106   int64_t parent_album_id;
107   char *album;
108   gboolean new_album;
109   int privacy;
110   gboolean export_tags; // deprecated - let here not to change params size. to be removed on next version change
111   gchar *tags;
112 } dt_storage_piwigo_params_t;
113 
114 /* low-level routine doing the HTTP POST request */
115 static void _piwigo_api_post(_piwigo_api_context_t *ctx, GList *args, char *filename, gboolean isauth);
116 
curl_write_data_cb(void * ptr,size_t size,size_t nmemb,void * data)117 static size_t curl_write_data_cb(void *ptr, size_t size, size_t nmemb, void *data)
118 {
119   GString *string = (GString *)data;
120   g_string_append_len(string, ptr, size * nmemb);
121 #if piwigo_EXTRA_VERBOSE == TRUE
122   g_printf("server reply: %s\n", string->str);
123 #endif
124   return size * nmemb;
125 }
126 
_piwigo_query_add_arguments(GList * args,const char * name,const char * value)127 static GList *_piwigo_query_add_arguments(GList *args, const char *name, const char *value)
128 {
129   _curl_args_t *arg = malloc(sizeof(_curl_args_t));
130   g_strlcpy(arg->name, name, sizeof(arg->name));
131   g_strlcpy(arg->value, value, sizeof(arg->value));
132   return g_list_append(args, arg);
133 }
134 
_piwigo_ctx_init(void)135 static _piwigo_api_context_t *_piwigo_ctx_init(void)
136 {
137   _piwigo_api_context_t *ctx = malloc(sizeof(struct _piwigo_api_context_t));
138 
139   ctx->curl_ctx = curl_easy_init();
140   ctx->json_parser = json_parser_new();
141   ctx->authenticated = FALSE;
142   ctx->url = NULL;
143   ctx->cookie_file = NULL;
144   ctx->error_occured = FALSE;
145   return ctx;
146 }
147 
_piwigo_ctx_destroy(_piwigo_api_context_t ** ctx)148 static void _piwigo_ctx_destroy(_piwigo_api_context_t **ctx)
149 {
150   if(*ctx)
151   {
152     curl_easy_cleanup((*ctx)->curl_ctx);
153     if((*ctx)->cookie_file) g_unlink((*ctx)->cookie_file);
154     g_object_unref((*ctx)->json_parser);
155     g_free((*ctx)->cookie_file);
156     g_free((*ctx)->url);
157     g_free((*ctx)->server);
158     g_free((*ctx)->username);
159     g_free((*ctx)->password);
160     free(*ctx);
161     *ctx = NULL;
162   }
163 }
164 
_piwigo_free_account(void * data)165 static void _piwigo_free_account(void *data)
166 {
167   _piwigo_account_t *account = (_piwigo_account_t *)data;
168   g_free(account->server);
169   g_free(account->username);
170   g_free(account->password);
171 }
172 
_piwigo_load_account(dt_storage_piwigo_gui_data_t * ui)173 static void _piwigo_load_account(dt_storage_piwigo_gui_data_t *ui)
174 {
175   if(!ui->accounts)
176   {
177     g_list_free_full(ui->accounts, _piwigo_free_account);
178     ui->accounts = NULL;
179   }
180 
181   GHashTable *table = dt_pwstorage_get("piwigo");
182   GHashTableIter iter;
183   gpointer key, value;
184 
185   g_hash_table_iter_init (&iter, table);
186 
187   while (g_hash_table_iter_next (&iter, &key, &value))
188   {
189     if(key && value)
190     {
191       gchar *data = (gchar *)value;
192       JsonParser *parser = json_parser_new();
193       json_parser_load_from_data(parser, data, strlen(data), NULL);
194       JsonNode *root = json_parser_get_root(parser);
195 
196       if(root)
197       {
198         JsonObject *obj = json_node_get_object(root);
199         _piwigo_account_t *account = malloc(sizeof(_piwigo_account_t));
200 
201         account->server =  g_strdup(json_object_get_string_member(obj, "server"));
202         account->username =  g_strdup(json_object_get_string_member(obj, "username"));
203         account->password =  g_strdup(json_object_get_string_member(obj, "password"));
204 
205         if(account->server && strlen(account->server)>0)
206           ui->accounts = g_list_append(ui->accounts, account);
207         else
208           free(account); // we didn't add account to list, freeing it
209       }
210 
211       g_object_unref(parser);
212     }
213   }
214 
215   g_hash_table_destroy(table);
216 }
217 
_piwigo_get_account(dt_storage_piwigo_gui_data_t * ui,const gchar * server)218 static _piwigo_account_t *_piwigo_get_account(dt_storage_piwigo_gui_data_t *ui, const gchar *server)
219 {
220   if(!server) return NULL;
221 
222   for(const GList *a = ui->accounts; a; a = g_list_next(a))
223   {
224     _piwigo_account_t *account = (_piwigo_account_t *)a->data;;
225     if(account->server && !strcmp(server, account->server)) return account;
226   }
227 
228   return NULL;
229 }
230 
_piwigo_set_account(dt_storage_piwigo_gui_data_t * ui)231 static void _piwigo_set_account(dt_storage_piwigo_gui_data_t *ui)
232 {
233   /// serialize data;
234   JsonBuilder *builder = json_builder_new();
235   json_builder_begin_object(builder);
236   json_builder_set_member_name(builder, "server");
237   json_builder_add_string_value(builder, gtk_entry_get_text(ui->server_entry));
238   json_builder_set_member_name(builder, "username");
239   json_builder_add_string_value(builder, gtk_entry_get_text(ui->user_entry));
240   json_builder_set_member_name(builder, "password");
241   json_builder_add_string_value(builder, gtk_entry_get_text(ui->pwd_entry));
242 
243   json_builder_end_object(builder);
244 
245   JsonNode *node = json_builder_get_root(builder);
246   JsonGenerator *generator = json_generator_new();
247   json_generator_set_root(generator, node);
248 #if JSON_CHECK_VERSION(0, 14, 0)
249   json_generator_set_pretty(generator, FALSE);
250 #endif
251   gchar *data = json_generator_to_data(generator, NULL);
252 
253   json_node_free(node);
254   g_object_unref(generator);
255   g_object_unref(builder);
256 
257   GHashTable *table = dt_pwstorage_get("piwigo");
258   g_hash_table_insert(table, g_strdup(gtk_entry_get_text(ui->server_entry)), data);
259   dt_pwstorage_set("piwigo", table);
260   g_hash_table_destroy(table);
261 }
262 
263 /** Set status connection text */
_piwigo_set_status(dt_storage_piwigo_gui_data_t * ui,gchar * message,gchar * color)264 static void _piwigo_set_status(dt_storage_piwigo_gui_data_t *ui, gchar *message, gchar *color)
265 {
266   if(!color) color = "#ffffff";
267   gchar mup[512] = { 0 };
268   snprintf(mup, sizeof(mup), "<span foreground=\"%s\" ><small>%s</small></span>", color, message);
269   gtk_label_set_markup(ui->status_label, mup);
270   gtk_widget_set_tooltip_markup(GTK_WIDGET(ui->status_label), mup);
271 }
272 
_piwigo_api_post_internal(_piwigo_api_context_t * ctx,GList * args,char * filename,gboolean isauth)273 static int _piwigo_api_post_internal(_piwigo_api_context_t *ctx, GList *args, char *filename, gboolean isauth)
274 {
275   curl_mime *form = NULL;
276 
277   GString *url = g_string_new(ctx->url);
278 
279   // send the requests
280   GString *response = g_string_new("");
281 
282   dt_curl_init(ctx->curl_ctx, piwigo_EXTRA_VERBOSE);
283 
284   curl_easy_setopt(ctx->curl_ctx, CURLOPT_URL, url->str);
285   curl_easy_setopt(ctx->curl_ctx, CURLOPT_POST, 1);
286   curl_easy_setopt(ctx->curl_ctx, CURLOPT_WRITEFUNCTION, curl_write_data_cb);
287   curl_easy_setopt(ctx->curl_ctx, CURLOPT_WRITEDATA, response);
288 
289   if(isauth)
290   {
291     /* construct a temporary file name */
292     char cookie_fmt[PATH_MAX] = { 0 };
293     dt_loc_get_tmp_dir(cookie_fmt, sizeof(cookie_fmt));
294     g_strlcat(cookie_fmt, "/cookies.%.4lf.txt", sizeof(cookie_fmt));
295 
296     ctx->cookie_file = g_strdup_printf(cookie_fmt, dt_get_wtime());
297 
298     // not that this is safe as the cookie file is written only when the curl context is finalized.
299     // At this stage we unlink the file.
300     curl_easy_setopt(ctx->curl_ctx, CURLOPT_COOKIEJAR, ctx->cookie_file);
301   }
302   else
303   {
304     curl_easy_setopt(ctx->curl_ctx, CURLOPT_COOKIEFILE, ctx->cookie_file);
305   }
306 
307   if(filename)
308   {
309     curl_mimepart *field = NULL;
310 
311     form = curl_mime_init(ctx->curl_ctx);
312 
313     for(const GList *a = args; a; a = g_list_next(a))
314     {
315       _curl_args_t *ca = (_curl_args_t *)a->data;
316       field = curl_mime_addpart(form);
317       curl_mime_name(field, ca->name);
318       curl_mime_data(field, ca->value, CURL_ZERO_TERMINATED);
319     }
320 
321     field = curl_mime_addpart(form);
322     curl_mime_name(field, "image");
323     curl_mime_filedata(field, filename);
324 
325     curl_easy_setopt(ctx->curl_ctx, CURLOPT_MIMEPOST, form);
326   }
327   else
328   {
329     GString *gargs = g_string_new("");
330 
331     for(const GList *a = args; a; a = g_list_next(a))
332     {
333       _curl_args_t *ca = (_curl_args_t *)a->data;
334       if(a!=args) g_string_append(gargs, "&");
335       g_string_append(gargs, ca->name);
336       g_string_append(gargs, "=");
337       g_string_append(gargs, ca->value);
338     }
339 
340     curl_easy_setopt(ctx->curl_ctx, CURLOPT_COPYPOSTFIELDS, gargs->str);
341     g_string_free(gargs, TRUE);
342   }
343 
344   int res = curl_easy_perform(ctx->curl_ctx);
345 
346 #if piwigo_EXTRA_VERBOSE == TRUE
347   g_printf("curl_easy_perform status %d\n", res);
348 #endif
349 
350   if(filename) curl_mime_free(form);
351 
352   g_string_free(url, TRUE);
353 
354   ctx->response = NULL;
355 
356   if(res == CURLE_OK)
357   {
358     GError *error = NULL;
359     gboolean ret = json_parser_load_from_data(ctx->json_parser, response->str, response->len, &error);
360     if(!ret) goto cleanup;
361     JsonNode *root = json_parser_get_root(ctx->json_parser);
362     // we should always have a dict
363     if(json_node_get_node_type(root) != JSON_NODE_OBJECT) goto cleanup;
364     ctx->response = json_node_get_object(root);
365     const char *status = json_object_get_string_member(ctx->response, "stat");
366     ctx->error_occured = (status && (strcmp(status,"fail")==0));
367   }
368   else
369     ctx->error_occured = TRUE;
370 
371  cleanup:
372   g_string_free(response, TRUE);
373   return res;
374 }
375 
_piwigo_api_authenticate(_piwigo_api_context_t * ctx)376 static void _piwigo_api_authenticate(_piwigo_api_context_t *ctx)
377 {
378   GList *args = NULL;
379 
380   args = _piwigo_query_add_arguments(args, "method", "pwg.session.login");
381   args = _piwigo_query_add_arguments(args, "username", ctx->username);
382   args = _piwigo_query_add_arguments(args, "password", ctx->password);
383   if(!strcmp(ctx->server, "piwigo.com"))
384     ctx->url = g_strdup_printf("https://%s.piwigo.com/ws.php?format=json", ctx->username);
385   else if(strstr(ctx->server, "http") == ctx->server)
386     ctx->url = g_strdup_printf("%s/ws.php?format=json", ctx->server);
387   else
388     ctx->url = g_strdup_printf("https://%s/ws.php?format=json", ctx->server);
389 
390   _piwigo_api_post(ctx, args, NULL, TRUE);
391 
392   g_list_free(args);
393 }
394 
_piwigo_api_post(_piwigo_api_context_t * ctx,GList * args,char * filename,gboolean isauth)395 static void _piwigo_api_post(_piwigo_api_context_t *ctx, GList *args, char *filename, gboolean isauth)
396 {
397   int res = _piwigo_api_post_internal(ctx, args, filename, isauth);
398 
399   if(res == CURLE_COULDNT_CONNECT || res == CURLE_SSL_CONNECT_ERROR)
400   {
401 #if piwigo_EXTRA_VERBOSE == TRUE
402     g_printf("curl post error (%d), try authentication again\n", res);
403 #endif
404 
405     //  recreate a new CURL connection
406     curl_easy_cleanup(ctx->curl_ctx);
407     ctx->curl_ctx = curl_easy_init();
408     ctx->authenticated = FALSE;
409 
410     if(!isauth)
411     {
412       // an error during the curl post command, could be an authentication issue, try to authenticate again
413       // but only if this is not an authentication post, otherwise this will happen below anyway.
414       _piwigo_api_authenticate(ctx);
415     }
416 
417     // authentication ok, send again
418     if(ctx->response && !ctx->error_occured)
419     {
420       ctx->authenticated = TRUE;
421 #if piwigo_EXTRA_VERBOSE == TRUE
422       g_printf("authenticated again, retry\n");
423 #endif
424       res = _piwigo_api_post_internal(ctx, args, filename, isauth);
425 #if piwigo_EXTRA_VERBOSE == TRUE
426       g_printf("second post exit with status %d\n", res);
427 #endif
428     }
429     else
430     {
431 #if piwigo_EXTRA_VERBOSE == TRUE
432       g_printf("failed second authentication\n");
433 #endif
434     }
435   }
436 }
437 
_piwigo_authenticate(dt_storage_piwigo_gui_data_t * ui)438 static void _piwigo_authenticate(dt_storage_piwigo_gui_data_t *ui)
439 {
440   if(!ui->api) ui->api = _piwigo_ctx_init();
441 
442   ui->api->server = g_strdup(gtk_entry_get_text(ui->server_entry));
443   ui->api->username = g_uri_escape_string(gtk_entry_get_text(ui->user_entry), NULL, FALSE);
444   ui->api->password = g_uri_escape_string(gtk_entry_get_text(ui->pwd_entry), NULL, FALSE);
445 
446   _piwigo_api_authenticate(ui->api);
447 
448   ui->api->authenticated = FALSE;
449 
450   if(ui->api->response && !ui->api->error_occured)
451   {
452     ui->api->authenticated = TRUE;
453     gtk_widget_set_sensitive(GTK_WIDGET(ui->album_list), ui->api->authenticated);
454 
455     if(ui->api->authenticated)
456     {
457       _piwigo_set_status(ui, _("authenticated"), "#7fe07f");
458       dt_conf_set_string("plugins/imageio/storage/export/piwigo/server", ui->api->server);
459       _piwigo_set_account(ui);
460     }
461     else
462     {
463       const gchar *errormessage = json_object_get_string_member(ui->api->response, "message");
464       fprintf(stderr, "[imageio_storage_piwigo] could not authenticate: `%s'!\n", errormessage);
465       _piwigo_set_status(ui, _("not authenticated"), "#e07f7f");
466       _piwigo_ctx_destroy(&ui->api);
467     }
468   }
469   else
470   {
471     _piwigo_set_status(ui, _("not authenticated, cannot reach server"), "#e07f7f");
472     _piwigo_ctx_destroy(&ui->api);
473   }
474 }
475 
_piwigo_entry_changed(GtkEntry * entry,gpointer data)476 static void _piwigo_entry_changed(GtkEntry *entry, gpointer data)
477 {
478   dt_storage_piwigo_gui_data_t *ui = (dt_storage_piwigo_gui_data_t *)data;
479 
480   _piwigo_set_status(ui, _("not authenticated"), "#e07f7f");
481     gtk_widget_set_sensitive(GTK_WIDGET(ui->album_list), FALSE);
482 
483   if(ui->api) _piwigo_ctx_destroy(&ui->api);
484 }
485 
_piwigo_server_entry_changed(GtkEntry * entry,gpointer data)486 static void _piwigo_server_entry_changed(GtkEntry *entry, gpointer data)
487 {
488   dt_storage_piwigo_gui_data_t *ui = (dt_storage_piwigo_gui_data_t *)data;
489 
490   if(ui->api)
491   {
492     _piwigo_set_status(ui, _("not authenticated"), "#e07f7f");
493     _piwigo_ctx_destroy(&ui->api);
494     gtk_widget_set_sensitive(GTK_WIDGET(ui->album_list), FALSE);
495   }
496 }
497 
_piwigo_account_changed(GtkComboBox * cb,gpointer data)498 static void _piwigo_account_changed(GtkComboBox *cb, gpointer data)
499 {
500   dt_storage_piwigo_gui_data_t *ui = (dt_storage_piwigo_gui_data_t *)data;
501   const gchar *value = dt_bauhaus_combobox_get_text(ui->account_list);
502   const _piwigo_account_t *account = _piwigo_get_account(ui, value);
503 
504   if(account)
505   {
506     gtk_entry_set_text(ui->server_entry, account->server);
507     gtk_entry_set_text(ui->user_entry, account->username);
508     gtk_entry_set_text(ui->pwd_entry, account->password);
509   }
510 }
511 
_piwigo_album_changed(GtkComboBox * cb,gpointer data)512 static void _piwigo_album_changed(GtkComboBox *cb, gpointer data)
513 {
514   dt_storage_piwigo_gui_data_t *ui = (dt_storage_piwigo_gui_data_t *)data;
515   const gchar *value = dt_bauhaus_combobox_get_text(ui->album_list);
516 
517   if(value != NULL && strcmp(value, _("create new album")) == 0)
518   {
519     gtk_widget_set_no_show_all(GTK_WIDGET(ui->create_box), FALSE);
520     gtk_widget_show_all(GTK_WIDGET(ui->create_box));
521   }
522   else
523   {
524     gtk_widget_hide(GTK_WIDGET(ui->create_box));
525 
526     // As the album name is have spaces as prefix (for indentation) and a
527     // count of entries in parenthesis as suffix, we need to do some clean-up.
528     gchar *v = g_strstrip(g_strdup(value));
529     gchar *p = v + strlen(v) - 1;
530     if(*p == ')')
531     {
532       while(*p && *p != '(') p--;
533       if(*p == '(')
534       {
535         p--;
536         *p = '\0';
537       }
538     }
539     dt_conf_set_string("storage/piwigo/last_album", v);
540     g_free(v);
541   }
542 }
543 
544 /** Refresh albums */
_piwigo_refresh_albums(dt_storage_piwigo_gui_data_t * ui,const gchar * select_album)545 static void _piwigo_refresh_albums(dt_storage_piwigo_gui_data_t *ui, const gchar *select_album)
546 {
547   gtk_widget_set_sensitive(GTK_WIDGET(ui->album_list), FALSE);
548   gtk_widget_set_sensitive(GTK_WIDGET(ui->parent_album_list), FALSE);
549 
550   if(ui->api == NULL || ui->api->authenticated == FALSE)
551   {
552     _piwigo_authenticate(ui);
553     if(ui->api == NULL || !ui->api->authenticated) return;
554   }
555 
556   gchar *to_select;
557   int index = 0;
558 
559   // get the new album name, it will be checked in the
560   if(select_album == NULL)
561   {
562     to_select = g_strdup(dt_bauhaus_combobox_get_text(ui->album_list));
563     if(to_select)
564     {
565       // cut the count of picture in album to get the name only
566       gchar *p = to_select;
567       while(*p)
568       {
569         if(*p == ' ' && *(p+1) == '(')
570         {
571           *p = '\0';
572           break;
573         }
574         p++;
575       }
576     }
577   }
578   else
579     to_select = g_strdup(select_album);
580 
581   // First clear the combobox except first 2 items (none / create new album)
582   dt_bauhaus_combobox_clear(ui->album_list);
583   dt_bauhaus_combobox_clear(ui->parent_album_list);
584   g_list_free(ui->albums);
585   ui->albums = NULL;
586 
587   GList *args = NULL;
588 
589   args = _piwigo_query_add_arguments(args, "method", "pwg.categories.getList");
590   args = _piwigo_query_add_arguments(args, "cat_id", "0");
591   args = _piwigo_query_add_arguments(args, "recursive", "true");
592 
593   _piwigo_api_post(ui->api, args, NULL, FALSE);
594 
595   g_list_free(args);
596 
597   if(ui->api->response && !ui->api->error_occured)
598   {
599     dt_bauhaus_combobox_add(ui->album_list, _("create new album"));
600     dt_bauhaus_combobox_add(ui->parent_album_list, _("---"));
601 
602     JsonObject *result = json_node_get_object(json_object_get_member(ui->api->response, "result"));
603     JsonArray *albums = json_object_get_array_member(result, "categories");
604 
605     if(json_array_get_length(albums)>0 && index==0) index = 1;
606     if(index > json_array_get_length(albums) - 1) index = json_array_get_length(albums) - 1;
607 
608     for(int i = 0; i < json_array_get_length(albums); i++)
609     {
610       char data[MAX_ALBUM_NAME_SIZE] = { 0 };
611       JsonObject *album = json_array_get_object_element(albums, i);
612 
613       _piwigo_album_t *new_album = g_malloc0(sizeof(struct _piwigo_album_t));
614 
615       g_strlcpy(new_album->name, json_object_get_string_member(album, "name"), sizeof(new_album->name));
616       new_album->id = json_object_get_int_member(album, "id");
617       new_album->size = json_object_get_int_member(album, "nb_images");
618       const int isroot = json_object_get_null_member(album, "id_uppercat");
619       int indent = 0;
620 
621       if(!isroot)
622       {
623         const char *hierarchy = json_object_get_string_member(album, "uppercats");
624         char const *p = hierarchy;
625         while(*p++) if(*p == ',') indent++;
626       }
627 
628       snprintf(data, sizeof(data), "%*c%s (%"PRId64")", indent * 3, ' ', new_album->name, new_album->size);
629 
630       if(to_select && !strcmp(new_album->name, to_select)) index = i + 1;
631 
632       g_strlcpy(new_album->label, data, sizeof(new_album->label));
633 
634       ui->albums = g_list_append(ui->albums, new_album);
635 
636       dt_bauhaus_combobox_add_aligned(ui->album_list, data, DT_BAUHAUS_COMBOBOX_ALIGN_LEFT);
637       dt_bauhaus_combobox_add_aligned(ui->parent_album_list, data, DT_BAUHAUS_COMBOBOX_ALIGN_LEFT);
638     }
639   }
640   else
641     dt_control_log(_("cannot refresh albums"));
642 
643   g_free(to_select);
644 
645   gtk_widget_set_sensitive(GTK_WIDGET(ui->album_list), TRUE);
646   gtk_widget_set_sensitive(GTK_WIDGET(ui->parent_album_list), TRUE);
647   dt_bauhaus_combobox_set(ui->album_list, index);
648   dt_bauhaus_combobox_set(ui->parent_album_list, 0);
649 }
650 
651 
_piwigo_api_create_new_album(dt_storage_piwigo_params_t * p)652 static gboolean _piwigo_api_create_new_album(dt_storage_piwigo_params_t *p)
653 {
654   GList *args = NULL;
655 
656   args = _piwigo_query_add_arguments(args, "method", "pwg.categories.add");
657   args = _piwigo_query_add_arguments(args, "name", p->album);
658   if(p->parent_album_id != 0)
659   {
660     char pid[100];
661     snprintf(pid, sizeof(pid), "%"PRId64, p->parent_album_id);
662     args = _piwigo_query_add_arguments(args, "parent", pid);
663   }
664   args = _piwigo_query_add_arguments(args, "status", p->privacy==0?"public":"private");
665 
666   _piwigo_api_post(p->api, args, NULL, FALSE);
667 
668   g_list_free(args);
669 
670   if (!p->api->response || p->api->error_occured)
671   {
672     return FALSE;
673   }
674   else
675   {
676     JsonObject *result = json_node_get_object(json_object_get_member(p->api->response, "result"));
677     // set new album id in parameter
678     p->album_id = json_object_get_int_member(result, "id");
679   }
680 
681   return TRUE;
682 }
683 
_piwigo_api_upload_photo(dt_storage_piwigo_params_t * p,gchar * fname,gchar * author,gchar * caption,gchar * description)684 static gboolean _piwigo_api_upload_photo(dt_storage_piwigo_params_t *p, gchar *fname,
685                                          gchar *author, gchar *caption, gchar *description)
686 {
687   GList *args = NULL;
688   char cat[10];
689   char privacy[10];
690 
691   snprintf(cat, sizeof(cat), "%"PRId64, p->album_id);
692   snprintf(privacy, sizeof(privacy), "%d", p->privacy);
693 
694   args = _piwigo_query_add_arguments(args, "method", "pwg.images.addSimple");
695   args = _piwigo_query_add_arguments(args, "image", fname);
696   args = _piwigo_query_add_arguments(args, "category", cat);
697   args = _piwigo_query_add_arguments(args, "level", privacy);
698 
699   if(caption && strlen(caption)>0)
700     args = _piwigo_query_add_arguments(args, "name", caption);
701 
702   if(author && strlen(author)>0)
703     args = _piwigo_query_add_arguments(args, "author", author);
704 
705   if(description && strlen(description)>0)
706     args = _piwigo_query_add_arguments(args, "comment", description);
707 
708   if(p->tags && strlen(p->tags)>0)
709     args = _piwigo_query_add_arguments(args, "tags", p->tags);
710   _piwigo_api_post(p->api, args, fname, FALSE);
711 
712   g_list_free(args);
713 
714   return !p->api->error_occured;
715 }
716 
717 // Login button pressed...
_piwigo_login_clicked(GtkButton * button,gpointer data)718 static void _piwigo_login_clicked(GtkButton *button, gpointer data)
719 {
720   dt_storage_piwigo_gui_data_t *ui = (dt_storage_piwigo_gui_data_t *)data;
721   _piwigo_ctx_destroy(&ui->api);
722 
723   gchar *last_album = dt_conf_get_string("storage/piwigo/last_album");
724   _piwigo_refresh_albums(ui, last_album);
725   dt_conf_set_string("storage/piwigo/last_album", last_album);
726   g_free(last_album);
727 }
728 
729 // Refresh button pressed...
_piwigo_refresh_clicked(GtkButton * button,gpointer data)730 static void _piwigo_refresh_clicked(GtkButton *button, gpointer data)
731 {
732   dt_storage_piwigo_gui_data_t *ui = (dt_storage_piwigo_gui_data_t *)data;
733 
734   gchar *last_album = dt_conf_get_string("storage/piwigo/last_album");
735   _piwigo_refresh_albums(ui, NULL);
736   dt_conf_set_string("storage/piwigo/last_album", last_album);
737   g_free(last_album);
738 }
739 
name(const struct dt_imageio_module_storage_t * self)740 const char *name(const struct dt_imageio_module_storage_t *self)
741 {
742   return _("piwigo");
743 }
744 
gui_init(dt_imageio_module_storage_t * self)745 void gui_init(dt_imageio_module_storage_t *self)
746 {
747   self->gui_data = (dt_storage_piwigo_gui_data_t *)g_malloc0(sizeof(dt_storage_piwigo_gui_data_t));
748   dt_storage_piwigo_gui_data_t *ui = self->gui_data;
749 
750   ui->albums = NULL;
751   ui->accounts = NULL;
752   ui->api = NULL;
753 
754   self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
755 
756   _piwigo_load_account(ui);
757 
758   gchar *server = dt_conf_get_string("plugins/imageio/storage/export/piwigo/server");
759 
760   // look for last server information
761   _piwigo_account_t *last_account = _piwigo_get_account(ui, server);
762 
763   GtkWidget *hbox, *label, *button;
764 
765   // account
766   ui->account_list = dt_bauhaus_combobox_new(NULL);
767   dt_bauhaus_widget_set_label(ui->account_list, NULL, N_("accounts"));
768   int account_index = -1, index=0;
769   for(const GList *a = ui->accounts; a; a = g_list_next(a))
770   {
771     _piwigo_account_t *account = (_piwigo_account_t *)a->data;
772     dt_bauhaus_combobox_add(ui->account_list, account->server);
773     if(!strcmp(account->server, server)) account_index = index;
774     index++;
775   }
776   gtk_widget_set_hexpand(ui->account_list, TRUE);
777   g_signal_connect(G_OBJECT(ui->account_list), "value-changed", G_CALLBACK(_piwigo_account_changed), (gpointer)ui);
778   gtk_box_pack_start(GTK_BOX(self->widget), ui->account_list, FALSE, FALSE, 0);
779 
780   // server
781   hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
782   ui->server_entry = GTK_ENTRY(gtk_entry_new());
783   gtk_widget_set_tooltip_text(GTK_WIDGET(ui->server_entry),
784                               _("the server name\ndefault protocol is https\nspecify http:// if non secure server"));
785   gtk_widget_set_hexpand(GTK_WIDGET(ui->server_entry), TRUE);
786   gtk_entry_set_text(ui->server_entry, last_account?last_account->server:"piwigo.com");
787   g_signal_connect(G_OBJECT(ui->server_entry), "changed", G_CALLBACK(_piwigo_server_entry_changed), (gpointer)ui);
788   gtk_entry_set_width_chars(GTK_ENTRY(ui->server_entry), 0);
789   gtk_box_pack_start(GTK_BOX(hbox), dt_ui_label_new(_("server")), FALSE, FALSE, 0);
790   gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(ui->server_entry), TRUE, TRUE, 0);
791   gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(hbox), TRUE, TRUE, 0);
792   g_free(server);
793 
794   // login
795   hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
796   ui->user_entry = GTK_ENTRY(gtk_entry_new());
797   gtk_widget_set_hexpand(GTK_WIDGET(ui->user_entry), TRUE);
798   gtk_entry_set_text(ui->user_entry, last_account?last_account->username:"");
799   g_signal_connect(G_OBJECT(ui->user_entry), "changed", G_CALLBACK(_piwigo_entry_changed), (gpointer)ui);
800   gtk_entry_set_width_chars(GTK_ENTRY(ui->user_entry), 0);
801   gtk_box_pack_start(GTK_BOX(hbox), dt_ui_label_new(_("user")), FALSE, FALSE, 0);
802   gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(ui->user_entry), TRUE, TRUE, 0);
803   gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(hbox), TRUE, TRUE, 0);
804 
805   // password
806   hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
807   ui->pwd_entry = GTK_ENTRY(gtk_entry_new());
808   gtk_entry_set_visibility(GTK_ENTRY(ui->pwd_entry), FALSE);
809   gtk_widget_set_hexpand(GTK_WIDGET(ui->pwd_entry), TRUE);
810   gtk_entry_set_text(ui->pwd_entry, last_account?last_account->password:"");
811   g_signal_connect(G_OBJECT(ui->pwd_entry), "changed", G_CALLBACK(_piwigo_entry_changed), (gpointer)ui);
812   gtk_entry_set_width_chars(GTK_ENTRY(ui->pwd_entry), 0);
813   gtk_box_pack_start(GTK_BOX(hbox), dt_ui_label_new(_("password")), FALSE, FALSE, 0);
814   gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(ui->pwd_entry), TRUE, TRUE, 0);
815   gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(hbox), TRUE, TRUE, 0);
816 
817   // login button
818   button = gtk_button_new_with_label(_("login"));
819   gtk_widget_set_tooltip_text(button, _("piwigo login"));
820   g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(_piwigo_login_clicked), (gpointer)ui);
821   gtk_box_pack_start(GTK_BOX(self->widget), button, FALSE, FALSE, 0);
822 
823   // status area
824   ui->status_label = GTK_LABEL(gtk_label_new(NULL));
825   gtk_label_set_ellipsize(ui->status_label, PANGO_ELLIPSIZE_END);
826   gtk_widget_set_halign(GTK_WIDGET(ui->status_label), GTK_ALIGN_START);
827   gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(ui->status_label), FALSE, FALSE, 0);
828 
829   // select account
830   if(account_index != -1) dt_bauhaus_combobox_set(ui->account_list, account_index);
831 
832   // permissions list
833   ui->permission_list = dt_bauhaus_combobox_new(NULL);
834   dt_bauhaus_widget_set_label(ui->permission_list, NULL, N_("visible to"));
835   dt_bauhaus_combobox_add(ui->permission_list, _("everyone"));
836   dt_bauhaus_combobox_add(ui->permission_list, _("contacts"));
837   dt_bauhaus_combobox_add(ui->permission_list, _("friends"));
838   dt_bauhaus_combobox_add(ui->permission_list, _("family"));
839   dt_bauhaus_combobox_add(ui->permission_list, _("you"));
840   dt_bauhaus_combobox_set(ui->permission_list, 0); // Set default permission to everyone
841   gtk_box_pack_start(GTK_BOX(self->widget), ui->permission_list, FALSE, FALSE, 0);
842 
843   // album list
844   hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
845 
846   ui->album_list = dt_bauhaus_combobox_new(NULL); // Available albums
847   dt_bauhaus_widget_set_label(ui->album_list, NULL, N_("album"));
848   g_signal_connect(G_OBJECT(ui->album_list), "value-changed", G_CALLBACK(_piwigo_album_changed), (gpointer)ui);
849   gtk_widget_set_sensitive(ui->album_list, FALSE);
850   gtk_box_pack_start(GTK_BOX(hbox), ui->album_list, TRUE, TRUE, 0);
851 
852   button = dtgtk_button_new(dtgtk_cairo_paint_refresh, CPF_NONE, NULL);
853   gtk_widget_set_tooltip_text(button, _("refresh album list"));
854   g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(_piwigo_refresh_clicked), (gpointer)ui);
855   gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
856 
857   gtk_box_pack_start(GTK_BOX(self->widget), hbox, FALSE, FALSE, 0);
858 
859   // new album
860   ui->create_box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0));
861   gtk_widget_set_no_show_all(GTK_WIDGET(ui->create_box), TRUE);
862   gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(ui->create_box), FALSE, FALSE, 0);
863 
864   hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
865 
866   label = gtk_label_new(_("title"));
867   g_object_set(G_OBJECT(label), "xalign", 0.0, (gchar *)0);
868   gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
869 
870   ui->new_album_entry = GTK_ENTRY(gtk_entry_new()); // Album title
871   gtk_entry_set_text(ui->new_album_entry, _("new album"));
872   gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(ui->new_album_entry), TRUE, TRUE, 0);
873   gtk_entry_set_width_chars(GTK_ENTRY(ui->new_album_entry), 0);
874 
875   gtk_box_pack_start(ui->create_box, hbox, FALSE, FALSE, 0);
876 
877   // parent album list
878   ui->parent_album_list = dt_bauhaus_combobox_new(NULL); // Available albums
879   dt_bauhaus_widget_set_label(ui->parent_album_list, NULL, N_("parent album"));
880   gtk_widget_set_sensitive(ui->parent_album_list, TRUE);
881   gtk_box_pack_start(ui->create_box, ui->parent_album_list, TRUE, TRUE, 0);
882 
883   _piwigo_set_status(ui, _("click login button to start"), "#ffffff");
884 }
885 
gui_cleanup(dt_imageio_module_storage_t * self)886 void gui_cleanup(dt_imageio_module_storage_t *self)
887 {
888   g_free(self->gui_data);
889 }
890 
gui_reset(dt_imageio_module_storage_t * self)891 void gui_reset(dt_imageio_module_storage_t *self)
892 {
893 }
894 
_finalize_store(gpointer user_data)895 static gboolean _finalize_store(gpointer user_data)
896 {
897   dt_storage_piwigo_gui_data_t *g = (dt_storage_piwigo_gui_data_t *)user_data;
898   _piwigo_refresh_albums(g, dt_bauhaus_combobox_get_text(g->album_list));
899   return FALSE;
900 }
901 
finalize_store(struct dt_imageio_module_storage_t * self,dt_imageio_module_data_t * data)902 void finalize_store(struct dt_imageio_module_storage_t *self, dt_imageio_module_data_t *data)
903 {
904   g_main_context_invoke(NULL, _finalize_store, self->gui_data);
905 }
906 
store(dt_imageio_module_storage_t * self,dt_imageio_module_data_t * sdata,const int imgid,dt_imageio_module_format_t * format,dt_imageio_module_data_t * fdata,const int num,const int total,const gboolean high_quality,const gboolean upscale,const gboolean export_masks,dt_colorspaces_color_profile_type_t icc_type,const gchar * icc_filename,dt_iop_color_intent_t icc_intent,dt_export_metadata_t * metadata)907 int store(dt_imageio_module_storage_t *self, dt_imageio_module_data_t *sdata, const int imgid,
908           dt_imageio_module_format_t *format, dt_imageio_module_data_t *fdata, const int num, const int total,
909           const gboolean high_quality, const gboolean upscale, const gboolean export_masks,
910           dt_colorspaces_color_profile_type_t icc_type, const gchar *icc_filename, dt_iop_color_intent_t icc_intent,
911           dt_export_metadata_t *metadata)
912 {
913   dt_storage_piwigo_gui_data_t *ui = self->gui_data;
914 
915   gint result = 0;
916 
917   const char *ext = format->extension(fdata);
918 
919   // Let's upload image...
920 
921   /* construct a temporary file name */
922   char fname[PATH_MAX] = { 0 };
923   dt_loc_get_tmp_dir(fname, sizeof(fname));
924   g_strlcat(fname, "/darktable.XXXXXX.", sizeof(fname));
925   g_strlcat(fname, ext, sizeof(fname));
926 
927   char *caption = NULL;
928   char *description = NULL;
929   char *author = NULL;
930 
931   gint fd = g_mkstemp(fname);
932   if(fd == -1)
933   {
934     dt_control_log("failed to create temporary image for piwigo export");
935     fprintf(stderr, "failed to create tempfile: %s\n", fname);
936     return 1;
937   }
938   close(fd);
939 
940   if ((metadata->flags & DT_META_METADATA) && !(metadata->flags & DT_META_CALCULATED))
941   {
942     const dt_image_t *img = dt_image_cache_get(darktable.image_cache, imgid, 'r');
943   // If title is not existing, then use the filename without extension. If not, then use title instead
944     GList *title = dt_metadata_get(img->id, "Xmp.dc.title", NULL);
945     if(title != NULL)
946     {
947       caption = g_strdup(title->data);
948       g_list_free_full(title, &g_free);
949     }
950     else
951     {
952       caption = g_path_get_basename(img->filename);
953       gchar *dot = g_strrstr(caption, ".");
954       if(dot) dot[0] = '\0'; // chop extension...
955     }
956 
957     GList *desc = dt_metadata_get(img->id, "Xmp.dc.description", NULL);
958     if(desc != NULL)
959     {
960       description = g_strdup(desc->data);
961       g_list_free_full(desc, &g_free);
962     }
963     dt_image_cache_read_release(darktable.image_cache, img);
964 
965     GList *auth = dt_metadata_get(img->id, "Xmp.dc.creator", NULL);
966     if(auth != NULL)
967     {
968       author = g_strdup(auth->data);
969       g_list_free_full(auth, &g_free);
970     }
971   }
972   if(dt_imageio_export(imgid, fname, format, fdata, high_quality, upscale, TRUE, export_masks, icc_type, icc_filename,
973                        icc_intent, self, sdata, num, total, metadata) != 0)
974   {
975     fprintf(stderr, "[imageio_storage_piwigo] could not export to file: `%s'!\n", fname);
976     dt_control_log(_("could not export to file `%s'!"), fname);
977     result = 1;
978     goto cleanup;
979   }
980   dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
981   {
982     gboolean status = TRUE;
983     dt_storage_piwigo_params_t *p = (dt_storage_piwigo_params_t *)sdata;
984 
985     if(metadata->flags & DT_META_TAG)
986     {
987       GList *tags_list = dt_tag_get_list_export(imgid, metadata->flags);
988       p->tags = dt_util_glist_to_str(",", tags_list);
989       g_list_free_full(tags_list, g_free);
990     }
991 
992     if(p->new_album)
993     {
994       status = _piwigo_api_create_new_album(p);
995       if(!status) dt_control_log(_("cannot create a new piwigo album!"));
996     }
997 
998     if(status)
999     {
1000       status = _piwigo_api_upload_photo(p, fname, author, caption, description);
1001       if(!status)
1002       {
1003         fprintf(stderr, "[imageio_storage_piwigo] could not upload to piwigo!\n");
1004         dt_control_log(_("could not upload to piwigo!"));
1005         result = 1;
1006       }
1007       else if (p->new_album)
1008       {
1009         // we do not want to create more albums when multiple upload
1010         p->new_album = FALSE;
1011         _piwigo_refresh_albums(ui, p->album);
1012       }
1013     }
1014     if (p->tags)
1015     {
1016       g_free(p->tags);
1017       p->tags = NULL;
1018     }
1019   }
1020   dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
1021 
1022 cleanup:
1023 
1024   // And remove from filesystem..
1025   g_unlink(fname);
1026   g_free(caption);
1027   g_free(description);
1028   g_free(author);
1029 
1030   if(!result)
1031   {
1032     // this makes sense only if the export was successful
1033     dt_control_log(ngettext("%d/%d exported to piwigo webalbum", "%d/%d exported to piwigo webalbum", num),
1034                    num, total);
1035   }
1036   return result;
1037 }
1038 
params_size(dt_imageio_module_storage_t * self)1039 size_t params_size(dt_imageio_module_storage_t *self)
1040 {
1041   return sizeof(int64_t);
1042 }
1043 
init(dt_imageio_module_storage_t * self)1044 void init(dt_imageio_module_storage_t *self)
1045 {
1046 }
1047 
_piwigo_album_id(const gchar * name,GList * albums)1048 static uint64_t _piwigo_album_id(const gchar *name, GList *albums)
1049 {
1050   uint64_t id = 0;
1051 
1052   for(const GList *a = albums; a; a = g_list_next(a))
1053   {
1054     _piwigo_album_t *album = (_piwigo_album_t *)a->data;
1055     if(!strcmp(name, album->label))
1056     {
1057       id = album->id;
1058       break;
1059     }
1060   }
1061 
1062   return id;
1063 }
1064 
get_params(dt_imageio_module_storage_t * self)1065 void *get_params(dt_imageio_module_storage_t *self)
1066 {
1067   dt_storage_piwigo_gui_data_t *ui = (dt_storage_piwigo_gui_data_t *)self->gui_data;
1068   if(!ui) return NULL; // gui not initialized, CLI mode
1069   dt_storage_piwigo_params_t *p = (dt_storage_piwigo_params_t *)g_malloc0(sizeof(dt_storage_piwigo_params_t));
1070 
1071   if(!p) return NULL;
1072 
1073   // fill d from controls in ui
1074   if(ui->api && ui->api->authenticated == TRUE)
1075   {
1076     // create a new context for the import. set username/password to be able to connect.
1077     p->api = _piwigo_ctx_init();
1078     p->api->authenticated = FALSE;
1079     p->api->server = g_strdup(ui->api->server);
1080     p->api->username = g_strdup(ui->api->username);
1081     p->api->password = g_strdup(ui->api->password);
1082     _piwigo_api_authenticate(p->api);
1083 
1084     int index = dt_bauhaus_combobox_get(ui->album_list);
1085 
1086     p->album_id = 0;
1087     p->tags = NULL;
1088 
1089     switch(dt_bauhaus_combobox_get(ui->permission_list))
1090     {
1091       case 0:                // everyone
1092         p->privacy = 0;
1093         break;
1094       case 1:                // contacts
1095         p->privacy = 1;
1096         break;
1097       case 2:                // friends
1098         p->privacy = 2;
1099         break;
1100       case 3:                // family
1101         p->privacy = 4;
1102         break;
1103       default:               // you / admin
1104         p->privacy = 8;
1105     }
1106 
1107     if(index >= 0)
1108     {
1109       switch(index)
1110       {
1111         case 0: // Create album
1112           p->parent_album_id = _piwigo_album_id(dt_bauhaus_combobox_get_text(ui->parent_album_list), ui->albums);
1113           p->album = g_strdup(gtk_entry_get_text(ui->new_album_entry));
1114           p->new_album = TRUE;
1115           break;
1116 
1117         default:
1118           p->album = g_strdup(dt_bauhaus_combobox_get_text(ui->album_list));
1119           p->new_album = FALSE;
1120 
1121           if(p->album == NULL)
1122           {
1123             // Something went wrong...
1124             fprintf(stderr, "Something went wrong.. album index %d = NULL\n", index - 2);
1125             goto cleanup;
1126           }
1127 
1128           p->album_id = _piwigo_album_id(p->album, ui->albums);
1129 
1130           if(!p->album_id)
1131           {
1132             fprintf(stderr, "[imageio_storage_piwigo] cannot find album `%s'!\n", p->album);
1133             goto cleanup;
1134           }
1135 
1136           break;
1137       }
1138     }
1139     else
1140       goto cleanup;
1141   }
1142   else
1143     goto cleanup;
1144 
1145   return p;
1146 
1147  cleanup:
1148     g_free(p);
1149     return NULL;
1150 }
1151 
set_params(dt_imageio_module_storage_t * self,const void * params,const int size)1152 int set_params(dt_imageio_module_storage_t *self, const void *params, const int size)
1153 {
1154   if(size != self->params_size(self)) return 1;
1155   // gui stuff not updated, as sensitive user data is not stored in the preset.
1156   // TODO: store name/hash in kwallet/etc module and get encrypted stuff from there!
1157   return 0;
1158 }
1159 
supported(dt_imageio_module_storage_t * storage,dt_imageio_module_format_t * format)1160 int supported(dt_imageio_module_storage_t *storage, dt_imageio_module_format_t *format)
1161 {
1162   if(strcmp(format->mime(NULL), "image/jpeg") == 0)
1163     return 1;
1164   else if(strcmp(format->mime(NULL), "image/png") == 0)
1165     return 1;
1166 
1167   return 0;
1168 }
1169 
free_params(dt_imageio_module_storage_t * self,dt_imageio_module_data_t * params)1170 void free_params(dt_imageio_module_storage_t *self, dt_imageio_module_data_t *params)
1171 {
1172   dt_storage_piwigo_params_t *p = (dt_storage_piwigo_params_t *)params;
1173 
1174   if(p)
1175   {
1176     g_free(p->album);
1177     g_free(p->tags);
1178     _piwigo_ctx_destroy(&p->api);
1179     free(p);
1180   }
1181 }
1182 
1183 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
1184 // vim: shiftwidth=2 expandtab tabstop=2 cindent
1185 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1186