r1566: Do double buffering manually. This has no advantage at the moment, but
[rox-filer.git] / ROX-Filer / src / remote.c
blobb0643a2383d31e52b98a2e880870a7d7212ca313
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2002, the ROX-Filer team.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* This code is used to communicate between two copies of the filer:
23 * If the filer is run and the same version of the filer is already
24 * running on the same machine then the new copy simply asks the old
25 * one deal with it and quits.
28 #include "config.h"
30 #include <string.h>
32 #include <gdk/gdkx.h>
33 #include <X11/X.h>
34 #include <X11/Xatom.h>
35 #include <gtk/gtk.h>
36 #include <gtk/gtkinvisible.h>
37 #include <libxml/parser.h>
39 #include "global.h"
41 #include "main.h"
42 #include "support.h"
43 #include "gui_support.h"
44 #include "run.h"
45 #include "remote.h"
46 #include "filer.h"
47 #include "pinboard.h"
48 #include "panel.h"
49 #include "action.h"
50 #include "type.h"
51 #include "display.h"
52 #include "xml.h"
53 #include "diritem.h"
55 static GdkAtom filer_atom; /* _ROX_FILER_EUID_VERSION_HOST */
56 static GdkAtom filer_atom_any; /* _ROX_FILER_EUID_HOST */
57 static GdkAtom xsoap; /* _XSOAP */
59 typedef struct _SOAP_call SOAP_call;
60 typedef xmlNodePtr (*SOAP_func)(GList *args);
62 struct _SOAP_call {
63 SOAP_func func;
64 gchar **required_args;
65 gchar **optional_args;
68 static GHashTable *rpc_calls = NULL; /* MethodName -> Function */
70 /* Static prototypes */
71 static GdkWindow *get_existing_ipc_window();
72 static gboolean get_ipc_property(GdkWindow *window, Window *r_xid);
73 static void soap_send(GtkWidget *from, GdkAtom prop, GdkWindow *dest);
74 static gboolean client_event(GtkWidget *window,
75 GdkEventClient *event,
76 gpointer data);
77 static void soap_done(GtkWidget *widget,
78 GdkEventProperty *event,
79 gpointer data);
80 static void soap_register(char *name, SOAP_func func, char *req, char *opt);
81 static xmlNodePtr soap_invoke(xmlNode *method);
83 static xmlNodePtr rpc_Version(GList *args);
84 static xmlNodePtr rpc_OpenDir(GList *args);
85 static xmlNodePtr rpc_CloseDir(GList *args);
86 static xmlNodePtr rpc_Examine(GList *args);
87 static xmlNodePtr rpc_Show(GList *args);
88 static xmlNodePtr rpc_Pinboard(GList *args);
89 static xmlNodePtr rpc_Panel(GList *args);
90 static xmlNodePtr rpc_Run(GList *args);
91 static xmlNodePtr rpc_Copy(GList *args);
92 static xmlNodePtr rpc_Move(GList *args);
93 static xmlNodePtr rpc_Link(GList *args);
94 static xmlNodePtr rpc_FileType(GList *args);
95 static xmlNodePtr rpc_Mount(GList *args);
97 static xmlNodePtr rpc_PanelAdd(GList *args);
98 static xmlNodePtr rpc_PinboardAdd(GList *args);
99 static xmlNodePtr rpc_SetBackdropApp(GList *args);
101 /****************************************************************
102 * EXTERNAL INTERFACE *
103 ****************************************************************/
106 /* Try to get an already-running filer to handle things (only if
107 * new_copy is FALSE); TRUE if we succeed.
108 * Create an IPC widget so that future filers can contact us.
110 gboolean remote_init(xmlDocPtr rpc, gboolean new_copy)
112 guchar *unique_id;
113 GdkWindow *existing_ipc_window;
114 GtkWidget *ipc_window;
115 Window xwindow;
117 /* xmlDocDump(stdout, rpc); */
119 rpc_calls = g_hash_table_new(g_str_hash, g_str_equal);
121 soap_register("Version", rpc_Version, NULL, NULL);
123 soap_register("Run", rpc_Run, "Filename", NULL);
124 soap_register("OpenDir", rpc_OpenDir, "Filename", "Style,Details,Sort");
125 soap_register("CloseDir", rpc_CloseDir, "Filename", NULL);
126 soap_register("Examine", rpc_Examine, "Filename", NULL);
127 soap_register("Show", rpc_Show, "Directory,Leafname", NULL);
129 soap_register("Pinboard", rpc_Pinboard, NULL, "Name");
130 soap_register("Panel", rpc_Panel, "Side", "Name");
132 soap_register("FileType", rpc_FileType, "Filename", NULL);
134 soap_register("Copy", rpc_Copy, "From,To", "Leafname,Quiet");
135 soap_register("Move", rpc_Move, "From,To", "Leafname,Quiet");
136 soap_register("Link", rpc_Link, "From,To", "Leafname");
137 soap_register("Mount", rpc_Mount, "MountPoints", "OpenDir,Quiet");
139 soap_register("SetBackdropApp", rpc_SetBackdropApp, "App", NULL);
140 soap_register("PinboardAdd", rpc_PinboardAdd, "Path,X,Y", "Label");
141 soap_register("PanelAdd", rpc_PanelAdd, "Side,Path", "Label,After");
143 /* Look for a property on the root window giving the IPC window
144 * of an already-running copy of this version of the filer, running
145 * on the same machine and with the same euid.
147 unique_id = g_strdup_printf("_ROX_FILER_%d_%s_%s",
148 (int) euid, VERSION, our_host_name());
149 filer_atom = gdk_atom_intern(unique_id, FALSE);
150 g_free(unique_id);
152 xsoap = gdk_atom_intern("_XSOAP", FALSE);
154 /* If we find a running copy, we'll need a window to put the
155 * SOAP message in before sending.
156 * If there's no running copy, we'll need a window to receive
157 * future SOAP message events.
158 * Either way, we'll need a window for it...
160 ipc_window = gtk_invisible_new();
161 gtk_widget_realize(ipc_window);
163 existing_ipc_window = new_copy ? NULL : get_existing_ipc_window();
164 if (existing_ipc_window)
166 xmlChar *mem;
167 int size;
169 xmlDocDumpMemory(rpc, &mem, &size);
170 g_return_val_if_fail(size > 0, FALSE);
172 /* Since Gtk might have selected this already, we'd
173 * better do it BEFORE changing the property.
175 gtk_widget_add_events(ipc_window, GDK_PROPERTY_CHANGE_MASK);
176 g_signal_connect(ipc_window, "property-notify-event",
177 G_CALLBACK(soap_done), GINT_TO_POINTER(xsoap));
179 gdk_property_change(ipc_window->window, xsoap,
180 gdk_x11_xatom_to_atom(XA_STRING), 8,
181 GDK_PROP_MODE_REPLACE, mem, size);
182 g_free(mem);
184 soap_send(ipc_window, xsoap, existing_ipc_window);
186 return TRUE;
189 xwindow = GDK_WINDOW_XWINDOW(ipc_window->window);
191 /* Make the IPC window contain a property pointing to
192 * itself - this can then be used to check that it really
193 * is an IPC window.
195 gdk_property_change(ipc_window->window, filer_atom,
196 gdk_x11_xatom_to_atom(XA_WINDOW), 32,
197 GDK_PROP_MODE_REPLACE,
198 (void *) &xwindow, 1);
200 /* Get notified when we get a message */
201 g_signal_connect(ipc_window, "client-event",
202 G_CALLBACK(client_event), NULL);
204 /* Make the root window contain a pointer to the IPC window */
205 gdk_property_change(gdk_get_default_root_window(), filer_atom,
206 gdk_x11_xatom_to_atom(XA_WINDOW), 32,
207 GDK_PROP_MODE_REPLACE,
208 (void *) &xwindow, 1);
210 /* Also have a property without the version number, for programs
211 * that are happy to talk to any version of the filer.
213 unique_id = g_strdup_printf("_ROX_FILER_%d_%s",
214 (int) euid, our_host_name());
215 filer_atom_any = gdk_atom_intern(unique_id, FALSE);
216 g_free(unique_id);
217 /* On the IPC window... */
218 gdk_property_change(ipc_window->window, filer_atom_any,
219 gdk_x11_xatom_to_atom(XA_WINDOW), 32,
220 GDK_PROP_MODE_REPLACE,
221 (void *) &xwindow, 1);
222 /* ... and on the root */
223 gdk_property_change(gdk_get_default_root_window(), filer_atom_any,
224 gdk_x11_xatom_to_atom(XA_WINDOW), 32,
225 GDK_PROP_MODE_REPLACE,
226 (void *) &xwindow, 1);
228 return FALSE;
231 /* Executes the RPC call(s) in the given SOAP message and returns
232 * the reply.
234 xmlDocPtr run_soap(xmlDocPtr soap)
236 xmlNodePtr body, node, rep_body, reply;
237 xmlDocPtr rep_doc = NULL;
239 g_return_val_if_fail(soap != NULL, NULL);
241 /* Make sure we don't quit before doing the whole list
242 * (there's a command that closes windows)
244 number_of_windows++;
246 node = xmlDocGetRootElement(soap);
247 if (!node->ns)
248 goto bad_soap;
250 if (strcmp(node->ns->href, SOAP_ENV_NS) != 0 &&
251 strcmp(node->ns->href, SOAP_ENV_NS_OLD) != 0)
252 goto bad_soap;
254 body = get_subnode(node, SOAP_ENV_NS, "Body");
255 if (!body)
256 body = get_subnode(node, SOAP_ENV_NS_OLD, "Body");
257 if (!body)
258 goto bad_soap;
260 for (node = body->xmlChildrenNode; node; node = node->next)
262 if (node->type != XML_ELEMENT_NODE)
263 continue;
265 if (node->ns == NULL || strcmp(node->ns->href, ROX_NS) != 0)
267 g_warning("Unknown namespace %s",
268 node->ns ? node->ns->href
269 : (xmlChar *) "(none)");
270 continue;
273 reply = soap_invoke(node);
275 if (reply)
277 if (!rep_doc)
278 rep_doc = soap_new(&rep_body);
279 xmlAddChild(rep_body, reply);
283 goto out;
285 bad_soap:
286 g_warning("Bad SOAP message received!");
288 out:
289 number_of_windows--;
291 return rep_doc;
295 /****************************************************************
296 * INTERNAL FUNCTIONS *
297 ****************************************************************/
299 /* Register this function to handle SOAP calls to method 'name'.
300 * 'req' and 'opt' are comma separated lists of argument names, in the order
301 * that they are to be delivered to the function.
302 * NULL will be passed for an opt argument if not given.
303 * Otherwise, the parameter is the xmlNode from the call.
305 static void soap_register(char *name, SOAP_func func, char *req, char *opt)
307 SOAP_call *call;
309 call = g_new(SOAP_call, 1);
310 call->func = func;
311 call->required_args = req ? g_strsplit(req, ",", 0) : NULL;
312 call->optional_args = opt ? g_strsplit(opt, ",", 0) : NULL;
314 g_hash_table_insert(rpc_calls, g_strdup(name), call);
317 /* Get the remote IPC window of the already-running filer if there
318 * is one.
320 static GdkWindow *get_existing_ipc_window(void)
322 Window xid, xid_confirm;
323 GdkWindow *window;
325 if (!get_ipc_property(gdk_get_default_root_window(), &xid))
326 return NULL;
328 if (gdk_window_lookup(xid))
329 return NULL; /* Stale handle which we now own */
331 window = gdk_window_foreign_new(xid);
332 if (!window)
333 return NULL;
335 if (!get_ipc_property(window, &xid_confirm) || xid_confirm != xid)
336 return NULL;
338 return window;
341 /* Returns the 'rox_atom' property of 'window' */
342 static gboolean get_ipc_property(GdkWindow *window, Window *r_xid)
344 guchar *data;
345 gint format, length;
346 gboolean retval = FALSE;
348 if (gdk_property_get(window, filer_atom,
349 gdk_x11_xatom_to_atom(XA_WINDOW), 0, 4,
350 FALSE, NULL, &format, &length, &data) && data)
352 if (format == 32 && length == 4)
354 retval = TRUE;
355 *r_xid = *((Window *) data);
357 g_free(data);
360 return retval;
363 static char *read_property(GdkWindow *window, GdkAtom prop, gint *out_length)
365 gint grab_len = 4096;
366 gint length;
367 guchar *data;
369 while (1)
371 if (!(gdk_property_get(window, prop,
372 gdk_x11_xatom_to_atom(XA_STRING), 0, grab_len,
373 FALSE, NULL, NULL,
374 &length, &data) && data))
375 return NULL; /* Error? */
377 if (length >= grab_len)
379 /* Didn't get all of it - try again */
380 grab_len <<= 1;
381 g_free(data);
382 continue;
385 data = g_realloc(data, length + 1);
386 data[length] = '\0'; /* libxml seems to need this */
387 *out_length = length;
389 return data;
393 static gboolean client_event(GtkWidget *window,
394 GdkEventClient *event,
395 gpointer user_data)
397 GdkWindow *src_window;
398 GdkAtom prop;
399 xmlDocPtr reply;
400 guchar *data;
401 gint length;
402 xmlDocPtr doc;
404 if (event->message_type != xsoap)
405 return FALSE;
407 src_window = gdk_window_foreign_new(event->data.l[0]);
408 g_return_val_if_fail(src_window != NULL, FALSE);
409 prop = gdk_x11_xatom_to_atom(event->data.l[1]);
411 data = read_property(src_window, prop, &length);
412 if (!data)
413 return TRUE;
415 doc = xmlParseMemory(g_strndup(data, length), length);
416 g_free(data);
418 reply = run_soap(doc);
419 if (number_of_windows == 0)
420 gtk_main_quit();
422 xmlFreeDoc(doc);
424 if (reply)
426 /* Send reply back... */
427 xmlChar *mem;
428 int size;
430 xmlDocDumpMemory(reply, &mem, &size);
431 g_return_val_if_fail(size > 0, TRUE);
433 gdk_property_change(src_window, prop,
434 gdk_x11_xatom_to_atom(XA_STRING), 8,
435 GDK_PROP_MODE_REPLACE, mem, size);
436 g_free(mem);
438 else
439 gdk_property_delete(src_window, prop);
441 return TRUE;
444 /* Some handy functions for processing SOAP RPC arguments... */
446 /* Returns TRUE, FALSE, or -1 (if argument is unspecified) */
447 static int bool_value(xmlNode *arg)
449 int answer;
450 char *optval;
452 if (!arg)
453 return -1;
455 optval = xmlNodeGetContent(arg);
456 answer = text_to_boolean(optval, -1); /* XXX: Report error? */
457 g_free(optval);
459 return answer;
462 /* Returns the text of this arg as a string, or NULL if the (optional)
463 * argument wasn't supplied. Returns "" if the arg is empty.
464 * g_free() the result.
466 static char *string_value(xmlNode *arg)
468 char *retval;
470 if (!arg)
471 return NULL;
473 retval = xmlNodeGetContent(arg);
475 return retval ? retval : g_strdup("");
478 /* Returns the text of this arg as an int, or the default value if not
479 * supplied or not an int.
481 static int int_value(xmlNode *arg, int def)
483 char *str, *end;
484 int i;
486 if (!arg)
487 return def;
489 str = xmlNodeGetContent(arg);
490 if (!str || !str[0])
491 return def;
493 i = (int) strtol(str, &end, 0);
495 return (end > str) ? i : def;
498 /* Return a list of strings, one for each child node of arg.
499 * g_list_free the list, and g_free each string.
501 static GList *list_value(xmlNode *arg)
503 GList *list = NULL;
504 xmlNode *node;
506 for (node = arg->xmlChildrenNode; node; node = node->next)
508 if (node->type != XML_ELEMENT_NODE)
509 continue;
511 list = g_list_append(list, string_value(node));
514 return list;
517 #define ARG(n) ((xmlNode *) g_list_nth(args, n)->data)
519 /* The RPC handlers all work in the same way -- they get passed a list of
520 * xmlNode arguments from the RPC call and they return the result node, or
521 * NULL if there isn't a result.
524 static xmlNodePtr rpc_Version(GList *args)
526 xmlNodePtr reply;
528 reply = xmlNewNode(NULL, "rox:VersionResponse");
529 xmlNewNs(reply, SOAP_RPC_NS, "soap");
530 xmlNewChild(reply, NULL, "soap:result", VERSION);
532 return reply;
535 /* Args: Path, [Style, Details, Sort] */
536 static xmlNodePtr rpc_OpenDir(GList *args)
538 char *path;
539 char *style, *details, *sort;
540 FilerWindow *fwin;
542 path = string_value(ARG(0));
543 style = string_value(ARG(1));
544 details = string_value(ARG(2));
545 sort = string_value(ARG(3));
547 fwin = filer_opendir(path, NULL);
548 g_free(path);
550 if (style)
552 DisplayStyle ds;
554 ds = !g_strcasecmp(style, "Large") ? LARGE_ICONS :
555 !g_strcasecmp(style, "Small") ? SMALL_ICONS :
556 !g_strcasecmp(style, "Huge") ? HUGE_ICONS :
557 UNKNOWN_STYLE;
558 if (ds == UNKNOWN_STYLE)
559 g_warning("Unknown style '%s'\n", style);
560 else
561 display_set_layout(fwin, ds, fwin->details_type);
563 g_free(style);
566 if (details)
568 DetailsType dt;
570 dt = !g_strcasecmp(details, "None") ? DETAILS_NONE :
571 !g_strcasecmp(details, "Summary") ? DETAILS_SUMMARY :
572 !g_strcasecmp(details, "Size") ? DETAILS_SIZE :
573 !g_strcasecmp(details, "Type") ? DETAILS_TYPE :
574 !g_strcasecmp(details, "Times") ? DETAILS_TIMES :
575 !g_strcasecmp(details, "Permissions")
576 ? DETAILS_PERMISSIONS :
577 DETAILS_UNKNOWN;
579 if (dt == DETAILS_UNKNOWN)
580 g_warning("Unknown details type '%s'\n", details);
581 else
582 display_set_layout(fwin, fwin->display_style, dt);
584 g_free(details);
587 if (sort)
589 int (*cmp)(const void *, const void *);
591 cmp = !g_strcasecmp(sort, "Name") ? sort_by_name :
592 !g_strcasecmp(sort, "Type") ? sort_by_type :
593 !g_strcasecmp(sort, "Date") ? sort_by_date :
594 !g_strcasecmp(sort, "Size") ? sort_by_size :
595 NULL;
596 if (!cmp)
597 g_warning("Unknown sorting criteria '%s'\n", sort);
598 else
599 display_set_sort_fn(fwin, cmp);
601 g_free(sort);
604 return NULL;
607 static xmlNodePtr rpc_Run(GList *args)
609 char *path;
611 path = string_value(ARG(0));
612 run_by_path(path);
613 g_free(path);
615 return NULL;
618 static xmlNodePtr rpc_CloseDir(GList *args)
620 char *path;
622 path = string_value(ARG(0));
623 filer_close_recursive(path);
624 g_free(path);
626 return NULL;
629 static xmlNodePtr rpc_Examine(GList *args)
631 char *path;
633 path = string_value(ARG(0));
634 examine(path);
635 g_free(path);
637 return NULL;
640 static xmlNodePtr rpc_Show(GList *args)
642 char *dir, *leaf;
644 dir = string_value(ARG(0));
645 leaf = string_value(ARG(1));
647 /* XXX: Seems silly to join them only to split again later... */
648 open_to_show(make_path(dir, leaf)->str);
650 g_free(dir);
651 g_free(leaf);
653 return NULL;
656 static xmlNodePtr rpc_Pinboard(GList *args)
658 char *name = NULL;
660 name = string_value(ARG(0));
661 pinboard_activate(name);
662 g_free(name);
664 return NULL;
667 /* args = App */
668 static xmlNodePtr rpc_SetBackdropApp(GList *args)
670 char *app;
672 app = string_value(ARG(0));
674 pinboard_set_backdrop_app(app);
676 g_free(app);
678 return NULL;
681 /* args = Path, X, Y, [Label] */
682 static xmlNodePtr rpc_PinboardAdd(GList *args)
684 char *path = NULL;
685 gchar *name;
686 int x, y;
688 path = string_value(ARG(0));
689 x = int_value(ARG(1), 0);
690 y = int_value(ARG(2), 0);
691 name = string_value(ARG(3));
693 pinboard_pin(path, name, x, y);
695 g_free(path);
697 return NULL;
700 /* Returns PANEL_NUMBER_OF_SIDES if name is invalid */
701 static PanelSide panel_name_to_side(gchar *side)
703 if (strcmp(side, "Top") == 0)
704 return PANEL_TOP;
705 else if (strcmp(side, "Bottom") == 0)
706 return PANEL_BOTTOM;
707 else if (strcmp(side, "Left") == 0)
708 return PANEL_LEFT;
709 else if (strcmp(side, "Right") == 0)
710 return PANEL_RIGHT;
711 else
712 g_warning("Unknown panel side '%s'", side);
713 return PANEL_NUMBER_OF_SIDES;
716 /* args = Side, [Name] */
717 static xmlNodePtr rpc_Panel(GList *args)
719 PanelSide side;
720 char *name, *side_name;
722 side_name = string_value(ARG(0));
723 side = panel_name_to_side(side_name);
724 g_free(side_name);
725 g_return_val_if_fail(side != PANEL_NUMBER_OF_SIDES, NULL);
727 name = string_value(ARG(1));
729 panel_new(name, side);
731 g_free(name);
733 return NULL;
736 /* args = Side, Path, [Label, After] */
737 static xmlNodePtr rpc_PanelAdd(GList *args)
739 PanelSide side;
740 char *path, *side_name, *label;
741 gboolean after = FALSE;
742 int tmp;
744 side_name = string_value(ARG(0));
745 side = panel_name_to_side(side_name);
746 g_free(side_name);
747 g_return_val_if_fail(side != PANEL_NUMBER_OF_SIDES, NULL);
749 path = string_value(ARG(1));
750 label = string_value(ARG(2));
752 tmp = bool_value(ARG(3));
753 after = (tmp == -1) ? FALSE : tmp;
755 panel_add(side, path, label, after);
757 g_free(path);
759 return NULL;
762 static xmlNodePtr rpc_Copy(GList *args)
764 GList *from;
765 char *to;
766 char *leaf;
767 int quiet;
769 from = list_value(ARG(0));
770 to = string_value(ARG(1));
771 leaf = string_value(ARG(2));
772 quiet = bool_value(ARG(3));
774 if (from)
775 action_copy(from, to, leaf, quiet);
777 g_list_foreach(from, (GFunc) g_free, NULL);
778 g_list_free(from);
779 g_free(to);
780 g_free(leaf);
782 return NULL;
785 static xmlNodePtr rpc_Move(GList *args)
787 GList *from;
788 char *to;
789 char *leaf;
790 int quiet;
792 from = list_value(ARG(0));
793 to = string_value(ARG(1));
794 leaf = string_value(ARG(2));
795 quiet = bool_value(ARG(3));
797 if (from)
798 action_move(from, to, leaf, quiet);
800 g_list_foreach(from, (GFunc) g_free, NULL);
801 g_list_free(from);
802 g_free(to);
803 g_free(leaf);
805 return NULL;
808 static xmlNodePtr rpc_Link(GList *args)
810 GList *from;
811 char *to;
812 char *leaf;
814 from = list_value(ARG(0));
815 to = string_value(ARG(1));
816 leaf = string_value(ARG(2));
818 if (from)
819 action_link(from, to, leaf);
821 g_list_foreach(from, (GFunc) g_free, NULL);
822 g_list_free(from);
823 g_free(to);
824 g_free(leaf);
826 return NULL;
829 static xmlNodePtr rpc_FileType(GList *args)
831 MIME_type *type;
832 char *path, *tname;
833 xmlNodePtr reply;
835 path = string_value(ARG(0));
836 type = type_get_type(path);
837 g_free(path);
839 reply = xmlNewNode(NULL, "rox:FileTypeResponse");
840 tname = g_strconcat(type->media_type, "/", type->subtype, NULL);
842 xmlNewNs(reply, SOAP_RPC_NS, "soap");
843 xmlNewChild(reply, NULL, "soap:result", tname);
844 g_free(tname);
846 return reply;
849 static xmlNodePtr rpc_Mount(GList *args)
851 GList *paths;
852 int open_dir, quiet;
854 paths = list_value(ARG(0));
855 open_dir = bool_value(ARG(1));
856 quiet = bool_value(ARG(2));
858 if (open_dir == -1)
859 open_dir = TRUE;
861 if (paths)
862 action_mount(paths, open_dir, quiet);
864 g_list_foreach(paths, (GFunc) g_free, NULL);
865 g_list_free(paths);
867 return NULL;
870 /* The first time the property changes, do nothing (it's us setting the
871 * property). The second time, get the result.
872 * Don't call this function three times!
874 static void soap_done(GtkWidget *widget, GdkEventProperty *event, gpointer data)
876 static gboolean times_called = 0;
877 GdkAtom prop = (GdkAtom) data;
879 if (prop != event->atom)
880 return;
882 times_called++;
883 g_return_if_fail(times_called < 3);
885 if (times_called == 1)
886 return;
888 /* If we got a reply, display it here */
889 if (event->state == GDK_PROPERTY_NEW_VALUE)
891 gint length;
893 data = read_property(event->window, event->atom, &length);
895 if (data)
896 puts(data);
899 gtk_main_quit();
902 gboolean too_slow(gpointer data)
904 g_warning("Existing ROX-Filer process is not responding! Try with -n");
905 gtk_main_quit();
907 return 0;
910 /* Send the SOAP message in property 'prop' on 'from' to 'dest' */
911 static void soap_send(GtkWidget *from, GdkAtom prop, GdkWindow *dest)
913 GdkEventClient event;
915 event.data.l[0] = GDK_WINDOW_XWINDOW(from->window);
916 event.data.l[1] = gdk_x11_atom_to_xatom(prop);
917 event.data_format = 32;
918 event.message_type = xsoap;
920 gdk_event_send_client_message((GdkEvent *) &event,
921 GDK_WINDOW_XWINDOW(dest));
923 gtk_timeout_add(10000, too_slow, NULL);
925 gtk_main();
928 /* Lookup this method in rpc_calls and invoke it.
929 * Returns the SOAP reply or fault, or NULL if this method
930 * doesn't return anything.
932 static xmlNodePtr soap_invoke(xmlNode *method)
934 GList *args = NULL;
935 SOAP_call *call;
936 gchar **arg;
937 xmlNodePtr retval = NULL;
938 GHashTable *name_to_node;
939 xmlNode *node;
941 call = g_hash_table_lookup(rpc_calls, method->name);
942 if (!call)
944 xmlNodePtr reply;
945 gchar *err;
947 err = g_strdup_printf(_("Attempt to invoke unknown SOAP "
948 "method '%s'"), method->name);
949 reply = xmlNewNode(NULL, "env:Fault");
950 xmlNewNs(reply, SOAP_RPC_NS, "rpc");
951 xmlNewNs(reply, SOAP_ENV_NS, "env");
952 xmlNewChild(reply, NULL, "faultcode",
953 "rpc:ProcedureNotPresent");
954 xmlNewChild(reply, NULL, "faultstring", err);
955 g_free(err);
956 return reply;
959 name_to_node = g_hash_table_new(g_str_hash, g_str_equal);
960 for (node = method->xmlChildrenNode; node; node = node->next)
962 if (node->type != XML_ELEMENT_NODE)
963 continue;
965 if (node->ns == NULL || strcmp(node->ns->href, ROX_NS) != 0)
966 continue;
968 g_hash_table_insert(name_to_node, (gchar *) node->name, node);
971 if (call->required_args)
973 for (arg = call->required_args; *arg; arg++)
975 node = g_hash_table_lookup(name_to_node, *arg);
976 if (!node)
978 g_warning("Missing required argument '%s' "
979 "in call to method '%s'", *arg,
980 method->name);
981 goto out;
984 args = g_list_append(args, node);
988 if (call->optional_args)
990 for (arg = call->optional_args; *arg; arg++)
991 args = g_list_append(args,
992 g_hash_table_lookup(name_to_node, *arg));
995 retval = call->func(args);
997 out:
998 g_hash_table_destroy(name_to_node);
999 g_list_free(args);
1001 return retval;