1 /*
2  * This file is part of the Main Menu.
3  *
4  * Copyright (c) 2007 Novell, Inc.
5  *
6  * The Main Menu is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the Free
8  * Software Foundation; either version 2 of the License, or (at your option)
9  * any later version.
10  *
11  * The Main Menu is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
14  * more details.
15  *
16  * You should have received a copy of the GNU General Public License along with
17  * the Main Menu; if not, write to the Free Software Foundation, Inc., 51
18  * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20 
21 #include "bookmark-agent.h"
22 
23 #ifdef HAVE_CONFIG_H
24 #	include <config.h>
25 #else
26 #	define PACKAGE "mate-main-menu"
27 #endif
28 
29 #include <gtk/gtk.h>
30 
31 #include <string.h>
32 #include <stdlib.h>
33 #include <glib/gi18n-lib.h>
34 #include <glib/gstdio.h>
35 #include <gio/gio.h>
36 
37 #include "libslab-utils.h"
38 
39 #define USER_APPS_STORE_FILE_NAME "applications.xbel"
40 #define USER_DOCS_STORE_FILE_NAME "documents.xbel"
41 #define USER_DIRS_STORE_FILE_NAME "places.xbel"
42 #define SYSTEM_STORE_FILE_NAME    "system-items.xbel"
43 #define CALC_TEMPLATE_FILE_NAME   "empty.ots"
44 #define WRITER_TEMPLATE_FILE_NAME "empty.ott"
45 
46 #define GTK_BOOKMARKS_FILE "bookmarks"
47 
48 #define TYPE_IS_RECENT(type) ((type) == BOOKMARK_STORE_RECENT_APPS || (type) == BOOKMARK_STORE_RECENT_DOCS)
49 
50 typedef struct {
51 	BookmarkStoreType        type;
52 
53 	BookmarkItem           **items;
54 	gint                     n_items;
55 	BookmarkStoreStatus      status;
56 
57 	GBookmarkFile           *store;
58 	gboolean                 needs_sync;
59 
60 	gchar                   *store_path;
61 	gchar                   *user_store_path;
62 	gboolean                 user_modifiable;
63 	gboolean                 reorderable;
64 	const gchar             *store_filename;
65 
66 	GFileMonitor            *store_monitor;
67 	GFileMonitor            *user_store_monitor;
68 
69 	void                  (* update_path) (BookmarkAgent *);
70 	void                  (* load_store)  (BookmarkAgent *);
71 	void                  (* save_store)  (BookmarkAgent *);
72 	void                  (* create_item) (BookmarkAgent *, const gchar *);
73 
74 	gchar                   *gtk_store_path;
75 	GFileMonitor            *gtk_store_monitor;
76 } BookmarkAgentPrivate;
77 
78 enum {
79 	PROP_0,
80 	PROP_ITEMS,
81 	PROP_STATUS
82 };
83 
84 static BookmarkAgent *instances [BOOKMARK_STORE_N_TYPES];
85 
86 static BookmarkAgentClass *bookmark_agent_parent_class = NULL;
87 
88 static void           bookmark_agent_base_init  (BookmarkAgentClass *);
89 static void           bookmark_agent_class_init (BookmarkAgentClass *);
90 static void           bookmark_agent_init       (BookmarkAgent      *);
91 static BookmarkAgent *bookmark_agent_new        (BookmarkStoreType   );
92 
93 static void get_property (GObject *, guint, GValue *, GParamSpec *);
94 static void set_property (GObject *, guint, const GValue *, GParamSpec *);
95 static void finalize     (GObject *);
96 
97 static void update_agent (BookmarkAgent *);
98 static void update_items (BookmarkAgent *);
99 static void save_store   (BookmarkAgent *);
100 static gint get_rank     (BookmarkAgent *, const gchar *);
101 static void set_rank     (BookmarkAgent *, const gchar *, gint);
102 
103 static void load_xbel_store          (BookmarkAgent *);
104 static void load_places_store        (BookmarkAgent *);
105 static void update_user_spec_path    (BookmarkAgent *);
106 static void save_xbel_store          (BookmarkAgent *);
107 static void create_app_item          (BookmarkAgent *, const gchar *);
108 static void create_doc_item          (BookmarkAgent *, const gchar *);
109 static void create_dir_item          (BookmarkAgent *, const gchar *);
110 
111 static void store_monitor_cb (GFileMonitor *, GFile *, GFile *,
112                               GFileMonitorEvent, gpointer);
113 static void weak_destroy_cb  (gpointer, GObject *);
114 
115 static gchar *find_package_data_file (const gchar *filename);
116 
117 static gint BookmarkAgent_private_offset;
118 
bookmark_agent_get_instance_private(BookmarkAgent * this)119 static inline gpointer bookmark_agent_get_instance_private (BookmarkAgent *this)
120 {
121 	return (G_STRUCT_MEMBER_P (this, BookmarkAgent_private_offset));
122 }
123 
124 GType
bookmark_agent_get_type()125 bookmark_agent_get_type ()
126 {
127 	static GType g_define_type_id = 0;
128 
129 	if (G_UNLIKELY (g_define_type_id == 0)) {
130 		static const GTypeInfo info = {
131 			sizeof (BookmarkAgentClass),
132 			(GBaseInitFunc) bookmark_agent_base_init,
133 			NULL,
134 			(GClassInitFunc) bookmark_agent_class_init,
135 			NULL, NULL,
136 			sizeof (BookmarkAgent), 0,
137 			(GInstanceInitFunc) bookmark_agent_init,
138 			NULL
139 		};
140 
141 		g_define_type_id = g_type_register_static (
142 			G_TYPE_OBJECT, "BookmarkAgent", & info, 0);
143 		G_ADD_PRIVATE (BookmarkAgent);
144 	}
145 
146 	return g_define_type_id;
147 }
148 
149 gboolean
bookmark_agent_has_item(BookmarkAgent * this,const gchar * uri)150 bookmark_agent_has_item (BookmarkAgent *this, const gchar *uri)
151 {
152 	BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
153 	return g_bookmark_file_has_item (priv->store, uri);
154 }
155 
156 void
bookmark_agent_add_item(BookmarkAgent * this,const BookmarkItem * item)157 bookmark_agent_add_item (BookmarkAgent *this, const BookmarkItem *item)
158 {
159 	BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
160 
161 	if (! item)
162 		return;
163 
164 	g_return_if_fail (priv->user_modifiable);
165 	g_return_if_fail (item->uri);
166 	g_return_if_fail (item->mime_type);
167 
168 	g_bookmark_file_set_mime_type (priv->store, item->uri, item->mime_type);
169 
170 	if (item->mtime)
171 		g_bookmark_file_set_modified_date_time (priv->store, item->uri, item->mtime);
172 
173 	if (item->title)
174 		g_bookmark_file_set_title (priv->store, item->uri, item->title);
175 
176 	g_bookmark_file_add_application (priv->store, item->uri, item->app_name, item->app_exec);
177 
178 	set_rank (this, item->uri, g_bookmark_file_get_size (priv->store) - 1);
179 
180 	save_store (this);
181 }
182 
183 void
bookmark_agent_move_item(BookmarkAgent * this,const gchar * uri,const gchar * uri_new)184 bookmark_agent_move_item (BookmarkAgent *this, const gchar *uri, const gchar *uri_new)
185 {
186 	BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
187 
188 	GError *error = NULL;
189 
190 	if (! TYPE_IS_RECENT (priv->type))
191 		return;
192 
193 	gtk_recent_manager_move_item (gtk_recent_manager_get_default (), uri, uri_new, &error);
194 	if (error) {
195 		g_warning ("Unable to update %s with renamed file, [%s] -> [%s]: %s",
196 		           priv->store_path, uri, uri_new, error->message);
197 		g_error_free (error);
198 	}
199 }
200 
201 void
bookmark_agent_purge_items(BookmarkAgent * this)202 bookmark_agent_purge_items (BookmarkAgent *this)
203 {
204 	BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
205 
206 	GError *error = NULL;
207 
208 	gchar **uris = NULL;
209 	gsize   uris_len;
210 	gint    i;
211 	g_return_if_fail (priv->user_modifiable);
212 
213 	uris = g_bookmark_file_get_uris (priv->store, &uris_len);
214 	if (TYPE_IS_RECENT (priv->type)) {
215 		for (i = 0; i < uris_len; i++) {
216 			gtk_recent_manager_remove_item (gtk_recent_manager_get_default (), uris [i], &error);
217 			if (error) {
218 				g_warning ("Unable to remove [%s] from %s: %s",
219 				           priv->store_path, uris [i], error->message);
220 				g_error_free (error);
221 			}
222 		}
223 	} else {
224 		for (i = 0; i < uris_len; i++) {
225 			g_bookmark_file_remove_item (priv->store, uris [i], NULL);
226 		}
227 		save_store (this);
228 	}
229 	g_strfreev (uris);
230 }
231 
232 void
bookmark_agent_remove_item(BookmarkAgent * this,const gchar * uri)233 bookmark_agent_remove_item (BookmarkAgent *this, const gchar *uri)
234 {
235         BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
236         gint rank;
237 
238         GError *error = NULL;
239 
240         gchar **uris = NULL;
241         gint    rank_i;
242         gint    i;
243 
244 
245         g_return_if_fail (priv->user_modifiable);
246 
247 	if (! bookmark_agent_has_item (this, uri))
248 		return;
249 
250 	if (TYPE_IS_RECENT (priv->type)) {
251 		gtk_recent_manager_remove_item (gtk_recent_manager_get_default (), uri, &error);
252 		if (error) {
253 			g_warning ("Unable to remove [%s] from %s: %s", priv->store_path, uri, error->message);
254 			g_error_free (error);
255 		}
256 	}
257 	else {
258 		rank = get_rank (this, uri);
259 
260 		g_bookmark_file_remove_item (priv->store, uri, NULL);
261 
262 		if (rank >= 0) {
263 			uris = g_bookmark_file_get_uris (priv->store, NULL);
264 
265 			for (i =  0; uris && uris [i]; ++i) {
266 				rank_i = get_rank (this, uris [i]);
267 
268 				if (rank_i > rank)
269 					set_rank (this, uris [i], rank_i - 1);
270 			}
271 
272 			g_strfreev (uris);
273 		}
274 
275 		save_store (this);
276 	}
277 }
278 
279 void
bookmark_agent_reorder_items(BookmarkAgent * this,const gchar ** uris)280 bookmark_agent_reorder_items (BookmarkAgent *this, const gchar **uris)
281 {
282 	BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
283 
284 	gint i;
285 
286 
287 	g_return_if_fail (priv->reorderable);
288 
289 	for (i = 0; uris && uris [i]; ++i)
290 		set_rank (this, uris [i], i);
291 
292 	save_store (this);
293 }
294 
295 static GList *
make_items_from_bookmark_file(BookmarkAgent * this,GBookmarkFile * store)296 make_items_from_bookmark_file (BookmarkAgent *this, GBookmarkFile *store)
297 {
298 	BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
299 	gchar **uris;
300 	gint i;
301 	GList *items_ordered;
302 
303 	if (!store)
304 		return NULL;
305 
306 	uris = g_bookmark_file_get_uris (store, NULL);
307 	items_ordered = NULL;
308 
309 	for (i = 0; uris && uris [i]; ++i) {
310 		gboolean include;
311 
312 		if (priv->type == BOOKMARK_STORE_RECENT_APPS)
313 			include = g_bookmark_file_has_group (store, uris [i], "recently-used-apps", NULL);
314 		else
315 			include = ! g_bookmark_file_get_is_private (store, uris [i], NULL);
316 
317 		if (include) {
318 			BookmarkItem *item;
319 
320 			item = g_new0 (BookmarkItem, 1);
321 
322 			item->uri       = g_strdup (uris [i]);
323 			item->mime_type = g_bookmark_file_get_mime_type (store, uris [i], NULL);
324 			item->mtime     = g_bookmark_file_get_modified_date_time  (store, uris [i], NULL);
325 
326 			items_ordered = g_list_prepend (items_ordered, item);
327 		}
328 	}
329 
330 	items_ordered = g_list_sort (items_ordered, g_date_time_compare);
331 
332 	g_strfreev (uris);
333 
334 	return items_ordered;
335 }
336 
337 void
bookmark_agent_update_from_bookmark_file(BookmarkAgent * this,GBookmarkFile * store)338 bookmark_agent_update_from_bookmark_file (BookmarkAgent *this, GBookmarkFile *store)
339 {
340 	BookmarkAgentPrivate *priv;
341 	GList *items_ordered;
342 	GList  *node;
343 
344 	g_return_if_fail (IS_BOOKMARK_AGENT (this));
345 
346 	priv = bookmark_agent_get_instance_private (this);
347 
348 	items_ordered = make_items_from_bookmark_file (this, store);
349 
350 	g_bookmark_file_free (priv->store);
351 	priv->store = g_bookmark_file_new ();
352 
353 	for (node = items_ordered; node; node = node->next) {
354 		BookmarkItem *item;
355 
356 		item = (BookmarkItem *) node->data;
357 
358 		g_bookmark_file_set_mime_type (priv->store, item->uri, item->mime_type);
359 		g_bookmark_file_set_modified_date_time  (priv->store, item->uri, item->mtime);
360 
361 		bookmark_item_free (item);
362 	}
363 
364 	g_list_free (items_ordered);
365 
366 	update_items (this);
367 }
368 
369 void
bookmark_item_free(BookmarkItem * item)370 bookmark_item_free (BookmarkItem *item)
371 {
372 	if (! item)
373 		return;
374 
375 	g_free (item->uri);
376 	g_free (item->title);
377 	g_free (item->mime_type);
378 	g_free (item->icon);
379 	g_free (item->app_name);
380 	g_free (item->app_exec);
381 	g_free (item);
382 }
383 
384 static void
bookmark_agent_base_init(BookmarkAgentClass * this_class)385 bookmark_agent_base_init (BookmarkAgentClass *this_class)
386 {
387 	gint i;
388 
389 	for (i = 0; i < BOOKMARK_STORE_N_TYPES; ++i)
390 		instances [i] = NULL;
391 }
392 
393 static void
bookmark_agent_class_init(BookmarkAgentClass * this_class)394 bookmark_agent_class_init (BookmarkAgentClass *this_class)
395 {
396 	GObjectClass *g_obj_class = G_OBJECT_CLASS (this_class);
397 
398 	GParamSpec *items_pspec;
399 	GParamSpec *status_pspec;
400 
401 	if (BookmarkAgent_private_offset != 0)
402 		g_type_class_adjust_private_offset (this_class, &BookmarkAgent_private_offset);
403 
404 	g_obj_class->get_property = get_property;
405 	g_obj_class->set_property = set_property;
406 	g_obj_class->finalize     = finalize;
407 
408 	items_pspec = g_param_spec_pointer (
409 		BOOKMARK_AGENT_ITEMS_PROP, BOOKMARK_AGENT_ITEMS_PROP,
410 		"the null-terminated list which contains the bookmark items in this store",
411 		G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
412 
413 	status_pspec = g_param_spec_int (
414 		BOOKMARK_AGENT_STORE_STATUS_PROP, BOOKMARK_AGENT_STORE_STATUS_PROP, "the status of the store",
415 		BOOKMARK_STORE_DEFAULT_ONLY, BOOKMARK_STORE_USER, BOOKMARK_STORE_DEFAULT,
416 		G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
417 
418 	g_object_class_install_property (g_obj_class, PROP_ITEMS,  items_pspec);
419 	g_object_class_install_property (g_obj_class, PROP_STATUS, status_pspec);
420 
421 	bookmark_agent_parent_class = g_type_class_peek_parent (this_class);
422 }
423 
424 static void
bookmark_agent_init(BookmarkAgent * this)425 bookmark_agent_init (BookmarkAgent *this)
426 {
427 	BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
428 
429 	priv->type                = -1;
430 
431  	priv->items               = NULL;
432  	priv->n_items             = 0;
433 	priv->status              = BOOKMARK_STORE_ABSENT;
434 
435 	priv->store               = NULL;
436 	priv->needs_sync          = FALSE;
437 
438 	priv->store_path          = NULL;
439 	priv->user_store_path     = NULL;
440 	priv->user_modifiable     = FALSE;
441 	priv->reorderable         = FALSE;
442 	priv->store_filename      = NULL;
443 
444 	priv->store_monitor       = NULL;
445 	priv->user_store_monitor  = NULL;
446 
447 	priv->update_path         = NULL;
448 	priv->load_store          = NULL;
449 	priv->save_store          = NULL;
450 	priv->create_item         = NULL;
451 
452 	priv->gtk_store_path      = NULL;
453 	priv->gtk_store_monitor   = NULL;
454 }
455 
456 static BookmarkAgent *
bookmark_agent_new(BookmarkStoreType type)457 bookmark_agent_new (BookmarkStoreType type)
458 {
459 	BookmarkAgent        *this;
460 	BookmarkAgentPrivate *priv;
461 	GFile *gtk_store_file;
462 
463 	this = g_object_new (BOOKMARK_AGENT_TYPE, NULL);
464 	priv = bookmark_agent_get_instance_private (this);
465 
466 	priv->type  = type;
467 	priv->store = g_bookmark_file_new ();
468 
469 	switch (type) {
470 		case BOOKMARK_STORE_USER_APPS:
471 			priv->store_filename = USER_APPS_STORE_FILE_NAME;
472 			priv->create_item    = create_app_item;
473 
474 			break;
475 
476 		case BOOKMARK_STORE_USER_DOCS:
477 			priv->store_filename = USER_DOCS_STORE_FILE_NAME;
478 			priv->create_item    = create_doc_item;
479 
480 			break;
481 
482 		case BOOKMARK_STORE_USER_DIRS:
483 			priv->store_filename = USER_DIRS_STORE_FILE_NAME;
484 			priv->create_item    = create_dir_item;
485 
486 			priv->user_modifiable = TRUE;
487 			priv->reorderable     = FALSE;
488 
489 			priv->load_store = load_places_store;
490 
491 			priv->gtk_store_path = g_build_filename (g_get_user_config_dir (),
492                                                      "gtk-3.0", GTK_BOOKMARKS_FILE, NULL);
493 			gtk_store_file = g_file_new_for_path (priv->gtk_store_path);
494 			priv->gtk_store_monitor = g_file_monitor_file (gtk_store_file,
495 								       0, NULL, NULL);
496 			if (priv->gtk_store_monitor) {
497 				g_signal_connect (priv->gtk_store_monitor, "changed",
498 						  G_CALLBACK (store_monitor_cb), this);
499 			}
500 
501 			g_object_unref (gtk_store_file);
502 
503 			break;
504 
505 		case BOOKMARK_STORE_RECENT_APPS:
506 		case BOOKMARK_STORE_RECENT_DOCS:
507 			priv->user_modifiable = TRUE;
508 			priv->reorderable     = FALSE;
509 
510 			priv->store_path = g_build_filename (g_get_user_data_dir (), "recently-used.xbel", NULL);
511 
512 			break;
513 
514 		case BOOKMARK_STORE_SYSTEM:
515 			priv->store_filename = SYSTEM_STORE_FILE_NAME;
516 			priv->create_item    = create_app_item;
517 
518 			break;
519 
520 		default:
521 			break;
522 	}
523 
524 	if (
525 		type == BOOKMARK_STORE_USER_APPS || type == BOOKMARK_STORE_USER_DOCS ||
526 		type == BOOKMARK_STORE_USER_DIRS || type == BOOKMARK_STORE_SYSTEM)
527 	{
528 		priv->user_modifiable = TRUE;
529 
530 		priv->user_store_path = g_build_filename (
531 			g_get_user_data_dir (), PACKAGE, priv->store_filename, NULL);
532 
533 		priv->update_path = update_user_spec_path;
534 	}
535 
536 	if (type == BOOKMARK_STORE_USER_APPS || type == BOOKMARK_STORE_USER_DOCS || type == BOOKMARK_STORE_SYSTEM) {
537 		priv->reorderable = TRUE;
538 		priv->load_store  = load_xbel_store;
539 		priv->save_store  = save_xbel_store;
540 	}
541 
542 	update_agent (this);
543 
544 	return this;
545 }
546 
547 BookmarkAgent *
bookmark_agent_get_instance(BookmarkStoreType type)548 bookmark_agent_get_instance (BookmarkStoreType type)
549 {
550 	g_return_val_if_fail (0 <= type, NULL);
551 	g_return_val_if_fail (type < BOOKMARK_STORE_N_TYPES, NULL);
552 
553 	if (! instances [type]) {
554 		instances [type] = bookmark_agent_new (type);
555 		g_object_weak_ref (G_OBJECT (instances [type]), weak_destroy_cb, GINT_TO_POINTER (type));
556 	}
557 	else
558 		g_object_ref (G_OBJECT (instances [type]));
559 
560 	return instances [type];
561 }
562 
563 static void
get_property(GObject * g_obj,guint prop_id,GValue * value,GParamSpec * pspec)564 get_property (GObject *g_obj, guint prop_id, GValue *value, GParamSpec *pspec)
565 {
566 	BookmarkAgent        *this = BOOKMARK_AGENT (g_obj);
567 	BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
568 
569 
570 	switch (prop_id) {
571 		case PROP_ITEMS:
572 			g_value_set_pointer (value, priv->items);
573 			break;
574 
575 		case PROP_STATUS:
576 			g_value_set_int (value, priv->status);
577 			break;
578 	}
579 }
580 
581 static void
set_property(GObject * g_obj,guint prop_id,const GValue * value,GParamSpec * pspec)582 set_property (GObject *g_obj, guint prop_id, const GValue *value, GParamSpec *pspec)
583 {
584 	/* no writeable properties */
585 }
586 
587 static void
finalize(GObject * g_obj)588 finalize (GObject *g_obj)
589 {
590 	BookmarkAgent *this = BOOKMARK_AGENT (g_obj);
591 	BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
592 
593 	gint i;
594 
595 
596 	for (i = 0; priv->items && priv->items [i]; ++i)
597 		bookmark_item_free (priv->items [i]);
598 
599 	g_free (priv->items);
600 	g_free (priv->store_path);
601 	g_free (priv->user_store_path);
602 	g_free (priv->gtk_store_path);
603 
604 	if (priv->store_monitor) {
605 		g_signal_handlers_disconnect_by_func (priv->store_monitor, store_monitor_cb, this);
606 		g_file_monitor_cancel (priv->store_monitor);
607 		g_object_unref (priv->store_monitor);
608 	}
609 
610 	if (priv->user_store_monitor) {
611 		g_signal_handlers_disconnect_by_func (priv->user_store_monitor, store_monitor_cb, this);
612 		g_file_monitor_cancel (priv->user_store_monitor);
613 		g_object_unref (priv->user_store_monitor);
614 	}
615 
616 	if (priv->gtk_store_monitor) {
617 		g_signal_handlers_disconnect_by_func (priv->gtk_store_monitor, store_monitor_cb, this);
618 		g_file_monitor_cancel (priv->gtk_store_monitor);
619 		g_object_unref (priv->gtk_store_monitor);
620 	}
621 
622 	g_bookmark_file_free (priv->store);
623 
624 	G_OBJECT_CLASS (bookmark_agent_parent_class)->finalize (g_obj);
625 }
626 
627 static void
update_agent(BookmarkAgent * this)628 update_agent (BookmarkAgent *this)
629 {
630 	BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
631 
632 	if (priv->update_path)
633 		priv->update_path (this);
634 
635 	if (priv->load_store)
636 		priv->load_store (this);
637 
638 	update_items (this);
639 }
640 
641 static void
update_items(BookmarkAgent * this)642 update_items (BookmarkAgent *this)
643 {
644 	BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
645 
646 	gchar    **uris            = NULL;
647 	gchar    **uris_ordered    = NULL;
648 	gsize      n_uris          = 0;
649 	gint       rank            = -1;
650 	gint       rank_corr       = -1;
651 	gboolean   needs_update    = FALSE;
652 	gboolean   store_corrupted = FALSE;
653 	gchar     *new_title, *old_title;
654 
655 	gint i;
656 
657 
658 	uris = g_bookmark_file_get_uris (priv->store, & n_uris);
659 	uris_ordered = g_new0 (gchar *, n_uris + 1);
660 	uris_ordered [n_uris] = NULL;
661 
662 	for (i = 0; uris && uris [i]; ++i) {
663 		rank = get_rank (this, uris [i]);
664 
665 		if (rank < 0 || rank >= n_uris)
666 			rank = i;
667 
668 		if (uris_ordered [rank]) {
669 			store_corrupted = TRUE;
670 			rank_corr = rank;
671 
672 			for (rank = 0; rank < n_uris; ++rank)
673 				if (! uris_ordered [rank])
674 					break;
675 
676 			g_warning (
677 				"store corruption [%s] - multiple uris with same rank (%d): [%s] [%s], moving latter to %d",
678 				priv->store_path, rank_corr, uris_ordered [rank_corr], uris [i], rank);
679 		}
680 
681 		set_rank (this, uris [i], rank);
682 
683 		uris_ordered [rank] = uris [i];
684 	}
685 
686 	if (priv->n_items != n_uris)
687 		needs_update = TRUE;
688 
689 	for (i = 0; ! needs_update && uris_ordered && uris_ordered [i]; ++i) {
690 		if (priv->type == BOOKMARK_STORE_USER_DIRS) {
691 			new_title = g_bookmark_file_get_title (priv->store, uris_ordered [i], NULL);
692 			old_title = priv->items [i]->title;
693 			if (!new_title && !old_title) {
694 				if (strcmp (priv->items [i]->uri, uris_ordered [i]))
695 					needs_update = TRUE;
696 			}
697 			else if ((new_title && !old_title) || (!new_title && old_title))
698 				needs_update = TRUE;
699 			else if (strcmp (old_title, new_title))
700 				needs_update = TRUE;
701 			g_free (new_title);
702 		}
703 		else if (strcmp (priv->items [i]->uri, uris_ordered [i]))
704 			needs_update = TRUE;
705 	}
706 
707 	if (needs_update) {
708 		for (i = 0; priv->items && priv->items [i]; ++i)
709 			bookmark_item_free (priv->items [i]);
710 
711 		g_free (priv->items);
712 
713 		priv->n_items = n_uris;
714 		priv->items = g_new0 (BookmarkItem *, priv->n_items + 1);
715 
716 		for (i = 0; uris_ordered && uris_ordered [i]; ++i) {
717 			priv->items [i]            = g_new0 (BookmarkItem, 1);
718 			priv->items [i]->uri       = g_strdup (uris_ordered [i]);
719 			priv->items [i]->title     = g_bookmark_file_get_title     (priv->store, uris_ordered [i], NULL);
720 			priv->items [i]->mime_type = g_bookmark_file_get_mime_type (priv->store, uris_ordered [i], NULL);
721 			priv->items [i]->mtime     = g_bookmark_file_get_modified_date_time (priv->store, uris_ordered [i], NULL);
722 			priv->items [i]->app_name  = NULL;
723 			priv->items [i]->app_exec  = NULL;
724 
725 			g_bookmark_file_get_icon (priv->store, uris_ordered [i], & priv->items [i]->icon, NULL, NULL);
726 		}
727 
728 		/* Since the bookmark store for recently-used items is updated by the caller of BookmarkAgent,
729 		 * we don't emit notifications in that case.  The caller will know when to update itself.
730 		 */
731 		if (!TYPE_IS_RECENT (priv->type))
732 			g_object_notify (G_OBJECT (this), BOOKMARK_AGENT_ITEMS_PROP);
733 	}
734 
735 	if (store_corrupted)
736 		save_store (this);
737 
738 	g_strfreev (uris);
739 	g_free (uris_ordered);
740 }
741 
742 static void
save_store(BookmarkAgent * this)743 save_store (BookmarkAgent *this)
744 {
745 	BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
746 
747 	gchar *dir;
748 
749 
750 	g_return_if_fail (priv->user_modifiable);
751 
752 	priv->needs_sync = TRUE;
753 	priv->update_path (this);
754 
755 	dir = g_path_get_dirname (priv->store_path);
756 	g_mkdir_with_parents (dir, 0700);
757 	g_free (dir);
758 
759 	priv->save_store (this);
760 	update_items (this);
761 }
762 
763 static gint
get_rank(BookmarkAgent * this,const gchar * uri)764 get_rank (BookmarkAgent *this, const gchar *uri)
765 {
766 	BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
767 
768 	gchar **groups;
769 	gint    rank;
770 
771 	gint i;
772 
773 
774 	if (! priv->reorderable)
775 		return -1;
776 
777 	groups = g_bookmark_file_get_groups (priv->store, uri, NULL, NULL);
778 	rank   = -1;
779 
780 	for (i = 0; groups && groups [i]; ++i) {
781 		if (g_str_has_prefix (groups [i], "rank-")) {
782 			if (rank >= 0)
783 				g_warning (
784 					"store corruption - multiple ranks for same uri: [%s] [%s]",
785 					priv->store_path, uri);
786 
787 			rank = atoi (& groups [i] [5]);
788 		}
789 	}
790 
791 	g_strfreev (groups);
792 
793 	return rank;
794 }
795 
796 static void
set_rank(BookmarkAgent * this,const gchar * uri,gint rank)797 set_rank (BookmarkAgent *this, const gchar *uri, gint rank)
798 {
799 	BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
800 
801 	gchar **groups;
802 	gchar  *group;
803 
804 	gint i;
805 
806 
807 	if (! (priv->reorderable && bookmark_agent_has_item (this, uri)))
808 		return;
809 
810 	groups = g_bookmark_file_get_groups (priv->store, uri, NULL, NULL);
811 
812 	for (i = 0; groups && groups [i]; ++i)
813 		if (g_str_has_prefix (groups [i], "rank-"))
814 			g_bookmark_file_remove_group (priv->store, uri, groups [i], NULL);
815 
816 	g_strfreev (groups);
817 
818 	group = g_strdup_printf ("rank-%d", rank);
819 	g_bookmark_file_add_group (priv->store, uri, group);
820 	g_free (group);
821 }
822 
823 static void
load_xbel_store(BookmarkAgent * this)824 load_xbel_store (BookmarkAgent *this)
825 {
826 	BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
827 
828 	gchar **uris = NULL;
829 
830 	GError *error = NULL;
831 
832 	gint i;
833 	gboolean success;
834 
835 	if (!priv->store_path)
836 		success = FALSE;
837 	else {
838 		success = g_bookmark_file_load_from_file (priv->store, priv->store_path, & error);
839 	}
840 
841 	if (!success) {
842 		g_bookmark_file_free (priv->store);
843 		priv->store = g_bookmark_file_new ();
844 
845 		if (error) {
846 			g_debug ("Couldn't load bookmark file [%s]: %s", priv->store_path, error->message);
847 			g_error_free (error);
848 		} else {
849 			g_debug ("Couldn't load bookmark file [NULL]");
850 		}
851 		return;
852 	}
853 
854 	uris = g_bookmark_file_get_uris (priv->store, NULL);
855 
856 	for (i = 0; uris && uris [i]; ++i)
857 		priv->create_item (this, uris [i]);
858 
859 	g_strfreev (uris);
860 }
861 
862 static void
load_places_store(BookmarkAgent * this)863 load_places_store (BookmarkAgent *this)
864 {
865 	BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
866 
867 	gchar **uris;
868 	gchar **groups;
869 	gchar **bookmarks = NULL;
870 
871 	gchar  *buf, *label, *uri;
872 
873 	gint i, j, bookmark_len;
874 
875 	load_xbel_store (this);
876 
877 	uris = g_bookmark_file_get_uris (priv->store, NULL);
878 
879 	for (i = 0; uris && uris [i]; ++i) {
880 		groups = g_bookmark_file_get_groups (priv->store, uris [i], NULL, NULL);
881 
882 		for (j = 0; groups && groups [j]; ++j) {
883 			if (! strcmp (groups [j], "gtk-bookmarks")) {
884 				g_bookmark_file_remove_item (priv->store, uris [i], NULL);
885 
886 				break;
887 			}
888 		}
889 
890 		g_strfreev (groups);
891 	}
892 
893 	g_strfreev (uris);
894 
895 	g_file_get_contents (priv->gtk_store_path, & buf, NULL, NULL);
896 
897 	if (buf) {
898 		bookmarks = g_strsplit (buf, "\n", -1);
899 		g_free (buf);
900 	}
901 
902 	for (i = 0; bookmarks && bookmarks [i]; ++i) {
903 		bookmark_len = strlen (bookmarks [i]);
904 		if (bookmark_len > 0) {
905 			label = strstr (bookmarks[i], " ");
906 			if (label != NULL)
907 				uri = g_strndup (bookmarks [i], bookmark_len - strlen (label));
908 			else
909 				uri = bookmarks [i];
910 			g_bookmark_file_add_group (priv->store, uri, "gtk-bookmarks");
911 			priv->create_item (this, uri);
912 			if (label != NULL) {
913 				label++;
914 				if (strlen (label) > 0)
915 					g_bookmark_file_set_title (priv->store, uri, label);
916 				g_free (uri);
917 			}
918 		}
919 	}
920 
921 	g_strfreev (bookmarks);
922 }
923 
924 static gchar *
find_package_data_file(const gchar * filename)925 find_package_data_file (const gchar *filename)
926 {
927 	const gchar * const *dirs = NULL;
928 	gchar               *path = NULL;
929 	gint                 i;
930 
931 
932 	dirs = g_get_system_data_dirs ();
933 
934 	for (i = 0; ! path && dirs && dirs [i]; ++i) {
935 		path = g_build_filename (dirs [i], PACKAGE, filename, NULL);
936 
937 		if (! g_file_test (path, G_FILE_TEST_EXISTS)) {
938 			g_free (path);
939 			path = NULL;
940 		}
941 	}
942 
943 	return path;
944 }
945 
946 static void
update_user_spec_path(BookmarkAgent * this)947 update_user_spec_path (BookmarkAgent *this)
948 {
949 	BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
950 
951 	gboolean  use_user_path;
952 	gchar    *path = NULL;
953 
954 	BookmarkStoreStatus status;
955 
956 	use_user_path = priv->user_modifiable &&
957 		(priv->needs_sync || g_file_test (priv->user_store_path, G_FILE_TEST_EXISTS));
958 
959 	if (use_user_path)
960 		path = g_strdup (priv->user_store_path);
961 	else
962 		path = find_package_data_file (priv->store_filename);
963 
964 	if (use_user_path)
965 		status = BOOKMARK_STORE_USER;
966 	else if (path && priv->user_modifiable)
967 		status = BOOKMARK_STORE_DEFAULT;
968 	else if (path)
969 		status = BOOKMARK_STORE_DEFAULT_ONLY;
970 	else
971 		status = BOOKMARK_STORE_ABSENT;
972 
973 	if (priv->status != status) {
974 		priv->status = status;
975 		g_object_notify (G_OBJECT (this), BOOKMARK_AGENT_STORE_STATUS_PROP);
976 
977 		if (priv->user_store_monitor) {
978 			g_file_monitor_cancel (priv->user_store_monitor);
979 			g_object_unref (priv->user_store_monitor);
980 			priv->user_store_monitor = NULL;
981 		}
982 
983 		if (priv->status == BOOKMARK_STORE_DEFAULT) {
984 			GFile *user_store_file;
985 
986 			user_store_file = g_file_new_for_path (priv->user_store_path);
987 			priv->user_store_monitor = g_file_monitor_file (user_store_file,
988 									0, NULL, NULL);
989 			if (priv->user_store_monitor) {
990 				g_signal_connect (priv->user_store_monitor, "changed",
991 						  G_CALLBACK (store_monitor_cb), this);
992 			}
993 
994 			g_object_unref (user_store_file);
995 		}
996 	}
997 
998 	if (g_strcmp0 (priv->store_path, path)) {
999 		g_free (priv->store_path);
1000 		priv->store_path = path;
1001 
1002 		if (priv->store_monitor) {
1003 			g_file_monitor_cancel (priv->store_monitor);
1004 			g_object_unref (priv->store_monitor);
1005 		}
1006 
1007 		if (priv->store_path) {
1008 			GFile *store_file;
1009 
1010 			store_file = g_file_new_for_path (priv->store_path);
1011 			priv->store_monitor = g_file_monitor_file (store_file,
1012 								   0, NULL, NULL);
1013 			if (priv->store_monitor) {
1014 				g_signal_connect (priv->store_monitor, "changed",
1015 						  G_CALLBACK (store_monitor_cb), this);
1016 			}
1017 
1018 			g_object_unref (store_file);
1019 		}
1020 	}
1021 	else
1022 		g_free (path);
1023 }
1024 
1025 static void
save_xbel_store(BookmarkAgent * this)1026 save_xbel_store (BookmarkAgent *this)
1027 {
1028 	BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
1029 
1030 	GError *error = NULL;
1031 
1032 	if (g_bookmark_file_to_file (priv->store, priv->store_path, &error))
1033 		return;
1034 
1035 	if (error) {
1036 		g_warning ("Couldn't save bookmark file [%s]: %s", priv->store_path, error->message);
1037 		g_error_free (error);
1038 	} else {
1039 		g_warning ("Couldn't save bookmark file [%s]", priv->store_path);
1040 	}
1041 }
1042 
1043 static void
create_app_item(BookmarkAgent * this,const gchar * uri)1044 create_app_item (BookmarkAgent *this, const gchar *uri)
1045 {
1046 	BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
1047 
1048 	MateDesktopItem *ditem;
1049 	gchar *uri_new = NULL;
1050 
1051 	ditem = libslab_mate_desktop_item_new_from_unknown_id (uri);
1052 
1053 	if (ditem) {
1054 		uri_new = g_strdup (mate_desktop_item_get_location (ditem));
1055 		mate_desktop_item_unref (ditem);
1056 	}
1057 
1058 	if (! uri_new)
1059 		return;
1060 
1061 	if (g_strcmp0 (uri, uri_new))
1062 		g_bookmark_file_move_item (priv->store, uri, uri_new, NULL);
1063 
1064 	g_free (uri_new);
1065 }
1066 
1067 static void
create_doc_item(BookmarkAgent * this,const gchar * uri)1068 create_doc_item (BookmarkAgent *this, const gchar *uri)
1069 {
1070 	BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
1071 
1072 	gchar *uri_new = NULL;
1073 
1074 	if ((strcmp (uri, "BLANK_SPREADSHEET") == 0) || (strcmp (uri, "BLANK_DOCUMENT") == 0)) {
1075 		gchar *template = NULL;
1076 		gchar *file;
1077 
1078 		gchar *dir = g_strdup (g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
1079 		if (!dir)
1080 			dir = g_build_filename (g_get_home_dir (), "Documents", NULL);
1081 
1082 		if (strcmp (uri, "BLANK_SPREADSHEET") == 0) {
1083 			g_bookmark_file_set_title (priv->store, uri, "BLANK_SPREADSHEET");
1084 			file = g_strconcat (_("New Spreadsheet"), ".ots", NULL);
1085 			template = find_package_data_file (CALC_TEMPLATE_FILE_NAME);
1086 		} else {
1087 			g_bookmark_file_set_title (priv->store, uri, "BLANK_DOCUMENT");
1088 			file = g_strconcat (_("New Document"), ".ott", NULL);
1089 			template = find_package_data_file (WRITER_TEMPLATE_FILE_NAME);
1090 		}
1091 
1092 		gchar *path = g_build_filename (dir, file, NULL);
1093 		if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
1094 			g_mkdir_with_parents (dir, 0700);
1095 
1096 			if (template != NULL) {
1097 				gchar *contents;
1098 				gsize length;
1099 
1100 				if (g_file_get_contents (template, &contents, &length, NULL))
1101 					g_file_set_contents (path, contents, length, NULL);
1102 
1103 				g_free (contents);
1104 			} else {
1105 				fclose (g_fopen (path, "w"));
1106 			}
1107 		}
1108 
1109 		uri_new = g_filename_to_uri (path, NULL, NULL);
1110 
1111 		g_free (dir);
1112 		g_free (file);
1113 		g_free (path);
1114 		g_free (template);
1115 	}
1116 
1117 	if (!uri_new)
1118 		return;
1119 
1120 	if (g_strcmp0 (uri, uri_new))
1121 		g_bookmark_file_move_item (priv->store, uri, uri_new, NULL);
1122 
1123 	g_free (uri_new);
1124 }
1125 
1126 static void
create_dir_item(BookmarkAgent * this,const gchar * uri)1127 create_dir_item (BookmarkAgent *this, const gchar *uri)
1128 {
1129 	BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
1130 
1131 	gchar *uri_new = NULL;
1132 	gchar *path = NULL;
1133 	gchar *name = NULL;
1134 	gchar *icon = NULL;
1135 
1136 	gchar *search_string = NULL;
1137 
1138 	gboolean gotta_free_name = FALSE;
1139 
1140 	if (strcmp (uri, "HOME") == 0) {
1141 		uri_new = g_filename_to_uri (g_get_home_dir (), NULL, NULL);
1142 		name = g_strdup (C_("Home folder", "Home"));
1143 		gotta_free_name = TRUE;
1144 		icon = "user-home";
1145 	} else if (strcmp (uri, "DOCUMENTS") == 0) {
1146 		path = g_strdup (g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
1147 		if (!path)
1148 			path = g_build_filename (g_get_home_dir (), "Documents", NULL);
1149 		name = _("Documents");
1150 		uri_new = g_filename_to_uri (path, NULL, NULL);
1151 	} else if (strcmp (uri, "DESKTOP") == 0) {
1152 		path = g_strdup (g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP));
1153 		if (!path)
1154 			path = g_build_filename (g_get_home_dir (), "Desktop", NULL);
1155 		name = _("Desktop");
1156 		uri_new = g_filename_to_uri (path, NULL, NULL);
1157 		icon = "user-desktop";
1158 	} else if (strcmp (uri, "file:///") == 0) {
1159 		icon = "drive-harddisk";
1160 		name = _("File System");
1161 	} else if (strcmp (uri, "network:") == 0) {
1162 		icon = "network-workgroup";
1163 		name = _("Network Servers");
1164 	} else if (g_str_has_prefix (uri, "x-caja-search")) {
1165 		icon = "system-search";
1166 
1167 		path = g_build_filename (g_get_user_data_dir (), "caja", "searches", & uri [21], NULL);
1168 
1169 		if (g_file_test (path, G_FILE_TEST_EXISTS)) {
1170 			gchar *buf = NULL;
1171 			g_file_get_contents (path, &buf, NULL, NULL);
1172 
1173 			gchar *tag_open_ptr  = NULL;
1174 			gchar *tag_close_ptr = NULL;
1175 
1176 			if (buf) {
1177 				tag_open_ptr  = strstr (buf, "<text>");
1178 				tag_close_ptr = strstr (buf, "</text>");
1179 			}
1180 
1181 			if (tag_open_ptr && tag_close_ptr) {
1182 				tag_close_ptr [0] = '\0';
1183 				tag_close_ptr [0] = 'a';
1184 				search_string = g_strdup_printf ("\"%s\"", &tag_open_ptr[6]);
1185 			}
1186 
1187 			g_free (buf);
1188 		}
1189 
1190 		if (search_string) {
1191 			name = search_string;
1192 			gotta_free_name = TRUE;
1193 		} else {
1194 			name = _("Search");
1195 		}
1196 	}
1197 
1198 	if (icon)
1199 		g_bookmark_file_set_icon (priv->store, uri, icon, "image/png");
1200 
1201 	if (name)
1202 		g_bookmark_file_set_title (priv->store, uri, name);
1203 
1204 	if (uri_new && g_strcmp0 (uri, uri_new))
1205 		g_bookmark_file_move_item (priv->store, uri, uri_new, NULL);
1206 
1207 	if (gotta_free_name) {
1208 		g_free (name);
1209 	}
1210 
1211 	g_free (path);
1212 	g_free (uri_new);
1213 }
1214 
1215 static void
store_monitor_cb(GFileMonitor * mon,GFile * f1,GFile * f2,GFileMonitorEvent event_type,gpointer user_data)1216 store_monitor_cb (GFileMonitor *mon, GFile *f1, GFile *f2,
1217                   GFileMonitorEvent event_type, gpointer user_data)
1218 {
1219 	update_agent (BOOKMARK_AGENT (user_data));
1220 }
1221 
1222 static void
weak_destroy_cb(gpointer data,GObject * g_obj)1223 weak_destroy_cb (gpointer data, GObject *g_obj)
1224 {
1225 	instances [GPOINTER_TO_INT (data)] = NULL;
1226 }
1227