r2228: Made 'Automatic' an icon size, rather than a separate option.
[rox-filer.git] / ROX-Filer / src / remote.c
blobb97cc62a54f18b7eb74660975222ea78aaf6dd73
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",
125 "Style,Details,Sort,Class");
126 soap_register("CloseDir", rpc_CloseDir, "Filename", NULL);
127 soap_register("Examine", rpc_Examine, "Filename", NULL);
128 soap_register("Show", rpc_Show, "Directory,Leafname", NULL);
130 soap_register("Pinboard", rpc_Pinboard, NULL, "Name");
131 soap_register("Panel", rpc_Panel, "Side", "Name");
133 soap_register("FileType", rpc_FileType, "Filename", NULL);
135 soap_register("Copy", rpc_Copy, "From,To", "Leafname,Quiet");
136 soap_register("Move", rpc_Move, "From,To", "Leafname,Quiet");
137 soap_register("Link", rpc_Link, "From,To", "Leafname");
138 soap_register("Mount", rpc_Mount, "MountPoints", "OpenDir,Quiet");
140 soap_register("SetBackdropApp", rpc_SetBackdropApp, "App", NULL);
141 soap_register("PinboardAdd", rpc_PinboardAdd, "Path,X,Y", "Label");
142 soap_register("PanelAdd", rpc_PanelAdd, "Side,Path", "Label,After");
144 /* Look for a property on the root window giving the IPC window
145 * of an already-running copy of this version of the filer, running
146 * on the same machine and with the same euid.
148 unique_id = g_strdup_printf("_ROX_FILER_%d_%s_%s",
149 (int) euid, VERSION, our_host_name());
150 filer_atom = gdk_atom_intern(unique_id, FALSE);
151 g_free(unique_id);
153 xsoap = gdk_atom_intern("_XSOAP", FALSE);
155 /* If we find a running copy, we'll need a window to put the
156 * SOAP message in before sending.
157 * If there's no running copy, we'll need a window to receive
158 * future SOAP message events.
159 * Either way, we'll need a window for it...
161 ipc_window = gtk_invisible_new();
162 gtk_widget_realize(ipc_window);
164 XGrabServer(GDK_DISPLAY());
166 existing_ipc_window = new_copy ? NULL : get_existing_ipc_window();
167 if (existing_ipc_window)
169 xmlChar *mem;
170 int size;
172 XUngrabServer(GDK_DISPLAY());
174 xmlDocDumpMemory(rpc, &mem, &size);
175 g_return_val_if_fail(size > 0, FALSE);
177 /* Since Gtk might have selected this already, we'd
178 * better do it BEFORE changing the property.
180 gtk_widget_add_events(ipc_window, GDK_PROPERTY_CHANGE_MASK);
181 g_signal_connect(ipc_window, "property-notify-event",
182 G_CALLBACK(soap_done), GINT_TO_POINTER(xsoap));
184 gdk_property_change(ipc_window->window, xsoap,
185 gdk_x11_xatom_to_atom(XA_STRING), 8,
186 GDK_PROP_MODE_REPLACE, mem, size);
187 g_free(mem);
189 soap_send(ipc_window, xsoap, existing_ipc_window);
191 return TRUE;
194 xwindow = GDK_WINDOW_XWINDOW(ipc_window->window);
196 /* Make the IPC window contain a property pointing to
197 * itself - this can then be used to check that it really
198 * is an IPC window.
200 gdk_property_change(ipc_window->window, filer_atom,
201 gdk_x11_xatom_to_atom(XA_WINDOW), 32,
202 GDK_PROP_MODE_REPLACE,
203 (void *) &xwindow, 1);
205 /* Get notified when we get a message */
206 g_signal_connect(ipc_window, "client-event",
207 G_CALLBACK(client_event), NULL);
209 /* Make the root window contain a pointer to the IPC window */
210 gdk_property_change(gdk_get_default_root_window(), filer_atom,
211 gdk_x11_xatom_to_atom(XA_WINDOW), 32,
212 GDK_PROP_MODE_REPLACE,
213 (void *) &xwindow, 1);
215 XUngrabServer(GDK_DISPLAY());
217 /* Also have a property without the version number, for programs
218 * that are happy to talk to any version of the filer.
220 unique_id = g_strdup_printf("_ROX_FILER_%d_%s",
221 (int) euid, our_host_name());
222 filer_atom_any = gdk_atom_intern(unique_id, FALSE);
223 g_free(unique_id);
224 /* On the IPC window... */
225 gdk_property_change(ipc_window->window, filer_atom_any,
226 gdk_x11_xatom_to_atom(XA_WINDOW), 32,
227 GDK_PROP_MODE_REPLACE,
228 (void *) &xwindow, 1);
229 /* ... and on the root */
230 gdk_property_change(gdk_get_default_root_window(), filer_atom_any,
231 gdk_x11_xatom_to_atom(XA_WINDOW), 32,
232 GDK_PROP_MODE_REPLACE,
233 (void *) &xwindow, 1);
235 return FALSE;
238 /* Executes the RPC call(s) in the given SOAP message and returns
239 * the reply.
241 xmlDocPtr run_soap(xmlDocPtr soap)
243 xmlNodePtr body, node, rep_body, reply;
244 xmlDocPtr rep_doc = NULL;
246 g_return_val_if_fail(soap != NULL, NULL);
248 /* Make sure we don't quit before doing the whole list
249 * (there's a command that closes windows)
251 number_of_windows++;
253 node = xmlDocGetRootElement(soap);
254 if (!node->ns)
255 goto bad_soap;
257 if (strcmp(node->ns->href, SOAP_ENV_NS) != 0 &&
258 strcmp(node->ns->href, SOAP_ENV_NS_OLD) != 0)
259 goto bad_soap;
261 body = get_subnode(node, SOAP_ENV_NS, "Body");
262 if (!body)
263 body = get_subnode(node, SOAP_ENV_NS_OLD, "Body");
264 if (!body)
265 goto bad_soap;
267 for (node = body->xmlChildrenNode; node; node = node->next)
269 if (node->type != XML_ELEMENT_NODE)
270 continue;
272 if (node->ns == NULL || strcmp(node->ns->href, ROX_NS) != 0)
274 g_warning("Unknown namespace %s",
275 node->ns ? node->ns->href
276 : (xmlChar *) "(none)");
277 continue;
280 reply = soap_invoke(node);
282 if (reply)
284 if (!rep_doc)
285 rep_doc = soap_new(&rep_body);
286 xmlAddChild(rep_body, reply);
290 goto out;
292 bad_soap:
293 g_warning("Bad SOAP message received!");
295 out:
296 number_of_windows--;
298 return rep_doc;
302 /****************************************************************
303 * INTERNAL FUNCTIONS *
304 ****************************************************************/
306 /* Register this function to handle SOAP calls to method 'name'.
307 * 'req' and 'opt' are comma separated lists of argument names, in the order
308 * that they are to be delivered to the function.
309 * NULL will be passed for an opt argument if not given.
310 * Otherwise, the parameter is the xmlNode from the call.
312 static void soap_register(char *name, SOAP_func func, char *req, char *opt)
314 SOAP_call *call;
316 call = g_new(SOAP_call, 1);
317 call->func = func;
318 call->required_args = req ? g_strsplit(req, ",", 0) : NULL;
319 call->optional_args = opt ? g_strsplit(opt, ",", 0) : NULL;
321 g_hash_table_insert(rpc_calls, g_strdup(name), call);
324 /* Get the remote IPC window of the already-running filer if there
325 * is one.
327 static GdkWindow *get_existing_ipc_window(void)
329 Window xid, xid_confirm;
330 GdkWindow *window;
332 if (!get_ipc_property(gdk_get_default_root_window(), &xid))
333 return NULL;
335 if (gdk_window_lookup(xid))
336 return NULL; /* Stale handle which we now own */
338 window = gdk_window_foreign_new(xid);
339 if (!window)
340 return NULL;
342 if (!get_ipc_property(window, &xid_confirm) || xid_confirm != xid)
343 return NULL;
345 return window;
348 /* Returns the 'rox_atom' property of 'window' */
349 static gboolean get_ipc_property(GdkWindow *window, Window *r_xid)
351 guchar *data;
352 gint format, length;
353 gboolean retval = FALSE;
355 if (gdk_property_get(window, filer_atom,
356 gdk_x11_xatom_to_atom(XA_WINDOW), 0, 4,
357 FALSE, NULL, &format, &length, &data) && data)
359 if (format == 32 && length == 4)
361 retval = TRUE;
362 *r_xid = *((Window *) data);
364 g_free(data);
367 return retval;
370 static char *read_property(GdkWindow *window, GdkAtom prop, gint *out_length)
372 gint grab_len = 4096;
373 gint length;
374 guchar *data;
375 guchar *retval = NULL;
377 gdk_error_trap_push();
379 while (!retval)
381 if (!(gdk_property_get(window, prop,
382 gdk_x11_xatom_to_atom(XA_STRING), 0, grab_len,
383 FALSE, NULL, NULL,
384 &length, &data) && data))
385 goto out; /* Error? */
387 if (length >= grab_len)
389 /* Didn't get all of it - try again */
390 grab_len <<= 1;
391 g_free(data);
392 continue;
395 data = g_realloc(data, length + 1);
396 data[length] = '\0'; /* libxml seems to need this */
397 *out_length = length;
399 retval = data;
401 out:
403 if (gdk_error_trap_pop() == Success)
404 return retval;
406 g_warning("Error reading %s property!", gdk_atom_name(prop));
408 g_free(retval);
409 return NULL;
412 static gboolean client_event(GtkWidget *window,
413 GdkEventClient *event,
414 gpointer user_data)
416 GdkWindow *src_window;
417 GdkAtom prop;
418 xmlDocPtr reply;
419 guchar *data;
420 gint length;
421 xmlDocPtr doc;
423 if (event->message_type != xsoap)
424 return FALSE;
426 src_window = gdk_window_foreign_new(event->data.l[0]);
427 g_return_val_if_fail(src_window != NULL, FALSE);
428 prop = gdk_x11_xatom_to_atom(event->data.l[1]);
430 data = read_property(src_window, prop, &length);
431 if (!data)
432 return TRUE;
434 doc = xmlParseMemory(g_strndup(data, length), length);
435 g_free(data);
437 reply = run_soap(doc);
438 if (number_of_windows == 0)
439 gtk_main_quit();
441 xmlFreeDoc(doc);
443 if (reply)
445 /* Send reply back... */
446 xmlChar *mem;
447 int size;
449 xmlDocDumpMemory(reply, &mem, &size);
450 g_return_val_if_fail(size > 0, TRUE);
452 gdk_error_trap_push();
453 gdk_property_change(src_window, prop,
454 gdk_x11_xatom_to_atom(XA_STRING), 8,
455 GDK_PROP_MODE_REPLACE, mem, size);
456 g_free(mem);
457 gdk_flush();
458 if (gdk_error_trap_pop() != Success)
459 g_warning("Failed to send SOAP reply!");
461 else
463 gdk_error_trap_push();
464 gdk_property_delete(src_window, prop);
465 gdk_flush();
466 if (gdk_error_trap_pop() != Success)
467 g_warning("Failed to send SOAP reply!");
470 return TRUE;
473 /* Some handy functions for processing SOAP RPC arguments... */
475 /* Returns TRUE, FALSE, or -1 (if argument is unspecified) */
476 static int bool_value(xmlNode *arg)
478 int answer;
479 char *optval;
481 if (!arg)
482 return -1;
484 optval = xmlNodeGetContent(arg);
485 answer = text_to_boolean(optval, -1); /* XXX: Report error? */
486 g_free(optval);
488 return answer;
491 /* Returns the text of this arg as a string, or NULL if the (optional)
492 * argument wasn't supplied. Returns "" if the arg is empty.
493 * g_free() the result.
495 static char *string_value(xmlNode *arg)
497 char *retval;
499 if (!arg)
500 return NULL;
502 retval = xmlNodeGetContent(arg);
504 return retval ? retval : g_strdup("");
507 /* Returns the text of this arg as an int, or the default value if not
508 * supplied or not an int.
510 static int int_value(xmlNode *arg, int def)
512 char *str, *end;
513 int i;
515 if (!arg)
516 return def;
518 str = xmlNodeGetContent(arg);
519 if (!str || !str[0])
520 return def;
522 i = (int) strtol(str, &end, 0);
524 return (end > str) ? i : def;
527 /* Return a list of strings, one for each child node of arg.
528 * g_list_free the list, and g_free each string.
530 static GList *list_value(xmlNode *arg)
532 GList *list = NULL;
533 xmlNode *node;
535 for (node = arg->xmlChildrenNode; node; node = node->next)
537 if (node->type != XML_ELEMENT_NODE)
538 continue;
540 list = g_list_append(list, string_value(node));
543 return list;
546 #define ARG(n) ((xmlNode *) g_list_nth(args, n)->data)
548 /* The RPC handlers all work in the same way -- they get passed a list of
549 * xmlNode arguments from the RPC call and they return the result node, or
550 * NULL if there isn't a result.
553 static xmlNodePtr rpc_Version(GList *args)
555 xmlNodePtr reply;
557 reply = xmlNewNode(NULL, "rox:VersionResponse");
558 xmlNewNs(reply, SOAP_RPC_NS, "soap");
559 xmlNewChild(reply, NULL, "soap:result", VERSION);
561 return reply;
564 /* Args: Path, [Style, Details, Sort, Class] */
565 static xmlNodePtr rpc_OpenDir(GList *args)
567 char *path;
568 char *style, *details, *sort, *class;
569 FilerWindow *fwin;
571 path = string_value(ARG(0));
572 style = string_value(ARG(1));
573 details = string_value(ARG(2));
574 sort = string_value(ARG(3));
575 class = string_value(ARG(4));
577 fwin = filer_opendir(path, NULL, class);
578 g_free(path);
579 g_free(class);
581 if (style)
583 DisplayStyle ds;
585 ds = !g_strcasecmp(style, "Large") ? LARGE_ICONS :
586 !g_strcasecmp(style, "Small") ? SMALL_ICONS :
587 !g_strcasecmp(style, "Huge") ? HUGE_ICONS :
588 !g_strcasecmp(style, "Automatic") ? AUTO_SIZE_ICONS :
589 UNKNOWN_STYLE;
590 if (ds == UNKNOWN_STYLE)
591 g_warning("Unknown style '%s'\n", style);
592 else
593 display_set_layout(fwin, ds, fwin->details_type);
595 g_free(style);
598 if (details)
600 DetailsType dt;
602 dt = !g_strcasecmp(details, "None") ? DETAILS_NONE :
603 !g_strcasecmp(details, "Summary") ? DETAILS_NONE :
604 !g_strcasecmp(details, "Size") ? DETAILS_SIZE :
605 !g_strcasecmp(details, "Type") ? DETAILS_TYPE :
606 !g_strcasecmp(details, "Times") ? DETAILS_TIMES :
607 !g_strcasecmp(details, "Permissions")
608 ? DETAILS_PERMISSIONS :
609 DETAILS_UNKNOWN;
611 if (dt == DETAILS_UNKNOWN)
612 g_warning("Unknown details type '%s'\n", details);
613 else
614 display_set_layout(fwin,
615 fwin->display_style_wanted, dt);
617 g_free(details);
620 if (sort)
622 int (*cmp)(const void *, const void *);
624 cmp = !g_strcasecmp(sort, "Name") ? sort_by_name :
625 !g_strcasecmp(sort, "Type") ? sort_by_type :
626 !g_strcasecmp(sort, "Date") ? sort_by_date :
627 !g_strcasecmp(sort, "Size") ? sort_by_size :
628 NULL;
629 if (!cmp)
630 g_warning("Unknown sorting criteria '%s'\n", sort);
631 else
632 display_set_sort_fn(fwin, cmp);
634 g_free(sort);
637 return NULL;
640 static xmlNodePtr rpc_Run(GList *args)
642 char *path;
644 path = string_value(ARG(0));
645 run_by_path(path);
646 g_free(path);
648 return NULL;
651 static xmlNodePtr rpc_CloseDir(GList *args)
653 char *path;
655 path = string_value(ARG(0));
656 filer_close_recursive(path);
657 g_free(path);
659 return NULL;
662 static xmlNodePtr rpc_Examine(GList *args)
664 char *path;
666 path = string_value(ARG(0));
667 examine(path);
668 g_free(path);
670 return NULL;
673 static xmlNodePtr rpc_Show(GList *args)
675 char *dir, *leaf;
677 dir = string_value(ARG(0));
678 leaf = string_value(ARG(1));
680 /* XXX: Seems silly to join them only to split again later... */
681 open_to_show(make_path(dir, leaf));
683 g_free(dir);
684 g_free(leaf);
686 return NULL;
689 static xmlNodePtr rpc_Pinboard(GList *args)
691 char *name = NULL;
693 name = string_value(ARG(0));
694 pinboard_activate(name);
695 g_free(name);
697 return NULL;
700 /* args = App */
701 static xmlNodePtr rpc_SetBackdropApp(GList *args)
703 char *app;
705 app = string_value(ARG(0));
707 pinboard_set_backdrop_app(app);
709 g_free(app);
711 return NULL;
714 /* args = Path, X, Y, [Label] */
715 static xmlNodePtr rpc_PinboardAdd(GList *args)
717 char *path = NULL;
718 gchar *name;
719 int x, y;
721 path = string_value(ARG(0));
722 x = int_value(ARG(1), 0);
723 y = int_value(ARG(2), 0);
724 name = string_value(ARG(3));
726 pinboard_pin(path, name, x, y, NULL);
728 g_free(path);
730 return NULL;
733 /* Returns PANEL_NUMBER_OF_SIDES if name is invalid */
734 static PanelSide panel_name_to_side(gchar *side)
736 if (strcmp(side, "Top") == 0)
737 return PANEL_TOP;
738 else if (strcmp(side, "Bottom") == 0)
739 return PANEL_BOTTOM;
740 else if (strcmp(side, "Left") == 0)
741 return PANEL_LEFT;
742 else if (strcmp(side, "Right") == 0)
743 return PANEL_RIGHT;
744 else
745 g_warning("Unknown panel side '%s'", side);
746 return PANEL_NUMBER_OF_SIDES;
749 /* args = Side, [Name] */
750 static xmlNodePtr rpc_Panel(GList *args)
752 PanelSide side;
753 char *name, *side_name;
755 side_name = string_value(ARG(0));
756 side = panel_name_to_side(side_name);
757 g_free(side_name);
758 g_return_val_if_fail(side != PANEL_NUMBER_OF_SIDES, NULL);
760 name = string_value(ARG(1));
762 panel_new(name, side);
764 g_free(name);
766 return NULL;
769 /* args = Side, Path, [Label, After] */
770 static xmlNodePtr rpc_PanelAdd(GList *args)
772 PanelSide side;
773 char *path, *side_name, *label;
774 gboolean after = FALSE;
775 int tmp;
777 side_name = string_value(ARG(0));
778 side = panel_name_to_side(side_name);
779 g_free(side_name);
780 g_return_val_if_fail(side != PANEL_NUMBER_OF_SIDES, NULL);
782 path = string_value(ARG(1));
783 label = string_value(ARG(2));
785 tmp = bool_value(ARG(3));
786 after = (tmp == -1) ? FALSE : tmp;
788 panel_add(side, path, label, after);
790 g_free(path);
792 return NULL;
795 static xmlNodePtr rpc_Copy(GList *args)
797 GList *from;
798 char *to;
799 char *leaf;
800 int quiet;
802 from = list_value(ARG(0));
803 to = string_value(ARG(1));
804 leaf = string_value(ARG(2));
805 quiet = bool_value(ARG(3));
807 if (from)
808 action_copy(from, to, leaf, quiet);
810 destroy_glist(&from);
811 g_free(to);
812 g_free(leaf);
814 return NULL;
817 static xmlNodePtr rpc_Move(GList *args)
819 GList *from;
820 char *to;
821 char *leaf;
822 int quiet;
824 from = list_value(ARG(0));
825 to = string_value(ARG(1));
826 leaf = string_value(ARG(2));
827 quiet = bool_value(ARG(3));
829 if (from)
830 action_move(from, to, leaf, quiet);
832 destroy_glist(&from);
833 g_free(to);
834 g_free(leaf);
836 return NULL;
839 static xmlNodePtr rpc_Link(GList *args)
841 GList *from;
842 char *to;
843 char *leaf;
845 from = list_value(ARG(0));
846 to = string_value(ARG(1));
847 leaf = string_value(ARG(2));
849 if (from)
850 action_link(from, to, leaf);
852 destroy_glist(&from);
853 g_free(to);
854 g_free(leaf);
856 return NULL;
859 static xmlNodePtr rpc_FileType(GList *args)
861 MIME_type *type;
862 char *path, *tname;
863 xmlNodePtr reply;
865 path = string_value(ARG(0));
866 type = type_get_type(path);
867 g_free(path);
869 reply = xmlNewNode(NULL, "rox:FileTypeResponse");
870 tname = g_strconcat(type->media_type, "/", type->subtype, NULL);
872 xmlNewNs(reply, SOAP_RPC_NS, "soap");
873 xmlNewChild(reply, NULL, "soap:result", tname);
874 g_free(tname);
876 return reply;
879 static xmlNodePtr rpc_Mount(GList *args)
881 GList *paths;
882 int open_dir, quiet;
884 paths = list_value(ARG(0));
885 open_dir = bool_value(ARG(1));
886 quiet = bool_value(ARG(2));
888 if (open_dir == -1)
889 open_dir = TRUE;
891 if (paths)
892 action_mount(paths, open_dir, quiet);
894 destroy_glist(&paths);
896 return NULL;
899 /* The first time the property changes, do nothing (it's us setting the
900 * property). The second time, get the result.
901 * Don't call this function three times!
903 static void soap_done(GtkWidget *widget, GdkEventProperty *event, gpointer data)
905 static int times_called = 0;
906 GdkAtom prop = (GdkAtom) data;
908 if (prop != event->atom)
909 return;
911 times_called++;
912 g_return_if_fail(times_called < 3);
914 if (times_called == 1)
915 return;
917 /* If we got a reply, display it here */
918 if (event->state == GDK_PROPERTY_NEW_VALUE)
920 gint length;
922 data = read_property(event->window, event->atom, &length);
924 if (data)
925 puts(data);
928 gtk_main_quit();
931 static gboolean too_slow(gpointer data)
933 g_warning("Existing ROX-Filer process is not responding! Try with -n");
934 gtk_main_quit();
936 return 0;
939 /* Send the SOAP message in property 'prop' on 'from' to 'dest' */
940 static void soap_send(GtkWidget *from, GdkAtom prop, GdkWindow *dest)
942 GdkEventClient event;
944 event.data.l[0] = GDK_WINDOW_XWINDOW(from->window);
945 event.data.l[1] = gdk_x11_atom_to_xatom(prop);
946 event.data_format = 32;
947 event.message_type = xsoap;
949 gdk_event_send_client_message((GdkEvent *) &event,
950 GDK_WINDOW_XWINDOW(dest));
952 gtk_timeout_add(10000, too_slow, NULL);
954 gtk_main();
957 /* Lookup this method in rpc_calls and invoke it.
958 * Returns the SOAP reply or fault, or NULL if this method
959 * doesn't return anything.
961 static xmlNodePtr soap_invoke(xmlNode *method)
963 GList *args = NULL;
964 SOAP_call *call;
965 gchar **arg;
966 xmlNodePtr retval = NULL;
967 GHashTable *name_to_node;
968 xmlNode *node;
970 call = g_hash_table_lookup(rpc_calls, method->name);
971 if (!call)
973 xmlNodePtr reply;
974 gchar *err;
976 err = g_strdup_printf(_("Attempt to invoke unknown SOAP "
977 "method '%s'"), method->name);
978 reply = xmlNewNode(NULL, "env:Fault");
979 xmlNewNs(reply, SOAP_RPC_NS, "rpc");
980 xmlNewNs(reply, SOAP_ENV_NS, "env");
981 xmlNewChild(reply, NULL, "faultcode",
982 "rpc:ProcedureNotPresent");
983 xmlNewChild(reply, NULL, "faultstring", err);
984 g_free(err);
985 return reply;
988 name_to_node = g_hash_table_new(g_str_hash, g_str_equal);
989 for (node = method->xmlChildrenNode; node; node = node->next)
991 if (node->type != XML_ELEMENT_NODE)
992 continue;
994 if (node->ns == NULL || strcmp(node->ns->href, ROX_NS) != 0)
995 continue;
997 g_hash_table_insert(name_to_node, (gchar *) node->name, node);
1000 if (call->required_args)
1002 for (arg = call->required_args; *arg; arg++)
1004 node = g_hash_table_lookup(name_to_node, *arg);
1005 if (!node)
1007 g_warning("Missing required argument '%s' "
1008 "in call to method '%s'", *arg,
1009 method->name);
1010 goto out;
1013 args = g_list_append(args, node);
1017 if (call->optional_args)
1019 for (arg = call->optional_args; *arg; arg++)
1020 args = g_list_append(args,
1021 g_hash_table_lookup(name_to_node, *arg));
1024 retval = call->func(args);
1026 out:
1027 g_hash_table_destroy(name_to_node);
1028 g_list_free(args);
1030 return retval;