Coverage Report

Created: 2025-07-18 06:08

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