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