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