Coverage Report

Created: 2025-08-26 06:04

/src/rauc/subprojects/glib-2.76.5/glib/gbookmarkfile.c
Line
Count
Source (jump to first uncovered line)
1
/* gbookmarkfile.c: parsing and building desktop bookmarks
2
 *
3
 * Copyright (C) 2005-2006 Emmanuele Bassi
4
 *
5
 * SPDX-License-Identifier: LGPL-2.1-or-later
6
 *
7
 * This library is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
11
 *
12
 * This library is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public License
18
 * along with this library; if not, see <http://www.gnu.org/licenses/>.
19
 */
20
21
#include "config.h"
22
23
#include "gbookmarkfile.h"
24
25
#include <stdio.h>
26
#include <stdlib.h>
27
#include <string.h>
28
#include <errno.h>
29
#include <fcntl.h>
30
#include <locale.h>
31
#include <time.h>
32
#include <stdarg.h>
33
34
#include "gconvert.h"
35
#include "gdataset.h"
36
#include "gdatetime.h"
37
#include "gerror.h"
38
#include "gfileutils.h"
39
#include "ghash.h"
40
#include "glibintl.h"
41
#include "glist.h"
42
#include "gmain.h"
43
#include "gmarkup.h"
44
#include "gmem.h"
45
#include "gmessages.h"
46
#include "gshell.h"
47
#include "gslice.h"
48
#include "gstdio.h"
49
#include "gstring.h"
50
#include "gstrfuncs.h"
51
#include "gtimer.h"
52
#include "gutils.h"
53
54
55
/**
56
 * SECTION:bookmarkfile
57
 * @title: Bookmark file parser
58
 * @short_description: parses files containing bookmarks
59
 *
60
 * GBookmarkFile lets you parse, edit or create files containing bookmarks
61
 * to URI, along with some meta-data about the resource pointed by the URI
62
 * like its MIME type, the application that is registering the bookmark and
63
 * the icon that should be used to represent the bookmark. The data is stored
64
 * using the
65
 * [Desktop Bookmark Specification](http://www.gnome.org/~ebassi/bookmark-spec).
66
 *
67
 * The syntax of the bookmark files is described in detail inside the
68
 * Desktop Bookmark Specification, here is a quick summary: bookmark
69
 * files use a sub-class of the XML Bookmark Exchange Language
70
 * specification, consisting of valid UTF-8 encoded XML, under the
71
 * <xbel> root element; each bookmark is stored inside a
72
 * <bookmark> element, using its URI: no relative paths can
73
 * be used inside a bookmark file. The bookmark may have a user defined
74
 * title and description, to be used instead of the URI. Under the
75
 * <metadata> element, with its owner attribute set to
76
 * `http://freedesktop.org`, is stored the meta-data about a resource
77
 * pointed by its URI. The meta-data consists of the resource's MIME
78
 * type; the applications that have registered a bookmark; the groups
79
 * to which a bookmark belongs to; a visibility flag, used to set the
80
 * bookmark as "private" to the applications and groups that has it
81
 * registered; the URI and MIME type of an icon, to be used when
82
 * displaying the bookmark inside a GUI.
83
 *
84
 * Here is an example of a bookmark file:
85
 * [bookmarks.xbel](https://gitlab.gnome.org/GNOME/glib/-/blob/HEAD/glib/tests/bookmarks.xbel)
86
 *
87
 * A bookmark file might contain more than one bookmark; each bookmark
88
 * is accessed through its URI.
89
 *
90
 * The important caveat of bookmark files is that when you add a new
91
 * bookmark you must also add the application that is registering it, using
92
 * g_bookmark_file_add_application() or g_bookmark_file_set_application_info().
93
 * If a bookmark has no applications then it won't be dumped when creating
94
 * the on disk representation, using g_bookmark_file_to_data() or
95
 * g_bookmark_file_to_file().
96
 *
97
 * The #GBookmarkFile parser was added in GLib 2.12.
98
 */
99
100
/* XBEL 1.0 standard entities */
101
0
#define XBEL_VERSION    "1.0"
102
#define XBEL_DTD_NICK   "xbel"
103
#define XBEL_DTD_SYSTEM   "+//IDN python.org//DTD XML Bookmark " \
104
        "Exchange Language 1.0//EN//XML"
105
106
#define XBEL_DTD_URI    "http://www.python.org/topics/xml/dtds/xbel-1.0.dtd"
107
108
0
#define XBEL_ROOT_ELEMENT "xbel"
109
#define XBEL_FOLDER_ELEMENT "folder"  /* unused */
110
0
#define XBEL_BOOKMARK_ELEMENT "bookmark"
111
#define XBEL_ALIAS_ELEMENT  "alias"   /* unused */
112
#define XBEL_SEPARATOR_ELEMENT  "separator"   /* unused */
113
0
#define XBEL_TITLE_ELEMENT  "title"
114
0
#define XBEL_DESC_ELEMENT "desc"
115
0
#define XBEL_INFO_ELEMENT "info"
116
0
#define XBEL_METADATA_ELEMENT "metadata"
117
118
#define XBEL_VERSION_ATTRIBUTE  "version"
119
#define XBEL_FOLDED_ATTRIBUTE "folded"  /* unused */
120
#define XBEL_OWNER_ATTRIBUTE  "owner"
121
#define XBEL_ADDED_ATTRIBUTE  "added"
122
#define XBEL_VISITED_ATTRIBUTE  "visited"
123
#define XBEL_MODIFIED_ATTRIBUTE "modified"
124
#define XBEL_ID_ATTRIBUTE "id"
125
0
#define XBEL_HREF_ATTRIBUTE "href"
126
#define XBEL_REF_ATTRIBUTE  "ref"     /* unused */
127
128
#define XBEL_YES_VALUE    "yes"
129
#define XBEL_NO_VALUE   "no"
130
131
/* Desktop bookmark spec entities */
132
0
#define BOOKMARK_METADATA_OWNER   "http://freedesktop.org"
133
134
#define BOOKMARK_NAMESPACE_NAME   "bookmark"
135
#define BOOKMARK_NAMESPACE_URI    "http://www.freedesktop.org/standards/desktop-bookmarks"
136
137
0
#define BOOKMARK_GROUPS_ELEMENT   "groups"
138
0
#define BOOKMARK_GROUP_ELEMENT    "group"
139
0
#define BOOKMARK_APPLICATIONS_ELEMENT "applications"
140
0
#define BOOKMARK_APPLICATION_ELEMENT  "application"
141
0
#define BOOKMARK_ICON_ELEMENT     "icon"
142
#define BOOKMARK_PRIVATE_ELEMENT  "private"
143
144
0
#define BOOKMARK_NAME_ATTRIBUTE   "name"
145
0
#define BOOKMARK_EXEC_ATTRIBUTE   "exec"
146
#define BOOKMARK_COUNT_ATTRIBUTE  "count"
147
#define BOOKMARK_TIMESTAMP_ATTRIBUTE  "timestamp"     /* deprecated by "modified" */
148
#define BOOKMARK_MODIFIED_ATTRIBUTE     "modified"
149
0
#define BOOKMARK_HREF_ATTRIBUTE   "href"
150
#define BOOKMARK_TYPE_ATTRIBUTE   "type"
151
152
/* Shared MIME Info entities */
153
#define MIME_NAMESPACE_NAME     "mime"
154
#define MIME_NAMESPACE_URI    "http://www.freedesktop.org/standards/shared-mime-info"
155
0
#define MIME_TYPE_ELEMENT     "mime-type"
156
#define MIME_TYPE_ATTRIBUTE     "type"
157
158
159
typedef struct _BookmarkAppInfo  BookmarkAppInfo;
160
typedef struct _BookmarkMetadata BookmarkMetadata;
161
typedef struct _BookmarkItem     BookmarkItem;
162
typedef struct _ParseData        ParseData;
163
164
struct _BookmarkAppInfo
165
{
166
  gchar *name;
167
  gchar *exec;
168
169
  guint count;
170
171
  GDateTime *stamp;  /* (owned) */
172
};
173
174
struct _BookmarkMetadata
175
{
176
  gchar *mime_type;
177
178
  GList *groups;
179
180
  GList *applications;
181
  GHashTable *apps_by_name;
182
183
  gchar *icon_href;
184
  gchar *icon_mime;
185
186
  guint is_private : 1;
187
};
188
189
struct _BookmarkItem
190
{
191
  gchar *uri;
192
193
  gchar *title;
194
  gchar *description;
195
196
  GDateTime *added;  /* (owned) */
197
  GDateTime *modified;  /* (owned) */
198
  GDateTime *visited;  /* (owned) */
199
200
  BookmarkMetadata *metadata;
201
};
202
203
struct _GBookmarkFile
204
{
205
  gchar *title;
206
  gchar *description;
207
208
  /* we store our items in a list and keep a copy inside
209
   * a hash table for faster lookup performances
210
   */
211
  GList *items;
212
  GHashTable *items_by_uri;
213
};
214
215
/* parser state machine */
216
typedef enum
217
{
218
  STATE_STARTED        = 0,
219
220
  STATE_ROOT,
221
  STATE_BOOKMARK,
222
  STATE_TITLE,
223
  STATE_DESC,
224
  STATE_INFO,
225
  STATE_METADATA,
226
  STATE_APPLICATIONS,
227
  STATE_APPLICATION,
228
  STATE_GROUPS,
229
  STATE_GROUP,
230
  STATE_MIME,
231
  STATE_ICON,
232
233
  STATE_FINISHED
234
} ParserState;
235
236
static void          g_bookmark_file_init        (GBookmarkFile  *bookmark);
237
static void          g_bookmark_file_clear       (GBookmarkFile  *bookmark);
238
static gboolean      g_bookmark_file_parse       (GBookmarkFile  *bookmark,
239
              const gchar    *buffer,
240
              gsize           length,
241
              GError        **error);
242
static gchar *       g_bookmark_file_dump        (GBookmarkFile  *bookmark,
243
              gsize          *length,
244
              GError        **error);
245
static BookmarkItem *g_bookmark_file_lookup_item (GBookmarkFile  *bookmark,
246
              const gchar    *uri);
247
static void          g_bookmark_file_add_item    (GBookmarkFile  *bookmark,
248
              BookmarkItem   *item,
249
              GError        **error);
250
251
static gboolean timestamp_from_iso8601 (const gchar  *iso_date,
252
                                        GDateTime   **out_date_time,
253
                                        GError      **error);
254
255
/********************************
256
 * BookmarkAppInfo              *
257
 *                              *
258
 * Application metadata storage *
259
 ********************************/
260
static BookmarkAppInfo *
261
bookmark_app_info_new (const gchar *name)
262
0
{
263
0
  BookmarkAppInfo *retval;
264
265
0
  g_warn_if_fail (name != NULL);
266
267
0
  retval = g_slice_new (BookmarkAppInfo);
268
269
0
  retval->name = g_strdup (name);
270
0
  retval->exec = NULL;
271
0
  retval->count = 0;
272
0
  retval->stamp = NULL;
273
274
0
  return retval;
275
0
}
276
277
static void
278
bookmark_app_info_free (BookmarkAppInfo *app_info)
279
0
{
280
0
  if (!app_info)
281
0
    return;
282
283
0
  g_free (app_info->name);
284
0
  g_free (app_info->exec);
285
0
  g_clear_pointer (&app_info->stamp, g_date_time_unref);
286
287
0
  g_slice_free (BookmarkAppInfo, app_info);
288
0
}
289
290
static BookmarkAppInfo *
291
bookmark_app_info_copy (BookmarkAppInfo *app_info)
292
0
{
293
0
  BookmarkAppInfo *copy;
294
295
0
  if (!app_info)
296
0
    return NULL;
297
298
0
  copy = bookmark_app_info_new (app_info->name);
299
0
  copy->count = app_info->count;
300
0
  copy->exec = g_strdup (app_info->exec);
301
302
0
  if (app_info->stamp)
303
0
    copy->stamp = g_date_time_ref (app_info->stamp);
304
305
0
  return copy;
306
0
}
307
308
static gchar *
309
bookmark_app_info_dump (BookmarkAppInfo *app_info)
310
0
{
311
0
  gchar *retval;
312
0
  gchar *name, *exec, *modified, *count;
313
314
0
  g_warn_if_fail (app_info != NULL);
315
316
0
  if (app_info->count == 0)
317
0
    return NULL;
318
319
0
  name = g_markup_escape_text (app_info->name, -1);
320
0
  exec = g_markup_escape_text (app_info->exec, -1);
321
0
  count = g_strdup_printf ("%u", app_info->count);
322
323
0
  if (app_info->stamp)
324
0
    {
325
0
      char *tmp;
326
327
0
      tmp = g_date_time_format_iso8601 (app_info->stamp);
328
0
      modified = g_strconcat (" " BOOKMARK_MODIFIED_ATTRIBUTE "=\"", tmp, "\"",
329
0
                              NULL);
330
0
      g_free (tmp);
331
0
    }
332
0
  else
333
0
    {
334
0
      modified = g_strdup ("");
335
0
    }
336
337
0
  retval = g_strconcat ("          "
338
0
                        "<" BOOKMARK_NAMESPACE_NAME ":" BOOKMARK_APPLICATION_ELEMENT
339
0
                        " " BOOKMARK_NAME_ATTRIBUTE "=\"", name, "\""
340
0
                        " " BOOKMARK_EXEC_ATTRIBUTE "=\"", exec, "\"",
341
0
                        modified,
342
0
                        " " BOOKMARK_COUNT_ATTRIBUTE "=\"", count, "\"/>\n",
343
0
                        NULL);
344
345
0
  g_free (name);
346
0
  g_free (exec);
347
0
  g_free (modified);
348
0
  g_free (count);
349
350
0
  return retval;
351
0
}
352
353
354
/***********************
355
 * BookmarkMetadata    *
356
 *                     *
357
 * Metadata storage    *
358
 ***********************/
359
static BookmarkMetadata *
360
bookmark_metadata_new (void)
361
0
{
362
0
  BookmarkMetadata *retval;
363
364
0
  retval = g_slice_new (BookmarkMetadata);
365
366
0
  retval->mime_type = NULL;
367
368
0
  retval->groups = NULL;
369
370
0
  retval->applications = NULL;
371
0
  retval->apps_by_name = g_hash_table_new_full (g_str_hash,
372
0
                                                g_str_equal,
373
0
                                                NULL,
374
0
                                                NULL);
375
376
0
  retval->is_private = FALSE;
377
378
0
  retval->icon_href = NULL;
379
0
  retval->icon_mime = NULL;
380
381
0
  return retval;
382
0
}
383
384
static void
385
bookmark_metadata_free (BookmarkMetadata *metadata)
386
0
{
387
0
  if (!metadata)
388
0
    return;
389
390
0
  g_free (metadata->mime_type);
391
392
0
  g_list_free_full (metadata->groups, g_free);
393
0
  g_list_free_full (metadata->applications, (GDestroyNotify) bookmark_app_info_free);
394
395
0
  g_hash_table_destroy (metadata->apps_by_name);
396
397
0
  g_free (metadata->icon_href);
398
0
  g_free (metadata->icon_mime);
399
400
0
  g_slice_free (BookmarkMetadata, metadata);
401
0
}
402
403
static BookmarkMetadata *
404
bookmark_metadata_copy (BookmarkMetadata *metadata)
405
0
{
406
0
  BookmarkMetadata *copy;
407
0
  GList *l;
408
409
0
  if (!metadata)
410
0
    return NULL;
411
412
0
  copy = bookmark_metadata_new ();
413
0
  copy->is_private = metadata->is_private;
414
0
  copy->mime_type = g_strdup (metadata->mime_type);
415
0
  copy->icon_href = g_strdup (metadata->icon_href);
416
0
  copy->icon_mime = g_strdup (metadata->icon_mime);
417
418
0
  copy->groups = g_list_copy_deep (metadata->groups, (GCopyFunc) g_strdup, NULL);
419
0
  copy->applications =
420
0
    g_list_copy_deep (metadata->applications, (GCopyFunc) bookmark_app_info_copy, NULL);
421
422
0
  for (l = copy->applications; l; l = l->next)
423
0
    {
424
0
      BookmarkAppInfo *app_info = l->data;
425
0
      g_hash_table_insert (copy->apps_by_name, app_info->name, app_info);
426
0
    }
427
428
0
  g_assert (g_hash_table_size (copy->apps_by_name) ==
429
0
            g_hash_table_size (metadata->apps_by_name));
430
431
0
  return copy;
432
0
}
433
434
static gchar *
435
bookmark_metadata_dump (BookmarkMetadata *metadata)
436
0
{
437
0
  GString *retval;
438
0
  gchar *buffer;
439
440
0
  if (!metadata->applications)
441
0
    return NULL;
442
443
0
  retval = g_string_sized_new (1024);
444
445
  /* metadata container */
446
0
  g_string_append (retval,
447
0
       "      "
448
0
       "<" XBEL_METADATA_ELEMENT
449
0
       " " XBEL_OWNER_ATTRIBUTE "=\"" BOOKMARK_METADATA_OWNER
450
0
       "\">\n");
451
452
  /* mime type */
453
0
  if (metadata->mime_type) {
454
0
    buffer = g_strconcat ("        "
455
0
        "<" MIME_NAMESPACE_NAME ":" MIME_TYPE_ELEMENT " "
456
0
        MIME_TYPE_ATTRIBUTE "=\"", metadata->mime_type, "\"/>\n",
457
0
        NULL);
458
0
    g_string_append (retval, buffer);
459
0
    g_free (buffer);
460
0
  }
461
462
0
  if (metadata->groups)
463
0
    {
464
0
      GList *l;
465
466
      /* open groups container */
467
0
      g_string_append (retval,
468
0
           "        "
469
0
           "<" BOOKMARK_NAMESPACE_NAME
470
0
           ":" BOOKMARK_GROUPS_ELEMENT ">\n");
471
472
0
      for (l = g_list_last (metadata->groups); l != NULL; l = l->prev)
473
0
        {
474
0
          gchar *group_name;
475
476
0
    group_name = g_markup_escape_text ((gchar *) l->data, -1);
477
0
    buffer = g_strconcat ("          "
478
0
        "<" BOOKMARK_NAMESPACE_NAME
479
0
        ":" BOOKMARK_GROUP_ELEMENT ">",
480
0
        group_name,
481
0
        "</" BOOKMARK_NAMESPACE_NAME
482
0
        ":"  BOOKMARK_GROUP_ELEMENT ">\n", NULL);
483
0
    g_string_append (retval, buffer);
484
485
0
    g_free (buffer);
486
0
    g_free (group_name);
487
0
        }
488
489
      /* close groups container */
490
0
      g_string_append (retval,
491
0
           "        "
492
0
           "</" BOOKMARK_NAMESPACE_NAME
493
0
           ":" BOOKMARK_GROUPS_ELEMENT ">\n");
494
0
    }
495
496
0
  if (metadata->applications)
497
0
    {
498
0
      GList *l;
499
500
      /* open applications container */
501
0
      g_string_append (retval,
502
0
           "        "
503
0
           "<" BOOKMARK_NAMESPACE_NAME
504
0
           ":" BOOKMARK_APPLICATIONS_ELEMENT ">\n");
505
506
0
      for (l = g_list_last (metadata->applications); l != NULL; l = l->prev)
507
0
        {
508
0
          BookmarkAppInfo *app_info = (BookmarkAppInfo *) l->data;
509
0
          gchar *app_data;
510
511
0
    g_warn_if_fail (app_info != NULL);
512
513
0
          app_data = bookmark_app_info_dump (app_info);
514
515
0
    if (app_data)
516
0
            {
517
0
              retval = g_string_append (retval, app_data);
518
519
0
        g_free (app_data);
520
0
      }
521
0
        }
522
523
      /* close applications container */
524
0
      g_string_append (retval,
525
0
           "        "
526
0
           "</" BOOKMARK_NAMESPACE_NAME
527
0
           ":" BOOKMARK_APPLICATIONS_ELEMENT ">\n");
528
0
    }
529
530
  /* icon */
531
0
  if (metadata->icon_href)
532
0
    {
533
0
      if (!metadata->icon_mime)
534
0
        metadata->icon_mime = g_strdup ("application/octet-stream");
535
536
0
      buffer = g_strconcat ("       "
537
0
          "<" BOOKMARK_NAMESPACE_NAME
538
0
          ":" BOOKMARK_ICON_ELEMENT
539
0
          " " BOOKMARK_HREF_ATTRIBUTE "=\"", metadata->icon_href,
540
0
          "\" " BOOKMARK_TYPE_ATTRIBUTE "=\"", metadata->icon_mime, "\"/>\n", NULL);
541
0
      g_string_append (retval, buffer);
542
543
0
      g_free (buffer);
544
0
    }
545
546
  /* private hint */
547
0
  if (metadata->is_private)
548
0
    g_string_append (retval,
549
0
         "        "
550
0
         "<" BOOKMARK_NAMESPACE_NAME
551
0
         ":" BOOKMARK_PRIVATE_ELEMENT "/>\n");
552
553
  /* close metadata container */
554
0
  g_string_append (retval,
555
0
       "      "
556
0
       "</" XBEL_METADATA_ELEMENT ">\n");
557
558
0
  return g_string_free (retval, FALSE);
559
0
}
560
561
/******************************************************
562
 * BookmarkItem                                       *
563
 *                                                    *
564
 * Storage for a single bookmark item inside the list *
565
 ******************************************************/
566
static BookmarkItem *
567
bookmark_item_new (const gchar *uri)
568
0
{
569
0
  BookmarkItem *item;
570
571
0
  g_warn_if_fail (uri != NULL);
572
573
0
  item = g_slice_new (BookmarkItem);
574
0
  item->uri = g_strdup (uri);
575
576
0
  item->title = NULL;
577
0
  item->description = NULL;
578
579
0
  item->added = NULL;
580
0
  item->modified = NULL;
581
0
  item->visited = NULL;
582
583
0
  item->metadata = NULL;
584
585
0
  return item;
586
0
}
587
588
static void
589
bookmark_item_free (BookmarkItem *item)
590
0
{
591
0
  if (!item)
592
0
    return;
593
594
0
  g_free (item->uri);
595
0
  g_free (item->title);
596
0
  g_free (item->description);
597
598
0
  if (item->metadata)
599
0
    bookmark_metadata_free (item->metadata);
600
601
0
  g_clear_pointer (&item->added, g_date_time_unref);
602
0
  g_clear_pointer (&item->modified, g_date_time_unref);
603
0
  g_clear_pointer (&item->visited, g_date_time_unref);
604
605
0
  g_slice_free (BookmarkItem, item);
606
0
}
607
608
static BookmarkItem *
609
bookmark_item_copy (BookmarkItem *item)
610
0
{
611
0
  BookmarkItem* copy;
612
613
0
  if (!item)
614
0
    return NULL;
615
616
0
  copy = bookmark_item_new (item->uri);
617
618
0
  copy->title = g_strdup (item->title);
619
0
  copy->description = g_strdup (item->description);
620
621
0
  copy->metadata = bookmark_metadata_copy (item->metadata);
622
623
0
  if (item->added)
624
0
    copy->added = g_date_time_ref (item->added);
625
0
  if (item->modified)
626
0
    copy->modified = g_date_time_ref (item->modified);
627
0
  if (item->visited)
628
0
    copy->visited = g_date_time_ref (item->visited);
629
630
0
  return copy;
631
0
}
632
633
static void
634
bookmark_item_touch_modified (BookmarkItem *item)
635
0
{
636
0
  g_clear_pointer (&item->modified, g_date_time_unref);
637
0
  item->modified = g_date_time_new_now_utc ();
638
0
}
639
640
static gchar *
641
bookmark_item_dump (BookmarkItem *item)
642
0
{
643
0
  GString *retval;
644
0
  gchar *escaped_uri;
645
646
  /* at this point, we must have at least a registered application; if we don't
647
   * we don't screw up the bookmark file, and just skip this item
648
   */
649
0
  if (!item->metadata || !item->metadata->applications)
650
0
    {
651
0
      g_warning ("Item for URI '%s' has no registered applications: skipping.", item->uri);
652
0
      return NULL;
653
0
    }
654
655
0
  retval = g_string_sized_new (4096);
656
657
0
  g_string_append (retval, "  <" XBEL_BOOKMARK_ELEMENT " ");
658
659
0
  escaped_uri = g_markup_escape_text (item->uri, -1);
660
661
0
  g_string_append (retval, XBEL_HREF_ATTRIBUTE "=\"");
662
0
  g_string_append (retval, escaped_uri);
663
0
  g_string_append (retval , "\" ");
664
665
0
  g_free (escaped_uri);
666
667
0
  if (item->added)
668
0
    {
669
0
      char *added;
670
671
0
      added = g_date_time_format_iso8601 (item->added);
672
0
      g_string_append (retval, XBEL_ADDED_ATTRIBUTE "=\"");
673
0
      g_string_append (retval, added);
674
0
      g_string_append (retval, "\" ");
675
0
      g_free (added);
676
0
    }
677
678
0
  if (item->modified)
679
0
    {
680
0
      char *modified;
681
682
0
      modified = g_date_time_format_iso8601 (item->modified);
683
0
      g_string_append (retval, XBEL_MODIFIED_ATTRIBUTE "=\"");
684
0
      g_string_append (retval, modified);
685
0
      g_string_append (retval, "\" ");
686
0
      g_free (modified);
687
0
    }
688
689
0
  if (item->visited)
690
0
    {
691
0
      char *visited;
692
693
0
      visited = g_date_time_format_iso8601 (item->visited);
694
0
      g_string_append (retval, XBEL_VISITED_ATTRIBUTE "=\"");
695
0
      g_string_append (retval, visited);
696
0
      g_string_append (retval, "\" ");
697
0
      g_free (visited);
698
0
    }
699
700
0
  if (retval->str[retval->len - 1] == ' ')
701
0
    g_string_truncate (retval, retval->len - 1);
702
0
  g_string_append (retval, ">\n");
703
704
0
  if (item->title)
705
0
    {
706
0
      gchar *escaped_title;
707
708
0
      escaped_title = g_markup_escape_text (item->title, -1);
709
0
      g_string_append (retval, "    " "<" XBEL_TITLE_ELEMENT ">");
710
0
      g_string_append (retval, escaped_title);
711
0
      g_string_append (retval, "</" XBEL_TITLE_ELEMENT ">\n");
712
713
0
      g_free (escaped_title);
714
0
    }
715
716
0
  if (item->description)
717
0
    {
718
0
      gchar *escaped_desc;
719
720
0
      escaped_desc = g_markup_escape_text (item->description, -1);
721
0
      g_string_append (retval, "    " "<" XBEL_DESC_ELEMENT ">");
722
0
      g_string_append (retval, escaped_desc);
723
0
      g_string_append (retval, "</" XBEL_DESC_ELEMENT ">\n");
724
725
0
      g_free (escaped_desc);
726
0
    }
727
728
0
  if (item->metadata)
729
0
    {
730
0
      gchar *metadata;
731
732
0
      metadata = bookmark_metadata_dump (item->metadata);
733
0
      if (metadata)
734
0
        {
735
0
          g_string_append (retval, "    " "<" XBEL_INFO_ELEMENT ">\n");
736
0
          g_string_append (retval, metadata);
737
0
          g_string_append (retval, "    " "</" XBEL_INFO_ELEMENT ">\n");
738
739
0
          g_free (metadata);
740
0
        }
741
0
    }
742
743
0
  g_string_append (retval, "  </" XBEL_BOOKMARK_ELEMENT ">\n");
744
745
0
  return g_string_free (retval, FALSE);
746
0
}
747
748
static BookmarkAppInfo *
749
bookmark_item_lookup_app_info (BookmarkItem *item,
750
             const gchar  *app_name)
751
0
{
752
0
  g_warn_if_fail (item != NULL && app_name != NULL);
753
754
0
  if (!item->metadata)
755
0
    return NULL;
756
757
0
  return g_hash_table_lookup (item->metadata->apps_by_name, app_name);
758
0
}
759
760
/*************************
761
 *    GBookmarkFile    *
762
 *************************/
763
764
static void
765
g_bookmark_file_init (GBookmarkFile *bookmark)
766
0
{
767
0
  bookmark->title = NULL;
768
0
  bookmark->description = NULL;
769
770
0
  bookmark->items = NULL;
771
0
  bookmark->items_by_uri = g_hash_table_new_full (g_str_hash,
772
0
                                                  g_str_equal,
773
0
                                                  NULL,
774
0
                                                  NULL);
775
0
}
776
777
static void
778
g_bookmark_file_clear (GBookmarkFile *bookmark)
779
0
{
780
0
  g_free (bookmark->title);
781
0
  g_free (bookmark->description);
782
783
0
  g_list_free_full (bookmark->items, (GDestroyNotify) bookmark_item_free);
784
0
  bookmark->items = NULL;
785
786
0
  g_clear_pointer (&bookmark->items_by_uri, g_hash_table_unref);
787
0
}
788
789
struct _ParseData
790
{
791
  ParserState state;
792
793
  GHashTable *namespaces;
794
795
  GBookmarkFile *bookmark_file;
796
  BookmarkItem *current_item;
797
};
798
799
static ParseData *
800
parse_data_new (void)
801
0
{
802
0
  ParseData *retval;
803
804
0
  retval = g_new (ParseData, 1);
805
806
0
  retval->state = STATE_STARTED;
807
0
  retval->namespaces = g_hash_table_new_full (g_str_hash, g_str_equal,
808
0
                  (GDestroyNotify) g_free,
809
0
                  (GDestroyNotify) g_free);
810
0
  retval->bookmark_file = NULL;
811
0
  retval->current_item = NULL;
812
813
0
  return retval;
814
0
}
815
816
static void
817
parse_data_free (ParseData *parse_data)
818
0
{
819
0
  g_hash_table_destroy (parse_data->namespaces);
820
821
0
  g_free (parse_data);
822
0
}
823
824
0
#define IS_ATTRIBUTE(s,a) ((0 == strcmp ((s), (a))))
825
826
static void
827
parse_bookmark_element (GMarkupParseContext  *context,
828
      ParseData            *parse_data,
829
      const gchar         **attribute_names,
830
      const gchar         **attribute_values,
831
      GError              **error)
832
0
{
833
0
  const gchar *uri, *added, *modified, *visited;
834
0
  const gchar *attr;
835
0
  gint i;
836
0
  BookmarkItem *item;
837
0
  GError *add_error;
838
839
0
  g_warn_if_fail ((parse_data != NULL) && (parse_data->state == STATE_BOOKMARK));
840
841
0
  i = 0;
842
0
  uri = added = modified = visited = NULL;
843
0
  for (attr = attribute_names[i]; attr != NULL; attr = attribute_names[++i])
844
0
    {
845
0
      if (IS_ATTRIBUTE (attr, XBEL_HREF_ATTRIBUTE))
846
0
        uri = attribute_values[i];
847
0
      else if (IS_ATTRIBUTE (attr, XBEL_ADDED_ATTRIBUTE))
848
0
        added = attribute_values[i];
849
0
      else if (IS_ATTRIBUTE (attr, XBEL_MODIFIED_ATTRIBUTE))
850
0
        modified = attribute_values[i];
851
0
      else if (IS_ATTRIBUTE (attr, XBEL_VISITED_ATTRIBUTE))
852
0
        visited = attribute_values[i];
853
0
      else
854
0
        {
855
          /* bookmark is defined by the XBEL spec, so we need
856
           * to error out if the element has different or
857
           * missing attributes
858
           */
859
0
          g_set_error (error, G_MARKUP_ERROR,
860
0
           G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
861
0
                   _("Unexpected attribute “%s” for element “%s”"),
862
0
                   attr,
863
0
                   XBEL_BOOKMARK_ELEMENT);
864
0
          return;
865
0
        }
866
0
    }
867
868
0
  if (!uri)
869
0
    {
870
0
      g_set_error (error, G_MARKUP_ERROR,
871
0
             G_MARKUP_ERROR_INVALID_CONTENT,
872
0
             _("Attribute “%s” of element “%s” not found"),
873
0
             XBEL_HREF_ATTRIBUTE,
874
0
             XBEL_BOOKMARK_ELEMENT);
875
0
      return;
876
0
    }
877
878
0
  g_warn_if_fail (parse_data->current_item == NULL);
879
880
0
  item = bookmark_item_new (uri);
881
882
0
  if (added != NULL && !timestamp_from_iso8601 (added, &item->added, error))
883
0
    {
884
0
      bookmark_item_free (item);
885
0
      return;
886
0
    }
887
888
0
  if (modified != NULL && !timestamp_from_iso8601 (modified, &item->modified, error))
889
0
    {
890
0
      bookmark_item_free (item);
891
0
      return;
892
0
    }
893
894
0
  if (visited != NULL && !timestamp_from_iso8601 (visited, &item->visited, error))
895
0
    {
896
0
      bookmark_item_free (item);
897
0
      return;
898
0
    }
899
900
0
  add_error = NULL;
901
0
  g_bookmark_file_add_item (parse_data->bookmark_file,
902
0
            item,
903
0
            &add_error);
904
0
  if (add_error)
905
0
    {
906
0
      bookmark_item_free (item);
907
908
0
      g_propagate_error (error, add_error);
909
910
0
      return;
911
0
    }
912
913
0
  parse_data->current_item = item;
914
0
}
915
916
static void
917
parse_application_element (GMarkupParseContext  *context,
918
         ParseData            *parse_data,
919
         const gchar         **attribute_names,
920
         const gchar         **attribute_values,
921
         GError              **error)
922
0
{
923
0
  const gchar *name, *exec, *count, *stamp, *modified;
924
0
  const gchar *attr;
925
0
  gint i;
926
0
  BookmarkItem *item;
927
0
  BookmarkAppInfo *ai;
928
929
0
  g_warn_if_fail ((parse_data != NULL) && (parse_data->state == STATE_APPLICATION));
930
931
0
  i = 0;
932
0
  name = exec = count = stamp = modified = NULL;
933
0
  for (attr = attribute_names[i]; attr != NULL; attr = attribute_names[++i])
934
0
    {
935
0
      if (IS_ATTRIBUTE (attr, BOOKMARK_NAME_ATTRIBUTE))
936
0
        name = attribute_values[i];
937
0
      else if (IS_ATTRIBUTE (attr, BOOKMARK_EXEC_ATTRIBUTE))
938
0
        exec = attribute_values[i];
939
0
      else if (IS_ATTRIBUTE (attr, BOOKMARK_COUNT_ATTRIBUTE))
940
0
        count = attribute_values[i];
941
0
      else if (IS_ATTRIBUTE (attr, BOOKMARK_TIMESTAMP_ATTRIBUTE))
942
0
        stamp = attribute_values[i];
943
0
      else if (IS_ATTRIBUTE (attr, BOOKMARK_MODIFIED_ATTRIBUTE))
944
0
        modified = attribute_values[i];
945
0
    }
946
947
  /* the "name" and "exec" attributes are mandatory */
948
0
  if (!name)
949
0
    {
950
0
      g_set_error (error, G_MARKUP_ERROR,
951
0
             G_MARKUP_ERROR_INVALID_CONTENT,
952
0
             _("Attribute “%s” of element “%s” not found"),
953
0
             BOOKMARK_NAME_ATTRIBUTE,
954
0
             BOOKMARK_APPLICATION_ELEMENT);
955
0
      return;
956
0
    }
957
958
0
  if (!exec)
959
0
    {
960
0
      g_set_error (error, G_MARKUP_ERROR,
961
0
             G_MARKUP_ERROR_INVALID_CONTENT,
962
0
             _("Attribute “%s” of element “%s” not found"),
963
0
             BOOKMARK_EXEC_ATTRIBUTE,
964
0
             BOOKMARK_APPLICATION_ELEMENT);
965
0
      return;
966
0
    }
967
968
0
  g_warn_if_fail (parse_data->current_item != NULL);
969
0
  item = parse_data->current_item;
970
971
0
  ai = bookmark_item_lookup_app_info (item, name);
972
0
  if (!ai)
973
0
    {
974
0
      ai = bookmark_app_info_new (name);
975
976
0
      if (!item->metadata)
977
0
  item->metadata = bookmark_metadata_new ();
978
979
0
      item->metadata->applications = g_list_prepend (item->metadata->applications, ai);
980
0
      g_hash_table_replace (item->metadata->apps_by_name, ai->name, ai);
981
0
    }
982
983
0
  g_free (ai->exec);
984
0
  ai->exec = g_strdup (exec);
985
986
0
  if (count)
987
0
    ai->count = atoi (count);
988
0
  else
989
0
    ai->count = 1;
990
991
0
  g_clear_pointer (&ai->stamp, g_date_time_unref);
992
0
  if (modified != NULL)
993
0
    {
994
0
      if (!timestamp_from_iso8601 (modified, &ai->stamp, error))
995
0
        return;
996
0
    }
997
0
  else
998
0
    {
999
      /* the timestamp attribute has been deprecated but we still parse
1000
       * it for backward compatibility
1001
       */
1002
0
      if (stamp)
1003
0
        ai->stamp = g_date_time_new_from_unix_utc (atol (stamp));
1004
0
      else
1005
0
        ai->stamp = g_date_time_new_now_utc ();
1006
0
    }
1007
0
}
1008
1009
static void
1010
parse_mime_type_element (GMarkupParseContext  *context,
1011
       ParseData            *parse_data,
1012
       const gchar         **attribute_names,
1013
       const gchar         **attribute_values,
1014
       GError              **error)
1015
0
{
1016
0
  const gchar *type;
1017
0
  const gchar *attr;
1018
0
  gint i;
1019
0
  BookmarkItem *item;
1020
1021
0
  g_warn_if_fail ((parse_data != NULL) && (parse_data->state == STATE_MIME));
1022
1023
0
  i = 0;
1024
0
  type = NULL;
1025
0
  for (attr = attribute_names[i]; attr != NULL; attr = attribute_names[++i])
1026
0
    {
1027
0
      if (IS_ATTRIBUTE (attr, MIME_TYPE_ATTRIBUTE))
1028
0
        type = attribute_values[i];
1029
0
    }
1030
1031
0
  if (!type)
1032
0
    type = "application/octet-stream";
1033
1034
0
  g_warn_if_fail (parse_data->current_item != NULL);
1035
0
  item = parse_data->current_item;
1036
1037
0
  if (!item->metadata)
1038
0
    item->metadata = bookmark_metadata_new ();
1039
1040
0
  g_free (item->metadata->mime_type);
1041
0
  item->metadata->mime_type = g_strdup (type);
1042
0
}
1043
1044
static void
1045
parse_icon_element (GMarkupParseContext  *context,
1046
        ParseData            *parse_data,
1047
        const gchar         **attribute_names,
1048
        const gchar         **attribute_values,
1049
        GError              **error)
1050
0
{
1051
0
  const gchar *href;
1052
0
  const gchar *type;
1053
0
  const gchar *attr;
1054
0
  gint i;
1055
0
  BookmarkItem *item;
1056
1057
0
  g_warn_if_fail ((parse_data != NULL) && (parse_data->state == STATE_ICON));
1058
1059
0
  i = 0;
1060
0
  href = NULL;
1061
0
  type = NULL;
1062
0
  for (attr = attribute_names[i]; attr != NULL; attr = attribute_names[++i])
1063
0
    {
1064
0
      if (IS_ATTRIBUTE (attr, BOOKMARK_HREF_ATTRIBUTE))
1065
0
        href = attribute_values[i];
1066
0
      else if (IS_ATTRIBUTE (attr, BOOKMARK_TYPE_ATTRIBUTE))
1067
0
        type = attribute_values[i];
1068
0
    }
1069
1070
  /* the "href" attribute is mandatory */
1071
0
  if (!href)
1072
0
    {
1073
0
      g_set_error (error, G_MARKUP_ERROR,
1074
0
             G_MARKUP_ERROR_INVALID_CONTENT,
1075
0
             _("Attribute “%s” of element “%s” not found"),
1076
0
             BOOKMARK_HREF_ATTRIBUTE,
1077
0
             BOOKMARK_ICON_ELEMENT);
1078
0
      return;
1079
0
    }
1080
1081
0
  if (!type)
1082
0
    type = "application/octet-stream";
1083
1084
0
  g_warn_if_fail (parse_data->current_item != NULL);
1085
0
  item = parse_data->current_item;
1086
1087
0
  if (!item->metadata)
1088
0
    item->metadata = bookmark_metadata_new ();
1089
1090
0
  g_free (item->metadata->icon_href);
1091
0
  g_free (item->metadata->icon_mime);
1092
0
  item->metadata->icon_href = g_strdup (href);
1093
0
  item->metadata->icon_mime = g_strdup (type);
1094
0
}
1095
1096
/* scans through the attributes of an element for the "xmlns" pragma, and
1097
 * adds any resulting namespace declaration to a per-parser hashtable, using
1098
 * the namespace name as a key for the namespace URI; if no key was found,
1099
 * the namespace is considered as default, and stored under the "default" key.
1100
 *
1101
 * FIXME: this works on the assumption that the generator of the XBEL file
1102
 * is either this code or is smart enough to place the namespace declarations
1103
 * inside the main root node or inside the metadata node and does not redefine
1104
 * a namespace inside an inner node; this does *not* conform to the
1105
 * XML-NS standard, although is a close approximation.  In order to make this
1106
 * conformant to the XML-NS specification we should use a per-element
1107
 * namespace table inside GMarkup and ask it to resolve the namespaces for us.
1108
 */
1109
static void
1110
map_namespace_to_name (ParseData    *parse_data,
1111
                       const gchar **attribute_names,
1112
           const gchar **attribute_values)
1113
0
{
1114
0
  const gchar *attr;
1115
0
  gint i;
1116
1117
0
  g_warn_if_fail (parse_data != NULL);
1118
1119
0
  if (!attribute_names || !attribute_names[0])
1120
0
    return;
1121
1122
0
  i = 0;
1123
0
  for (attr = attribute_names[i]; attr; attr = attribute_names[++i])
1124
0
    {
1125
0
      if (g_str_has_prefix (attr, "xmlns"))
1126
0
        {
1127
0
          gchar *namespace_name, *namespace_uri;
1128
0
          gchar *p;
1129
1130
0
          p = g_utf8_strchr (attr, -1, ':');
1131
0
          if (p)
1132
0
            p = g_utf8_next_char (p);
1133
0
          else
1134
0
            p = "default";
1135
1136
0
          namespace_name = g_strdup (p);
1137
0
          namespace_uri = g_strdup (attribute_values[i]);
1138
1139
0
          g_hash_table_replace (parse_data->namespaces,
1140
0
                                namespace_name,
1141
0
                                namespace_uri);
1142
0
        }
1143
0
     }
1144
0
}
1145
1146
/* checks whether @element_full is equal to @element.
1147
 *
1148
 * if @namespace is set, it tries to resolve the namespace to a known URI,
1149
 * and if found is prepended to the element name, from which is separated
1150
 * using the character specified in the @sep parameter.
1151
 */
1152
static gboolean
1153
is_element_full (ParseData   *parse_data,
1154
                 const gchar *element_full,
1155
                 const gchar *namespace,
1156
                 const gchar *element,
1157
                 const gchar  sep)
1158
0
{
1159
0
  gchar *ns_uri, *ns_name;
1160
0
  const gchar *p, *element_name;
1161
0
  gboolean retval;
1162
1163
0
  g_warn_if_fail (parse_data != NULL);
1164
0
  g_warn_if_fail (element_full != NULL);
1165
1166
0
  if (!element)
1167
0
    return FALSE;
1168
1169
  /* no namespace requested: dumb element compare */
1170
0
  if (!namespace)
1171
0
    return (0 == strcmp (element_full, element));
1172
1173
  /* search for namespace separator; if none found, assume we are under the
1174
   * default namespace, and set ns_name to our "default" marker; if no default
1175
   * namespace has been set, just do a plain comparison between @full_element
1176
   * and @element.
1177
   */
1178
0
  p = g_utf8_strchr (element_full, -1, ':');
1179
0
  if (p)
1180
0
    {
1181
0
      ns_name = g_strndup (element_full, p - element_full);
1182
0
      element_name = g_utf8_next_char (p);
1183
0
    }
1184
0
  else
1185
0
    {
1186
0
      ns_name = g_strdup ("default");
1187
0
      element_name = element_full;
1188
0
    }
1189
1190
0
  ns_uri = g_hash_table_lookup (parse_data->namespaces, ns_name);
1191
0
  if (!ns_uri)
1192
0
    {
1193
      /* no default namespace found */
1194
0
      g_free (ns_name);
1195
1196
0
      return (0 == strcmp (element_full, element));
1197
0
    }
1198
1199
0
  retval = (0 == strcmp (ns_uri, namespace) &&
1200
0
            0 == strcmp (element_name, element));
1201
1202
0
  g_free (ns_name);
1203
1204
0
  return retval;
1205
0
}
1206
1207
0
#define IS_ELEMENT(p,s,e) (is_element_full ((p), (s), NULL, (e), '\0'))
1208
0
#define IS_ELEMENT_NS(p,s,n,e)  (is_element_full ((p), (s), (n), (e), '|'))
1209
1210
static const gchar *
1211
parser_state_to_element_name (ParserState state)
1212
0
{
1213
0
  switch (state)
1214
0
    {
1215
0
    case STATE_STARTED:
1216
0
    case STATE_FINISHED:
1217
0
      return "(top-level)";
1218
0
    case STATE_ROOT:
1219
0
      return XBEL_ROOT_ELEMENT;
1220
0
    case STATE_BOOKMARK:
1221
0
      return XBEL_BOOKMARK_ELEMENT;
1222
0
    case STATE_TITLE:
1223
0
      return XBEL_TITLE_ELEMENT;
1224
0
    case STATE_DESC:
1225
0
      return XBEL_DESC_ELEMENT;
1226
0
    case STATE_INFO:
1227
0
      return XBEL_INFO_ELEMENT;
1228
0
    case STATE_METADATA:
1229
0
      return XBEL_METADATA_ELEMENT;
1230
0
    case STATE_APPLICATIONS:
1231
0
      return BOOKMARK_APPLICATIONS_ELEMENT;
1232
0
    case STATE_APPLICATION:
1233
0
      return BOOKMARK_APPLICATION_ELEMENT;
1234
0
    case STATE_GROUPS:
1235
0
      return BOOKMARK_GROUPS_ELEMENT;
1236
0
    case STATE_GROUP:
1237
0
      return BOOKMARK_GROUP_ELEMENT;
1238
0
    case STATE_MIME:
1239
0
      return MIME_TYPE_ELEMENT;
1240
0
    case STATE_ICON:
1241
0
      return BOOKMARK_ICON_ELEMENT;
1242
0
    default:
1243
0
      g_assert_not_reached ();
1244
0
    }
1245
0
}
1246
1247
static void
1248
start_element_raw_cb (GMarkupParseContext *context,
1249
                      const gchar         *element_name,
1250
                      const gchar        **attribute_names,
1251
                      const gchar        **attribute_values,
1252
                      gpointer             user_data,
1253
                      GError             **error)
1254
0
{
1255
0
  ParseData *parse_data = (ParseData *) user_data;
1256
1257
  /* we must check for namespace declarations first
1258
   *
1259
   * XXX - we could speed up things by checking for namespace declarations
1260
   * only on the root node, where they usually are; this would probably break
1261
   * on streams not produced by us or by "smart" generators
1262
   */
1263
0
  map_namespace_to_name (parse_data, attribute_names, attribute_values);
1264
1265
0
  switch (parse_data->state)
1266
0
    {
1267
0
    case STATE_STARTED:
1268
0
      if (IS_ELEMENT (parse_data, element_name, XBEL_ROOT_ELEMENT))
1269
0
        {
1270
0
          const gchar *attr;
1271
0
          gint i;
1272
1273
0
          i = 0;
1274
0
          for (attr = attribute_names[i]; attr; attr = attribute_names[++i])
1275
0
            {
1276
0
              if ((IS_ATTRIBUTE (attr, XBEL_VERSION_ATTRIBUTE)) &&
1277
0
                  (0 == strcmp (attribute_values[i], XBEL_VERSION)))
1278
0
                parse_data->state = STATE_ROOT;
1279
0
            }
1280
0
  }
1281
0
      else
1282
0
        g_set_error (error, G_MARKUP_ERROR,
1283
0
         G_MARKUP_ERROR_INVALID_CONTENT,
1284
0
                 _("Unexpected tag “%s”, tag “%s” expected"),
1285
0
                 element_name, XBEL_ROOT_ELEMENT);
1286
0
      break;
1287
0
    case STATE_ROOT:
1288
0
      if (IS_ELEMENT (parse_data, element_name, XBEL_TITLE_ELEMENT))
1289
0
        parse_data->state = STATE_TITLE;
1290
0
      else if (IS_ELEMENT (parse_data, element_name, XBEL_DESC_ELEMENT))
1291
0
        parse_data->state = STATE_DESC;
1292
0
      else if (IS_ELEMENT (parse_data, element_name, XBEL_BOOKMARK_ELEMENT))
1293
0
        {
1294
0
          GError *inner_error = NULL;
1295
1296
0
          parse_data->state = STATE_BOOKMARK;
1297
1298
0
          parse_bookmark_element (context,
1299
0
                  parse_data,
1300
0
                  attribute_names,
1301
0
                  attribute_values,
1302
0
                  &inner_error);
1303
0
          if (inner_error)
1304
0
            g_propagate_error (error, inner_error);
1305
0
        }
1306
0
      else
1307
0
        g_set_error (error, G_MARKUP_ERROR,
1308
0
               G_MARKUP_ERROR_INVALID_CONTENT,
1309
0
               _("Unexpected tag “%s” inside “%s”"),
1310
0
               element_name,
1311
0
               XBEL_ROOT_ELEMENT);
1312
0
      break;
1313
0
    case STATE_BOOKMARK:
1314
0
      if (IS_ELEMENT (parse_data, element_name, XBEL_TITLE_ELEMENT))
1315
0
        parse_data->state = STATE_TITLE;
1316
0
      else if (IS_ELEMENT (parse_data, element_name, XBEL_DESC_ELEMENT))
1317
0
        parse_data->state = STATE_DESC;
1318
0
      else if (IS_ELEMENT (parse_data, element_name, XBEL_INFO_ELEMENT))
1319
0
        parse_data->state = STATE_INFO;
1320
0
      else
1321
0
        g_set_error (error, G_MARKUP_ERROR,
1322
0
               G_MARKUP_ERROR_INVALID_CONTENT,
1323
0
                 _("Unexpected tag “%s” inside “%s”"),
1324
0
                 element_name,
1325
0
                 XBEL_BOOKMARK_ELEMENT);
1326
0
      break;
1327
0
    case STATE_INFO:
1328
0
      if (IS_ELEMENT (parse_data, element_name, XBEL_METADATA_ELEMENT))
1329
0
        {
1330
0
          const gchar *attr;
1331
0
          gint i;
1332
1333
0
          i = 0;
1334
0
          for (attr = attribute_names[i]; attr; attr = attribute_names[++i])
1335
0
            {
1336
0
              if ((IS_ATTRIBUTE (attr, XBEL_OWNER_ATTRIBUTE)) &&
1337
0
                  (0 == strcmp (attribute_values[i], BOOKMARK_METADATA_OWNER)))
1338
0
                {
1339
0
                  parse_data->state = STATE_METADATA;
1340
1341
0
                  if (!parse_data->current_item->metadata)
1342
0
                    parse_data->current_item->metadata = bookmark_metadata_new ();
1343
0
                }
1344
0
            }
1345
0
        }
1346
0
      else
1347
0
        g_set_error (error, G_MARKUP_ERROR,
1348
0
               G_MARKUP_ERROR_INVALID_CONTENT,
1349
0
               _("Unexpected tag “%s”, tag “%s” expected"),
1350
0
               element_name,
1351
0
               XBEL_METADATA_ELEMENT);
1352
0
      break;
1353
0
    case STATE_METADATA:
1354
0
      if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_APPLICATIONS_ELEMENT))
1355
0
        parse_data->state = STATE_APPLICATIONS;
1356
0
      else if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_GROUPS_ELEMENT))
1357
0
        parse_data->state = STATE_GROUPS;
1358
0
      else if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_PRIVATE_ELEMENT))
1359
0
        parse_data->current_item->metadata->is_private = TRUE;
1360
0
      else if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_ICON_ELEMENT))
1361
0
        {
1362
0
          GError *inner_error = NULL;
1363
1364
0
    parse_data->state = STATE_ICON;
1365
1366
0
          parse_icon_element (context,
1367
0
                    parse_data,
1368
0
                    attribute_names,
1369
0
                    attribute_values,
1370
0
                    &inner_error);
1371
0
          if (inner_error)
1372
0
            g_propagate_error (error, inner_error);
1373
0
        }
1374
0
      else if (IS_ELEMENT_NS (parse_data, element_name, MIME_NAMESPACE_URI, MIME_TYPE_ELEMENT))
1375
0
        {
1376
0
          GError *inner_error = NULL;
1377
1378
0
          parse_data->state = STATE_MIME;
1379
1380
0
          parse_mime_type_element (context,
1381
0
                   parse_data,
1382
0
                   attribute_names,
1383
0
                   attribute_values,
1384
0
                   &inner_error);
1385
0
          if (inner_error)
1386
0
            g_propagate_error (error, inner_error);
1387
0
        }
1388
0
      else
1389
0
        g_set_error (error, G_MARKUP_ERROR,
1390
0
               G_MARKUP_ERROR_UNKNOWN_ELEMENT,
1391
0
               _("Unexpected tag “%s” inside “%s”"),
1392
0
               element_name,
1393
0
               XBEL_METADATA_ELEMENT);
1394
0
      break;
1395
0
    case STATE_APPLICATIONS:
1396
0
      if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_APPLICATION_ELEMENT))
1397
0
        {
1398
0
          GError *inner_error = NULL;
1399
1400
0
          parse_data->state = STATE_APPLICATION;
1401
1402
0
          parse_application_element (context,
1403
0
                     parse_data,
1404
0
                     attribute_names,
1405
0
                     attribute_values,
1406
0
                     &inner_error);
1407
0
          if (inner_error)
1408
0
            g_propagate_error (error, inner_error);
1409
0
        }
1410
0
      else
1411
0
        g_set_error (error, G_MARKUP_ERROR,
1412
0
               G_MARKUP_ERROR_INVALID_CONTENT,
1413
0
               _("Unexpected tag “%s”, tag “%s” expected"),
1414
0
               element_name,
1415
0
               BOOKMARK_APPLICATION_ELEMENT);
1416
0
      break;
1417
0
    case STATE_GROUPS:
1418
0
      if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_GROUP_ELEMENT))
1419
0
        parse_data->state = STATE_GROUP;
1420
0
      else
1421
0
        g_set_error (error, G_MARKUP_ERROR,
1422
0
               G_MARKUP_ERROR_INVALID_CONTENT,
1423
0
               _("Unexpected tag “%s”, tag “%s” expected"),
1424
0
               element_name,
1425
0
               BOOKMARK_GROUP_ELEMENT);
1426
0
      break;
1427
1428
0
    case STATE_TITLE:
1429
0
    case STATE_DESC:
1430
0
    case STATE_APPLICATION:
1431
0
    case STATE_GROUP:
1432
0
    case STATE_MIME:
1433
0
    case STATE_ICON:
1434
0
    case STATE_FINISHED:
1435
0
      g_set_error (error, G_MARKUP_ERROR,
1436
0
                   G_MARKUP_ERROR_INVALID_CONTENT,
1437
0
                   _("Unexpected tag “%s” inside “%s”"),
1438
0
                   element_name,
1439
0
                   parser_state_to_element_name (parse_data->state));
1440
0
      break;
1441
1442
0
    default:
1443
0
      g_assert_not_reached ();
1444
0
      break;
1445
0
    }
1446
0
}
1447
1448
static void
1449
end_element_raw_cb (GMarkupParseContext *context,
1450
                    const gchar         *element_name,
1451
                    gpointer             user_data,
1452
                    GError             **error)
1453
0
{
1454
0
  ParseData *parse_data = (ParseData *) user_data;
1455
1456
0
  if (IS_ELEMENT (parse_data, element_name, XBEL_ROOT_ELEMENT))
1457
0
    parse_data->state = STATE_FINISHED;
1458
0
  else if (IS_ELEMENT (parse_data, element_name, XBEL_BOOKMARK_ELEMENT))
1459
0
    {
1460
0
      parse_data->current_item = NULL;
1461
1462
0
      parse_data->state = STATE_ROOT;
1463
0
    }
1464
0
  else if ((IS_ELEMENT (parse_data, element_name, XBEL_INFO_ELEMENT)) ||
1465
0
           (IS_ELEMENT (parse_data, element_name, XBEL_TITLE_ELEMENT)) ||
1466
0
           (IS_ELEMENT (parse_data, element_name, XBEL_DESC_ELEMENT)))
1467
0
    {
1468
0
      if (parse_data->current_item)
1469
0
        parse_data->state = STATE_BOOKMARK;
1470
0
      else
1471
0
        parse_data->state = STATE_ROOT;
1472
0
    }
1473
0
  else if (IS_ELEMENT (parse_data, element_name, XBEL_METADATA_ELEMENT))
1474
0
    parse_data->state = STATE_INFO;
1475
0
  else if (IS_ELEMENT_NS (parse_data, element_name,
1476
0
                          BOOKMARK_NAMESPACE_URI,
1477
0
                          BOOKMARK_APPLICATION_ELEMENT))
1478
0
    parse_data->state = STATE_APPLICATIONS;
1479
0
  else if (IS_ELEMENT_NS (parse_data, element_name,
1480
0
                          BOOKMARK_NAMESPACE_URI,
1481
0
                          BOOKMARK_GROUP_ELEMENT))
1482
0
    parse_data->state = STATE_GROUPS;
1483
0
  else if ((IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_APPLICATIONS_ELEMENT)) ||
1484
0
           (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_GROUPS_ELEMENT)) ||
1485
0
           (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_PRIVATE_ELEMENT)) ||
1486
0
           (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_ICON_ELEMENT)) ||
1487
0
           (IS_ELEMENT_NS (parse_data, element_name, MIME_NAMESPACE_URI, MIME_TYPE_ELEMENT)))
1488
0
    parse_data->state = STATE_METADATA;
1489
0
}
1490
1491
static void
1492
text_raw_cb (GMarkupParseContext *context,
1493
             const gchar         *text,
1494
             gsize                length,
1495
             gpointer             user_data,
1496
             GError             **error)
1497
0
{
1498
0
  ParseData *parse_data = (ParseData *) user_data;
1499
0
  gchar *payload;
1500
1501
0
  payload = g_strndup (text, length);
1502
1503
0
  switch (parse_data->state)
1504
0
    {
1505
0
    case STATE_TITLE:
1506
0
      if (parse_data->current_item)
1507
0
        {
1508
0
          g_free (parse_data->current_item->title);
1509
0
          parse_data->current_item->title = g_strdup (payload);
1510
0
        }
1511
0
      else
1512
0
        {
1513
0
          g_free (parse_data->bookmark_file->title);
1514
0
          parse_data->bookmark_file->title = g_strdup (payload);
1515
0
        }
1516
0
      break;
1517
0
    case STATE_DESC:
1518
0
      if (parse_data->current_item)
1519
0
        {
1520
0
          g_free (parse_data->current_item->description);
1521
0
          parse_data->current_item->description = g_strdup (payload);
1522
0
        }
1523
0
      else
1524
0
        {
1525
0
          g_free (parse_data->bookmark_file->description);
1526
0
          parse_data->bookmark_file->description = g_strdup (payload);
1527
0
        }
1528
0
      break;
1529
0
    case STATE_GROUP:
1530
0
      {
1531
0
      GList *groups;
1532
1533
0
      g_warn_if_fail (parse_data->current_item != NULL);
1534
1535
0
      if (!parse_data->current_item->metadata)
1536
0
        parse_data->current_item->metadata = bookmark_metadata_new ();
1537
1538
0
      groups = parse_data->current_item->metadata->groups;
1539
0
      parse_data->current_item->metadata->groups = g_list_prepend (groups, g_strdup (payload));
1540
0
      }
1541
0
      break;
1542
0
    case STATE_ROOT:
1543
0
    case STATE_BOOKMARK:
1544
0
    case STATE_INFO:
1545
0
    case STATE_METADATA:
1546
0
    case STATE_APPLICATIONS:
1547
0
    case STATE_APPLICATION:
1548
0
    case STATE_GROUPS:
1549
0
    case STATE_MIME:
1550
0
    case STATE_ICON:
1551
0
      break;
1552
0
    default:
1553
0
      g_warn_if_reached ();
1554
0
      break;
1555
0
    }
1556
1557
0
  g_free (payload);
1558
0
}
1559
1560
static const GMarkupParser markup_parser =
1561
{
1562
  start_element_raw_cb, /* start_element */
1563
  end_element_raw_cb,   /* end_element */
1564
  text_raw_cb,          /* text */
1565
  NULL,                 /* passthrough */
1566
  NULL
1567
};
1568
1569
static gboolean
1570
g_bookmark_file_parse (GBookmarkFile  *bookmark,
1571
       const gchar  *buffer,
1572
       gsize         length,
1573
       GError       **error)
1574
0
{
1575
0
  GMarkupParseContext *context;
1576
0
  ParseData *parse_data;
1577
0
  GError *parse_error, *end_error;
1578
0
  gboolean retval;
1579
1580
0
  g_warn_if_fail (bookmark != NULL);
1581
1582
0
  if (!buffer)
1583
0
    return FALSE;
1584
1585
0
  parse_error = NULL;
1586
0
  end_error = NULL;
1587
1588
0
  if (length == (gsize) -1)
1589
0
    length = strlen (buffer);
1590
1591
0
  parse_data = parse_data_new ();
1592
0
  parse_data->bookmark_file = bookmark;
1593
1594
0
  context = g_markup_parse_context_new (&markup_parser,
1595
0
            G_MARKUP_DEFAULT_FLAGS,
1596
0
            parse_data,
1597
0
            (GDestroyNotify) parse_data_free);
1598
1599
0
  retval = g_markup_parse_context_parse (context,
1600
0
             buffer,
1601
0
             length,
1602
0
             &parse_error);
1603
0
  if (!retval)
1604
0
    g_propagate_error (error, parse_error);
1605
0
  else
1606
0
   {
1607
0
     retval = g_markup_parse_context_end_parse (context, &end_error);
1608
0
      if (!retval)
1609
0
        g_propagate_error (error, end_error);
1610
0
   }
1611
1612
0
  g_markup_parse_context_free (context);
1613
1614
0
  return retval;
1615
0
}
1616
1617
static gchar *
1618
g_bookmark_file_dump (GBookmarkFile  *bookmark,
1619
          gsize          *length,
1620
          GError        **error)
1621
0
{
1622
0
  GString *retval;
1623
0
  gchar *buffer;
1624
0
  GList *l;
1625
1626
0
  retval = g_string_sized_new (4096);
1627
1628
0
  g_string_append (retval,
1629
0
       "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1630
#if 0
1631
       /* XXX - do we really need the doctype? */
1632
       "<!DOCTYPE " XBEL_DTD_NICK "\n"
1633
       "  PUBLIC \"" XBEL_DTD_SYSTEM "\"\n"
1634
       "         \"" XBEL_DTD_URI "\">\n"
1635
#endif
1636
0
       "<" XBEL_ROOT_ELEMENT " " XBEL_VERSION_ATTRIBUTE "=\"" XBEL_VERSION "\"\n"
1637
0
       "      xmlns:" BOOKMARK_NAMESPACE_NAME "=\"" BOOKMARK_NAMESPACE_URI "\"\n"
1638
0
       "      xmlns:" MIME_NAMESPACE_NAME     "=\"" MIME_NAMESPACE_URI "\"\n>");
1639
1640
0
  if (bookmark->title)
1641
0
    {
1642
0
      gchar *escaped_title;
1643
1644
0
      escaped_title = g_markup_escape_text (bookmark->title, -1);
1645
1646
0
      buffer = g_strconcat ("  "
1647
0
          "<" XBEL_TITLE_ELEMENT ">",
1648
0
          escaped_title,
1649
0
          "</" XBEL_TITLE_ELEMENT ">\n", NULL);
1650
1651
0
      g_string_append (retval, buffer);
1652
1653
0
      g_free (buffer);
1654
0
      g_free (escaped_title);
1655
0
    }
1656
1657
0
  if (bookmark->description)
1658
0
    {
1659
0
      gchar *escaped_desc;
1660
1661
0
      escaped_desc = g_markup_escape_text (bookmark->description, -1);
1662
1663
0
      buffer = g_strconcat ("  "
1664
0
          "<" XBEL_DESC_ELEMENT ">",
1665
0
          escaped_desc,
1666
0
          "</" XBEL_DESC_ELEMENT ">\n", NULL);
1667
0
      g_string_append (retval, buffer);
1668
1669
0
      g_free (buffer);
1670
0
      g_free (escaped_desc);
1671
0
    }
1672
1673
0
  if (!bookmark->items)
1674
0
    goto out;
1675
0
  else
1676
0
    retval = g_string_append (retval, "\n");
1677
1678
  /* the items are stored in reverse order */
1679
0
  for (l = g_list_last (bookmark->items);
1680
0
       l != NULL;
1681
0
       l = l->prev)
1682
0
    {
1683
0
      BookmarkItem *item = (BookmarkItem *) l->data;
1684
0
      gchar *item_dump;
1685
1686
0
      item_dump = bookmark_item_dump (item);
1687
0
      if (!item_dump)
1688
0
        continue;
1689
1690
0
      retval = g_string_append (retval, item_dump);
1691
1692
0
      g_free (item_dump);
1693
0
    }
1694
1695
0
out:
1696
0
  g_string_append (retval, "</" XBEL_ROOT_ELEMENT ">");
1697
1698
0
  if (length)
1699
0
    *length = retval->len;
1700
1701
0
  return g_string_free (retval, FALSE);
1702
0
}
1703
1704
/**************
1705
 *    Misc    *
1706
 **************/
1707
1708
static gboolean
1709
timestamp_from_iso8601 (const gchar  *iso_date,
1710
                        GDateTime   **out_date_time,
1711
                        GError      **error)
1712
0
{
1713
0
  GDateTime *dt = g_date_time_new_from_iso8601 (iso_date, NULL);
1714
0
  if (dt == NULL)
1715
0
    {
1716
0
      g_set_error (error, G_BOOKMARK_FILE_ERROR, G_BOOKMARK_FILE_ERROR_READ,
1717
0
                   _("Invalid date/time ‘%s’ in bookmark file"), iso_date);
1718
0
      return FALSE;
1719
0
    }
1720
1721
0
  *out_date_time = g_steal_pointer (&dt);
1722
0
  return TRUE;
1723
0
}
1724
1725
G_DEFINE_QUARK (g-bookmark-file-error-quark, g_bookmark_file_error)
1726
1727
/********************
1728
 *    Public API    *
1729
 ********************/
1730
1731
/**
1732
 * g_bookmark_file_new: (constructor)
1733
 *
1734
 * Creates a new empty #GBookmarkFile object.
1735
 *
1736
 * Use g_bookmark_file_load_from_file(), g_bookmark_file_load_from_data()
1737
 * or g_bookmark_file_load_from_data_dirs() to read an existing bookmark
1738
 * file.
1739
 *
1740
 * Returns: an empty #GBookmarkFile
1741
 *
1742
 * Since: 2.12
1743
 */
1744
GBookmarkFile *
1745
g_bookmark_file_new (void)
1746
0
{
1747
0
  GBookmarkFile *bookmark;
1748
1749
0
  bookmark = g_new (GBookmarkFile, 1);
1750
1751
0
  g_bookmark_file_init (bookmark);
1752
1753
0
  return bookmark;
1754
0
}
1755
1756
/**
1757
 * g_bookmark_file_copy:
1758
 * @bookmark: A #GBookmarkFile
1759
 *
1760
 * Deeply copies a @bookmark #GBookmarkFile object to a new one.
1761
 *
1762
 * Returns: (transfer full): the copy of @bookmark. Use
1763
 *   g_bookmark_free() when finished using it.
1764
 *
1765
 * Since: 2.76
1766
 */
1767
GBookmarkFile *
1768
g_bookmark_file_copy (GBookmarkFile *bookmark)
1769
0
{
1770
0
  GBookmarkFile *copy;
1771
0
  GList *l;
1772
1773
0
  g_return_val_if_fail (bookmark != NULL, NULL);
1774
1775
0
  copy = g_bookmark_file_new ();
1776
0
  copy->title = g_strdup (bookmark->title);
1777
0
  copy->description = g_strdup (bookmark->description);
1778
0
  copy->items = g_list_copy_deep (bookmark->items, (GCopyFunc) bookmark_item_copy, NULL);
1779
1780
0
  for (l = copy->items; l; l = l->next)
1781
0
    {
1782
0
      BookmarkItem *item = l->data;
1783
0
      g_hash_table_insert (copy->items_by_uri, item->uri, item);
1784
0
    }
1785
1786
0
  g_assert (g_hash_table_size (copy->items_by_uri) ==
1787
0
            g_hash_table_size (bookmark->items_by_uri));
1788
1789
0
  return copy;
1790
0
}
1791
1792
/**
1793
 * g_bookmark_file_free:
1794
 * @bookmark: a #GBookmarkFile
1795
 *
1796
 * Frees a #GBookmarkFile.
1797
 *
1798
 * Since: 2.12
1799
 */
1800
void
1801
g_bookmark_file_free (GBookmarkFile *bookmark)
1802
0
{
1803
0
  if (!bookmark)
1804
0
    return;
1805
1806
0
  g_bookmark_file_clear (bookmark);
1807
1808
0
  g_free (bookmark);
1809
0
}
1810
1811
/**
1812
 * g_bookmark_file_load_from_data:
1813
 * @bookmark: an empty #GBookmarkFile struct
1814
 * @data: (array length=length) (element-type guint8): desktop bookmarks
1815
 *    loaded in memory
1816
 * @length: the length of @data in bytes
1817
 * @error: return location for a #GError, or %NULL
1818
 *
1819
 * Loads a bookmark file from memory into an empty #GBookmarkFile
1820
 * structure.  If the object cannot be created then @error is set to a
1821
 * #GBookmarkFileError.
1822
 *
1823
 * Returns: %TRUE if a desktop bookmark could be loaded.
1824
 *
1825
 * Since: 2.12
1826
 */
1827
gboolean
1828
g_bookmark_file_load_from_data (GBookmarkFile  *bookmark,
1829
        const gchar    *data,
1830
        gsize           length,
1831
        GError        **error)
1832
0
{
1833
0
  GError *parse_error;
1834
0
  gboolean retval;
1835
1836
0
  g_return_val_if_fail (bookmark != NULL, FALSE);
1837
1838
0
  if (length == (gsize) -1)
1839
0
    length = strlen (data);
1840
1841
0
  if (bookmark->items)
1842
0
    {
1843
0
      g_bookmark_file_clear (bookmark);
1844
0
      g_bookmark_file_init (bookmark);
1845
0
    }
1846
1847
0
  parse_error = NULL;
1848
0
  retval = g_bookmark_file_parse (bookmark, data, length, &parse_error);
1849
1850
0
  if (!retval)
1851
0
    g_propagate_error (error, parse_error);
1852
1853
0
  return retval;
1854
0
}
1855
1856
/**
1857
 * g_bookmark_file_load_from_file:
1858
 * @bookmark: an empty #GBookmarkFile struct
1859
 * @filename: (type filename): the path of a filename to load, in the
1860
 *     GLib file name encoding
1861
 * @error: return location for a #GError, or %NULL
1862
 *
1863
 * Loads a desktop bookmark file into an empty #GBookmarkFile structure.
1864
 * If the file could not be loaded then @error is set to either a #GFileError
1865
 * or #GBookmarkFileError.
1866
 *
1867
 * Returns: %TRUE if a desktop bookmark file could be loaded
1868
 *
1869
 * Since: 2.12
1870
 */
1871
gboolean
1872
g_bookmark_file_load_from_file (GBookmarkFile  *bookmark,
1873
        const gchar    *filename,
1874
        GError        **error)
1875
0
{
1876
0
  gboolean ret = FALSE;
1877
0
  gchar *buffer = NULL;
1878
0
  gsize len;
1879
1880
0
  g_return_val_if_fail (bookmark != NULL, FALSE);
1881
0
  g_return_val_if_fail (filename != NULL, FALSE);
1882
1883
0
  if (!g_file_get_contents (filename, &buffer, &len, error))
1884
0
    goto out;
1885
1886
0
  if (!g_bookmark_file_load_from_data (bookmark, buffer, len, error))
1887
0
    goto out;
1888
1889
0
  ret = TRUE;
1890
0
 out:
1891
0
  g_free (buffer);
1892
0
  return ret;
1893
0
}
1894
1895
1896
/* Iterates through all the directories in *dirs trying to
1897
 * find file.  When it successfully locates file, returns a
1898
 * string its absolute path.  It also leaves the unchecked
1899
 * directories in *dirs.  You should free the returned string
1900
 *
1901
 * Adapted from gkeyfile.c
1902
 */
1903
static gchar *
1904
find_file_in_data_dirs (const gchar   *file,
1905
                        gchar       ***dirs,
1906
                        GError       **error)
1907
0
{
1908
0
  gchar **data_dirs, *data_dir, *path;
1909
1910
0
  path = NULL;
1911
1912
0
  if (dirs == NULL)
1913
0
    return NULL;
1914
1915
0
  data_dirs = *dirs;
1916
0
  path = NULL;
1917
0
  while (data_dirs && (data_dir = *data_dirs) && !path)
1918
0
    {
1919
0
      gchar *candidate_file, *sub_dir;
1920
1921
0
      candidate_file = (gchar *) file;
1922
0
      sub_dir = g_strdup ("");
1923
0
      while (candidate_file != NULL && !path)
1924
0
        {
1925
0
          gchar *p;
1926
1927
0
          path = g_build_filename (data_dir, sub_dir,
1928
0
                                   candidate_file, NULL);
1929
1930
0
          candidate_file = strchr (candidate_file, '-');
1931
1932
0
          if (candidate_file == NULL)
1933
0
            break;
1934
1935
0
          candidate_file++;
1936
1937
0
          g_free (sub_dir);
1938
0
          sub_dir = g_strndup (file, candidate_file - file - 1);
1939
1940
0
          for (p = sub_dir; *p != '\0'; p++)
1941
0
            {
1942
0
              if (*p == '-')
1943
0
                *p = G_DIR_SEPARATOR;
1944
0
            }
1945
0
        }
1946
0
      g_free (sub_dir);
1947
0
      data_dirs++;
1948
0
    }
1949
1950
0
  *dirs = data_dirs;
1951
1952
0
  if (!path)
1953
0
    {
1954
0
      g_set_error_literal (error, G_BOOKMARK_FILE_ERROR,
1955
0
                           G_BOOKMARK_FILE_ERROR_FILE_NOT_FOUND,
1956
0
                           _("No valid bookmark file found in data dirs"));
1957
1958
0
      return NULL;
1959
0
    }
1960
1961
0
  return path;
1962
0
}
1963
1964
1965
/**
1966
 * g_bookmark_file_load_from_data_dirs:
1967
 * @bookmark: a #GBookmarkFile
1968
 * @file: (type filename): a relative path to a filename to open and parse
1969
 * @full_path: (out) (optional) (type filename): return location for a string
1970
 *    containing the full path of the file, or %NULL
1971
 * @error: return location for a #GError, or %NULL
1972
 *
1973
 * This function looks for a desktop bookmark file named @file in the
1974
 * paths returned from g_get_user_data_dir() and g_get_system_data_dirs(),
1975
 * loads the file into @bookmark and returns the file's full path in
1976
 * @full_path.  If the file could not be loaded then @error is
1977
 * set to either a #GFileError or #GBookmarkFileError.
1978
 *
1979
 * Returns: %TRUE if a key file could be loaded, %FALSE otherwise
1980
 *
1981
 * Since: 2.12
1982
 */
1983
gboolean
1984
g_bookmark_file_load_from_data_dirs (GBookmarkFile  *bookmark,
1985
             const gchar    *file,
1986
             gchar         **full_path,
1987
             GError        **error)
1988
0
{
1989
0
  GError *file_error = NULL;
1990
0
  gchar **all_data_dirs, **data_dirs;
1991
0
  const gchar *user_data_dir;
1992
0
  const gchar * const * system_data_dirs;
1993
0
  gsize i, j;
1994
0
  gchar *output_path;
1995
0
  gboolean found_file;
1996
1997
0
  g_return_val_if_fail (bookmark != NULL, FALSE);
1998
0
  g_return_val_if_fail (!g_path_is_absolute (file), FALSE);
1999
2000
0
  user_data_dir = g_get_user_data_dir ();
2001
0
  system_data_dirs = g_get_system_data_dirs ();
2002
0
  all_data_dirs = g_new0 (gchar *, g_strv_length ((gchar **)system_data_dirs) + 2);
2003
2004
0
  i = 0;
2005
0
  all_data_dirs[i++] = g_strdup (user_data_dir);
2006
2007
0
  j = 0;
2008
0
  while (system_data_dirs[j] != NULL)
2009
0
    all_data_dirs[i++] = g_strdup (system_data_dirs[j++]);
2010
2011
0
  found_file = FALSE;
2012
0
  data_dirs = all_data_dirs;
2013
0
  output_path = NULL;
2014
0
  while (*data_dirs != NULL && !found_file)
2015
0
    {
2016
0
      g_free (output_path);
2017
2018
0
      output_path = find_file_in_data_dirs (file, &data_dirs, &file_error);
2019
2020
0
      if (file_error)
2021
0
        {
2022
0
          g_propagate_error (error, file_error);
2023
0
    break;
2024
0
        }
2025
2026
0
      found_file = g_bookmark_file_load_from_file (bookmark,
2027
0
                     output_path,
2028
0
                     &file_error);
2029
0
      if (file_error)
2030
0
        {
2031
0
    g_propagate_error (error, file_error);
2032
0
    break;
2033
0
        }
2034
0
    }
2035
2036
0
  if (found_file && full_path)
2037
0
    *full_path = output_path;
2038
0
  else
2039
0
    g_free (output_path);
2040
2041
0
  g_strfreev (all_data_dirs);
2042
2043
0
  return found_file;
2044
0
}
2045
2046
2047
/**
2048
 * g_bookmark_file_to_data:
2049
 * @bookmark: a #GBookmarkFile
2050
 * @length: (out) (optional): return location for the length of the returned string, or %NULL
2051
 * @error: return location for a #GError, or %NULL
2052
 *
2053
 * This function outputs @bookmark as a string.
2054
 *
2055
 * Returns: (transfer full) (array length=length) (element-type guint8):
2056
 *   a newly allocated string holding the contents of the #GBookmarkFile
2057
 *
2058
 * Since: 2.12
2059
 */
2060
gchar *
2061
g_bookmark_file_to_data (GBookmarkFile  *bookmark,
2062
       gsize          *length,
2063
       GError        **error)
2064
0
{
2065
0
  GError *write_error = NULL;
2066
0
  gchar *retval;
2067
2068
0
  g_return_val_if_fail (bookmark != NULL, NULL);
2069
2070
0
  retval = g_bookmark_file_dump (bookmark, length, &write_error);
2071
0
  if (write_error)
2072
0
    {
2073
0
      g_propagate_error (error, write_error);
2074
2075
0
      return NULL;
2076
0
    }
2077
2078
0
  return retval;
2079
0
}
2080
2081
/**
2082
 * g_bookmark_file_to_file:
2083
 * @bookmark: a #GBookmarkFile
2084
 * @filename: (type filename): path of the output file
2085
 * @error: return location for a #GError, or %NULL
2086
 *
2087
 * This function outputs @bookmark into a file.  The write process is
2088
 * guaranteed to be atomic by using g_file_set_contents() internally.
2089
 *
2090
 * Returns: %TRUE if the file was successfully written.
2091
 *
2092
 * Since: 2.12
2093
 */
2094
gboolean
2095
g_bookmark_file_to_file (GBookmarkFile  *bookmark,
2096
       const gchar    *filename,
2097
       GError        **error)
2098
0
{
2099
0
  gchar *data;
2100
0
  GError *data_error, *write_error;
2101
0
  gsize len;
2102
0
  gboolean retval;
2103
2104
0
  g_return_val_if_fail (bookmark != NULL, FALSE);
2105
0
  g_return_val_if_fail (filename != NULL, FALSE);
2106
2107
0
  data_error = NULL;
2108
0
  data = g_bookmark_file_to_data (bookmark, &len, &data_error);
2109
0
  if (data_error)
2110
0
    {
2111
0
      g_propagate_error (error, data_error);
2112
2113
0
      return FALSE;
2114
0
    }
2115
2116
0
  write_error = NULL;
2117
0
  g_file_set_contents (filename, data, len, &write_error);
2118
0
  if (write_error)
2119
0
    {
2120
0
      g_propagate_error (error, write_error);
2121
2122
0
      retval = FALSE;
2123
0
    }
2124
0
  else
2125
0
    retval = TRUE;
2126
2127
0
  g_free (data);
2128
2129
0
  return retval;
2130
0
}
2131
2132
static BookmarkItem *
2133
g_bookmark_file_lookup_item (GBookmarkFile *bookmark,
2134
           const gchar   *uri)
2135
0
{
2136
0
  g_warn_if_fail (bookmark != NULL && uri != NULL);
2137
2138
0
  return g_hash_table_lookup (bookmark->items_by_uri, uri);
2139
0
}
2140
2141
/* this function adds a new item to the list */
2142
static void
2143
g_bookmark_file_add_item (GBookmarkFile  *bookmark,
2144
        BookmarkItem   *item,
2145
        GError        **error)
2146
0
{
2147
0
  g_warn_if_fail (bookmark != NULL);
2148
0
  g_warn_if_fail (item != NULL);
2149
2150
  /* this should never happen; and if it does, then we are
2151
   * screwing up something big time.
2152
   */
2153
0
  if (G_UNLIKELY (g_bookmark_file_has_item (bookmark, item->uri)))
2154
0
    {
2155
0
      g_set_error (error, G_BOOKMARK_FILE_ERROR,
2156
0
       G_BOOKMARK_FILE_ERROR_INVALID_URI,
2157
0
       _("A bookmark for URI “%s” already exists"),
2158
0
       item->uri);
2159
0
      return;
2160
0
    }
2161
2162
0
  bookmark->items = g_list_prepend (bookmark->items, item);
2163
2164
0
  g_hash_table_replace (bookmark->items_by_uri,
2165
0
      item->uri,
2166
0
      item);
2167
2168
0
  if (item->added == NULL)
2169
0
    item->added = g_date_time_new_now_utc ();
2170
2171
0
  if (item->modified == NULL)
2172
0
    item->modified = g_date_time_new_now_utc ();
2173
2174
0
  if (item->visited == NULL)
2175
0
    item->visited = g_date_time_new_now_utc ();
2176
0
}
2177
2178
/**
2179
 * g_bookmark_file_remove_item:
2180
 * @bookmark: a #GBookmarkFile
2181
 * @uri: a valid URI
2182
 * @error: return location for a #GError, or %NULL
2183
 *
2184
 * Removes the bookmark for @uri from the bookmark file @bookmark.
2185
 *
2186
 * Returns: %TRUE if the bookmark was removed successfully.
2187
 *
2188
 * Since: 2.12
2189
 */
2190
gboolean
2191
g_bookmark_file_remove_item (GBookmarkFile  *bookmark,
2192
           const gchar    *uri,
2193
           GError        **error)
2194
0
{
2195
0
  BookmarkItem *item;
2196
2197
0
  g_return_val_if_fail (bookmark != NULL, FALSE);
2198
0
  g_return_val_if_fail (uri != NULL, FALSE);
2199
2200
0
  item = g_bookmark_file_lookup_item (bookmark, uri);
2201
2202
0
  if (!item)
2203
0
    {
2204
0
      g_set_error (error, G_BOOKMARK_FILE_ERROR,
2205
0
       G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
2206
0
       _("No bookmark found for URI “%s”"),
2207
0
       uri);
2208
0
      return FALSE;
2209
0
    }
2210
2211
0
  bookmark->items = g_list_remove (bookmark->items, item);
2212
0
  g_hash_table_remove (bookmark->items_by_uri, item->uri);
2213
2214
0
  bookmark_item_free (item);
2215
2216
0
  return TRUE;
2217
0
}
2218
2219
/**
2220
 * g_bookmark_file_has_item:
2221
 * @bookmark: a #GBookmarkFile
2222
 * @uri: a valid URI
2223
 *
2224
 * Looks whether the desktop bookmark has an item with its URI set to @uri.
2225
 *
2226
 * Returns: %TRUE if @uri is inside @bookmark, %FALSE otherwise
2227
 *
2228
 * Since: 2.12
2229
 */
2230
gboolean
2231
g_bookmark_file_has_item (GBookmarkFile *bookmark,
2232
        const gchar   *uri)
2233
0
{
2234
0
  g_return_val_if_fail (bookmark != NULL, FALSE);
2235
0
  g_return_val_if_fail (uri != NULL, FALSE);
2236
2237
0
  return (NULL != g_hash_table_lookup (bookmark->items_by_uri, uri));
2238
0
}
2239
2240
/**
2241
 * g_bookmark_file_get_uris:
2242
 * @bookmark: a #GBookmarkFile
2243
 * @length: (out) (optional): return location for the number of returned URIs, or %NULL
2244
 *
2245
 * Returns all URIs of the bookmarks in the bookmark file @bookmark.
2246
 * The array of returned URIs will be %NULL-terminated, so @length may
2247
 * optionally be %NULL.
2248
 *
2249
 * Returns: (array length=length) (transfer full): a newly allocated %NULL-terminated array of strings.
2250
 *   Use g_strfreev() to free it.
2251
 *
2252
 * Since: 2.12
2253
 */
2254
gchar **
2255
g_bookmark_file_get_uris (GBookmarkFile *bookmark,
2256
        gsize         *length)
2257
0
{
2258
0
  GList *l;
2259
0
  gchar **uris;
2260
0
  gsize i, n_items;
2261
2262
0
  g_return_val_if_fail (bookmark != NULL, NULL);
2263
2264
0
  n_items = g_list_length (bookmark->items);
2265
0
  uris = g_new0 (gchar *, n_items + 1);
2266
2267
  /* the items are stored in reverse order, so we walk the list backward */
2268
0
  for (l = g_list_last (bookmark->items), i = 0; l != NULL; l = l->prev)
2269
0
    {
2270
0
      BookmarkItem *item = (BookmarkItem *) l->data;
2271
2272
0
      g_warn_if_fail (item != NULL);
2273
2274
0
      uris[i++] = g_strdup (item->uri);
2275
0
    }
2276
0
  uris[i] = NULL;
2277
2278
0
  if (length)
2279
0
    *length = i;
2280
2281
0
  return uris;
2282
0
}
2283
2284
/**
2285
 * g_bookmark_file_set_title:
2286
 * @bookmark: a #GBookmarkFile
2287
 * @uri: (nullable): a valid URI or %NULL
2288
 * @title: a UTF-8 encoded string
2289
 *
2290
 * Sets @title as the title of the bookmark for @uri inside the
2291
 * bookmark file @bookmark.
2292
 *
2293
 * If @uri is %NULL, the title of @bookmark is set.
2294
 *
2295
 * If a bookmark for @uri cannot be found then it is created.
2296
 *
2297
 * Since: 2.12
2298
 */
2299
void
2300
g_bookmark_file_set_title (GBookmarkFile *bookmark,
2301
         const gchar   *uri,
2302
         const gchar   *title)
2303
0
{
2304
0
  g_return_if_fail (bookmark != NULL);
2305
2306
0
  if (!uri)
2307
0
    {
2308
0
      g_free (bookmark->title);
2309
0
      bookmark->title = g_strdup (title);
2310
0
    }
2311
0
  else
2312
0
    {
2313
0
      BookmarkItem *item;
2314
2315
0
      item = g_bookmark_file_lookup_item (bookmark, uri);
2316
0
      if (!item)
2317
0
        {
2318
0
          item = bookmark_item_new (uri);
2319
0
          g_bookmark_file_add_item (bookmark, item, NULL);
2320
0
        }
2321
2322
0
      g_free (item->title);
2323
0
      item->title = g_strdup (title);
2324
2325
0
      bookmark_item_touch_modified (item);
2326
0
    }
2327
0
}
2328
2329
/**
2330
 * g_bookmark_file_get_title:
2331
 * @bookmark: a #GBookmarkFile
2332
 * @uri: (nullable): a valid URI or %NULL
2333
 * @error: return location for a #GError, or %NULL
2334
 *
2335
 * Returns the title of the bookmark for @uri.
2336
 *
2337
 * If @uri is %NULL, the title of @bookmark is returned.
2338
 *
2339
 * In the event the URI cannot be found, %NULL is returned and
2340
 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
2341
 *
2342
 * Returns: (transfer full): a newly allocated string or %NULL if the specified
2343
 *   URI cannot be found.
2344
 *
2345
 * Since: 2.12
2346
 */
2347
gchar *
2348
g_bookmark_file_get_title (GBookmarkFile  *bookmark,
2349
         const gchar    *uri,
2350
         GError        **error)
2351
0
{
2352
0
  BookmarkItem *item;
2353
2354
0
  g_return_val_if_fail (bookmark != NULL, NULL);
2355
2356
0
  if (!uri)
2357
0
    return g_strdup (bookmark->title);
2358
2359
0
  item = g_bookmark_file_lookup_item (bookmark, uri);
2360
0
  if (!item)
2361
0
    {
2362
0
      g_set_error (error, G_BOOKMARK_FILE_ERROR,
2363
0
       G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
2364
0
       _("No bookmark found for URI “%s”"),
2365
0
       uri);
2366
0
      return NULL;
2367
0
    }
2368
2369
0
  return g_strdup (item->title);
2370
0
}
2371
2372
/**
2373
 * g_bookmark_file_set_description:
2374
 * @bookmark: a #GBookmarkFile
2375
 * @uri: (nullable): a valid URI or %NULL
2376
 * @description: a string
2377
 *
2378
 * Sets @description as the description of the bookmark for @uri.
2379
 *
2380
 * If @uri is %NULL, the description of @bookmark is set.
2381
 *
2382
 * If a bookmark for @uri cannot be found then it is created.
2383
 *
2384
 * Since: 2.12
2385
 */
2386
void
2387
g_bookmark_file_set_description (GBookmarkFile *bookmark,
2388
         const gchar   *uri,
2389
         const gchar   *description)
2390
0
{
2391
0
  g_return_if_fail (bookmark != NULL);
2392
2393
0
  if (!uri)
2394
0
    {
2395
0
      g_free (bookmark->description);
2396
0
      bookmark->description = g_strdup (description);
2397
0
    }
2398
0
  else
2399
0
    {
2400
0
      BookmarkItem *item;
2401
2402
0
      item = g_bookmark_file_lookup_item (bookmark, uri);
2403
0
      if (!item)
2404
0
        {
2405
0
          item = bookmark_item_new (uri);
2406
0
          g_bookmark_file_add_item (bookmark, item, NULL);
2407
0
        }
2408
2409
0
      g_free (item->description);
2410
0
      item->description = g_strdup (description);
2411
2412
0
      bookmark_item_touch_modified (item);
2413
0
    }
2414
0
}
2415
2416
/**
2417
 * g_bookmark_file_get_description:
2418
 * @bookmark: a #GBookmarkFile
2419
 * @uri: a valid URI
2420
 * @error: return location for a #GError, or %NULL
2421
 *
2422
 * Retrieves the description of the bookmark for @uri.
2423
 *
2424
 * In the event the URI cannot be found, %NULL is returned and
2425
 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
2426
 *
2427
 * Returns: (transfer full): a newly allocated string or %NULL if the specified
2428
 *   URI cannot be found.
2429
 *
2430
 * Since: 2.12
2431
 */
2432
gchar *
2433
g_bookmark_file_get_description (GBookmarkFile  *bookmark,
2434
         const gchar    *uri,
2435
         GError        **error)
2436
0
{
2437
0
  BookmarkItem *item;
2438
2439
0
  g_return_val_if_fail (bookmark != NULL, NULL);
2440
2441
0
  if (!uri)
2442
0
    return g_strdup (bookmark->description);
2443
2444
0
  item = g_bookmark_file_lookup_item (bookmark, uri);
2445
0
  if (!item)
2446
0
    {
2447
0
      g_set_error (error, G_BOOKMARK_FILE_ERROR,
2448
0
       G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
2449
0
       _("No bookmark found for URI “%s”"),
2450
0
       uri);
2451
0
      return NULL;
2452
0
    }
2453
2454
0
  return g_strdup (item->description);
2455
0
}
2456
2457
/**
2458
 * g_bookmark_file_set_mime_type:
2459
 * @bookmark: a #GBookmarkFile
2460
 * @uri: a valid URI
2461
 * @mime_type: a MIME type
2462
 *
2463
 * Sets @mime_type as the MIME type of the bookmark for @uri.
2464
 *
2465
 * If a bookmark for @uri cannot be found then it is created.
2466
 *
2467
 * Since: 2.12
2468
 */
2469
void
2470
g_bookmark_file_set_mime_type (GBookmarkFile *bookmark,
2471
             const gchar   *uri,
2472
             const gchar   *mime_type)
2473
0
{
2474
0
  BookmarkItem *item;
2475
2476
0
  g_return_if_fail (bookmark != NULL);
2477
0
  g_return_if_fail (uri != NULL);
2478
0
  g_return_if_fail (mime_type != NULL);
2479
2480
0
  item = g_bookmark_file_lookup_item (bookmark, uri);
2481
0
  if (!item)
2482
0
    {
2483
0
      item = bookmark_item_new (uri);
2484
0
      g_bookmark_file_add_item (bookmark, item, NULL);
2485
0
    }
2486
2487
0
  if (!item->metadata)
2488
0
    item->metadata = bookmark_metadata_new ();
2489
2490
0
  g_free (item->metadata->mime_type);
2491
2492
0
  item->metadata->mime_type = g_strdup (mime_type);
2493
0
  bookmark_item_touch_modified (item);
2494
0
}
2495
2496
/**
2497
 * g_bookmark_file_get_mime_type:
2498
 * @bookmark: a #GBookmarkFile
2499
 * @uri: a valid URI
2500
 * @error: return location for a #GError, or %NULL
2501
 *
2502
 * Retrieves the MIME type of the resource pointed by @uri.
2503
 *
2504
 * In the event the URI cannot be found, %NULL is returned and
2505
 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.  In the
2506
 * event that the MIME type cannot be found, %NULL is returned and
2507
 * @error is set to %G_BOOKMARK_FILE_ERROR_INVALID_VALUE.
2508
 *
2509
 * Returns: (transfer full): a newly allocated string or %NULL if the specified
2510
 *   URI cannot be found.
2511
 *
2512
 * Since: 2.12
2513
 */
2514
gchar *
2515
g_bookmark_file_get_mime_type (GBookmarkFile  *bookmark,
2516
             const gchar    *uri,
2517
             GError        **error)
2518
0
{
2519
0
  BookmarkItem *item;
2520
2521
0
  g_return_val_if_fail (bookmark != NULL, NULL);
2522
0
  g_return_val_if_fail (uri != NULL, NULL);
2523
2524
0
  item = g_bookmark_file_lookup_item (bookmark, uri);
2525
0
  if (!item)
2526
0
    {
2527
0
      g_set_error (error, G_BOOKMARK_FILE_ERROR,
2528
0
       G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
2529
0
       _("No bookmark found for URI “%s”"),
2530
0
       uri);
2531
0
      return NULL;
2532
0
    }
2533
2534
0
  if (!item->metadata)
2535
0
    {
2536
0
      g_set_error (error, G_BOOKMARK_FILE_ERROR,
2537
0
       G_BOOKMARK_FILE_ERROR_INVALID_VALUE,
2538
0
       _("No MIME type defined in the bookmark for URI “%s”"),
2539
0
       uri);
2540
0
      return NULL;
2541
0
    }
2542
2543
0
  return g_strdup (item->metadata->mime_type);
2544
0
}
2545
2546
/**
2547
 * g_bookmark_file_set_is_private:
2548
 * @bookmark: a #GBookmarkFile
2549
 * @uri: a valid URI
2550
 * @is_private: %TRUE if the bookmark should be marked as private
2551
 *
2552
 * Sets the private flag of the bookmark for @uri.
2553
 *
2554
 * If a bookmark for @uri cannot be found then it is created.
2555
 *
2556
 * Since: 2.12
2557
 */
2558
void
2559
g_bookmark_file_set_is_private (GBookmarkFile *bookmark,
2560
        const gchar   *uri,
2561
        gboolean       is_private)
2562
0
{
2563
0
  BookmarkItem *item;
2564
2565
0
  g_return_if_fail (bookmark != NULL);
2566
0
  g_return_if_fail (uri != NULL);
2567
2568
0
  item = g_bookmark_file_lookup_item (bookmark, uri);
2569
0
  if (!item)
2570
0
    {
2571
0
      item = bookmark_item_new (uri);
2572
0
      g_bookmark_file_add_item (bookmark, item, NULL);
2573
0
    }
2574
2575
0
  if (!item->metadata)
2576
0
    item->metadata = bookmark_metadata_new ();
2577
2578
0
  item->metadata->is_private = (is_private == TRUE);
2579
0
  bookmark_item_touch_modified (item);
2580
0
}
2581
2582
/**
2583
 * g_bookmark_file_get_is_private:
2584
 * @bookmark: a #GBookmarkFile
2585
 * @uri: a valid URI
2586
 * @error: return location for a #GError, or %NULL
2587
 *
2588
 * Gets whether the private flag of the bookmark for @uri is set.
2589
 *
2590
 * In the event the URI cannot be found, %FALSE is returned and
2591
 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.  In the
2592
 * event that the private flag cannot be found, %FALSE is returned and
2593
 * @error is set to %G_BOOKMARK_FILE_ERROR_INVALID_VALUE.
2594
 *
2595
 * Returns: %TRUE if the private flag is set, %FALSE otherwise.
2596
 *
2597
 * Since: 2.12
2598
 */
2599
gboolean
2600
g_bookmark_file_get_is_private (GBookmarkFile  *bookmark,
2601
        const gchar    *uri,
2602
        GError        **error)
2603
0
{
2604
0
  BookmarkItem *item;
2605
2606
0
  g_return_val_if_fail (bookmark != NULL, FALSE);
2607
0
  g_return_val_if_fail (uri != NULL, FALSE);
2608
2609
0
  item = g_bookmark_file_lookup_item (bookmark, uri);
2610
0
  if (!item)
2611
0
    {
2612
0
      g_set_error (error, G_BOOKMARK_FILE_ERROR,
2613
0
       G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
2614
0
       _("No bookmark found for URI “%s”"),
2615
0
       uri);
2616
0
      return FALSE;
2617
0
    }
2618
2619
0
  if (!item->metadata)
2620
0
    {
2621
0
      g_set_error (error, G_BOOKMARK_FILE_ERROR,
2622
0
       G_BOOKMARK_FILE_ERROR_INVALID_VALUE,
2623
0
       _("No private flag has been defined in bookmark for URI “%s”"),
2624
0
        uri);
2625
0
      return FALSE;
2626
0
    }
2627
2628
0
  return item->metadata->is_private;
2629
0
}
2630
2631
/**
2632
 * g_bookmark_file_set_added:
2633
 * @bookmark: a #GBookmarkFile
2634
 * @uri: a valid URI
2635
 * @added: a timestamp or -1 to use the current time
2636
 *
2637
 * Sets the time the bookmark for @uri was added into @bookmark.
2638
 *
2639
 * If no bookmark for @uri is found then it is created.
2640
 *
2641
 * Since: 2.12
2642
 * Deprecated: 2.66: Use g_bookmark_file_set_added_date_time() instead, as
2643
 *    `time_t` is deprecated due to the year 2038 problem.
2644
 */
2645
void
2646
g_bookmark_file_set_added (GBookmarkFile *bookmark,
2647
         const gchar   *uri,
2648
         time_t         added)
2649
0
{
2650
0
  GDateTime *added_dt = (added != (time_t) -1) ? g_date_time_new_from_unix_utc (added) : g_date_time_new_now_utc ();
2651
0
  g_bookmark_file_set_added_date_time (bookmark, uri, added_dt);
2652
0
  g_date_time_unref (added_dt);
2653
0
}
2654
2655
/**
2656
 * g_bookmark_file_set_added_date_time:
2657
 * @bookmark: a #GBookmarkFile
2658
 * @uri: a valid URI
2659
 * @added: a #GDateTime
2660
 *
2661
 * Sets the time the bookmark for @uri was added into @bookmark.
2662
 *
2663
 * If no bookmark for @uri is found then it is created.
2664
 *
2665
 * Since: 2.66
2666
 */
2667
void
2668
g_bookmark_file_set_added_date_time (GBookmarkFile *bookmark,
2669
                                     const char    *uri,
2670
                                     GDateTime     *added)
2671
0
{
2672
0
  BookmarkItem *item;
2673
2674
0
  g_return_if_fail (bookmark != NULL);
2675
0
  g_return_if_fail (uri != NULL);
2676
0
  g_return_if_fail (added != NULL);
2677
2678
0
  item = g_bookmark_file_lookup_item (bookmark, uri);
2679
0
  if (!item)
2680
0
    {
2681
0
      item = bookmark_item_new (uri);
2682
0
      g_bookmark_file_add_item (bookmark, item, NULL);
2683
0
    }
2684
2685
0
  g_clear_pointer (&item->added, g_date_time_unref);
2686
0
  item->added = g_date_time_ref (added);
2687
0
  g_clear_pointer (&item->modified, g_date_time_unref);
2688
0
  item->modified = g_date_time_ref (added);
2689
0
}
2690
2691
/**
2692
 * g_bookmark_file_get_added:
2693
 * @bookmark: a #GBookmarkFile
2694
 * @uri: a valid URI
2695
 * @error: return location for a #GError, or %NULL
2696
 *
2697
 * Gets the time the bookmark for @uri was added to @bookmark
2698
 *
2699
 * In the event the URI cannot be found, -1 is returned and
2700
 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
2701
 *
2702
 * Returns: a timestamp
2703
 *
2704
 * Since: 2.12
2705
 * Deprecated: 2.66: Use g_bookmark_file_get_added_date_time() instead, as
2706
 *    `time_t` is deprecated due to the year 2038 problem.
2707
 */
2708
time_t
2709
g_bookmark_file_get_added (GBookmarkFile  *bookmark,
2710
         const gchar    *uri,
2711
         GError        **error)
2712
0
{
2713
0
  GDateTime *added = g_bookmark_file_get_added_date_time (bookmark, uri, error);
2714
0
  return (added != NULL) ? g_date_time_to_unix (added) : (time_t) -1;
2715
0
}
2716
2717
/**
2718
 * g_bookmark_file_get_added_date_time:
2719
 * @bookmark: a #GBookmarkFile
2720
 * @uri: a valid URI
2721
 * @error: return location for a #GError, or %NULL
2722
 *
2723
 * Gets the time the bookmark for @uri was added to @bookmark
2724
 *
2725
 * In the event the URI cannot be found, %NULL is returned and
2726
 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
2727
 *
2728
 * Returns: (transfer none): a #GDateTime
2729
 *
2730
 * Since: 2.66
2731
 */
2732
GDateTime *
2733
g_bookmark_file_get_added_date_time (GBookmarkFile  *bookmark,
2734
                                     const char     *uri,
2735
                                     GError        **error)
2736
0
{
2737
0
  BookmarkItem *item;
2738
2739
0
  g_return_val_if_fail (bookmark != NULL, NULL);
2740
0
  g_return_val_if_fail (uri != NULL, NULL);
2741
0
  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2742
2743
0
  item = g_bookmark_file_lookup_item (bookmark, uri);
2744
0
  if (!item)
2745
0
    {
2746
0
      g_set_error (error, G_BOOKMARK_FILE_ERROR,
2747
0
                   G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
2748
0
                   _("No bookmark found for URI “%s”"),
2749
0
                   uri);
2750
0
      return NULL;
2751
0
    }
2752
2753
0
  return item->added;
2754
0
}
2755
2756
/**
2757
 * g_bookmark_file_set_modified:
2758
 * @bookmark: a #GBookmarkFile
2759
 * @uri: a valid URI
2760
 * @modified: a timestamp or -1 to use the current time
2761
 *
2762
 * Sets the last time the bookmark for @uri was last modified.
2763
 *
2764
 * If no bookmark for @uri is found then it is created.
2765
 *
2766
 * The "modified" time should only be set when the bookmark's meta-data
2767
 * was actually changed.  Every function of #GBookmarkFile that
2768
 * modifies a bookmark also changes the modification time, except for
2769
 * g_bookmark_file_set_visited_date_time().
2770
 *
2771
 * Since: 2.12
2772
 * Deprecated: 2.66: Use g_bookmark_file_set_modified_date_time() instead, as
2773
 *    `time_t` is deprecated due to the year 2038 problem.
2774
 */
2775
void
2776
g_bookmark_file_set_modified (GBookmarkFile *bookmark,
2777
            const gchar   *uri,
2778
            time_t         modified)
2779
0
{
2780
0
  GDateTime *modified_dt = (modified != (time_t) -1) ? g_date_time_new_from_unix_utc (modified) : g_date_time_new_now_utc ();
2781
0
  g_bookmark_file_set_modified_date_time (bookmark, uri, modified_dt);
2782
0
  g_date_time_unref (modified_dt);
2783
0
}
2784
2785
/**
2786
 * g_bookmark_file_set_modified_date_time:
2787
 * @bookmark: a #GBookmarkFile
2788
 * @uri: a valid URI
2789
 * @modified: a #GDateTime
2790
 *
2791
 * Sets the last time the bookmark for @uri was last modified.
2792
 *
2793
 * If no bookmark for @uri is found then it is created.
2794
 *
2795
 * The "modified" time should only be set when the bookmark's meta-data
2796
 * was actually changed.  Every function of #GBookmarkFile that
2797
 * modifies a bookmark also changes the modification time, except for
2798
 * g_bookmark_file_set_visited_date_time().
2799
 *
2800
 * Since: 2.66
2801
 */
2802
void
2803
g_bookmark_file_set_modified_date_time (GBookmarkFile *bookmark,
2804
                                        const char    *uri,
2805
                                        GDateTime     *modified)
2806
0
{
2807
0
  BookmarkItem *item;
2808
2809
0
  g_return_if_fail (bookmark != NULL);
2810
0
  g_return_if_fail (uri != NULL);
2811
0
  g_return_if_fail (modified != NULL);
2812
2813
0
  item = g_bookmark_file_lookup_item (bookmark, uri);
2814
0
  if (!item)
2815
0
    {
2816
0
      item = bookmark_item_new (uri);
2817
0
      g_bookmark_file_add_item (bookmark, item, NULL);
2818
0
    }
2819
2820
0
  g_clear_pointer (&item->modified, g_date_time_unref);
2821
0
  item->modified = g_date_time_ref (modified);
2822
0
}
2823
2824
/**
2825
 * g_bookmark_file_get_modified:
2826
 * @bookmark: a #GBookmarkFile
2827
 * @uri: a valid URI
2828
 * @error: return location for a #GError, or %NULL
2829
 *
2830
 * Gets the time when the bookmark for @uri was last modified.
2831
 *
2832
 * In the event the URI cannot be found, -1 is returned and
2833
 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
2834
 *
2835
 * Returns: a timestamp
2836
 *
2837
 * Since: 2.12
2838
 * Deprecated: 2.66: Use g_bookmark_file_get_modified_date_time() instead, as
2839
 *    `time_t` is deprecated due to the year 2038 problem.
2840
 */
2841
time_t
2842
g_bookmark_file_get_modified (GBookmarkFile  *bookmark,
2843
            const gchar    *uri,
2844
            GError        **error)
2845
0
{
2846
0
  GDateTime *modified = g_bookmark_file_get_modified_date_time (bookmark, uri, error);
2847
0
  return (modified != NULL) ? g_date_time_to_unix (modified) : (time_t) -1;
2848
0
}
2849
2850
/**
2851
 * g_bookmark_file_get_modified_date_time:
2852
 * @bookmark: a #GBookmarkFile
2853
 * @uri: a valid URI
2854
 * @error: return location for a #GError, or %NULL
2855
 *
2856
 * Gets the time when the bookmark for @uri was last modified.
2857
 *
2858
 * In the event the URI cannot be found, %NULL is returned and
2859
 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
2860
 *
2861
 * Returns: (transfer none): a #GDateTime
2862
 *
2863
 * Since: 2.66
2864
 */
2865
GDateTime *
2866
g_bookmark_file_get_modified_date_time (GBookmarkFile  *bookmark,
2867
                                        const char     *uri,
2868
                                        GError        **error)
2869
0
{
2870
0
  BookmarkItem *item;
2871
2872
0
  g_return_val_if_fail (bookmark != NULL, NULL);
2873
0
  g_return_val_if_fail (uri != NULL, NULL);
2874
0
  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2875
2876
0
  item = g_bookmark_file_lookup_item (bookmark, uri);
2877
0
  if (!item)
2878
0
    {
2879
0
      g_set_error (error, G_BOOKMARK_FILE_ERROR,
2880
0
                   G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
2881
0
                   _("No bookmark found for URI “%s”"),
2882
0
                   uri);
2883
0
      return NULL;
2884
0
    }
2885
2886
0
  return item->modified;
2887
0
}
2888
2889
/**
2890
 * g_bookmark_file_set_visited:
2891
 * @bookmark: a #GBookmarkFile
2892
 * @uri: a valid URI
2893
 * @visited: a timestamp or -1 to use the current time
2894
 *
2895
 * Sets the time the bookmark for @uri was last visited.
2896
 *
2897
 * If no bookmark for @uri is found then it is created.
2898
 *
2899
 * The "visited" time should only be set if the bookmark was launched,
2900
 * either using the command line retrieved by g_bookmark_file_get_application_info()
2901
 * or by the default application for the bookmark's MIME type, retrieved
2902
 * using g_bookmark_file_get_mime_type().  Changing the "visited" time
2903
 * does not affect the "modified" time.
2904
 *
2905
 * Since: 2.12
2906
 * Deprecated: 2.66: Use g_bookmark_file_set_visited_date_time() instead, as
2907
 *    `time_t` is deprecated due to the year 2038 problem.
2908
 */
2909
void
2910
g_bookmark_file_set_visited (GBookmarkFile *bookmark,
2911
           const gchar   *uri,
2912
           time_t         visited)
2913
0
{
2914
0
  GDateTime *visited_dt = (visited != (time_t) -1) ? g_date_time_new_from_unix_utc (visited) : g_date_time_new_now_utc ();
2915
0
  g_bookmark_file_set_visited_date_time (bookmark, uri, visited_dt);
2916
0
  g_date_time_unref (visited_dt);
2917
0
}
2918
2919
/**
2920
 * g_bookmark_file_set_visited_date_time:
2921
 * @bookmark: a #GBookmarkFile
2922
 * @uri: a valid URI
2923
 * @visited: a #GDateTime
2924
 *
2925
 * Sets the time the bookmark for @uri was last visited.
2926
 *
2927
 * If no bookmark for @uri is found then it is created.
2928
 *
2929
 * The "visited" time should only be set if the bookmark was launched,
2930
 * either using the command line retrieved by g_bookmark_file_get_application_info()
2931
 * or by the default application for the bookmark's MIME type, retrieved
2932
 * using g_bookmark_file_get_mime_type().  Changing the "visited" time
2933
 * does not affect the "modified" time.
2934
 *
2935
 * Since: 2.66
2936
 */
2937
void
2938
g_bookmark_file_set_visited_date_time (GBookmarkFile *bookmark,
2939
                                       const char    *uri,
2940
                                       GDateTime     *visited)
2941
0
{
2942
0
  BookmarkItem *item;
2943
2944
0
  g_return_if_fail (bookmark != NULL);
2945
0
  g_return_if_fail (uri != NULL);
2946
0
  g_return_if_fail (visited != NULL);
2947
2948
0
  item = g_bookmark_file_lookup_item (bookmark, uri);
2949
0
  if (!item)
2950
0
    {
2951
0
      item = bookmark_item_new (uri);
2952
0
      g_bookmark_file_add_item (bookmark, item, NULL);
2953
0
    }
2954
2955
0
  g_clear_pointer (&item->visited, g_date_time_unref);
2956
0
  item->visited = g_date_time_ref (visited);
2957
0
}
2958
2959
/**
2960
 * g_bookmark_file_get_visited:
2961
 * @bookmark: a #GBookmarkFile
2962
 * @uri: a valid URI
2963
 * @error: return location for a #GError, or %NULL
2964
 *
2965
 * Gets the time the bookmark for @uri was last visited.
2966
 *
2967
 * In the event the URI cannot be found, -1 is returned and
2968
 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
2969
 *
2970
 * Returns: a timestamp.
2971
 *
2972
 * Since: 2.12
2973
 * Deprecated: 2.66: Use g_bookmark_file_get_visited_date_time() instead, as
2974
 *    `time_t` is deprecated due to the year 2038 problem.
2975
 */
2976
time_t
2977
g_bookmark_file_get_visited (GBookmarkFile  *bookmark,
2978
           const gchar    *uri,
2979
           GError        **error)
2980
0
{
2981
0
  GDateTime *visited = g_bookmark_file_get_visited_date_time (bookmark, uri, error);
2982
0
  return (visited != NULL) ? g_date_time_to_unix (visited) : (time_t) -1;
2983
0
}
2984
2985
/**
2986
 * g_bookmark_file_get_visited_date_time:
2987
 * @bookmark: a #GBookmarkFile
2988
 * @uri: a valid URI
2989
 * @error: return location for a #GError, or %NULL
2990
 *
2991
 * Gets the time the bookmark for @uri was last visited.
2992
 *
2993
 * In the event the URI cannot be found, %NULL is returned and
2994
 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
2995
 *
2996
 * Returns: (transfer none): a #GDateTime
2997
 *
2998
 * Since: 2.66
2999
 */
3000
GDateTime *
3001
g_bookmark_file_get_visited_date_time (GBookmarkFile  *bookmark,
3002
                                       const char     *uri,
3003
                                       GError        **error)
3004
0
{
3005
0
  BookmarkItem *item;
3006
3007
0
  g_return_val_if_fail (bookmark != NULL, NULL);
3008
0
  g_return_val_if_fail (uri != NULL, NULL);
3009
0
  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
3010
3011
0
  item = g_bookmark_file_lookup_item (bookmark, uri);
3012
0
  if (!item)
3013
0
    {
3014
0
      g_set_error (error, G_BOOKMARK_FILE_ERROR,
3015
0
                   G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
3016
0
                   _("No bookmark found for URI “%s”"),
3017
0
                   uri);
3018
0
      return NULL;
3019
0
    }
3020
3021
0
  return item->visited;
3022
0
}
3023
3024
/**
3025
 * g_bookmark_file_has_group:
3026
 * @bookmark: a #GBookmarkFile
3027
 * @uri: a valid URI
3028
 * @group: the group name to be searched
3029
 * @error: return location for a #GError, or %NULL
3030
 *
3031
 * Checks whether @group appears in the list of groups to which
3032
 * the bookmark for @uri belongs to.
3033
 *
3034
 * In the event the URI cannot be found, %FALSE is returned and
3035
 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
3036
 *
3037
 * Returns: %TRUE if @group was found.
3038
 *
3039
 * Since: 2.12
3040
 */
3041
gboolean
3042
g_bookmark_file_has_group (GBookmarkFile  *bookmark,
3043
         const gchar    *uri,
3044
         const gchar    *group,
3045
         GError        **error)
3046
0
{
3047
0
  BookmarkItem *item;
3048
0
  GList *l;
3049
3050
0
  g_return_val_if_fail (bookmark != NULL, FALSE);
3051
0
  g_return_val_if_fail (uri != NULL, FALSE);
3052
3053
0
  item = g_bookmark_file_lookup_item (bookmark, uri);
3054
0
  if (!item)
3055
0
    {
3056
0
      g_set_error (error, G_BOOKMARK_FILE_ERROR,
3057
0
       G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
3058
0
       _("No bookmark found for URI “%s”"),
3059
0
       uri);
3060
0
      return FALSE;
3061
0
    }
3062
3063
0
  if (!item->metadata)
3064
0
    return FALSE;
3065
3066
0
  for (l = item->metadata->groups; l != NULL; l = l->next)
3067
0
    {
3068
0
      if (strcmp (l->data, group) == 0)
3069
0
        return TRUE;
3070
0
    }
3071
3072
0
  return FALSE;
3073
3074
0
}
3075
3076
/**
3077
 * g_bookmark_file_add_group:
3078
 * @bookmark: a #GBookmarkFile
3079
 * @uri: a valid URI
3080
 * @group: the group name to be added
3081
 *
3082
 * Adds @group to the list of groups to which the bookmark for @uri
3083
 * belongs to.
3084
 *
3085
 * If no bookmark for @uri is found then it is created.
3086
 *
3087
 * Since: 2.12
3088
 */
3089
void
3090
g_bookmark_file_add_group (GBookmarkFile *bookmark,
3091
         const gchar   *uri,
3092
         const gchar   *group)
3093
0
{
3094
0
  BookmarkItem *item;
3095
3096
0
  g_return_if_fail (bookmark != NULL);
3097
0
  g_return_if_fail (uri != NULL);
3098
0
  g_return_if_fail (group != NULL && group[0] != '\0');
3099
3100
0
  item = g_bookmark_file_lookup_item (bookmark, uri);
3101
0
  if (!item)
3102
0
    {
3103
0
      item = bookmark_item_new (uri);
3104
0
      g_bookmark_file_add_item (bookmark, item, NULL);
3105
0
    }
3106
3107
0
  if (!item->metadata)
3108
0
    item->metadata = bookmark_metadata_new ();
3109
3110
0
  if (!g_bookmark_file_has_group (bookmark, uri, group, NULL))
3111
0
    {
3112
0
      item->metadata->groups = g_list_prepend (item->metadata->groups,
3113
0
                                               g_strdup (group));
3114
3115
0
      bookmark_item_touch_modified (item);
3116
0
    }
3117
0
}
3118
3119
/**
3120
 * g_bookmark_file_remove_group:
3121
 * @bookmark: a #GBookmarkFile
3122
 * @uri: a valid URI
3123
 * @group: the group name to be removed
3124
 * @error: return location for a #GError, or %NULL
3125
 *
3126
 * Removes @group from the list of groups to which the bookmark
3127
 * for @uri belongs to.
3128
 *
3129
 * In the event the URI cannot be found, %FALSE is returned and
3130
 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
3131
 * In the event no group was defined, %FALSE is returned and
3132
 * @error is set to %G_BOOKMARK_FILE_ERROR_INVALID_VALUE.
3133
 *
3134
 * Returns: %TRUE if @group was successfully removed.
3135
 *
3136
 * Since: 2.12
3137
 */
3138
gboolean
3139
g_bookmark_file_remove_group (GBookmarkFile  *bookmark,
3140
            const gchar    *uri,
3141
            const gchar    *group,
3142
            GError        **error)
3143
0
{
3144
0
  BookmarkItem *item;
3145
0
  GList *l;
3146
3147
0
  g_return_val_if_fail (bookmark != NULL, FALSE);
3148
0
  g_return_val_if_fail (uri != NULL, FALSE);
3149
3150
0
  item = g_bookmark_file_lookup_item (bookmark, uri);
3151
0
  if (!item)
3152
0
    {
3153
0
      g_set_error (error, G_BOOKMARK_FILE_ERROR,
3154
0
       G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
3155
0
       _("No bookmark found for URI “%s”"),
3156
0
       uri);
3157
0
      return FALSE;
3158
0
    }
3159
3160
0
  if (!item->metadata)
3161
0
    {
3162
0
      g_set_error (error, G_BOOKMARK_FILE_ERROR,
3163
0
                   G_BOOKMARK_FILE_ERROR_INVALID_VALUE,
3164
0
                   _("No groups set in bookmark for URI “%s”"),
3165
0
                   uri);
3166
0
      return FALSE;
3167
0
    }
3168
3169
0
  for (l = item->metadata->groups; l != NULL; l = l->next)
3170
0
    {
3171
0
      if (strcmp (l->data, group) == 0)
3172
0
        {
3173
0
          item->metadata->groups = g_list_remove_link (item->metadata->groups, l);
3174
0
          g_free (l->data);
3175
0
    g_list_free_1 (l);
3176
3177
0
          bookmark_item_touch_modified (item);
3178
3179
0
          return TRUE;
3180
0
        }
3181
0
    }
3182
3183
0
  return FALSE;
3184
0
}
3185
3186
/**
3187
 * g_bookmark_file_set_groups:
3188
 * @bookmark: a #GBookmarkFile
3189
 * @uri: an item's URI
3190
 * @groups: (nullable) (array length=length) (element-type utf8): an array of
3191
 *    group names, or %NULL to remove all groups
3192
 * @length: number of group name values in @groups
3193
 *
3194
 * Sets a list of group names for the item with URI @uri.  Each previously
3195
 * set group name list is removed.
3196
 *
3197
 * If @uri cannot be found then an item for it is created.
3198
 *
3199
 * Since: 2.12
3200
 */
3201
void
3202
g_bookmark_file_set_groups (GBookmarkFile  *bookmark,
3203
          const gchar    *uri,
3204
          const gchar   **groups,
3205
          gsize           length)
3206
0
{
3207
0
  BookmarkItem *item;
3208
0
  gsize i;
3209
3210
0
  g_return_if_fail (bookmark != NULL);
3211
0
  g_return_if_fail (uri != NULL);
3212
0
  g_return_if_fail (groups != NULL);
3213
3214
0
  item = g_bookmark_file_lookup_item (bookmark, uri);
3215
0
  if (!item)
3216
0
    {
3217
0
      item = bookmark_item_new (uri);
3218
0
      g_bookmark_file_add_item (bookmark, item, NULL);
3219
0
    }
3220
3221
0
  if (!item->metadata)
3222
0
    item->metadata = bookmark_metadata_new ();
3223
3224
0
  g_list_free_full (item->metadata->groups, g_free);
3225
0
  item->metadata->groups = NULL;
3226
3227
0
  if (groups)
3228
0
    {
3229
0
      for (i = 0; i < length && groups[i] != NULL; i++)
3230
0
        item->metadata->groups = g_list_append (item->metadata->groups,
3231
0
                  g_strdup (groups[i]));
3232
0
    }
3233
3234
0
  bookmark_item_touch_modified (item);
3235
0
}
3236
3237
/**
3238
 * g_bookmark_file_get_groups:
3239
 * @bookmark: a #GBookmarkFile
3240
 * @uri: a valid URI
3241
 * @length: (out) (optional): return location for the length of the returned string, or %NULL
3242
 * @error: return location for a #GError, or %NULL
3243
 *
3244
 * Retrieves the list of group names of the bookmark for @uri.
3245
 *
3246
 * In the event the URI cannot be found, %NULL is returned and
3247
 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
3248
 *
3249
 * The returned array is %NULL terminated, so @length may optionally
3250
 * be %NULL.
3251
 *
3252
 * Returns: (array length=length) (transfer full): a newly allocated %NULL-terminated array of group names.
3253
 *   Use g_strfreev() to free it.
3254
 *
3255
 * Since: 2.12
3256
 */
3257
gchar **
3258
g_bookmark_file_get_groups (GBookmarkFile  *bookmark,
3259
          const gchar    *uri,
3260
          gsize          *length,
3261
          GError        **error)
3262
0
{
3263
0
  BookmarkItem *item;
3264
0
  GList *l;
3265
0
  gsize len, i;
3266
0
  gchar **retval;
3267
3268
0
  g_return_val_if_fail (bookmark != NULL, NULL);
3269
0
  g_return_val_if_fail (uri != NULL, NULL);
3270
3271
0
  item = g_bookmark_file_lookup_item (bookmark, uri);
3272
0
  if (!item)
3273
0
    {
3274
0
      g_set_error (error, G_BOOKMARK_FILE_ERROR,
3275
0
       G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
3276
0
       _("No bookmark found for URI “%s”"),
3277
0
       uri);
3278
0
      return NULL;
3279
0
    }
3280
3281
0
  if (!item->metadata)
3282
0
    {
3283
0
      if (length)
3284
0
  *length = 0;
3285
3286
0
      return NULL;
3287
0
    }
3288
3289
0
  len = g_list_length (item->metadata->groups);
3290
0
  retval = g_new0 (gchar *, len + 1);
3291
0
  for (l = g_list_last (item->metadata->groups), i = 0;
3292
0
       l != NULL;
3293
0
       l = l->prev)
3294
0
    {
3295
0
      gchar *group_name = (gchar *) l->data;
3296
3297
0
      g_warn_if_fail (group_name != NULL);
3298
3299
0
      retval[i++] = g_strdup (group_name);
3300
0
    }
3301
0
  retval[i] = NULL;
3302
3303
0
  if (length)
3304
0
    *length = len;
3305
3306
0
  return retval;
3307
0
}
3308
3309
/**
3310
 * g_bookmark_file_add_application:
3311
 * @bookmark: a #GBookmarkFile
3312
 * @uri: a valid URI
3313
 * @name: (nullable): the name of the application registering the bookmark
3314
 *   or %NULL
3315
 * @exec: (nullable): command line to be used to launch the bookmark or %NULL
3316
 *
3317
 * Adds the application with @name and @exec to the list of
3318
 * applications that have registered a bookmark for @uri into
3319
 * @bookmark.
3320
 *
3321
 * Every bookmark inside a #GBookmarkFile must have at least an
3322
 * application registered.  Each application must provide a name, a
3323
 * command line useful for launching the bookmark, the number of times
3324
 * the bookmark has been registered by the application and the last
3325
 * time the application registered this bookmark.
3326
 *
3327
 * If @name is %NULL, the name of the application will be the
3328
 * same returned by g_get_application_name(); if @exec is %NULL, the
3329
 * command line will be a composition of the program name as
3330
 * returned by g_get_prgname() and the "\%u" modifier, which will be
3331
 * expanded to the bookmark's URI.
3332
 *
3333
 * This function will automatically take care of updating the
3334
 * registrations count and timestamping in case an application
3335
 * with the same @name had already registered a bookmark for
3336
 * @uri inside @bookmark.
3337
 *
3338
 * If no bookmark for @uri is found, one is created.
3339
 *
3340
 * Since: 2.12
3341
 */
3342
void
3343
g_bookmark_file_add_application (GBookmarkFile *bookmark,
3344
         const gchar   *uri,
3345
         const gchar   *name,
3346
         const gchar   *exec)
3347
0
{
3348
0
  BookmarkItem *item;
3349
0
  gchar *app_name, *app_exec;
3350
0
  GDateTime *stamp;
3351
3352
0
  g_return_if_fail (bookmark != NULL);
3353
0
  g_return_if_fail (uri != NULL);
3354
3355
0
  item = g_bookmark_file_lookup_item (bookmark, uri);
3356
0
  if (!item)
3357
0
    {
3358
0
      item = bookmark_item_new (uri);
3359
0
      g_bookmark_file_add_item (bookmark, item, NULL);
3360
0
    }
3361
3362
0
  if (name && name[0] != '\0')
3363
0
    app_name = g_strdup (name);
3364
0
  else
3365
0
    app_name = g_strdup (g_get_application_name ());
3366
3367
0
  if (exec && exec[0] != '\0')
3368
0
    app_exec = g_strdup (exec);
3369
0
  else
3370
0
    app_exec = g_strjoin (" ", g_get_prgname(), "%u", NULL);
3371
3372
0
  stamp = g_date_time_new_now_utc ();
3373
3374
0
  g_bookmark_file_set_application_info (bookmark, uri,
3375
0
                                        app_name,
3376
0
                                        app_exec,
3377
0
                                        -1,
3378
0
                                        stamp,
3379
0
                                        NULL);
3380
3381
0
  g_date_time_unref (stamp);
3382
0
  g_free (app_exec);
3383
0
  g_free (app_name);
3384
0
}
3385
3386
/**
3387
 * g_bookmark_file_remove_application:
3388
 * @bookmark: a #GBookmarkFile
3389
 * @uri: a valid URI
3390
 * @name: the name of the application
3391
 * @error: return location for a #GError or %NULL
3392
 *
3393
 * Removes application registered with @name from the list of applications
3394
 * that have registered a bookmark for @uri inside @bookmark.
3395
 *
3396
 * In the event the URI cannot be found, %FALSE is returned and
3397
 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
3398
 * In the event that no application with name @app_name has registered
3399
 * a bookmark for @uri,  %FALSE is returned and error is set to
3400
 * %G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED.
3401
 *
3402
 * Returns: %TRUE if the application was successfully removed.
3403
 *
3404
 * Since: 2.12
3405
 */
3406
gboolean
3407
g_bookmark_file_remove_application (GBookmarkFile  *bookmark,
3408
            const gchar    *uri,
3409
            const gchar    *name,
3410
            GError        **error)
3411
0
{
3412
0
  GError *set_error;
3413
0
  gboolean retval;
3414
3415
0
  g_return_val_if_fail (bookmark != NULL, FALSE);
3416
0
  g_return_val_if_fail (uri != NULL, FALSE);
3417
0
  g_return_val_if_fail (name != NULL, FALSE);
3418
3419
0
  set_error = NULL;
3420
0
  retval = g_bookmark_file_set_application_info (bookmark, uri,
3421
0
                                                 name,
3422
0
                                                 "",
3423
0
                                                 0,
3424
0
                                                 NULL,
3425
0
                                                 &set_error);
3426
0
  if (set_error)
3427
0
    {
3428
0
      g_propagate_error (error, set_error);
3429
3430
0
      return FALSE;
3431
0
    }
3432
3433
0
  return retval;
3434
0
}
3435
3436
/**
3437
 * g_bookmark_file_has_application:
3438
 * @bookmark: a #GBookmarkFile
3439
 * @uri: a valid URI
3440
 * @name: the name of the application
3441
 * @error: return location for a #GError or %NULL
3442
 *
3443
 * Checks whether the bookmark for @uri inside @bookmark has been
3444
 * registered by application @name.
3445
 *
3446
 * In the event the URI cannot be found, %FALSE is returned and
3447
 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
3448
 *
3449
 * Returns: %TRUE if the application @name was found
3450
 *
3451
 * Since: 2.12
3452
 */
3453
gboolean
3454
g_bookmark_file_has_application (GBookmarkFile  *bookmark,
3455
         const gchar    *uri,
3456
         const gchar    *name,
3457
         GError        **error)
3458
0
{
3459
0
  BookmarkItem *item;
3460
3461
0
  g_return_val_if_fail (bookmark != NULL, FALSE);
3462
0
  g_return_val_if_fail (uri != NULL, FALSE);
3463
0
  g_return_val_if_fail (name != NULL, FALSE);
3464
3465
0
  item = g_bookmark_file_lookup_item (bookmark, uri);
3466
0
  if (!item)
3467
0
    {
3468
0
      g_set_error (error, G_BOOKMARK_FILE_ERROR,
3469
0
       G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
3470
0
       _("No bookmark found for URI “%s”"),
3471
0
       uri);
3472
0
      return FALSE;
3473
0
    }
3474
3475
0
  return (NULL != bookmark_item_lookup_app_info (item, name));
3476
0
}
3477
3478
/**
3479
 * g_bookmark_file_set_app_info:
3480
 * @bookmark: a #GBookmarkFile
3481
 * @uri: a valid URI
3482
 * @name: an application's name
3483
 * @exec: an application's command line
3484
 * @count: the number of registrations done for this application
3485
 * @stamp: the time of the last registration for this application
3486
 * @error: return location for a #GError or %NULL
3487
 *
3488
 * Sets the meta-data of application @name inside the list of
3489
 * applications that have registered a bookmark for @uri inside
3490
 * @bookmark.
3491
 *
3492
 * You should rarely use this function; use g_bookmark_file_add_application()
3493
 * and g_bookmark_file_remove_application() instead.
3494
 *
3495
 * @name can be any UTF-8 encoded string used to identify an
3496
 * application.
3497
 * @exec can have one of these two modifiers: "\%f", which will
3498
 * be expanded as the local file name retrieved from the bookmark's
3499
 * URI; "\%u", which will be expanded as the bookmark's URI.
3500
 * The expansion is done automatically when retrieving the stored
3501
 * command line using the g_bookmark_file_get_application_info() function.
3502
 * @count is the number of times the application has registered the
3503
 * bookmark; if is < 0, the current registration count will be increased
3504
 * by one, if is 0, the application with @name will be removed from
3505
 * the list of registered applications.
3506
 * @stamp is the Unix time of the last registration; if it is -1, the
3507
 * current time will be used.
3508
 *
3509
 * If you try to remove an application by setting its registration count to
3510
 * zero, and no bookmark for @uri is found, %FALSE is returned and
3511
 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND; similarly,
3512
 * in the event that no application @name has registered a bookmark
3513
 * for @uri,  %FALSE is returned and error is set to
3514
 * %G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED.  Otherwise, if no bookmark
3515
 * for @uri is found, one is created.
3516
 *
3517
 * Returns: %TRUE if the application's meta-data was successfully
3518
 *   changed.
3519
 *
3520
 * Since: 2.12
3521
 * Deprecated: 2.66: Use g_bookmark_file_set_application_info() instead, as
3522
 *    `time_t` is deprecated due to the year 2038 problem.
3523
 */
3524
gboolean
3525
g_bookmark_file_set_app_info (GBookmarkFile  *bookmark,
3526
            const gchar    *uri,
3527
            const gchar    *name,
3528
            const gchar    *exec,
3529
            gint            count,
3530
            time_t          stamp,
3531
            GError        **error)
3532
0
{
3533
0
  GDateTime *stamp_dt = (stamp != (time_t) -1) ? g_date_time_new_from_unix_utc (stamp) : g_date_time_new_now_utc ();
3534
0
  gboolean retval;
3535
0
  retval = g_bookmark_file_set_application_info (bookmark, uri, name, exec, count,
3536
0
                                                 stamp_dt, error);
3537
0
  g_date_time_unref (stamp_dt);
3538
0
  return retval;
3539
0
}
3540
3541
/**
3542
 * g_bookmark_file_set_application_info:
3543
 * @bookmark: a #GBookmarkFile
3544
 * @uri: a valid URI
3545
 * @name: an application's name
3546
 * @exec: an application's command line
3547
 * @count: the number of registrations done for this application
3548
 * @stamp: (nullable): the time of the last registration for this application,
3549
 *    which may be %NULL if @count is 0
3550
 * @error: return location for a #GError or %NULL
3551
 *
3552
 * Sets the meta-data of application @name inside the list of
3553
 * applications that have registered a bookmark for @uri inside
3554
 * @bookmark.
3555
 *
3556
 * You should rarely use this function; use g_bookmark_file_add_application()
3557
 * and g_bookmark_file_remove_application() instead.
3558
 *
3559
 * @name can be any UTF-8 encoded string used to identify an
3560
 * application.
3561
 * @exec can have one of these two modifiers: "\%f", which will
3562
 * be expanded as the local file name retrieved from the bookmark's
3563
 * URI; "\%u", which will be expanded as the bookmark's URI.
3564
 * The expansion is done automatically when retrieving the stored
3565
 * command line using the g_bookmark_file_get_application_info() function.
3566
 * @count is the number of times the application has registered the
3567
 * bookmark; if is < 0, the current registration count will be increased
3568
 * by one, if is 0, the application with @name will be removed from
3569
 * the list of registered applications.
3570
 * @stamp is the Unix time of the last registration.
3571
 *
3572
 * If you try to remove an application by setting its registration count to
3573
 * zero, and no bookmark for @uri is found, %FALSE is returned and
3574
 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND; similarly,
3575
 * in the event that no application @name has registered a bookmark
3576
 * for @uri,  %FALSE is returned and error is set to
3577
 * %G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED.  Otherwise, if no bookmark
3578
 * for @uri is found, one is created.
3579
 *
3580
 * Returns: %TRUE if the application's meta-data was successfully
3581
 *   changed.
3582
 *
3583
 * Since: 2.66
3584
 */
3585
gboolean
3586
g_bookmark_file_set_application_info (GBookmarkFile  *bookmark,
3587
                                      const char     *uri,
3588
                                      const char     *name,
3589
                                      const char     *exec,
3590
                                      int             count,
3591
                                      GDateTime      *stamp,
3592
                                      GError        **error)
3593
0
{
3594
0
  BookmarkItem *item;
3595
0
  BookmarkAppInfo *ai;
3596
3597
0
  g_return_val_if_fail (bookmark != NULL, FALSE);
3598
0
  g_return_val_if_fail (uri != NULL, FALSE);
3599
0
  g_return_val_if_fail (name != NULL, FALSE);
3600
0
  g_return_val_if_fail (exec != NULL, FALSE);
3601
0
  g_return_val_if_fail (count == 0 || stamp != NULL, FALSE);
3602
0
  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
3603
3604
0
  item = g_bookmark_file_lookup_item (bookmark, uri);
3605
0
  if (!item)
3606
0
    {
3607
0
      if (count == 0)
3608
0
        {
3609
0
          g_set_error (error, G_BOOKMARK_FILE_ERROR,
3610
0
           G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
3611
0
           _("No bookmark found for URI “%s”"),
3612
0
           uri);
3613
0
    return FALSE;
3614
0
  }
3615
0
      else
3616
0
        {
3617
0
          item = bookmark_item_new (uri);
3618
0
    g_bookmark_file_add_item (bookmark, item, NULL);
3619
0
  }
3620
0
    }
3621
3622
0
  if (!item->metadata)
3623
0
    item->metadata = bookmark_metadata_new ();
3624
3625
0
  ai = bookmark_item_lookup_app_info (item, name);
3626
0
  if (!ai)
3627
0
    {
3628
0
      if (count == 0)
3629
0
        {
3630
0
          g_set_error (error, G_BOOKMARK_FILE_ERROR,
3631
0
           G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED,
3632
0
           _("No application with name “%s” registered a bookmark for “%s”"),
3633
0
           name,
3634
0
           uri);
3635
0
          return FALSE;
3636
0
        }
3637
0
      else
3638
0
        {
3639
0
          ai = bookmark_app_info_new (name);
3640
3641
0
          item->metadata->applications = g_list_prepend (item->metadata->applications, ai);
3642
0
          g_hash_table_replace (item->metadata->apps_by_name, ai->name, ai);
3643
0
        }
3644
0
    }
3645
3646
0
  if (count == 0)
3647
0
    {
3648
0
      item->metadata->applications = g_list_remove (item->metadata->applications, ai);
3649
0
      g_hash_table_remove (item->metadata->apps_by_name, ai->name);
3650
0
      bookmark_app_info_free (ai);
3651
3652
0
      bookmark_item_touch_modified (item);
3653
3654
0
      return TRUE;
3655
0
    }
3656
0
  else if (count > 0)
3657
0
    ai->count = count;
3658
0
  else
3659
0
    ai->count += 1;
3660
3661
0
  g_clear_pointer (&ai->stamp, g_date_time_unref);
3662
0
  ai->stamp = g_date_time_ref (stamp);
3663
3664
0
  if (exec && exec[0] != '\0')
3665
0
    {
3666
0
      g_free (ai->exec);
3667
0
      ai->exec = g_shell_quote (exec);
3668
0
    }
3669
3670
0
  bookmark_item_touch_modified (item);
3671
3672
0
  return TRUE;
3673
0
}
3674
3675
/* expands the application's command line */
3676
static gchar *
3677
expand_exec_line (const gchar *exec_fmt,
3678
      const gchar *uri)
3679
0
{
3680
0
  GString *exec;
3681
0
  gchar ch;
3682
3683
0
  exec = g_string_sized_new (512);
3684
0
  while ((ch = *exec_fmt++) != '\0')
3685
0
   {
3686
0
     if (ch != '%')
3687
0
       {
3688
0
         exec = g_string_append_c (exec, ch);
3689
0
         continue;
3690
0
       }
3691
3692
0
     ch = *exec_fmt++;
3693
0
     switch (ch)
3694
0
       {
3695
0
       case '\0':
3696
0
   goto out;
3697
0
       case 'U':
3698
0
       case 'u':
3699
0
         g_string_append (exec, uri);
3700
0
         break;
3701
0
       case 'F':
3702
0
       case 'f':
3703
0
         {
3704
0
     gchar *file = g_filename_from_uri (uri, NULL, NULL);
3705
0
           if (file)
3706
0
             {
3707
0
         g_string_append (exec, file);
3708
0
         g_free (file);
3709
0
             }
3710
0
           else
3711
0
             {
3712
0
               g_string_free (exec, TRUE);
3713
0
               return NULL;
3714
0
             }
3715
0
         }
3716
0
         break;
3717
0
       case '%':
3718
0
       default:
3719
0
         exec = g_string_append_c (exec, ch);
3720
0
         break;
3721
0
       }
3722
0
   }
3723
3724
0
 out:
3725
0
  return g_string_free (exec, FALSE);
3726
0
}
3727
3728
/**
3729
 * g_bookmark_file_get_app_info:
3730
 * @bookmark: a #GBookmarkFile
3731
 * @uri: a valid URI
3732
 * @name: an application's name
3733
 * @exec: (out) (optional): return location for the command line of the application, or %NULL
3734
 * @count: (out) (optional): return location for the registration count, or %NULL
3735
 * @stamp: (out) (optional): return location for the last registration time, or %NULL
3736
 * @error: return location for a #GError, or %NULL
3737
 *
3738
 * Gets the registration information of @app_name for the bookmark for
3739
 * @uri.  See g_bookmark_file_set_application_info() for more information about
3740
 * the returned data.
3741
 *
3742
 * The string returned in @app_exec must be freed.
3743
 *
3744
 * In the event the URI cannot be found, %FALSE is returned and
3745
 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.  In the
3746
 * event that no application with name @app_name has registered a bookmark
3747
 * for @uri,  %FALSE is returned and error is set to
3748
 * %G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED. In the event that unquoting
3749
 * the command line fails, an error of the %G_SHELL_ERROR domain is
3750
 * set and %FALSE is returned.
3751
 *
3752
 * Returns: %TRUE on success.
3753
 *
3754
 * Since: 2.12
3755
 * Deprecated: 2.66: Use g_bookmark_file_get_application_info() instead, as
3756
 *    `time_t` is deprecated due to the year 2038 problem.
3757
 */
3758
gboolean
3759
g_bookmark_file_get_app_info (GBookmarkFile  *bookmark,
3760
            const gchar    *uri,
3761
            const gchar    *name,
3762
            gchar         **exec,
3763
            guint          *count,
3764
            time_t         *stamp,
3765
            GError        **error)
3766
0
{
3767
0
  GDateTime *stamp_dt = NULL;
3768
0
  gboolean retval;
3769
3770
0
  retval = g_bookmark_file_get_application_info (bookmark, uri, name, exec, count, &stamp_dt, error);
3771
0
  if (!retval)
3772
0
    return FALSE;
3773
3774
0
  if (stamp != NULL)
3775
0
    *stamp = g_date_time_to_unix (stamp_dt);
3776
3777
0
  return TRUE;
3778
0
}
3779
3780
/**
3781
 * g_bookmark_file_get_application_info:
3782
 * @bookmark: a #GBookmarkFile
3783
 * @uri: a valid URI
3784
 * @name: an application's name
3785
 * @exec: (out) (optional): return location for the command line of the application, or %NULL
3786
 * @count: (out) (optional): return location for the registration count, or %NULL
3787
 * @stamp: (out) (optional) (transfer none): return location for the last registration time, or %NULL
3788
 * @error: return location for a #GError, or %NULL
3789
 *
3790
 * Gets the registration information of @app_name for the bookmark for
3791
 * @uri.  See g_bookmark_file_set_application_info() for more information about
3792
 * the returned data.
3793
 *
3794
 * The string returned in @app_exec must be freed.
3795
 *
3796
 * In the event the URI cannot be found, %FALSE is returned and
3797
 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.  In the
3798
 * event that no application with name @app_name has registered a bookmark
3799
 * for @uri,  %FALSE is returned and error is set to
3800
 * %G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED. In the event that unquoting
3801
 * the command line fails, an error of the %G_SHELL_ERROR domain is
3802
 * set and %FALSE is returned.
3803
 *
3804
 * Returns: %TRUE on success.
3805
 *
3806
 * Since: 2.66
3807
 */
3808
gboolean
3809
g_bookmark_file_get_application_info (GBookmarkFile  *bookmark,
3810
                                      const char     *uri,
3811
                                      const char     *name,
3812
                                      char          **exec,
3813
                                      unsigned int   *count,
3814
                                      GDateTime     **stamp,
3815
                                      GError        **error)
3816
0
{
3817
0
  BookmarkItem *item;
3818
0
  BookmarkAppInfo *ai;
3819
3820
0
  g_return_val_if_fail (bookmark != NULL, FALSE);
3821
0
  g_return_val_if_fail (uri != NULL, FALSE);
3822
0
  g_return_val_if_fail (name != NULL, FALSE);
3823
0
  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
3824
3825
0
  item = g_bookmark_file_lookup_item (bookmark, uri);
3826
0
  if (!item)
3827
0
    {
3828
0
      g_set_error (error, G_BOOKMARK_FILE_ERROR,
3829
0
       G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
3830
0
       _("No bookmark found for URI “%s”"),
3831
0
       uri);
3832
0
      return FALSE;
3833
0
    }
3834
3835
0
  ai = bookmark_item_lookup_app_info (item, name);
3836
0
  if (!ai)
3837
0
    {
3838
0
      g_set_error (error, G_BOOKMARK_FILE_ERROR,
3839
0
       G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED,
3840
0
       _("No application with name “%s” registered a bookmark for “%s”"),
3841
0
       name,
3842
0
       uri);
3843
0
      return FALSE;
3844
0
    }
3845
3846
0
  if (exec)
3847
0
    {
3848
0
      GError *unquote_error = NULL;
3849
0
      gchar *command_line;
3850
3851
0
      command_line = g_shell_unquote (ai->exec, &unquote_error);
3852
0
      if (unquote_error)
3853
0
        {
3854
0
          g_propagate_error (error, unquote_error);
3855
0
          return FALSE;
3856
0
        }
3857
3858
0
      *exec = expand_exec_line (command_line, uri);
3859
0
      if (!*exec)
3860
0
        {
3861
0
          g_set_error (error, G_BOOKMARK_FILE_ERROR,
3862
0
           G_BOOKMARK_FILE_ERROR_INVALID_URI,
3863
0
           _("Failed to expand exec line “%s” with URI “%s”"),
3864
0
         ai->exec, uri);
3865
0
          g_free (command_line);
3866
3867
0
          return FALSE;
3868
0
        }
3869
0
      else
3870
0
        g_free (command_line);
3871
0
    }
3872
3873
0
  if (count)
3874
0
    *count = ai->count;
3875
3876
0
  if (stamp)
3877
0
    *stamp = ai->stamp;
3878
3879
0
  return TRUE;
3880
0
}
3881
3882
/**
3883
 * g_bookmark_file_get_applications:
3884
 * @bookmark: a #GBookmarkFile
3885
 * @uri: a valid URI
3886
 * @length: (out) (optional): return location of the length of the returned list, or %NULL
3887
 * @error: return location for a #GError, or %NULL
3888
 *
3889
 * Retrieves the names of the applications that have registered the
3890
 * bookmark for @uri.
3891
 *
3892
 * In the event the URI cannot be found, %NULL is returned and
3893
 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
3894
 *
3895
 * Returns: (array length=length) (transfer full): a newly allocated %NULL-terminated array of strings.
3896
 *   Use g_strfreev() to free it.
3897
 *
3898
 * Since: 2.12
3899
 */
3900
gchar **
3901
g_bookmark_file_get_applications (GBookmarkFile  *bookmark,
3902
          const gchar    *uri,
3903
          gsize          *length,
3904
          GError        **error)
3905
0
{
3906
0
  BookmarkItem *item;
3907
0
  GList *l;
3908
0
  gchar **apps;
3909
0
  gsize i, n_apps;
3910
3911
0
  g_return_val_if_fail (bookmark != NULL, NULL);
3912
0
  g_return_val_if_fail (uri != NULL, NULL);
3913
3914
0
  item = g_bookmark_file_lookup_item (bookmark, uri);
3915
0
  if (!item)
3916
0
    {
3917
0
      g_set_error (error, G_BOOKMARK_FILE_ERROR,
3918
0
       G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
3919
0
       _("No bookmark found for URI “%s”"),
3920
0
       uri);
3921
0
      return NULL;
3922
0
    }
3923
3924
0
  if (!item->metadata)
3925
0
    {
3926
0
      if (length)
3927
0
  *length = 0;
3928
3929
0
      return NULL;
3930
0
    }
3931
3932
0
  n_apps = g_list_length (item->metadata->applications);
3933
0
  apps = g_new0 (gchar *, n_apps + 1);
3934
3935
0
  for (l = g_list_last (item->metadata->applications), i = 0;
3936
0
       l != NULL;
3937
0
       l = l->prev)
3938
0
    {
3939
0
      BookmarkAppInfo *ai;
3940
3941
0
      ai = (BookmarkAppInfo *) l->data;
3942
3943
0
      g_warn_if_fail (ai != NULL);
3944
0
      g_warn_if_fail (ai->name != NULL);
3945
3946
0
      apps[i++] = g_strdup (ai->name);
3947
0
    }
3948
0
  apps[i] = NULL;
3949
3950
0
  if (length)
3951
0
    *length = i;
3952
3953
0
  return apps;
3954
0
}
3955
3956
/**
3957
 * g_bookmark_file_get_size:
3958
 * @bookmark: a #GBookmarkFile
3959
 *
3960
 * Gets the number of bookmarks inside @bookmark.
3961
 *
3962
 * Returns: the number of bookmarks
3963
 *
3964
 * Since: 2.12
3965
 */
3966
gint
3967
g_bookmark_file_get_size (GBookmarkFile *bookmark)
3968
0
{
3969
0
  g_return_val_if_fail (bookmark != NULL, 0);
3970
3971
0
  return g_list_length (bookmark->items);
3972
0
}
3973
3974
/**
3975
 * g_bookmark_file_move_item:
3976
 * @bookmark: a #GBookmarkFile
3977
 * @old_uri: a valid URI
3978
 * @new_uri: (nullable): a valid URI, or %NULL
3979
 * @error: return location for a #GError or %NULL
3980
 *
3981
 * Changes the URI of a bookmark item from @old_uri to @new_uri.  Any
3982
 * existing bookmark for @new_uri will be overwritten.  If @new_uri is
3983
 * %NULL, then the bookmark is removed.
3984
 *
3985
 * In the event the URI cannot be found, %FALSE is returned and
3986
 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
3987
 *
3988
 * Returns: %TRUE if the URI was successfully changed
3989
 *
3990
 * Since: 2.12
3991
 */
3992
gboolean
3993
g_bookmark_file_move_item (GBookmarkFile  *bookmark,
3994
         const gchar    *old_uri,
3995
         const gchar    *new_uri,
3996
         GError        **error)
3997
0
{
3998
0
  BookmarkItem *item;
3999
4000
0
  g_return_val_if_fail (bookmark != NULL, FALSE);
4001
0
  g_return_val_if_fail (old_uri != NULL, FALSE);
4002
4003
0
  item = g_bookmark_file_lookup_item (bookmark, old_uri);
4004
0
  if (!item)
4005
0
    {
4006
0
      g_set_error (error, G_BOOKMARK_FILE_ERROR,
4007
0
       G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
4008
0
       _("No bookmark found for URI “%s”"),
4009
0
       old_uri);
4010
0
      return FALSE;
4011
0
    }
4012
4013
0
  if (new_uri && new_uri[0] != '\0')
4014
0
    {
4015
0
      if (g_strcmp0 (old_uri, new_uri) == 0)
4016
0
        return TRUE;
4017
4018
0
      if (g_bookmark_file_has_item (bookmark, new_uri))
4019
0
        {
4020
0
          if (!g_bookmark_file_remove_item (bookmark, new_uri, error))
4021
0
            return FALSE;
4022
0
        }
4023
4024
0
      g_hash_table_steal (bookmark->items_by_uri, item->uri);
4025
4026
0
      g_free (item->uri);
4027
0
      item->uri = g_strdup (new_uri);
4028
0
      bookmark_item_touch_modified (item);
4029
4030
0
      g_hash_table_replace (bookmark->items_by_uri, item->uri, item);
4031
4032
0
      return TRUE;
4033
0
    }
4034
0
  else
4035
0
    {
4036
0
      if (!g_bookmark_file_remove_item (bookmark, old_uri, error))
4037
0
        return FALSE;
4038
4039
0
      return TRUE;
4040
0
    }
4041
0
}
4042
4043
/**
4044
 * g_bookmark_file_set_icon:
4045
 * @bookmark: a #GBookmarkFile
4046
 * @uri: a valid URI
4047
 * @href: (nullable): the URI of the icon for the bookmark, or %NULL
4048
 * @mime_type: the MIME type of the icon for the bookmark
4049
 *
4050
 * Sets the icon for the bookmark for @uri. If @href is %NULL, unsets
4051
 * the currently set icon. @href can either be a full URL for the icon
4052
 * file or the icon name following the Icon Naming specification.
4053
 *
4054
 * If no bookmark for @uri is found one is created.
4055
 *
4056
 * Since: 2.12
4057
 */
4058
void
4059
g_bookmark_file_set_icon (GBookmarkFile *bookmark,
4060
        const gchar   *uri,
4061
        const gchar   *href,
4062
        const gchar   *mime_type)
4063
0
{
4064
0
  BookmarkItem *item;
4065
4066
0
  g_return_if_fail (bookmark != NULL);
4067
0
  g_return_if_fail (uri != NULL);
4068
4069
0
  item = g_bookmark_file_lookup_item (bookmark, uri);
4070
0
  if (!item)
4071
0
    {
4072
0
      item = bookmark_item_new (uri);
4073
0
      g_bookmark_file_add_item (bookmark, item, NULL);
4074
0
    }
4075
4076
0
  if (!item->metadata)
4077
0
    item->metadata = bookmark_metadata_new ();
4078
4079
0
  g_free (item->metadata->icon_href);
4080
0
  g_free (item->metadata->icon_mime);
4081
4082
0
  item->metadata->icon_href = g_strdup (href);
4083
4084
0
  if (mime_type && mime_type[0] != '\0')
4085
0
    item->metadata->icon_mime = g_strdup (mime_type);
4086
0
  else
4087
0
    item->metadata->icon_mime = g_strdup ("application/octet-stream");
4088
4089
0
  bookmark_item_touch_modified (item);
4090
0
}
4091
4092
/**
4093
 * g_bookmark_file_get_icon:
4094
 * @bookmark: a #GBookmarkFile
4095
 * @uri: a valid URI
4096
 * @href: (out) (optional): return location for the icon's location or %NULL
4097
 * @mime_type: (out) (optional): return location for the icon's MIME type or %NULL
4098
 * @error: return location for a #GError or %NULL
4099
 *
4100
 * Gets the icon of the bookmark for @uri.
4101
 *
4102
 * In the event the URI cannot be found, %FALSE is returned and
4103
 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
4104
 *
4105
 * Returns: %TRUE if the icon for the bookmark for the URI was found.
4106
 *   You should free the returned strings.
4107
 *
4108
 * Since: 2.12
4109
 */
4110
gboolean
4111
g_bookmark_file_get_icon (GBookmarkFile  *bookmark,
4112
        const gchar    *uri,
4113
        gchar         **href,
4114
        gchar         **mime_type,
4115
        GError        **error)
4116
0
{
4117
0
  BookmarkItem *item;
4118
4119
0
  g_return_val_if_fail (bookmark != NULL, FALSE);
4120
0
  g_return_val_if_fail (uri != NULL, FALSE);
4121
4122
0
  item = g_bookmark_file_lookup_item (bookmark, uri);
4123
0
  if (!item)
4124
0
    {
4125
0
      g_set_error (error, G_BOOKMARK_FILE_ERROR,
4126
0
       G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
4127
0
       _("No bookmark found for URI “%s”"),
4128
0
       uri);
4129
0
      return FALSE;
4130
0
    }
4131
4132
0
  if ((!item->metadata) || (!item->metadata->icon_href))
4133
0
    return FALSE;
4134
4135
0
  if (href)
4136
0
    *href = g_strdup (item->metadata->icon_href);
4137
4138
0
  if (mime_type)
4139
0
    *mime_type = g_strdup (item->metadata->icon_mime);
4140
4141
0
  return TRUE;
4142
0
}