r4654: Allow pinboard and panel icons to be locked (Dennis Tomas, suggested by
[rox-filer.git] / ROX-Filer / src / remote.c
blob1f06457d8b964e7269afeac2e1986d21115d1703
1 /*
2 * ROX-Filer, filer for the ROX desktop project
3 * Copyright (C) 2006, Thomas Leonard and others (see changelog for details).
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
17 * Place, Suite 330, Boston, MA 02111-1307 USA
20 /* This code is used to communicate between two copies of the filer:
21 * If the filer is run and the same version of the filer is already
22 * running on the same machine then the new copy simply asks the old
23 * one deal with it and quits.
26 #include "config.h"
28 #include <string.h>
30 #include <gdk/gdkx.h>
31 #include <X11/X.h>
32 #include <X11/Xatom.h>
33 #include <gtk/gtk.h>
34 #include <gtk/gtkinvisible.h>
35 #include <libxml/parser.h>
37 #include "global.h"
39 #include "main.h"
40 #include "support.h"
41 #include "gui_support.h"
42 #include "run.h"
43 #include "remote.h"
44 #include "filer.h"
45 #include "pinboard.h"
46 #include "panel.h"
47 #include "action.h"
48 #include "type.h"
49 #include "display.h"
50 #include "xml.h"
51 #include "diritem.h"
52 #include "usericons.h"
54 static GdkAtom filer_atom; /* _ROX_FILER_EUID_VERSION_HOST */
55 static GdkAtom filer_atom_any; /* _ROX_FILER_EUID_HOST */
56 static GdkAtom xsoap; /* _XSOAP */
58 typedef struct _SOAP_call SOAP_call;
59 typedef xmlNodePtr (*SOAP_func)(GList *args);
61 struct _SOAP_call {
62 SOAP_func func;
63 gchar **required_args;
64 gchar **optional_args;
67 static GHashTable *rpc_calls = NULL; /* MethodName -> Function */
69 /* Static prototypes */
70 static GdkWindow *get_existing_ipc_window(void);
71 static gboolean get_ipc_property(GdkWindow *window, Window *r_xid);
72 static void soap_send(GtkWidget *from, GdkAtom prop, GdkWindow *dest);
73 static gboolean client_event(GtkWidget *window,
74 GdkEventClient *event,
75 gpointer data);
76 static void soap_done(GtkWidget *widget,
77 GdkEventProperty *event,
78 gpointer data);
79 static void soap_register(char *name, SOAP_func func, char *req, char *opt);
80 static xmlNodePtr soap_invoke(xmlNode *method);
82 static xmlNodePtr rpc_Version(GList *args);
83 static xmlNodePtr rpc_OpenDir(GList *args);
84 static xmlNodePtr rpc_CloseDir(GList *args);
85 static xmlNodePtr rpc_Examine(GList *args);
86 static xmlNodePtr rpc_Show(GList *args);
87 static xmlNodePtr rpc_Pinboard(GList *args);
88 static xmlNodePtr rpc_Panel(GList *args);
89 static xmlNodePtr rpc_Run(GList *args);
90 static xmlNodePtr rpc_Copy(GList *args);
91 static xmlNodePtr rpc_Move(GList *args);
92 static xmlNodePtr rpc_Link(GList *args);
93 static xmlNodePtr rpc_FileType(GList *args);
94 static xmlNodePtr rpc_Mount(GList *args);
95 static xmlNodePtr rpc_Unmount(GList *args);
97 static xmlNodePtr rpc_PanelAdd(GList *args);
98 static xmlNodePtr rpc_PanelRemove(GList *args);
99 static xmlNodePtr rpc_PinboardAdd(GList *args);
100 static xmlNodePtr rpc_PinboardRemove(GList *args);
101 static xmlNodePtr rpc_SetBackdrop(GList *args);
102 static xmlNodePtr rpc_SetBackdropApp(GList *args);
104 static xmlNodePtr rpc_SetIcon(GList *args);
105 static xmlNodePtr rpc_UnsetIcon(GList *args);
107 /****************************************************************
108 * EXTERNAL INTERFACE *
109 ****************************************************************/
112 /* Try to get an already-running filer to handle things (only if
113 * new_copy is FALSE); TRUE if we succeed.
114 * Create an IPC widget so that future filers can contact us.
116 gboolean remote_init(xmlDocPtr rpc, gboolean new_copy)
118 guchar *unique_id;
119 GdkWindow *existing_ipc_window;
120 GtkWidget *ipc_window;
121 Window xwindow;
123 /* xmlDocDump(stdout, rpc); */
125 rpc_calls = g_hash_table_new(g_str_hash, g_str_equal);
127 soap_register("Version", rpc_Version, NULL, NULL);
129 soap_register("Run", rpc_Run, "Filename", NULL);
130 soap_register("OpenDir", rpc_OpenDir, "Filename",
131 "Style,Details,Sort,Class,ID,Hidden,Filter");
132 soap_register("CloseDir", rpc_CloseDir, "Filename", NULL);
133 soap_register("Examine", rpc_Examine, "Filename", NULL);
134 soap_register("Show", rpc_Show, "Directory,Leafname", NULL);
136 soap_register("Pinboard", rpc_Pinboard, NULL, "Name");
137 soap_register("Panel", rpc_Panel, NULL, "Side,Name");
139 soap_register("FileType", rpc_FileType, "Filename", NULL);
141 soap_register("Copy", rpc_Copy, "From,To", "Leafname,Quiet");
142 soap_register("Move", rpc_Move, "From,To", "Leafname,Quiet");
143 soap_register("Link", rpc_Link, "From,To", "Leafname");
144 soap_register("Mount", rpc_Mount, "MountPoints", "OpenDir,Quiet");
145 soap_register("Unmount", rpc_Unmount, "MountPoints", "Quiet");
147 soap_register("SetBackdrop", rpc_SetBackdrop, "Filename,Style", NULL);
148 soap_register("SetBackdropApp", rpc_SetBackdropApp, "App", NULL);
149 soap_register("PinboardAdd", rpc_PinboardAdd, "Path,X,Y", "Label,Shortcut,Args,Locked");
150 soap_register("PinboardRemove", rpc_PinboardRemove, "Path", "Label");
151 soap_register("PanelAdd", rpc_PanelAdd, "Side,Path", "Label,After,Shortcut,Args,Locked");
152 soap_register("PanelRemove", rpc_PanelRemove, "Side", "Path,Label");
153 soap_register("SetIcon", rpc_SetIcon, "Path,Icon", NULL);
154 soap_register("UnsetIcon", rpc_UnsetIcon, "Path", NULL);
156 /* Look for a property on the root window giving the IPC window
157 * of an already-running copy of this version of the filer, running
158 * on the same machine and with the same euid.
160 unique_id = g_strdup_printf("_ROX_FILER_%d_%s_%s",
161 (int) euid, VERSION, our_host_name());
162 filer_atom = gdk_atom_intern(unique_id, FALSE);
163 g_free(unique_id);
165 xsoap = gdk_atom_intern("_XSOAP", FALSE);
167 /* If we find a running copy, we'll need a window to put the
168 * SOAP message in before sending.
169 * If there's no running copy, we'll need a window to receive
170 * future SOAP message events.
171 * Either way, we'll need a window for it...
173 ipc_window = gtk_invisible_new();
174 gtk_widget_realize(ipc_window);
176 XGrabServer(GDK_DISPLAY());
178 existing_ipc_window = new_copy ? NULL : get_existing_ipc_window();
179 if (existing_ipc_window)
181 xmlChar *mem;
182 int size;
184 XUngrabServer(GDK_DISPLAY());
186 xmlDocDumpMemory(rpc, &mem, &size);
187 g_return_val_if_fail(size > 0, FALSE);
189 /* Since Gtk might have selected this already, we'd
190 * better do it BEFORE changing the property.
192 gtk_widget_add_events(ipc_window, GDK_PROPERTY_CHANGE_MASK);
193 g_signal_connect(ipc_window, "property-notify-event",
194 G_CALLBACK(soap_done), GINT_TO_POINTER(xsoap));
196 gdk_property_change(ipc_window->window, xsoap,
197 gdk_x11_xatom_to_atom(XA_STRING), 8,
198 GDK_PROP_MODE_REPLACE, mem, size);
199 g_free(mem);
201 soap_send(ipc_window, xsoap, existing_ipc_window);
203 return TRUE;
206 xwindow = GDK_WINDOW_XWINDOW(ipc_window->window);
208 /* Make the IPC window contain a property pointing to
209 * itself - this can then be used to check that it really
210 * is an IPC window.
212 gdk_property_change(ipc_window->window, filer_atom,
213 gdk_x11_xatom_to_atom(XA_WINDOW), 32,
214 GDK_PROP_MODE_REPLACE,
215 (void *) &xwindow, 1);
217 /* Get notified when we get a message */
218 g_signal_connect(ipc_window, "client-event",
219 G_CALLBACK(client_event), NULL);
221 /* Make the root window contain a pointer to the IPC window */
222 gdk_property_change(gdk_get_default_root_window(), filer_atom,
223 gdk_x11_xatom_to_atom(XA_WINDOW), 32,
224 GDK_PROP_MODE_REPLACE,
225 (void *) &xwindow, 1);
227 XUngrabServer(GDK_DISPLAY());
229 /* Also have a property without the version number, for programs
230 * that are happy to talk to any version of the filer.
232 unique_id = g_strdup_printf("_ROX_FILER_%d_%s",
233 (int) euid, our_host_name());
234 filer_atom_any = gdk_atom_intern(unique_id, FALSE);
235 g_free(unique_id);
236 /* On the IPC window... */
237 gdk_property_change(ipc_window->window, filer_atom_any,
238 gdk_x11_xatom_to_atom(XA_WINDOW), 32,
239 GDK_PROP_MODE_REPLACE,
240 (void *) &xwindow, 1);
241 /* ... and on the root */
242 gdk_property_change(gdk_get_default_root_window(), filer_atom_any,
243 gdk_x11_xatom_to_atom(XA_WINDOW), 32,
244 GDK_PROP_MODE_REPLACE,
245 (void *) &xwindow, 1);
247 return FALSE;
250 /* Executes the RPC call(s) in the given SOAP message and returns
251 * the reply.
253 xmlDocPtr run_soap(xmlDocPtr soap)
255 xmlNodePtr body, node, rep_body, reply;
256 xmlDocPtr rep_doc = NULL;
258 g_return_val_if_fail(soap != NULL, NULL);
260 /* Make sure we don't quit before doing the whole list
261 * (there's a command that closes windows)
263 number_of_windows++;
265 node = xmlDocGetRootElement(soap);
266 if (!node->ns)
267 goto bad_soap;
269 if (strcmp(node->ns->href, SOAP_ENV_NS) != 0 &&
270 strcmp(node->ns->href, SOAP_ENV_NS_OLD) != 0)
271 goto bad_soap;
273 body = get_subnode(node, SOAP_ENV_NS, "Body");
274 if (!body)
275 body = get_subnode(node, SOAP_ENV_NS_OLD, "Body");
276 if (!body)
277 goto bad_soap;
279 for (node = body->xmlChildrenNode; node; node = node->next)
281 if (node->type != XML_ELEMENT_NODE)
282 continue;
284 if (node->ns == NULL || strcmp(node->ns->href, ROX_NS) != 0)
286 g_warning("Unknown namespace %s",
287 node->ns ? node->ns->href
288 : (xmlChar *) "(none)");
289 continue;
292 reply = soap_invoke(node);
294 if (reply)
296 if (!rep_doc)
297 rep_doc = soap_new(&rep_body);
298 xmlAddChild(rep_body, reply);
302 goto out;
304 bad_soap:
305 g_warning("Bad SOAP message received!");
307 out:
308 number_of_windows--;
310 return rep_doc;
314 /****************************************************************
315 * INTERNAL FUNCTIONS *
316 ****************************************************************/
318 /* Register this function to handle SOAP calls to method 'name'.
319 * 'req' and 'opt' are comma separated lists of argument names, in the order
320 * that they are to be delivered to the function.
321 * NULL will be passed for an opt argument if not given.
322 * Otherwise, the parameter is the xmlNode from the call.
324 static void soap_register(char *name, SOAP_func func, char *req, char *opt)
326 SOAP_call *call;
328 call = g_new(SOAP_call, 1);
329 call->func = func;
330 call->required_args = req ? g_strsplit(req, ",", 0) : NULL;
331 call->optional_args = opt ? g_strsplit(opt, ",", 0) : NULL;
333 g_hash_table_insert(rpc_calls, g_strdup(name), call);
336 /* Get the remote IPC window of the already-running filer if there
337 * is one.
339 static GdkWindow *get_existing_ipc_window(void)
341 Window xid, xid_confirm;
342 GdkWindow *window;
344 if (!get_ipc_property(gdk_get_default_root_window(), &xid))
345 return NULL;
347 if (gdk_window_lookup(xid))
348 return NULL; /* Stale handle which we now own */
350 window = gdk_window_foreign_new(xid);
351 if (!window)
352 return NULL;
354 if (!get_ipc_property(window, &xid_confirm) || xid_confirm != xid)
355 return NULL;
357 return window;
360 /* Returns the 'rox_atom' property of 'window' */
361 static gboolean get_ipc_property(GdkWindow *window, Window *r_xid)
363 guchar *data;
364 gint format, length;
365 gboolean retval = FALSE;
367 if (gdk_property_get(window, filer_atom,
368 gdk_x11_xatom_to_atom(XA_WINDOW), 0, 4,
369 FALSE, NULL, &format, &length, &data) && data)
371 /* Note: values with format=32 are stored as longs client-side,
372 * which may be more than 32 bits on some systems.
374 if (format == 32 && length == sizeof(long))
376 long windowID = *((long *) data);
377 retval = TRUE;
378 *r_xid = (Window) windowID;
380 g_free(data);
383 return retval;
386 static char *read_property(GdkWindow *window, GdkAtom prop, gint *out_length)
388 gint grab_len = 4096;
389 gint length;
390 guchar *data;
391 guchar *retval = NULL;
393 gdk_error_trap_push();
395 while (!retval)
397 if (!(gdk_property_get(window, prop,
398 gdk_x11_xatom_to_atom(XA_STRING), 0, grab_len,
399 FALSE, NULL, NULL,
400 &length, &data) && data))
401 goto out; /* Error? */
403 if (length >= grab_len)
405 /* Didn't get all of it - try again */
406 grab_len <<= 1;
407 g_free(data);
408 continue;
411 data = g_realloc(data, length + 1);
412 data[length] = '\0'; /* libxml seems to need this */
413 *out_length = length;
415 retval = data;
417 out:
419 if (gdk_error_trap_pop() == Success)
420 return retval;
422 g_warning("Error reading %s property!", gdk_atom_name(prop));
424 g_free(retval);
425 return NULL;
428 static gboolean client_event(GtkWidget *window,
429 GdkEventClient *event,
430 gpointer user_data)
432 GdkWindow *src_window;
433 GdkAtom prop;
434 xmlDocPtr reply;
435 guchar *data;
436 gint length;
437 xmlDocPtr doc;
439 if (event->message_type != xsoap)
440 return FALSE;
442 src_window = gdk_window_foreign_new(event->data.l[0]);
443 if (!src_window)
445 g_warning("SOAP message sender window was destroyed before I \n"
446 "could read it.");
447 return FALSE;
449 prop = gdk_x11_xatom_to_atom(event->data.l[1]);
451 data = read_property(src_window, prop, &length);
452 if (!data)
453 return TRUE;
455 doc = xmlParseMemory(g_strndup(data, length), length);
456 g_free(data);
458 reply = run_soap(doc);
459 if (number_of_windows == 0)
460 gtk_main_quit();
462 xmlFreeDoc(doc);
464 if (reply)
466 /* Send reply back... */
467 xmlChar *mem;
468 int size;
470 xmlDocDumpMemory(reply, &mem, &size);
471 g_return_val_if_fail(size > 0, TRUE);
473 gdk_error_trap_push();
474 gdk_property_change(src_window, prop,
475 gdk_x11_xatom_to_atom(XA_STRING), 8,
476 GDK_PROP_MODE_REPLACE, mem, size);
477 g_free(mem);
478 gdk_flush();
479 if (gdk_error_trap_pop() != Success)
480 g_warning("Failed to send SOAP reply!");
482 else
484 gdk_error_trap_push();
485 gdk_property_delete(src_window, prop);
486 gdk_flush();
487 if (gdk_error_trap_pop() != Success)
488 g_warning("Failed to send SOAP reply!");
491 return TRUE;
494 /* Some handy functions for processing SOAP RPC arguments... */
496 /* Returns TRUE, FALSE, or -1 (if argument is unspecified) */
497 static int bool_value(xmlNode *arg)
499 int answer;
500 char *optval;
502 if (!arg)
503 return -1;
505 optval = xmlNodeGetContent(arg);
506 answer = text_to_boolean(optval, -1); /* XXX: Report error? */
507 g_free(optval);
509 return answer;
512 /* Returns the text of this arg as a string, or NULL if the (optional)
513 * argument wasn't supplied. Returns "" if the arg is empty.
514 * g_free() the result.
516 static char *string_value(xmlNode *arg)
518 char *retval;
520 if (!arg)
521 return NULL;
523 retval = xmlNodeGetContent(arg);
525 return retval ? retval : g_strdup("");
528 /* Returns the text of this arg as an int, or the default value if not
529 * supplied or not an int.
531 static int int_value(xmlNode *arg, int def)
533 char *str, *end;
534 int i;
536 if (!arg)
537 return def;
539 str = xmlNodeGetContent(arg);
540 if (!str || !str[0])
541 return def;
543 i = (int) strtol(str, &end, 0);
545 return (end > str) ? i : def;
548 /* Return a list of strings, one for each child node of arg.
549 * g_list_free the list, and g_free each string.
551 static GList *list_value(xmlNode *arg)
553 GList *list = NULL;
554 xmlNode *node;
556 for (node = arg->xmlChildrenNode; node; node = node->next)
558 if (node->type != XML_ELEMENT_NODE)
559 continue;
561 list = g_list_append(list, string_value(node));
564 return list;
567 #define ARG(n) ((xmlNode *) g_list_nth(args, n)->data)
569 /* The RPC handlers all work in the same way -- they get passed a list of
570 * xmlNode arguments from the RPC call and they return the result node, or
571 * NULL if there isn't a result.
574 static xmlNodePtr rpc_SetIcon(GList *args)
576 char *path, *icon;
578 path = string_value(ARG(0));
579 icon = string_value(ARG(1));
581 add_globicon(path,icon);
583 g_free(path);
584 g_free(icon);
586 return NULL;
589 static xmlNodePtr rpc_UnsetIcon(GList *args)
591 char *path;
593 path = string_value(ARG(0));
595 delete_globicon(path);
597 g_free(path);
599 return NULL;
602 static xmlNodePtr rpc_Version(GList *args)
604 xmlNodePtr reply;
606 reply = xmlNewNode(NULL, "rox:VersionResponse");
607 xmlNewNs(reply, SOAP_RPC_NS, "soap");
608 xmlNewTextChild(reply, NULL, "soap:result", VERSION);
610 return reply;
613 /* Args: Path, [Style, Details, Sort, Class, Window] */
614 static xmlNodePtr rpc_OpenDir(GList *args)
616 char *path;
617 char *style, *details, *sort, *class, *window;
618 int hidden=FALSE;
619 char *filter_string=NULL;
620 FilerWindow *fwin = NULL;
622 path = string_value(ARG(0));
623 class = string_value(ARG(4));
624 window = string_value(ARG(5));
626 if (window)
627 fwin = filer_get_by_id(window);
629 if (!fwin)
631 fwin = filer_opendir(path, NULL, class);
632 if (window)
633 filer_set_id(fwin, window);
635 else
636 filer_change_to(fwin, path, NULL);
638 g_free(path);
639 g_free(class);
640 g_free(window);
641 if (!fwin)
642 return NULL;
644 style = string_value(ARG(1));
645 details = string_value(ARG(2));
646 sort = string_value(ARG(3));
647 hidden = bool_value(ARG(6));
648 filter_string = string_value(ARG(7));
650 if (style)
652 DisplayStyle ds;
654 ds = !g_strcasecmp(style, "Large") ? LARGE_ICONS :
655 !g_strcasecmp(style, "Small") ? SMALL_ICONS :
656 !g_strcasecmp(style, "Huge") ? HUGE_ICONS :
657 !g_strcasecmp(style, "Automatic") ? AUTO_SIZE_ICONS :
658 UNKNOWN_STYLE;
659 if (ds == UNKNOWN_STYLE)
660 delayed_error(_("Unknown style '%s'"), style);
661 else
662 display_set_layout(fwin, ds, fwin->details_type, TRUE);
664 g_free(style);
667 if (details)
669 DetailsType dt;
670 ViewType view_type;
672 dt = !g_strcasecmp(details, "None") ? DETAILS_NONE :
673 !g_strcasecmp(details, "ListView") ? DETAILS_NONE :
674 !g_strcasecmp(details, "Size") ? DETAILS_SIZE :
675 !g_strcasecmp(details, "Type") ? DETAILS_TYPE :
676 !g_strcasecmp(details, "Times") ? DETAILS_TIMES :
677 !g_strcasecmp(details, "Permissions")
678 ? DETAILS_PERMISSIONS :
679 DETAILS_UNKNOWN;
681 if (dt == DETAILS_UNKNOWN)
682 delayed_error(_("Unknown details type '%s'"), details);
683 else
684 display_set_layout(fwin, fwin->display_style_wanted,
685 dt, TRUE);
687 if (g_strcasecmp(details, "ListView") == 0)
688 view_type = VIEW_TYPE_DETAILS;
689 else
690 view_type = VIEW_TYPE_COLLECTION;
692 if (view_type != fwin->view_type)
693 filer_set_view_type(fwin, view_type);
695 g_free(details);
698 if (sort)
700 SortType type;
702 type = !g_strcasecmp(sort, "Name") ? SORT_NAME :
703 !g_strcasecmp(sort, "Type") ? SORT_TYPE :
704 !g_strcasecmp(sort, "Date") ? SORT_DATE :
705 !g_strcasecmp(sort, "Size") ? SORT_SIZE :
706 !g_strcasecmp(sort, "Owner") ? SORT_OWNER :
707 !g_strcasecmp(sort, "Group") ? SORT_GROUP :
709 if (type == -1)
710 delayed_error(_("Unknown sorting type '%s'"), sort);
711 else
712 display_set_sort_type(fwin, type, GTK_SORT_ASCENDING);
714 g_free(sort);
716 if (hidden!=-1)
718 display_set_hidden(fwin, hidden);
721 if (filter_string)
722 display_set_filter(fwin, FILER_SHOW_GLOB, filter_string);
723 else
724 display_set_filter(fwin, FILER_SHOW_ALL, NULL);
726 return NULL;
729 static xmlNodePtr rpc_Run(GList *args)
731 char *path;
733 path = string_value(ARG(0));
734 run_by_path(path);
735 g_free(path);
737 return NULL;
740 static xmlNodePtr rpc_CloseDir(GList *args)
742 char *path;
744 path = string_value(ARG(0));
745 filer_close_recursive(path);
746 g_free(path);
748 return NULL;
751 static xmlNodePtr rpc_Examine(GList *args)
753 char *path;
755 path = string_value(ARG(0));
756 examine(path);
757 g_free(path);
759 return NULL;
762 static xmlNodePtr rpc_Show(GList *args)
764 char *dir, *leaf;
766 dir = string_value(ARG(0));
767 leaf = string_value(ARG(1));
769 /* XXX: Seems silly to join them only to split again later... */
770 open_to_show(make_path(dir, leaf));
772 g_free(dir);
773 g_free(leaf);
775 return NULL;
778 static xmlNodePtr rpc_Pinboard(GList *args)
780 char *name = NULL;
782 name = string_value(ARG(0));
783 pinboard_activate(name);
784 g_free(name);
786 return NULL;
789 /* args = Filename, Style */
790 static xmlNodePtr rpc_SetBackdrop(GList *args)
792 char *file;
793 char *style;
794 BackdropStyle s;
796 file = string_value(ARG(0));
797 style = string_value(ARG(1));
799 s = !g_strcasecmp(style, "Tile") ? BACKDROP_TILE :
800 !g_strcasecmp(style, "Scale") ? BACKDROP_SCALE :
801 !g_strcasecmp(style, "Stretch") ? BACKDROP_STRETCH :
802 !g_strcasecmp(style, "Centre") ? BACKDROP_CENTRE :
803 BACKDROP_NONE;
805 if (s == BACKDROP_NONE)
806 g_warning("Invalid style '%s' for backdrop", style);
807 else
808 pinboard_set_backdrop(file, s);
810 g_free(file);
811 g_free(style);
813 return NULL;
816 /* args = App */
817 static xmlNodePtr rpc_SetBackdropApp(GList *args)
819 char *app;
821 app = string_value(ARG(0));
823 pinboard_set_backdrop_app(app);
825 g_free(app);
827 return NULL;
830 /* args = Path, X, Y, [Label, Shortcut, Args, Locked] */
831 static xmlNodePtr rpc_PinboardAdd(GList *args)
833 char *path = NULL;
834 gchar *name, *shortcut, *xargs;
835 int x, y, locked;
837 path = string_value(ARG(0));
838 x = int_value(ARG(1), 0);
839 y = int_value(ARG(2), 0);
840 name = string_value(ARG(3));
841 shortcut = string_value(ARG(4));
842 xargs = string_value(ARG(5));
843 locked = bool_value(ARG(6));
845 pinboard_pin_with_args(path, name, x, y, shortcut, xargs,
846 (locked==-1) ? FALSE : locked);
848 g_free(path);
849 g_free(name);
850 g_free(shortcut);
851 g_free(xargs);
853 return NULL;
856 /* args = Path, [Label] */
857 static xmlNodePtr rpc_PinboardRemove(GList *args)
859 char *path = NULL;
860 gchar *name;
862 path = string_value(ARG(0));
863 name = string_value(ARG(1));
865 pinboard_remove(path, name);
867 g_free(path);
868 if(name)
869 g_free(name);
871 return NULL;
874 /* args = Side, [Name] */
875 static xmlNodePtr rpc_Panel(GList *args)
877 PanelSide side;
878 char *name, *side_name;
880 side_name = string_value(ARG(0));
881 if (side_name)
883 side = panel_name_to_side(side_name);
884 g_free(side_name);
885 g_return_val_if_fail(side != PANEL_NUMBER_OF_SIDES, NULL);
887 else
888 side = PANEL_DEFAULT_SIDE;
890 name = string_value(ARG(1));
892 panel_new(name, side);
894 g_free(name);
896 return NULL;
899 /* args = Side, Path, [Label, After, Shortcut, Args, Locked] */
900 static xmlNodePtr rpc_PanelAdd(GList *args)
902 PanelSide side;
903 char *path, *side_name, *label, *shortcut, *arg;
904 gboolean after = FALSE;
905 gboolean locked = FALSE;
906 int tmp;
908 side_name = string_value(ARG(0));
909 side = panel_name_to_side(side_name);
910 g_free(side_name);
911 g_return_val_if_fail(side != PANEL_NUMBER_OF_SIDES, NULL);
913 path = string_value(ARG(1));
914 label = string_value(ARG(2));
916 tmp = bool_value(ARG(3));
917 after = (tmp == -1) ? FALSE : tmp;
919 shortcut = string_value(ARG(4));
920 arg = string_value(ARG(5));
921 locked = bool_value(ARG(6));
923 panel_add(side, path, label, after, shortcut, arg, (locked== -1) ? FALSE : locked);
925 g_free(path);
926 g_free(label);
927 g_free(shortcut);
928 g_free(arg);
930 return NULL;
933 static xmlNodePtr rpc_PanelRemove(GList *args)
935 PanelSide side;
936 char *path, *side_name, *label;
938 side_name = string_value(ARG(0));
939 side = panel_name_to_side(side_name);
940 g_free(side_name);
941 g_return_val_if_fail(side != PANEL_NUMBER_OF_SIDES, NULL);
943 path = string_value(ARG(1));
944 label = string_value(ARG(2));
946 if (path || label)
947 panel_remove_item(side, path, label);
948 else
949 g_warning("Must specify either path or label");
951 g_free(path);
952 g_free(label);
954 return NULL;
957 static xmlNodePtr rpc_Copy(GList *args)
959 GList *from;
960 char *to;
961 char *leaf;
962 int quiet;
964 from = list_value(ARG(0));
965 to = string_value(ARG(1));
966 leaf = string_value(ARG(2));
967 quiet = bool_value(ARG(3));
969 if (from)
970 action_copy(from, to, leaf, quiet);
971 else
972 g_warning("No files in SOAP request list");
974 destroy_glist(&from);
975 g_free(to);
976 g_free(leaf);
978 return NULL;
981 static xmlNodePtr rpc_Move(GList *args)
983 GList *from;
984 char *to;
985 char *leaf;
986 int quiet;
988 from = list_value(ARG(0));
989 to = string_value(ARG(1));
990 leaf = string_value(ARG(2));
991 quiet = bool_value(ARG(3));
993 if (from)
994 action_move(from, to, leaf, quiet);
995 else
996 g_warning("No files in SOAP request list");
998 destroy_glist(&from);
999 g_free(to);
1000 g_free(leaf);
1002 return NULL;
1005 static xmlNodePtr rpc_Link(GList *args)
1007 GList *from;
1008 char *to;
1009 char *leaf;
1011 from = list_value(ARG(0));
1012 to = string_value(ARG(1));
1013 leaf = string_value(ARG(2));
1015 if (from)
1016 action_link(from, to, leaf, TRUE);
1017 else
1018 g_warning("No files in SOAP request list");
1020 destroy_glist(&from);
1021 g_free(to);
1022 g_free(leaf);
1024 return NULL;
1027 static xmlNodePtr rpc_FileType(GList *args)
1029 MIME_type *type;
1030 char *path, *tname;
1031 xmlNodePtr reply;
1033 path = string_value(ARG(0));
1034 type = type_get_type(path);
1035 g_free(path);
1037 reply = xmlNewNode(NULL, "rox:FileTypeResponse");
1038 tname = g_strconcat(type->media_type, "/", type->subtype, NULL);
1040 xmlNewNs(reply, SOAP_RPC_NS, "soap");
1041 xmlNewTextChild(reply, NULL, "soap:result", tname);
1042 g_free(tname);
1044 return reply;
1047 static xmlNodePtr rpc_Mount(GList *args)
1049 GList *paths;
1050 int open_dir, quiet;
1052 paths = list_value(ARG(0));
1053 open_dir = bool_value(ARG(1));
1054 quiet = bool_value(ARG(2));
1056 if (open_dir == -1)
1057 open_dir = TRUE;
1059 if (paths)
1060 action_mount(paths, open_dir, TRUE, quiet);
1062 destroy_glist(&paths);
1064 return NULL;
1067 static xmlNodePtr rpc_Unmount(GList *args)
1069 GList *paths;
1070 int quiet;
1072 paths = list_value(ARG(0));
1073 quiet = bool_value(ARG(1));
1075 if (paths)
1076 action_mount(paths, FALSE, FALSE, quiet);
1078 destroy_glist(&paths);
1080 return NULL;
1083 /* The first time the property changes, do nothing (it's us setting the
1084 * property). The second time, get the result.
1085 * Don't call this function three times!
1087 static void soap_done(GtkWidget *widget, GdkEventProperty *event, gpointer data)
1089 static int times_called = 0;
1090 GdkAtom prop = (GdkAtom) data;
1092 if (prop != event->atom)
1093 return;
1095 times_called++;
1096 g_return_if_fail(times_called < 3);
1098 if (times_called == 1)
1099 return;
1101 /* If we got a reply, display it here */
1102 if (event->state == GDK_PROPERTY_NEW_VALUE)
1104 gint length;
1106 data = read_property(event->window, event->atom, &length);
1108 if (data)
1109 puts(data);
1112 gtk_main_quit();
1115 static gboolean too_slow(gpointer data)
1117 g_warning("Existing ROX-Filer process is not responding! Try with -n");
1118 gtk_main_quit();
1120 return 0;
1123 /* Send the SOAP message in property 'prop' on 'from' to 'dest' */
1124 static void soap_send(GtkWidget *from, GdkAtom prop, GdkWindow *dest)
1126 GdkEventClient event;
1128 event.data.l[0] = GDK_WINDOW_XWINDOW(from->window);
1129 event.data.l[1] = gdk_x11_atom_to_xatom(prop);
1130 event.data_format = 32;
1131 event.message_type = xsoap;
1133 gdk_event_send_client_message((GdkEvent *) &event,
1134 GDK_WINDOW_XWINDOW(dest));
1136 g_timeout_add(10000, too_slow, NULL);
1138 gtk_main();
1141 /* Lookup this method in rpc_calls and invoke it.
1142 * Returns the SOAP reply or fault, or NULL if this method
1143 * doesn't return anything.
1145 static xmlNodePtr soap_invoke(xmlNode *method)
1147 GList *args = NULL;
1148 SOAP_call *call;
1149 gchar **arg;
1150 xmlNodePtr retval = NULL;
1151 GHashTable *name_to_node;
1152 xmlNode *node;
1154 call = g_hash_table_lookup(rpc_calls, method->name);
1155 if (!call)
1157 xmlNodePtr reply;
1158 gchar *err;
1160 err = g_strdup_printf(_("Attempt to invoke unknown SOAP "
1161 "method '%s'"), method->name);
1162 reply = xmlNewNode(NULL, "env:Fault");
1163 xmlNewNs(reply, SOAP_RPC_NS, "rpc");
1164 xmlNewNs(reply, SOAP_ENV_NS, "env");
1165 xmlNewTextChild(reply, NULL, "faultcode",
1166 "rpc:ProcedureNotPresent");
1167 xmlNewTextChild(reply, NULL, "faultstring", err);
1168 g_free(err);
1169 return reply;
1172 name_to_node = g_hash_table_new(g_str_hash, g_str_equal);
1173 for (node = method->xmlChildrenNode; node; node = node->next)
1175 if (node->type != XML_ELEMENT_NODE)
1176 continue;
1178 if (node->ns == NULL || strcmp(node->ns->href, ROX_NS) != 0)
1179 continue;
1181 g_hash_table_insert(name_to_node, (gchar *) node->name, node);
1184 if (call->required_args)
1186 for (arg = call->required_args; *arg; arg++)
1188 node = g_hash_table_lookup(name_to_node, *arg);
1189 if (!node)
1191 g_warning("Missing required argument '%s' "
1192 "in call to method '%s'", *arg,
1193 method->name);
1194 goto out;
1197 args = g_list_append(args, node);
1201 if (call->optional_args)
1203 for (arg = call->optional_args; *arg; arg++)
1204 args = g_list_append(args,
1205 g_hash_table_lookup(name_to_node, *arg));
1208 retval = call->func(args);
1210 out:
1211 g_hash_table_destroy(name_to_node);
1212 g_list_free(args);
1214 return retval;