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)
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
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.
32 #include <X11/Xatom.h>
34 #include <gtk/gtkinvisible.h>
35 #include <libxml/parser.h>
41 #include "gui_support.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
);
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
,
76 static void soap_done(GtkWidget
*widget
,
77 GdkEventProperty
*event
,
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
)
120 GdkWindow
*existing_ipc_window
;
121 GtkWidget
*ipc_window
;
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
);
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
)
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
);
203 soap_send(ipc_window
, xsoap
, existing_ipc_window
);
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
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
);
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);
252 /* Executes the RPC call(s) in the given SOAP message and returns
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)
267 node
= xmlDocGetRootElement(soap
);
271 if (strcmp(node
->ns
->href
, SOAP_ENV_NS
) != 0 &&
272 strcmp(node
->ns
->href
, SOAP_ENV_NS_OLD
) != 0)
275 body
= get_subnode(node
, SOAP_ENV_NS
, "Body");
277 body
= get_subnode(node
, SOAP_ENV_NS_OLD
, "Body");
281 for (node
= body
->xmlChildrenNode
; node
; node
= node
->next
)
283 if (node
->type
!= XML_ELEMENT_NODE
)
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)");
294 reply
= soap_invoke(node
);
299 rep_doc
= soap_new(&rep_body
);
300 xmlAddChild(rep_body
, reply
);
307 g_warning("Bad SOAP message received!");
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
)
322 GSList
*errlist
=NULL
, *tmp
;
325 xmlNodePtr root
, node
;
330 root
=xmlDocGetRootElement(reply
);
331 if(strcmp(root
->name
, "Envelope")==0) {
332 node
=get_subnode(root
, SOAP_ENV_NS
, "Body");
334 xmlNodePtr sub
, fault
;
335 for(sub
=node
->xmlChildrenNode
; sub
;
337 if(sub
->type
!= XML_ELEMENT_NODE
)
339 if(strcmp(sub
->name
, "env:Fault")!=0)
342 /*if (sub->ns == NULL)
345 if(strcmp(sub->ns->href,
349 fault
=get_subnode(sub
, NULL
,
353 txt
=xmlNodeGetContent(fault
);
355 errlist
=g_slist_append(errlist
,
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
))
371 g_slist_free(errlist
);
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
)
390 call
= g_new(SOAP_call
, 1);
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
401 static GdkWindow
*get_existing_ipc_window(void)
403 Window xid
, xid_confirm
;
406 if (!get_ipc_property(gdk_get_default_root_window(), &xid
))
409 if (gdk_window_lookup(xid
))
410 return NULL
; /* Stale handle which we now own */
412 window
= gdk_window_foreign_new(xid
);
416 if (!get_ipc_property(window
, &xid_confirm
) || xid_confirm
!= xid
)
422 /* Returns the 'rox_atom' property of 'window' */
423 static gboolean
get_ipc_property(GdkWindow
*window
, Window
*r_xid
)
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
);
440 *r_xid
= (Window
) windowID
;
448 static char *read_property(GdkWindow
*window
, GdkAtom prop
, gint
*out_length
)
450 gint grab_len
= 4096;
453 guchar
*retval
= NULL
;
455 gdk_error_trap_push();
459 if (!(gdk_property_get(window
, prop
,
460 gdk_x11_xatom_to_atom(XA_STRING
), 0, grab_len
,
462 &length
, &data
) && data
))
463 goto out
; /* Error? */
465 if (length
>= grab_len
)
467 /* Didn't get all of it - try again */
473 data
= g_realloc(data
, length
+ 1);
474 data
[length
] = '\0'; /* libxml seems to need this */
475 *out_length
= length
;
481 if (gdk_error_trap_pop() == Success
)
484 g_warning("Error reading %s property!", gdk_atom_name(prop
));
490 static gboolean
client_event(GtkWidget
*window
,
491 GdkEventClient
*event
,
494 GdkWindow
*src_window
;
501 if (event
->message_type
!= xsoap
)
504 src_window
= gdk_window_foreign_new(event
->data
.l
[0]);
507 g_warning("SOAP message sender window was destroyed before I \n"
511 prop
= gdk_x11_xatom_to_atom(event
->data
.l
[1]);
513 data
= read_property(src_window
, prop
, &length
);
517 doc
= xmlParseMemory(g_strndup(data
, length
), length
);
520 reply
= run_soap(doc
);
521 if (number_of_windows
== 0)
528 /* Send reply back... */
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
);
541 if (gdk_error_trap_pop() != Success
)
542 g_warning("Failed to send SOAP reply!");
546 gdk_error_trap_push();
547 gdk_property_delete(src_window
, prop
);
549 if (gdk_error_trap_pop() != Success
)
550 g_warning("Failed to send SOAP reply!");
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
)
567 optval
= xmlNodeGetContent(arg
);
568 answer
= text_to_boolean(optval
, -1); /* XXX: Report error? */
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
)
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
)
601 str
= xmlNodeGetContent(arg
);
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
)
618 for (node
= arg
->xmlChildrenNode
; node
; node
= node
->next
)
620 if (node
->type
!= XML_ELEMENT_NODE
)
623 list
= g_list_append(list
, string_value(node
));
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
)
640 path
= string_value(ARG(0));
641 icon
= string_value(ARG(1));
643 add_globicon(path
,icon
);
651 static xmlNodePtr
rpc_UnsetIcon(GList
*args
)
655 path
= string_value(ARG(0));
657 delete_globicon(path
);
664 static xmlNodePtr
rpc_Version(GList
*args
)
668 reply
= xmlNewNode(NULL
, "rox:VersionResponse");
669 xmlNewNs(reply
, SOAP_RPC_NS
, "soap");
670 xmlNewTextChild(reply
, NULL
, "soap:result", VERSION
);
675 /* Args: Path, [Style, Details, Sort, Class, Window] */
676 static xmlNodePtr
rpc_OpenDir(GList
*args
)
679 char *style
, *details
, *sort
, *class, *window
;
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));
689 fwin
= filer_get_by_id(window
);
693 fwin
= filer_opendir(path
, NULL
, class);
695 filer_set_id(fwin
, window
);
698 filer_change_to(fwin
, path
, 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));
716 ds
= !g_strcasecmp(style
, "Large") ? LARGE_ICONS
:
717 !g_strcasecmp(style
, "Small") ? SMALL_ICONS
:
718 !g_strcasecmp(style
, "Huge") ? HUGE_ICONS
:
719 !g_strcasecmp(style
, "Automatic") ? AUTO_SIZE_ICONS
:
721 if (ds
== UNKNOWN_STYLE
)
722 delayed_error(_("Unknown style '%s'"), style
);
724 display_set_layout(fwin
, ds
, fwin
->details_type
, TRUE
);
734 dt
= !g_strcasecmp(details
, "None") ? DETAILS_NONE
:
735 !g_strcasecmp(details
, "ListView") ? DETAILS_NONE
:
736 !g_strcasecmp(details
, "Size") ? DETAILS_SIZE
:
737 !g_strcasecmp(details
, "Type") ? DETAILS_TYPE
:
738 !g_strcasecmp(details
, "Times") ? DETAILS_TIMES
:
739 !g_strcasecmp(details
, "Permissions")
740 ? DETAILS_PERMISSIONS
:
743 if (dt
== DETAILS_UNKNOWN
)
744 delayed_error(_("Unknown details type '%s'"), details
);
746 display_set_layout(fwin
, fwin
->display_style_wanted
,
749 if (g_strcasecmp(details
, "ListView") == 0)
750 view_type
= VIEW_TYPE_DETAILS
;
752 view_type
= VIEW_TYPE_COLLECTION
;
754 if (view_type
!= fwin
->view_type
)
755 filer_set_view_type(fwin
, view_type
);
764 type
= !g_strcasecmp(sort
, "Name") ? SORT_NAME
:
765 !g_strcasecmp(sort
, "Type") ? SORT_TYPE
:
766 !g_strcasecmp(sort
, "Date") ? SORT_DATE
:
767 !g_strcasecmp(sort
, "Size") ? SORT_SIZE
:
768 !g_strcasecmp(sort
, "Owner") ? SORT_OWNER
:
769 !g_strcasecmp(sort
, "Group") ? SORT_GROUP
:
772 delayed_error(_("Unknown sorting type '%s'"), sort
);
774 display_set_sort_type(fwin
, type
, GTK_SORT_ASCENDING
);
780 display_set_hidden(fwin
, hidden
);
784 display_set_filter(fwin
, FILER_SHOW_GLOB
, filter_string
);
786 display_set_filter(fwin
, FILER_SHOW_ALL
, NULL
);
791 static xmlNodePtr
rpc_Run(GList
*args
)
795 path
= string_value(ARG(0));
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
);
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",
818 xmlNewTextChild(reply
, NULL
, "faultstring", errmsg
);
825 static xmlNodePtr
rpc_CloseDir(GList
*args
)
829 path
= string_value(ARG(0));
830 filer_close_recursive(path
);
836 static xmlNodePtr
rpc_Examine(GList
*args
)
840 path
= string_value(ARG(0));
847 static xmlNodePtr
rpc_Show(GList
*args
)
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
));
863 static xmlNodePtr
rpc_Pinboard(GList
*args
)
867 name
= string_value(ARG(0));
868 pinboard_activate(name
);
874 /* args = Filename, Style */
875 static xmlNodePtr
rpc_SetBackdrop(GList
*args
)
881 file
= string_value(ARG(0));
882 style
= string_value(ARG(1));
884 s
= !g_strcasecmp(style
, "Tile") ? BACKDROP_TILE
:
885 !g_strcasecmp(style
, "Scale") ? BACKDROP_SCALE
:
886 !g_strcasecmp(style
, "Stretch") ? BACKDROP_STRETCH
:
887 !g_strcasecmp(style
, "Centre") ? BACKDROP_CENTRE
:
890 if (s
== BACKDROP_NONE
)
891 g_warning("Invalid style '%s' for backdrop", style
);
893 pinboard_set_backdrop(file
, s
);
902 static xmlNodePtr
rpc_SetBackdropApp(GList
*args
)
906 app
= string_value(ARG(0));
908 pinboard_set_backdrop_app(app
);
915 /* args = Path, [X, Y, Label, Shortcut, Args, Locked, Update] */
916 static xmlNodePtr
rpc_PinboardAdd(GList
*args
)
919 gchar
*name
, *shortcut
, *xargs
;
920 int x
, y
, locked
, update
;
922 path
= string_value(ARG(0));
923 x
= int_value(ARG(1), -1);
924 y
= int_value(ARG(2), -1);
925 name
= string_value(ARG(3));
926 shortcut
= string_value(ARG(4));
927 xargs
= string_value(ARG(5));
928 locked
= bool_value(ARG(6));
929 update
= bool_value(ARG(7));
931 pinboard_pin_with_args(path
, name
, x
, y
, shortcut
, xargs
,
932 (locked
==-1) ? FALSE
: locked
,
933 (update
==-1) ? FALSE
: update
);
943 /* args = Path, [Label] */
944 static xmlNodePtr
rpc_PinboardRemove(GList
*args
)
949 path
= string_value(ARG(0));
950 name
= string_value(ARG(1));
952 pinboard_remove(path
, name
);
961 /* args = Side, [Name] */
962 static xmlNodePtr
rpc_Panel(GList
*args
)
965 char *name
, *side_name
;
967 side_name
= string_value(ARG(0));
970 side
= panel_name_to_side(side_name
);
972 g_return_val_if_fail(side
!= PANEL_NUMBER_OF_SIDES
, NULL
);
975 side
= PANEL_DEFAULT_SIDE
;
977 name
= string_value(ARG(1));
979 panel_new(name
, side
);
986 /* args = Side, Path, [Label, After, Shortcut, Args, Locked] */
987 static xmlNodePtr
rpc_PanelAdd(GList
*args
)
990 char *path
, *side_name
, *label
, *shortcut
, *arg
;
991 gboolean after
= FALSE
;
992 gboolean locked
= FALSE
;
995 side_name
= string_value(ARG(0));
996 side
= panel_name_to_side(side_name
);
998 g_return_val_if_fail(side
!= PANEL_NUMBER_OF_SIDES
, NULL
);
1000 path
= string_value(ARG(1));
1001 label
= string_value(ARG(2));
1003 tmp
= bool_value(ARG(3));
1004 after
= (tmp
== -1) ? FALSE
: tmp
;
1006 shortcut
= string_value(ARG(4));
1007 arg
= string_value(ARG(5));
1008 locked
= bool_value(ARG(6));
1010 panel_add(side
, path
, label
, after
, shortcut
, arg
, (locked
== -1) ? FALSE
: locked
);
1020 static xmlNodePtr
rpc_PanelRemove(GList
*args
)
1023 char *path
, *side_name
, *label
;
1025 side_name
= string_value(ARG(0));
1026 side
= panel_name_to_side(side_name
);
1028 g_return_val_if_fail(side
!= PANEL_NUMBER_OF_SIDES
, NULL
);
1030 path
= string_value(ARG(1));
1031 label
= string_value(ARG(2));
1034 panel_remove_item(side
, path
, label
);
1036 g_warning("Must specify either path or label");
1044 static xmlNodePtr
rpc_Copy(GList
*args
)
1051 from
= list_value(ARG(0));
1052 to
= string_value(ARG(1));
1053 leaf
= string_value(ARG(2));
1054 quiet
= bool_value(ARG(3));
1057 action_copy(from
, to
, leaf
, quiet
);
1059 g_warning("No files in SOAP request list");
1061 destroy_glist(&from
);
1068 static xmlNodePtr
rpc_Move(GList
*args
)
1075 from
= list_value(ARG(0));
1076 to
= string_value(ARG(1));
1077 leaf
= string_value(ARG(2));
1078 quiet
= bool_value(ARG(3));
1081 action_move(from
, to
, leaf
, quiet
);
1083 g_warning("No files in SOAP request list");
1085 destroy_glist(&from
);
1092 static xmlNodePtr
rpc_Link(GList
*args
)
1098 from
= list_value(ARG(0));
1099 to
= string_value(ARG(1));
1100 leaf
= string_value(ARG(2));
1103 action_link(from
, to
, leaf
, TRUE
);
1105 g_warning("No files in SOAP request list");
1107 destroy_glist(&from
);
1114 static xmlNodePtr
rpc_FileType(GList
*args
)
1120 path
= string_value(ARG(0));
1121 type
= type_get_type(path
);
1124 reply
= xmlNewNode(NULL
, "rox:FileTypeResponse");
1125 tname
= g_strconcat(type
->media_type
, "/", type
->subtype
, NULL
);
1127 xmlNewNs(reply
, SOAP_RPC_NS
, "soap");
1128 xmlNewTextChild(reply
, NULL
, "soap:result", tname
);
1134 static xmlNodePtr
rpc_Mount(GList
*args
)
1137 int open_dir
, quiet
;
1139 paths
= list_value(ARG(0));
1140 open_dir
= bool_value(ARG(1));
1141 quiet
= bool_value(ARG(2));
1147 action_mount(paths
, open_dir
, TRUE
, quiet
);
1149 destroy_glist(&paths
);
1154 static xmlNodePtr
rpc_Unmount(GList
*args
)
1159 paths
= list_value(ARG(0));
1160 quiet
= bool_value(ARG(1));
1163 action_mount(paths
, FALSE
, FALSE
, quiet
);
1165 destroy_glist(&paths
);
1170 /* The first time the property changes, do nothing (it's us setting the
1171 * property). The second time, get the result.
1172 * Don't call this function three times!
1174 static void soap_done(GtkWidget
*widget
, GdkEventProperty
*event
, gpointer data
)
1176 static int times_called
= 0;
1177 GdkAtom prop
= (GdkAtom
) data
;
1179 if (prop
!= event
->atom
)
1183 g_return_if_fail(times_called
< 3);
1185 if (times_called
== 1)
1188 /* If we got a reply, display it here */
1189 if (event
->state
== GDK_PROPERTY_NEW_VALUE
)
1193 data
= read_property(event
->window
, event
->atom
, &length
);
1202 static gboolean
too_slow(gpointer data
)
1204 g_warning("Existing ROX-Filer process is not responding! Try with -n");
1210 /* Send the SOAP message in property 'prop' on 'from' to 'dest' */
1211 static void soap_send(GtkWidget
*from
, GdkAtom prop
, GdkWindow
*dest
)
1213 GdkEventClient event
;
1215 event
.data
.l
[0] = GDK_WINDOW_XWINDOW(from
->window
);
1216 event
.data
.l
[1] = gdk_x11_atom_to_xatom(prop
);
1217 event
.data_format
= 32;
1218 event
.message_type
= xsoap
;
1220 gdk_event_send_client_message((GdkEvent
*) &event
,
1221 GDK_WINDOW_XWINDOW(dest
));
1223 g_timeout_add(10000, too_slow
, NULL
);
1228 /* Lookup this method in rpc_calls and invoke it.
1229 * Returns the SOAP reply or fault, or NULL if this method
1230 * doesn't return anything.
1232 static xmlNodePtr
soap_invoke(xmlNode
*method
)
1237 xmlNodePtr retval
= NULL
;
1238 GHashTable
*name_to_node
;
1241 call
= g_hash_table_lookup(rpc_calls
, method
->name
);
1247 err
= g_strdup_printf(_("Attempt to invoke unknown SOAP "
1248 "method '%s'"), method
->name
);
1249 reply
= xmlNewNode(NULL
, "env:Fault");
1250 xmlNewNs(reply
, SOAP_RPC_NS
, "rpc");
1251 xmlNewNs(reply
, SOAP_ENV_NS
, "env");
1252 xmlNewTextChild(reply
, NULL
, "faultcode",
1253 "rpc:ProcedureNotPresent");
1254 xmlNewTextChild(reply
, NULL
, "faultstring", err
);
1259 name_to_node
= g_hash_table_new(g_str_hash
, g_str_equal
);
1260 for (node
= method
->xmlChildrenNode
; node
; node
= node
->next
)
1262 if (node
->type
!= XML_ELEMENT_NODE
)
1265 if (node
->ns
== NULL
|| strcmp(node
->ns
->href
, ROX_NS
) != 0)
1268 g_hash_table_insert(name_to_node
, (gchar
*) node
->name
, node
);
1271 if (call
->required_args
)
1273 for (arg
= call
->required_args
; *arg
; arg
++)
1275 node
= g_hash_table_lookup(name_to_node
, *arg
);
1278 g_warning("Missing required argument '%s' "
1279 "in call to method '%s'", *arg
,
1284 args
= g_list_append(args
, node
);
1288 if (call
->optional_args
)
1290 for (arg
= call
->optional_args
; *arg
; arg
++)
1291 args
= g_list_append(args
,
1292 g_hash_table_lookup(name_to_node
, *arg
));
1295 retval
= call
->func(args
);
1298 g_hash_table_destroy(name_to_node
);