1 /*
2 * ROX-Filer, filer for the ROX desktop project
3 * Copyright (C) 2006, Thomas Leonard and others (see changelog for details).
4 *
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.
9 *
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.
14 *
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
18 */
19
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.
24 */
25
26 #include "config.h"
27
28 #include <string.h>
29
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>
36
37 #include "global.h"
38
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"
53
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 */
57
58 typedef struct _SOAP_call SOAP_call;
59 typedef xmlNodePtr (*SOAP_func)(GList *args);
60
61 struct _SOAP_call {
62 SOAP_func func;
63 gchar **required_args;
64 gchar **optional_args;
65 };
66
67 static GHashTable *rpc_calls = NULL; /* MethodName -> Function */
68
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);
81
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);
97
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);
104
105 static xmlNodePtr rpc_SetIcon(GList *args);
106 static xmlNodePtr rpc_UnsetIcon(GList *args);
107
108 /****************************************************************
109 * EXTERNAL INTERFACE *
110 ****************************************************************/
111
112
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.
116 */
remote_init(xmlDocPtr rpc,gboolean new_copy)117 gboolean remote_init(xmlDocPtr rpc, gboolean new_copy)
118 {
119 guchar *unique_id;
120 GdkWindow *existing_ipc_window;
121 GtkWidget *ipc_window;
122 Window xwindow;
123
124 /* xmlDocDump(stdout, rpc); */
125
126 rpc_calls = g_hash_table_new(g_str_hash, g_str_equal);
127
128 soap_register("Version", rpc_Version, NULL, NULL);
129
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);
137
138 soap_register("Pinboard", rpc_Pinboard, NULL, "Name");
139 soap_register("Panel", rpc_Panel, NULL, "Side,Name");
140
141 soap_register("FileType", rpc_FileType, "Filename", NULL);
142
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");
148
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);
157
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.
161 */
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);
166
167 xsoap = gdk_atom_intern("_XSOAP", FALSE);
168
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...
174 */
175 ipc_window = gtk_invisible_new();
176 gtk_widget_realize(ipc_window);
177
178 XGrabServer(GDK_DISPLAY());
179
180 existing_ipc_window = new_copy ? NULL : get_existing_ipc_window();
181 if (existing_ipc_window)
182 {
183 xmlChar *mem;
184 int size;
185
186 XUngrabServer(GDK_DISPLAY());
187
188 xmlDocDumpMemory(rpc, &mem, &size);
189 g_return_val_if_fail(size > 0, FALSE);
190
191 /* Since Gtk might have selected this already, we'd
192 * better do it BEFORE changing the property.
193 */
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));
197
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);
202
203 soap_send(ipc_window, xsoap, existing_ipc_window);
204
205 return TRUE;
206 }
207
208 xwindow = GDK_WINDOW_XWINDOW(ipc_window->window);
209
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.
213 */
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);
218
219 /* Get notified when we get a message */
220 g_signal_connect(ipc_window, "client-event",
221 G_CALLBACK(client_event), NULL);
222
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);
228
229 XUngrabServer(GDK_DISPLAY());
230
231 /* Also have a property without the version number, for programs
232 * that are happy to talk to any version of the filer.
233 */
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);
248
249 return FALSE;
250 }
251
252 /* Executes the RPC call(s) in the given SOAP message and returns
253 * the reply.
254 */
run_soap(xmlDocPtr soap)255 xmlDocPtr run_soap(xmlDocPtr soap)
256 {
257 xmlNodePtr body, node, rep_body, reply;
258 xmlDocPtr rep_doc = NULL;
259
260 g_return_val_if_fail(soap != NULL, NULL);
261
262 /* Make sure we don't quit before doing the whole list
263 * (there's a command that closes windows)
264 */
265 number_of_windows++;
266
267 node = xmlDocGetRootElement(soap);
268 if (!node->ns)
269 goto bad_soap;
270
271 if (strcmp(node->ns->href, SOAP_ENV_NS) != 0 &&
272 strcmp(node->ns->href, SOAP_ENV_NS_OLD) != 0)
273 goto bad_soap;
274
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;
280
281 for (node = body->xmlChildrenNode; node; node = node->next)
282 {
283 if (node->type != XML_ELEMENT_NODE)
284 continue;
285
286 if (node->ns == NULL || strcmp(node->ns->href, ROX_NS) != 0)
287 {
288 g_warning("Unknown namespace %s",
289 node->ns ? node->ns->href
290 : (xmlChar *) "(none)");
291 continue;
292 }
293
294 reply = soap_invoke(node);
295
296 if (reply)
297 {
298 if (!rep_doc)
299 rep_doc = soap_new(&rep_body);
300 xmlAddChild(rep_body, reply);
301 }
302 }
303
304 goto out;
305
306 bad_soap:
307 g_warning("Bad SOAP message received!");
308
309 out:
310 number_of_windows--;
311
312 return rep_doc;
313 }
314
315
316 /* Parse a SOAP reply and extract any fault strings, returning them as
317 * a NULL terminated list of strings (g_strfreev).
318 */
extract_soap_errors(xmlDocPtr reply)319 gchar **extract_soap_errors(xmlDocPtr reply)
320 {
321 gchar **errs;
322 GSList *errlist=NULL, *tmp;
323 int i, n;
324
325 xmlNodePtr root, node;
326
327 if(!reply)
328 return NULL;
329
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;
341
342 /*if (sub->ns == NULL)
343 continue;
344
345 if(strcmp(sub->ns->href,
346 SOAP_ENV_NS)!=0)
347 continue;*/
348
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));
357
358 xmlFree(txt);
359 }
360 }
361 }
362 }
363 }
364
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;
370
371 g_slist_free(errlist);
372
373 return errs;
374 }
375
376 /****************************************************************
377 * INTERNAL FUNCTIONS *
378 ****************************************************************/
379
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.
385 */
soap_register(char * name,SOAP_func func,char * req,char * opt)386 static void soap_register(char *name, SOAP_func func, char *req, char *opt)
387 {
388 SOAP_call *call;
389
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;
394
395 g_hash_table_insert(rpc_calls, g_strdup(name), call);
396 }
397
398 /* Get the remote IPC window of the already-running filer if there
399 * is one.
400 */
get_existing_ipc_window(void)401 static GdkWindow *get_existing_ipc_window(void)
402 {
403 Window xid, xid_confirm;
404 GdkWindow *window;
405
406 if (!get_ipc_property(gdk_get_default_root_window(), &xid))
407 return NULL;
408
409 if (gdk_window_lookup(xid))
410 return NULL; /* Stale handle which we now own */
411
412 window = gdk_window_foreign_new(xid);
413 if (!window)
414 return NULL;
415
416 if (!get_ipc_property(window, &xid_confirm) || xid_confirm != xid)
417 return NULL;
418
419 return window;
420 }
421
422 /* Returns the 'rox_atom' property of 'window' */
get_ipc_property(GdkWindow * window,Window * r_xid)423 static gboolean get_ipc_property(GdkWindow *window, Window *r_xid)
424 {
425 guchar *data;
426 gint format, length;
427 gboolean retval = FALSE;
428
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)
432 {
433 /* Note: values with format=32 are stored as longs client-side,
434 * which may be more than 32 bits on some systems.
435 */
436 if (format == 32 && length == sizeof(long))
437 {
438 long windowID = *((long *) data);
439 retval = TRUE;
440 *r_xid = (Window) windowID;
441 }
442 g_free(data);
443 }
444
445 return retval;
446 }
447
read_property(GdkWindow * window,GdkAtom prop,gint * out_length)448 static char *read_property(GdkWindow *window, GdkAtom prop, gint *out_length)
449 {
450 gint grab_len = 4096;
451 gint length;
452 guchar *data;
453 guchar *retval = NULL;
454
455 gdk_error_trap_push();
456
457 while (!retval)
458 {
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? */
464
465 if (length >= grab_len)
466 {
467 /* Didn't get all of it - try again */
468 grab_len <<= 1;
469 g_free(data);
470 continue;
471 }
472
473 data = g_realloc(data, length + 1);
474 data[length] = '\0'; /* libxml seems to need this */
475 *out_length = length;
476
477 retval = data;
478 }
479 out:
480
481 if (gdk_error_trap_pop() == Success)
482 return retval;
483
484 g_warning("Error reading %s property!", gdk_atom_name(prop));
485
486 g_free(retval);
487 return NULL;
488 }
489
client_event(GtkWidget * window,GdkEventClient * event,gpointer user_data)490 static gboolean client_event(GtkWidget *window,
491 GdkEventClient *event,
492 gpointer user_data)
493 {
494 GdkWindow *src_window;
495 GdkAtom prop;
496 xmlDocPtr reply;
497 guchar *data;
498 gint length;
499 xmlDocPtr doc;
500
501 if (event->message_type != xsoap)
502 return FALSE;
503
504 src_window = gdk_window_foreign_new(event->data.l[0]);
505 if (!src_window)
506 {
507 g_warning("SOAP message sender window was destroyed before I \n"
508 "could read it.");
509 return FALSE;
510 }
511 prop = gdk_x11_xatom_to_atom(event->data.l[1]);
512
513 data = read_property(src_window, prop, &length);
514 if (!data)
515 return TRUE;
516
517 doc = xmlParseMemory(g_strndup(data, length), length);
518 g_free(data);
519
520 reply = run_soap(doc);
521 if (number_of_windows == 0)
522 gtk_main_quit();
523
524 xmlFreeDoc(doc);
525
526 if (reply)
527 {
528 /* Send reply back... */
529 xmlChar *mem;
530 int size;
531
532 xmlDocDumpMemory(reply, &mem, &size);
533 g_return_val_if_fail(size > 0, TRUE);
534
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!");
543 }
544 else
545 {
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!");
551 }
552
553 return TRUE;
554 }
555
556 /* Some handy functions for processing SOAP RPC arguments... */
557
558 /* Returns TRUE, FALSE, or -1 (if argument is unspecified) */
bool_value(xmlNode * arg)559 static int bool_value(xmlNode *arg)
560 {
561 int answer;
562 char *optval;
563
564 if (!arg)
565 return -1;
566
567 optval = xmlNodeGetContent(arg);
568 answer = text_to_boolean(optval, -1); /* XXX: Report error? */
569 g_free(optval);
570
571 return answer;
572 }
573
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.
577 */
string_value(xmlNode * arg)578 static char *string_value(xmlNode *arg)
579 {
580 char *retval;
581
582 if (!arg)
583 return NULL;
584
585 retval = xmlNodeGetContent(arg);
586
587 return retval ? retval : g_strdup("");
588 }
589
590 /* Returns the text of this arg as an int, or the default value if not
591 * supplied or not an int.
592 */
int_value(xmlNode * arg,int def)593 static int int_value(xmlNode *arg, int def)
594 {
595 char *str, *end;
596 int i;
597
598 if (!arg)
599 return def;
600
601 str = xmlNodeGetContent(arg);
602 if (!str || !str[0])
603 return def;
604
605 i = (int) strtol(str, &end, 0);
606
607 return (end > str) ? i : def;
608 }
609
610 /* Return a list of strings, one for each child node of arg.
611 * g_list_free the list, and g_free each string.
612 */
list_value(xmlNode * arg)613 static GList *list_value(xmlNode *arg)
614 {
615 GList *list = NULL;
616 xmlNode *node;
617
618 for (node = arg->xmlChildrenNode; node; node = node->next)
619 {
620 if (node->type != XML_ELEMENT_NODE)
621 continue;
622
623 list = g_list_append(list, string_value(node));
624 }
625
626 return list;
627 }
628
629 #define ARG(n) ((xmlNode *) g_list_nth(args, n)->data)
630
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.
634 */
635
rpc_SetIcon(GList * args)636 static xmlNodePtr rpc_SetIcon(GList *args)
637 {
638 char *path, *icon;
639
640 path = string_value(ARG(0));
641 icon = string_value(ARG(1));
642
643 add_globicon(path,icon);
644
645 g_free(path);
646 g_free(icon);
647
648 return NULL;
649 }
650
rpc_UnsetIcon(GList * args)651 static xmlNodePtr rpc_UnsetIcon(GList *args)
652 {
653 char *path;
654
655 path = string_value(ARG(0));
656
657 delete_globicon(path);
658
659 g_free(path);
660
661 return NULL;
662 }
663
rpc_Version(GList * args)664 static xmlNodePtr rpc_Version(GList *args)
665 {
666 xmlNodePtr reply;
667
668 reply = xmlNewNode(NULL, "rox:VersionResponse");
669 xmlNewNs(reply, SOAP_RPC_NS, "soap");
670 xmlNewTextChild(reply, NULL, "soap:result", VERSION);
671
672 return reply;
673 }
674
675 /* Args: Path, [Style, Details, Sort, Class, Window] */
rpc_OpenDir(GList * args)676 static xmlNodePtr rpc_OpenDir(GList *args)
677 {
678 char *path;
679 char *style, *details, *sort, *class, *window;
680 int hidden=FALSE;
681 char *filter_string=NULL;
682 FilerWindow *fwin = NULL;
683
684 path = string_value(ARG(0));
685 class = string_value(ARG(4));
686 window = string_value(ARG(5));
687
688 if (window)
689 fwin = filer_get_by_id(window);
690
691 if (!fwin)
692 {
693 fwin = filer_opendir(path, NULL, class);
694 if (window)
695 filer_set_id(fwin, window);
696 }
697 else
698 filer_change_to(fwin, path, NULL);
699
700 g_free(path);
701 g_free(class);
702 g_free(window);
703 if (!fwin)
704 return NULL;
705
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));
711
712 if (style)
713 {
714 DisplayStyle ds;
715
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 :
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);
725
726 g_free(style);
727 }
728
729 if (details)
730 {
731 DetailsType dt;
732 ViewType view_type;
733
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 :
741 DETAILS_UNKNOWN;
742
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);
748
749 if (g_strcasecmp(details, "ListView") == 0)
750 view_type = VIEW_TYPE_DETAILS;
751 else
752 view_type = VIEW_TYPE_COLLECTION;
753
754 if (view_type != fwin->view_type)
755 filer_set_view_type(fwin, view_type);
756
757 g_free(details);
758 }
759
760 if (sort)
761 {
762 SortType type;
763
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 :
770 -1;
771 if (type == -1)
772 delayed_error(_("Unknown sorting type '%s'"), sort);
773 else
774 display_set_sort_type(fwin, type, GTK_SORT_ASCENDING);
775
776 g_free(sort);
777 }
778 if (hidden!=-1)
779 {
780 display_set_hidden(fwin, hidden);
781 }
782
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);
787
788 return NULL;
789 }
790
rpc_Run(GList * args)791 static xmlNodePtr rpc_Run(GList *args)
792 {
793 char *path;
794
795 path = string_value(ARG(0));
796 run_by_path(path);
797 g_free(path);
798
799 return NULL;
800 }
801
rpc_RunURI(GList * args)802 static xmlNodePtr rpc_RunURI(GList *args)
803 {
804 char *uri, *errmsg=NULL;
805 xmlNodePtr reply=NULL;
806
807 uri = string_value(ARG(0));
808 run_by_uri(uri, &errmsg);
809 g_free(uri);
810
811 if(errmsg)
812 {
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);
820 }
821
822 return reply;
823 }
824
rpc_CloseDir(GList * args)825 static xmlNodePtr rpc_CloseDir(GList *args)
826 {
827 char *path;
828
829 path = string_value(ARG(0));
830 filer_close_recursive(path);
831 g_free(path);
832
833 return NULL;
834 }
835
rpc_Examine(GList * args)836 static xmlNodePtr rpc_Examine(GList *args)
837 {
838 char *path;
839
840 path = string_value(ARG(0));
841 examine(path);
842 g_free(path);
843
844 return NULL;
845 }
846
rpc_Show(GList * args)847 static xmlNodePtr rpc_Show(GList *args)
848 {
849 char *dir, *leaf;
850
851 dir = string_value(ARG(0));
852 leaf = string_value(ARG(1));
853
854 /* XXX: Seems silly to join them only to split again later... */
855 open_to_show(make_path(dir, leaf));
856
857 g_free(dir);
858 g_free(leaf);
859
860 return NULL;
861 }
862
rpc_Pinboard(GList * args)863 static xmlNodePtr rpc_Pinboard(GList *args)
864 {
865 char *name = NULL;
866
867 name = string_value(ARG(0));
868 pinboard_activate(name);
869 g_free(name);
870
871 return NULL;
872 }
873
874 /* args = Filename, Style */
rpc_SetBackdrop(GList * args)875 static xmlNodePtr rpc_SetBackdrop(GList *args)
876 {
877 char *file;
878 char *style;
879 BackdropStyle s;
880
881 file = string_value(ARG(0));
882 style = string_value(ARG(1));
883
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 :
888 BACKDROP_NONE;
889
890 if (s == BACKDROP_NONE)
891 g_warning("Invalid style '%s' for backdrop", style);
892 else
893 pinboard_set_backdrop(file, s);
894
895 g_free(file);
896 g_free(style);
897
898 return NULL;
899 }
900
901 /* args = App */
rpc_SetBackdropApp(GList * args)902 static xmlNodePtr rpc_SetBackdropApp(GList *args)
903 {
904 char *app;
905
906 app = string_value(ARG(0));
907
908 pinboard_set_backdrop_app(app);
909
910 g_free(app);
911
912 return NULL;
913 }
914
915 /* args = Path, [X, Y, Label, Shortcut, Args, Locked, Update] */
rpc_PinboardAdd(GList * args)916 static xmlNodePtr rpc_PinboardAdd(GList *args)
917 {
918 char *path = NULL;
919 gchar *name, *shortcut, *xargs;
920 int x, y, locked, update;
921
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));
930
931 pinboard_pin_with_args(path, name, x, y, shortcut, xargs,
932 (locked==-1) ? FALSE : locked,
933 (update==-1) ? FALSE : update);
934
935 g_free(path);
936 g_free(name);
937 g_free(shortcut);
938 g_free(xargs);
939
940 return NULL;
941 }
942
943 /* args = Path, [Label] */
rpc_PinboardRemove(GList * args)944 static xmlNodePtr rpc_PinboardRemove(GList *args)
945 {
946 char *path = NULL;
947 gchar *name;
948
949 path = string_value(ARG(0));
950 name = string_value(ARG(1));
951
952 pinboard_remove(path, name);
953
954 g_free(path);
955 if(name)
956 g_free(name);
957
958 return NULL;
959 }
960
961 /* args = Side, [Name] */
rpc_Panel(GList * args)962 static xmlNodePtr rpc_Panel(GList *args)
963 {
964 PanelSide side;
965 char *name, *side_name;
966
967 side_name = string_value(ARG(0));
968 if (side_name)
969 {
970 side = panel_name_to_side(side_name);
971 g_free(side_name);
972 g_return_val_if_fail(side != PANEL_NUMBER_OF_SIDES, NULL);
973 }
974 else
975 side = PANEL_DEFAULT_SIDE;
976
977 name = string_value(ARG(1));
978
979 panel_new(name, side);
980
981 g_free(name);
982
983 return NULL;
984 }
985
986 /* args = Side, Path, [Label, After, Shortcut, Args, Locked] */
rpc_PanelAdd(GList * args)987 static xmlNodePtr rpc_PanelAdd(GList *args)
988 {
989 PanelSide side;
990 char *path, *side_name, *label, *shortcut, *arg;
991 gboolean after = FALSE;
992 gboolean locked = FALSE;
993 int tmp;
994
995 side_name = string_value(ARG(0));
996 side = panel_name_to_side(side_name);
997 g_free(side_name);
998 g_return_val_if_fail(side != PANEL_NUMBER_OF_SIDES, NULL);
999
1000 path = string_value(ARG(1));
1001 label = string_value(ARG(2));
1002
1003 tmp = bool_value(ARG(3));
1004 after = (tmp == -1) ? FALSE : tmp;
1005
1006 shortcut = string_value(ARG(4));
1007 arg = string_value(ARG(5));
1008 locked = bool_value(ARG(6));
1009
1010 panel_add(side, path, label, after, shortcut, arg, (locked== -1) ? FALSE : locked);
1011
1012 g_free(path);
1013 g_free(label);
1014 g_free(shortcut);
1015 g_free(arg);
1016
1017 return NULL;
1018 }
1019
rpc_PanelRemove(GList * args)1020 static xmlNodePtr rpc_PanelRemove(GList *args)
1021 {
1022 PanelSide side;
1023 char *path, *side_name, *label;
1024
1025 side_name = string_value(ARG(0));
1026 side = panel_name_to_side(side_name);
1027 g_free(side_name);
1028 g_return_val_if_fail(side != PANEL_NUMBER_OF_SIDES, NULL);
1029
1030 path = string_value(ARG(1));
1031 label = string_value(ARG(2));
1032
1033 if (path || label)
1034 panel_remove_item(side, path, label);
1035 else
1036 g_warning("Must specify either path or label");
1037
1038 g_free(path);
1039 g_free(label);
1040
1041 return NULL;
1042 }
1043
rpc_Copy(GList * args)1044 static xmlNodePtr rpc_Copy(GList *args)
1045 {
1046 GList *from;
1047 char *to;
1048 char *leaf;
1049 int quiet;
1050
1051 from = list_value(ARG(0));
1052 to = string_value(ARG(1));
1053 leaf = string_value(ARG(2));
1054 quiet = bool_value(ARG(3));
1055
1056 if (from)
1057 action_copy(from, to, leaf, quiet);
1058 else
1059 g_warning("No files in SOAP request list");
1060
1061 destroy_glist(&from);
1062 g_free(to);
1063 g_free(leaf);
1064
1065 return NULL;
1066 }
1067
rpc_Move(GList * args)1068 static xmlNodePtr rpc_Move(GList *args)
1069 {
1070 GList *from;
1071 char *to;
1072 char *leaf;
1073 int quiet;
1074
1075 from = list_value(ARG(0));
1076 to = string_value(ARG(1));
1077 leaf = string_value(ARG(2));
1078 quiet = bool_value(ARG(3));
1079
1080 if (from)
1081 action_move(from, to, leaf, quiet);
1082 else
1083 g_warning("No files in SOAP request list");
1084
1085 destroy_glist(&from);
1086 g_free(to);
1087 g_free(leaf);
1088
1089 return NULL;
1090 }
1091
rpc_Link(GList * args)1092 static xmlNodePtr rpc_Link(GList *args)
1093 {
1094 GList *from;
1095 char *to;
1096 char *leaf;
1097
1098 from = list_value(ARG(0));
1099 to = string_value(ARG(1));
1100 leaf = string_value(ARG(2));
1101
1102 if (from)
1103 action_link(from, to, leaf, TRUE);
1104 else
1105 g_warning("No files in SOAP request list");
1106
1107 destroy_glist(&from);
1108 g_free(to);
1109 g_free(leaf);
1110
1111 return NULL;
1112 }
1113
rpc_FileType(GList * args)1114 static xmlNodePtr rpc_FileType(GList *args)
1115 {
1116 MIME_type *type;
1117 char *path, *tname;
1118 xmlNodePtr reply;
1119
1120 path = string_value(ARG(0));
1121 type = type_get_type(path);
1122 g_free(path);
1123
1124 reply = xmlNewNode(NULL, "rox:FileTypeResponse");
1125 tname = g_strconcat(type->media_type, "/", type->subtype, NULL);
1126
1127 xmlNewNs(reply, SOAP_RPC_NS, "soap");
1128 xmlNewTextChild(reply, NULL, "soap:result", tname);
1129 g_free(tname);
1130
1131 return reply;
1132 }
1133
rpc_Mount(GList * args)1134 static xmlNodePtr rpc_Mount(GList *args)
1135 {
1136 GList *paths;
1137 int open_dir, quiet;
1138
1139 paths = list_value(ARG(0));
1140 open_dir = bool_value(ARG(1));
1141 quiet = bool_value(ARG(2));
1142
1143 if (open_dir == -1)
1144 open_dir = TRUE;
1145
1146 if (paths)
1147 action_mount(paths, open_dir, TRUE, quiet);
1148
1149 destroy_glist(&paths);
1150
1151 return NULL;
1152 }
1153
rpc_Unmount(GList * args)1154 static xmlNodePtr rpc_Unmount(GList *args)
1155 {
1156 GList *paths;
1157 int quiet;
1158
1159 paths = list_value(ARG(0));
1160 quiet = bool_value(ARG(1));
1161
1162 if (paths)
1163 action_mount(paths, FALSE, FALSE, quiet);
1164
1165 destroy_glist(&paths);
1166
1167 return NULL;
1168 }
1169
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!
1173 */
soap_done(GtkWidget * widget,GdkEventProperty * event,gpointer data)1174 static void soap_done(GtkWidget *widget, GdkEventProperty *event, gpointer data)
1175 {
1176 static int times_called = 0;
1177 GdkAtom prop = (GdkAtom) data;
1178
1179 if (prop != event->atom)
1180 return;
1181
1182 times_called++;
1183 g_return_if_fail(times_called < 3);
1184
1185 if (times_called == 1)
1186 return;
1187
1188 /* If we got a reply, display it here */
1189 if (event->state == GDK_PROPERTY_NEW_VALUE)
1190 {
1191 gint length;
1192
1193 data = read_property(event->window, event->atom, &length);
1194
1195 if (data)
1196 puts(data);
1197 }
1198
1199 gtk_main_quit();
1200 }
1201
too_slow(gpointer data)1202 static gboolean too_slow(gpointer data)
1203 {
1204 g_warning("Existing ROX-Filer process is not responding! Try with -n");
1205 gtk_main_quit();
1206
1207 return 0;
1208 }
1209
1210 /* Send the SOAP message in property 'prop' on 'from' to 'dest' */
soap_send(GtkWidget * from,GdkAtom prop,GdkWindow * dest)1211 static void soap_send(GtkWidget *from, GdkAtom prop, GdkWindow *dest)
1212 {
1213 GdkEventClient event;
1214
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;
1219
1220 gdk_event_send_client_message((GdkEvent *) &event,
1221 GDK_WINDOW_XWINDOW(dest));
1222
1223 g_timeout_add(10000, too_slow, NULL);
1224
1225 gtk_main();
1226 }
1227
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.
1231 */
soap_invoke(xmlNode * method)1232 static xmlNodePtr soap_invoke(xmlNode *method)
1233 {
1234 GList *args = NULL;
1235 SOAP_call *call;
1236 gchar **arg;
1237 xmlNodePtr retval = NULL;
1238 GHashTable *name_to_node;
1239 xmlNode *node;
1240
1241 call = g_hash_table_lookup(rpc_calls, method->name);
1242 if (!call)
1243 {
1244 xmlNodePtr reply;
1245 gchar *err;
1246
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);
1255 g_free(err);
1256 return reply;
1257 }
1258
1259 name_to_node = g_hash_table_new(g_str_hash, g_str_equal);
1260 for (node = method->xmlChildrenNode; node; node = node->next)
1261 {
1262 if (node->type != XML_ELEMENT_NODE)
1263 continue;
1264
1265 if (node->ns == NULL || strcmp(node->ns->href, ROX_NS) != 0)
1266 continue;
1267
1268 g_hash_table_insert(name_to_node, (gchar *) node->name, node);
1269 }
1270
1271 if (call->required_args)
1272 {
1273 for (arg = call->required_args; *arg; arg++)
1274 {
1275 node = g_hash_table_lookup(name_to_node, *arg);
1276 if (!node)
1277 {
1278 g_warning("Missing required argument '%s' "
1279 "in call to method '%s'", *arg,
1280 method->name);
1281 goto out;
1282 }
1283
1284 args = g_list_append(args, node);
1285 }
1286 }
1287
1288 if (call->optional_args)
1289 {
1290 for (arg = call->optional_args; *arg; arg++)
1291 args = g_list_append(args,
1292 g_hash_table_lookup(name_to_node, *arg));
1293 }
1294
1295 retval = call->func(args);
1296
1297 out:
1298 g_hash_table_destroy(name_to_node);
1299 g_list_free(args);
1300
1301 return retval;
1302 }
1303