f5c58e5f8d8bc50077fad6b923a56c0e0c98482c
[geeqie.git] / src / remote.c
1 /*
2  * Copyright (C) 2004 John Ellis
3  * Copyright (C) 2008 - 2016 The Geeqie Team
4  *
5  * Author: John Ellis
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "main.h"
23 #include "remote.h"
24
25 #include "cache_maint.h"
26 #include "collect.h"
27 #include "filedata.h"
28 #include "image.h"
29 #include "img-view.h"
30 #include "layout.h"
31 #include "layout_image.h"
32 #include "layout_util.h"
33 #include "misc.h"
34 #include "pixbuf-renderer.h"
35 #include "slideshow.h"
36 #include "ui_fileops.h"
37 #include "rcfile.h"
38
39 #include <sys/socket.h>
40 #include <sys/un.h>
41 #include <signal.h>
42 #include <errno.h>
43
44 #include "glua.h"
45
46 #define SERVER_MAX_CLIENTS 8
47
48 #define REMOTE_SERVER_BACKLOG 4
49
50
51 #ifndef UNIX_PATH_MAX
52 #define UNIX_PATH_MAX 108
53 #endif
54
55
56 static RemoteConnection *remote_client_open(const gchar *path);
57 static gint remote_client_send(RemoteConnection *rc, const gchar *text);
58 static void gr_raise(const gchar *text, GIOChannel *channel, gpointer data);
59
60 static LayoutWindow *lw_id = NULL; /* points to the window set by the --id option */
61
62 typedef struct _RemoteClient RemoteClient;
63 struct _RemoteClient {
64         gint fd;
65         guint channel_id; /* event source id */
66         RemoteConnection *rc;
67 };
68
69 typedef struct _RemoteData RemoteData;
70 struct _RemoteData {
71         CollectionData *command_collection;
72 };
73
74
75 static gboolean remote_server_client_cb(GIOChannel *source, GIOCondition condition, gpointer data)
76 {
77         RemoteClient *client = data;
78         RemoteConnection *rc;
79         GIOStatus status = G_IO_STATUS_NORMAL;
80
81         lw_id = NULL;
82         rc = client->rc;
83
84         if (condition & G_IO_IN)
85                 {
86                 gchar *buffer = NULL;
87                 GError *error = NULL;
88                 gsize termpos;
89
90                 while ((status = g_io_channel_read_line(source, &buffer, NULL, &termpos, &error)) == G_IO_STATUS_NORMAL)
91                         {
92                         if (buffer)
93                                 {
94                                 buffer[termpos] = '\0';
95
96                                 if (strlen(buffer) > 0)
97                                         {
98                                         if (rc->read_func) rc->read_func(rc, buffer, source, rc->read_data);
99                                         g_io_channel_write_chars(source, "\n", -1, NULL, NULL); /* empty line finishes the command */
100                                         g_io_channel_flush(source, NULL);
101                                         }
102                                 g_free(buffer);
103
104                                 buffer = NULL;
105                                 }
106                         }
107
108                 if (error)
109                         {
110                         log_printf("error reading socket: %s\n", error->message);
111                         g_error_free(error);
112                         }
113                 }
114
115         if (condition & G_IO_HUP || status == G_IO_STATUS_EOF || status == G_IO_STATUS_ERROR)
116                 {
117                 rc->clients = g_list_remove(rc->clients, client);
118
119                 DEBUG_1("HUP detected, closing client.");
120                 DEBUG_1("client count %d", g_list_length(rc->clients));
121
122                 g_source_remove(client->channel_id);
123                 close(client->fd);
124                 g_free(client);
125                 }
126
127         return TRUE;
128 }
129
130 static void remote_server_client_add(RemoteConnection *rc, gint fd)
131 {
132         RemoteClient *client;
133         GIOChannel *channel;
134
135         if (g_list_length(rc->clients) > SERVER_MAX_CLIENTS)
136                 {
137                 log_printf("maximum remote clients of %d exceeded, closing connection\n", SERVER_MAX_CLIENTS);
138                 close(fd);
139                 return;
140                 }
141
142         client = g_new0(RemoteClient, 1);
143         client->rc = rc;
144         client->fd = fd;
145
146         channel = g_io_channel_unix_new(fd);
147         client->channel_id = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, G_IO_IN | G_IO_HUP,
148                                                  remote_server_client_cb, client, NULL);
149         g_io_channel_unref(channel);
150
151         rc->clients = g_list_append(rc->clients, client);
152         DEBUG_1("client count %d", g_list_length(rc->clients));
153 }
154
155 static void remote_server_clients_close(RemoteConnection *rc)
156 {
157         while (rc->clients)
158                 {
159                 RemoteClient *client = rc->clients->data;
160
161                 rc->clients = g_list_remove(rc->clients, client);
162
163                 g_source_remove(client->channel_id);
164                 close(client->fd);
165                 g_free(client);
166                 }
167 }
168
169 static gboolean remote_server_read_cb(GIOChannel *source, GIOCondition condition, gpointer data)
170 {
171         RemoteConnection *rc = data;
172         gint fd;
173         guint alen;
174
175         fd = accept(rc->fd, NULL, &alen);
176         if (fd == -1)
177                 {
178                 log_printf("error accepting socket: %s\n", strerror(errno));
179                 return TRUE;
180                 }
181
182         remote_server_client_add(rc, fd);
183
184         return TRUE;
185 }
186
187 gboolean remote_server_exists(const gchar *path)
188 {
189         RemoteConnection *rc;
190
191         /* verify server up */
192         rc = remote_client_open(path);
193         remote_close(rc);
194
195         if (rc) return TRUE;
196
197         /* unable to connect, remove socket file to free up address */
198         unlink(path);
199         return FALSE;
200 }
201
202 static RemoteConnection *remote_server_open(const gchar *path)
203 {
204         RemoteConnection *rc;
205         struct sockaddr_un addr;
206         gint sun_path_len;
207         gint fd;
208         GIOChannel *channel;
209
210         if (remote_server_exists(path))
211                 {
212                 log_printf("Address already in use: %s\n", path);
213                 return NULL;
214                 }
215
216         fd = socket(PF_UNIX, SOCK_STREAM, 0);
217         if (fd == -1) return NULL;
218
219         addr.sun_family = AF_UNIX;
220         sun_path_len = MIN(strlen(path) + 1, UNIX_PATH_MAX);
221         strncpy(addr.sun_path, path, sun_path_len);
222         if (bind(fd, &addr, sizeof(addr)) == -1 ||
223             listen(fd, REMOTE_SERVER_BACKLOG) == -1)
224                 {
225                 log_printf("error subscribing to socket: %s\n", strerror(errno));
226                 close(fd);
227                 return NULL;
228                 }
229
230         rc = g_new0(RemoteConnection, 1);
231
232         rc->server = TRUE;
233         rc->fd = fd;
234         rc->path = g_strdup(path);
235
236         channel = g_io_channel_unix_new(rc->fd);
237         g_io_channel_set_flags(channel, G_IO_FLAG_NONBLOCK, NULL);
238
239         rc->channel_id = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, G_IO_IN,
240                                              remote_server_read_cb, rc, NULL);
241         g_io_channel_unref(channel);
242
243         return rc;
244 }
245
246 static void remote_server_subscribe(RemoteConnection *rc, RemoteReadFunc *func, gpointer data)
247 {
248         if (!rc || !rc->server) return;
249
250         rc->read_func = func;
251         rc->read_data = data;
252 }
253
254
255 static RemoteConnection *remote_client_open(const gchar *path)
256 {
257         RemoteConnection *rc;
258         struct stat st;
259         struct sockaddr_un addr;
260         gint sun_path_len;
261         gint fd;
262
263         if (stat(path, &st) != 0 || !S_ISSOCK(st.st_mode)) return NULL;
264
265         fd = socket(PF_UNIX, SOCK_STREAM, 0);
266         if (fd == -1) return NULL;
267
268         addr.sun_family = AF_UNIX;
269         sun_path_len = MIN(strlen(path) + 1, UNIX_PATH_MAX);
270         strncpy(addr.sun_path, path, sun_path_len);
271         if (connect(fd, &addr, sizeof(addr)) == -1)
272                 {
273                 DEBUG_1("error connecting to socket: %s", strerror(errno));
274                 close(fd);
275                 return NULL;
276                 }
277
278         rc = g_new0(RemoteConnection, 1);
279         rc->server = FALSE;
280         rc->fd = fd;
281         rc->path = g_strdup(path);
282
283         return rc;
284 }
285
286 static sig_atomic_t sigpipe_occurred = FALSE;
287
288 static void sighandler_sigpipe(gint sig)
289 {
290         sigpipe_occurred = TRUE;
291 }
292
293 static gboolean remote_client_send(RemoteConnection *rc, const gchar *text)
294 {
295         struct sigaction new_action, old_action;
296         gboolean ret = FALSE;
297         GError *error = NULL;
298         GIOChannel *channel;
299
300         if (!rc || rc->server) return FALSE;
301         if (!text) return TRUE;
302
303         sigpipe_occurred = FALSE;
304
305         new_action.sa_handler = sighandler_sigpipe;
306         sigemptyset(&new_action.sa_mask);
307         new_action.sa_flags = 0;
308
309         /* setup our signal handler */
310         sigaction(SIGPIPE, &new_action, &old_action);
311
312         channel = g_io_channel_unix_new(rc->fd);
313
314         g_io_channel_write_chars(channel, text, -1, NULL, &error);
315         g_io_channel_write_chars(channel, "\n", -1, NULL, &error);
316         g_io_channel_flush(channel, &error);
317
318         if (error)
319                 {
320                 log_printf("error reading socket: %s\n", error->message);
321                 g_error_free(error);
322                 ret = FALSE;;
323                 }
324         else
325                 {
326                 ret = TRUE;
327                 }
328
329         if (ret)
330                 {
331                 gchar *buffer = NULL;
332                 gsize termpos;
333                 while (g_io_channel_read_line(channel, &buffer, NULL, &termpos, &error) == G_IO_STATUS_NORMAL)
334                         {
335                         if (buffer)
336                                 {
337                                 if (buffer[0] == '\n') /* empty line finishes the command */
338                                         {
339                                         g_free(buffer);
340                                         fflush(stdout);
341                                         break;
342                                         }
343                                 buffer[termpos] = '\0';
344                                 printf("%s\n", buffer);
345                                 g_free(buffer);
346                                 buffer = NULL;
347                                 }
348                         }
349
350                 if (error)
351                         {
352                         log_printf("error reading socket: %s\n", error->message);
353                         g_error_free(error);
354                         ret = FALSE;
355                         }
356                 }
357
358
359         /* restore the original signal handler */
360         sigaction(SIGPIPE, &old_action, NULL);
361         g_io_channel_unref(channel);
362         return ret;
363 }
364
365 void remote_close(RemoteConnection *rc)
366 {
367         if (!rc) return;
368
369         if (rc->server)
370                 {
371                 remote_server_clients_close(rc);
372
373                 g_source_remove(rc->channel_id);
374                 unlink(rc->path);
375                 }
376
377         if (rc->read_data)
378                 g_free(rc->read_data);
379
380         close(rc->fd);
381
382         g_free(rc->path);
383         g_free(rc);
384 }
385
386 /*
387  *-----------------------------------------------------------------------------
388  * remote functions
389  *-----------------------------------------------------------------------------
390  */
391
392 static void gr_image_next(const gchar *text, GIOChannel *channel, gpointer data)
393 {
394         layout_image_next(lw_id);
395 }
396
397 static void gr_new_window(const gchar *text, GIOChannel *channel, gpointer data)
398 {
399         LayoutWindow *lw = NULL;
400
401         if (!layout_valid(&lw)) return;
402
403         lw_id = layout_menu_new_window(NULL, lw);
404 }
405
406 static gboolean gr_close_window_cb()
407 {
408         if (!layout_valid(&lw_id)) return FALSE;
409
410         layout_menu_close_cb(NULL, lw_id);
411
412         return FALSE;
413 }
414
415 static void gr_close_window(const gchar *text, GIOChannel *channel, gpointer data)
416 {
417         g_idle_add(gr_close_window_cb, NULL);
418 }
419
420 static void gr_image_prev(const gchar *text, GIOChannel *channel, gpointer data)
421 {
422         layout_image_prev(lw_id);
423 }
424
425 static void gr_image_first(const gchar *text, GIOChannel *channel, gpointer data)
426 {
427         layout_image_first(lw_id);
428 }
429
430 static void gr_image_last(const gchar *text, GIOChannel *channel, gpointer data)
431 {
432         layout_image_last(lw_id);
433 }
434
435 static void gr_fullscreen_toggle(const gchar *text, GIOChannel *channel, gpointer data)
436 {
437         layout_image_full_screen_toggle(lw_id);
438 }
439
440 static void gr_fullscreen_start(const gchar *text, GIOChannel *channel, gpointer data)
441 {
442         layout_image_full_screen_start(lw_id);
443 }
444
445 static void gr_fullscreen_stop(const gchar *text, GIOChannel *channel, gpointer data)
446 {
447         layout_image_full_screen_stop(lw_id);
448 }
449
450 static void gr_lw_id(const gchar *text, GIOChannel *channel, gpointer data)
451 {
452         lw_id = layout_find_by_layout_id(text);
453         if (!lw_id)
454                 {
455                 log_printf("remote sent window ID that does not exist:\"%s\"\n",text);
456                 }
457         layout_valid(&lw_id);
458 }
459
460 static void gr_slideshow_start_rec(const gchar *text, GIOChannel *channel, gpointer data)
461 {
462         GList *list;
463         FileData *dir_fd = file_data_new_dir(text);
464         list = filelist_recursive(dir_fd);
465         file_data_unref(dir_fd);
466         if (!list) return;
467 //printf("length: %d\n", g_list_length(list));
468         layout_image_slideshow_stop(lw_id);
469         layout_image_slideshow_start_from_list(lw_id, list);
470 }
471
472 static void gr_cache_thumb(const gchar *text, GIOChannel *channel, gpointer data)
473 {
474         if (!g_strcmp0(text, "clear"))
475                 cache_maintain_home_remote(FALSE, TRUE);
476         else if (!g_strcmp0(text, "clean"))
477                 cache_maintain_home_remote(FALSE, FALSE);
478 }
479
480 static void gr_cache_shared(const gchar *text, GIOChannel *channel, gpointer data)
481 {
482         if (!g_strcmp0(text, "clear"))
483                 cache_manager_standard_process_remote(TRUE);
484         else if (!g_strcmp0(text, "clean"))
485                 cache_manager_standard_process_remote(FALSE);
486 }
487
488 static void gr_cache_metadata(const gchar *text, GIOChannel *channel, gpointer data)
489 {
490         cache_maintain_home_remote(TRUE, FALSE);
491 }
492
493 static void gr_cache_render(const gchar *text, GIOChannel *channel, gpointer data)
494 {
495         cache_manager_render_remote(text, FALSE, FALSE);
496 }
497
498 static void gr_cache_render_recurse(const gchar *text, GIOChannel *channel, gpointer data)
499 {
500         cache_manager_render_remote(text, TRUE, FALSE);
501 }
502
503 static void gr_cache_render_standard(const gchar *text, GIOChannel *channel, gpointer data)
504 {
505         if(options->thumbnails.spec_standard)
506                 cache_manager_render_remote(text, FALSE, TRUE);
507 }
508
509 static void gr_cache_render_standard_recurse(const gchar *text, GIOChannel *channel, gpointer data)
510 {
511         if(options->thumbnails.spec_standard)
512                 cache_manager_render_remote(text, TRUE, TRUE);
513 }
514
515 static void gr_slideshow_toggle(const gchar *text, GIOChannel *channel, gpointer data)
516 {
517         layout_image_slideshow_toggle(lw_id);
518 }
519
520 static void gr_slideshow_start(const gchar *text, GIOChannel *channel, gpointer data)
521 {
522         layout_image_slideshow_start(lw_id);
523 }
524
525 static void gr_slideshow_stop(const gchar *text, GIOChannel *channel, gpointer data)
526 {
527         layout_image_slideshow_stop(lw_id);
528 }
529
530 static void gr_slideshow_delay(const gchar *text, GIOChannel *channel, gpointer data)
531 {
532         gdouble t1, t2, t3, n;
533         gint res;
534
535         res = sscanf(text, "%lf:%lf:%lf", &t1, &t2, &t3);
536         if (res == 3)
537                 {
538                 n = (t1 * 3600) + (t2 * 60) + t3;
539                 if (n < SLIDESHOW_MIN_SECONDS || n > SLIDESHOW_MAX_SECONDS ||
540                                 t1 >= 24 || t2 >= 60 || t3 >= 60)
541                         {
542                         printf_term(TRUE, "Remote slideshow delay out of range (%.1f to %.1f)\n",
543                                                                 SLIDESHOW_MIN_SECONDS, SLIDESHOW_MAX_SECONDS);
544                         return;
545                         }
546                 }
547         else if (res == 2)
548                 {
549                 n = t1 * 60 + t2;
550                 if (n < SLIDESHOW_MIN_SECONDS || n > SLIDESHOW_MAX_SECONDS ||
551                                 t1 >= 60 || t2 >= 60)
552                         {
553                         printf_term(TRUE, "Remote slideshow delay out of range (%.1f to %.1f)\n",
554                                                                 SLIDESHOW_MIN_SECONDS, SLIDESHOW_MAX_SECONDS);
555                         return;
556                         }
557                 }
558         else if (res == 1)
559                 {
560                 n = t1;
561                 if (n < SLIDESHOW_MIN_SECONDS || n > SLIDESHOW_MAX_SECONDS)
562                         {
563                         printf_term(TRUE, "Remote slideshow delay out of range (%.1f to %.1f)\n",
564                                                                 SLIDESHOW_MIN_SECONDS, SLIDESHOW_MAX_SECONDS);
565                         return;
566                         }
567                 }
568         else
569                 {
570                 n = 0;
571                 }
572
573         options->slideshow.delay = (gint)(n * 10.0 + 0.01);
574 }
575
576 static void gr_tools_show(const gchar *text, GIOChannel *channel, gpointer data)
577 {
578         gboolean popped;
579         gboolean hidden;
580
581         if (layout_tools_float_get(lw_id, &popped, &hidden) && hidden)
582                 {
583                 layout_tools_float_set(lw_id, popped, FALSE);
584                 }
585 }
586
587 static void gr_tools_hide(const gchar *text, GIOChannel *channel, gpointer data)
588 {
589         gboolean popped;
590         gboolean hidden;
591
592         if (layout_tools_float_get(lw_id, &popped, &hidden) && !hidden)
593                 {
594                 layout_tools_float_set(lw_id, popped, TRUE);
595                 }
596 }
597
598 static gboolean gr_quit_idle_cb(gpointer data)
599 {
600         exit_program();
601
602         return FALSE;
603 }
604
605 static void gr_quit(const gchar *text, GIOChannel *channel, gpointer data)
606 {
607         /* schedule exit when idle, if done from within a
608          * remote handler remote_close will crash
609          */
610         g_idle_add(gr_quit_idle_cb, NULL);
611 }
612
613 static void gr_file_load_no_raise(const gchar *text, GIOChannel *channel, gpointer data)
614 {
615         gchar *filename = expand_tilde(text);
616
617         if (isfile(filename))
618                 {
619                 if (file_extension_match(filename, GQ_COLLECTION_EXT))
620                         {
621                         collection_window_new(filename);
622                         }
623                 else
624                         {
625                         layout_set_path(lw_id, filename);
626                         }
627                 }
628         else if (isdir(filename))
629                 {
630                 layout_set_path(lw_id, filename);
631                 }
632         else
633                 {
634                 log_printf("remote sent filename that does not exist:\"%s\"\n", filename);
635                 layout_set_path(lw_id, homedir());
636                 }
637
638         g_free(filename);
639 }
640
641 static void gr_file_load(const gchar *text, GIOChannel *channel, gpointer data)
642 {
643         gr_file_load_no_raise(text, channel, data);
644
645         gr_raise(text, channel, data);
646 }
647
648 static void gr_pixel_info(const gchar *text, GIOChannel *channel, gpointer data)
649 {
650         gchar *pixel_info;
651         gint x_pixel, y_pixel;
652         gint width, height;
653         gint r_mouse, g_mouse, b_mouse;
654         PixbufRenderer *pr;
655         LayoutWindow *lw = NULL;
656
657         if (!layout_valid(&lw_id)) return;
658
659         pr = (PixbufRenderer*)lw_id->image->pr;
660
661         if (pr)
662                 {
663                 pixbuf_renderer_get_image_size(pr, &width, &height);
664                 if (width < 1 || height < 1) return;
665
666                 pixbuf_renderer_get_mouse_position(pr, &x_pixel, &y_pixel);
667
668                 if (x_pixel >= 0 && y_pixel >= 0)
669                         {
670                         pixbuf_renderer_get_pixel_colors(pr, x_pixel, y_pixel,
671                                                          &r_mouse, &g_mouse, &b_mouse);
672
673                         pixel_info = g_strdup_printf(_("[%d,%d]: RGB(%3d,%3d,%3d)"),
674                                                  x_pixel, y_pixel,
675                                                  r_mouse, g_mouse, b_mouse);
676
677                         g_io_channel_write_chars(channel, pixel_info, -1, NULL, NULL);
678                         g_io_channel_write_chars(channel, "\n", -1, NULL, NULL);
679
680                         g_free(pixel_info);
681                         }
682                 else
683                         {
684                         return;
685                         }
686                 }
687         else
688                 {
689                 return;
690                 }
691 }
692
693 static void gr_rectangle(const gchar *text, GIOChannel *channel, gpointer data)
694 {
695         gchar *rectangle_info;
696         PixbufRenderer *pr;
697         LayoutWindow *lw = NULL;
698         gint x1, y1, x2, y2;
699
700         if (!layout_valid(&lw_id)) return;
701
702         pr = (PixbufRenderer*)lw_id->image->pr;
703
704         if (pr)
705                 {
706                 image_get_rectangle(&x1, &y1, &x2, &y2);
707                 rectangle_info = g_strdup_printf(_("%dx%d+%d+%d"),
708                                         (x2 > x1) ? x2 - x1 : x1 - x2,
709                                         (y2 > y1) ? y2 - y1 : y1 - y2,
710                                         (x2 > x1) ? x1 : x2,
711                                         (y2 > y1) ? y1 : y2);
712
713                 g_io_channel_write_chars(channel, rectangle_info, -1, NULL, NULL);
714                 g_io_channel_write_chars(channel, "\n", -1, NULL, NULL);
715
716                 g_free(rectangle_info);
717                 }
718 }
719
720 static void gr_file_tell(const gchar *text, GIOChannel *channel, gpointer data)
721 {
722         if (!layout_valid(&lw_id)) return;
723
724         if (image_get_path(lw_id->image))
725                 {
726                 g_io_channel_write_chars(channel, image_get_path(lw_id->image), -1, NULL, NULL);
727                 g_io_channel_write_chars(channel, "\n", -1, NULL, NULL);
728                 }
729 }
730
731 static void gr_config_load(const gchar *text, GIOChannel *channel, gpointer data)
732 {
733         gchar *filename = expand_tilde(text);
734
735         if (isfile(filename))
736                 {
737                 load_config_from_file(filename, FALSE);
738                 }
739         else
740                 {
741                 log_printf("remote sent filename that does not exist:\"%s\"\n", filename);
742                 layout_set_path(NULL, homedir());
743                 }
744
745         g_free(filename);
746 }
747
748 static void gr_get_sidecars(const gchar *text, GIOChannel *channel, gpointer data)
749 {
750         gchar *filename = expand_tilde(text);
751         FileData *fd = file_data_new_group(filename);
752
753         GList *work;
754         if (fd->parent) fd = fd->parent;
755
756         g_io_channel_write_chars(channel, fd->path, -1, NULL, NULL);
757         g_io_channel_write_chars(channel, "\n", -1, NULL, NULL);
758
759         work = fd->sidecar_files;
760
761         while (work)
762                 {
763                 fd = work->data;
764                 work = work->next;
765                 g_io_channel_write_chars(channel, fd->path, -1, NULL, NULL);
766                 g_io_channel_write_chars(channel, "\n", -1, NULL, NULL);
767                 }
768         g_free(filename);
769 }
770
771 static void gr_get_destination(const gchar *text, GIOChannel *channel, gpointer data)
772 {
773         gchar *filename = expand_tilde(text);
774         FileData *fd = file_data_new_group(filename);
775
776         if (fd->change && fd->change->dest)
777                 {
778                 g_io_channel_write_chars(channel, fd->change->dest, -1, NULL, NULL);
779                 g_io_channel_write_chars(channel, "\n", -1, NULL, NULL);
780                 }
781         g_free(filename);
782 }
783
784 static void gr_file_view(const gchar *text, GIOChannel *channel, gpointer data)
785 {
786         gchar *filename = expand_tilde(text);
787
788         view_window_new(file_data_new_group(filename));
789         g_free(filename);
790 }
791
792 static void gr_list_clear(const gchar *text, GIOChannel *channel, gpointer data)
793 {
794         RemoteData *remote_data = data;
795
796         if (remote_data->command_collection)
797                 {
798                 collection_unref(remote_data->command_collection);
799                 remote_data->command_collection = NULL;
800                 }
801 }
802
803 static void gr_list_add(const gchar *text, GIOChannel *channel, gpointer data)
804 {
805         RemoteData *remote_data = data;
806         gboolean new = TRUE;
807
808         if (!remote_data->command_collection)
809                 {
810                 CollectionData *cd;
811
812                 cd = collection_new("");
813
814                 g_free(cd->path);
815                 cd->path = NULL;
816                 g_free(cd->name);
817                 cd->name = g_strdup(_("Command line"));
818
819                 remote_data->command_collection = cd;
820                 }
821         else
822                 {
823                 new = (!collection_get_first(remote_data->command_collection));
824                 }
825
826         if (collection_add(remote_data->command_collection, file_data_new_group(text), FALSE) && new)
827                 {
828                 layout_image_set_collection(NULL, remote_data->command_collection,
829                                             collection_get_first(remote_data->command_collection));
830                 }
831 }
832
833 static void gr_raise(const gchar *text, GIOChannel *channel, gpointer data)
834 {
835         LayoutWindow *lw = NULL;
836
837         if (layout_valid(&lw_id))
838                 {
839                 gtk_window_present(GTK_WINDOW(lw_id->window));
840                 }
841 }
842
843 #ifdef HAVE_LUA
844 static void gr_lua(const gchar *text, GIOChannel *channel, gpointer data)
845 {
846         gchar *result = NULL;
847         gchar **lua_command;
848
849         lua_command = g_strsplit(text, ",", 2);
850
851         if (lua_command[0] && lua_command[1])
852                 {
853                 FileData *fd = file_data_new_group(lua_command[0]);
854                 result = g_strdup(lua_callvalue(fd, lua_command[1], NULL));
855                 if (result)
856                         {
857                         g_io_channel_write_chars(channel, result, -1, NULL, NULL);
858                         }
859                 else
860                         {
861                         g_io_channel_write_chars(channel, N_("lua error: no data"), -1, NULL, NULL);
862                         }
863                 }
864         else
865                 {
866                 g_io_channel_write_chars(channel, N_("lua error: no data"), -1, NULL, NULL);
867                 }
868
869         g_io_channel_write_chars(channel, "\n", -1, NULL, NULL);
870
871         g_strfreev(lua_command);
872         g_free(result);
873 }
874 #endif
875
876 typedef struct _RemoteCommandEntry RemoteCommandEntry;
877 struct _RemoteCommandEntry {
878         gchar *opt_s;
879         gchar *opt_l;
880         void (*func)(const gchar *text, GIOChannel *channel, gpointer data);
881         gboolean needs_extra;
882         gboolean prefer_command_line;
883         gchar *parameter;
884         gchar *description;
885 };
886
887 static RemoteCommandEntry remote_commands[] = {
888         /* short, long                  callback,               extra, prefer, parameter, description */
889         { "-n", "--next",               gr_image_next,          FALSE, FALSE, NULL, N_("next image") },
890         { "-b", "--back",               gr_image_prev,          FALSE, FALSE, NULL, N_("previous image") },
891         { NULL, "--first",              gr_image_first,         FALSE, FALSE, NULL, N_("first image") },
892         { NULL, "--last",               gr_image_last,          FALSE, FALSE, NULL, N_("last image") },
893         { "-f", "--fullscreen",         gr_fullscreen_toggle,   FALSE, TRUE,  NULL, N_("toggle full screen") },
894         { "-fs","--fullscreen-start",   gr_fullscreen_start,    FALSE, FALSE, NULL, N_("start full screen") },
895         { "-fS","--fullscreen-stop",    gr_fullscreen_stop,     FALSE, FALSE, NULL, N_("stop full screen") },
896         { "-s", "--slideshow",          gr_slideshow_toggle,    FALSE, TRUE,  NULL, N_("toggle slide show") },
897         { "-ss","--slideshow-start",    gr_slideshow_start,     FALSE, FALSE, NULL, N_("start slide show") },
898         { "-sS","--slideshow-stop",     gr_slideshow_stop,      FALSE, FALSE, NULL, N_("stop slide show") },
899         { NULL, "--slideshow-recurse:", gr_slideshow_start_rec, TRUE,  FALSE, N_("<FOLDER>"), N_("start recursive slide show in FOLDER") },
900         { "-d", "--delay=",             gr_slideshow_delay,     TRUE,  FALSE, N_("<[H:][M:][N][.M]>"), N_("set slide show delay to Hrs Mins N.M seconds") },
901         { "+t", "--tools-show",         gr_tools_show,          FALSE, TRUE,  NULL, N_("show tools") },
902         { "-t", "--tools-hide",         gr_tools_hide,          FALSE, TRUE,  NULL, N_("hide tools") },
903         { "-q", "--quit",               gr_quit,                FALSE, FALSE, NULL, N_("quit") },
904         { NULL, "--config-load:",       gr_config_load,         TRUE,  FALSE, N_("<FILE>"), N_("load configuration from FILE") },
905         { NULL, "--get-sidecars:",      gr_get_sidecars,        TRUE,  FALSE, N_("<FILE>"), N_("get list of sidecars of FILE") },
906         { NULL, "--get-destination:",   gr_get_destination,     TRUE,  FALSE, N_("<FILE>"), N_("get destination path of FILE") },
907         { NULL, "file:",                gr_file_load,           TRUE,  FALSE, N_("<FILE>"), N_("open FILE, bring Geeqie window to the top") },
908         { NULL, "File:",                gr_file_load_no_raise,  TRUE,  FALSE, N_("<FILE>"), N_("open FILE, do not bring Geeqie window to the top") },
909         { NULL, "--tell",               gr_file_tell,           FALSE, FALSE, NULL, N_("print filename of current image") },
910         { NULL, "--pixel-info",         gr_pixel_info,          FALSE, FALSE, NULL, N_("print pixel info of mouse pointer on current image") },
911         { NULL, "--get-rectangle",      gr_rectangle,           FALSE, FALSE, NULL, N_("get rectangle co-ordinates") },
912         { NULL, "view:",                gr_file_view,           TRUE,  FALSE, N_("<FILE>"), N_("open FILE in new window") },
913         { NULL, "--list-clear",         gr_list_clear,          FALSE, FALSE, NULL, N_("clear command line collection list") },
914         { NULL, "--list-add:",          gr_list_add,            TRUE,  FALSE, N_("<FILE>"), N_("add FILE to command line collection list") },
915         { NULL, "raise",                gr_raise,               FALSE, FALSE, NULL, N_("bring the Geeqie window to the top") },
916         { NULL, "--id:",                gr_lw_id,               TRUE, FALSE, N_("<ID>"), N_("window id for following commands") },
917         { NULL, "--new-window",         gr_new_window,          FALSE, FALSE, NULL, N_("new window") },
918         { NULL, "--close-window",       gr_close_window,        FALSE, FALSE, NULL, N_("close window") },
919         { "-ct:", "--cache-thumbs:",    gr_cache_thumb,         TRUE, FALSE, N_("clear|clean"), N_("clear or clean thumbnail cache") },
920         { "-cs:", "--cache-shared:",    gr_cache_shared,        TRUE, FALSE, N_("clear|clean"), N_("clear or clean shared thumbnail cache") },
921         { "-cm","--cache-metadata",      gr_cache_metadata,               FALSE, FALSE, NULL, N_("    clean the metadata cache") },
922         { "-cr:", "--cache-render:",    gr_cache_render,        TRUE, FALSE, N_("<folder>  "), N_(" render thumbnails") },
923         { "-crr:", "--cache-render-recurse:", gr_cache_render_recurse, TRUE, FALSE, N_("<folder> "), N_("render thumbnails recursively") },
924         { "-crs:", "--cache-render-shared:", gr_cache_render_standard, TRUE, FALSE, N_("<folder> "), N_(" render thumbnails (see Help)") },
925         { "-crsr:", "--cache-render-shared-recurse:", gr_cache_render_standard_recurse, TRUE, FALSE, N_("<folder>"), N_(" render thumbnails recursively (see Help)") },
926 #ifdef HAVE_LUA
927         { NULL, "--lua:",               gr_lua,                 TRUE, FALSE, N_("<FILE>,<lua script>"), N_("run lua script on FILE") },
928 #endif
929         { NULL, NULL, NULL, FALSE, FALSE, NULL, NULL }
930 };
931
932 static RemoteCommandEntry *remote_command_find(const gchar *text, const gchar **offset)
933 {
934         gboolean match = FALSE;
935         gint i;
936
937         i = 0;
938         while (!match && remote_commands[i].func != NULL)
939                 {
940                 if (remote_commands[i].needs_extra)
941                         {
942                         if (remote_commands[i].opt_s &&
943                             strncmp(remote_commands[i].opt_s, text, strlen(remote_commands[i].opt_s)) == 0)
944                                 {
945                                 if (offset) *offset = text + strlen(remote_commands[i].opt_s);
946                                 return &remote_commands[i];
947                                 }
948                         else if (remote_commands[i].opt_l &&
949                                  strncmp(remote_commands[i].opt_l, text, strlen(remote_commands[i].opt_l)) == 0)
950                                 {
951                                 if (offset) *offset = text + strlen(remote_commands[i].opt_l);
952                                 return &remote_commands[i];
953                                 }
954                         }
955                 else
956                         {
957                         if ((remote_commands[i].opt_s && strcmp(remote_commands[i].opt_s, text) == 0) ||
958                             (remote_commands[i].opt_l && strcmp(remote_commands[i].opt_l, text) == 0))
959                                 {
960                                 if (offset) *offset = text;
961                                 return &remote_commands[i];
962                                 }
963                         }
964
965                 i++;
966                 }
967
968         return NULL;
969 }
970
971 static void remote_cb(RemoteConnection *rc, const gchar *text, GIOChannel *channel, gpointer data)
972 {
973         RemoteCommandEntry *entry;
974         const gchar *offset;
975
976         entry = remote_command_find(text, &offset);
977         if (entry && entry->func)
978                 {
979                 entry->func(offset, channel, data);
980                 }
981         else
982                 {
983                 log_printf("unknown remote command:%s\n", text);
984                 }
985 }
986
987 void remote_help(void)
988 {
989         gint i;
990         gchar *s_opt_param;
991         gchar *l_opt_param;
992
993         print_term(FALSE, _("Remote command list:\n"));
994
995         i = 0;
996         while (remote_commands[i].func != NULL)
997                 {
998                 if (remote_commands[i].description)
999                         {
1000                         s_opt_param = g_strconcat(remote_commands[i].opt_s, remote_commands[i].parameter, NULL);
1001                         l_opt_param = g_strconcat(remote_commands[i].opt_l, remote_commands[i].parameter, NULL);
1002                         printf_term(FALSE, "  %-11s%-1s %-30s%-s\n",
1003                                     (remote_commands[i].opt_s) ? s_opt_param : "",
1004                                     (remote_commands[i].opt_s && remote_commands[i].opt_l) ? "," : " ",
1005                                     (remote_commands[i].opt_l) ? l_opt_param : "",
1006                                     _(remote_commands[i].description));
1007                         g_free(s_opt_param);
1008                         g_free(l_opt_param);
1009                         }
1010                 i++;
1011                 }
1012         printf_term(FALSE, N_("\n  All other command line parameters are used as plain files if they exists.\n"));
1013 }
1014
1015 GList *remote_build_list(GList *list, gint argc, gchar *argv[], GList **errors)
1016 {
1017         gint i;
1018
1019         i = 1;
1020         while (i < argc)
1021                 {
1022                 RemoteCommandEntry *entry;
1023
1024                 entry = remote_command_find(argv[i], NULL);
1025                 if (entry)
1026                         {
1027                         list = g_list_append(list, argv[i]);
1028                         }
1029                 else if (errors && !isfile(argv[i]))
1030                         {
1031                         *errors = g_list_append(*errors, argv[i]);
1032                         }
1033                 i++;
1034                 }
1035
1036         return list;
1037 }
1038
1039 /**
1040  * \param arg_exec Binary (argv0)
1041  * \param remote_list Evaluated and recognized remote commands
1042  * \param path The current path
1043  * \param cmd_list List of all non collections in Path
1044  * \param collection_list List of all collections in argv
1045  */
1046 void remote_control(const gchar *arg_exec, GList *remote_list, const gchar *path,
1047                     GList *cmd_list, GList *collection_list)
1048 {
1049         RemoteConnection *rc;
1050         gboolean started = FALSE;
1051         gchar *buf;
1052
1053         buf = g_build_filename(get_rc_dir(), ".command", NULL);
1054         rc = remote_client_open(buf);
1055         if (!rc)
1056                 {
1057                 GString *command;
1058                 GList *work;
1059                 gint retry_count = 12;
1060                 gboolean blank = FALSE;
1061
1062                 printf_term(FALSE, _("Remote %s not running, starting..."), GQ_APPNAME);
1063
1064                 command = g_string_new(arg_exec);
1065
1066                 work = remote_list;
1067                 while (work)
1068                         {
1069                         gchar *text;
1070                         RemoteCommandEntry *entry;
1071
1072                         text = work->data;
1073                         work = work->next;
1074
1075                         entry = remote_command_find(text, NULL);
1076                         if (entry)
1077                                 {
1078                                 if (entry->prefer_command_line)
1079                                         {
1080                                         remote_list = g_list_remove(remote_list, text);
1081                                         g_string_append(command, " ");
1082                                         g_string_append(command, text);
1083                                         }
1084                                 if (entry->opt_l && strcmp(entry->opt_l, "file:") == 0)
1085                                         {
1086                                         blank = TRUE;
1087                                         }
1088                                 }
1089                         }
1090
1091                 if (blank || cmd_list || path) g_string_append(command, " --blank");
1092                 if (get_debug_level()) g_string_append(command, " --debug");
1093
1094                 g_string_append(command, " &");
1095                 runcmd(command->str);
1096                 g_string_free(command, TRUE);
1097
1098                 while (!rc && retry_count > 0)
1099                         {
1100                         usleep((retry_count > 10) ? 500000 : 1000000);
1101                         rc = remote_client_open(buf);
1102                         if (!rc) print_term(FALSE, ".");
1103                         retry_count--;
1104                         }
1105
1106                 print_term(FALSE, "\n");
1107
1108                 started = TRUE;
1109                 }
1110         g_free(buf);
1111
1112         if (rc)
1113                 {
1114                 GList *work;
1115                 const gchar *prefix;
1116                 gboolean use_path = TRUE;
1117                 gboolean sent = FALSE;
1118
1119                 work = remote_list;
1120                 while (work)
1121                         {
1122                         gchar *text;
1123                         RemoteCommandEntry *entry;
1124
1125                         text = work->data;
1126                         work = work->next;
1127
1128                         entry = remote_command_find(text, NULL);
1129                         if (entry &&
1130                             entry->opt_l &&
1131                             strcmp(entry->opt_l, "file:") == 0) use_path = FALSE;
1132
1133                         remote_client_send(rc, text);
1134
1135                         sent = TRUE;
1136                         }
1137
1138                 if (cmd_list && cmd_list->next)
1139                         {
1140                         prefix = "--list-add:";
1141                         remote_client_send(rc, "--list-clear");
1142                         }
1143                 else
1144                         {
1145                         prefix = "file:";
1146                         }
1147
1148                 work = cmd_list;
1149                 while (work)
1150                         {
1151                         FileData *fd;
1152                         gchar *text;
1153
1154                         fd = work->data;
1155                         work = work->next;
1156
1157                         text = g_strconcat(prefix, fd->path, NULL);
1158                         remote_client_send(rc, text);
1159                         g_free(text);
1160
1161                         sent = TRUE;
1162                         }
1163
1164                 if (path && !cmd_list && use_path)
1165                         {
1166                         gchar *text;
1167
1168                         text = g_strdup_printf("file:%s", path);
1169                         remote_client_send(rc, text);
1170                         g_free(text);
1171
1172                         sent = TRUE;
1173                         }
1174
1175                 work = collection_list;
1176                 while (work)
1177                         {
1178                         const gchar *name;
1179                         gchar *text;
1180
1181                         name = work->data;
1182                         work = work->next;
1183
1184                         text = g_strdup_printf("file:%s", name);
1185                         remote_client_send(rc, text);
1186                         g_free(text);
1187
1188                         sent = TRUE;
1189                         }
1190
1191                 if (!started && !sent)
1192                         {
1193                         remote_client_send(rc, "raise");
1194                         }
1195                 }
1196         else
1197                 {
1198                 print_term(TRUE, _("Remote not available\n"));
1199                 }
1200
1201         _exit(0);
1202 }
1203
1204 RemoteConnection *remote_server_init(gchar *path, CollectionData *command_collection)
1205 {
1206         RemoteConnection *remote_connection = remote_server_open(path);
1207         RemoteData *remote_data = g_new(RemoteData, 1);
1208
1209         remote_data->command_collection = command_collection;
1210
1211         remote_server_subscribe(remote_connection, remote_cb, remote_data);
1212         return remote_connection;
1213 }
1214 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */