r1996: Cope slightly better with invalid filenames in various places (reported by
[rox-filer.git] / ROX-Filer / src / remote.c
blob185c426c4410dedc968d34cc1c2d831386484148
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(void);
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,Class");
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;
368 guchar *retval = NULL;
370 gdk_error_trap_push();
372 while (!retval)
374 if (!(gdk_property_get(window, prop,
375 gdk_x11_xatom_to_atom(XA_STRING), 0, grab_len,
376 FALSE, NULL, NULL,
377 &length, &data) && data))
378 goto out; /* Error? */
380 if (length >= grab_len)
382 /* Didn't get all of it - try again */
383 grab_len <<= 1;
384 g_free(data);
385 continue;
388 data = g_realloc(data, length + 1);
389 data[length] = '\0'; /* libxml seems to need this */
390 *out_length = length;
392 retval = data;
394 out:
396 if (gdk_error_trap_pop() == Success)
397 return retval;
399 g_warning("Error reading %s property!", gdk_atom_name(prop));
401 g_free(retval);
402 return NULL;
405 static gboolean client_event(GtkWidget *window,
406 GdkEventClient *event,
407 gpointer user_data)
409 GdkWindow *src_window;
410 GdkAtom prop;
411 xmlDocPtr reply;
412 guchar *data;
413 gint length;
414 xmlDocPtr doc;
416 if (event->message_type != xsoap)
417 return FALSE;
419 src_window = gdk_window_foreign_new(event->data.l[0]);
420 g_return_val_if_fail(src_window != NULL, FALSE);
421 prop = gdk_x11_xatom_to_atom(event->data.l[1]);
423 data = read_property(src_window, prop, &length);
424 if (!data)
425 return TRUE;
427 doc = xmlParseMemory(g_strndup(data, length), length);
428 g_free(data);
430 reply = run_soap(doc);
431 if (number_of_windows == 0)
432 gtk_main_quit();
434 xmlFreeDoc(doc);
436 if (reply)
438 /* Send reply back... */
439 xmlChar *mem;
440 int size;
442 xmlDocDumpMemory(reply, &mem, &size);
443 g_return_val_if_fail(size > 0, TRUE);
445 gdk_error_trap_push();
446 gdk_property_change(src_window, prop,
447 gdk_x11_xatom_to_atom(XA_STRING), 8,
448 GDK_PROP_MODE_REPLACE, mem, size);
449 g_free(mem);
450 gdk_flush();
451 if (gdk_error_trap_pop() != Success)
452 g_warning("Failed to send SOAP reply!");
454 else
456 gdk_error_trap_push();
457 gdk_property_delete(src_window, prop);
458 gdk_flush();
459 if (gdk_error_trap_pop() != Success)
460 g_warning("Failed to send SOAP reply!");
463 return TRUE;
466 /* Some handy functions for processing SOAP RPC arguments... */
468 /* Returns TRUE, FALSE, or -1 (if argument is unspecified) */
469 static int bool_value(xmlNode *arg)
471 int answer;
472 char *optval;
474 if (!arg)
475 return -1;
477 optval = xmlNodeGetContent(arg);
478 answer = text_to_boolean(optval, -1); /* XXX: Report error? */
479 g_free(optval);
481 return answer;
484 /* Returns the text of this arg as a string, or NULL if the (optional)
485 * argument wasn't supplied. Returns "" if the arg is empty.
486 * g_free() the result.
488 static char *string_value(xmlNode *arg)
490 char *retval;
492 if (!arg)
493 return NULL;
495 retval = xmlNodeGetContent(arg);
497 return retval ? retval : g_strdup("");
500 /* Returns the text of this arg as an int, or the default value if not
501 * supplied or not an int.
503 static int int_value(xmlNode *arg, int def)
505 char *str, *end;
506 int i;
508 if (!arg)
509 return def;
511 str = xmlNodeGetContent(arg);
512 if (!str || !str[0])
513 return def;
515 i = (int) strtol(str, &end, 0);
517 return (end > str) ? i : def;
520 /* Return a list of strings, one for each child node of arg.
521 * g_list_free the list, and g_free each string.
523 static GList *list_value(xmlNode *arg)
525 GList *list = NULL;
526 xmlNode *node;
528 for (node = arg->xmlChildrenNode; node; node = node->next)
530 if (node->type != XML_ELEMENT_NODE)
531 continue;
533 list = g_list_append(list, string_value(node));
536 return list;
539 #define ARG(n) ((xmlNode *) g_list_nth(args, n)->data)
541 /* The RPC handlers all work in the same way -- they get passed a list of
542 * xmlNode arguments from the RPC call and they return the result node, or
543 * NULL if there isn't a result.
546 static xmlNodePtr rpc_Version(GList *args)
548 xmlNodePtr reply;
550 reply = xmlNewNode(NULL, "rox:VersionResponse");
551 xmlNewNs(reply, SOAP_RPC_NS, "soap");
552 xmlNewChild(reply, NULL, "soap:result", VERSION);
554 return reply;
557 /* Args: Path, [Style, Details, Sort, Class] */
558 static xmlNodePtr rpc_OpenDir(GList *args)
560 char *path;
561 char *style, *details, *sort, *class;
562 FilerWindow *fwin;
564 path = string_value(ARG(0));
565 style = string_value(ARG(1));
566 details = string_value(ARG(2));
567 sort = string_value(ARG(3));
568 class = string_value(ARG(4));
570 fwin = filer_opendir(path, NULL, class);
571 g_free(path);
572 g_free(class);
574 if (style)
576 DisplayStyle ds;
578 ds = !g_strcasecmp(style, "Large") ? LARGE_ICONS :
579 !g_strcasecmp(style, "Small") ? SMALL_ICONS :
580 !g_strcasecmp(style, "Huge") ? HUGE_ICONS :
581 UNKNOWN_STYLE;
582 if (ds == UNKNOWN_STYLE)
583 g_warning("Unknown style '%s'\n", style);
584 else
585 display_set_layout(fwin, ds, fwin->details_type);
587 g_free(style);
590 if (details)
592 DetailsType dt;
594 dt = !g_strcasecmp(details, "None") ? DETAILS_NONE :
595 !g_strcasecmp(details, "Summary") ? DETAILS_SUMMARY :
596 !g_strcasecmp(details, "Size") ? DETAILS_SIZE :
597 !g_strcasecmp(details, "Type") ? DETAILS_TYPE :
598 !g_strcasecmp(details, "Times") ? DETAILS_TIMES :
599 !g_strcasecmp(details, "Permissions")
600 ? DETAILS_PERMISSIONS :
601 DETAILS_UNKNOWN;
603 if (dt == DETAILS_UNKNOWN)
604 g_warning("Unknown details type '%s'\n", details);
605 else
606 display_set_layout(fwin, fwin->display_style, dt);
608 g_free(details);
611 if (sort)
613 int (*cmp)(const void *, const void *);
615 cmp = !g_strcasecmp(sort, "Name") ? sort_by_name :
616 !g_strcasecmp(sort, "Type") ? sort_by_type :
617 !g_strcasecmp(sort, "Date") ? sort_by_date :
618 !g_strcasecmp(sort, "Size") ? sort_by_size :
619 NULL;
620 if (!cmp)
621 g_warning("Unknown sorting criteria '%s'\n", sort);
622 else
623 display_set_sort_fn(fwin, cmp);
625 g_free(sort);
628 return NULL;
631 static xmlNodePtr rpc_Run(GList *args)
633 char *path;
635 path = string_value(ARG(0));
636 run_by_path(path);
637 g_free(path);
639 return NULL;
642 static xmlNodePtr rpc_CloseDir(GList *args)
644 char *path;
646 path = string_value(ARG(0));
647 filer_close_recursive(path);
648 g_free(path);
650 return NULL;
653 static xmlNodePtr rpc_Examine(GList *args)
655 char *path;
657 path = string_value(ARG(0));
658 examine(path);
659 g_free(path);
661 return NULL;
664 static xmlNodePtr rpc_Show(GList *args)
666 char *dir, *leaf;
668 dir = string_value(ARG(0));
669 leaf = string_value(ARG(1));
671 /* XXX: Seems silly to join them only to split again later... */
672 open_to_show(make_path(dir, leaf)->str);
674 g_free(dir);
675 g_free(leaf);
677 return NULL;
680 static xmlNodePtr rpc_Pinboard(GList *args)
682 char *name = NULL;
684 name = string_value(ARG(0));
685 pinboard_activate(name);
686 g_free(name);
688 return NULL;
691 /* args = App */
692 static xmlNodePtr rpc_SetBackdropApp(GList *args)
694 char *app;
696 app = string_value(ARG(0));
698 pinboard_set_backdrop_app(app);
700 g_free(app);
702 return NULL;
705 /* args = Path, X, Y, [Label] */
706 static xmlNodePtr rpc_PinboardAdd(GList *args)
708 char *path = NULL;
709 gchar *name;
710 int x, y;
712 path = string_value(ARG(0));
713 x = int_value(ARG(1), 0);
714 y = int_value(ARG(2), 0);
715 name = string_value(ARG(3));
717 pinboard_pin(path, name, x, y, NULL);
719 g_free(path);
721 return NULL;
724 /* Returns PANEL_NUMBER_OF_SIDES if name is invalid */
725 static PanelSide panel_name_to_side(gchar *side)
727 if (strcmp(side, "Top") == 0)
728 return PANEL_TOP;
729 else if (strcmp(side, "Bottom") == 0)
730 return PANEL_BOTTOM;
731 else if (strcmp(side, "Left") == 0)
732 return PANEL_LEFT;
733 else if (strcmp(side, "Right") == 0)
734 return PANEL_RIGHT;
735 else
736 g_warning("Unknown panel side '%s'", side);
737 return PANEL_NUMBER_OF_SIDES;
740 /* args = Side, [Name] */
741 static xmlNodePtr rpc_Panel(GList *args)
743 PanelSide side;
744 char *name, *side_name;
746 side_name = string_value(ARG(0));
747 side = panel_name_to_side(side_name);
748 g_free(side_name);
749 g_return_val_if_fail(side != PANEL_NUMBER_OF_SIDES, NULL);
751 name = string_value(ARG(1));
753 panel_new(name, side);
755 g_free(name);
757 return NULL;
760 /* args = Side, Path, [Label, After] */
761 static xmlNodePtr rpc_PanelAdd(GList *args)
763 PanelSide side;
764 char *path, *side_name, *label;
765 gboolean after = FALSE;
766 int tmp;
768 side_name = string_value(ARG(0));
769 side = panel_name_to_side(side_name);
770 g_free(side_name);
771 g_return_val_if_fail(side != PANEL_NUMBER_OF_SIDES, NULL);
773 path = string_value(ARG(1));
774 label = string_value(ARG(2));
776 tmp = bool_value(ARG(3));
777 after = (tmp == -1) ? FALSE : tmp;
779 panel_add(side, path, label, after);
781 g_free(path);
783 return NULL;
786 static xmlNodePtr rpc_Copy(GList *args)
788 GList *from;
789 char *to;
790 char *leaf;
791 int quiet;
793 from = list_value(ARG(0));
794 to = string_value(ARG(1));
795 leaf = string_value(ARG(2));
796 quiet = bool_value(ARG(3));
798 if (from)
799 action_copy(from, to, leaf, quiet);
801 destroy_glist(&from);
802 g_free(to);
803 g_free(leaf);
805 return NULL;
808 static xmlNodePtr rpc_Move(GList *args)
810 GList *from;
811 char *to;
812 char *leaf;
813 int quiet;
815 from = list_value(ARG(0));
816 to = string_value(ARG(1));
817 leaf = string_value(ARG(2));
818 quiet = bool_value(ARG(3));
820 if (from)
821 action_move(from, to, leaf, quiet);
823 destroy_glist(&from);
824 g_free(to);
825 g_free(leaf);
827 return NULL;
830 static xmlNodePtr rpc_Link(GList *args)
832 GList *from;
833 char *to;
834 char *leaf;
836 from = list_value(ARG(0));
837 to = string_value(ARG(1));
838 leaf = string_value(ARG(2));
840 if (from)
841 action_link(from, to, leaf);
843 destroy_glist(&from);
844 g_free(to);
845 g_free(leaf);
847 return NULL;
850 static xmlNodePtr rpc_FileType(GList *args)
852 MIME_type *type;
853 char *path, *tname;
854 xmlNodePtr reply;
856 path = string_value(ARG(0));
857 type = type_get_type(path);
858 g_free(path);
860 reply = xmlNewNode(NULL, "rox:FileTypeResponse");
861 tname = g_strconcat(type->media_type, "/", type->subtype, NULL);
863 xmlNewNs(reply, SOAP_RPC_NS, "soap");
864 xmlNewChild(reply, NULL, "soap:result", tname);
865 g_free(tname);
867 return reply;
870 static xmlNodePtr rpc_Mount(GList *args)
872 GList *paths;
873 int open_dir, quiet;
875 paths = list_value(ARG(0));
876 open_dir = bool_value(ARG(1));
877 quiet = bool_value(ARG(2));
879 if (open_dir == -1)
880 open_dir = TRUE;
882 if (paths)
883 action_mount(paths, open_dir, quiet);
885 destroy_glist(&paths);
887 return NULL;
890 /* The first time the property changes, do nothing (it's us setting the
891 * property). The second time, get the result.
892 * Don't call this function three times!
894 static void soap_done(GtkWidget *widget, GdkEventProperty *event, gpointer data)
896 static int times_called = 0;
897 GdkAtom prop = (GdkAtom) data;
899 if (prop != event->atom)
900 return;
902 times_called++;
903 g_return_if_fail(times_called < 3);
905 if (times_called == 1)
906 return;
908 /* If we got a reply, display it here */
909 if (event->state == GDK_PROPERTY_NEW_VALUE)
911 gint length;
913 data = read_property(event->window, event->atom, &length);
915 if (data)
916 puts(data);
919 gtk_main_quit();
922 static gboolean too_slow(gpointer data)
924 g_warning("Existing ROX-Filer process is not responding! Try with -n");
925 gtk_main_quit();
927 return 0;
930 /* Send the SOAP message in property 'prop' on 'from' to 'dest' */
931 static void soap_send(GtkWidget *from, GdkAtom prop, GdkWindow *dest)
933 GdkEventClient event;
935 event.data.l[0] = GDK_WINDOW_XWINDOW(from->window);
936 event.data.l[1] = gdk_x11_atom_to_xatom(prop);
937 event.data_format = 32;
938 event.message_type = xsoap;
940 gdk_event_send_client_message((GdkEvent *) &event,
941 GDK_WINDOW_XWINDOW(dest));
943 gtk_timeout_add(10000, too_slow, NULL);
945 gtk_main();
948 /* Lookup this method in rpc_calls and invoke it.
949 * Returns the SOAP reply or fault, or NULL if this method
950 * doesn't return anything.
952 static xmlNodePtr soap_invoke(xmlNode *method)
954 GList *args = NULL;
955 SOAP_call *call;
956 gchar **arg;
957 xmlNodePtr retval = NULL;
958 GHashTable *name_to_node;
959 xmlNode *node;
961 call = g_hash_table_lookup(rpc_calls, method->name);
962 if (!call)
964 xmlNodePtr reply;
965 gchar *err;
967 err = g_strdup_printf(_("Attempt to invoke unknown SOAP "
968 "method '%s'"), method->name);
969 reply = xmlNewNode(NULL, "env:Fault");
970 xmlNewNs(reply, SOAP_RPC_NS, "rpc");
971 xmlNewNs(reply, SOAP_ENV_NS, "env");
972 xmlNewChild(reply, NULL, "faultcode",
973 "rpc:ProcedureNotPresent");
974 xmlNewChild(reply, NULL, "faultstring", err);
975 g_free(err);
976 return reply;
979 name_to_node = g_hash_table_new(g_str_hash, g_str_equal);
980 for (node = method->xmlChildrenNode; node; node = node->next)
982 if (node->type != XML_ELEMENT_NODE)
983 continue;
985 if (node->ns == NULL || strcmp(node->ns->href, ROX_NS) != 0)
986 continue;
988 g_hash_table_insert(name_to_node, (gchar *) node->name, node);
991 if (call->required_args)
993 for (arg = call->required_args; *arg; arg++)
995 node = g_hash_table_lookup(name_to_node, *arg);
996 if (!node)
998 g_warning("Missing required argument '%s' "
999 "in call to method '%s'", *arg,
1000 method->name);
1001 goto out;
1004 args = g_list_append(args, node);
1008 if (call->optional_args)
1010 for (arg = call->optional_args; *arg; arg++)
1011 args = g_list_append(args,
1012 g_hash_table_lookup(name_to_node, *arg));
1015 retval = call->func(args);
1017 out:
1018 g_hash_table_destroy(name_to_node);
1019 g_list_free(args);
1021 return retval;