r4523: Updated headers:
[rox-filer/dt.git] / ROX-Filer / src / remote.c
blobce0c7af71c7b601c149652499b524e0c04711e1a
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"
53 static GdkAtom filer_atom; /* _ROX_FILER_EUID_VERSION_HOST */
54 static GdkAtom filer_atom_any; /* _ROX_FILER_EUID_HOST */
55 static GdkAtom xsoap; /* _XSOAP */
57 typedef struct _SOAP_call SOAP_call;
58 typedef xmlNodePtr (*SOAP_func)(GList *args);
60 struct _SOAP_call {
61 SOAP_func func;
62 gchar **required_args;
63 gchar **optional_args;
66 static GHashTable *rpc_calls = NULL; /* MethodName -> Function */
68 /* Static prototypes */
69 static GdkWindow *get_existing_ipc_window(void);
70 static gboolean get_ipc_property(GdkWindow *window, Window *r_xid);
71 static void soap_send(GtkWidget *from, GdkAtom prop, GdkWindow *dest);
72 static gboolean client_event(GtkWidget *window,
73 GdkEventClient *event,
74 gpointer data);
75 static void soap_done(GtkWidget *widget,
76 GdkEventProperty *event,
77 gpointer data);
78 static void soap_register(char *name, SOAP_func func, char *req, char *opt);
79 static xmlNodePtr soap_invoke(xmlNode *method);
81 static xmlNodePtr rpc_Version(GList *args);
82 static xmlNodePtr rpc_OpenDir(GList *args);
83 static xmlNodePtr rpc_CloseDir(GList *args);
84 static xmlNodePtr rpc_Examine(GList *args);
85 static xmlNodePtr rpc_Show(GList *args);
86 static xmlNodePtr rpc_Pinboard(GList *args);
87 static xmlNodePtr rpc_Panel(GList *args);
88 static xmlNodePtr rpc_Run(GList *args);
89 static xmlNodePtr rpc_Copy(GList *args);
90 static xmlNodePtr rpc_Move(GList *args);
91 static xmlNodePtr rpc_Link(GList *args);
92 static xmlNodePtr rpc_FileType(GList *args);
93 static xmlNodePtr rpc_Mount(GList *args);
94 static xmlNodePtr rpc_Unmount(GList *args);
96 static xmlNodePtr rpc_PanelAdd(GList *args);
97 static xmlNodePtr rpc_PanelRemove(GList *args);
98 static xmlNodePtr rpc_PinboardAdd(GList *args);
99 static xmlNodePtr rpc_PinboardRemove(GList *args);
100 static xmlNodePtr rpc_SetBackdrop(GList *args);
101 static xmlNodePtr rpc_SetBackdropApp(GList *args);
103 /****************************************************************
104 * EXTERNAL INTERFACE *
105 ****************************************************************/
108 /* Try to get an already-running filer to handle things (only if
109 * new_copy is FALSE); TRUE if we succeed.
110 * Create an IPC widget so that future filers can contact us.
112 gboolean remote_init(xmlDocPtr rpc, gboolean new_copy)
114 guchar *unique_id;
115 GdkWindow *existing_ipc_window;
116 GtkWidget *ipc_window;
117 Window xwindow;
119 /* xmlDocDump(stdout, rpc); */
121 rpc_calls = g_hash_table_new(g_str_hash, g_str_equal);
123 soap_register("Version", rpc_Version, NULL, NULL);
125 soap_register("Run", rpc_Run, "Filename", NULL);
126 soap_register("OpenDir", rpc_OpenDir, "Filename",
127 "Style,Details,Sort,Class,ID,Hidden,Filter");
128 soap_register("CloseDir", rpc_CloseDir, "Filename", NULL);
129 soap_register("Examine", rpc_Examine, "Filename", NULL);
130 soap_register("Show", rpc_Show, "Directory,Leafname", NULL);
132 soap_register("Pinboard", rpc_Pinboard, NULL, "Name");
133 soap_register("Panel", rpc_Panel, NULL, "Side,Name");
135 soap_register("FileType", rpc_FileType, "Filename", NULL);
137 soap_register("Copy", rpc_Copy, "From,To", "Leafname,Quiet");
138 soap_register("Move", rpc_Move, "From,To", "Leafname,Quiet");
139 soap_register("Link", rpc_Link, "From,To", "Leafname");
140 soap_register("Mount", rpc_Mount, "MountPoints", "OpenDir,Quiet");
141 soap_register("Unmount", rpc_Unmount, "MountPoints", "Quiet");
143 soap_register("SetBackdrop", rpc_SetBackdrop, "Filename,Style", NULL);
144 soap_register("SetBackdropApp", rpc_SetBackdropApp, "App", NULL);
145 soap_register("PinboardAdd", rpc_PinboardAdd, "Path,X,Y", "Label,Shortcut,Args");
146 soap_register("PinboardRemove", rpc_PinboardRemove, "Path", "Label");
147 soap_register("PanelAdd", rpc_PanelAdd, "Side,Path", "Label,After,Shortcut,Args");
148 soap_register("PanelRemove", rpc_PanelRemove, "Side", "Path,Label");
150 /* Look for a property on the root window giving the IPC window
151 * of an already-running copy of this version of the filer, running
152 * on the same machine and with the same euid.
154 unique_id = g_strdup_printf("_ROX_FILER_%d_%s_%s",
155 (int) euid, VERSION, our_host_name());
156 filer_atom = gdk_atom_intern(unique_id, FALSE);
157 g_free(unique_id);
159 xsoap = gdk_atom_intern("_XSOAP", FALSE);
161 /* If we find a running copy, we'll need a window to put the
162 * SOAP message in before sending.
163 * If there's no running copy, we'll need a window to receive
164 * future SOAP message events.
165 * Either way, we'll need a window for it...
167 ipc_window = gtk_invisible_new();
168 gtk_widget_realize(ipc_window);
170 XGrabServer(GDK_DISPLAY());
172 existing_ipc_window = new_copy ? NULL : get_existing_ipc_window();
173 if (existing_ipc_window)
175 xmlChar *mem;
176 int size;
178 XUngrabServer(GDK_DISPLAY());
180 xmlDocDumpMemory(rpc, &mem, &size);
181 g_return_val_if_fail(size > 0, FALSE);
183 /* Since Gtk might have selected this already, we'd
184 * better do it BEFORE changing the property.
186 gtk_widget_add_events(ipc_window, GDK_PROPERTY_CHANGE_MASK);
187 g_signal_connect(ipc_window, "property-notify-event",
188 G_CALLBACK(soap_done), GINT_TO_POINTER(xsoap));
190 gdk_property_change(ipc_window->window, xsoap,
191 gdk_x11_xatom_to_atom(XA_STRING), 8,
192 GDK_PROP_MODE_REPLACE, mem, size);
193 g_free(mem);
195 soap_send(ipc_window, xsoap, existing_ipc_window);
197 return TRUE;
200 xwindow = GDK_WINDOW_XWINDOW(ipc_window->window);
202 /* Make the IPC window contain a property pointing to
203 * itself - this can then be used to check that it really
204 * is an IPC window.
206 gdk_property_change(ipc_window->window, filer_atom,
207 gdk_x11_xatom_to_atom(XA_WINDOW), 32,
208 GDK_PROP_MODE_REPLACE,
209 (void *) &xwindow, 1);
211 /* Get notified when we get a message */
212 g_signal_connect(ipc_window, "client-event",
213 G_CALLBACK(client_event), NULL);
215 /* Make the root window contain a pointer to the IPC window */
216 gdk_property_change(gdk_get_default_root_window(), filer_atom,
217 gdk_x11_xatom_to_atom(XA_WINDOW), 32,
218 GDK_PROP_MODE_REPLACE,
219 (void *) &xwindow, 1);
221 XUngrabServer(GDK_DISPLAY());
223 /* Also have a property without the version number, for programs
224 * that are happy to talk to any version of the filer.
226 unique_id = g_strdup_printf("_ROX_FILER_%d_%s",
227 (int) euid, our_host_name());
228 filer_atom_any = gdk_atom_intern(unique_id, FALSE);
229 g_free(unique_id);
230 /* On the IPC window... */
231 gdk_property_change(ipc_window->window, filer_atom_any,
232 gdk_x11_xatom_to_atom(XA_WINDOW), 32,
233 GDK_PROP_MODE_REPLACE,
234 (void *) &xwindow, 1);
235 /* ... and on the root */
236 gdk_property_change(gdk_get_default_root_window(), filer_atom_any,
237 gdk_x11_xatom_to_atom(XA_WINDOW), 32,
238 GDK_PROP_MODE_REPLACE,
239 (void *) &xwindow, 1);
241 return FALSE;
244 /* Executes the RPC call(s) in the given SOAP message and returns
245 * the reply.
247 xmlDocPtr run_soap(xmlDocPtr soap)
249 xmlNodePtr body, node, rep_body, reply;
250 xmlDocPtr rep_doc = NULL;
252 g_return_val_if_fail(soap != NULL, NULL);
254 /* Make sure we don't quit before doing the whole list
255 * (there's a command that closes windows)
257 number_of_windows++;
259 node = xmlDocGetRootElement(soap);
260 if (!node->ns)
261 goto bad_soap;
263 if (strcmp(node->ns->href, SOAP_ENV_NS) != 0 &&
264 strcmp(node->ns->href, SOAP_ENV_NS_OLD) != 0)
265 goto bad_soap;
267 body = get_subnode(node, SOAP_ENV_NS, "Body");
268 if (!body)
269 body = get_subnode(node, SOAP_ENV_NS_OLD, "Body");
270 if (!body)
271 goto bad_soap;
273 for (node = body->xmlChildrenNode; node; node = node->next)
275 if (node->type != XML_ELEMENT_NODE)
276 continue;
278 if (node->ns == NULL || strcmp(node->ns->href, ROX_NS) != 0)
280 g_warning("Unknown namespace %s",
281 node->ns ? node->ns->href
282 : (xmlChar *) "(none)");
283 continue;
286 reply = soap_invoke(node);
288 if (reply)
290 if (!rep_doc)
291 rep_doc = soap_new(&rep_body);
292 xmlAddChild(rep_body, reply);
296 goto out;
298 bad_soap:
299 g_warning("Bad SOAP message received!");
301 out:
302 number_of_windows--;
304 return rep_doc;
308 /****************************************************************
309 * INTERNAL FUNCTIONS *
310 ****************************************************************/
312 /* Register this function to handle SOAP calls to method 'name'.
313 * 'req' and 'opt' are comma separated lists of argument names, in the order
314 * that they are to be delivered to the function.
315 * NULL will be passed for an opt argument if not given.
316 * Otherwise, the parameter is the xmlNode from the call.
318 static void soap_register(char *name, SOAP_func func, char *req, char *opt)
320 SOAP_call *call;
322 call = g_new(SOAP_call, 1);
323 call->func = func;
324 call->required_args = req ? g_strsplit(req, ",", 0) : NULL;
325 call->optional_args = opt ? g_strsplit(opt, ",", 0) : NULL;
327 g_hash_table_insert(rpc_calls, g_strdup(name), call);
330 /* Get the remote IPC window of the already-running filer if there
331 * is one.
333 static GdkWindow *get_existing_ipc_window(void)
335 Window xid, xid_confirm;
336 GdkWindow *window;
338 if (!get_ipc_property(gdk_get_default_root_window(), &xid))
339 return NULL;
341 if (gdk_window_lookup(xid))
342 return NULL; /* Stale handle which we now own */
344 window = gdk_window_foreign_new(xid);
345 if (!window)
346 return NULL;
348 if (!get_ipc_property(window, &xid_confirm) || xid_confirm != xid)
349 return NULL;
351 return window;
354 /* Returns the 'rox_atom' property of 'window' */
355 static gboolean get_ipc_property(GdkWindow *window, Window *r_xid)
357 guchar *data;
358 gint format, length;
359 gboolean retval = FALSE;
361 if (gdk_property_get(window, filer_atom,
362 gdk_x11_xatom_to_atom(XA_WINDOW), 0, 4,
363 FALSE, NULL, &format, &length, &data) && data)
365 /* Note: values with format=32 are stored as longs client-side,
366 * which may be more than 32 bits on some systems.
368 if (format == 32 && length == sizeof(long))
370 long windowID = *((long *) data);
371 retval = TRUE;
372 *r_xid = (Window) windowID;
374 g_free(data);
377 return retval;
380 static char *read_property(GdkWindow *window, GdkAtom prop, gint *out_length)
382 gint grab_len = 4096;
383 gint length;
384 guchar *data;
385 guchar *retval = NULL;
387 gdk_error_trap_push();
389 while (!retval)
391 if (!(gdk_property_get(window, prop,
392 gdk_x11_xatom_to_atom(XA_STRING), 0, grab_len,
393 FALSE, NULL, NULL,
394 &length, &data) && data))
395 goto out; /* Error? */
397 if (length >= grab_len)
399 /* Didn't get all of it - try again */
400 grab_len <<= 1;
401 g_free(data);
402 continue;
405 data = g_realloc(data, length + 1);
406 data[length] = '\0'; /* libxml seems to need this */
407 *out_length = length;
409 retval = data;
411 out:
413 if (gdk_error_trap_pop() == Success)
414 return retval;
416 g_warning("Error reading %s property!", gdk_atom_name(prop));
418 g_free(retval);
419 return NULL;
422 static gboolean client_event(GtkWidget *window,
423 GdkEventClient *event,
424 gpointer user_data)
426 GdkWindow *src_window;
427 GdkAtom prop;
428 xmlDocPtr reply;
429 guchar *data;
430 gint length;
431 xmlDocPtr doc;
433 if (event->message_type != xsoap)
434 return FALSE;
436 src_window = gdk_window_foreign_new(event->data.l[0]);
437 if (!src_window)
439 g_warning("SOAP message sender window was destroyed before I \n"
440 "could read it.");
441 return FALSE;
443 prop = gdk_x11_xatom_to_atom(event->data.l[1]);
445 data = read_property(src_window, prop, &length);
446 if (!data)
447 return TRUE;
449 doc = xmlParseMemory(g_strndup(data, length), length);
450 g_free(data);
452 reply = run_soap(doc);
453 if (number_of_windows == 0)
454 gtk_main_quit();
456 xmlFreeDoc(doc);
458 if (reply)
460 /* Send reply back... */
461 xmlChar *mem;
462 int size;
464 xmlDocDumpMemory(reply, &mem, &size);
465 g_return_val_if_fail(size > 0, TRUE);
467 gdk_error_trap_push();
468 gdk_property_change(src_window, prop,
469 gdk_x11_xatom_to_atom(XA_STRING), 8,
470 GDK_PROP_MODE_REPLACE, mem, size);
471 g_free(mem);
472 gdk_flush();
473 if (gdk_error_trap_pop() != Success)
474 g_warning("Failed to send SOAP reply!");
476 else
478 gdk_error_trap_push();
479 gdk_property_delete(src_window, prop);
480 gdk_flush();
481 if (gdk_error_trap_pop() != Success)
482 g_warning("Failed to send SOAP reply!");
485 return TRUE;
488 /* Some handy functions for processing SOAP RPC arguments... */
490 /* Returns TRUE, FALSE, or -1 (if argument is unspecified) */
491 static int bool_value(xmlNode *arg)
493 int answer;
494 char *optval;
496 if (!arg)
497 return -1;
499 optval = xmlNodeGetContent(arg);
500 answer = text_to_boolean(optval, -1); /* XXX: Report error? */
501 g_free(optval);
503 return answer;
506 /* Returns the text of this arg as a string, or NULL if the (optional)
507 * argument wasn't supplied. Returns "" if the arg is empty.
508 * g_free() the result.
510 static char *string_value(xmlNode *arg)
512 char *retval;
514 if (!arg)
515 return NULL;
517 retval = xmlNodeGetContent(arg);
519 return retval ? retval : g_strdup("");
522 /* Returns the text of this arg as an int, or the default value if not
523 * supplied or not an int.
525 static int int_value(xmlNode *arg, int def)
527 char *str, *end;
528 int i;
530 if (!arg)
531 return def;
533 str = xmlNodeGetContent(arg);
534 if (!str || !str[0])
535 return def;
537 i = (int) strtol(str, &end, 0);
539 return (end > str) ? i : def;
542 /* Return a list of strings, one for each child node of arg.
543 * g_list_free the list, and g_free each string.
545 static GList *list_value(xmlNode *arg)
547 GList *list = NULL;
548 xmlNode *node;
550 for (node = arg->xmlChildrenNode; node; node = node->next)
552 if (node->type != XML_ELEMENT_NODE)
553 continue;
555 list = g_list_append(list, string_value(node));
558 return list;
561 #define ARG(n) ((xmlNode *) g_list_nth(args, n)->data)
563 /* The RPC handlers all work in the same way -- they get passed a list of
564 * xmlNode arguments from the RPC call and they return the result node, or
565 * NULL if there isn't a result.
568 static xmlNodePtr rpc_Version(GList *args)
570 xmlNodePtr reply;
572 reply = xmlNewNode(NULL, "rox:VersionResponse");
573 xmlNewNs(reply, SOAP_RPC_NS, "soap");
574 xmlNewTextChild(reply, NULL, "soap:result", VERSION);
576 return reply;
579 /* Args: Path, [Style, Details, Sort, Class, Window] */
580 static xmlNodePtr rpc_OpenDir(GList *args)
582 char *path;
583 char *style, *details, *sort, *class, *window;
584 int hidden=FALSE;
585 char *filter_string=NULL;
586 FilerWindow *fwin = NULL;
588 path = string_value(ARG(0));
589 class = string_value(ARG(4));
590 window = string_value(ARG(5));
592 if (window)
593 fwin = filer_get_by_id(window);
595 if (!fwin)
597 fwin = filer_opendir(path, NULL, class);
598 if (window)
599 filer_set_id(fwin, window);
601 else
602 filer_change_to(fwin, path, NULL);
604 g_free(path);
605 g_free(class);
606 g_free(window);
607 if (!fwin)
608 return NULL;
610 style = string_value(ARG(1));
611 details = string_value(ARG(2));
612 sort = string_value(ARG(3));
613 hidden = bool_value(ARG(6));
614 filter_string = string_value(ARG(7));
616 if (style)
618 DisplayStyle ds;
620 ds = !g_strcasecmp(style, "Large") ? LARGE_ICONS :
621 !g_strcasecmp(style, "Small") ? SMALL_ICONS :
622 !g_strcasecmp(style, "Huge") ? HUGE_ICONS :
623 !g_strcasecmp(style, "Automatic") ? AUTO_SIZE_ICONS :
624 UNKNOWN_STYLE;
625 if (ds == UNKNOWN_STYLE)
626 delayed_error(_("Unknown style '%s'"), style);
627 else
628 display_set_layout(fwin, ds, fwin->details_type, TRUE);
630 g_free(style);
633 if (details)
635 DetailsType dt;
636 ViewType view_type;
638 dt = !g_strcasecmp(details, "None") ? DETAILS_NONE :
639 !g_strcasecmp(details, "ListView") ? DETAILS_NONE :
640 !g_strcasecmp(details, "Size") ? DETAILS_SIZE :
641 !g_strcasecmp(details, "Type") ? DETAILS_TYPE :
642 !g_strcasecmp(details, "Times") ? DETAILS_TIMES :
643 !g_strcasecmp(details, "Permissions")
644 ? DETAILS_PERMISSIONS :
645 DETAILS_UNKNOWN;
647 if (dt == DETAILS_UNKNOWN)
648 delayed_error(_("Unknown details type '%s'"), details);
649 else
650 display_set_layout(fwin, fwin->display_style_wanted,
651 dt, TRUE);
653 if (g_strcasecmp(details, "ListView") == 0)
654 view_type = VIEW_TYPE_DETAILS;
655 else
656 view_type = VIEW_TYPE_COLLECTION;
658 if (view_type != fwin->view_type)
659 filer_set_view_type(fwin, view_type);
661 g_free(details);
664 if (sort)
666 SortType type;
668 type = !g_strcasecmp(sort, "Name") ? SORT_NAME :
669 !g_strcasecmp(sort, "Type") ? SORT_TYPE :
670 !g_strcasecmp(sort, "Date") ? SORT_DATE :
671 !g_strcasecmp(sort, "Size") ? SORT_SIZE :
672 !g_strcasecmp(sort, "Owner") ? SORT_OWNER :
673 !g_strcasecmp(sort, "Group") ? SORT_GROUP :
675 if (type == -1)
676 delayed_error(_("Unknown sorting type '%s'"), sort);
677 else
678 display_set_sort_type(fwin, type, GTK_SORT_ASCENDING);
680 g_free(sort);
682 if (hidden!=-1)
684 display_set_hidden(fwin, hidden);
687 if (filter_string)
688 display_set_filter(fwin, FILER_SHOW_GLOB, filter_string);
689 else
690 display_set_filter(fwin, FILER_SHOW_ALL, NULL);
692 return NULL;
695 static xmlNodePtr rpc_Run(GList *args)
697 char *path;
699 path = string_value(ARG(0));
700 run_by_path(path);
701 g_free(path);
703 return NULL;
706 static xmlNodePtr rpc_CloseDir(GList *args)
708 char *path;
710 path = string_value(ARG(0));
711 filer_close_recursive(path);
712 g_free(path);
714 return NULL;
717 static xmlNodePtr rpc_Examine(GList *args)
719 char *path;
721 path = string_value(ARG(0));
722 examine(path);
723 g_free(path);
725 return NULL;
728 static xmlNodePtr rpc_Show(GList *args)
730 char *dir, *leaf;
732 dir = string_value(ARG(0));
733 leaf = string_value(ARG(1));
735 /* XXX: Seems silly to join them only to split again later... */
736 open_to_show(make_path(dir, leaf));
738 g_free(dir);
739 g_free(leaf);
741 return NULL;
744 static xmlNodePtr rpc_Pinboard(GList *args)
746 char *name = NULL;
748 name = string_value(ARG(0));
749 pinboard_activate(name);
750 g_free(name);
752 return NULL;
755 /* args = Filename, Style */
756 static xmlNodePtr rpc_SetBackdrop(GList *args)
758 char *file;
759 char *style;
760 BackdropStyle s;
762 file = string_value(ARG(0));
763 style = string_value(ARG(1));
765 s = !g_strcasecmp(style, "Tile") ? BACKDROP_TILE :
766 !g_strcasecmp(style, "Scale") ? BACKDROP_SCALE :
767 !g_strcasecmp(style, "Stretch") ? BACKDROP_STRETCH :
768 !g_strcasecmp(style, "Centre") ? BACKDROP_CENTRE :
769 BACKDROP_NONE;
771 if (s == BACKDROP_NONE)
772 g_warning("Invalid style '%s' for backdrop", style);
773 else
774 pinboard_set_backdrop(file, s);
776 g_free(file);
777 g_free(style);
779 return NULL;
782 /* args = App */
783 static xmlNodePtr rpc_SetBackdropApp(GList *args)
785 char *app;
787 app = string_value(ARG(0));
789 pinboard_set_backdrop_app(app);
791 g_free(app);
793 return NULL;
796 /* args = Path, X, Y, [Label] */
797 static xmlNodePtr rpc_PinboardAdd(GList *args)
799 char *path = NULL;
800 gchar *name, *shortcut, *xargs;
801 int x, y;
803 path = string_value(ARG(0));
804 x = int_value(ARG(1), 0);
805 y = int_value(ARG(2), 0);
806 name = string_value(ARG(3));
807 shortcut = string_value(ARG(4));
808 xargs = string_value(ARG(5));
810 pinboard_pin_with_args(path, name, x, y, shortcut, xargs);
812 g_free(path);
813 g_free(name);
814 g_free(shortcut);
815 g_free(xargs);
817 return NULL;
820 /* args = Path, [Label] */
821 static xmlNodePtr rpc_PinboardRemove(GList *args)
823 char *path = NULL;
824 gchar *name;
826 path = string_value(ARG(0));
827 name = string_value(ARG(1));
829 pinboard_remove(path, name);
831 g_free(path);
832 if(name)
833 g_free(name);
835 return NULL;
838 /* args = Side, [Name] */
839 static xmlNodePtr rpc_Panel(GList *args)
841 PanelSide side;
842 char *name, *side_name;
844 side_name = string_value(ARG(0));
845 if (side_name)
847 side = panel_name_to_side(side_name);
848 g_free(side_name);
849 g_return_val_if_fail(side != PANEL_NUMBER_OF_SIDES, NULL);
851 else
852 side = PANEL_DEFAULT_SIDE;
854 name = string_value(ARG(1));
856 panel_new(name, side);
858 g_free(name);
860 return NULL;
863 /* args = Side, Path, [Label, After] */
864 static xmlNodePtr rpc_PanelAdd(GList *args)
866 PanelSide side;
867 char *path, *side_name, *label, *shortcut, *arg;
868 gboolean after = FALSE;
869 int tmp;
871 side_name = string_value(ARG(0));
872 side = panel_name_to_side(side_name);
873 g_free(side_name);
874 g_return_val_if_fail(side != PANEL_NUMBER_OF_SIDES, NULL);
876 path = string_value(ARG(1));
877 label = string_value(ARG(2));
879 tmp = bool_value(ARG(3));
880 after = (tmp == -1) ? FALSE : tmp;
882 shortcut = string_value(ARG(4));
883 arg = string_value(ARG(5));
885 panel_add(side, path, label, after, shortcut, arg);
887 g_free(path);
888 g_free(label);
889 g_free(shortcut);
890 g_free(arg);
892 return NULL;
895 static xmlNodePtr rpc_PanelRemove(GList *args)
897 PanelSide side;
898 char *path, *side_name, *label;
900 side_name = string_value(ARG(0));
901 side = panel_name_to_side(side_name);
902 g_free(side_name);
903 g_return_val_if_fail(side != PANEL_NUMBER_OF_SIDES, NULL);
905 path = string_value(ARG(1));
906 label = string_value(ARG(2));
908 if (path || label)
909 panel_remove_item(side, path, label);
910 else
911 g_warning("Must specify either path or label");
913 g_free(path);
914 g_free(label);
916 return NULL;
919 static xmlNodePtr rpc_Copy(GList *args)
921 GList *from;
922 char *to;
923 char *leaf;
924 int quiet;
926 from = list_value(ARG(0));
927 to = string_value(ARG(1));
928 leaf = string_value(ARG(2));
929 quiet = bool_value(ARG(3));
931 if (from)
932 action_copy(from, to, leaf, quiet);
933 else
934 g_warning("No files in SOAP request list");
936 destroy_glist(&from);
937 g_free(to);
938 g_free(leaf);
940 return NULL;
943 static xmlNodePtr rpc_Move(GList *args)
945 GList *from;
946 char *to;
947 char *leaf;
948 int quiet;
950 from = list_value(ARG(0));
951 to = string_value(ARG(1));
952 leaf = string_value(ARG(2));
953 quiet = bool_value(ARG(3));
955 if (from)
956 action_move(from, to, leaf, quiet);
957 else
958 g_warning("No files in SOAP request list");
960 destroy_glist(&from);
961 g_free(to);
962 g_free(leaf);
964 return NULL;
967 static xmlNodePtr rpc_Link(GList *args)
969 GList *from;
970 char *to;
971 char *leaf;
973 from = list_value(ARG(0));
974 to = string_value(ARG(1));
975 leaf = string_value(ARG(2));
977 if (from)
978 action_link(from, to, leaf, TRUE);
979 else
980 g_warning("No files in SOAP request list");
982 destroy_glist(&from);
983 g_free(to);
984 g_free(leaf);
986 return NULL;
989 static xmlNodePtr rpc_FileType(GList *args)
991 MIME_type *type;
992 char *path, *tname;
993 xmlNodePtr reply;
995 path = string_value(ARG(0));
996 type = type_get_type(path);
997 g_free(path);
999 reply = xmlNewNode(NULL, "rox:FileTypeResponse");
1000 tname = g_strconcat(type->media_type, "/", type->subtype, NULL);
1002 xmlNewNs(reply, SOAP_RPC_NS, "soap");
1003 xmlNewTextChild(reply, NULL, "soap:result", tname);
1004 g_free(tname);
1006 return reply;
1009 static xmlNodePtr rpc_Mount(GList *args)
1011 GList *paths;
1012 int open_dir, quiet;
1014 paths = list_value(ARG(0));
1015 open_dir = bool_value(ARG(1));
1016 quiet = bool_value(ARG(2));
1018 if (open_dir == -1)
1019 open_dir = TRUE;
1021 if (paths)
1022 action_mount(paths, open_dir, TRUE, quiet);
1024 destroy_glist(&paths);
1026 return NULL;
1029 static xmlNodePtr rpc_Unmount(GList *args)
1031 GList *paths;
1032 int quiet;
1034 paths = list_value(ARG(0));
1035 quiet = bool_value(ARG(1));
1037 if (paths)
1038 action_mount(paths, FALSE, FALSE, quiet);
1040 destroy_glist(&paths);
1042 return NULL;
1045 /* The first time the property changes, do nothing (it's us setting the
1046 * property). The second time, get the result.
1047 * Don't call this function three times!
1049 static void soap_done(GtkWidget *widget, GdkEventProperty *event, gpointer data)
1051 static int times_called = 0;
1052 GdkAtom prop = (GdkAtom) data;
1054 if (prop != event->atom)
1055 return;
1057 times_called++;
1058 g_return_if_fail(times_called < 3);
1060 if (times_called == 1)
1061 return;
1063 /* If we got a reply, display it here */
1064 if (event->state == GDK_PROPERTY_NEW_VALUE)
1066 gint length;
1068 data = read_property(event->window, event->atom, &length);
1070 if (data)
1071 puts(data);
1074 gtk_main_quit();
1077 static gboolean too_slow(gpointer data)
1079 g_warning("Existing ROX-Filer process is not responding! Try with -n");
1080 gtk_main_quit();
1082 return 0;
1085 /* Send the SOAP message in property 'prop' on 'from' to 'dest' */
1086 static void soap_send(GtkWidget *from, GdkAtom prop, GdkWindow *dest)
1088 GdkEventClient event;
1090 event.data.l[0] = GDK_WINDOW_XWINDOW(from->window);
1091 event.data.l[1] = gdk_x11_atom_to_xatom(prop);
1092 event.data_format = 32;
1093 event.message_type = xsoap;
1095 gdk_event_send_client_message((GdkEvent *) &event,
1096 GDK_WINDOW_XWINDOW(dest));
1098 g_timeout_add(10000, too_slow, NULL);
1100 gtk_main();
1103 /* Lookup this method in rpc_calls and invoke it.
1104 * Returns the SOAP reply or fault, or NULL if this method
1105 * doesn't return anything.
1107 static xmlNodePtr soap_invoke(xmlNode *method)
1109 GList *args = NULL;
1110 SOAP_call *call;
1111 gchar **arg;
1112 xmlNodePtr retval = NULL;
1113 GHashTable *name_to_node;
1114 xmlNode *node;
1116 call = g_hash_table_lookup(rpc_calls, method->name);
1117 if (!call)
1119 xmlNodePtr reply;
1120 gchar *err;
1122 err = g_strdup_printf(_("Attempt to invoke unknown SOAP "
1123 "method '%s'"), method->name);
1124 reply = xmlNewNode(NULL, "env:Fault");
1125 xmlNewNs(reply, SOAP_RPC_NS, "rpc");
1126 xmlNewNs(reply, SOAP_ENV_NS, "env");
1127 xmlNewTextChild(reply, NULL, "faultcode",
1128 "rpc:ProcedureNotPresent");
1129 xmlNewTextChild(reply, NULL, "faultstring", err);
1130 g_free(err);
1131 return reply;
1134 name_to_node = g_hash_table_new(g_str_hash, g_str_equal);
1135 for (node = method->xmlChildrenNode; node; node = node->next)
1137 if (node->type != XML_ELEMENT_NODE)
1138 continue;
1140 if (node->ns == NULL || strcmp(node->ns->href, ROX_NS) != 0)
1141 continue;
1143 g_hash_table_insert(name_to_node, (gchar *) node->name, node);
1146 if (call->required_args)
1148 for (arg = call->required_args; *arg; arg++)
1150 node = g_hash_table_lookup(name_to_node, *arg);
1151 if (!node)
1153 g_warning("Missing required argument '%s' "
1154 "in call to method '%s'", *arg,
1155 method->name);
1156 goto out;
1159 args = g_list_append(args, node);
1163 if (call->optional_args)
1165 for (arg = call->optional_args; *arg; arg++)
1166 args = g_list_append(args,
1167 g_hash_table_lookup(name_to_node, *arg));
1170 retval = call->func(args);
1172 out:
1173 g_hash_table_destroy(name_to_node);
1174 g_list_free(args);
1176 return retval;