Converted README to markdown
[rox-filer.git] / ROX-Filer / src / remote.c
blob8351e5d2c97225163f16e56b8a154f0a8ff6c8d7
1 /*
2 * ROX-Filer, filer for the ROX desktop project
3 * Copyright (C) 2006, Thomas Leonard and others (see changelog for details).
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
17 * Place, Suite 330, Boston, MA 02111-1307 USA
20 /* This code is used to communicate between two copies of the filer:
21 * If the filer is run and the same version of the filer is already
22 * running on the same machine then the new copy simply asks the old
23 * one deal with it and quits.
26 #include "config.h"
28 #include <string.h>
30 #include <gdk/gdkx.h>
31 #include <X11/X.h>
32 #include <X11/Xatom.h>
33 #include <gtk/gtk.h>
34 #include <gtk/gtkinvisible.h>
35 #include <libxml/parser.h>
37 #include "global.h"
39 #include "main.h"
40 #include "support.h"
41 #include "gui_support.h"
42 #include "run.h"
43 #include "remote.h"
44 #include "filer.h"
45 #include "pinboard.h"
46 #include "panel.h"
47 #include "action.h"
48 #include "type.h"
49 #include "display.h"
50 #include "xml.h"
51 #include "diritem.h"
52 #include "usericons.h"
54 static GdkAtom filer_atom; /* _ROX_FILER_EUID_VERSION_HOST */
55 static GdkAtom filer_atom_any; /* _ROX_FILER_EUID_HOST */
56 static GdkAtom xsoap; /* _XSOAP */
58 typedef struct _SOAP_call SOAP_call;
59 typedef xmlNodePtr (*SOAP_func)(GList *args);
61 struct _SOAP_call {
62 SOAP_func func;
63 gchar **required_args;
64 gchar **optional_args;
67 static GHashTable *rpc_calls = NULL; /* MethodName -> Function */
69 /* Static prototypes */
70 static GdkWindow *get_existing_ipc_window(void);
71 static gboolean get_ipc_property(GdkWindow *window, Window *r_xid);
72 static void soap_send(GtkWidget *from, GdkAtom prop, GdkWindow *dest);
73 static gboolean client_event(GtkWidget *window,
74 GdkEventClient *event,
75 gpointer data);
76 static void soap_done(GtkWidget *widget,
77 GdkEventProperty *event,
78 gpointer data);
79 static void soap_register(char *name, SOAP_func func, char *req, char *opt);
80 static xmlNodePtr soap_invoke(xmlNode *method);
82 static xmlNodePtr rpc_Version(GList *args);
83 static xmlNodePtr rpc_OpenDir(GList *args);
84 static xmlNodePtr rpc_CloseDir(GList *args);
85 static xmlNodePtr rpc_Examine(GList *args);
86 static xmlNodePtr rpc_Show(GList *args);
87 static xmlNodePtr rpc_Pinboard(GList *args);
88 static xmlNodePtr rpc_Panel(GList *args);
89 static xmlNodePtr rpc_Run(GList *args);
90 static xmlNodePtr rpc_RunURI(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 static xmlNodePtr rpc_SetIcon(GList *args);
106 static xmlNodePtr rpc_UnsetIcon(GList *args);
108 /****************************************************************
109 * EXTERNAL INTERFACE *
110 ****************************************************************/
113 /* Try to get an already-running filer to handle things (only if
114 * new_copy is FALSE); TRUE if we succeed.
115 * Create an IPC widget so that future filers can contact us.
117 gboolean remote_init(xmlDocPtr rpc, gboolean new_copy)
119 guchar *unique_id;
120 GdkWindow *existing_ipc_window;
121 GtkWidget *ipc_window;
122 Window xwindow;
124 /* xmlDocDump(stdout, rpc); */
126 rpc_calls = g_hash_table_new(g_str_hash, g_str_equal);
128 soap_register("Version", rpc_Version, NULL, NULL);
130 soap_register("Run", rpc_Run, "Filename", NULL);
131 soap_register("OpenDir", rpc_OpenDir, "Filename",
132 "Style,Details,Sort,Class,ID,Hidden,Filter");
133 soap_register("CloseDir", rpc_CloseDir, "Filename", NULL);
134 soap_register("Examine", rpc_Examine, "Filename", NULL);
135 soap_register("Show", rpc_Show, "Directory,Leafname", NULL);
136 soap_register("RunURI", rpc_RunURI, "URI", NULL);
138 soap_register("Pinboard", rpc_Pinboard, NULL, "Name");
139 soap_register("Panel", rpc_Panel, NULL, "Side,Name");
141 soap_register("FileType", rpc_FileType, "Filename", NULL);
143 soap_register("Copy", rpc_Copy, "From,To", "Leafname,Quiet");
144 soap_register("Move", rpc_Move, "From,To", "Leafname,Quiet");
145 soap_register("Link", rpc_Link, "From,To", "Leafname");
146 soap_register("Mount", rpc_Mount, "MountPoints", "OpenDir,Quiet");
147 soap_register("Unmount", rpc_Unmount, "MountPoints", "Quiet");
149 soap_register("SetBackdrop", rpc_SetBackdrop, "Filename,Style", NULL);
150 soap_register("SetBackdropApp", rpc_SetBackdropApp, "App", NULL);
151 soap_register("PinboardAdd", rpc_PinboardAdd, "Path", "X,Y,Label,Shortcut,Args,Locked,Update");
152 soap_register("PinboardRemove", rpc_PinboardRemove, "Path", "Label");
153 soap_register("PanelAdd", rpc_PanelAdd, "Side,Path", "Label,After,Shortcut,Args,Locked");
154 soap_register("PanelRemove", rpc_PanelRemove, "Side", "Path,Label");
155 soap_register("SetIcon", rpc_SetIcon, "Path,Icon", NULL);
156 soap_register("UnsetIcon", rpc_UnsetIcon, "Path", NULL);
158 /* Look for a property on the root window giving the IPC window
159 * of an already-running copy of this version of the filer, running
160 * on the same machine and with the same euid.
162 unique_id = g_strdup_printf("_ROX_FILER_%d_%s_%s",
163 (int) euid, VERSION, our_host_name());
164 filer_atom = gdk_atom_intern(unique_id, FALSE);
165 g_free(unique_id);
167 xsoap = gdk_atom_intern("_XSOAP", FALSE);
169 /* If we find a running copy, we'll need a window to put the
170 * SOAP message in before sending.
171 * If there's no running copy, we'll need a window to receive
172 * future SOAP message events.
173 * Either way, we'll need a window for it...
175 ipc_window = gtk_invisible_new();
176 gtk_widget_realize(ipc_window);
178 XGrabServer(GDK_DISPLAY());
180 existing_ipc_window = new_copy ? NULL : get_existing_ipc_window();
181 if (existing_ipc_window)
183 xmlChar *mem;
184 int size;
186 XUngrabServer(GDK_DISPLAY());
188 xmlDocDumpMemory(rpc, &mem, &size);
189 g_return_val_if_fail(size > 0, FALSE);
191 /* Since Gtk might have selected this already, we'd
192 * better do it BEFORE changing the property.
194 gtk_widget_add_events(ipc_window, GDK_PROPERTY_CHANGE_MASK);
195 g_signal_connect(ipc_window, "property-notify-event",
196 G_CALLBACK(soap_done), GINT_TO_POINTER(xsoap));
198 gdk_property_change(ipc_window->window, xsoap,
199 gdk_x11_xatom_to_atom(XA_STRING), 8,
200 GDK_PROP_MODE_REPLACE, mem, size);
201 g_free(mem);
203 soap_send(ipc_window, xsoap, existing_ipc_window);
205 return TRUE;
208 xwindow = GDK_WINDOW_XWINDOW(ipc_window->window);
210 /* Make the IPC window contain a property pointing to
211 * itself - this can then be used to check that it really
212 * is an IPC window.
214 gdk_property_change(ipc_window->window, filer_atom,
215 gdk_x11_xatom_to_atom(XA_WINDOW), 32,
216 GDK_PROP_MODE_REPLACE,
217 (void *) &xwindow, 1);
219 /* Get notified when we get a message */
220 g_signal_connect(ipc_window, "client-event",
221 G_CALLBACK(client_event), NULL);
223 /* Make the root window contain a pointer to the IPC window */
224 gdk_property_change(gdk_get_default_root_window(), filer_atom,
225 gdk_x11_xatom_to_atom(XA_WINDOW), 32,
226 GDK_PROP_MODE_REPLACE,
227 (void *) &xwindow, 1);
229 XUngrabServer(GDK_DISPLAY());
231 /* Also have a property without the version number, for programs
232 * that are happy to talk to any version of the filer.
234 unique_id = g_strdup_printf("_ROX_FILER_%d_%s",
235 (int) euid, our_host_name());
236 filer_atom_any = gdk_atom_intern(unique_id, FALSE);
237 g_free(unique_id);
238 /* On the IPC window... */
239 gdk_property_change(ipc_window->window, filer_atom_any,
240 gdk_x11_xatom_to_atom(XA_WINDOW), 32,
241 GDK_PROP_MODE_REPLACE,
242 (void *) &xwindow, 1);
243 /* ... and on the root */
244 gdk_property_change(gdk_get_default_root_window(), filer_atom_any,
245 gdk_x11_xatom_to_atom(XA_WINDOW), 32,
246 GDK_PROP_MODE_REPLACE,
247 (void *) &xwindow, 1);
249 return FALSE;
252 /* Executes the RPC call(s) in the given SOAP message and returns
253 * the reply.
255 xmlDocPtr run_soap(xmlDocPtr soap)
257 xmlNodePtr body, node, rep_body, reply;
258 xmlDocPtr rep_doc = NULL;
260 g_return_val_if_fail(soap != NULL, NULL);
262 /* Make sure we don't quit before doing the whole list
263 * (there's a command that closes windows)
265 number_of_windows++;
267 node = xmlDocGetRootElement(soap);
268 if (!node->ns)
269 goto bad_soap;
271 if (strcmp(node->ns->href, SOAP_ENV_NS) != 0 &&
272 strcmp(node->ns->href, SOAP_ENV_NS_OLD) != 0)
273 goto bad_soap;
275 body = get_subnode(node, SOAP_ENV_NS, "Body");
276 if (!body)
277 body = get_subnode(node, SOAP_ENV_NS_OLD, "Body");
278 if (!body)
279 goto bad_soap;
281 for (node = body->xmlChildrenNode; node; node = node->next)
283 if (node->type != XML_ELEMENT_NODE)
284 continue;
286 if (node->ns == NULL || strcmp(node->ns->href, ROX_NS) != 0)
288 g_warning("Unknown namespace %s",
289 node->ns ? node->ns->href
290 : (xmlChar *) "(none)");
291 continue;
294 reply = soap_invoke(node);
296 if (reply)
298 if (!rep_doc)
299 rep_doc = soap_new(&rep_body);
300 xmlAddChild(rep_body, reply);
304 goto out;
306 bad_soap:
307 g_warning("Bad SOAP message received!");
309 out:
310 number_of_windows--;
312 return rep_doc;
316 /* Parse a SOAP reply and extract any fault strings, returning them as
317 * a NULL terminated list of strings (g_strfreev).
319 gchar **extract_soap_errors(xmlDocPtr reply)
321 gchar **errs;
322 GSList *errlist=NULL, *tmp;
323 int i, n;
325 xmlNodePtr root, node;
327 if(!reply)
328 return NULL;
330 root=xmlDocGetRootElement(reply);
331 if(strcmp(root->name, "Envelope")==0) {
332 node=get_subnode(root, SOAP_ENV_NS, "Body");
333 if(node) {
334 xmlNodePtr sub, fault;
335 for(sub=node->xmlChildrenNode; sub;
336 sub=sub->next) {
337 if(sub->type != XML_ELEMENT_NODE)
338 continue;
339 if(strcmp(sub->name, "env:Fault")!=0)
340 continue;
342 /*if (sub->ns == NULL)
343 continue;
345 if(strcmp(sub->ns->href,
346 SOAP_ENV_NS)!=0)
347 continue;*/
349 fault=get_subnode(sub, NULL,
350 "faultstring");
351 if(fault) {
352 xmlChar *txt;
353 txt=xmlNodeGetContent(fault);
354 if(txt) {
355 errlist=g_slist_append(errlist,
356 g_strdup(txt));
358 xmlFree(txt);
365 n=g_slist_length(errlist);
366 errs=g_malloc(sizeof(gchar *)*(n+1));
367 for(i=0, tmp=errlist; i<n; i++, tmp=g_slist_next(tmp))
368 errs[i]=tmp->data;
369 errs[n]=NULL;
371 g_slist_free(errlist);
373 return errs;
376 /****************************************************************
377 * INTERNAL FUNCTIONS *
378 ****************************************************************/
380 /* Register this function to handle SOAP calls to method 'name'.
381 * 'req' and 'opt' are comma separated lists of argument names, in the order
382 * that they are to be delivered to the function.
383 * NULL will be passed for an opt argument if not given.
384 * Otherwise, the parameter is the xmlNode from the call.
386 static void soap_register(char *name, SOAP_func func, char *req, char *opt)
388 SOAP_call *call;
390 call = g_new(SOAP_call, 1);
391 call->func = func;
392 call->required_args = req ? g_strsplit(req, ",", 0) : NULL;
393 call->optional_args = opt ? g_strsplit(opt, ",", 0) : NULL;
395 g_hash_table_insert(rpc_calls, g_strdup(name), call);
398 /* Get the remote IPC window of the already-running filer if there
399 * is one.
401 static GdkWindow *get_existing_ipc_window(void)
403 Window xid, xid_confirm;
404 GdkWindow *window;
406 if (!get_ipc_property(gdk_get_default_root_window(), &xid))
407 return NULL;
409 if (gdk_window_lookup(xid))
410 return NULL; /* Stale handle which we now own */
412 window = gdk_window_foreign_new(xid);
413 if (!window)
414 return NULL;
416 if (!get_ipc_property(window, &xid_confirm) || xid_confirm != xid)
417 return NULL;
419 return window;
422 /* Returns the 'rox_atom' property of 'window' */
423 static gboolean get_ipc_property(GdkWindow *window, Window *r_xid)
425 guchar *data;
426 gint format, length;
427 gboolean retval = FALSE;
429 if (gdk_property_get(window, filer_atom,
430 gdk_x11_xatom_to_atom(XA_WINDOW), 0, 4,
431 FALSE, NULL, &format, &length, &data) && data)
433 /* Note: values with format=32 are stored as longs client-side,
434 * which may be more than 32 bits on some systems.
436 if (format == 32 && length == sizeof(long))
438 long windowID = *((long *) data);
439 retval = TRUE;
440 *r_xid = (Window) windowID;
442 g_free(data);
445 return retval;
448 static char *read_property(GdkWindow *window, GdkAtom prop, gint *out_length)
450 gint grab_len = 4096;
451 gint length;
452 guchar *data;
453 guchar *retval = NULL;
455 gdk_error_trap_push();
457 while (!retval)
459 if (!(gdk_property_get(window, prop,
460 gdk_x11_xatom_to_atom(XA_STRING), 0, grab_len,
461 FALSE, NULL, NULL,
462 &length, &data) && data))
463 goto out; /* Error? */
465 if (length >= grab_len)
467 /* Didn't get all of it - try again */
468 grab_len <<= 1;
469 g_free(data);
470 continue;
473 data = g_realloc(data, length + 1);
474 data[length] = '\0'; /* libxml seems to need this */
475 *out_length = length;
477 retval = data;
479 out:
481 if (gdk_error_trap_pop() == Success)
482 return retval;
484 g_warning("Error reading %s property!", gdk_atom_name(prop));
486 g_free(retval);
487 return NULL;
490 static gboolean client_event(GtkWidget *window,
491 GdkEventClient *event,
492 gpointer user_data)
494 GdkWindow *src_window;
495 GdkAtom prop;
496 xmlDocPtr reply;
497 guchar *data;
498 gint length;
499 xmlDocPtr doc;
501 if (event->message_type != xsoap)
502 return FALSE;
504 src_window = gdk_window_foreign_new(event->data.l[0]);
505 if (!src_window)
507 g_warning("SOAP message sender window was destroyed before I \n"
508 "could read it.");
509 return FALSE;
511 prop = gdk_x11_xatom_to_atom(event->data.l[1]);
513 data = read_property(src_window, prop, &length);
514 if (!data)
515 return TRUE;
517 doc = xmlParseMemory(g_strndup(data, length), length);
518 g_free(data);
520 reply = run_soap(doc);
521 if (number_of_windows == 0)
522 gtk_main_quit();
524 xmlFreeDoc(doc);
526 if (reply)
528 /* Send reply back... */
529 xmlChar *mem;
530 int size;
532 xmlDocDumpMemory(reply, &mem, &size);
533 g_return_val_if_fail(size > 0, TRUE);
535 gdk_error_trap_push();
536 gdk_property_change(src_window, prop,
537 gdk_x11_xatom_to_atom(XA_STRING), 8,
538 GDK_PROP_MODE_REPLACE, mem, size);
539 g_free(mem);
540 gdk_flush();
541 if (gdk_error_trap_pop() != Success)
542 g_warning("Failed to send SOAP reply!");
544 else
546 gdk_error_trap_push();
547 gdk_property_delete(src_window, prop);
548 gdk_flush();
549 if (gdk_error_trap_pop() != Success)
550 g_warning("Failed to send SOAP reply!");
553 return TRUE;
556 /* Some handy functions for processing SOAP RPC arguments... */
558 /* Returns TRUE, FALSE, or -1 (if argument is unspecified) */
559 static int bool_value(xmlNode *arg)
561 int answer;
562 char *optval;
564 if (!arg)
565 return -1;
567 optval = xmlNodeGetContent(arg);
568 answer = text_to_boolean(optval, -1); /* XXX: Report error? */
569 g_free(optval);
571 return answer;
574 /* Returns the text of this arg as a string, or NULL if the (optional)
575 * argument wasn't supplied. Returns "" if the arg is empty.
576 * g_free() the result.
578 static char *string_value(xmlNode *arg)
580 char *retval;
582 if (!arg)
583 return NULL;
585 retval = xmlNodeGetContent(arg);
587 return retval ? retval : g_strdup("");
590 /* Returns the text of this arg as an int, or the default value if not
591 * supplied or not an int.
593 static int int_value(xmlNode *arg, int def)
595 char *str, *end;
596 int i;
598 if (!arg)
599 return def;
601 str = xmlNodeGetContent(arg);
602 if (!str || !str[0])
603 return def;
605 i = (int) strtol(str, &end, 0);
607 return (end > str) ? i : def;
610 /* Return a list of strings, one for each child node of arg.
611 * g_list_free the list, and g_free each string.
613 static GList *list_value(xmlNode *arg)
615 GList *list = NULL;
616 xmlNode *node;
618 for (node = arg->xmlChildrenNode; node; node = node->next)
620 if (node->type != XML_ELEMENT_NODE)
621 continue;
623 list = g_list_append(list, string_value(node));
626 return list;
629 #define ARG(n) ((xmlNode *) g_list_nth(args, n)->data)
631 /* The RPC handlers all work in the same way -- they get passed a list of
632 * xmlNode arguments from the RPC call and they return the result node, or
633 * NULL if there isn't a result.
636 static xmlNodePtr rpc_SetIcon(GList *args)
638 char *path, *icon;
640 path = string_value(ARG(0));
641 icon = string_value(ARG(1));
643 add_globicon(path,icon);
645 g_free(path);
646 g_free(icon);
648 return NULL;
651 static xmlNodePtr rpc_UnsetIcon(GList *args)
653 char *path;
655 path = string_value(ARG(0));
657 delete_globicon(path);
659 g_free(path);
661 return NULL;
664 static xmlNodePtr rpc_Version(GList *args)
666 xmlNodePtr reply;
668 reply = xmlNewNode(NULL, "rox:VersionResponse");
669 xmlNewNs(reply, SOAP_RPC_NS, "soap");
670 xmlNewTextChild(reply, NULL, "soap:result", VERSION);
672 return reply;
675 /* Args: Path, [Style, Details, Sort, Class, Window] */
676 static xmlNodePtr rpc_OpenDir(GList *args)
678 char *path;
679 char *style, *details, *sort, *class, *window;
680 int hidden=FALSE;
681 char *filter_string=NULL;
682 FilerWindow *fwin = NULL;
684 path = string_value(ARG(0));
685 class = string_value(ARG(4));
686 window = string_value(ARG(5));
688 if (window)
689 fwin = filer_get_by_id(window);
691 if (!fwin)
693 fwin = filer_opendir(path, NULL, class);
694 if (window)
695 filer_set_id(fwin, window);
697 else
698 filer_change_to(fwin, path, NULL);
700 g_free(path);
701 g_free(class);
702 g_free(window);
703 if (!fwin)
704 return NULL;
706 style = string_value(ARG(1));
707 details = string_value(ARG(2));
708 sort = string_value(ARG(3));
709 hidden = bool_value(ARG(6));
710 filter_string = string_value(ARG(7));
712 if (style)
714 DisplayStyle ds;
716 ds = !g_ascii_strcasecmp(style, "Large") ? LARGE_ICONS :
717 !g_ascii_strcasecmp(style, "Small") ? SMALL_ICONS :
718 !g_ascii_strcasecmp(style, "Huge") ? HUGE_ICONS :
719 !g_ascii_strcasecmp(style, "Automatic") ? AUTO_SIZE_ICONS :
720 UNKNOWN_STYLE;
721 if (ds == UNKNOWN_STYLE)
722 delayed_error(_("Unknown style '%s'"), style);
723 else
724 display_set_layout(fwin, ds, fwin->details_type, TRUE);
726 g_free(style);
729 if (details)
731 DetailsType dt;
732 ViewType view_type;
734 dt = !g_ascii_strcasecmp(details, "None") ? DETAILS_NONE :
735 !g_ascii_strcasecmp(details, "ListView") ? DETAILS_NONE :
736 !g_ascii_strcasecmp(details, "Size") ? DETAILS_SIZE :
737 !g_ascii_strcasecmp(details, "Type") ? DETAILS_TYPE :
738 !g_ascii_strcasecmp(details, "Times") ? DETAILS_TIMES :
739 !g_ascii_strcasecmp(details, "Permissions")
740 ? DETAILS_PERMISSIONS :
741 DETAILS_UNKNOWN;
743 if (dt == DETAILS_UNKNOWN)
744 delayed_error(_("Unknown details type '%s'"), details);
745 else
746 display_set_layout(fwin, fwin->display_style_wanted,
747 dt, TRUE);
749 if (g_ascii_strcasecmp(details, "ListView") == 0)
750 view_type = VIEW_TYPE_DETAILS;
751 else
752 view_type = VIEW_TYPE_COLLECTION;
754 if (view_type != fwin->view_type)
755 filer_set_view_type(fwin, view_type);
757 g_free(details);
760 if (sort)
762 SortType type;
764 type = !g_ascii_strcasecmp(sort, "Name") ? SORT_NAME :
765 !g_ascii_strcasecmp(sort, "Type") ? SORT_TYPE :
766 !g_ascii_strcasecmp(sort, "Date") ? SORT_DATE :
767 !g_ascii_strcasecmp(sort, "Size") ? SORT_SIZE :
768 !g_ascii_strcasecmp(sort, "Owner") ? SORT_OWNER :
769 !g_ascii_strcasecmp(sort, "Group") ? SORT_GROUP :
771 if (type == -1)
772 delayed_error(_("Unknown sorting type '%s'"), sort);
773 else
774 display_set_sort_type(fwin, type, GTK_SORT_ASCENDING);
776 g_free(sort);
778 if (hidden!=-1)
780 display_set_hidden(fwin, hidden);
783 if (filter_string)
784 display_set_filter(fwin, FILER_SHOW_GLOB, filter_string);
785 else
786 display_set_filter(fwin, FILER_SHOW_ALL, NULL);
788 return NULL;
791 static xmlNodePtr rpc_Run(GList *args)
793 char *path;
795 path = string_value(ARG(0));
796 run_by_path(path);
797 g_free(path);
799 return NULL;
802 static xmlNodePtr rpc_RunURI(GList *args)
804 char *uri, *errmsg=NULL;
805 xmlNodePtr reply=NULL;
807 uri = string_value(ARG(0));
808 run_by_uri(uri, &errmsg);
809 g_free(uri);
811 if(errmsg)
813 reply = xmlNewNode(NULL, "env:Fault");
814 xmlNewNs(reply, SOAP_RPC_NS, "rpc");
815 xmlNewNs(reply, SOAP_ENV_NS, "env");
816 xmlNewTextChild(reply, NULL, "faultcode",
817 "Failed");
818 xmlNewTextChild(reply, NULL, "faultstring", errmsg);
819 g_free(errmsg);
822 return reply;
825 static xmlNodePtr rpc_CloseDir(GList *args)
827 char *path;
829 path = string_value(ARG(0));
830 filer_close_recursive(path);
831 g_free(path);
833 return NULL;
836 static xmlNodePtr rpc_Examine(GList *args)
838 char *path;
840 path = string_value(ARG(0));
841 examine(path);
842 g_free(path);
844 return NULL;
847 static xmlNodePtr rpc_Show(GList *args)
849 char *dir, *leaf;
851 dir = string_value(ARG(0));
852 leaf = string_value(ARG(1));
854 /* XXX: Seems silly to join them only to split again later... */
855 open_to_show(make_path(dir, leaf));
857 g_free(dir);
858 g_free(leaf);
860 return NULL;
863 static xmlNodePtr rpc_Pinboard(GList *args)
865 char *name = NULL;
867 name = string_value(ARG(0));
868 pinboard_activate(name);
869 g_free(name);
871 return NULL;
874 /* args = Filename, Style */
875 static xmlNodePtr rpc_SetBackdrop(GList *args)
877 char *file;
878 char *style;
879 BackdropStyle s;
881 file = string_value(ARG(0));
882 style = string_value(ARG(1));
884 s = !g_ascii_strcasecmp(style, "Tile") ? BACKDROP_TILE :
885 !g_ascii_strcasecmp(style, "Scale") ? BACKDROP_SCALE :
886 !g_ascii_strcasecmp(style, "Stretch") ? BACKDROP_STRETCH :
887 !g_ascii_strcasecmp(style, "Centre") ? BACKDROP_CENTRE :
888 !g_ascii_strcasecmp(style, "Fit") ? BACKDROP_FIT :
889 BACKDROP_NONE;
891 if (s == BACKDROP_NONE)
892 g_warning("Invalid style '%s' for backdrop", style);
893 else
894 pinboard_set_backdrop(file, s);
896 g_free(file);
897 g_free(style);
899 return NULL;
902 /* args = App */
903 static xmlNodePtr rpc_SetBackdropApp(GList *args)
905 char *app;
907 app = string_value(ARG(0));
909 pinboard_set_backdrop_app(app);
911 g_free(app);
913 return NULL;
916 /* args = Path, [X, Y, Label, Shortcut, Args, Locked, Update] */
917 static xmlNodePtr rpc_PinboardAdd(GList *args)
919 char *path = NULL;
920 gchar *name, *shortcut, *xargs;
921 int x, y, locked, update;
923 path = string_value(ARG(0));
924 x = int_value(ARG(1), -1);
925 y = int_value(ARG(2), -1);
926 name = string_value(ARG(3));
927 shortcut = string_value(ARG(4));
928 xargs = string_value(ARG(5));
929 locked = bool_value(ARG(6));
930 update = bool_value(ARG(7));
932 pinboard_pin_with_args(path, name, x, y, shortcut, xargs,
933 (locked==-1) ? FALSE : locked,
934 (update==-1) ? FALSE : update);
936 g_free(path);
937 g_free(name);
938 g_free(shortcut);
939 g_free(xargs);
941 return NULL;
944 /* args = Path, [Label] */
945 static xmlNodePtr rpc_PinboardRemove(GList *args)
947 char *path = NULL;
948 gchar *name;
950 path = string_value(ARG(0));
951 name = string_value(ARG(1));
953 pinboard_remove(path, name);
955 g_free(path);
956 if(name)
957 g_free(name);
959 return NULL;
962 /* args = Side, [Name] */
963 static xmlNodePtr rpc_Panel(GList *args)
965 PanelSide side;
966 char *name, *side_name;
968 side_name = string_value(ARG(0));
969 if (side_name)
971 side = panel_name_to_side(side_name);
972 g_free(side_name);
973 g_return_val_if_fail(side != PANEL_NUMBER_OF_SIDES, NULL);
975 else
976 side = PANEL_DEFAULT_SIDE;
978 name = string_value(ARG(1));
980 panel_new(name, side);
982 g_free(name);
984 return NULL;
987 /* args = Side, Path, [Label, After, Shortcut, Args, Locked] */
988 static xmlNodePtr rpc_PanelAdd(GList *args)
990 PanelSide side;
991 char *path, *side_name, *label, *shortcut, *arg;
992 gboolean after = FALSE;
993 gboolean locked = FALSE;
994 int tmp;
996 side_name = string_value(ARG(0));
997 side = panel_name_to_side(side_name);
998 g_free(side_name);
999 g_return_val_if_fail(side != PANEL_NUMBER_OF_SIDES, NULL);
1001 path = string_value(ARG(1));
1002 label = string_value(ARG(2));
1004 tmp = bool_value(ARG(3));
1005 after = (tmp == -1) ? FALSE : tmp;
1007 shortcut = string_value(ARG(4));
1008 arg = string_value(ARG(5));
1009 locked = bool_value(ARG(6));
1011 panel_add(side, path, label, after, shortcut, arg, (locked== -1) ? FALSE : locked);
1013 g_free(path);
1014 g_free(label);
1015 g_free(shortcut);
1016 g_free(arg);
1018 return NULL;
1021 static xmlNodePtr rpc_PanelRemove(GList *args)
1023 PanelSide side;
1024 char *path, *side_name, *label;
1026 side_name = string_value(ARG(0));
1027 side = panel_name_to_side(side_name);
1028 g_free(side_name);
1029 g_return_val_if_fail(side != PANEL_NUMBER_OF_SIDES, NULL);
1031 path = string_value(ARG(1));
1032 label = string_value(ARG(2));
1034 if (path || label)
1035 panel_remove_item(side, path, label);
1036 else
1037 g_warning("Must specify either path or label");
1039 g_free(path);
1040 g_free(label);
1042 return NULL;
1045 static xmlNodePtr rpc_Copy(GList *args)
1047 GList *from;
1048 char *to;
1049 char *leaf;
1050 int quiet;
1052 from = list_value(ARG(0));
1053 to = string_value(ARG(1));
1054 leaf = string_value(ARG(2));
1055 quiet = bool_value(ARG(3));
1057 if (from)
1058 action_copy(from, to, leaf, quiet);
1059 else
1060 g_warning("No files in SOAP request list");
1062 destroy_glist(&from);
1063 g_free(to);
1064 g_free(leaf);
1066 return NULL;
1069 static xmlNodePtr rpc_Move(GList *args)
1071 GList *from;
1072 char *to;
1073 char *leaf;
1074 int quiet;
1076 from = list_value(ARG(0));
1077 to = string_value(ARG(1));
1078 leaf = string_value(ARG(2));
1079 quiet = bool_value(ARG(3));
1081 if (from)
1082 action_move(from, to, leaf, quiet);
1083 else
1084 g_warning("No files in SOAP request list");
1086 destroy_glist(&from);
1087 g_free(to);
1088 g_free(leaf);
1090 return NULL;
1093 static xmlNodePtr rpc_Link(GList *args)
1095 GList *from;
1096 char *to;
1097 char *leaf;
1099 from = list_value(ARG(0));
1100 to = string_value(ARG(1));
1101 leaf = string_value(ARG(2));
1103 if (from)
1104 action_link(from, to, leaf, TRUE);
1105 else
1106 g_warning("No files in SOAP request list");
1108 destroy_glist(&from);
1109 g_free(to);
1110 g_free(leaf);
1112 return NULL;
1115 static xmlNodePtr rpc_FileType(GList *args)
1117 MIME_type *type;
1118 char *path, *tname;
1119 xmlNodePtr reply;
1121 path = string_value(ARG(0));
1122 type = type_get_type(path);
1123 g_free(path);
1125 reply = xmlNewNode(NULL, "rox:FileTypeResponse");
1126 tname = g_strconcat(type->media_type, "/", type->subtype, NULL);
1128 xmlNewNs(reply, SOAP_RPC_NS, "soap");
1129 xmlNewTextChild(reply, NULL, "soap:result", tname);
1130 g_free(tname);
1132 return reply;
1135 static xmlNodePtr rpc_Mount(GList *args)
1137 GList *paths;
1138 int open_dir, quiet;
1140 paths = list_value(ARG(0));
1141 open_dir = bool_value(ARG(1));
1142 quiet = bool_value(ARG(2));
1144 if (open_dir == -1)
1145 open_dir = TRUE;
1147 if (paths)
1148 action_mount(paths, open_dir, TRUE, quiet);
1150 destroy_glist(&paths);
1152 return NULL;
1155 static xmlNodePtr rpc_Unmount(GList *args)
1157 GList *paths;
1158 int quiet;
1160 paths = list_value(ARG(0));
1161 quiet = bool_value(ARG(1));
1163 if (paths)
1164 action_mount(paths, FALSE, FALSE, quiet);
1166 destroy_glist(&paths);
1168 return NULL;
1171 /* The first time the property changes, do nothing (it's us setting the
1172 * property). The second time, get the result.
1173 * Don't call this function three times!
1175 static void soap_done(GtkWidget *widget, GdkEventProperty *event, gpointer data)
1177 static int times_called = 0;
1178 GdkAtom prop = (GdkAtom) data;
1180 if (prop != event->atom)
1181 return;
1183 times_called++;
1184 g_return_if_fail(times_called < 3);
1186 if (times_called == 1)
1187 return;
1189 /* If we got a reply, display it here */
1190 if (event->state == GDK_PROPERTY_NEW_VALUE)
1192 gint length;
1194 data = read_property(event->window, event->atom, &length);
1196 if (data)
1197 puts(data);
1200 gtk_main_quit();
1203 static gboolean too_slow(gpointer data)
1205 g_warning("Existing ROX-Filer process is not responding! Try with -n");
1206 gtk_main_quit();
1208 return 0;
1211 /* Send the SOAP message in property 'prop' on 'from' to 'dest' */
1212 static void soap_send(GtkWidget *from, GdkAtom prop, GdkWindow *dest)
1214 GdkEventClient event;
1216 event.data.l[0] = GDK_WINDOW_XWINDOW(from->window);
1217 event.data.l[1] = gdk_x11_atom_to_xatom(prop);
1218 event.data_format = 32;
1219 event.message_type = xsoap;
1221 gdk_event_send_client_message((GdkEvent *) &event,
1222 GDK_WINDOW_XWINDOW(dest));
1224 g_timeout_add(10000, too_slow, NULL);
1226 gtk_main();
1229 /* Lookup this method in rpc_calls and invoke it.
1230 * Returns the SOAP reply or fault, or NULL if this method
1231 * doesn't return anything.
1233 static xmlNodePtr soap_invoke(xmlNode *method)
1235 GList *args = NULL;
1236 SOAP_call *call;
1237 gchar **arg;
1238 xmlNodePtr retval = NULL;
1239 GHashTable *name_to_node;
1240 xmlNode *node;
1242 call = g_hash_table_lookup(rpc_calls, method->name);
1243 if (!call)
1245 xmlNodePtr reply;
1246 gchar *err;
1248 err = g_strdup_printf(_("Attempt to invoke unknown SOAP "
1249 "method '%s'"), method->name);
1250 reply = xmlNewNode(NULL, "env:Fault");
1251 xmlNewNs(reply, SOAP_RPC_NS, "rpc");
1252 xmlNewNs(reply, SOAP_ENV_NS, "env");
1253 xmlNewTextChild(reply, NULL, "faultcode",
1254 "rpc:ProcedureNotPresent");
1255 xmlNewTextChild(reply, NULL, "faultstring", err);
1256 g_free(err);
1257 return reply;
1260 name_to_node = g_hash_table_new(g_str_hash, g_str_equal);
1261 for (node = method->xmlChildrenNode; node; node = node->next)
1263 if (node->type != XML_ELEMENT_NODE)
1264 continue;
1266 if (node->ns == NULL || strcmp(node->ns->href, ROX_NS) != 0)
1267 continue;
1269 g_hash_table_insert(name_to_node, (gchar *) node->name, node);
1272 if (call->required_args)
1274 for (arg = call->required_args; *arg; arg++)
1276 node = g_hash_table_lookup(name_to_node, *arg);
1277 if (!node)
1279 g_warning("Missing required argument '%s' "
1280 "in call to method '%s'", *arg,
1281 method->name);
1282 goto out;
1285 args = g_list_append(args, node);
1289 if (call->optional_args)
1291 for (arg = call->optional_args; *arg; arg++)
1292 args = g_list_append(args,
1293 g_hash_table_lookup(name_to_node, *arg));
1296 retval = call->func(args);
1298 out:
1299 g_hash_table_destroy(name_to_node);
1300 g_list_free(args);
1302 return retval;