r4269: Bugfix: New detail crashes cleanice theme. Try another value... (reported
[rox-filer/translations.git] / ROX-Filer / src / remote.c
blob9db528ab139e694092d041859bd1cd869d405f1b
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2005, 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);
96 static xmlNodePtr rpc_Unmount(GList *args);
98 static xmlNodePtr rpc_PanelAdd(GList *args);
99 static xmlNodePtr rpc_PanelRemove(GList *args);
100 static xmlNodePtr rpc_PinboardAdd(GList *args);
101 static xmlNodePtr rpc_PinboardRemove(GList *args);
102 static xmlNodePtr rpc_SetBackdrop(GList *args);
103 static xmlNodePtr rpc_SetBackdropApp(GList *args);
105 /****************************************************************
106 * EXTERNAL INTERFACE *
107 ****************************************************************/
110 /* Try to get an already-running filer to handle things (only if
111 * new_copy is FALSE); TRUE if we succeed.
112 * Create an IPC widget so that future filers can contact us.
114 gboolean remote_init(xmlDocPtr rpc, gboolean new_copy)
116 guchar *unique_id;
117 GdkWindow *existing_ipc_window;
118 GtkWidget *ipc_window;
119 Window xwindow;
121 /* xmlDocDump(stdout, rpc); */
123 rpc_calls = g_hash_table_new(g_str_hash, g_str_equal);
125 soap_register("Version", rpc_Version, NULL, NULL);
127 soap_register("Run", rpc_Run, "Filename", NULL);
128 soap_register("OpenDir", rpc_OpenDir, "Filename",
129 "Style,Details,Sort,Class,ID,Hidden,Filter");
130 soap_register("CloseDir", rpc_CloseDir, "Filename", NULL);
131 soap_register("Examine", rpc_Examine, "Filename", NULL);
132 soap_register("Show", rpc_Show, "Directory,Leafname", NULL);
134 soap_register("Pinboard", rpc_Pinboard, NULL, "Name");
135 soap_register("Panel", rpc_Panel, NULL, "Side,Name");
137 soap_register("FileType", rpc_FileType, "Filename", NULL);
139 soap_register("Copy", rpc_Copy, "From,To", "Leafname,Quiet");
140 soap_register("Move", rpc_Move, "From,To", "Leafname,Quiet");
141 soap_register("Link", rpc_Link, "From,To", "Leafname");
142 soap_register("Mount", rpc_Mount, "MountPoints", "OpenDir,Quiet");
143 soap_register("Unmount", rpc_Unmount, "MountPoints", "Quiet");
145 soap_register("SetBackdrop", rpc_SetBackdrop, "Filename,Style", NULL);
146 soap_register("SetBackdropApp", rpc_SetBackdropApp, "App", NULL);
147 soap_register("PinboardAdd", rpc_PinboardAdd, "Path,X,Y", "Label,Shortcut,Args");
148 soap_register("PinboardRemove", rpc_PinboardRemove, "Path", "Label");
149 soap_register("PanelAdd", rpc_PanelAdd, "Side,Path", "Label,After,Shortcut,Args");
150 soap_register("PanelRemove", rpc_PanelRemove, "Side", "Path,Label");
152 /* Look for a property on the root window giving the IPC window
153 * of an already-running copy of this version of the filer, running
154 * on the same machine and with the same euid.
156 unique_id = g_strdup_printf("_ROX_FILER_%d_%s_%s",
157 (int) euid, VERSION, our_host_name());
158 filer_atom = gdk_atom_intern(unique_id, FALSE);
159 g_free(unique_id);
161 xsoap = gdk_atom_intern("_XSOAP", FALSE);
163 /* If we find a running copy, we'll need a window to put the
164 * SOAP message in before sending.
165 * If there's no running copy, we'll need a window to receive
166 * future SOAP message events.
167 * Either way, we'll need a window for it...
169 ipc_window = gtk_invisible_new();
170 gtk_widget_realize(ipc_window);
172 XGrabServer(GDK_DISPLAY());
174 existing_ipc_window = new_copy ? NULL : get_existing_ipc_window();
175 if (existing_ipc_window)
177 xmlChar *mem;
178 int size;
180 XUngrabServer(GDK_DISPLAY());
182 xmlDocDumpMemory(rpc, &mem, &size);
183 g_return_val_if_fail(size > 0, FALSE);
185 /* Since Gtk might have selected this already, we'd
186 * better do it BEFORE changing the property.
188 gtk_widget_add_events(ipc_window, GDK_PROPERTY_CHANGE_MASK);
189 g_signal_connect(ipc_window, "property-notify-event",
190 G_CALLBACK(soap_done), GINT_TO_POINTER(xsoap));
192 gdk_property_change(ipc_window->window, xsoap,
193 gdk_x11_xatom_to_atom(XA_STRING), 8,
194 GDK_PROP_MODE_REPLACE, mem, size);
195 g_free(mem);
197 soap_send(ipc_window, xsoap, existing_ipc_window);
199 return TRUE;
202 xwindow = GDK_WINDOW_XWINDOW(ipc_window->window);
204 /* Make the IPC window contain a property pointing to
205 * itself - this can then be used to check that it really
206 * is an IPC window.
208 gdk_property_change(ipc_window->window, filer_atom,
209 gdk_x11_xatom_to_atom(XA_WINDOW), 32,
210 GDK_PROP_MODE_REPLACE,
211 (void *) &xwindow, 1);
213 /* Get notified when we get a message */
214 g_signal_connect(ipc_window, "client-event",
215 G_CALLBACK(client_event), NULL);
217 /* Make the root window contain a pointer to the IPC window */
218 gdk_property_change(gdk_get_default_root_window(), filer_atom,
219 gdk_x11_xatom_to_atom(XA_WINDOW), 32,
220 GDK_PROP_MODE_REPLACE,
221 (void *) &xwindow, 1);
223 XUngrabServer(GDK_DISPLAY());
225 /* Also have a property without the version number, for programs
226 * that are happy to talk to any version of the filer.
228 unique_id = g_strdup_printf("_ROX_FILER_%d_%s",
229 (int) euid, our_host_name());
230 filer_atom_any = gdk_atom_intern(unique_id, FALSE);
231 g_free(unique_id);
232 /* On the IPC window... */
233 gdk_property_change(ipc_window->window, filer_atom_any,
234 gdk_x11_xatom_to_atom(XA_WINDOW), 32,
235 GDK_PROP_MODE_REPLACE,
236 (void *) &xwindow, 1);
237 /* ... and on the root */
238 gdk_property_change(gdk_get_default_root_window(), filer_atom_any,
239 gdk_x11_xatom_to_atom(XA_WINDOW), 32,
240 GDK_PROP_MODE_REPLACE,
241 (void *) &xwindow, 1);
243 return FALSE;
246 /* Executes the RPC call(s) in the given SOAP message and returns
247 * the reply.
249 xmlDocPtr run_soap(xmlDocPtr soap)
251 xmlNodePtr body, node, rep_body, reply;
252 xmlDocPtr rep_doc = NULL;
254 g_return_val_if_fail(soap != NULL, NULL);
256 /* Make sure we don't quit before doing the whole list
257 * (there's a command that closes windows)
259 number_of_windows++;
261 node = xmlDocGetRootElement(soap);
262 if (!node->ns)
263 goto bad_soap;
265 if (strcmp(node->ns->href, SOAP_ENV_NS) != 0 &&
266 strcmp(node->ns->href, SOAP_ENV_NS_OLD) != 0)
267 goto bad_soap;
269 body = get_subnode(node, SOAP_ENV_NS, "Body");
270 if (!body)
271 body = get_subnode(node, SOAP_ENV_NS_OLD, "Body");
272 if (!body)
273 goto bad_soap;
275 for (node = body->xmlChildrenNode; node; node = node->next)
277 if (node->type != XML_ELEMENT_NODE)
278 continue;
280 if (node->ns == NULL || strcmp(node->ns->href, ROX_NS) != 0)
282 g_warning("Unknown namespace %s",
283 node->ns ? node->ns->href
284 : (xmlChar *) "(none)");
285 continue;
288 reply = soap_invoke(node);
290 if (reply)
292 if (!rep_doc)
293 rep_doc = soap_new(&rep_body);
294 xmlAddChild(rep_body, reply);
298 goto out;
300 bad_soap:
301 g_warning("Bad SOAP message received!");
303 out:
304 number_of_windows--;
306 return rep_doc;
310 /****************************************************************
311 * INTERNAL FUNCTIONS *
312 ****************************************************************/
314 /* Register this function to handle SOAP calls to method 'name'.
315 * 'req' and 'opt' are comma separated lists of argument names, in the order
316 * that they are to be delivered to the function.
317 * NULL will be passed for an opt argument if not given.
318 * Otherwise, the parameter is the xmlNode from the call.
320 static void soap_register(char *name, SOAP_func func, char *req, char *opt)
322 SOAP_call *call;
324 call = g_new(SOAP_call, 1);
325 call->func = func;
326 call->required_args = req ? g_strsplit(req, ",", 0) : NULL;
327 call->optional_args = opt ? g_strsplit(opt, ",", 0) : NULL;
329 g_hash_table_insert(rpc_calls, g_strdup(name), call);
332 /* Get the remote IPC window of the already-running filer if there
333 * is one.
335 static GdkWindow *get_existing_ipc_window(void)
337 Window xid, xid_confirm;
338 GdkWindow *window;
340 if (!get_ipc_property(gdk_get_default_root_window(), &xid))
341 return NULL;
343 if (gdk_window_lookup(xid))
344 return NULL; /* Stale handle which we now own */
346 window = gdk_window_foreign_new(xid);
347 if (!window)
348 return NULL;
350 if (!get_ipc_property(window, &xid_confirm) || xid_confirm != xid)
351 return NULL;
353 return window;
356 /* Returns the 'rox_atom' property of 'window' */
357 static gboolean get_ipc_property(GdkWindow *window, Window *r_xid)
359 guchar *data;
360 gint format, length;
361 gboolean retval = FALSE;
363 if (gdk_property_get(window, filer_atom,
364 gdk_x11_xatom_to_atom(XA_WINDOW), 0, 4,
365 FALSE, NULL, &format, &length, &data) && data)
367 /* Note: values with format=32 are stored as longs client-side,
368 * which may be more than 32 bits on some systems.
370 if (format == 32 && length == sizeof(long))
372 long windowID = *((long *) data);
373 retval = TRUE;
374 *r_xid = (Window) windowID;
376 g_free(data);
379 return retval;
382 static char *read_property(GdkWindow *window, GdkAtom prop, gint *out_length)
384 gint grab_len = 4096;
385 gint length;
386 guchar *data;
387 guchar *retval = NULL;
389 gdk_error_trap_push();
391 while (!retval)
393 if (!(gdk_property_get(window, prop,
394 gdk_x11_xatom_to_atom(XA_STRING), 0, grab_len,
395 FALSE, NULL, NULL,
396 &length, &data) && data))
397 goto out; /* Error? */
399 if (length >= grab_len)
401 /* Didn't get all of it - try again */
402 grab_len <<= 1;
403 g_free(data);
404 continue;
407 data = g_realloc(data, length + 1);
408 data[length] = '\0'; /* libxml seems to need this */
409 *out_length = length;
411 retval = data;
413 out:
415 if (gdk_error_trap_pop() == Success)
416 return retval;
418 g_warning("Error reading %s property!", gdk_atom_name(prop));
420 g_free(retval);
421 return NULL;
424 static gboolean client_event(GtkWidget *window,
425 GdkEventClient *event,
426 gpointer user_data)
428 GdkWindow *src_window;
429 GdkAtom prop;
430 xmlDocPtr reply;
431 guchar *data;
432 gint length;
433 xmlDocPtr doc;
435 if (event->message_type != xsoap)
436 return FALSE;
438 src_window = gdk_window_foreign_new(event->data.l[0]);
439 if (!src_window)
441 g_warning("SOAP message sender window was destroyed before I \n"
442 "could read it.");
443 return FALSE;
445 prop = gdk_x11_xatom_to_atom(event->data.l[1]);
447 data = read_property(src_window, prop, &length);
448 if (!data)
449 return TRUE;
451 doc = xmlParseMemory(g_strndup(data, length), length);
452 g_free(data);
454 reply = run_soap(doc);
455 if (number_of_windows == 0)
456 gtk_main_quit();
458 xmlFreeDoc(doc);
460 if (reply)
462 /* Send reply back... */
463 xmlChar *mem;
464 int size;
466 xmlDocDumpMemory(reply, &mem, &size);
467 g_return_val_if_fail(size > 0, TRUE);
469 gdk_error_trap_push();
470 gdk_property_change(src_window, prop,
471 gdk_x11_xatom_to_atom(XA_STRING), 8,
472 GDK_PROP_MODE_REPLACE, mem, size);
473 g_free(mem);
474 gdk_flush();
475 if (gdk_error_trap_pop() != Success)
476 g_warning("Failed to send SOAP reply!");
478 else
480 gdk_error_trap_push();
481 gdk_property_delete(src_window, prop);
482 gdk_flush();
483 if (gdk_error_trap_pop() != Success)
484 g_warning("Failed to send SOAP reply!");
487 return TRUE;
490 /* Some handy functions for processing SOAP RPC arguments... */
492 /* Returns TRUE, FALSE, or -1 (if argument is unspecified) */
493 static int bool_value(xmlNode *arg)
495 int answer;
496 char *optval;
498 if (!arg)
499 return -1;
501 optval = xmlNodeGetContent(arg);
502 answer = text_to_boolean(optval, -1); /* XXX: Report error? */
503 g_free(optval);
505 return answer;
508 /* Returns the text of this arg as a string, or NULL if the (optional)
509 * argument wasn't supplied. Returns "" if the arg is empty.
510 * g_free() the result.
512 static char *string_value(xmlNode *arg)
514 char *retval;
516 if (!arg)
517 return NULL;
519 retval = xmlNodeGetContent(arg);
521 return retval ? retval : g_strdup("");
524 /* Returns the text of this arg as an int, or the default value if not
525 * supplied or not an int.
527 static int int_value(xmlNode *arg, int def)
529 char *str, *end;
530 int i;
532 if (!arg)
533 return def;
535 str = xmlNodeGetContent(arg);
536 if (!str || !str[0])
537 return def;
539 i = (int) strtol(str, &end, 0);
541 return (end > str) ? i : def;
544 /* Return a list of strings, one for each child node of arg.
545 * g_list_free the list, and g_free each string.
547 static GList *list_value(xmlNode *arg)
549 GList *list = NULL;
550 xmlNode *node;
552 for (node = arg->xmlChildrenNode; node; node = node->next)
554 if (node->type != XML_ELEMENT_NODE)
555 continue;
557 list = g_list_append(list, string_value(node));
560 return list;
563 #define ARG(n) ((xmlNode *) g_list_nth(args, n)->data)
565 /* The RPC handlers all work in the same way -- they get passed a list of
566 * xmlNode arguments from the RPC call and they return the result node, or
567 * NULL if there isn't a result.
570 static xmlNodePtr rpc_Version(GList *args)
572 xmlNodePtr reply;
574 reply = xmlNewNode(NULL, "rox:VersionResponse");
575 xmlNewNs(reply, SOAP_RPC_NS, "soap");
576 xmlNewTextChild(reply, NULL, "soap:result", VERSION);
578 return reply;
581 /* Args: Path, [Style, Details, Sort, Class, Window] */
582 static xmlNodePtr rpc_OpenDir(GList *args)
584 char *path;
585 char *style, *details, *sort, *class, *window;
586 int hidden=FALSE;
587 char *filter_string=NULL;
588 FilerWindow *fwin = NULL;
590 path = string_value(ARG(0));
591 class = string_value(ARG(4));
592 window = string_value(ARG(5));
594 if (window)
595 fwin = filer_get_by_id(window);
597 if (!fwin)
599 fwin = filer_opendir(path, NULL, class);
600 if (window)
601 filer_set_id(fwin, window);
603 else
604 filer_change_to(fwin, path, NULL);
606 g_free(path);
607 g_free(class);
608 g_free(window);
609 if (!fwin)
610 return NULL;
612 style = string_value(ARG(1));
613 details = string_value(ARG(2));
614 sort = string_value(ARG(3));
615 hidden = bool_value(ARG(6));
616 filter_string = string_value(ARG(7));
618 if (style)
620 DisplayStyle ds;
622 ds = !g_strcasecmp(style, "Large") ? LARGE_ICONS :
623 !g_strcasecmp(style, "Small") ? SMALL_ICONS :
624 !g_strcasecmp(style, "Huge") ? HUGE_ICONS :
625 !g_strcasecmp(style, "Automatic") ? AUTO_SIZE_ICONS :
626 UNKNOWN_STYLE;
627 if (ds == UNKNOWN_STYLE)
628 delayed_error(_("Unknown style '%s'"), style);
629 else
630 display_set_layout(fwin, ds, fwin->details_type, TRUE);
632 g_free(style);
635 if (details)
637 DetailsType dt;
638 ViewType view_type;
640 dt = !g_strcasecmp(details, "None") ? DETAILS_NONE :
641 !g_strcasecmp(details, "ListView") ? DETAILS_NONE :
642 !g_strcasecmp(details, "Size") ? DETAILS_SIZE :
643 !g_strcasecmp(details, "Type") ? DETAILS_TYPE :
644 !g_strcasecmp(details, "Times") ? DETAILS_TIMES :
645 !g_strcasecmp(details, "Permissions")
646 ? DETAILS_PERMISSIONS :
647 DETAILS_UNKNOWN;
649 if (dt == DETAILS_UNKNOWN)
650 delayed_error(_("Unknown details type '%s'"), details);
651 else
652 display_set_layout(fwin, fwin->display_style_wanted,
653 dt, TRUE);
655 if (g_strcasecmp(details, "ListView") == 0)
656 view_type = VIEW_TYPE_DETAILS;
657 else
658 view_type = VIEW_TYPE_COLLECTION;
660 if (view_type != fwin->view_type)
661 filer_set_view_type(fwin, view_type);
663 g_free(details);
666 if (sort)
668 SortType type;
670 type = !g_strcasecmp(sort, "Name") ? SORT_NAME :
671 !g_strcasecmp(sort, "Type") ? SORT_TYPE :
672 !g_strcasecmp(sort, "Date") ? SORT_DATE :
673 !g_strcasecmp(sort, "Size") ? SORT_SIZE :
674 !g_strcasecmp(sort, "Owner") ? SORT_OWNER :
675 !g_strcasecmp(sort, "Group") ? SORT_GROUP :
677 if (type == -1)
678 delayed_error(_("Unknown sorting type '%s'"), sort);
679 else
680 display_set_sort_type(fwin, type, GTK_SORT_ASCENDING);
682 g_free(sort);
684 if (hidden!=-1)
686 display_set_hidden(fwin, hidden);
689 if (filter_string)
690 display_set_filter(fwin, FILER_SHOW_GLOB, filter_string);
691 else
692 display_set_filter(fwin, FILER_SHOW_ALL, NULL);
694 return NULL;
697 static xmlNodePtr rpc_Run(GList *args)
699 char *path;
701 path = string_value(ARG(0));
702 run_by_path(path);
703 g_free(path);
705 return NULL;
708 static xmlNodePtr rpc_CloseDir(GList *args)
710 char *path;
712 path = string_value(ARG(0));
713 filer_close_recursive(path);
714 g_free(path);
716 return NULL;
719 static xmlNodePtr rpc_Examine(GList *args)
721 char *path;
723 path = string_value(ARG(0));
724 examine(path);
725 g_free(path);
727 return NULL;
730 static xmlNodePtr rpc_Show(GList *args)
732 char *dir, *leaf;
734 dir = string_value(ARG(0));
735 leaf = string_value(ARG(1));
737 /* XXX: Seems silly to join them only to split again later... */
738 open_to_show(make_path(dir, leaf));
740 g_free(dir);
741 g_free(leaf);
743 return NULL;
746 static xmlNodePtr rpc_Pinboard(GList *args)
748 char *name = NULL;
750 name = string_value(ARG(0));
751 pinboard_activate(name);
752 g_free(name);
754 return NULL;
757 /* args = Filename, Style */
758 static xmlNodePtr rpc_SetBackdrop(GList *args)
760 char *file;
761 char *style;
762 BackdropStyle s;
764 file = string_value(ARG(0));
765 style = string_value(ARG(1));
767 s = !g_strcasecmp(style, "Tile") ? BACKDROP_TILE :
768 !g_strcasecmp(style, "Scale") ? BACKDROP_SCALE :
769 !g_strcasecmp(style, "Stretch") ? BACKDROP_STRETCH :
770 !g_strcasecmp(style, "Centre") ? BACKDROP_CENTRE :
771 BACKDROP_NONE;
773 if (s == BACKDROP_NONE)
774 g_warning("Invalid style '%s' for backdrop", style);
775 else
776 pinboard_set_backdrop(file, s);
778 g_free(file);
779 g_free(style);
781 return NULL;
784 /* args = App */
785 static xmlNodePtr rpc_SetBackdropApp(GList *args)
787 char *app;
789 app = string_value(ARG(0));
791 pinboard_set_backdrop_app(app);
793 g_free(app);
795 return NULL;
798 /* args = Path, X, Y, [Label] */
799 static xmlNodePtr rpc_PinboardAdd(GList *args)
801 char *path = NULL;
802 gchar *name, *shortcut, *xargs;
803 int x, y;
805 path = string_value(ARG(0));
806 x = int_value(ARG(1), 0);
807 y = int_value(ARG(2), 0);
808 name = string_value(ARG(3));
809 shortcut = string_value(ARG(4));
810 xargs = string_value(ARG(5));
812 pinboard_pin_with_args(path, name, x, y, shortcut, xargs);
814 g_free(path);
815 g_free(name);
816 g_free(shortcut);
817 g_free(xargs);
819 return NULL;
822 /* args = Path, [Label] */
823 static xmlNodePtr rpc_PinboardRemove(GList *args)
825 char *path = NULL;
826 gchar *name;
828 path = string_value(ARG(0));
829 name = string_value(ARG(1));
831 pinboard_remove(path, name);
833 g_free(path);
834 if(name)
835 g_free(name);
837 return NULL;
840 /* args = Side, [Name] */
841 static xmlNodePtr rpc_Panel(GList *args)
843 PanelSide side;
844 char *name, *side_name;
846 side_name = string_value(ARG(0));
847 if (side_name)
849 side = panel_name_to_side(side_name);
850 g_free(side_name);
851 g_return_val_if_fail(side != PANEL_NUMBER_OF_SIDES, NULL);
853 else
854 side = PANEL_DEFAULT_SIDE;
856 name = string_value(ARG(1));
858 panel_new(name, side);
860 g_free(name);
862 return NULL;
865 /* args = Side, Path, [Label, After] */
866 static xmlNodePtr rpc_PanelAdd(GList *args)
868 PanelSide side;
869 char *path, *side_name, *label, *shortcut, *arg;
870 gboolean after = FALSE;
871 int tmp;
873 side_name = string_value(ARG(0));
874 side = panel_name_to_side(side_name);
875 g_free(side_name);
876 g_return_val_if_fail(side != PANEL_NUMBER_OF_SIDES, NULL);
878 path = string_value(ARG(1));
879 label = string_value(ARG(2));
881 tmp = bool_value(ARG(3));
882 after = (tmp == -1) ? FALSE : tmp;
884 shortcut = string_value(ARG(4));
885 arg = string_value(ARG(5));
887 panel_add(side, path, label, after, shortcut, arg);
889 g_free(path);
890 g_free(label);
891 g_free(shortcut);
892 g_free(arg);
894 return NULL;
897 static xmlNodePtr rpc_PanelRemove(GList *args)
899 PanelSide side;
900 char *path, *side_name, *label;
902 side_name = string_value(ARG(0));
903 side = panel_name_to_side(side_name);
904 g_free(side_name);
905 g_return_val_if_fail(side != PANEL_NUMBER_OF_SIDES, NULL);
907 path = string_value(ARG(1));
908 label = string_value(ARG(2));
910 if (path || label)
911 panel_remove_item(side, path, label);
912 else
913 g_warning("Must specify either path or label");
915 g_free(path);
916 g_free(label);
918 return NULL;
921 static xmlNodePtr rpc_Copy(GList *args)
923 GList *from;
924 char *to;
925 char *leaf;
926 int quiet;
928 from = list_value(ARG(0));
929 to = string_value(ARG(1));
930 leaf = string_value(ARG(2));
931 quiet = bool_value(ARG(3));
933 if (from)
934 action_copy(from, to, leaf, quiet);
935 else
936 g_warning("No files in SOAP request list");
938 destroy_glist(&from);
939 g_free(to);
940 g_free(leaf);
942 return NULL;
945 static xmlNodePtr rpc_Move(GList *args)
947 GList *from;
948 char *to;
949 char *leaf;
950 int quiet;
952 from = list_value(ARG(0));
953 to = string_value(ARG(1));
954 leaf = string_value(ARG(2));
955 quiet = bool_value(ARG(3));
957 if (from)
958 action_move(from, to, leaf, quiet);
959 else
960 g_warning("No files in SOAP request list");
962 destroy_glist(&from);
963 g_free(to);
964 g_free(leaf);
966 return NULL;
969 static xmlNodePtr rpc_Link(GList *args)
971 GList *from;
972 char *to;
973 char *leaf;
975 from = list_value(ARG(0));
976 to = string_value(ARG(1));
977 leaf = string_value(ARG(2));
979 if (from)
980 action_link(from, to, leaf, TRUE);
981 else
982 g_warning("No files in SOAP request list");
984 destroy_glist(&from);
985 g_free(to);
986 g_free(leaf);
988 return NULL;
991 static xmlNodePtr rpc_FileType(GList *args)
993 MIME_type *type;
994 char *path, *tname;
995 xmlNodePtr reply;
997 path = string_value(ARG(0));
998 type = type_get_type(path);
999 g_free(path);
1001 reply = xmlNewNode(NULL, "rox:FileTypeResponse");
1002 tname = g_strconcat(type->media_type, "/", type->subtype, NULL);
1004 xmlNewNs(reply, SOAP_RPC_NS, "soap");
1005 xmlNewTextChild(reply, NULL, "soap:result", tname);
1006 g_free(tname);
1008 return reply;
1011 static xmlNodePtr rpc_Mount(GList *args)
1013 GList *paths;
1014 int open_dir, quiet;
1016 paths = list_value(ARG(0));
1017 open_dir = bool_value(ARG(1));
1018 quiet = bool_value(ARG(2));
1020 if (open_dir == -1)
1021 open_dir = TRUE;
1023 if (paths)
1024 action_mount(paths, open_dir, TRUE, quiet);
1026 destroy_glist(&paths);
1028 return NULL;
1031 static xmlNodePtr rpc_Unmount(GList *args)
1033 GList *paths;
1034 int quiet;
1036 paths = list_value(ARG(0));
1037 quiet = bool_value(ARG(1));
1039 if (paths)
1040 action_mount(paths, FALSE, FALSE, quiet);
1042 destroy_glist(&paths);
1044 return NULL;
1047 /* The first time the property changes, do nothing (it's us setting the
1048 * property). The second time, get the result.
1049 * Don't call this function three times!
1051 static void soap_done(GtkWidget *widget, GdkEventProperty *event, gpointer data)
1053 static int times_called = 0;
1054 GdkAtom prop = (GdkAtom) data;
1056 if (prop != event->atom)
1057 return;
1059 times_called++;
1060 g_return_if_fail(times_called < 3);
1062 if (times_called == 1)
1063 return;
1065 /* If we got a reply, display it here */
1066 if (event->state == GDK_PROPERTY_NEW_VALUE)
1068 gint length;
1070 data = read_property(event->window, event->atom, &length);
1072 if (data)
1073 puts(data);
1076 gtk_main_quit();
1079 static gboolean too_slow(gpointer data)
1081 g_warning("Existing ROX-Filer process is not responding! Try with -n");
1082 gtk_main_quit();
1084 return 0;
1087 /* Send the SOAP message in property 'prop' on 'from' to 'dest' */
1088 static void soap_send(GtkWidget *from, GdkAtom prop, GdkWindow *dest)
1090 GdkEventClient event;
1092 event.data.l[0] = GDK_WINDOW_XWINDOW(from->window);
1093 event.data.l[1] = gdk_x11_atom_to_xatom(prop);
1094 event.data_format = 32;
1095 event.message_type = xsoap;
1097 gdk_event_send_client_message((GdkEvent *) &event,
1098 GDK_WINDOW_XWINDOW(dest));
1100 g_timeout_add(10000, too_slow, NULL);
1102 gtk_main();
1105 /* Lookup this method in rpc_calls and invoke it.
1106 * Returns the SOAP reply or fault, or NULL if this method
1107 * doesn't return anything.
1109 static xmlNodePtr soap_invoke(xmlNode *method)
1111 GList *args = NULL;
1112 SOAP_call *call;
1113 gchar **arg;
1114 xmlNodePtr retval = NULL;
1115 GHashTable *name_to_node;
1116 xmlNode *node;
1118 call = g_hash_table_lookup(rpc_calls, method->name);
1119 if (!call)
1121 xmlNodePtr reply;
1122 gchar *err;
1124 err = g_strdup_printf(_("Attempt to invoke unknown SOAP "
1125 "method '%s'"), method->name);
1126 reply = xmlNewNode(NULL, "env:Fault");
1127 xmlNewNs(reply, SOAP_RPC_NS, "rpc");
1128 xmlNewNs(reply, SOAP_ENV_NS, "env");
1129 xmlNewTextChild(reply, NULL, "faultcode",
1130 "rpc:ProcedureNotPresent");
1131 xmlNewTextChild(reply, NULL, "faultstring", err);
1132 g_free(err);
1133 return reply;
1136 name_to_node = g_hash_table_new(g_str_hash, g_str_equal);
1137 for (node = method->xmlChildrenNode; node; node = node->next)
1139 if (node->type != XML_ELEMENT_NODE)
1140 continue;
1142 if (node->ns == NULL || strcmp(node->ns->href, ROX_NS) != 0)
1143 continue;
1145 g_hash_table_insert(name_to_node, (gchar *) node->name, node);
1148 if (call->required_args)
1150 for (arg = call->required_args; *arg; arg++)
1152 node = g_hash_table_lookup(name_to_node, *arg);
1153 if (!node)
1155 g_warning("Missing required argument '%s' "
1156 "in call to method '%s'", *arg,
1157 method->name);
1158 goto out;
1161 args = g_list_append(args, node);
1165 if (call->optional_args)
1167 for (arg = call->optional_args; *arg; arg++)
1168 args = g_list_append(args,
1169 g_hash_table_lookup(name_to_node, *arg));
1172 retval = call->func(args);
1174 out:
1175 g_hash_table_destroy(name_to_node);
1176 g_list_free(args);
1178 return retval;