1 /*
2 * frogr-controller.c -- Controller of the whole application
3 *
4 * Copyright (C) 2009-2020 Mario Sanchez Prada
5 * Authors: Mario Sanchez Prada <msanchez@gnome.org>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of version 3 of the GNU General Public
9 * License as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>
18 *
19 */
20
21 #include "frogr-controller.h"
22
23 #include "frogr-about-dialog.h"
24 #include "frogr-account.h"
25 #include "frogr-add-tags-dialog.h"
26 #include "frogr-add-to-group-dialog.h"
27 #include "frogr-add-to-set-dialog.h"
28 #include "frogr-auth-dialog.h"
29 #include "frogr-config.h"
30 #include "frogr-create-new-set-dialog.h"
31 #include "frogr-details-dialog.h"
32 #include "frogr-file-loader.h"
33 #include "frogr-global-defs.h"
34 #include "frogr-main-view.h"
35 #include "frogr-settings-dialog.h"
36 #include "frogr-util.h"
37
38 #include <config.h>
39 #include <flicksoup/flicksoup.h>
40 #include <glib/gi18n.h>
41 #include <gtk/gtk.h>
42 #include <json-glib/json-glib.h>
43 #include <string.h>
44
45 #define API_KEY "18861766601de84f0921ce6be729f925"
46 #define SHARED_SECRET "6233fbefd85f733a"
47
48 #define DEFAULT_TIMEOUT 100
49 #define MAX_AUTH_TIMEOUT 60000
50
51 #define MAX_ATTEMPTS 5
52
53
54 struct _FrogrController
55 {
56 GObject parent;
57
58 FrogrControllerState state;
59
60 FrogrMainView *mainview;
61 FrogrConfig *config;
62 FrogrAccount *account;
63
64 FspSession *session;
65 GList *cancellables;
66
67 /* We use this booleans as flags */
68 gboolean app_running;
69 gboolean fetching_token_replacement;
70 gboolean fetching_auth_url;
71 gboolean fetching_auth_token;
72 gboolean fetching_photosets;
73 gboolean fetching_groups;
74 gboolean fetching_tags;
75 gboolean setting_license;
76 gboolean setting_location;
77 gboolean setting_replace_date_posted;
78 gboolean adding_to_set;
79 gboolean adding_to_group;
80
81 gboolean photosets_fetched;
82 gboolean groups_fetched;
83 gboolean tags_fetched;
84
85 /* Event sources IDs for dialogs to be shown on idle */
86 guint show_details_dialog_source_id;
87 guint show_add_tags_dialog_source_id;
88 guint show_create_new_set_dialog_source_id;
89 guint show_add_to_set_dialog_source_id;
90 guint show_add_to_group_dialog_source_id;
91 };
92
93 G_DEFINE_TYPE (FrogrController, frogr_controller, G_TYPE_OBJECT)
94
95
96 /* Signals */
97 enum {
98 STATE_CHANGED,
99 ACTIVE_ACCOUNT_CHANGED,
100 ACCOUNTS_CHANGED,
101 N_SIGNALS
102 };
103
104 static guint signals[N_SIGNALS] = { 0 };
105
106 static FrogrController *_instance = NULL;
107
108 typedef enum {
109 AFTER_UPLOAD_OP_SETTING_LICENSE,
110 AFTER_UPLOAD_OP_SETTING_LOCATION,
111 AFTER_UPLOAD_OP_SETTING_REPLACE_DATE_POSTED,
112 AFTER_UPLOAD_OP_ADDING_TO_SET,
113 AFTER_UPLOAD_OP_ADDING_TO_GROUP,
114 N_AFTER_UPLOAD_OPS
115 } AfterUploadOp;
116
117 typedef struct {
118 gboolean retrieve_everything;
119 gboolean force_extra_data;
120 } FetchAccountInfoData;
121
122 typedef struct {
123 GSList *pictures;
124 GSList *current;
125 guint index;
126 guint n_pictures;
127 gint upload_attempts;
128 GError *error;
129 } UploadPicturesData;
130
131 typedef struct {
132 FrogrController *controller;
133 FrogrPicture *picture;
134 GSList *photosets;
135 GSList *groups;
136 gint after_upload_attempts[N_AFTER_UPLOAD_OPS];
137 GCancellable *cancellable;
138 UploadPicturesData *up_data;
139 } UploadOnePictureData;
140
141 typedef struct {
142 FrogrController *controller;
143 GCancellable *cancellable;
144 } CancellableOperationData;
145
146 typedef enum {
147 FETCHING_NOTHING,
148 FETCHING_TOKEN_REPLACEMENT,
149 FETCHING_AUTH_URL,
150 FETCHING_AUTH_TOKEN,
151 FETCHING_ACCOUNT_INFO,
152 FETCHING_ACCOUNT_EXTRA_INFO,
153 FETCHING_PHOTOSETS,
154 FETCHING_GROUPS,
155 FETCHING_TAGS
156 } FetchingActivity;
157
158 /* Prototypes */
159
160 static gboolean _load_pictures_on_idle (gpointer data);
161
162 static gboolean _load_project_file_on_idle (gpointer data);
163
164 static void _g_application_startup_cb (GApplication *app, gpointer data);
165
166 static void _g_application_activate_cb (GApplication *app, gpointer data);
167
168 static void _g_application_open_files_cb (GApplication *app, GFile **files, gint n_files, gchar *hint, gpointer data);
169
170 static void _g_application_shutdown_cb (GApplication *app, gpointer data);
171
172 static void _set_active_account (FrogrController *self, FrogrAccount *account);
173
174 static void _set_state (FrogrController *self, FrogrControllerState state);
175
176 static GCancellable *_register_new_cancellable (FrogrController *self);
177
178 static void _clear_cancellable (FrogrController *self, GCancellable *cancellable);
179
180 static void _handle_flicksoup_error (FrogrController *self, GError *error, gboolean notify_user);
181
182 static void _show_auth_failed_dialog (GtkWindow *parent, const gchar *message, gboolean auto_retry);
183
184 static void _show_auth_failed_dialog_and_retry (GtkWindow *parent, const gchar *message);
185
186 static void _data_fraction_sent_cb (FspSession *session, gdouble fraction, gpointer data);
187
188 static void _auth_failed_dialog_response_cb (GtkDialog *dialog, gint response, gpointer data);
189
190 static void _get_auth_url_cb (GObject *obj, GAsyncResult *res, gpointer data);
191
192 static void _complete_auth_cb (GObject *object, GAsyncResult *result, gpointer data);
193
194 static void _exchange_token_cb (GObject *object, GAsyncResult *result, gpointer data);
195
196 static gboolean _cancel_authorization_on_timeout (gpointer data);
197
198 static gboolean _should_retry_operation (GError *error, gint attempts);
199
200 static void _invalidate_extra_data (FrogrController *self);
201
202 static void _update_upload_progress (FrogrController *self, UploadPicturesData *up_data);
203
204 static void _upload_next_picture (FrogrController *self, UploadPicturesData *up_data);
205
206 static void _upload_picture (FrogrController *self, FrogrPicture *picture, UploadPicturesData *up_data);
207
208 static void _upload_picture_cb (GObject *object, GAsyncResult *res, gpointer data);
209
210 static void _finish_upload_one_picture_process (FrogrController *self, UploadOnePictureData *uop_data);
211
212 static void _finish_upload_pictures_process (FrogrController *self, UploadPicturesData *up_data);
213
214 static void _perform_after_upload_operations (FrogrController *controller, UploadOnePictureData *uop_data);
215
216 static void _set_license_cb (GObject *object, GAsyncResult *res, gpointer data);
217
218 static void _set_license_for_picture (FrogrController *self, UploadOnePictureData *uop_data);
219
220 static void _set_location_cb (GObject *object, GAsyncResult *res, gpointer data);
221
222 static void _set_location_for_picture (FrogrController *self, UploadOnePictureData *uop_data);
223
224 static void _set_replace_date_posted_cb (GObject *object, GAsyncResult *res, gpointer data);
225
226 static void _set_replace_date_posted_for_picture (FrogrController *self, UploadOnePictureData *uop_data);
227
228 static gboolean _add_picture_to_photosets_or_create (FrogrController *self, UploadOnePictureData *uop_data);
229
230 static void _create_photoset_for_picture (FrogrController *self, UploadOnePictureData *uop_data);
231
232 static void _create_photoset_cb (GObject *object, GAsyncResult *res, gpointer data);
233
234 static void _add_picture_to_photoset (FrogrController *self, UploadOnePictureData *uop_data);
235
236 static void _add_to_photoset_cb (GObject *object, GAsyncResult *res, gpointer data);
237
238 static gboolean _add_picture_to_groups (FrogrController *self, UploadOnePictureData *uop_data);
239
240 static void _add_picture_to_group (FrogrController *self, UploadOnePictureData *uop_data);
241
242 static void _add_to_group_cb (GObject *object, GAsyncResult *res, gpointer data);
243
244 static gboolean _complete_picture_upload (gpointer data);
245
246 static void _on_file_loaded (FrogrFileLoader *loader, FrogrPicture *picture, FrogrController *self);
247
248 static void _on_files_loaded (FrogrFileLoader *loader, FrogrController *self);
249
250 static void _on_model_deserialized (FrogrModel *model, FrogrController *self);
251
252 static void _fetch_everything (FrogrController *self, gboolean force_extra_data);
253
254 static void _fetch_account_info (FrogrController *self);
255
256 static void _fetch_account_info_finish (FrogrController *self, FetchAccountInfoData *data);
257
258 static void _fetch_account_basic_info (FrogrController *self, FetchAccountInfoData *data);
259
260 static void _fetch_account_basic_info_cb (GObject *object, GAsyncResult *res, FetchAccountInfoData *data);
261
262 static void _fetch_account_upload_status (FrogrController *self, FetchAccountInfoData *data);
263
264 static void _fetch_account_upload_status_cb (GObject *object, GAsyncResult *res, FetchAccountInfoData *data);
265
266 static void _fetch_extra_data (FrogrController *self, gboolean force);
267
268 static void _fetch_photosets (FrogrController *self);
269
270 static void _fetch_photosets_cb (GObject *object, GAsyncResult *res, gpointer data);
271
272 static void _fetch_groups (FrogrController *self);
273
274 static void _fetch_groups_cb (GObject *object, GAsyncResult *res, gpointer data);
275
276 static void _fetch_tags (FrogrController *self);
277
278 static void _fetch_tags_cb (GObject *object, GAsyncResult *res, gpointer data);
279
280 static void _dispose_slist_of_objects (GSList *objects);
281
282 static gboolean _show_progress_on_idle (gpointer data);
283
284 static gboolean _show_details_dialog_on_idle (GSList *pictures);
285
286 static gboolean _show_add_tags_dialog_on_idle (GSList *pictures);
287
288 static gboolean _show_create_new_set_dialog_on_idle (GSList *pictures);
289
290 static gboolean _show_add_to_set_dialog_on_idle (GSList *pictures);
291
292 static gboolean _show_add_to_group_dialog_on_idle (GSList *pictures);
293
294 static gboolean _is_modal_dialog_about_to_be_shown (FrogrController *self);
295
296 /* Private functions */
297
298 static gboolean
_load_pictures_on_idle(gpointer data)299 _load_pictures_on_idle (gpointer data)
300 {
301 FrogrController *fcontroller = NULL;
302 GSList *fileuris = NULL;
303
304 g_return_val_if_fail (data, FALSE);
305
306 fcontroller = frogr_controller_get_instance ();
307 fileuris = (GSList *)data;
308
309 frogr_controller_load_pictures (fcontroller, fileuris);
310 return G_SOURCE_REMOVE;
311 }
312
313 static gboolean
_load_project_file_on_idle(gpointer data)314 _load_project_file_on_idle (gpointer data)
315 {
316 FrogrController *fcontroller = NULL;
317 g_autofree gchar *filepath = NULL;
318
319 g_return_val_if_fail (data, FALSE);
320
321 fcontroller = frogr_controller_get_instance ();
322 filepath = (gchar *)data;
323
324 frogr_controller_open_project_from_file (fcontroller, filepath);
325
326 return G_SOURCE_REMOVE;
327 }
328
329 static void
_g_application_startup_cb(GApplication * app,gpointer data)330 _g_application_startup_cb (GApplication *app, gpointer data)
331 {
332 FrogrController *self = FROGR_CONTROLLER (data);
333 FrogrModel *model = NULL;
334 FrogrAccount *account = NULL;
335 gboolean use_dark_theme;
336
337 DEBUG ("%s", "Application started!\n");
338
339 /* Create UI window */
340 self->mainview = frogr_main_view_new (GTK_APPLICATION (app));
341 g_object_add_weak_pointer (G_OBJECT (self->mainview),
342 (gpointer) & self->mainview);
343 /* Connect to signals */
344 model = frogr_main_view_get_model (self->mainview);
345 g_signal_connect (G_OBJECT (model), "model-deserialized",
346 G_CALLBACK (_on_model_deserialized),
347 self);
348
349 /* Start on idle state */
350 _set_state (self, FROGR_STATE_IDLE);
351
352 /* Select the dark theme if needed */
353 use_dark_theme = frogr_config_get_use_dark_theme (self->config);
354 frogr_controller_set_use_dark_theme (self, use_dark_theme);
355
356 /* Select the right account */
357 account = frogr_config_get_active_account (self->config);
358 if (account)
359 _set_active_account (self, account);
360
361 /* Set HTTP proxy if needed */
362 if (frogr_config_get_use_proxy (self->config))
363 {
364 const gchar *host = frogr_config_get_proxy_host (self->config);
365 const gchar *port = frogr_config_get_proxy_port (self->config);
366 const gchar *username = frogr_config_get_proxy_username (self->config);
367 const gchar *password = frogr_config_get_proxy_password (self->config);
368 frogr_controller_set_proxy (self, FALSE, host, port, username, password);
369 }
370 }
371
372 static void
_g_application_activate_cb(GApplication * app,gpointer data)373 _g_application_activate_cb (GApplication *app, gpointer data)
374 {
375 FrogrController *self = FROGR_CONTROLLER (data);
376
377 DEBUG ("%s", "Application activated!\n");
378
379 /* Show the UI */
380 gtk_widget_show (GTK_WIDGET(self->mainview));
381 gtk_window_present (GTK_WINDOW (self->mainview));
382 }
383
384 static void
_g_application_open_files_cb(GApplication * app,GFile ** files,gint n_files,gchar * hint,gpointer data)385 _g_application_open_files_cb (GApplication *app, GFile **files, gint n_files, gchar *hint, gpointer data)
386 {
387 FrogrController *self = FROGR_CONTROLLER (data);
388 g_autoptr(GFileInfo) file_info = NULL;
389 gboolean is_project_file = FALSE;
390
391 DEBUG ("Trying to open %d files\n", n_files);
392
393 /* Check the first file's MIME type to check whether we are loading
394 media files (pictures, videos) or project files (text files). */
395 file_info = g_file_query_info (files[0],
396 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
397 G_FILE_QUERY_INFO_NONE,
398 NULL,
399 NULL);
400 if (file_info)
401 {
402 const gchar *mime_type = NULL;
403
404 /* Thus, if the file being opened is a text file, we will assume
405 is a project file, since the other kind of files frogr can
406 handle two type of files: media files (pictures and videos),
407 normally with MIME types 'image' and 'video' */
408 mime_type = g_file_info_get_content_type (file_info);
409 is_project_file = g_str_has_prefix (mime_type, "text");
410 }
411
412 if (is_project_file)
413 {
414 gchar *filepath = NULL;
415
416 /* Assume the first file is a project file and ignore the rest */
417 filepath = g_strdup (g_file_get_path (files[0]));
418 gdk_threads_add_idle (_load_project_file_on_idle, filepath);
419 }
420 else
421 {
422 GSList *fileuris = NULL;
423 gchar *fileuri = NULL;
424 int i = 0;
425
426 /* Load media files otherwise */
427 for (i = 0; i < n_files; i++)
428 {
429 fileuri = g_strdup (g_file_get_uri (files[i]));
430 if (fileuri)
431 fileuris = g_slist_append (fileuris, fileuri);
432 }
433
434 if (fileuris)
435 gdk_threads_add_idle (_load_pictures_on_idle, fileuris);
436 }
437
438 /* Show the UI */
439 gtk_widget_show (GTK_WIDGET(self->mainview));
440 gtk_window_present (GTK_WINDOW (self->mainview));
441 }
442
443 static void
_g_application_shutdown_cb(GApplication * app,gpointer data)444 _g_application_shutdown_cb (GApplication *app, gpointer data)
445 {
446 FrogrController *self = FROGR_CONTROLLER (data);
447
448 DEBUG ("%s", "Shutting down application...");
449
450 if (self->app_running)
451 {
452 while (gtk_events_pending ())
453 gtk_main_iteration ();
454
455 gtk_widget_destroy (GTK_WIDGET (self->mainview));
456 self->app_running = FALSE;
457
458 frogr_config_save_all (self->config);
459 }
460 }
461
462 static void
_set_active_account(FrogrController * self,FrogrAccount * account)463 _set_active_account (FrogrController *self, FrogrAccount *account)
464 {
465 FrogrAccount *new_account = NULL;
466 gboolean accounts_changed = FALSE;
467 const gchar *token = NULL;
468 const gchar *token_secret = NULL;
469 const gchar *account_version = NULL;
470
471 g_return_if_fail(FROGR_IS_CONTROLLER (self));
472
473 new_account = FROGR_IS_ACCOUNT (account) ? g_object_ref (account) : NULL;
474 if (new_account)
475 {
476 const gchar *new_username = NULL;
477
478 new_username = frogr_account_get_username (new_account);
479 if (!frogr_config_set_active_account (self->config, new_username))
480 {
481 /* Fallback to manually creating a new account */
482 frogr_account_set_is_active (new_account, TRUE);
483 accounts_changed = frogr_config_add_account (self->config, new_account);
484 }
485
486 /* Get the token for setting it later on */
487 token = frogr_account_get_token (new_account);
488 token_secret = frogr_account_get_token_secret (new_account);
489 }
490 else if (FROGR_IS_ACCOUNT (self->account))
491 {
492 /* If NULL is passed it means 'delete current account' */
493 const gchar *username = frogr_account_get_username (self->account);
494 accounts_changed = frogr_config_remove_account (self->config, username);
495 }
496
497 /* Update internal pointer in the controller */
498 if (self->account)
499 g_object_unref (self->account);
500 self->account = new_account;
501
502 /* Update token in the session */
503 fsp_session_set_token (self->session, token);
504 fsp_session_set_token_secret (self->session, token_secret);
505
506 /* Fetch needed info for this account or update tokens stored */
507 account_version = new_account ? frogr_account_get_version (new_account) : NULL;
508 if (account_version && g_strcmp0 (account_version, ACCOUNTS_CURRENT_VERSION))
509 {
510 self->fetching_token_replacement = TRUE;
511 fsp_session_exchange_token (self->session, NULL, _exchange_token_cb, self);
512 gdk_threads_add_timeout (DEFAULT_TIMEOUT, (GSourceFunc) _show_progress_on_idle, GINT_TO_POINTER (FETCHING_TOKEN_REPLACEMENT));
513
514 /* Make sure we show proper feedback if connection is too slow */
515 gdk_threads_add_timeout (MAX_AUTH_TIMEOUT, (GSourceFunc) _cancel_authorization_on_timeout, self);
516 }
517 else
518 {
519 /* If a new account has been activated, fetch everything */
520 if (new_account)
521 _fetch_everything (self, TRUE);
522
523 /* Emit proper signals */
524 g_signal_emit (self, signals[ACTIVE_ACCOUNT_CHANGED], 0, new_account);
525 if (accounts_changed)
526 g_signal_emit (self, signals[ACCOUNTS_CHANGED], 0);
527 }
528
529 /* Save new state in configuration */
530 frogr_config_save_accounts (self->config);
531 }
532
533 void
_set_state(FrogrController * self,FrogrControllerState state)534 _set_state (FrogrController *self, FrogrControllerState state)
535 {
536 self->state = state;
537 g_signal_emit (self, signals[STATE_CHANGED], 0, state);
538 }
539
540 static GCancellable *
_register_new_cancellable(FrogrController * self)541 _register_new_cancellable (FrogrController *self)
542 {
543 GCancellable *cancellable = NULL;
544 cancellable = g_cancellable_new();
545 self->cancellables = g_list_prepend (self->cancellables, cancellable);
546
547 return cancellable;
548 }
549
550 static void
_clear_cancellable(FrogrController * self,GCancellable * cancellable)551 _clear_cancellable (FrogrController *self, GCancellable *cancellable)
552 {
553 GList *item = NULL;
554
555 item = g_list_find (self->cancellables, cancellable);
556 if (item)
557 {
558 g_object_unref (G_OBJECT (item->data));
559 self->cancellables = g_list_delete_link (self->cancellables, item);
560 }
561 }
562
563 static void
_handle_flicksoup_error(FrogrController * self,GError * error,gboolean notify_user)564 _handle_flicksoup_error (FrogrController *self, GError *error, gboolean notify_user)
565 {
566 void (* error_function) (GtkWindow *, const gchar *) = NULL;
567 g_autofree gchar *msg = NULL;
568 g_autofree gchar *video_quota_msg = NULL;
569 gint n_videos = 0;
570
571 error_function = frogr_util_show_error_dialog;
572 switch (error->code)
573 {
574 case FSP_ERROR_CANCELLED:
575 msg = g_strdup (_("Process cancelled"));
576 error_function = NULL; /* Don't notify the user about this */
577 break;
578
579 case FSP_ERROR_NETWORK_ERROR:
580 msg = g_strdup (_("Connection error:\nNetwork not available"));
581 break;
582
583 case FSP_ERROR_CLIENT_ERROR:
584 msg = g_strdup (_("Connection error:\nBad request"));
585 break;
586
587 case FSP_ERROR_SERVER_ERROR:
588 msg = g_strdup (_("Connection error:\nServer-side error"));
589 break;
590
591 case FSP_ERROR_UPLOAD_INVALID_FILE:
592 msg = g_strdup (_("Error uploading:\nFile invalid"));
593 break;
594
595 case FSP_ERROR_UPLOAD_QUOTA_PICTURE_EXCEEDED:
596 msg = g_strdup (_("Error uploading picture:\nQuota exceeded"));
597 break;
598
599 case FSP_ERROR_UPLOAD_QUOTA_VIDEO_EXCEEDED:
600 n_videos = frogr_account_get_current_videos (self->account);
601 video_quota_msg = g_strdup_printf (ngettext ("Quota exceeded (limit: %d video per month)",
602 "Quota exceeded (limit: %d videos per month)", n_videos),
603 n_videos);
604 msg = g_strdup_printf ("%s\n%s",
605 _("Error uploading video:\nYou can't upload more videos with this account"),
606 video_quota_msg);
607 break;
608
609 case FSP_ERROR_PHOTO_NOT_FOUND:
610 msg = g_strdup (_("Error:\nPhoto not found"));
611 break;
612
613 case FSP_ERROR_PHOTOSET_PHOTO_ALREADY_IN:
614 msg = g_strdup (_("Error:\nPhoto already in photoset"));
615 break;
616
617 case FSP_ERROR_GROUP_PHOTO_ALREADY_IN:
618 msg = g_strdup (_("Error:\nPhoto already in group"));
619 break;
620
621 case FSP_ERROR_GROUP_PHOTO_IN_MAX_NUM:
622 msg = g_strdup (_("Error:\nPhoto already in the maximum number of groups possible"));
623 break;
624
625 case FSP_ERROR_GROUP_LIMIT_REACHED:
626 msg = g_strdup (_("Error:\nGroup limit already reached"));
627 break;
628
629 case FSP_ERROR_GROUP_PHOTO_ADDED_TO_QUEUE:
630 msg = g_strdup (_("Error:\nPhoto added to group's queue"));
631 break;
632
633 case FSP_ERROR_GROUP_PHOTO_ALREADY_IN_QUEUE:
634 msg = g_strdup (_("Error:\nPhoto already added to group's queue"));
635 break;
636
637 case FSP_ERROR_GROUP_CONTENT_NOT_ALLOWED:
638 msg = g_strdup (_("Error:\nContent not allowed for this group"));
639 break;
640
641 case FSP_ERROR_AUTHENTICATION_FAILED:
642 msg = g_strdup_printf (_("Authorization failed.\nPlease try again"));
643 error_function = _show_auth_failed_dialog_and_retry;
644 break;
645
646 case FSP_ERROR_NOT_AUTHENTICATED:
647 frogr_controller_revoke_authorization (self);
648 msg = g_strdup_printf (_("Error\n%s is not properly authorized to upload pictures "
649 "to Flickr.\nPlease re-authorize it"), APP_SHORTNAME);
650 break;
651
652 case FSP_ERROR_OAUTH_UNKNOWN_ERROR:
653 msg = g_strdup_printf (_("Unable to authenticate in Flickr\nPlease try again."));
654 break;
655
656 case FSP_ERROR_OAUTH_NOT_AUTHORIZED_YET:
657 msg = g_strdup_printf (_("You have not properly authorized %s yet.\n"
658 "Please try again."), APP_SHORTNAME);
659 break;
660
661 case FSP_ERROR_OAUTH_VERIFIER_INVALID:
662 msg = g_strdup_printf (_("Invalid verification code.\nPlease try again."));
663 break;
664
665 case FSP_ERROR_SERVICE_UNAVAILABLE:
666 msg = g_strdup_printf (_("Error:\nService not available"));
667 break;
668
669 default:
670 /* General error: just dump the raw error description */
671 msg = g_strdup_printf (_("An error happened: %s."), error->message);
672 }
673
674 if (notify_user && error_function)
675 error_function (GTK_WINDOW (self->mainview), msg);
676
677 DEBUG ("%s", msg);
678 }
679
680 static void
_show_auth_failed_dialog(GtkWindow * parent,const gchar * message,gboolean auto_retry)681 _show_auth_failed_dialog (GtkWindow *parent, const gchar *message, gboolean auto_retry)
682 {
683 GtkWidget *dialog = NULL;
684
685 dialog = gtk_message_dialog_new (parent,
686 GTK_DIALOG_MODAL,
687 GTK_MESSAGE_ERROR,
688 GTK_BUTTONS_CLOSE,
689 "%s", message);
690 gtk_window_set_title (GTK_WINDOW (dialog), APP_SHORTNAME);
691
692 g_signal_connect (G_OBJECT (dialog), "response",
693 G_CALLBACK (_auth_failed_dialog_response_cb),
694 GINT_TO_POINTER ((gint)auto_retry));
695
696 gtk_widget_show (dialog);
697 }
698
699 static void
_show_auth_failed_dialog_and_retry(GtkWindow * parent,const gchar * message)700 _show_auth_failed_dialog_and_retry (GtkWindow *parent, const gchar *message)
701 {
702 _show_auth_failed_dialog (parent, message, TRUE);
703 }
704
705 static void
_data_fraction_sent_cb(FspSession * session,gdouble fraction,gpointer data)706 _data_fraction_sent_cb (FspSession *session, gdouble fraction, gpointer data)
707 {
708 FrogrController *self = NULL;
709 self = FROGR_CONTROLLER(data);
710 frogr_main_view_set_progress_status_fraction (self->mainview, fraction);
711 }
712
713 static void
_auth_failed_dialog_response_cb(GtkDialog * dialog,gint response,gpointer data)714 _auth_failed_dialog_response_cb (GtkDialog *dialog, gint response, gpointer data)
715 {
716 gboolean auto_retry = (gboolean)GPOINTER_TO_INT (data);
717 if (auto_retry && response == GTK_RESPONSE_CLOSE)
718 {
719 frogr_controller_show_auth_dialog (frogr_controller_get_instance ());
720 DEBUG ("%s", "Showing the authorization dialog once again...");
721 }
722
723 gtk_widget_destroy (GTK_WIDGET (dialog));
724 }
725
726 static void
_get_auth_url_cb(GObject * obj,GAsyncResult * res,gpointer data)727 _get_auth_url_cb (GObject *obj, GAsyncResult *res, gpointer data)
728 {
729 FspSession *session = NULL;
730 CancellableOperationData *co_data = NULL;
731 FrogrController *self = NULL;
732 g_autoptr(GError) error = NULL;
733 g_autofree gchar *auth_url = NULL;
734
735 session = FSP_SESSION (obj);
736 co_data = (CancellableOperationData*) data;
737 self = co_data->controller;
738
739 auth_url = fsp_session_get_auth_url_finish (session, res, &error);
740 if (auth_url != NULL && error == NULL)
741 {
742 g_autofree gchar *url_with_permissions = NULL;
743
744 url_with_permissions = g_strdup_printf ("%s&perms=write", auth_url);
745 frogr_util_open_uri (url_with_permissions);
746
747 /* Run the auth confirmation dialog */
748 frogr_auth_dialog_show (GTK_WINDOW (self->mainview), CONFIRM_AUTHORIZATION);
749
750 DEBUG ("Auth URL: %s", url_with_permissions);
751 }
752
753 if (error != NULL)
754 {
755 _handle_flicksoup_error (self, error, TRUE);
756 DEBUG ("Error getting auth URL: %s", error->message);
757 }
758
759 frogr_main_view_hide_progress (self->mainview);
760
761 _clear_cancellable (self, co_data->cancellable);
762 g_slice_free (CancellableOperationData, co_data);
763
764 self->fetching_auth_url = FALSE;
765 }
766
767 static void
_complete_auth_cb(GObject * object,GAsyncResult * result,gpointer data)768 _complete_auth_cb (GObject *object, GAsyncResult *result, gpointer data)
769 {
770 FspSession *session = NULL;
771 CancellableOperationData *co_data = NULL;
772 FrogrController *controller = NULL;
773 FspDataAuthToken *auth_token = NULL;
774 g_autoptr(GError) error = NULL;
775
776 session = FSP_SESSION (object);
777 co_data = (CancellableOperationData*) data;
778 controller = co_data->controller;
779
780 auth_token = fsp_session_complete_auth_finish (session, result, &error);
781 if (auth_token)
782 {
783 if (auth_token->token)
784 {
785 FrogrAccount *account = NULL;
786
787 /* Set and save the auth token and the settings to disk */
788 account = frogr_account_new_full (auth_token->token, auth_token->token_secret);
789 frogr_account_set_id (account, auth_token->nsid);
790 frogr_account_set_username (account, auth_token->username);
791 frogr_account_set_fullname (account, auth_token->fullname);
792
793 /* Frogr always always ask for 'write' permissions at the moment */
794 frogr_account_set_permissions (account, "write");
795
796 /* Try to set the active account again */
797 _set_active_account (controller, account);
798
799 DEBUG ("%s", "Authorization successfully completed!");
800 }
801
802 fsp_data_free (FSP_DATA (auth_token));
803 }
804
805 if (error != NULL)
806 {
807 _handle_flicksoup_error (controller, error, TRUE);
808 DEBUG ("Authorization failed: %s", error->message);
809 }
810
811 frogr_main_view_hide_progress (controller->mainview);
812
813 _clear_cancellable (controller, co_data->cancellable);
814 g_slice_free (CancellableOperationData, co_data);
815
816 controller->fetching_auth_token = FALSE;
817 }
818
819 static void
_exchange_token_cb(GObject * object,GAsyncResult * result,gpointer data)820 _exchange_token_cb (GObject *object, GAsyncResult *result, gpointer data)
821 {
822 FspSession *session = NULL;
823 FrogrController *controller = NULL;
824 g_autoptr(GError) error = NULL;
825
826 session = FSP_SESSION (object);
827 controller = FROGR_CONTROLLER (data);
828
829 fsp_session_exchange_token_finish (session, result, &error);
830 if (error == NULL)
831 {
832 const gchar *token = NULL;
833 const gchar *token_secret = NULL;
834
835 /* If everything went fine, get the token and secret from the
836 session and update the current user account */
837 token = fsp_session_get_token (controller->session);
838 frogr_account_set_token (controller->account, token);
839
840 token_secret = fsp_session_get_token_secret (controller->session);
841 frogr_account_set_token_secret (controller->account, token_secret);
842
843 /* Make sure we update the version for the account too */
844 frogr_account_set_version (controller->account, ACCOUNTS_CURRENT_VERSION);
845
846 /* Finally, try to set the active account again */
847 _set_active_account (controller, controller->account);
848 }
849 else
850 {
851 _handle_flicksoup_error (controller, error, TRUE);
852 DEBUG ("Authorization failed: %s", error->message);
853 }
854
855 frogr_main_view_hide_progress (controller->mainview);
856 controller->fetching_token_replacement = FALSE;
857 }
858
859 static gboolean
_cancel_authorization_on_timeout(gpointer data)860 _cancel_authorization_on_timeout (gpointer data)
861 {
862 FrogrController *self = FROGR_CONTROLLER (data);
863
864 if (self->fetching_auth_url || self->fetching_auth_token || self->fetching_token_replacement)
865 {
866 frogr_controller_cancel_ongoing_requests (self);
867 frogr_main_view_hide_progress (self->mainview);
868
869 _show_auth_failed_dialog (GTK_WINDOW (self->mainview), _("Authorization failed (timed out)"), FALSE);
870 }
871
872 return G_SOURCE_REMOVE;
873 }
874
875 static gboolean
_should_retry_operation(GError * error,gint attempts)876 _should_retry_operation (GError *error, gint attempts)
877 {
878 if (error->code == FSP_ERROR_CANCELLED
879 || error->code == FSP_ERROR_UPLOAD_INVALID_FILE
880 || error->code == FSP_ERROR_UPLOAD_QUOTA_PICTURE_EXCEEDED
881 || error->code == FSP_ERROR_UPLOAD_QUOTA_VIDEO_EXCEEDED
882 || error->code == FSP_ERROR_OAUTH_NOT_AUTHORIZED_YET
883 || error->code == FSP_ERROR_NOT_AUTHENTICATED
884 || error->code == FSP_ERROR_NOT_ENOUGH_PERMISSIONS
885 || error->code == FSP_ERROR_INVALID_API_KEY)
886 {
887 /* We are pretty sure we don't want to retry in these cases */
888 return FALSE;
889 }
890
891 return attempts < MAX_ATTEMPTS;
892 }
893
894 static void
_invalidate_extra_data(FrogrController * self)895 _invalidate_extra_data (FrogrController *self)
896 {
897 /* Just reset the flags */
898 self->photosets_fetched = FALSE;
899 self->groups_fetched = FALSE;
900 self->tags_fetched = FALSE;
901 }
902
903 static void
_update_upload_progress(FrogrController * self,UploadPicturesData * up_data)904 _update_upload_progress (FrogrController *self, UploadPicturesData *up_data)
905 {
906 g_autofree gchar *description = NULL;
907 g_autofree gchar *status_text = NULL;
908
909 if (up_data->current)
910 {
911 FrogrPicture *picture = FROGR_PICTURE (up_data->current->data);
912 g_autofree gchar *title = g_strdup (frogr_picture_get_title (picture));
913
914 /* Update progress */
915 if (up_data->upload_attempts > 0)
916 {
917 description = g_strdup_printf (_("Retrying Upload (attempt %d/%d)…"),
918 (up_data->upload_attempts), MAX_ATTEMPTS);
919 }
920 else
921 {
922 description = g_strdup_printf (_("Uploading '%s'…"), title);
923 }
924 status_text = g_strdup_printf ("%d / %d", up_data->index, up_data->n_pictures);
925 }
926 frogr_main_view_set_progress_description(self->mainview, description);
927 frogr_main_view_set_progress_status_text (self->mainview, status_text);
928 }
929
930 static void
_upload_next_picture(FrogrController * self,UploadPicturesData * up_data)931 _upload_next_picture (FrogrController *self, UploadPicturesData *up_data)
932 {
933 /* Advance the list only if not in the first element */
934 if (up_data->index > 0)
935 up_data->current = g_slist_next(up_data->current);
936
937 if (up_data->current)
938 {
939 FrogrPicture *picture = FROGR_PICTURE (up_data->current->data);
940
941 up_data->index++;
942 up_data->upload_attempts = 0;
943 up_data->error = NULL;
944
945 _update_upload_progress (self, up_data);
946 _upload_picture (self, picture, up_data);
947 }
948 else
949 {
950 /* Hide progress bar dialog and finish */
951 frogr_main_view_hide_progress (self->mainview);
952 _finish_upload_pictures_process (self, up_data);
953 }
954 }
955
956 static void
_upload_picture(FrogrController * self,FrogrPicture * picture,UploadPicturesData * up_data)957 _upload_picture (FrogrController *self, FrogrPicture *picture, UploadPicturesData *up_data)
958 {
959 UploadOnePictureData *uop_data = NULL;
960 FspVisibility public_visibility = FSP_VISIBILITY_NONE;
961 FspVisibility family_visibility = FSP_VISIBILITY_NONE;
962 FspVisibility friend_visibility = FSP_VISIBILITY_NONE;
963 FspSafetyLevel safety_level = FSP_SAFETY_LEVEL_NONE;
964 FspContentType content_type = FSP_CONTENT_TYPE_NONE;
965 FspSearchScope search_scope = FSP_SEARCH_SCOPE_NONE;
966
967 g_return_if_fail(FROGR_IS_CONTROLLER (self));
968 g_return_if_fail(FROGR_IS_PICTURE (picture));
969
970 uop_data = g_slice_new0 (UploadOnePictureData);
971 uop_data->controller = self;
972 uop_data->picture = picture;
973 uop_data->photosets = NULL;
974 uop_data->groups = NULL;
975 uop_data->cancellable = _register_new_cancellable (self);
976 uop_data->up_data = up_data;
977
978 g_object_ref (picture);
979
980 public_visibility = frogr_picture_is_public (picture) ? FSP_VISIBILITY_YES : FSP_VISIBILITY_NO;
981 family_visibility = frogr_picture_is_family (picture) ? FSP_VISIBILITY_YES : FSP_VISIBILITY_NO;
982 friend_visibility = frogr_picture_is_friend (picture) ? FSP_VISIBILITY_YES : FSP_VISIBILITY_NO;
983 safety_level = frogr_picture_get_safety_level (picture);
984 content_type = frogr_picture_get_content_type (picture);
985 search_scope = frogr_picture_show_in_search (picture) ? FSP_SEARCH_SCOPE_PUBLIC : FSP_SEARCH_SCOPE_HIDDEN;
986
987 /* Connect to this signal to report progress to the user */
988 g_signal_connect (G_OBJECT (self->session), "data-fraction-sent",
989 G_CALLBACK (_data_fraction_sent_cb),
990 self);
991
992 fsp_session_upload (self->session,
993 frogr_picture_get_fileuri (picture),
994 frogr_picture_get_title (picture),
995 frogr_picture_get_description (picture),
996 frogr_picture_get_tags (picture),
997 public_visibility,
998 family_visibility,
999 friend_visibility,
1000 safety_level,
1001 content_type,
1002 search_scope,
1003 uop_data->cancellable,
1004 _upload_picture_cb, uop_data);
1005 }
1006
1007 static void
_upload_picture_cb(GObject * object,GAsyncResult * res,gpointer data)1008 _upload_picture_cb (GObject *object, GAsyncResult *res, gpointer data)
1009 {
1010 FspSession *session = NULL;
1011 UploadOnePictureData *uop_data = NULL;
1012 UploadPicturesData *up_data = NULL;
1013 FrogrController *controller = NULL;
1014 FrogrPicture *picture = NULL;
1015 g_autofree gchar *photo_id = NULL;
1016 GError *error = NULL;
1017
1018 session = FSP_SESSION (object);
1019 uop_data = (UploadOnePictureData*) data;
1020 controller = uop_data->controller;
1021 picture = uop_data->picture;
1022
1023 photo_id = fsp_session_upload_finish (session, res, &error);
1024 if (photo_id)
1025 frogr_picture_set_id (picture, photo_id);
1026
1027 /* Stop reporting to the user */
1028 g_signal_handlers_disconnect_by_func (controller->session, _data_fraction_sent_cb, controller);
1029
1030 up_data = uop_data->up_data;
1031 if (g_cancellable_is_cancelled (uop_data->cancellable)) {
1032 _complete_picture_upload (uop_data);
1033 return;
1034 }
1035
1036 if (error && _should_retry_operation (error, up_data->upload_attempts))
1037 {
1038 up_data->upload_attempts++;
1039 _update_upload_progress (controller, up_data);
1040
1041 DEBUG("Error uploading picture %s: %s. Retrying... (attempt %d / %d)",
1042 frogr_picture_get_title (picture), error->message,
1043 up_data->upload_attempts, MAX_ATTEMPTS);
1044 g_error_free (error);
1045
1046 /* Try again! */
1047 _finish_upload_one_picture_process (controller, uop_data);
1048 _upload_picture (controller, picture, up_data);
1049 }
1050 else
1051 {
1052 if (!error)
1053 _perform_after_upload_operations (controller, uop_data);
1054 else
1055 {
1056 DEBUG ("Error uploading picture %s: %s",
1057 frogr_picture_get_title (picture), error->message);
1058 uop_data->up_data->error = error;
1059 }
1060
1061 /* Complete the upload process when possible */
1062 gdk_threads_add_timeout (DEFAULT_TIMEOUT, _complete_picture_upload, uop_data);
1063 }
1064 }
1065
1066 static void
_finish_upload_one_picture_process(FrogrController * self,UploadOnePictureData * uop_data)1067 _finish_upload_one_picture_process (FrogrController *self, UploadOnePictureData *uop_data)
1068 {
1069 g_object_unref (uop_data->picture);
1070 if (uop_data->cancellable)
1071 _clear_cancellable (self, uop_data->cancellable);
1072 g_slice_free (UploadOnePictureData, uop_data);
1073 }
1074
1075 static void
_finish_upload_pictures_process(FrogrController * self,UploadPicturesData * up_data)1076 _finish_upload_pictures_process (FrogrController *self, UploadPicturesData *up_data)
1077 {
1078 if (!up_data->error)
1079 {
1080 /* Fetch sets and tags (if needed) right after finishing */
1081 _fetch_photosets (self);
1082 _fetch_tags (self);
1083
1084 DEBUG ("%s", "Finished uploading pictures!");
1085 }
1086 else
1087 {
1088 _handle_flicksoup_error (self, up_data->error, TRUE);
1089 DEBUG ("Error uploading pictures: %s", up_data->error->message);
1090 g_error_free (up_data->error);
1091 }
1092
1093 /* Change state and clean up */
1094 _set_state (self, FROGR_STATE_IDLE);
1095
1096 if (up_data->pictures)
1097 g_slist_free_full (up_data->pictures, g_object_unref);
1098
1099 g_slice_free (UploadPicturesData, up_data);
1100 }
1101
1102 static void
_perform_after_upload_operations(FrogrController * controller,UploadOnePictureData * uop_data)1103 _perform_after_upload_operations (FrogrController *controller, UploadOnePictureData *uop_data)
1104 {
1105 if (frogr_picture_get_license (uop_data->picture) != FSP_LICENSE_NONE)
1106 {
1107 uop_data->after_upload_attempts[AFTER_UPLOAD_OP_SETTING_LICENSE] = 0;
1108 _set_license_for_picture (controller, uop_data);
1109 }
1110
1111 if (frogr_picture_send_location (uop_data->picture)
1112 && frogr_picture_get_location (uop_data->picture) != NULL)
1113 {
1114 uop_data->after_upload_attempts[AFTER_UPLOAD_OP_SETTING_LOCATION] = 0;
1115 _set_location_for_picture (controller, uop_data);
1116 }
1117
1118 if (frogr_picture_replace_date_posted (uop_data->picture))
1119 {
1120 uop_data->after_upload_attempts[AFTER_UPLOAD_OP_SETTING_REPLACE_DATE_POSTED] = 0;
1121 _set_replace_date_posted_for_picture (controller, uop_data);
1122 }
1123
1124 if (frogr_picture_get_photosets (uop_data->picture))
1125 {
1126 uop_data->photosets = frogr_picture_get_photosets (uop_data->picture);
1127 _add_picture_to_photosets_or_create (controller, uop_data);
1128 }
1129
1130 if (frogr_picture_get_groups (uop_data->picture))
1131 {
1132 uop_data->groups = frogr_picture_get_groups (uop_data->picture);
1133 _add_picture_to_groups (controller, uop_data);
1134 }
1135 }
1136
1137 static void
_set_license_cb(GObject * object,GAsyncResult * res,gpointer data)1138 _set_license_cb (GObject *object, GAsyncResult *res, gpointer data)
1139 {
1140 FspSession *session = NULL;
1141 UploadOnePictureData *uop_data = NULL;
1142 FrogrController *controller = NULL;
1143 GError *error = NULL;
1144
1145 session = FSP_SESSION (object);
1146 uop_data = (UploadOnePictureData*) data;
1147 controller = uop_data->controller;
1148
1149 fsp_session_set_license_finish (session, res, &error);
1150 if (error && _should_retry_operation (error, uop_data->after_upload_attempts[AFTER_UPLOAD_OP_SETTING_LICENSE]))
1151 {
1152 uop_data->after_upload_attempts[AFTER_UPLOAD_OP_SETTING_LICENSE]++;
1153
1154 DEBUG("Error setting license for picture %s: %s. Retrying... (attempt %d / %d)",
1155 frogr_picture_get_title (uop_data->picture),
1156 error->message,
1157 uop_data->after_upload_attempts[AFTER_UPLOAD_OP_SETTING_LICENSE],
1158 MAX_ATTEMPTS);
1159 g_error_free (error);
1160
1161 /* Try again! */
1162 _set_license_for_picture (controller, uop_data);
1163 }
1164 else
1165 {
1166 if (error)
1167 {
1168 DEBUG ("Error setting license for picture: %s", error->message);
1169 uop_data->up_data->error = error;
1170 }
1171
1172 controller->setting_license = FALSE;
1173 }
1174 }
1175
1176 static void
_set_license_for_picture(FrogrController * self,UploadOnePictureData * uop_data)1177 _set_license_for_picture (FrogrController *self, UploadOnePictureData *uop_data)
1178 {
1179 FrogrPicture *picture = NULL;
1180 g_autofree gchar *debug_msg = NULL;
1181
1182 self->setting_license = TRUE;
1183 picture = uop_data->picture;
1184
1185 fsp_session_set_license (self->session,
1186 frogr_picture_get_id (picture),
1187 frogr_picture_get_license (picture),
1188 uop_data->cancellable,
1189 _set_license_cb,
1190 uop_data);
1191
1192 debug_msg = g_strdup_printf ("Setting license %d for picture %s…",
1193 frogr_picture_get_license (picture),
1194 frogr_picture_get_title (picture));
1195 DEBUG ("%s", debug_msg);
1196 }
1197
1198 static void
_set_location_cb(GObject * object,GAsyncResult * res,gpointer data)1199 _set_location_cb (GObject *object, GAsyncResult *res, gpointer data)
1200 {
1201 FspSession *session = NULL;
1202 UploadOnePictureData *uop_data = NULL;
1203 FrogrController *controller = NULL;
1204 GError *error = NULL;
1205
1206 session = FSP_SESSION (object);
1207 uop_data = (UploadOnePictureData*) data;
1208 controller = uop_data->controller;
1209
1210 fsp_session_set_location_finish (session, res, &error);
1211 if (error && _should_retry_operation (error, uop_data->after_upload_attempts[AFTER_UPLOAD_OP_SETTING_LOCATION]))
1212 {
1213 uop_data->after_upload_attempts[AFTER_UPLOAD_OP_SETTING_LOCATION]++;
1214
1215 DEBUG("Error setting location for picture %s: %s. Retrying... (attempt %d / %d)",
1216 frogr_picture_get_title (uop_data->picture),
1217 error->message,
1218 uop_data->after_upload_attempts[AFTER_UPLOAD_OP_SETTING_LOCATION],
1219 MAX_ATTEMPTS);
1220 g_error_free (error);
1221
1222 _set_location_for_picture (controller, uop_data);
1223 }
1224 else
1225 {
1226 if (error)
1227 {
1228 DEBUG ("Error setting location for picture: %s", error->message);
1229 uop_data->up_data->error = error;
1230 }
1231
1232 controller->setting_location = FALSE;
1233 }
1234 }
1235
1236 static void
_set_location_for_picture(FrogrController * self,UploadOnePictureData * uop_data)1237 _set_location_for_picture (FrogrController *self, UploadOnePictureData *uop_data)
1238 {
1239 FrogrPicture *picture = NULL;
1240 FrogrLocation *location = NULL;
1241 FspDataLocation *data_location = NULL;
1242 g_autofree gchar *debug_msg = NULL;
1243
1244 picture = uop_data->picture;
1245 location = frogr_picture_get_location (picture);
1246 data_location = FSP_DATA_LOCATION (fsp_data_new (FSP_LOCATION));
1247 data_location->latitude = frogr_location_get_latitude (location);
1248 data_location->longitude = frogr_location_get_longitude (location);
1249
1250 self->setting_location = TRUE;
1251
1252 fsp_session_set_location (self->session,
1253 frogr_picture_get_id (picture),
1254 data_location,
1255 uop_data->cancellable,
1256 _set_location_cb,
1257 uop_data);
1258
1259 location = frogr_picture_get_location (picture);
1260 debug_msg = g_strdup_printf ("Setting geolocation (%f, %f) for picture %s…",
1261 frogr_location_get_latitude (location),
1262 frogr_location_get_longitude (location),
1263 frogr_picture_get_title (picture));
1264 DEBUG ("%s", debug_msg);
1265
1266 fsp_data_free (FSP_DATA (data_location));
1267 }
1268
1269 static void
_set_replace_date_posted_cb(GObject * object,GAsyncResult * res,gpointer data)1270 _set_replace_date_posted_cb (GObject *object, GAsyncResult *res, gpointer data)
1271 {
1272 FspSession *session = NULL;
1273 UploadOnePictureData *uop_data = NULL;
1274 FrogrController *controller = NULL;
1275 GError *error = NULL;
1276
1277 session = FSP_SESSION (object);
1278 uop_data = (UploadOnePictureData*) data;
1279 controller = uop_data->controller;
1280
1281 fsp_session_set_date_posted_finish (session, res, &error);
1282 if (error && _should_retry_operation (error, uop_data->after_upload_attempts[AFTER_UPLOAD_OP_SETTING_REPLACE_DATE_POSTED]))
1283 {
1284 uop_data->after_upload_attempts[AFTER_UPLOAD_OP_SETTING_REPLACE_DATE_POSTED]++;
1285
1286 DEBUG("Error replacing 'date posted' with 'date taken' for picture %s: %s. Retrying... (attempt %d / %d)",
1287 frogr_picture_get_title (uop_data->picture),
1288 error->message,
1289 uop_data->after_upload_attempts[AFTER_UPLOAD_OP_SETTING_REPLACE_DATE_POSTED],
1290 MAX_ATTEMPTS);
1291 g_error_free (error);
1292
1293 _set_replace_date_posted_for_picture (controller, uop_data);
1294 }
1295 else
1296 {
1297 if (error)
1298 {
1299 DEBUG ("Error replacing 'date posted' with 'date taken' for picture: %s", error->message);
1300 uop_data->up_data->error = error;
1301 }
1302
1303 controller->setting_replace_date_posted = FALSE;
1304 }
1305 }
1306
1307 static void
_set_replace_date_posted_for_picture(FrogrController * self,UploadOnePictureData * uop_data)1308 _set_replace_date_posted_for_picture (FrogrController *self, UploadOnePictureData *uop_data)
1309 {
1310 FrogrPicture *picture = NULL;
1311 g_autoptr(GDateTime) picture_date = NULL;
1312 const gchar *picture_date_str = NULL;
1313 g_autofree gchar *debug_msg = NULL;
1314 gchar date_iso8601[20];
1315
1316 picture = uop_data->picture;
1317 picture_date_str = frogr_picture_get_datetime (picture);
1318 if (!picture_date_str)
1319 return;
1320
1321 if (g_strlcpy (date_iso8601, picture_date_str, 20) < 19)
1322 return;
1323
1324 /* Exif's date time values stored in FrogrPicture are in the format
1325 '%Y:%m:%d %H:%M:%S', while iso8601 uses format '%Y-%m-%dT%H:%M:%S' */
1326 date_iso8601[10] = 'T';
1327 date_iso8601[4] = '-';
1328 date_iso8601[7] = '-';
1329 date_iso8601[19] = '\0';
1330
1331 picture_date = g_date_time_new_from_iso8601 (date_iso8601, g_time_zone_new_utc ());
1332 if (!picture_date)
1333 return;
1334
1335 self->setting_replace_date_posted = TRUE;
1336
1337 fsp_session_set_date_posted (self->session,
1338 frogr_picture_get_id (picture),
1339 picture_date,
1340 uop_data->cancellable,
1341 _set_replace_date_posted_cb,
1342 uop_data);
1343
1344 debug_msg = g_strdup_printf ("Replacing 'date posted' with 'date taken' (%s) for picture %s…",
1345 date_iso8601, frogr_picture_get_title (picture));
1346 DEBUG ("%s", debug_msg);
1347 }
1348
1349 static gboolean
_add_picture_to_photosets_or_create(FrogrController * self,UploadOnePictureData * uop_data)1350 _add_picture_to_photosets_or_create (FrogrController *self, UploadOnePictureData *uop_data)
1351 {
1352 FrogrPhotoSet *set = NULL;
1353
1354 if (g_slist_length (uop_data->photosets) == 0)
1355 return FALSE;
1356
1357 self->adding_to_set = TRUE;
1358
1359 uop_data->after_upload_attempts[AFTER_UPLOAD_OP_ADDING_TO_SET] = 0;
1360
1361 set = FROGR_PHOTOSET (uop_data->photosets->data);
1362 if (frogr_photoset_is_local (set))
1363 _create_photoset_for_picture (self, uop_data);
1364 else
1365 _add_picture_to_photoset (self, uop_data);
1366
1367 return TRUE;
1368 }
1369
1370 static void
_create_photoset_for_picture(FrogrController * self,UploadOnePictureData * uop_data)1371 _create_photoset_for_picture (FrogrController *self, UploadOnePictureData *uop_data)
1372 {
1373 FrogrPicture *picture = NULL;
1374 FrogrPhotoSet *set = NULL;
1375 g_autofree gchar *debug_msg = NULL;
1376
1377 picture = uop_data->picture;
1378 set = FROGR_PHOTOSET (uop_data->photosets->data);
1379
1380 /* Set with ID: Create set along with this picture */
1381 fsp_session_create_photoset (self->session,
1382 frogr_photoset_get_title (set),
1383 frogr_photoset_get_description (set),
1384 frogr_picture_get_id (picture),
1385 uop_data->cancellable,
1386 _create_photoset_cb,
1387 uop_data);
1388
1389 debug_msg = g_strdup_printf ("Creating new photoset for picture %s. "
1390 "Title: %s / Description: %s",
1391 frogr_picture_get_title (picture),
1392 frogr_photoset_get_title (set),
1393 frogr_photoset_get_description (set));
1394 DEBUG ("%s", debug_msg);
1395 }
1396
1397 static void
_create_photoset_cb(GObject * object,GAsyncResult * res,gpointer data)1398 _create_photoset_cb (GObject *object, GAsyncResult *res, gpointer data)
1399 {
1400 FspSession *session = NULL;
1401 UploadOnePictureData *uop_data = NULL;
1402 FrogrController *controller = NULL;
1403 FrogrPhotoSet *set = NULL;
1404 GSList *photosets = NULL;
1405 g_autofree gchar *photoset_id = NULL;
1406 GError *error = NULL;
1407
1408 session = FSP_SESSION (object);
1409 uop_data = (UploadOnePictureData*) data;
1410 controller = uop_data->controller;
1411 photosets = uop_data->photosets;
1412 set = FROGR_PHOTOSET (photosets->data);
1413
1414 photoset_id = fsp_session_create_photoset_finish (session, res, &error);
1415 if (error && _should_retry_operation (error, uop_data->after_upload_attempts[AFTER_UPLOAD_OP_ADDING_TO_SET]))
1416 {
1417 uop_data->after_upload_attempts[AFTER_UPLOAD_OP_ADDING_TO_SET]++;
1418
1419 DEBUG("Error adding picture %s to NEW photoset %s: %s. Retrying... (attempt %d / %d)",
1420 frogr_picture_get_title (uop_data->picture),
1421 error->message,
1422 frogr_photoset_get_title (set),
1423 uop_data->after_upload_attempts[AFTER_UPLOAD_OP_ADDING_TO_SET],
1424 MAX_ATTEMPTS);
1425 g_error_free (error);
1426
1427 _add_picture_to_photoset (controller, uop_data);
1428 }
1429 else
1430 {
1431 gboolean keep_going = FALSE;
1432
1433 if (!error)
1434 {
1435 frogr_photoset_set_id (set, photoset_id);
1436 frogr_photoset_set_n_photos (set, frogr_photoset_get_n_photos (set) + 1);
1437
1438 uop_data->after_upload_attempts[AFTER_UPLOAD_OP_ADDING_TO_SET] = 0;
1439 uop_data->photosets = g_slist_next (photosets);
1440 keep_going = _add_picture_to_photosets_or_create (controller, uop_data);
1441 }
1442 else
1443 {
1444 DEBUG ("Error creating set: %s", error->message);
1445 uop_data->up_data->error = error;
1446 }
1447
1448 if (!keep_going)
1449 controller->adding_to_set = FALSE;
1450 }
1451 }
1452
1453 static void
_add_picture_to_photoset(FrogrController * self,UploadOnePictureData * uop_data)1454 _add_picture_to_photoset (FrogrController *self, UploadOnePictureData *uop_data)
1455 {
1456 FrogrPicture *picture = NULL;
1457 FrogrPhotoSet *set = NULL;
1458 g_autofree gchar *debug_msg = NULL;
1459
1460 picture = uop_data->picture;
1461 set = FROGR_PHOTOSET (uop_data->photosets->data);
1462
1463 /* Set with ID: Add picture to it */
1464 fsp_session_add_to_photoset (self->session,
1465 frogr_picture_get_id (picture),
1466 frogr_photoset_get_id (set),
1467 uop_data->cancellable,
1468 _add_to_photoset_cb,
1469 uop_data);
1470
1471 debug_msg = g_strdup_printf ("Adding picture %s to photoset %s…",
1472 frogr_picture_get_title (picture),
1473 frogr_photoset_get_title (set));
1474 DEBUG ("%s", debug_msg);
1475 }
1476
1477 static void
_add_to_photoset_cb(GObject * object,GAsyncResult * res,gpointer data)1478 _add_to_photoset_cb (GObject *object, GAsyncResult *res, gpointer data)
1479 {
1480 FspSession *session = NULL;
1481 UploadOnePictureData *uop_data = NULL;
1482 FrogrController *controller = NULL;
1483 FrogrPhotoSet *set = NULL;
1484 GSList *photosets = NULL;
1485 GError *error = NULL;
1486
1487 session = FSP_SESSION (object);
1488 uop_data = (UploadOnePictureData*) data;
1489 controller = uop_data->controller;
1490 photosets = uop_data->photosets;
1491 set = FROGR_PHOTOSET (photosets->data);
1492
1493 fsp_session_add_to_photoset_finish (session, res, &error);
1494 if (error && _should_retry_operation (error, uop_data->after_upload_attempts[AFTER_UPLOAD_OP_ADDING_TO_SET]))
1495 {
1496 uop_data->after_upload_attempts[AFTER_UPLOAD_OP_ADDING_TO_SET]++;
1497
1498 DEBUG("Error adding picture %s to EXISTING photoset %s: %s. Retrying... (attempt %d / %d)",
1499 frogr_picture_get_title (uop_data->picture),
1500 error->message,
1501 frogr_photoset_get_title (set),
1502 uop_data->after_upload_attempts[AFTER_UPLOAD_OP_ADDING_TO_SET],
1503 MAX_ATTEMPTS);
1504 g_error_free (error);
1505
1506 _add_picture_to_photoset (controller, uop_data);
1507 }
1508 else
1509 {
1510 gboolean keep_going = FALSE;
1511
1512 if (!error)
1513 {
1514 frogr_photoset_set_n_photos (set, frogr_photoset_get_n_photos (set) + 1);
1515
1516 uop_data->after_upload_attempts[AFTER_UPLOAD_OP_ADDING_TO_SET] = 0;
1517 uop_data->photosets = g_slist_next (photosets);
1518 keep_going = _add_picture_to_photosets_or_create (controller, uop_data);
1519 }
1520 else
1521 {
1522 DEBUG ("Error adding picture to set: %s", error->message);
1523 uop_data->up_data->error = error;
1524 }
1525
1526 if (!keep_going)
1527 controller->adding_to_set = FALSE;
1528 }
1529 }
1530
1531 static gboolean
_add_picture_to_groups(FrogrController * self,UploadOnePictureData * uop_data)1532 _add_picture_to_groups (FrogrController *self, UploadOnePictureData *uop_data)
1533 {
1534 /* Add pictures to groups, if any */
1535 if (g_slist_length (uop_data->groups) == 0)
1536 return FALSE;
1537
1538 self->adding_to_group = TRUE;
1539
1540 _add_picture_to_group (self, uop_data);
1541
1542 return TRUE;
1543 }
1544
1545 static void
_add_picture_to_group(FrogrController * self,UploadOnePictureData * uop_data)1546 _add_picture_to_group (FrogrController *self, UploadOnePictureData *uop_data)
1547 {
1548 FrogrPicture *picture = NULL;
1549 FrogrGroup *group = NULL;
1550 g_autofree gchar *debug_msg = NULL;
1551
1552 picture = uop_data->picture;
1553 group = FROGR_GROUP (uop_data->groups->data);
1554
1555 fsp_session_add_to_group (self->session,
1556 frogr_picture_get_id (picture),
1557 frogr_group_get_id (group),
1558 uop_data->cancellable,
1559 _add_to_group_cb,
1560 uop_data);
1561
1562 debug_msg = g_strdup_printf ("Adding picture %s to group %s…",
1563 frogr_picture_get_title (picture),
1564 frogr_group_get_name (group));
1565 DEBUG ("%s", debug_msg);
1566 }
1567
1568 static void
_add_to_group_cb(GObject * object,GAsyncResult * res,gpointer data)1569 _add_to_group_cb (GObject *object, GAsyncResult *res, gpointer data)
1570 {
1571 FspSession *session = NULL;
1572 UploadOnePictureData *uop_data = NULL;
1573 FrogrController *controller = NULL;
1574 FrogrGroup *group = NULL;
1575 GSList *groups = NULL;
1576 GError *error = NULL;
1577
1578 session = FSP_SESSION (object);
1579 uop_data = (UploadOnePictureData*) data;
1580 controller = uop_data->controller;
1581 groups = uop_data->groups;
1582 group = FROGR_GROUP (groups->data);
1583
1584 fsp_session_add_to_group_finish (session, res, &error);
1585 if (error && _should_retry_operation (error, uop_data->after_upload_attempts[AFTER_UPLOAD_OP_ADDING_TO_GROUP]))
1586 {
1587 uop_data->after_upload_attempts[AFTER_UPLOAD_OP_ADDING_TO_GROUP]++;
1588
1589 DEBUG("Error adding picture %s to group %s: %s. Retrying... (attempt %d / %d)",
1590 frogr_picture_get_title (uop_data->picture),
1591 error->message,
1592 frogr_group_get_name (group),
1593 uop_data->after_upload_attempts[AFTER_UPLOAD_OP_ADDING_TO_GROUP],
1594 MAX_ATTEMPTS);
1595 g_error_free (error);
1596
1597 _add_picture_to_group (controller, uop_data);
1598 }
1599 else
1600 {
1601 gboolean keep_going = FALSE;
1602
1603 if (!error)
1604 {
1605 frogr_group_set_n_photos (group, frogr_group_get_n_photos (group) + 1);
1606
1607 uop_data->after_upload_attempts[AFTER_UPLOAD_OP_ADDING_TO_GROUP] = 0;
1608 uop_data->groups = g_slist_next (groups);
1609 keep_going = _add_picture_to_groups (controller, uop_data);
1610 }
1611 else
1612 {
1613 DEBUG ("Error adding picture to group: %s", error->message);
1614 uop_data->up_data->error = error;
1615 }
1616
1617 if (!keep_going)
1618 controller->adding_to_group = FALSE;
1619 }
1620 }
1621
1622 static gboolean
_complete_picture_upload(gpointer data)1623 _complete_picture_upload (gpointer data)
1624 {
1625 UploadOnePictureData *uop_data = NULL;
1626 UploadPicturesData *up_data = NULL;
1627 FrogrController *controller = NULL;
1628 FrogrPicture *picture = NULL;
1629
1630 uop_data = (UploadOnePictureData*) data;
1631 controller = uop_data->controller;
1632 up_data = uop_data->up_data;
1633
1634 /* Keep the source while busy */
1635 if (controller->setting_license || controller->setting_location || controller->setting_replace_date_posted
1636 || controller->adding_to_set || controller->adding_to_group)
1637 {
1638 frogr_main_view_pulse_progress (controller->mainview);
1639 _update_upload_progress (controller, up_data);
1640 return G_SOURCE_CONTINUE;
1641 }
1642 picture = uop_data->picture;
1643
1644 if (g_cancellable_is_cancelled (uop_data->cancellable) || up_data->error)
1645 {
1646 up_data->current = NULL;
1647 }
1648 else
1649 {
1650 /* Remove it from the model if no error happened */
1651 FrogrModel *model = NULL;
1652 model = frogr_main_view_get_model (controller->mainview);
1653 frogr_model_remove_picture (model, picture);
1654 }
1655
1656 /* Re-check account info to make sure we have up-to-date info */
1657 _fetch_account_info (controller);
1658
1659 _finish_upload_one_picture_process (controller, uop_data);
1660 _upload_next_picture (controller, up_data);
1661
1662 return G_SOURCE_REMOVE;
1663 }
1664
1665 static void
_on_file_loaded(FrogrFileLoader * loader,FrogrPicture * picture,FrogrController * self)1666 _on_file_loaded (FrogrFileLoader *loader, FrogrPicture *picture, FrogrController *self)
1667 {
1668 FrogrModel *model = NULL;
1669
1670 g_return_if_fail (FROGR_IS_CONTROLLER (self));
1671 g_return_if_fail (FROGR_IS_PICTURE (picture));
1672
1673 model = frogr_main_view_get_model (self->mainview);
1674 frogr_model_add_picture (model, picture);
1675 }
1676
1677 static void
_on_files_loaded(FrogrFileLoader * loader,FrogrController * self)1678 _on_files_loaded (FrogrFileLoader *loader, FrogrController *self)
1679 {
1680 g_return_if_fail (FROGR_IS_CONTROLLER (self));
1681 _set_state (self, FROGR_STATE_IDLE);
1682 }
1683
1684 static void
_on_model_deserialized(FrogrModel * model,FrogrController * self)1685 _on_model_deserialized (FrogrModel *model, FrogrController *self)
1686 {
1687 g_return_if_fail (FROGR_IS_CONTROLLER (self));
1688 _set_state (self, FROGR_STATE_IDLE);
1689 }
1690
1691 static void
_fetch_everything(FrogrController * self,gboolean force_extra_data)1692 _fetch_everything (FrogrController *self, gboolean force_extra_data)
1693 {
1694 g_return_if_fail(FROGR_IS_CONTROLLER (self));
1695
1696 if (!frogr_controller_is_authorized (self))
1697 return;
1698
1699 /* Invalidate all fetched data as soon as possible if we already
1700 know upfront we will force fetching it again later. */
1701 if (force_extra_data)
1702 _invalidate_extra_data (self);
1703
1704 FetchAccountInfoData *data = g_slice_new0 (FetchAccountInfoData);
1705 data->retrieve_everything = TRUE;
1706 data->force_extra_data = force_extra_data;
1707
1708 _fetch_account_basic_info (self, data);
1709 }
1710
1711 static void
_fetch_account_info(FrogrController * self)1712 _fetch_account_info (FrogrController *self)
1713 {
1714 g_return_if_fail(FROGR_IS_CONTROLLER (self));
1715
1716 if (!frogr_controller_is_authorized (self))
1717 return;
1718
1719 FetchAccountInfoData *data = g_slice_new0 (FetchAccountInfoData);
1720 data->retrieve_everything = FALSE;
1721 data->force_extra_data = FALSE;
1722
1723 _fetch_account_basic_info (self, data);
1724 }
1725
1726 static void
_fetch_account_info_finish(FrogrController * self,FetchAccountInfoData * data)1727 _fetch_account_info_finish (FrogrController *self, FetchAccountInfoData *data)
1728 {
1729 g_return_if_fail(FROGR_IS_CONTROLLER (self));
1730 if (data)
1731 g_slice_free (FetchAccountInfoData, data);
1732 }
1733
1734 static void
_fetch_account_basic_info(FrogrController * self,FetchAccountInfoData * data)1735 _fetch_account_basic_info (FrogrController *self, FetchAccountInfoData *data)
1736 {
1737 g_return_if_fail(FROGR_IS_CONTROLLER (self));
1738
1739 if (!frogr_controller_is_authorized (self))
1740 {
1741 _fetch_account_info_finish (self, data);
1742 return;
1743 }
1744
1745 fsp_session_check_auth_info (self->session, NULL,
1746 (GAsyncReadyCallback)_fetch_account_basic_info_cb,
1747 data);
1748 }
1749
1750 static void
_fetch_account_basic_info_cb(GObject * object,GAsyncResult * res,FetchAccountInfoData * data)1751 _fetch_account_basic_info_cb (GObject *object, GAsyncResult *res, FetchAccountInfoData *data)
1752 {
1753 FspSession *session = NULL;
1754 FrogrController *controller = NULL;
1755 FspDataAuthToken *auth_token = NULL;
1756 g_autoptr(GError) error = NULL;
1757
1758 session = FSP_SESSION (object);
1759 controller = frogr_controller_get_instance ();
1760
1761 auth_token = fsp_session_check_auth_info_finish (session, res, &error);
1762 if (auth_token && controller->account)
1763 {
1764 const gchar *old_username = NULL;
1765 const gchar *old_fullname = NULL;
1766 gboolean username_changed = FALSE;
1767
1768 /* Check for changes (only for fields that it makes sense) */
1769 old_username = frogr_account_get_username (controller->account);
1770 old_fullname = frogr_account_get_fullname (controller->account);
1771 if (g_strcmp0 (old_username, auth_token->username)
1772 || g_strcmp0 (old_fullname, auth_token->fullname))
1773 {
1774 username_changed = TRUE;
1775 }
1776
1777 frogr_account_set_username (controller->account, auth_token->username);
1778 frogr_account_set_fullname (controller->account, auth_token->fullname);
1779
1780 if (username_changed)
1781 {
1782 /* Save to disk and emit signal if basic info changed */
1783 frogr_config_save_accounts (controller->config);
1784 g_signal_emit (controller, signals[ACTIVE_ACCOUNT_CHANGED], 0, controller->account);
1785 }
1786
1787 /* Now fetch the remaining bits of information */
1788 _fetch_account_upload_status (controller, data);
1789 }
1790 else
1791 {
1792 if (error != NULL)
1793 {
1794 DEBUG ("Fetching basic info from the account: %s", error->message);
1795 _handle_flicksoup_error (controller, error, FALSE);
1796 }
1797 _fetch_account_info_finish (controller, data);
1798 }
1799
1800 fsp_data_free (FSP_DATA (auth_token));
1801 }
1802
_fetch_account_upload_status(FrogrController * self,FetchAccountInfoData * data)1803 static void _fetch_account_upload_status (FrogrController *self, FetchAccountInfoData *data)
1804 {
1805 g_return_if_fail(FROGR_IS_CONTROLLER (self));
1806
1807 if (!frogr_controller_is_authorized (self))
1808 {
1809 _fetch_account_info_finish (self, data);
1810 return;
1811 }
1812
1813 fsp_session_get_upload_status (self->session, NULL,
1814 (GAsyncReadyCallback)_fetch_account_upload_status_cb,
1815 data);
1816 }
1817
1818 static void
_fetch_account_upload_status_cb(GObject * object,GAsyncResult * res,FetchAccountInfoData * data)1819 _fetch_account_upload_status_cb (GObject *object, GAsyncResult *res, FetchAccountInfoData *data)
1820 {
1821 FspSession *session = NULL;
1822 FrogrController *controller = NULL;
1823 FspDataUploadStatus *upload_status = NULL;
1824 g_autoptr(GError) error = NULL;
1825
1826 session = FSP_SESSION (object);
1827 controller = frogr_controller_get_instance ();
1828
1829 upload_status = fsp_session_get_upload_status_finish (session, res, &error);
1830 if (upload_status && controller->account)
1831 {
1832 gulong old_remaining_bw;
1833 gulong old_max_bw;
1834 gboolean old_is_pro;
1835
1836 /* Check for changes */
1837 old_remaining_bw = frogr_account_get_remaining_bandwidth (controller->account);
1838 old_max_bw = frogr_account_get_max_bandwidth (controller->account);
1839 old_is_pro = frogr_account_is_pro (controller->account);
1840
1841 frogr_account_set_remaining_bandwidth (controller->account, upload_status->bw_remaining_kb);
1842 frogr_account_set_max_bandwidth (controller->account, upload_status->bw_max_kb);
1843 frogr_account_set_max_picture_filesize (controller->account, upload_status->picture_fs_max_kb);
1844
1845 frogr_account_set_remaining_videos (controller->account, upload_status->bw_remaining_videos);
1846 frogr_account_set_current_videos (controller->account, upload_status->bw_used_videos);
1847 frogr_account_set_max_video_filesize (controller->account, upload_status->video_fs_max_kb);
1848
1849 frogr_account_set_is_pro (controller->account, upload_status->pro_user);
1850
1851 /* Mark that we received this extra info for the user */
1852 frogr_account_set_has_extra_info (controller->account, TRUE);
1853
1854 if (old_remaining_bw != upload_status->bw_remaining_kb
1855 || old_max_bw != upload_status->bw_max_kb
1856 || old_is_pro != upload_status->pro_user)
1857 {
1858 /* Emit signal if extra info changed */
1859 g_signal_emit (controller, signals[ACTIVE_ACCOUNT_CHANGED], 0, controller->account);
1860 }
1861
1862 /* Chain with the continuation function if any */
1863 if (data->retrieve_everything)
1864 _fetch_extra_data (controller, data->force_extra_data);
1865 }
1866 else if (error != NULL)
1867 {
1868 DEBUG ("Fetching upload status for the account: %s", error->message);
1869 _handle_flicksoup_error (controller, error, FALSE);
1870 }
1871
1872 fsp_data_free (FSP_DATA (upload_status));
1873 _fetch_account_info_finish (controller, data);
1874 }
1875
1876 static void
_fetch_extra_data(FrogrController * self,gboolean force)1877 _fetch_extra_data (FrogrController *self, gboolean force)
1878 {
1879 g_return_if_fail(FROGR_IS_CONTROLLER (self));
1880
1881 if (!frogr_controller_is_connected (self))
1882 return;
1883
1884 /* Invalidate all fetched data. */
1885 if (force)
1886 _invalidate_extra_data (self);
1887
1888 /* Sets, groups and tags can take much longer to retrieve,
1889 so we only retrieve that if actually needed */
1890 if (force || !self->photosets_fetched)
1891 _fetch_photosets (self);
1892 if (force || !self->groups_fetched)
1893 _fetch_groups (self);
1894 if (force || !self->tags_fetched)
1895 _fetch_tags (self);
1896 }
1897
1898 static void
_fetch_photosets(FrogrController * self)1899 _fetch_photosets (FrogrController *self)
1900 {
1901 CancellableOperationData *co_data = NULL;
1902
1903 g_return_if_fail(FROGR_IS_CONTROLLER (self));
1904
1905 if (!frogr_controller_is_connected (self))
1906 return;
1907
1908 self->photosets_fetched = FALSE;
1909 self->fetching_photosets = TRUE;
1910
1911 co_data = g_slice_new0 (CancellableOperationData);
1912 co_data->controller = self;
1913 co_data->cancellable = _register_new_cancellable (self);
1914 fsp_session_get_photosets (self->session, co_data->cancellable,
1915 _fetch_photosets_cb, co_data);
1916 }
1917
1918 static void
_fetch_photosets_cb(GObject * object,GAsyncResult * res,gpointer data)1919 _fetch_photosets_cb (GObject *object, GAsyncResult *res, gpointer data)
1920 {
1921 FspSession *session = NULL;
1922 CancellableOperationData *co_data = NULL;
1923 FrogrController *controller = NULL;
1924 GSList *sets_list = NULL;
1925 g_autoptr(GSList) data_sets_list = NULL;
1926 g_autoptr(GError) error = NULL;
1927 gboolean valid = FALSE;
1928
1929 session = FSP_SESSION (object);
1930 co_data = (CancellableOperationData*) data;
1931 controller = co_data->controller;
1932
1933 data_sets_list = fsp_session_get_photosets_finish (session, res, &error);
1934 if (error != NULL)
1935 {
1936 DEBUG ("Fetching list of sets: %s (%d)", error->message, error->code);
1937
1938 _handle_flicksoup_error (controller, error, FALSE);
1939
1940 /* If no photosets are found is a valid outcome */
1941 if (error->code == FSP_ERROR_MISSING_DATA)
1942 valid = TRUE;
1943 }
1944 else
1945 {
1946 if (data_sets_list)
1947 {
1948 GSList *item = NULL;
1949 FspDataPhotoSet *current_data_set = NULL;
1950 FrogrPhotoSet *current_set = NULL;
1951
1952 /* Consider the received result valid if no previous one has arrived first */
1953 valid = !controller->photosets_fetched;
1954
1955 for (item = data_sets_list; item; item = g_slist_next (item))
1956 {
1957 current_data_set = FSP_DATA_PHOTO_SET (item->data);
1958 if (valid)
1959 {
1960 current_set = frogr_photoset_new (current_data_set->id,
1961 current_data_set->title,
1962 current_data_set->description);
1963 frogr_photoset_set_primary_photo_id (current_set, current_data_set->primary_photo_id);
1964 frogr_photoset_set_n_photos (current_set, current_data_set->n_photos);
1965
1966 sets_list = g_slist_append (sets_list, current_set);
1967 }
1968 fsp_data_free (FSP_DATA (current_data_set));
1969 }
1970 }
1971 }
1972
1973 if (valid)
1974 {
1975 FrogrModel *model = frogr_main_view_get_model (controller->mainview);
1976 frogr_model_set_remote_photosets (model, sets_list);
1977 controller->photosets_fetched = TRUE;
1978 }
1979
1980 _clear_cancellable (controller, co_data->cancellable);
1981 g_slice_free (CancellableOperationData, co_data);
1982
1983 controller->fetching_photosets = FALSE;
1984 }
1985
1986 static void
_fetch_groups(FrogrController * self)1987 _fetch_groups (FrogrController *self)
1988 {
1989 CancellableOperationData *co_data = NULL;
1990
1991 g_return_if_fail(FROGR_IS_CONTROLLER (self));
1992
1993 if (!frogr_controller_is_connected (self))
1994 return;
1995
1996 self->groups_fetched = FALSE;
1997 self->fetching_groups = TRUE;
1998
1999 co_data = g_slice_new0 (CancellableOperationData);
2000 co_data->controller = self;
2001 co_data->cancellable = _register_new_cancellable (self);
2002 fsp_session_get_groups (self->session, co_data->cancellable,
2003 _fetch_groups_cb, co_data);
2004 }
2005
2006 static void
_fetch_groups_cb(GObject * object,GAsyncResult * res,gpointer data)2007 _fetch_groups_cb (GObject *object, GAsyncResult *res, gpointer data)
2008 {
2009 FspSession *session = NULL;
2010 CancellableOperationData *co_data = NULL;
2011 FrogrController *controller = NULL;
2012 GSList *groups_list = NULL;
2013 g_autoptr(GSList) data_groups_list = NULL;
2014 g_autoptr(GError) error = NULL;
2015 gboolean valid = FALSE;
2016
2017 session = FSP_SESSION (object);
2018 co_data = (CancellableOperationData*) data;
2019 controller = co_data->controller;
2020
2021 data_groups_list = fsp_session_get_groups_finish (session, res, &error);
2022 if (error != NULL)
2023 {
2024 DEBUG ("Fetching list of groups: %s (%d)", error->message, error->code);
2025
2026 _handle_flicksoup_error (controller, error, FALSE);
2027
2028 /* If no groups are found is a valid outcome */
2029 if (error->code == FSP_ERROR_MISSING_DATA)
2030 valid = TRUE;
2031 }
2032 else
2033 {
2034 if (data_groups_list)
2035 {
2036 GSList *item = NULL;
2037 FspDataGroup *data_group = NULL;
2038 FrogrGroup *current_group = NULL;
2039
2040 /* Consider the received result valid if no previous one has arrived first */
2041 valid = !controller->groups_fetched;
2042
2043 for (item = data_groups_list; item; item = g_slist_next (item))
2044 {
2045 data_group = FSP_DATA_GROUP (item->data);
2046 if (valid)
2047 {
2048 current_group = frogr_group_new (data_group->id,
2049 data_group->name,
2050 data_group->privacy,
2051 data_group->n_photos);
2052
2053 groups_list = g_slist_append (groups_list, current_group);
2054 }
2055 fsp_data_free (FSP_DATA (data_group));
2056 }
2057 }
2058 }
2059
2060 if (valid)
2061 {
2062 FrogrModel *model = frogr_main_view_get_model (controller->mainview);
2063 frogr_model_set_groups (model, groups_list);
2064 controller->groups_fetched = TRUE;
2065 }
2066
2067 _clear_cancellable (controller, co_data->cancellable);
2068 g_slice_free (CancellableOperationData, co_data);
2069
2070 controller->fetching_groups = FALSE;
2071 }
2072
2073 static void
_fetch_tags(FrogrController * self)2074 _fetch_tags (FrogrController *self)
2075 {
2076 CancellableOperationData *co_data = NULL;
2077
2078 g_return_if_fail(FROGR_IS_CONTROLLER (self));
2079
2080 if (!frogr_controller_is_connected (self))
2081 return;
2082
2083 self->tags_fetched = FALSE;
2084
2085 /* Do not actually fetch tags if the autocompletion is off */
2086 if (!frogr_config_get_tags_autocompletion (self->config))
2087 return;
2088 self->fetching_tags = TRUE;
2089
2090 co_data = g_slice_new0 (CancellableOperationData);
2091 co_data->controller = self;
2092 co_data->cancellable = _register_new_cancellable (self);
2093 fsp_session_get_tags_list (self->session, co_data->cancellable,
2094 _fetch_tags_cb, co_data);
2095 }
2096
2097 static void
_fetch_tags_cb(GObject * object,GAsyncResult * res,gpointer data)2098 _fetch_tags_cb (GObject *object, GAsyncResult *res, gpointer data)
2099 {
2100 FspSession *session = NULL;
2101 CancellableOperationData *co_data = NULL;
2102 FrogrController *controller = NULL;
2103 GSList *tags_list = NULL;
2104 g_autoptr(GError) error = NULL;
2105 gboolean valid = FALSE;
2106
2107 session = FSP_SESSION (object);
2108 co_data = (CancellableOperationData*) data;
2109 controller = co_data->controller;
2110
2111 tags_list = fsp_session_get_tags_list_finish (session, res, &error);
2112 if (error != NULL)
2113 {
2114 DEBUG ("Fetching list of tags: %s", error->message);
2115
2116 _handle_flicksoup_error (controller, error, FALSE);
2117
2118 /* If no tags are found is a valid outcome */
2119 if (error->code == FSP_ERROR_MISSING_DATA)
2120 valid = TRUE;
2121
2122 tags_list = NULL;
2123 }
2124 else
2125 {
2126 /* Consider the received result valid if no previous one has arrived first */
2127 valid = !controller->tags_fetched;
2128 if (!valid)
2129 g_slist_free_full (tags_list, g_free);
2130 }
2131
2132 if (valid)
2133 {
2134 FrogrModel *model = frogr_main_view_get_model (controller->mainview);
2135 frogr_model_set_remote_tags (model, tags_list);
2136 controller->tags_fetched = TRUE;
2137 }
2138
2139 _clear_cancellable (controller, co_data->cancellable);
2140 g_slice_free (CancellableOperationData, co_data);
2141
2142 controller->fetching_tags = FALSE;
2143 }
2144
2145 static void
_dispose_slist_of_objects(GSList * objects)2146 _dispose_slist_of_objects (GSList *objects)
2147 {
2148 if (!objects)
2149 return;
2150
2151 /* FrogrController's responsibility over this list ends here */
2152 g_slist_free_full (objects, g_object_unref);
2153 }
2154
2155 static gboolean
_show_progress_on_idle(gpointer data)2156 _show_progress_on_idle (gpointer data)
2157 {
2158 FrogrController *controller = NULL;
2159 FetchingActivity activity = FETCHING_NOTHING;
2160 const gchar *text = NULL;
2161 gboolean show_dialog = FALSE;
2162
2163 controller = frogr_controller_get_instance ();
2164
2165 activity = GPOINTER_TO_INT (data);
2166 switch (activity)
2167 {
2168 case FETCHING_TOKEN_REPLACEMENT:
2169 text = _("Updating credentials…");
2170 show_dialog = controller->fetching_token_replacement;
2171 break;
2172
2173 case FETCHING_AUTH_URL:
2174 text = _("Retrieving data for authorization…");
2175 show_dialog = controller->fetching_auth_url;
2176 break;
2177
2178 case FETCHING_AUTH_TOKEN:
2179 text = _("Finishing authorization…");
2180 show_dialog = controller->fetching_auth_token;
2181 break;
2182
2183 case FETCHING_PHOTOSETS:
2184 text = _("Retrieving list of sets…");
2185 show_dialog = controller->fetching_photosets;
2186 break;
2187
2188 case FETCHING_GROUPS:
2189 text = _("Retrieving list of groups…");
2190 show_dialog = controller->fetching_groups;
2191 break;
2192
2193 case FETCHING_TAGS:
2194 text = _("Retrieving list of tags…");
2195 show_dialog = controller->fetching_tags;
2196 break;
2197
2198 default:
2199 text = NULL;
2200 }
2201
2202 /* Pulse and show/hide the progress dialog as needed */
2203 frogr_main_view_pulse_progress (controller->mainview);
2204 if (show_dialog)
2205 {
2206 g_autofree gchar *title = NULL;
2207
2208 switch (activity)
2209 {
2210 case FETCHING_TOKEN_REPLACEMENT:
2211 case FETCHING_AUTH_URL:
2212 case FETCHING_AUTH_TOKEN:
2213 title = g_strdup_printf (_("Authorize %s"), APP_SHORTNAME);
2214 break;
2215
2216 case FETCHING_PHOTOSETS:
2217 case FETCHING_GROUPS:
2218 case FETCHING_TAGS:
2219 title = g_strdup_printf (_("Fetching information"));
2220 break;
2221
2222 default:
2223 title = g_strdup ("");
2224 }
2225
2226 frogr_main_view_show_progress (controller->mainview, title, text);
2227 return G_SOURCE_CONTINUE;
2228 }
2229 else
2230 {
2231 frogr_main_view_hide_progress (controller->mainview);
2232 return G_SOURCE_REMOVE;
2233 }
2234 }
2235
2236 static gboolean
_show_details_dialog_on_idle(GSList * pictures)2237 _show_details_dialog_on_idle (GSList *pictures)
2238 {
2239 FrogrController *controller = NULL;
2240 FrogrModel *model = NULL;
2241 GSList *tags_list = NULL;
2242
2243 controller = frogr_controller_get_instance ();
2244
2245 /* Keep the source while internally busy */
2246 if (controller->fetching_tags && frogr_config_get_tags_autocompletion (controller->config))
2247 return G_SOURCE_CONTINUE;
2248
2249 model = frogr_main_view_get_model (controller->mainview);
2250 tags_list = frogr_model_get_tags (model);
2251
2252 /* Sets already pre-fetched: show the dialog */
2253 frogr_details_dialog_show (GTK_WINDOW (controller->mainview), pictures, tags_list);
2254
2255 controller->show_details_dialog_source_id = 0;
2256 return G_SOURCE_REMOVE;
2257 }
2258
2259 static gboolean
_show_add_tags_dialog_on_idle(GSList * pictures)2260 _show_add_tags_dialog_on_idle (GSList *pictures)
2261 {
2262 FrogrController *controller = NULL;
2263 FrogrModel *model = NULL;
2264 GSList *tags_list = NULL;
2265
2266 controller = frogr_controller_get_instance ();
2267
2268 /* Keep the source while internally busy */
2269 if (controller->fetching_tags && frogr_config_get_tags_autocompletion (controller->config))
2270 return G_SOURCE_CONTINUE;
2271
2272 model = frogr_main_view_get_model (controller->mainview);
2273 tags_list = frogr_model_get_tags (model);
2274
2275 /* Sets already pre-fetched: show the dialog */
2276 frogr_add_tags_dialog_show (GTK_WINDOW (controller->mainview), pictures, tags_list);
2277
2278 controller->show_add_tags_dialog_source_id = 0;
2279 return G_SOURCE_REMOVE;
2280 }
2281
2282 static gboolean
_show_create_new_set_dialog_on_idle(GSList * pictures)2283 _show_create_new_set_dialog_on_idle (GSList *pictures)
2284 {
2285 FrogrController *controller = NULL;
2286 FrogrModel *model = NULL;
2287 GSList *photosets = NULL;
2288
2289 controller = frogr_controller_get_instance ();
2290
2291 /* Keep the source while internally busy */
2292 if (controller->fetching_photosets)
2293 return G_SOURCE_CONTINUE;
2294
2295 model = frogr_main_view_get_model (controller->mainview);
2296 photosets = frogr_model_get_photosets (model);
2297
2298 frogr_create_new_set_dialog_show (GTK_WINDOW (controller->mainview), pictures, photosets);
2299
2300 controller->show_create_new_set_dialog_source_id = 0;
2301 return G_SOURCE_REMOVE;
2302 }
2303
2304 static gboolean
_show_add_to_set_dialog_on_idle(GSList * pictures)2305 _show_add_to_set_dialog_on_idle (GSList *pictures)
2306 {
2307 FrogrController *controller = NULL;
2308 FrogrModel *model = NULL;
2309 GSList *photosets = NULL;
2310
2311 controller = frogr_controller_get_instance ();
2312
2313 /* Keep the source while internally busy */
2314 if (controller->fetching_photosets)
2315 return G_SOURCE_CONTINUE;
2316
2317 model = frogr_main_view_get_model (controller->mainview);
2318 photosets = frogr_model_get_photosets (model);
2319
2320 if (frogr_model_n_photosets (model) > 0)
2321 frogr_add_to_set_dialog_show (GTK_WINDOW (controller->mainview), pictures, photosets);
2322 else if (controller->photosets_fetched)
2323 frogr_util_show_info_dialog (GTK_WINDOW (controller->mainview), _("No sets found"));
2324
2325 controller->show_add_to_set_dialog_source_id = 0;
2326 return G_SOURCE_REMOVE;
2327 }
2328
2329 static gboolean
_show_add_to_group_dialog_on_idle(GSList * pictures)2330 _show_add_to_group_dialog_on_idle (GSList *pictures)
2331 {
2332 FrogrController *controller = NULL;
2333 FrogrModel *model = NULL;
2334 GSList *groups = NULL;
2335
2336 controller = frogr_controller_get_instance ();
2337
2338 /* Keep the source while internally busy */
2339 if (controller->fetching_groups)
2340 return G_SOURCE_CONTINUE;
2341
2342 model = frogr_main_view_get_model (controller->mainview);
2343 groups = frogr_model_get_groups (model);
2344
2345 if (frogr_model_n_groups (model) > 0)
2346 frogr_add_to_group_dialog_show (GTK_WINDOW (controller->mainview), pictures, groups);
2347 else if (controller->groups_fetched)
2348 frogr_util_show_info_dialog (GTK_WINDOW (controller->mainview), _("No groups found"));
2349
2350 controller->show_add_to_group_dialog_source_id = 0;
2351 return G_SOURCE_REMOVE;
2352 }
2353
2354 static gboolean
_is_modal_dialog_about_to_be_shown(FrogrController * self)2355 _is_modal_dialog_about_to_be_shown (FrogrController *self)
2356 {
2357 if (self->show_details_dialog_source_id
2358 || self->show_add_tags_dialog_source_id
2359 || self->show_create_new_set_dialog_source_id
2360 || self->show_add_to_set_dialog_source_id
2361 || self->show_add_to_group_dialog_source_id)
2362 return TRUE;
2363
2364 return FALSE;
2365 }
2366
2367 static GObject *
_frogr_controller_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_properties)2368 _frogr_controller_constructor (GType type,
2369 guint n_construct_properties,
2370 GObjectConstructParam *construct_properties)
2371 {
2372 GObject *object;
2373
2374 if (!_instance)
2375 {
2376 object =
2377 G_OBJECT_CLASS (frogr_controller_parent_class)->constructor (type,
2378 n_construct_properties,
2379 construct_properties);
2380 _instance = FROGR_CONTROLLER (object);
2381 }
2382 else
2383 object = G_OBJECT (_instance);
2384
2385 return object;
2386 }
2387
2388 static void
_frogr_controller_dispose(GObject * object)2389 _frogr_controller_dispose (GObject* object)
2390 {
2391 FrogrController *controller = FROGR_CONTROLLER (object);
2392
2393 g_clear_object (&controller->mainview);
2394 g_clear_object (&controller->config);
2395 g_clear_object (&controller->account);
2396 g_clear_object (&controller->session);
2397
2398 if (controller->cancellables)
2399 {
2400 g_list_free_full (controller->cancellables, g_object_unref);
2401 controller->cancellables = NULL;
2402 }
2403
2404 G_OBJECT_CLASS (frogr_controller_parent_class)->dispose (object);
2405 }
2406
2407 static void
frogr_controller_class_init(FrogrControllerClass * klass)2408 frogr_controller_class_init (FrogrControllerClass *klass)
2409 {
2410 GObjectClass *obj_class = G_OBJECT_CLASS (klass);
2411
2412 obj_class->constructor = _frogr_controller_constructor;
2413 obj_class->dispose = _frogr_controller_dispose;
2414
2415 signals[STATE_CHANGED] =
2416 g_signal_new ("state-changed",
2417 G_OBJECT_CLASS_TYPE (klass),
2418 G_SIGNAL_RUN_FIRST,
2419 0, NULL, NULL,
2420 g_cclosure_marshal_VOID__INT,
2421 G_TYPE_NONE, 1, G_TYPE_INT);
2422
2423 signals[ACTIVE_ACCOUNT_CHANGED] =
2424 g_signal_new ("active-account-changed",
2425 G_OBJECT_CLASS_TYPE (klass),
2426 G_SIGNAL_RUN_FIRST,
2427 0, NULL, NULL,
2428 g_cclosure_marshal_VOID__OBJECT,
2429 G_TYPE_NONE, 1, FROGR_TYPE_ACCOUNT);
2430
2431 signals[ACCOUNTS_CHANGED] =
2432 g_signal_new ("accounts-changed",
2433 G_OBJECT_CLASS_TYPE (klass),
2434 G_SIGNAL_RUN_FIRST,
2435 0, NULL, NULL,
2436 g_cclosure_marshal_VOID__VOID,
2437 G_TYPE_NONE, 0);
2438 }
2439
2440 static void
frogr_controller_init(FrogrController * self)2441 frogr_controller_init (FrogrController *self)
2442 {
2443 /* Default variables */
2444 self->state = FROGR_STATE_IDLE;
2445 self->mainview = NULL;
2446
2447 self->config = frogr_config_get_instance ();
2448 g_object_ref (self->config);
2449
2450 self->session = fsp_session_new (API_KEY, SHARED_SECRET, NULL);
2451 self->cancellables = NULL;
2452 self->app_running = FALSE;
2453 self->fetching_token_replacement = FALSE;
2454 self->fetching_auth_url = FALSE;
2455 self->fetching_auth_token = FALSE;
2456 self->fetching_photosets = FALSE;
2457 self->fetching_groups = FALSE;
2458 self->fetching_tags = FALSE;
2459 self->setting_license = FALSE;
2460 self->setting_location = FALSE;
2461 self->setting_replace_date_posted = FALSE;
2462 self->adding_to_set = FALSE;
2463 self->adding_to_group = FALSE;
2464 self->photosets_fetched = FALSE;
2465 self->groups_fetched = FALSE;
2466 self->tags_fetched = FALSE;
2467 self->show_details_dialog_source_id = 0;
2468 self->show_add_tags_dialog_source_id = 0;
2469 self->show_create_new_set_dialog_source_id = 0;
2470 self->show_add_to_set_dialog_source_id = 0;
2471 self->show_add_to_group_dialog_source_id = 0;
2472 }
2473
2474
2475 /* Public API */
2476
2477 FrogrController *
frogr_controller_get_instance(void)2478 frogr_controller_get_instance (void)
2479 {
2480 if (_instance)
2481 return _instance;
2482
2483 return FROGR_CONTROLLER (g_object_new (FROGR_TYPE_CONTROLLER, NULL));
2484 }
2485
2486 gint
frogr_controller_run_app(FrogrController * self,int argc,char ** argv)2487 frogr_controller_run_app (FrogrController *self, int argc, char **argv)
2488 {
2489 g_autoptr(GtkApplication) app = NULL;
2490 gint status;
2491
2492 g_return_val_if_fail(FROGR_IS_CONTROLLER (self), -1);
2493
2494 if (self->app_running)
2495 {
2496 DEBUG ("%s", "Application already running");
2497 return -1;
2498 }
2499 self->app_running = TRUE;
2500
2501 /* Initialize and run the Gtk application */
2502 g_set_application_name(APP_SHORTNAME);
2503 app = gtk_application_new (APP_ID,
2504 G_APPLICATION_NON_UNIQUE
2505 | G_APPLICATION_HANDLES_OPEN);
2506
2507 g_signal_connect (app, "startup", G_CALLBACK (_g_application_startup_cb), self);
2508 g_signal_connect (app, "activate", G_CALLBACK (_g_application_activate_cb), self);
2509 g_signal_connect (app, "shutdown", G_CALLBACK (_g_application_shutdown_cb), self);
2510 g_signal_connect (app, "open", G_CALLBACK (_g_application_open_files_cb), self);
2511
2512 status = g_application_run (G_APPLICATION (app), argc, argv);
2513
2514 return status;
2515 }
2516
2517 FrogrMainView *
frogr_controller_get_main_view(FrogrController * self)2518 frogr_controller_get_main_view (FrogrController *self)
2519 {
2520 g_return_val_if_fail(FROGR_IS_CONTROLLER (self), FALSE);
2521 return self->mainview;
2522 }
2523
2524 FrogrModel *
frogr_controller_get_model(FrogrController * self)2525 frogr_controller_get_model (FrogrController *self)
2526 {
2527 g_return_val_if_fail(FROGR_IS_CONTROLLER (self), FALSE);
2528
2529 if (!self->mainview)
2530 return NULL;
2531
2532 return frogr_main_view_get_model (self->mainview);;
2533 }
2534
2535 void
frogr_controller_set_active_account(FrogrController * self,const gchar * username)2536 frogr_controller_set_active_account (FrogrController *self, const gchar *username)
2537 {
2538 FrogrAccount *account = NULL;
2539
2540 g_return_if_fail(FROGR_IS_CONTROLLER (self));
2541 g_return_if_fail(username);
2542
2543 frogr_config_set_active_account (self->config, username);
2544 account = frogr_config_get_active_account (self->config);
2545
2546 _set_active_account (self, account);
2547 }
2548
2549 FrogrAccount *
frogr_controller_get_active_account(FrogrController * self)2550 frogr_controller_get_active_account (FrogrController *self)
2551 {
2552 g_return_val_if_fail(FROGR_IS_CONTROLLER (self), NULL);
2553 return self->account;
2554 }
2555
2556 GSList *
frogr_controller_get_all_accounts(FrogrController * self)2557 frogr_controller_get_all_accounts (FrogrController *self)
2558 {
2559 g_return_val_if_fail(FROGR_IS_CONTROLLER (self), NULL);
2560 return frogr_config_get_accounts (self->config);
2561 }
2562
2563 FrogrControllerState
frogr_controller_get_state(FrogrController * self)2564 frogr_controller_get_state (FrogrController *self)
2565 {
2566 g_return_val_if_fail(FROGR_IS_CONTROLLER (self), FROGR_STATE_UNKNOWN);
2567 return self->state;
2568 }
2569
2570 void
frogr_controller_set_proxy(FrogrController * self,gboolean use_default_proxy,const char * host,const char * port,const char * username,const char * password)2571 frogr_controller_set_proxy (FrogrController *self,
2572 gboolean use_default_proxy,
2573 const char *host, const char *port,
2574 const char *username, const char *password)
2575 {
2576 gboolean proxy_changed = FALSE;
2577
2578 g_return_if_fail(FROGR_IS_CONTROLLER (self));
2579
2580 if (use_default_proxy)
2581 {
2582 DEBUG ("Using default proxy settings");
2583 fsp_session_set_default_proxy (self->session, TRUE);
2584
2585 if (!self->photosets_fetched || !self->groups_fetched || !self->tags_fetched)
2586 _fetch_everything (self, FALSE);
2587
2588 return;
2589 }
2590
2591 /* The host is mandatory to set up a proxy */
2592 if (host == NULL || *host == '\0') {
2593 proxy_changed = fsp_session_set_custom_proxy (self->session, NULL, NULL, NULL, NULL);
2594 DEBUG ("%s", "Not enabling the HTTP proxy");
2595 } else {
2596 g_autofree gchar *auth_part = NULL;
2597 gboolean has_username = FALSE;
2598 gboolean has_password = FALSE;
2599
2600 has_username = (username != NULL && *username != '\0');
2601 has_password = (password != NULL && *password != '\0');
2602
2603 if (has_username && has_password)
2604 auth_part = g_strdup_printf ("%s:%s@", username, password);
2605
2606 DEBUG ("Using HTTP proxy: %s%s:%s", auth_part ? auth_part : "", host, port);
2607
2608 proxy_changed = fsp_session_set_custom_proxy (self->session,
2609 host, port,
2610 username, password);
2611 }
2612
2613 /* Re-fetch information if needed after changing proxy configuration */
2614 if (self->app_running && proxy_changed)
2615 _fetch_everything (self, FALSE);
2616 }
2617
2618 void
frogr_controller_fetch_tags_if_needed(FrogrController * self)2619 frogr_controller_fetch_tags_if_needed (FrogrController *self)
2620 {
2621 g_return_if_fail(FROGR_IS_CONTROLLER (self));
2622 if (!self->fetching_tags && !self->tags_fetched)
2623 _fetch_tags (self);
2624 }
2625
2626 void
frogr_controller_show_about_dialog(FrogrController * self)2627 frogr_controller_show_about_dialog (FrogrController *self)
2628 {
2629 g_return_if_fail(FROGR_IS_CONTROLLER (self));
2630
2631 /* Run the about dialog */
2632 frogr_about_dialog_show (GTK_WINDOW (self->mainview));
2633 }
2634
2635 void
frogr_controller_show_auth_dialog(FrogrController * self)2636 frogr_controller_show_auth_dialog (FrogrController *self)
2637 {
2638 g_return_if_fail(FROGR_IS_CONTROLLER (self));
2639
2640 /* Do not show the authorization dialog if we are exchanging an old
2641 token for a new one, as it should re-authorize automatically */
2642 if (self->fetching_token_replacement)
2643 return;
2644
2645 /* Run the auth dialog */
2646 frogr_auth_dialog_show (GTK_WINDOW (self->mainview), REQUEST_AUTHORIZATION);
2647 }
2648
2649 void
frogr_controller_show_settings_dialog(FrogrController * self)2650 frogr_controller_show_settings_dialog (FrogrController *self)
2651 {
2652 g_return_if_fail(FROGR_IS_CONTROLLER (self));
2653
2654 /* Run the auth dialog */
2655 frogr_settings_dialog_show (GTK_WINDOW (self->mainview));
2656 }
2657
2658 void
frogr_controller_show_details_dialog(FrogrController * self,GSList * pictures)2659 frogr_controller_show_details_dialog (FrogrController *self,
2660 GSList *pictures)
2661 {
2662 g_return_if_fail(FROGR_IS_CONTROLLER (self));
2663
2664 /* Don't show the dialog if one is to be shown already */
2665 if (_is_modal_dialog_about_to_be_shown (self))
2666 return;
2667
2668 /* Fetch the tags list first if needed */
2669 if (frogr_config_get_tags_autocompletion (self->config) && !self->tags_fetched)
2670 {
2671 gdk_threads_add_timeout (DEFAULT_TIMEOUT, (GSourceFunc) _show_progress_on_idle, GINT_TO_POINTER (FETCHING_TAGS));
2672 if (!self->fetching_tags)
2673 _fetch_tags (self);
2674 }
2675
2676 /* Show the dialog when possible */
2677 self->show_details_dialog_source_id =
2678 gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT_IDLE, DEFAULT_TIMEOUT,
2679 (GSourceFunc) _show_details_dialog_on_idle, pictures,
2680 (GDestroyNotify) _dispose_slist_of_objects);
2681 }
2682
2683 void
frogr_controller_show_add_tags_dialog(FrogrController * self,GSList * pictures)2684 frogr_controller_show_add_tags_dialog (FrogrController *self,
2685 GSList *pictures)
2686 {
2687 g_return_if_fail(FROGR_IS_CONTROLLER (self));
2688
2689 /* Don't show the dialog if one is to be shown already */
2690 if (_is_modal_dialog_about_to_be_shown (self))
2691 return;
2692
2693 /* Fetch the tags list first if needed */
2694 if (frogr_config_get_tags_autocompletion (self->config) && !self->tags_fetched)
2695 {
2696 gdk_threads_add_timeout (DEFAULT_TIMEOUT, (GSourceFunc) _show_progress_on_idle, GINT_TO_POINTER (FETCHING_TAGS));
2697 if (!self->fetching_tags)
2698 _fetch_tags (self);
2699 }
2700
2701 /* Show the dialog when possible */
2702 self->show_add_tags_dialog_source_id =
2703 gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT_IDLE, DEFAULT_TIMEOUT,
2704 (GSourceFunc) _show_add_tags_dialog_on_idle, pictures,
2705 (GDestroyNotify) _dispose_slist_of_objects);
2706 }
2707
2708 void
frogr_controller_show_create_new_set_dialog(FrogrController * self,GSList * pictures)2709 frogr_controller_show_create_new_set_dialog (FrogrController *self,
2710 GSList *pictures)
2711 {
2712 g_return_if_fail(FROGR_IS_CONTROLLER (self));
2713
2714 /* Don't show the dialog if one is to be shown already */
2715 if (_is_modal_dialog_about_to_be_shown (self))
2716 return;
2717
2718 /* Fetch the sets first if needed */
2719 if (!self->photosets_fetched)
2720 {
2721 gdk_threads_add_timeout (DEFAULT_TIMEOUT, (GSourceFunc) _show_progress_on_idle, GINT_TO_POINTER (FETCHING_PHOTOSETS));
2722 if (!self->fetching_photosets)
2723 _fetch_photosets (self);
2724 }
2725
2726 /* Show the dialog when possible */
2727 self->show_create_new_set_dialog_source_id =
2728 gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT_IDLE, DEFAULT_TIMEOUT,
2729 (GSourceFunc) _show_create_new_set_dialog_on_idle, pictures,
2730 (GDestroyNotify) _dispose_slist_of_objects);
2731 }
2732
2733 void
frogr_controller_show_add_to_set_dialog(FrogrController * self,GSList * pictures)2734 frogr_controller_show_add_to_set_dialog (FrogrController *self,
2735 GSList *pictures)
2736 {
2737 g_return_if_fail(FROGR_IS_CONTROLLER (self));
2738
2739 /* Don't show the dialog if one is to be shown already */
2740 if (_is_modal_dialog_about_to_be_shown (self))
2741 return;
2742
2743 /* Fetch the sets first if needed */
2744 if (!self->photosets_fetched)
2745 {
2746 gdk_threads_add_timeout (DEFAULT_TIMEOUT, (GSourceFunc) _show_progress_on_idle, GINT_TO_POINTER (FETCHING_PHOTOSETS));
2747 if (!self->fetching_photosets)
2748 _fetch_photosets (self);
2749 }
2750
2751 /* Show the dialog when possible */
2752 self->show_add_to_set_dialog_source_id =
2753 gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT_IDLE, DEFAULT_TIMEOUT,
2754 (GSourceFunc) _show_add_to_set_dialog_on_idle, pictures,
2755 (GDestroyNotify) _dispose_slist_of_objects);
2756 }
2757
2758 void
frogr_controller_show_add_to_group_dialog(FrogrController * self,GSList * pictures)2759 frogr_controller_show_add_to_group_dialog (FrogrController *self,
2760 GSList *pictures)
2761 {
2762 g_return_if_fail(FROGR_IS_CONTROLLER (self));
2763
2764 /* Don't show the dialog if one is to be shown already */
2765 if (_is_modal_dialog_about_to_be_shown (self))
2766 return;
2767
2768 /* Fetch the groups first if needed */
2769 if (!self->groups_fetched)
2770
2771 {
2772 gdk_threads_add_timeout (DEFAULT_TIMEOUT, (GSourceFunc) _show_progress_on_idle, GINT_TO_POINTER (FETCHING_GROUPS));
2773 if (!self->fetching_groups)
2774 _fetch_groups (self);
2775 }
2776
2777 /* Show the dialog when possible */
2778 self->show_add_to_group_dialog_source_id =
2779 gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT_IDLE, DEFAULT_TIMEOUT,
2780 (GSourceFunc) _show_add_to_group_dialog_on_idle, pictures,
2781 (GDestroyNotify) _dispose_slist_of_objects);
2782 }
2783
2784 void
frogr_controller_open_auth_url(FrogrController * self)2785 frogr_controller_open_auth_url (FrogrController *self)
2786 {
2787 CancellableOperationData *co_data = NULL;
2788
2789 g_return_if_fail(FROGR_IS_CONTROLLER (self));
2790
2791 self->fetching_auth_url = TRUE;
2792
2793 co_data = g_slice_new0 (CancellableOperationData);
2794 co_data->controller = self;
2795 co_data->cancellable = _register_new_cancellable (self);
2796 fsp_session_get_auth_url (self->session, co_data->cancellable,
2797 _get_auth_url_cb, co_data);
2798
2799 gdk_threads_add_timeout (DEFAULT_TIMEOUT, (GSourceFunc) _show_progress_on_idle, GINT_TO_POINTER (FETCHING_AUTH_URL));
2800
2801 /* Make sure we show proper feedback if connection is too slow */
2802 gdk_threads_add_timeout (MAX_AUTH_TIMEOUT, (GSourceFunc) _cancel_authorization_on_timeout, self);
2803 }
2804
2805 void
frogr_controller_complete_auth(FrogrController * self,const gchar * verification_code)2806 frogr_controller_complete_auth (FrogrController *self, const gchar *verification_code)
2807 {
2808 CancellableOperationData *co_data = NULL;
2809
2810 g_return_if_fail(FROGR_IS_CONTROLLER (self));
2811
2812 self->fetching_auth_token = TRUE;
2813
2814 co_data = g_slice_new0 (CancellableOperationData);
2815 co_data->controller = self;
2816 co_data->cancellable = _register_new_cancellable (self);
2817 fsp_session_complete_auth (self->session, verification_code, co_data->cancellable,
2818 _complete_auth_cb, co_data);
2819
2820 gdk_threads_add_timeout (DEFAULT_TIMEOUT, (GSourceFunc) _show_progress_on_idle, GINT_TO_POINTER (FETCHING_AUTH_TOKEN));
2821
2822 /* Make sure we show proper feedback if connection is too slow */
2823 gdk_threads_add_timeout (MAX_AUTH_TIMEOUT, (GSourceFunc) _cancel_authorization_on_timeout, self);
2824 }
2825
2826 gboolean
frogr_controller_is_authorized(FrogrController * self)2827 frogr_controller_is_authorized (FrogrController *self)
2828 {
2829 g_return_val_if_fail(FROGR_IS_CONTROLLER (self), FALSE);
2830
2831 if (!self->account)
2832 return FALSE;
2833
2834 /* Old versions for accounts previously stored must be updated first */
2835 if (g_strcmp0 (frogr_account_get_version (self->account), ACCOUNTS_CURRENT_VERSION))
2836 return FALSE;
2837
2838 return TRUE;;
2839 }
2840
2841 void
frogr_controller_revoke_authorization(FrogrController * self)2842 frogr_controller_revoke_authorization (FrogrController *self)
2843 {
2844 g_return_if_fail(FROGR_IS_CONTROLLER (self));
2845
2846 /* Ensure there's the token/account is no longer active anywhere */
2847 fsp_session_set_token (self->session, NULL);
2848 fsp_session_set_token_secret (self->session, NULL);
2849 _set_active_account (self, NULL);
2850 }
2851
2852 gboolean
frogr_controller_is_connected(FrogrController * self)2853 frogr_controller_is_connected (FrogrController *self)
2854 {
2855 g_return_val_if_fail(FROGR_IS_CONTROLLER (self), FALSE);
2856
2857 /* We can't be sure 100% about having connected to flickr until we
2858 received the extra information for the current account */
2859 if (self->account)
2860 return frogr_account_has_extra_info (self->account);
2861
2862 return FALSE;
2863 }
2864
2865 void
frogr_controller_load_pictures(FrogrController * self,GSList * fileuris)2866 frogr_controller_load_pictures (FrogrController *self,
2867 GSList *fileuris)
2868 {
2869 FrogrFileLoader *loader = NULL;
2870 gulong max_picture_filesize = G_MAXULONG;
2871 gulong max_video_filesize = G_MAXULONG;
2872
2873 g_return_if_fail(FROGR_IS_CONTROLLER (self));
2874
2875 if (self->account)
2876 {
2877 max_picture_filesize = frogr_account_get_max_picture_filesize (self->account);
2878 max_video_filesize = frogr_account_get_max_video_filesize (self->account);
2879 }
2880
2881 loader = frogr_file_loader_new_from_uris (fileuris, max_picture_filesize, max_video_filesize);
2882
2883 g_signal_connect (G_OBJECT (loader), "file-loaded",
2884 G_CALLBACK (_on_file_loaded),
2885 self);
2886
2887 g_signal_connect (G_OBJECT (loader), "files-loaded",
2888 G_CALLBACK (_on_files_loaded),
2889 self);
2890
2891 /* Load the pictures! */
2892 _set_state (self, FROGR_STATE_LOADING_PICTURES);
2893 frogr_file_loader_load (loader);
2894 }
2895
2896 void
frogr_controller_upload_pictures(FrogrController * self,GSList * pictures)2897 frogr_controller_upload_pictures (FrogrController *self, GSList *pictures)
2898 {
2899 g_return_if_fail(FROGR_IS_CONTROLLER (self));
2900 g_return_if_fail(pictures);
2901
2902 /* Upload pictures */
2903 if (!frogr_controller_is_authorized (self))
2904 {
2905 g_autofree gchar *msg = NULL;
2906 msg = g_strdup_printf (_("You need to properly authorize %s before"
2907 " uploading any pictures to Flickr.\n"
2908 "Please re-authorize it."), APP_SHORTNAME);
2909
2910 frogr_util_show_error_dialog (GTK_WINDOW (self->mainview), msg);
2911 }
2912 else if (!frogr_controller_is_connected (self))
2913 {
2914 g_autofree gchar *msg = NULL;
2915 msg = g_strdup_printf (_("You need to be connected before"
2916 " uploading any pictures to Flickr."));
2917 frogr_util_show_error_dialog (GTK_WINDOW (self->mainview), msg);
2918 }
2919 else
2920 {
2921 UploadPicturesData *up_data = g_slice_new0 (UploadPicturesData);
2922 up_data->pictures = g_slist_copy (pictures);
2923 up_data->current = up_data->pictures;
2924 up_data->index = 0;
2925 up_data->n_pictures = g_slist_length (pictures);
2926
2927 /* Add references */
2928 g_slist_foreach (up_data->pictures, (GFunc)g_object_ref, NULL);
2929
2930 /* Load the pictures! */
2931 _set_state (self, FROGR_STATE_UPLOADING_PICTURES);
2932 frogr_main_view_show_progress (self->mainview, _("Uploading Pictures"), NULL);
2933 _upload_next_picture (self, up_data);
2934 }
2935 }
2936
2937 void
frogr_controller_reorder_pictures(FrogrController * self)2938 frogr_controller_reorder_pictures (FrogrController *self)
2939 {
2940 g_return_if_fail(FROGR_IS_CONTROLLER (self));
2941 frogr_main_view_reorder_pictures (self->mainview);
2942 }
2943
2944 void
frogr_controller_cancel_ongoing_requests(FrogrController * self)2945 frogr_controller_cancel_ongoing_requests (FrogrController *self)
2946 {
2947 GCancellable *cancellable = NULL;
2948 GList *item = NULL;
2949
2950 g_return_if_fail(FROGR_IS_CONTROLLER (self));
2951
2952 for (item = self->cancellables; item; item = g_list_next (item))
2953 {
2954 cancellable = G_CANCELLABLE (item->data);
2955 if (!g_cancellable_is_cancelled (cancellable))
2956 g_cancellable_cancel (cancellable);
2957 }
2958 }
2959
2960 gboolean
frogr_controller_open_project_from_file(FrogrController * self,const gchar * path)2961 frogr_controller_open_project_from_file (FrogrController *self, const gchar *path)
2962 {
2963 g_autoptr(JsonParser) json_parser = NULL;
2964 g_autoptr(GError) error = NULL;
2965 gboolean result = FALSE;
2966
2967 g_return_val_if_fail(FROGR_IS_CONTROLLER (self), FALSE);
2968 g_return_val_if_fail(path, FALSE);
2969
2970 /* Load from disk */
2971 json_parser = json_parser_new ();
2972 json_parser_load_from_file (json_parser, path, &error);
2973 if (error)
2974 {
2975 g_autofree gchar *msg = NULL;
2976
2977 msg = g_strdup_printf (_("Error opening project file"));
2978 frogr_util_show_error_dialog (GTK_WINDOW (self->mainview), msg);
2979
2980 DEBUG ("Error loading project file: %s", error->message);
2981 }
2982 else
2983 {
2984 FrogrModel *model = NULL;
2985 JsonNode *root_node = NULL;
2986 JsonObject *root_object = NULL;
2987 JsonObject *data_object = NULL;
2988
2989 /* Make sure we are not fetching any data from the network at
2990 this moment, or cancel otherwise, so the model is ready */
2991 if (self->fetching_photosets || self->fetching_groups || self->fetching_tags)
2992 frogr_controller_cancel_ongoing_requests (self);
2993
2994 /* Deserialize from the JSON data and update the model */
2995 _set_state (self, FROGR_STATE_LOADING_PICTURES);
2996
2997 model = frogr_main_view_get_model (self->mainview);
2998
2999 root_node = json_parser_get_root (json_parser);
3000 root_object = json_node_get_object (root_node);
3001 data_object = json_object_get_object_member (root_object, "data");
3002
3003 frogr_main_view_update_project_path (self->mainview, path);
3004 frogr_model_deserialize (model, data_object);
3005 result = TRUE;
3006 }
3007
3008 return result;
3009 }
3010
3011 gboolean
frogr_controller_save_project_to_file(FrogrController * self,const gchar * path)3012 frogr_controller_save_project_to_file (FrogrController *self, const gchar *path)
3013 {
3014 FrogrModel *model = NULL;
3015 g_autoptr(JsonGenerator) json_gen = NULL;
3016 g_autoptr(JsonNode) root_node = NULL;
3017 g_autoptr(JsonObject) root_object = NULL;
3018 JsonObject *serialized_model = NULL;
3019 gint n_pictures;
3020 gint n_photosets;
3021 gint n_groups;
3022 gint n_tags;
3023 g_autoptr(GError) error = NULL;
3024
3025 g_return_val_if_fail(FROGR_IS_CONTROLLER (self), FALSE);
3026 g_return_val_if_fail(path, FALSE);
3027
3028 model = frogr_main_view_get_model (self->mainview);
3029
3030 n_pictures = frogr_model_n_pictures (model);
3031 n_photosets = frogr_model_n_photosets (model);
3032 n_groups = frogr_model_n_groups (model);
3033 n_tags = frogr_model_n_local_tags (model);
3034
3035 root_node = json_node_new (JSON_NODE_OBJECT);
3036 root_object = json_object_new ();
3037 json_object_set_string_member (root_object, "frogr-version", APP_VERSION);
3038 json_object_set_int_member (root_object, "n_pictures", n_pictures);
3039 json_object_set_int_member (root_object, "n_photosets", n_photosets);
3040 json_object_set_int_member (root_object, "n_groups", n_groups);
3041 json_object_set_int_member (root_object, "n_tags", n_tags);
3042
3043 DEBUG ("Saving project to file %s:\n"
3044 "\tNumber of pictures: %d\n"
3045 "\tNumber of photosets: %d\n"
3046 "\tNumber of groups: %d\n"
3047 "\tNumber of tags: %d\n",
3048 path, n_pictures, n_photosets, n_groups, n_tags);
3049
3050 serialized_model = frogr_model_serialize (model);
3051 json_object_set_object_member (root_object, "data", serialized_model);
3052 json_node_set_object (root_node, root_object);
3053
3054 /* Create a JsonGenerator using the JsonNode as root */
3055 json_gen = json_generator_new ();
3056 json_generator_set_root (json_gen, root_node);
3057
3058 /* Save to disk */
3059 json_generator_to_file (json_gen, path, &error);
3060
3061 if (error)
3062 {
3063 DEBUG ("Error serializing current state to %s: %s",
3064 path, error->message);
3065 return FALSE;
3066 }
3067
3068 frogr_main_view_update_project_path (self->mainview, path);
3069 return TRUE;
3070 }
3071
3072 void
frogr_controller_set_use_dark_theme(FrogrController * self,gboolean value)3073 frogr_controller_set_use_dark_theme (FrogrController *self, gboolean value)
3074 {
3075 GtkSettings *gtk_settings = NULL;
3076
3077 gtk_settings = gtk_settings_get_default ();
3078 g_object_set (G_OBJECT (gtk_settings), "gtk-application-prefer-dark-theme", value, NULL);
3079 }
3080