58d4ec31c954ca090e7d47fc528168c83caa7290
[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 (!options->draw_rectangle) return;
701         if (!layout_valid(&lw_id)) return;
702
703         pr = (PixbufRenderer*)lw_id->image->pr;
704
705         if (pr)
706                 {
707                 image_get_rectangle(&x1, &y1, &x2, &y2);
708                 rectangle_info = g_strdup_printf(_("%dx%d+%d+%d"),
709                                         (x2 > x1) ? x2 - x1 : x1 - x2,
710                                         (y2 > y1) ? y2 - y1 : y1 - y2,
711                                         (x2 > x1) ? x1 : x2,
712                                         (y2 > y1) ? y1 : y2);
713
714                 g_io_channel_write_chars(channel, rectangle_info, -1, NULL, NULL);
715                 g_io_channel_write_chars(channel, "\n", -1, NULL, NULL);
716
717                 g_free(rectangle_info);
718                 }
719 }
720
721 static void gr_file_tell(const gchar *text, GIOChannel *channel, gpointer data)
722 {
723         if (!layout_valid(&lw_id)) return;
724
725         if (image_get_path(lw_id->image))
726                 {
727                 g_io_channel_write_chars(channel, image_get_path(lw_id->image), -1, NULL, NULL);
728                 g_io_channel_write_chars(channel, "\n", -1, NULL, NULL);
729                 }
730 }
731
732 static void gr_config_load(const gchar *text, GIOChannel *channel, gpointer data)
733 {
734         gchar *filename = expand_tilde(text);
735
736         if (isfile(filename))
737                 {
738                 load_config_from_file(filename, FALSE);
739                 }
740         else
741                 {
742                 log_printf("remote sent filename that does not exist:\"%s\"\n", filename);
743                 layout_set_path(NULL, homedir());
744                 }
745
746         g_free(filename);
747 }
748
749 static void gr_get_sidecars(const gchar *text, GIOChannel *channel, gpointer data)
750 {
751         gchar *filename = expand_tilde(text);
752         FileData *fd = file_data_new_group(filename);
753
754         GList *work;
755         if (fd->parent) fd = fd->parent;
756
757         g_io_channel_write_chars(channel, fd->path, -1, NULL, NULL);
758         g_io_channel_write_chars(channel, "\n", -1, NULL, NULL);
759
760         work = fd->sidecar_files;
761
762         while (work)
763                 {
764                 fd = work->data;
765                 work = work->next;
766                 g_io_channel_write_chars(channel, fd->path, -1, NULL, NULL);
767                 g_io_channel_write_chars(channel, "\n", -1, NULL, NULL);
768                 }
769         g_free(filename);
770 }
771
772 static void gr_get_destination(const gchar *text, GIOChannel *channel, gpointer data)
773 {
774         gchar *filename = expand_tilde(text);
775         FileData *fd = file_data_new_group(filename);
776
777         if (fd->change && fd->change->dest)
778                 {
779                 g_io_channel_write_chars(channel, fd->change->dest, -1, NULL, NULL);
780                 g_io_channel_write_chars(channel, "\n", -1, NULL, NULL);
781                 }
782         g_free(filename);
783 }
784
785 static void gr_file_view(const gchar *text, GIOChannel *channel, gpointer data)
786 {
787         gchar *filename = expand_tilde(text);
788
789         view_window_new(file_data_new_group(filename));
790         g_free(filename);
791 }
792
793 static void gr_list_clear(const gchar *text, GIOChannel *channel, gpointer data)
794 {
795         RemoteData *remote_data = data;
796
797         if (remote_data->command_collection)
798                 {
799                 collection_unref(remote_data->command_collection);
800                 remote_data->command_collection = NULL;
801                 }
802 }
803
804 static void gr_list_add(const gchar *text, GIOChannel *channel, gpointer data)
805 {
806         RemoteData *remote_data = data;
807         gboolean new = TRUE;
808
809         if (!remote_data->command_collection)
810                 {
811                 CollectionData *cd;
812
813                 cd = collection_new("");
814
815                 g_free(cd->path);
816                 cd->path = NULL;
817                 g_free(cd->name);
818                 cd->name = g_strdup(_("Command line"));
819
820                 remote_data->command_collection = cd;
821                 }
822         else
823                 {
824                 new = (!collection_get_first(remote_data->command_collection));
825                 }
826
827         if (collection_add(remote_data->command_collection, file_data_new_group(text), FALSE) && new)
828                 {
829                 layout_image_set_collection(NULL, remote_data->command_collection,
830                                             collection_get_first(remote_data->command_collection));
831                 }
832 }
833
834 static void gr_raise(const gchar *text, GIOChannel *channel, gpointer data)
835 {
836         LayoutWindow *lw = NULL;
837
838         if (layout_valid(&lw_id))
839                 {
840                 gtk_window_present(GTK_WINDOW(lw_id->window));
841                 }
842 }
843
844 #ifdef HAVE_LUA
845 static void gr_lua(const gchar *text, GIOChannel *channel, gpointer data)
846 {
847         gchar *result = NULL;
848         gchar **lua_command;
849
850         lua_command = g_strsplit(text, ",", 2);
851
852         if (lua_command[0] && lua_command[1])
853                 {
854                 FileData *fd = file_data_new_group(lua_command[0]);
855                 result = g_strdup(lua_callvalue(fd, lua_command[1], NULL));
856                 if (result)
857                         {
858                         g_io_channel_write_chars(channel, result, -1, NULL, NULL);
859                         }
860                 else
861                         {
862                         g_io_channel_write_chars(channel, N_("lua error: no data"), -1, NULL, NULL);
863                         }
864                 }
865         else
866                 {
867                 g_io_channel_write_chars(channel, N_("lua error: no data"), -1, NULL, NULL);
868                 }
869
870         g_io_channel_write_chars(channel, "\n", -1, NULL, NULL);
871
872         g_strfreev(lua_command);
873         g_free(result);
874 }
875 #endif
876
877 typedef struct _RemoteCommandEntry RemoteCommandEntry;
878 struct _RemoteCommandEntry {
879         gchar *opt_s;
880         gchar *opt_l;
881         void (*func)(const gchar *text, GIOChannel *channel, gpointer data);
882         gboolean needs_extra;
883         gboolean prefer_command_line;
884         gchar *parameter;
885         gchar *description;
886 };
887
888 static RemoteCommandEntry remote_commands[] = {
889         /* short, long                  callback,               extra, prefer, parameter, description */
890         { "-n", "--next",               gr_image_next,          FALSE, FALSE, NULL, N_("next image") },
891         { "-b", "--back",               gr_image_prev,          FALSE, FALSE, NULL, N_("previous image") },
892         { NULL, "--first",              gr_image_first,         FALSE, FALSE, NULL, N_("first image") },
893         { NULL, "--last",               gr_image_last,          FALSE, FALSE, NULL, N_("last image") },
894         { "-f", "--fullscreen",         gr_fullscreen_toggle,   FALSE, TRUE,  NULL, N_("toggle full screen") },
895         { "-fs","--fullscreen-start",   gr_fullscreen_start,    FALSE, FALSE, NULL, N_("start full screen") },
896         { "-fS","--fullscreen-stop",    gr_fullscreen_stop,     FALSE, FALSE, NULL, N_("stop full screen") },
897         { "-s", "--slideshow",          gr_slideshow_toggle,    FALSE, TRUE,  NULL, N_("toggle slide show") },
898         { "-ss","--slideshow-start",    gr_slideshow_start,     FALSE, FALSE, NULL, N_("start slide show") },
899         { "-sS","--slideshow-stop",     gr_slideshow_stop,      FALSE, FALSE, NULL, N_("stop slide show") },
900         { NULL, "--slideshow-recurse:", gr_slideshow_start_rec, TRUE,  FALSE, N_("<FOLDER>"), N_("start recursive slide show in FOLDER") },
901         { "-d", "--delay=",             gr_slideshow_delay,     TRUE,  FALSE, N_("<[H:][M:][N][.M]>"), N_("set slide show delay to Hrs Mins N.M seconds") },
902         { "+t", "--tools-show",         gr_tools_show,          FALSE, TRUE,  NULL, N_("show tools") },
903         { "-t", "--tools-hide",         gr_tools_hide,          FALSE, TRUE,  NULL, N_("hide tools") },
904         { "-q", "--quit",               gr_quit,                FALSE, FALSE, NULL, N_("quit") },
905         { NULL, "--config-load:",       gr_config_load,         TRUE,  FALSE, N_("<FILE>"), N_("load configuration from FILE") },
906         { NULL, "--get-sidecars:",      gr_get_sidecars,        TRUE,  FALSE, N_("<FILE>"), N_("get list of sidecars of FILE") },
907         { NULL, "--get-destination:",   gr_get_destination,     TRUE,  FALSE, N_("<FILE>"), N_("get destination path of FILE") },
908         { NULL, "file:",                gr_file_load,           TRUE,  FALSE, N_("<FILE>"), N_("open FILE, bring Geeqie window to the top") },
909         { NULL, "File:",                gr_file_load_no_raise,  TRUE,  FALSE, N_("<FILE>"), N_("open FILE, do not bring Geeqie window to the top") },
910         { NULL, "--tell",               gr_file_tell,           FALSE, FALSE, NULL, N_("print filename of current image") },
911         { NULL, "--pixel-info",         gr_pixel_info,          FALSE, FALSE, NULL, N_("print pixel info of mouse pointer on current image") },
912         { NULL, "--get-rectangle",      gr_rectangle,           FALSE, FALSE, NULL, N_("get rectangle co-ordinates") },
913         { NULL, "view:",                gr_file_view,           TRUE,  FALSE, N_("<FILE>"), N_("open FILE in new window") },
914         { NULL, "--list-clear",         gr_list_clear,          FALSE, FALSE, NULL, N_("clear command line collection list") },
915         { NULL, "--list-add:",          gr_list_add,            TRUE,  FALSE, N_("<FILE>"), N_("add FILE to command line collection list") },
916         { NULL, "raise",                gr_raise,               FALSE, FALSE, NULL, N_("bring the Geeqie window to the top") },
917         { NULL, "--id:",                gr_lw_id,               TRUE, FALSE, N_("<ID>"), N_("window id for following commands") },
918         { NULL, "--new-window",         gr_new_window,          FALSE, FALSE, NULL, N_("new window") },
919         { NULL, "--close-window",       gr_close_window,        FALSE, FALSE, NULL, N_("close window") },
920         { "-ct:", "--cache-thumbs:",    gr_cache_thumb,         TRUE, FALSE, N_("clear|clean"), N_("clear or clean thumbnail cache") },
921         { "-cs:", "--cache-shared:",    gr_cache_shared,        TRUE, FALSE, N_("clear|clean"), N_("clear or clean shared thumbnail cache") },
922         { "-cm","--cache-metadata",      gr_cache_metadata,               FALSE, FALSE, NULL, N_("    clean the metadata cache") },
923         { "-cr:", "--cache-render:",    gr_cache_render,        TRUE, FALSE, N_("<folder>  "), N_(" render thumbnails") },
924         { "-crr:", "--cache-render-recurse:", gr_cache_render_recurse, TRUE, FALSE, N_("<folder> "), N_("render thumbnails recursively") },
925         { "-crs:", "--cache-render-shared:", gr_cache_render_standard, TRUE, FALSE, N_("<folder> "), N_(" render thumbnails (see Help)") },
926         { "-crsr:", "--cache-render-shared-recurse:", gr_cache_render_standard_recurse, TRUE, FALSE, N_("<folder>"), N_(" render thumbnails recursively (see Help)") },
927 #ifdef HAVE_LUA
928         { NULL, "--lua:",               gr_lua,                 TRUE, FALSE, N_("<FILE>,<lua script>"), N_("run lua script on FILE") },
929 #endif
930         { NULL, NULL, NULL, FALSE, FALSE, NULL, NULL }
931 };
932
933 static RemoteCommandEntry *remote_command_find(const gchar *text, const gchar **offset)
934 {
935         gboolean match = FALSE;
936         gint i;
937
938         i = 0;
939         while (!match && remote_commands[i].func != NULL)
940                 {
941                 if (remote_commands[i].needs_extra)
942                         {
943                         if (remote_commands[i].opt_s &&
944                             strncmp(remote_commands[i].opt_s, text, strlen(remote_commands[i].opt_s)) == 0)
945                                 {
946                                 if (offset) *offset = text + strlen(remote_commands[i].opt_s);
947                                 return &remote_commands[i];
948                                 }
949                         else if (remote_commands[i].opt_l &&
950                                  strncmp(remote_commands[i].opt_l, text, strlen(remote_commands[i].opt_l)) == 0)
951                                 {
952                                 if (offset) *offset = text + strlen(remote_commands[i].opt_l);
953                                 return &remote_commands[i];
954                                 }
955                         }
956                 else
957                         {
958                         if ((remote_commands[i].opt_s && strcmp(remote_commands[i].opt_s, text) == 0) ||
959                             (remote_commands[i].opt_l && strcmp(remote_commands[i].opt_l, text) == 0))
960                                 {
961                                 if (offset) *offset = text;
962                                 return &remote_commands[i];
963                                 }
964                         }
965
966                 i++;
967                 }
968
969         return NULL;
970 }
971
972 static void remote_cb(RemoteConnection *rc, const gchar *text, GIOChannel *channel, gpointer data)
973 {
974         RemoteCommandEntry *entry;
975         const gchar *offset;
976
977         entry = remote_command_find(text, &offset);
978         if (entry && entry->func)
979                 {
980                 entry->func(offset, channel, data);
981                 }
982         else
983                 {
984                 log_printf("unknown remote command:%s\n", text);
985                 }
986 }
987
988 void remote_help(void)
989 {
990         gint i;
991         gchar *s_opt_param;
992         gchar *l_opt_param;
993
994         print_term(FALSE, _("Remote command list:\n"));
995
996         i = 0;
997         while (remote_commands[i].func != NULL)
998                 {
999                 if (remote_commands[i].description)
1000                         {
1001                         s_opt_param = g_strconcat(remote_commands[i].opt_s, remote_commands[i].parameter, NULL);
1002                         l_opt_param = g_strconcat(remote_commands[i].opt_l, remote_commands[i].parameter, NULL);
1003                         printf_term(FALSE, "  %-11s%-1s %-30s%-s\n",
1004                                     (remote_commands[i].opt_s) ? s_opt_param : "",
1005                                     (remote_commands[i].opt_s && remote_commands[i].opt_l) ? "," : " ",
1006                                     (remote_commands[i].opt_l) ? l_opt_param : "",
1007                                     _(remote_commands[i].description));
1008                         g_free(s_opt_param);
1009                         g_free(l_opt_param);
1010                         }
1011                 i++;
1012                 }
1013         printf_term(FALSE, N_("\n  All other command line parameters are used as plain files if they exists.\n"));
1014 }
1015
1016 GList *remote_build_list(GList *list, gint argc, gchar *argv[], GList **errors)
1017 {
1018         gint i;
1019
1020         i = 1;
1021         while (i < argc)
1022                 {
1023                 RemoteCommandEntry *entry;
1024
1025                 entry = remote_command_find(argv[i], NULL);
1026                 if (entry)
1027                         {
1028                         list = g_list_append(list, argv[i]);
1029                         }
1030                 else if (errors && !isfile(argv[i]))
1031                         {
1032                         *errors = g_list_append(*errors, argv[i]);
1033                         }
1034                 i++;
1035                 }
1036
1037         return list;
1038 }
1039
1040 /**
1041  * \param arg_exec Binary (argv0)
1042  * \param remote_list Evaluated and recognized remote commands
1043  * \param path The current path
1044  * \param cmd_list List of all non collections in Path
1045  * \param collection_list List of all collections in argv
1046  */
1047 void remote_control(const gchar *arg_exec, GList *remote_list, const gchar *path,
1048                     GList *cmd_list, GList *collection_list)
1049 {
1050         RemoteConnection *rc;
1051         gboolean started = FALSE;
1052         gchar *buf;
1053
1054         buf = g_build_filename(get_rc_dir(), ".command", NULL);
1055         rc = remote_client_open(buf);
1056         if (!rc)
1057                 {
1058                 GString *command;
1059                 GList *work;
1060                 gint retry_count = 12;
1061                 gboolean blank = FALSE;
1062
1063                 printf_term(FALSE, _("Remote %s not running, starting..."), GQ_APPNAME);
1064
1065                 command = g_string_new(arg_exec);
1066
1067                 work = remote_list;
1068                 while (work)
1069                         {
1070                         gchar *text;
1071                         RemoteCommandEntry *entry;
1072
1073                         text = work->data;
1074                         work = work->next;
1075
1076                         entry = remote_command_find(text, NULL);
1077                         if (entry)
1078                                 {
1079                                 if (entry->prefer_command_line)
1080                                         {
1081                                         remote_list = g_list_remove(remote_list, text);
1082                                         g_string_append(command, " ");
1083                                         g_string_append(command, text);
1084                                         }
1085                                 if (entry->opt_l && strcmp(entry->opt_l, "file:") == 0)
1086                                         {
1087                                         blank = TRUE;
1088                                         }
1089                                 }
1090                         }
1091
1092                 if (blank || cmd_list || path) g_string_append(command, " --blank");
1093                 if (get_debug_level()) g_string_append(command, " --debug");
1094
1095                 g_string_append(command, " &");
1096                 runcmd(command->str);
1097                 g_string_free(command, TRUE);
1098
1099                 while (!rc && retry_count > 0)
1100                         {
1101                         usleep((retry_count > 10) ? 500000 : 1000000);
1102                         rc = remote_client_open(buf);
1103                         if (!rc) print_term(FALSE, ".");
1104                         retry_count--;
1105                         }
1106
1107                 print_term(FALSE, "\n");
1108
1109                 started = TRUE;
1110                 }
1111         g_free(buf);
1112
1113         if (rc)
1114                 {
1115                 GList *work;
1116                 const gchar *prefix;
1117                 gboolean use_path = TRUE;
1118                 gboolean sent = FALSE;
1119
1120                 work = remote_list;
1121                 while (work)
1122                         {
1123                         gchar *text;
1124                         RemoteCommandEntry *entry;
1125
1126                         text = work->data;
1127                         work = work->next;
1128
1129                         entry = remote_command_find(text, NULL);
1130                         if (entry &&
1131                             entry->opt_l &&
1132                             strcmp(entry->opt_l, "file:") == 0) use_path = FALSE;
1133
1134                         remote_client_send(rc, text);
1135
1136                         sent = TRUE;
1137                         }
1138
1139                 if (cmd_list && cmd_list->next)
1140                         {
1141                         prefix = "--list-add:";
1142                         remote_client_send(rc, "--list-clear");
1143                         }
1144                 else
1145                         {
1146                         prefix = "file:";
1147                         }
1148
1149                 work = cmd_list;
1150                 while (work)
1151                         {
1152                         FileData *fd;
1153                         gchar *text;
1154
1155                         fd = work->data;
1156                         work = work->next;
1157
1158                         text = g_strconcat(prefix, fd->path, NULL);
1159                         remote_client_send(rc, text);
1160                         g_free(text);
1161
1162                         sent = TRUE;
1163                         }
1164
1165                 if (path && !cmd_list && use_path)
1166                         {
1167                         gchar *text;
1168
1169                         text = g_strdup_printf("file:%s", path);
1170                         remote_client_send(rc, text);
1171                         g_free(text);
1172
1173                         sent = TRUE;
1174                         }
1175
1176                 work = collection_list;
1177                 while (work)
1178                         {
1179                         const gchar *name;
1180                         gchar *text;
1181
1182                         name = work->data;
1183                         work = work->next;
1184
1185                         text = g_strdup_printf("file:%s", name);
1186                         remote_client_send(rc, text);
1187                         g_free(text);
1188
1189                         sent = TRUE;
1190                         }
1191
1192                 if (!started && !sent)
1193                         {
1194                         remote_client_send(rc, "raise");
1195                         }
1196                 }
1197         else
1198                 {
1199                 print_term(TRUE, _("Remote not available\n"));
1200                 }
1201
1202         _exit(0);
1203 }
1204
1205 RemoteConnection *remote_server_init(gchar *path, CollectionData *command_collection)
1206 {
1207         RemoteConnection *remote_connection = remote_server_open(path);
1208         RemoteData *remote_data = g_new(RemoteData, 1);
1209
1210         remote_data->command_collection = command_collection;
1211
1212         remote_server_subscribe(remote_connection, remote_cb, remote_data);
1213         return remote_connection;
1214 }
1215 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */