~mfisch/brasero/update-to-3.8.0

« back to all changes in this revision

Viewing changes to src/brasero-io.c

  • Committer: Bazaar Package Importer
  • Author(s): Robert Ancell
  • Date: 2009-06-03 10:36:30 UTC
  • mfrom: (1.1.23 upstream)
  • Revision ID: james.westby@ubuntu.com-20090603103630-2r72408gk45sc0ws
Tags: 2.27.2-0ubuntu1
* New upstream release (LP: #380850)
  - Split burning backend into a new library called libbrasero-burn
  - Split some utilities into a new library called libbrasero-utils
  - Use Brasero as a single instance application using libunique
  - Data spanning
  - Memleak fixes
  - Bug Fixes
  - String fixes
  - Use autogenerated Changelog via git
  - Translation Updates
  - Fixes (LP: #360671)
* Bump GTK+ requirement and add libunique requirement

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2
 
/*
3
 
 * brasero
4
 
 * Copyright (C) Philippe Rouquier 2005-2008 <bonfire-app@wanadoo.fr>
5
 
 * 
6
 
 *  Brasero is free software; you can redistribute it and/or modify
7
 
 *  it under the terms of the GNU General Public License as published by
8
 
 *  the Free Software Foundation; either version 2 of the License, or
9
 
 *  (at your option) any later version.
10
 
 * 
11
 
 * brasero is distributed in the hope that it will be useful,
12
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
 
 * See the GNU General Public License for more details.
15
 
 * 
16
 
 * You should have received a copy of the GNU General Public License
17
 
 * along with brasero.  If not, write to:
18
 
 *      The Free Software Foundation, Inc.,
19
 
 *      51 Franklin Street, Fifth Floor
20
 
 *      Boston, MA  02110-1301, USA.
21
 
 */
22
 
 
23
 
#ifdef HAVE_CONFIG_H
24
 
#  include <config.h>
25
 
#endif
26
 
 
27
 
#include <string.h>
28
 
#include <errno.h>
29
 
 
30
 
#include <glib.h>
31
 
#include <glib-object.h>
32
 
#include <glib/gi18n-lib.h>
33
 
#include <glib/gstdio.h>
34
 
 
35
 
#include <gio/gio.h>
36
 
 
37
 
#include <gtk/gtk.h>
38
 
 
39
 
#ifdef BUILD_PLAYLIST
40
 
#include <totem-pl-parser.h>
41
 
#endif
42
 
 
43
 
#include "burn-basics.h"
44
 
#include "burn-debug.h"
45
 
#include "burn-volume.h"
46
 
 
47
 
#include "brasero-app.h"
48
 
#include "brasero-utils.h"
49
 
#include "brasero-io.h"
50
 
#include "brasero-metadata.h"
51
 
#include "brasero-async-task-manager.h"
52
 
 
53
 
typedef struct _BraseroIOPrivate BraseroIOPrivate;
54
 
struct _BraseroIOPrivate
55
 
{
56
 
        GMutex *lock;
57
 
 
58
 
        GSList *mounted;
59
 
 
60
 
        /* used for returning results */
61
 
        GSList *results;
62
 
        gint results_id;
63
 
 
64
 
        /* used for metadata */
65
 
        GMutex *lock_metadata;
66
 
 
67
 
        GSList *metadatas;
68
 
        GSList *metadata_running;
69
 
 
70
 
        /* used to "buffer" some results returned by metadata.
71
 
         * It takes time to return metadata and it's not unusual
72
 
         * to fetch metadata three times in a row, once for size
73
 
         * preview, once for preview, once adding to selection */
74
 
        GQueue *meta_buffer;
75
 
 
76
 
        guint progress_id;
77
 
        GSList *progress;
78
 
};
79
 
 
80
 
#define BRASERO_IO_PRIVATE(o)  (G_TYPE_INSTANCE_GET_PRIVATE ((o), BRASERO_TYPE_IO, BraseroIOPrivate))
81
 
 
82
 
/* so far 2 metadata at a time has shown to be the best for performance */
83
 
#define MAX_CONCURENT_META      2
84
 
#define MAX_BUFFERED_META       20
85
 
 
86
 
struct _BraseroIOResultCallbackData {
87
 
        gpointer callback_data;
88
 
        gint ref;
89
 
};
90
 
typedef struct _BraseroIOResultCallbackData BraseroIOResultCallbackData;
91
 
 
92
 
struct _BraseroIOJob {
93
 
        gchar *uri;
94
 
        BraseroIOFlags options;
95
 
 
96
 
        const BraseroIOJobBase *base;
97
 
        BraseroIOResultCallbackData *callback_data;
98
 
};
99
 
typedef struct _BraseroIOJob BraseroIOJob;
100
 
 
101
 
#define BRASERO_IO_JOB(data)    ((BraseroIOJob *) (data))
102
 
 
103
 
struct _BraseroIOJobResult {
104
 
        const BraseroIOJobBase *base;
105
 
        BraseroIOResultCallbackData *callback_data;
106
 
 
107
 
        GFileInfo *info;
108
 
        GError *error;
109
 
        gchar *uri;
110
 
};
111
 
typedef struct _BraseroIOJobResult BraseroIOJobResult;
112
 
 
113
 
 
114
 
typedef void    (*BraseroIOJobProgressCallback) (BraseroIOJob *job,
115
 
                                                 BraseroIOJobProgress *progress);
116
 
 
117
 
struct _BraseroIOJobProgress {
118
 
        BraseroIOJob *job;
119
 
        BraseroIOJobProgressCallback progress;
120
 
 
121
 
        BraseroIOPhase phase;
122
 
 
123
 
        guint files_num;
124
 
        guint files_invalid;
125
 
 
126
 
        guint64 read_b;
127
 
        guint64 total_b;
128
 
 
129
 
        guint64 current_read_b;
130
 
        guint64 current_total_b;
131
 
 
132
 
        gchar *current;
133
 
};
134
 
 
135
 
G_DEFINE_TYPE (BraseroIO, brasero_io, BRASERO_TYPE_ASYNC_TASK_MANAGER);
136
 
 
137
 
 
138
 
/**
139
 
 * That's the structure to pass the progress on
140
 
 */
141
 
 
142
 
static gboolean
143
 
brasero_io_job_progress_report_cb (gpointer callback_data)
144
 
{
145
 
        BraseroIOPrivate *priv;
146
 
        GSList *iter;
147
 
 
148
 
        priv = BRASERO_IO_PRIVATE (callback_data);
149
 
 
150
 
        g_mutex_lock (priv->lock);
151
 
        for (iter = priv->progress; iter; iter = iter->next) {
152
 
                BraseroIOJobProgress *progress;
153
 
                gpointer callback_data;
154
 
 
155
 
                progress = iter->data;
156
 
 
157
 
                callback_data = progress->job->callback_data?
158
 
                                progress->job->callback_data->callback_data:
159
 
                                NULL;
160
 
 
161
 
                /* update our progress */
162
 
                progress->progress (progress->job, progress);
163
 
                progress->job->base->progress (progress->job->base->object,
164
 
                                               progress,
165
 
                                               callback_data);
166
 
        }
167
 
        g_mutex_unlock (priv->lock);
168
 
 
169
 
        return TRUE;
170
 
}
171
 
 
172
 
static void
173
 
brasero_io_job_progress_report_start (BraseroIO *self,
174
 
                                      BraseroIOJob *job,
175
 
                                      BraseroIOJobProgressCallback callback)
176
 
{
177
 
        BraseroIOJobProgress *progress;
178
 
        BraseroIOPrivate *priv;
179
 
 
180
 
        priv = BRASERO_IO_PRIVATE (self);
181
 
 
182
 
        if (!job->base->progress)
183
 
                return;
184
 
 
185
 
        progress = g_new0 (BraseroIOJobProgress, 1);
186
 
        progress->job = job;
187
 
        progress->progress = callback;
188
 
 
189
 
        g_mutex_lock (priv->lock);
190
 
        priv->progress = g_slist_prepend (priv->progress, progress);
191
 
        if (!priv->progress_id)
192
 
                priv->progress_id = g_timeout_add (500, brasero_io_job_progress_report_cb, self);
193
 
        g_mutex_unlock (priv->lock);
194
 
}
195
 
 
196
 
static void
197
 
brasero_io_job_progress_report_stop (BraseroIO *self,
198
 
                                     BraseroIOJob *job)
199
 
{
200
 
        BraseroIOPrivate *priv;
201
 
        GSList *iter;
202
 
 
203
 
        priv = BRASERO_IO_PRIVATE (self);
204
 
        g_mutex_lock (priv->lock);
205
 
        for (iter = priv->progress; iter; iter = iter->next) {
206
 
                BraseroIOJobProgress *progress;
207
 
 
208
 
                progress = iter->data;
209
 
                if (progress->job == job) {
210
 
                        priv->progress = g_slist_remove (priv->progress, progress);
211
 
                        if (progress->current)
212
 
                                g_free (progress->current);
213
 
 
214
 
                        g_free (progress);
215
 
                        break;
216
 
                }
217
 
        }
218
 
 
219
 
        if (!priv->progress) {
220
 
                if (priv->progress_id) {
221
 
                        g_source_remove (priv->progress_id);
222
 
                        priv->progress_id = 0;
223
 
                }
224
 
        }
225
 
 
226
 
        g_mutex_unlock (priv->lock);
227
 
}
228
 
 
229
 
const gchar *
230
 
brasero_io_job_progress_get_current (BraseroIOJobProgress *progress)
231
 
{
232
 
        return g_strdup (progress->current);
233
 
}
234
 
 
235
 
guint
236
 
brasero_io_job_progress_get_file_processed (BraseroIOJobProgress *progress)
237
 
{
238
 
        return progress->files_num;
239
 
}
240
 
 
241
 
guint64
242
 
brasero_io_job_progress_get_read (BraseroIOJobProgress *progress)
243
 
{
244
 
        return progress->current_read_b + progress->read_b;
245
 
}
246
 
 
247
 
guint64
248
 
brasero_io_job_progress_get_total (BraseroIOJobProgress *progress)
249
 
{
250
 
        return progress->total_b;
251
 
}
252
 
 
253
 
BraseroIOPhase
254
 
brasero_io_job_progress_get_phase (BraseroIOJobProgress *progress)
255
 
{
256
 
        return progress->phase;
257
 
}
258
 
 
259
 
static void
260
 
brasero_io_unref_result_callback_data (BraseroIOResultCallbackData *data,
261
 
                                       GObject *object,
262
 
                                       BraseroIODestroyCallback destroy,
263
 
                                       gboolean cancelled)
264
 
{
265
 
        if (!data)
266
 
                return;
267
 
 
268
 
        /* see atomically if we are the last to hold a lock */
269
 
        if (!g_atomic_int_dec_and_test (&data->ref))
270
 
                return;
271
 
 
272
 
        if (destroy)
273
 
                destroy (object,
274
 
                         cancelled,
275
 
                         data->callback_data);
276
 
        g_free (data);
277
 
}
278
 
 
279
 
static void
280
 
brasero_io_job_result_free (BraseroIOJobResult *result)
281
 
{
282
 
        if (result->info)
283
 
                g_object_unref (result->info);
284
 
 
285
 
        if (result->error)
286
 
                g_error_free (result->error);
287
 
 
288
 
        if (result->uri)
289
 
                g_free (result->uri);
290
 
 
291
 
        g_free (result);
292
 
}
293
 
 
294
 
/**
295
 
 * Used to return the results
296
 
 */
297
 
 
298
 
static gboolean
299
 
brasero_io_return_result_idle (gpointer callback_data)
300
 
{
301
 
        BraseroIO *self = BRASERO_IO (callback_data);
302
 
        BraseroIOResultCallbackData *data;
303
 
        BraseroIOJobResult *result;
304
 
        BraseroIOPrivate *priv;
305
 
        guint results_id;
306
 
        int i;
307
 
 
308
 
        priv = BRASERO_IO_PRIVATE (self);
309
 
 
310
 
        g_mutex_lock (priv->lock);
311
 
 
312
 
        /* Put that to 0 for now so that a new idle call will be scheduled while
313
 
         * we are in the loop. That way if we block the other one will be able 
314
 
         * to deliver the results. */
315
 
        results_id = priv->results_id;
316
 
        priv->results_id = 0;
317
 
 
318
 
        /* Return several results at a time that can be a huge speed gain.
319
 
         * What should be the value that provides speed and responsiveness? */
320
 
        for (i = 0; priv->results && i < 25; i ++) {
321
 
                result = priv->results->data;
322
 
                priv->results = g_slist_remove (priv->results, result);
323
 
 
324
 
                g_mutex_unlock (priv->lock);
325
 
 
326
 
                data = result->callback_data;
327
 
                if (result->uri || result->info || result->error)
328
 
                        result->base->callback (result->base->object,
329
 
                                                result->error,
330
 
                                                result->uri,
331
 
                                                result->info,
332
 
                                                data? data->callback_data:NULL);
333
 
 
334
 
                /* Else this is just to call destroy () for callback data */
335
 
                brasero_io_unref_result_callback_data (data,
336
 
                                                       result->base->object,
337
 
                                                       result->base->destroy,
338
 
                                                       FALSE);
339
 
                brasero_io_job_result_free (result);
340
 
 
341
 
                g_mutex_lock (priv->lock);
342
 
        }
343
 
 
344
 
        if (!priv->results_id && priv->results) {
345
 
                /* There are still results and no idle call is scheduled so we
346
 
                 * have to restart ourselves to make sure we empty the queue */
347
 
                priv->results_id = results_id;
348
 
                g_mutex_unlock (priv->lock);
349
 
                return TRUE;
350
 
        }
351
 
 
352
 
        g_mutex_unlock (priv->lock);
353
 
        return FALSE;
354
 
}
355
 
 
356
 
static void
357
 
brasero_io_queue_result (BraseroIO *self,
358
 
                         BraseroIOJobResult *result)
359
 
{
360
 
        BraseroIOPrivate *priv;
361
 
 
362
 
        priv = BRASERO_IO_PRIVATE (self);
363
 
 
364
 
        /* insert the task in the results queue */
365
 
        g_mutex_lock (priv->lock);
366
 
        priv->results = g_slist_append (priv->results, result);
367
 
        if (!priv->results_id)
368
 
                priv->results_id = g_idle_add ((GSourceFunc) brasero_io_return_result_idle, self);
369
 
        g_mutex_unlock (priv->lock);
370
 
}
371
 
 
372
 
static void
373
 
brasero_io_return_result (BraseroIO *self,
374
 
                          const BraseroIOJobBase *base,
375
 
                          const gchar *uri,
376
 
                          GFileInfo *info,
377
 
                          GError *error,
378
 
                          BraseroIOResultCallbackData *callback_data)
379
 
{
380
 
        BraseroIOJobResult *result;
381
 
 
382
 
        /* even if it is cancelled we let the result go through to be able to 
383
 
         * call its destroy callback in the main thread. */
384
 
 
385
 
        result = g_new0 (BraseroIOJobResult, 1);
386
 
        result->base = base;
387
 
        result->info = info;
388
 
        result->error = error;
389
 
        result->uri = g_strdup (uri);
390
 
 
391
 
        if (callback_data) {
392
 
                g_atomic_int_inc (&callback_data->ref);
393
 
                result->callback_data = callback_data;
394
 
        }
395
 
 
396
 
        brasero_io_queue_result (self, result);
397
 
}
398
 
 
399
 
/**
400
 
 * Used to push a job
401
 
 */
402
 
 
403
 
static void
404
 
brasero_io_set_job (BraseroIOJob *job,
405
 
                    const BraseroIOJobBase *base,
406
 
                    const gchar *uri,
407
 
                    BraseroIOFlags options,
408
 
                    BraseroIOResultCallbackData *callback_data)
409
 
{
410
 
        job->base = base;
411
 
        job->uri = g_strdup (uri);
412
 
        job->options = options;
413
 
        job->callback_data = callback_data;
414
 
 
415
 
        if (callback_data)
416
 
                g_atomic_int_inc (&job->callback_data->ref);
417
 
}
418
 
 
419
 
static void
420
 
brasero_io_push_job (BraseroIO *self,
421
 
                     BraseroIOJob *job,
422
 
                     const BraseroAsyncTaskType *type)
423
 
{
424
 
        if (job->options & BRASERO_IO_INFO_URGENT)
425
 
                brasero_async_task_manager_queue (BRASERO_ASYNC_TASK_MANAGER (self),
426
 
                                                  BRASERO_ASYNC_URGENT,
427
 
                                                  type,
428
 
                                                  job);
429
 
        else if (job->options & BRASERO_IO_INFO_IDLE)
430
 
                brasero_async_task_manager_queue (BRASERO_ASYNC_TASK_MANAGER (self),
431
 
                                                  BRASERO_ASYNC_IDLE,
432
 
                                                  type,
433
 
                                                  job);
434
 
        else
435
 
                brasero_async_task_manager_queue (BRASERO_ASYNC_TASK_MANAGER (self),
436
 
                                                  BRASERO_ASYNC_NORMAL,
437
 
                                                  type,
438
 
                                                  job);
439
 
}
440
 
 
441
 
/**
442
 
 * Job destruction
443
 
 */
444
 
 
445
 
static void
446
 
brasero_io_job_free (BraseroIO *self,
447
 
                     gboolean cancelled,
448
 
                     BraseroIOJob *job)
449
 
{
450
 
        /* NOTE: the callback_data member is never destroyed here since it would
451
 
         * be destroyed in a thread different from the main loop.
452
 
         * Either it's destroyed in the thread that called brasero_io_cancel ()
453
 
         * or after all results are returned (and therefore in main loop).
454
 
         * As a special case, some jobs like read directory contents have to
455
 
         * return a dummy result to destroy the callback_data if the directory
456
 
         * is empty.
457
 
         * If the job happens to be the last to carry a reference then destroy
458
 
         * it but do it in the main loop by sending a dummy result. */
459
 
 
460
 
        /* NOTE2: that's also used for directory loading when there aren't any
461
 
         * result to notify the caller that the operation has finished but that
462
 
         * there weren't any result to report. */
463
 
        if (job->callback_data) {
464
 
                /* see atomically if we are the last to hold a lock:
465
 
                 * If so, and if cancelled is TRUE destroy it now since we are
466
 
                 * always cancelled (and destroyed in the main loop). Otherwise
467
 
                 * add a dummy result to destroy callback_data. */
468
 
                if (g_atomic_int_dec_and_test (&job->callback_data->ref)) {
469
 
                        if (cancelled) {
470
 
                                if (job->base->destroy)
471
 
                                        job->base->destroy (job->base->object,
472
 
                                                            TRUE,
473
 
                                                            job->callback_data->callback_data);
474
 
 
475
 
                                g_free (job->callback_data);
476
 
                        }
477
 
                        else
478
 
                                brasero_io_return_result (self,
479
 
                                                          job->base,
480
 
                                                          NULL,
481
 
                                                          NULL,
482
 
                                                          NULL,
483
 
                                                          job->callback_data);
484
 
                }
485
 
        }
486
 
 
487
 
        g_free (job->uri);
488
 
        g_free (job);
489
 
}
490
 
 
491
 
static void
492
 
brasero_io_job_destroy (BraseroAsyncTaskManager *manager,
493
 
                        gboolean cancelled,
494
 
                        gpointer callback_data)
495
 
{
496
 
        BraseroIOJob *job = callback_data;
497
 
 
498
 
        /* If a job is destroyed we don't call the destroy callback since it
499
 
         * otherwise it would be called in a different thread. All object that
500
 
         * cancel io ops are doing it either in destroy () and therefore handle
501
 
         * all destruction for callback_data or if they don't they usually don't
502
 
         * pass any callback data anyway. */
503
 
        /* NOTE: usually threads are cancelled from the main thread/loop and
504
 
         * block until the active task is removed which means that if we called
505
 
         * the destroy () then the destruction would be done in the main loop */
506
 
        brasero_io_job_free (BRASERO_IO (manager), cancelled, job);
507
 
}
508
 
 
509
 
/**
510
 
 * That's when we need to mount a remote volume
511
 
 */
512
 
 
513
 
struct _BraseroIOMount {
514
 
        GError *error;
515
 
        gboolean result;
516
 
        gboolean finished;
517
 
};
518
 
typedef struct _BraseroIOMount BraseroIOMount;
519
 
 
520
 
static void
521
 
brasero_io_mount_enclosing_volume_cb (GObject *source,
522
 
                                      GAsyncResult *result,
523
 
                                      gpointer callback_data)
524
 
{
525
 
        BraseroIOMount *mount = callback_data;
526
 
 
527
 
        BRASERO_BURN_LOG ("Volume mounting operation result");
528
 
        mount->result = g_file_mount_enclosing_volume_finish (G_FILE (source),
529
 
                                                              result,
530
 
                                                              &mount->error);
531
 
        mount->finished = TRUE;
532
 
}
533
 
 
534
 
static gboolean
535
 
brasero_io_mount_enclosing_volume (BraseroIO *self,
536
 
                                   GFile *file,
537
 
                                   GCancellable *cancel,
538
 
                                   GError **error)
539
 
{
540
 
        GMount *mounted;
541
 
        GMountOperation *operation;
542
 
        BraseroIOMount mount = { NULL, };
543
 
 
544
 
        operation = gtk_mount_operation_new (GTK_WINDOW (brasero_app_get_default ()));
545
 
        g_file_mount_enclosing_volume (file,
546
 
                                       G_MOUNT_MOUNT_NONE,
547
 
                                       operation,
548
 
                                       cancel,
549
 
                                       brasero_io_mount_enclosing_volume_cb,
550
 
                                       &mount);
551
 
 
552
 
        /* sleep and wait operation end */
553
 
        while (!mount.finished && !g_cancellable_is_cancelled (cancel))
554
 
                sleep (1);
555
 
 
556
 
        mounted = g_file_find_enclosing_mount (file, cancel, NULL);
557
 
        if (mounted) {
558
 
                BraseroIOPrivate *priv;
559
 
 
560
 
                priv = BRASERO_IO_PRIVATE (self);
561
 
 
562
 
                /* Keep these for later to unmount them */
563
 
                if (mount.result) {
564
 
                        g_mutex_lock (priv->lock);
565
 
                        priv->mounted = g_slist_prepend (priv->mounted, mounted);
566
 
                        g_mutex_unlock (priv->lock);
567
 
                }
568
 
                else
569
 
                        g_object_unref (mounted);
570
 
        }
571
 
 
572
 
        if (!mounted
573
 
        &&   mount.error
574
 
        &&  !g_cancellable_is_cancelled (cancel))
575
 
                g_propagate_error (error, mount.error);
576
 
        else if (mount.error)
577
 
                g_error_free (mount.error);
578
 
 
579
 
        BRASERO_BURN_LOG ("Parent volume is %s",
580
 
                          (mounted != NULL && !g_cancellable_is_cancelled (cancel))?
581
 
                          "mounted":"not mounted");
582
 
 
583
 
        return (mounted != NULL && !g_cancellable_is_cancelled (cancel));
584
 
}
585
 
 
586
 
/**
587
 
 * This part deals with symlinks, that allows to get unique filenames by
588
 
 * replacing any parent symlink by its target and check for recursive
589
 
 * symlinks
590
 
 */
591
 
 
592
 
static gchar *
593
 
brasero_io_check_for_parent_symlink (const gchar *escaped_uri,
594
 
                                     GCancellable *cancel)
595
 
{
596
 
        GFile *parent;
597
 
        GFile *file;
598
 
        gchar *uri;
599
 
 
600
 
        /* don't check if the node itself is a symlink since that'll be done */
601
 
        file = g_file_new_for_uri (escaped_uri);
602
 
        uri = g_file_get_uri (file);
603
 
        parent = g_file_get_parent (file);
604
 
        g_object_unref (file);
605
 
 
606
 
        while (parent) {
607
 
                GFile *tmp;
608
 
                GFileInfo *info;
609
 
 
610
 
                info = g_file_query_info (parent,
611
 
                                          G_FILE_ATTRIBUTE_STANDARD_TYPE ","
612
 
                                          G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK ","
613
 
                                          G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET,
614
 
                                          G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,  /* don't follow symlinks */
615
 
                                          NULL,
616
 
                                          NULL);
617
 
                if (!info)
618
 
                        break;
619
 
 
620
 
                /* NOTE: no need to check for broken symlinks since
621
 
                 * we wouldn't have reached this point otherwise */
622
 
                if (g_file_info_get_is_symlink (info)) {
623
 
                        const gchar *target_path;
624
 
                        gchar *parent_uri;
625
 
                        gchar *new_root;
626
 
                        gchar *newuri;
627
 
 
628
 
                        parent_uri = g_file_get_uri (parent);
629
 
                        target_path = g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET);
630
 
 
631
 
                        /* check if this is not a relative path */
632
 
                         if (!g_path_is_absolute (target_path)) {
633
 
                                gchar *tmp;
634
 
 
635
 
                                tmp = g_path_get_dirname (parent_uri);
636
 
                                new_root = g_build_path (G_DIR_SEPARATOR_S,
637
 
                                                         tmp,
638
 
                                                         target_path,
639
 
                                                         NULL);
640
 
                                g_free (tmp);
641
 
                        }
642
 
                        else
643
 
                                new_root = g_filename_to_uri (target_path, NULL, NULL);
644
 
 
645
 
                        newuri = g_build_path (G_DIR_SEPARATOR_S,
646
 
                                               new_root,
647
 
                                               uri + strlen (parent_uri),
648
 
                                               NULL);
649
 
                        g_free (uri);
650
 
                        uri = newuri;   
651
 
 
652
 
                        g_object_unref (parent);
653
 
                        g_free (parent_uri);
654
 
 
655
 
                        parent = g_file_new_for_uri (new_root);
656
 
                        g_free (new_root);
657
 
                }
658
 
 
659
 
                tmp = parent;
660
 
                parent = g_file_get_parent (parent);
661
 
                g_object_unref (tmp);
662
 
 
663
 
                g_object_unref (info);
664
 
        }
665
 
 
666
 
        if (parent)
667
 
                g_object_unref (parent);
668
 
 
669
 
        return uri;
670
 
}
671
 
 
672
 
static gchar *
673
 
brasero_io_get_uri_from_path (GFile *file,
674
 
                              const gchar *path)
675
 
{
676
 
        gchar *uri;
677
 
 
678
 
        if (!g_path_is_absolute (path))
679
 
                file = g_file_resolve_relative_path (file, path);
680
 
        else
681
 
                file = g_file_new_for_path (path);
682
 
 
683
 
        if (!file)
684
 
                return NULL;
685
 
 
686
 
        uri = g_file_get_uri (file);
687
 
        g_object_unref (file);
688
 
        return uri;
689
 
}
690
 
 
691
 
static gboolean
692
 
brasero_io_check_symlink_target (GFile *parent,
693
 
                                 GFileInfo *info)
694
 
{
695
 
        const gchar *target;
696
 
        gchar *target_uri;
697
 
        guint size;
698
 
        gchar *uri;
699
 
 
700
 
        target = g_file_info_get_symlink_target (info);
701
 
        if (!target)
702
 
                return FALSE;
703
 
 
704
 
        target_uri = brasero_io_get_uri_from_path (parent, target);
705
 
        if (!target_uri)
706
 
                return FALSE;
707
 
 
708
 
        /* we check for circular dependency here :
709
 
         * if the target is one of the parent of symlink */
710
 
        size = strlen (target_uri);
711
 
        uri = g_file_get_uri (parent);
712
 
 
713
 
        if (!strncmp (target_uri, uri, size)
714
 
        && (*(uri + size) == '/' || *(uri + size) == '\0')) {
715
 
                g_free (target_uri);
716
 
                g_free (uri);
717
 
                return FALSE;
718
 
        }
719
 
        g_free (uri);
720
 
 
721
 
        g_file_info_set_symlink_target (info, target_uri);
722
 
        g_free (target_uri);
723
 
 
724
 
        return TRUE;
725
 
}
726
 
 
727
 
/**
728
 
 * Used to retrieve metadata for audio files
729
 
 */
730
 
 
731
 
struct _BraserIOMetadataTask {
732
 
        gchar *uri;
733
 
        GSList *results;
734
 
};
735
 
typedef struct _BraseroIOMetadataTask BraseroIOMetadataTask;
736
 
 
737
 
struct _BraseroIOMetadataCached {
738
 
        guint64 last_modified;
739
 
        BraseroMetadataInfo *info;
740
 
};
741
 
typedef struct _BraseroIOMetadataCached BraseroIOMetadataCached;
742
 
 
743
 
static gint
744
 
brasero_io_metadata_lookup_buffer (gconstpointer a, gconstpointer b)
745
 
{
746
 
        const BraseroIOMetadataCached *cached = a;
747
 
        const gchar *uri = b;
748
 
 
749
 
        return strcmp (uri, cached->info->uri);
750
 
}
751
 
 
752
 
static void
753
 
brasero_io_metadata_cached_free (BraseroIOMetadataCached *cached)
754
 
{
755
 
        brasero_metadata_info_free (cached->info);
756
 
        g_free (cached);
757
 
}
758
 
 
759
 
static void
760
 
brasero_io_set_metadata_attributes (GFileInfo *info,
761
 
                                    BraseroMetadataInfo *metadata)
762
 
{
763
 
        g_file_info_set_attribute_int32 (info, BRASERO_IO_ISRC, metadata->isrc);
764
 
        g_file_info_set_attribute_uint64 (info, BRASERO_IO_LEN, metadata->len);
765
 
 
766
 
        if (metadata->artist)
767
 
                g_file_info_set_attribute_string (info, BRASERO_IO_ARTIST, metadata->artist);
768
 
 
769
 
        if (metadata->title)
770
 
                g_file_info_set_attribute_string (info, BRASERO_IO_TITLE, metadata->title);
771
 
 
772
 
        if (metadata->album)
773
 
                g_file_info_set_attribute_string (info, BRASERO_IO_ALBUM, metadata->album);
774
 
 
775
 
        if (metadata->genre)
776
 
                g_file_info_set_attribute_string (info, BRASERO_IO_GENRE, metadata->genre);
777
 
 
778
 
        if (metadata->composer)
779
 
                g_file_info_set_attribute_string (info, BRASERO_IO_COMPOSER, metadata->composer);
780
 
 
781
 
        g_file_info_set_attribute_boolean (info, BRASERO_IO_HAS_AUDIO, metadata->has_audio);
782
 
        g_file_info_set_attribute_boolean (info, BRASERO_IO_HAS_VIDEO, metadata->has_video);
783
 
        g_file_info_set_attribute_boolean (info, BRASERO_IO_IS_SEEKABLE, metadata->is_seekable);
784
 
 
785
 
        if (metadata->snapshot)
786
 
                g_file_info_set_attribute_object (info, BRASERO_IO_THUMBNAIL, G_OBJECT (metadata->snapshot));
787
 
 
788
 
        /* FIXME: what about silences */
789
 
}
790
 
 
791
 
static BraseroMetadata *
792
 
brasero_io_find_metadata (BraseroIO *self,
793
 
                          GCancellable *cancel,
794
 
                          const gchar *uri,
795
 
                          BraseroMetadataFlag flags,
796
 
                          GError **error)
797
 
{
798
 
        GSList *iter;
799
 
        BraseroIOPrivate *priv;
800
 
        BraseroMetadata *metadata;
801
 
 
802
 
        priv = BRASERO_IO_PRIVATE (self);
803
 
 
804
 
        BRASERO_BURN_LOG ("Retrieving available metadata %s", uri);
805
 
 
806
 
        /* First see if a metadata is running with the same uri and the same
807
 
         * flags as us. In this case, acquire the lock and wait for the lock
808
 
         * to become available which will mean that it has finished
809
 
         * NOTE: since we will hold the lock another thread waiting to get 
810
 
         * an available metadata won't be able to have this one. */
811
 
        for (iter = priv->metadata_running; iter; iter = iter->next) {
812
 
                const gchar *metadata_uri;
813
 
                BraseroMetadataFlag metadata_flags;
814
 
 
815
 
                metadata = iter->data;
816
 
                metadata_uri = brasero_metadata_get_uri (metadata);
817
 
                if (!metadata_uri) {
818
 
                        /* It could a metadata that was running but failed to
819
 
                         * retrieve anything and is waiting to be inserted back
820
 
                         * in the available list. Ignore it. */
821
 
                        continue;
822
 
                }
823
 
 
824
 
                metadata_flags = brasero_metadata_get_flags (metadata);
825
 
 
826
 
                if (((flags & metadata_flags) == flags)
827
 
                &&  !strcmp (uri, metadata_uri)) {
828
 
                        /* Found one: release the IO lock to let other threads
829
 
                         * do what they need to do then lock the metadata lock
830
 
                         * Let the thread that got the lock first move it back
831
 
                         * to waiting queue */
832
 
                        BRASERO_BURN_LOG ("Already ongoing search for %s", uri);
833
 
                        brasero_metadata_increase_listener_number (metadata);
834
 
                        return metadata;
835
 
                }
836
 
        }
837
 
 
838
 
        /* Grab an available metadata (NOTE: there should always be at least one
839
 
         * since we run 2 threads at max and have two metadatas available) */
840
 
        while (!priv->metadatas) {
841
 
                if (g_cancellable_is_cancelled (cancel))
842
 
                        return NULL;
843
 
 
844
 
                g_mutex_unlock (priv->lock_metadata);
845
 
                g_usleep (250);
846
 
                g_mutex_lock (priv->lock_metadata);
847
 
        }
848
 
 
849
 
        /* One metadata is finally available */
850
 
        metadata = priv->metadatas->data;
851
 
 
852
 
        /* Try to set it up for running */
853
 
        if (!brasero_metadata_set_uri (metadata, flags, uri, error))
854
 
                return NULL;
855
 
 
856
 
        /* The metadata is ready for running put it in right queue */
857
 
        brasero_metadata_increase_listener_number (metadata);
858
 
        priv->metadatas = g_slist_remove (priv->metadatas, metadata);
859
 
        priv->metadata_running = g_slist_prepend (priv->metadata_running, metadata);
860
 
 
861
 
        return metadata;
862
 
}
863
 
 
864
 
static gboolean
865
 
brasero_io_wait_for_metadata (BraseroIO *self,
866
 
                              GCancellable *cancel,
867
 
                              GFileInfo *info,
868
 
                              BraseroMetadata *metadata,
869
 
                              BraseroMetadataInfo *meta_info)
870
 
{
871
 
        gboolean result;
872
 
        gboolean is_last;
873
 
        BraseroIOPrivate *priv;
874
 
 
875
 
        priv = BRASERO_IO_PRIVATE (self);
876
 
 
877
 
        brasero_metadata_wait (metadata, cancel);
878
 
        g_mutex_lock (priv->lock_metadata);
879
 
 
880
 
        is_last = brasero_metadata_decrease_listener_number (metadata);
881
 
 
882
 
        if (!g_cancellable_is_cancelled (cancel))
883
 
                result = brasero_metadata_get_result (metadata, meta_info, NULL);
884
 
        else
885
 
                result = FALSE;
886
 
 
887
 
        /* Only the last thread waiting for the result will put metadata back
888
 
         * into the available queue and cache the results. */
889
 
        if (!is_last) {
890
 
                g_mutex_unlock (priv->lock_metadata);
891
 
                return result;
892
 
        }
893
 
 
894
 
        if (result) {
895
 
                /* see if we should add it to the buffer */
896
 
                if (meta_info->has_audio || meta_info->has_video) {
897
 
                        BraseroIOMetadataCached *cached;
898
 
 
899
 
                        cached = g_new0 (BraseroIOMetadataCached, 1);
900
 
                        cached->last_modified = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_SIZE);
901
 
 
902
 
                        cached->info = g_new0 (BraseroMetadataInfo, 1);
903
 
                        brasero_metadata_get_result (metadata, cached->info, NULL);
904
 
 
905
 
                        g_queue_push_head (priv->meta_buffer, cached);
906
 
                        if (g_queue_get_length (priv->meta_buffer) > MAX_BUFFERED_META) {
907
 
                                cached = g_queue_pop_tail (priv->meta_buffer);
908
 
                                brasero_io_metadata_cached_free (cached);
909
 
                        }
910
 
                }
911
 
        }
912
 
 
913
 
        /* Make sure it is stopped */
914
 
        BRASERO_BURN_LOG ("Stopping metadata information retrieval (%p)", metadata);
915
 
        brasero_metadata_cancel (metadata);
916
 
 
917
 
        priv->metadata_running = g_slist_remove (priv->metadata_running, metadata);
918
 
        priv->metadatas = g_slist_append (priv->metadatas, metadata);
919
 
 
920
 
        g_mutex_unlock (priv->lock_metadata);
921
 
 
922
 
        return result;
923
 
}
924
 
 
925
 
static gboolean
926
 
brasero_io_get_metadata_info (BraseroIO *self,
927
 
                              GCancellable *cancel,
928
 
                              const gchar *uri,
929
 
                              GFileInfo *info,
930
 
                              BraseroMetadataFlag flags,
931
 
                              BraseroMetadataInfo *meta_info)
932
 
{
933
 
        BraseroMetadata *metadata = NULL;
934
 
        BraseroIOPrivate *priv;
935
 
        const gchar *mime;
936
 
        GList *node;
937
 
 
938
 
        if (g_cancellable_is_cancelled (cancel))
939
 
                return FALSE;
940
 
 
941
 
        priv = BRASERO_IO_PRIVATE (self);
942
 
 
943
 
        mime = g_file_info_get_content_type (info);
944
 
        if (mime
945
 
        && (!strncmp (mime, "image/", 6)
946
 
        ||  !strcmp (mime, "text/plain")
947
 
        ||  !strcmp (mime, "application/x-cue") /* this one make gstreamer crash */
948
 
        ||  !strcmp (mime, "application/x-cd-image")
949
 
        ||  !strcmp (mime, "application/octet-stream")))
950
 
                return FALSE;
951
 
 
952
 
        BRASERO_BURN_LOG ("Retrieving metadata info");
953
 
        g_mutex_lock (priv->lock_metadata);
954
 
 
955
 
        /* Seek in the buffer if we have already explored these metadata. Check 
956
 
         * the info last modified time in case a result should be updated. */
957
 
        node = g_queue_find_custom (priv->meta_buffer,
958
 
                                    uri,
959
 
                                    brasero_io_metadata_lookup_buffer);
960
 
        if (node) {
961
 
                guint64 last_modified;
962
 
                BraseroIOMetadataCached *cached;
963
 
 
964
 
                cached = node->data;
965
 
                last_modified = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_SIZE);
966
 
                if (last_modified == cached->last_modified) {
967
 
                        if (flags & BRASERO_METADATA_FLAG_THUMBNAIL) {
968
 
                                /* If there isn't any snapshot retry */
969
 
                                if (cached->info->snapshot) {
970
 
                                        brasero_metadata_info_copy (meta_info, cached->info);
971
 
                                        g_mutex_unlock (priv->lock_metadata);
972
 
                                        return TRUE;
973
 
                                }
974
 
                        }
975
 
                        else {
976
 
                                brasero_metadata_info_copy (meta_info, cached->info);
977
 
                                g_mutex_unlock (priv->lock_metadata);
978
 
                                return TRUE;
979
 
                        }
980
 
                }
981
 
 
982
 
                /* Found the same URI but it didn't have all required flags so
983
 
                 * we'll get another metadata information; Remove it from the
984
 
                 * queue => no same URI twice */
985
 
                g_queue_remove (priv->meta_buffer, cached);
986
 
                brasero_io_metadata_cached_free (cached);
987
 
 
988
 
                BRASERO_BURN_LOG ("Updating cache information for %s", uri);
989
 
        }
990
 
 
991
 
        /* Find a metadata */
992
 
        metadata = brasero_io_find_metadata (self, cancel, uri, flags, NULL);
993
 
        g_mutex_unlock (priv->lock_metadata);
994
 
 
995
 
        if (!metadata)
996
 
                return FALSE;
997
 
 
998
 
        return brasero_io_wait_for_metadata (self,
999
 
                                             cancel,
1000
 
                                             info,
1001
 
                                             metadata,
1002
 
                                             meta_info);
1003
 
}
1004
 
 
1005
 
/**
1006
 
 * Used to get information about files
1007
 
 */
1008
 
 
1009
 
static GFileInfo *
1010
 
brasero_io_get_file_info_thread_real (BraseroAsyncTaskManager *manager,
1011
 
                                      GCancellable *cancel,
1012
 
                                      GFile *file,
1013
 
                                      BraseroIOFlags options,
1014
 
                                      GError **error)
1015
 
{
1016
 
        gchar attributes [256] = {G_FILE_ATTRIBUTE_STANDARD_NAME ","
1017
 
                                  G_FILE_ATTRIBUTE_STANDARD_SIZE ","
1018
 
                                  G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK ","
1019
 
                                  G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET ","
1020
 
                                  G_FILE_ATTRIBUTE_STANDARD_TYPE};
1021
 
        GError *local_error = NULL;
1022
 
        gboolean should_thumbnail;
1023
 
        GFileInfo *info;
1024
 
 
1025
 
        if (g_cancellable_is_cancelled (cancel))
1026
 
                return NULL;
1027
 
 
1028
 
        if (options & BRASERO_IO_INFO_PERM)
1029
 
                strcat (attributes, "," G_FILE_ATTRIBUTE_ACCESS_CAN_READ);
1030
 
        if (options & BRASERO_IO_INFO_MIME)
1031
 
                strcat (attributes, "," G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
1032
 
        if (options & BRASERO_IO_INFO_ICON)
1033
 
                strcat (attributes, "," G_FILE_ATTRIBUTE_STANDARD_ICON);
1034
 
        if (options & BRASERO_IO_INFO_METADATA_THUMBNAIL)
1035
 
                strcat (attributes, "," G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
1036
 
 
1037
 
        /* if retrieving metadata we need this one to check if a possible result
1038
 
         * in cache should be updated or used */
1039
 
        if (options & BRASERO_IO_INFO_METADATA)
1040
 
                strcat (attributes, "," G_FILE_ATTRIBUTE_STANDARD_SIZE);
1041
 
 
1042
 
        info = g_file_query_info (file,
1043
 
                                  attributes,
1044
 
                                  (options & BRASERO_IO_INFO_FOLLOW_SYMLINK)?G_FILE_QUERY_INFO_NONE:G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,        /* follow symlinks by default*/
1045
 
                                  cancel,
1046
 
                                  &local_error);
1047
 
        if (!info) {
1048
 
                if (local_error && local_error->code == G_IO_ERROR_NOT_MOUNTED) {
1049
 
                        gboolean res;
1050
 
 
1051
 
                        BRASERO_BURN_LOG ("Starting to mount parent volume");
1052
 
                        g_error_free (local_error);
1053
 
                        local_error = NULL;
1054
 
 
1055
 
                        /* try to mount whatever has to be mounted */
1056
 
                        /* NOTE: of course, we block a thread but one advantage
1057
 
                         * is that we won't have many queries to mount the same
1058
 
                         * remote volume at the same time. */
1059
 
                        res = brasero_io_mount_enclosing_volume (BRASERO_IO (manager),
1060
 
                                                                 file,
1061
 
                                                                 cancel,
1062
 
                                                                 error);
1063
 
                        if (!res)
1064
 
                                return NULL;
1065
 
 
1066
 
                        return brasero_io_get_file_info_thread_real (manager,
1067
 
                                                                     cancel,
1068
 
                                                                     file,
1069
 
                                                                     options,
1070
 
                                                                     error);
1071
 
                }
1072
 
 
1073
 
                g_propagate_error (error, local_error);
1074
 
                return NULL;
1075
 
        }
1076
 
 
1077
 
        if (g_file_info_get_is_symlink (info)) {
1078
 
                GFile *parent;
1079
 
 
1080
 
                parent = g_file_get_parent (file);
1081
 
                if (!brasero_io_check_symlink_target (parent, info)) {
1082
 
                        g_set_error (error,
1083
 
                                     BRASERO_ERROR,
1084
 
                                     BRASERO_ERROR_SYMLINK_LOOP,
1085
 
                                     _("Recursive symbolic link"));
1086
 
 
1087
 
                        g_object_unref (info);
1088
 
                        g_object_unref (file);
1089
 
                        g_object_unref (parent);
1090
 
                        return NULL;
1091
 
                }
1092
 
                g_object_unref (parent);
1093
 
        }
1094
 
 
1095
 
        should_thumbnail = FALSE;
1096
 
        if (options & BRASERO_IO_INFO_METADATA_THUMBNAIL) {
1097
 
                const gchar *path;
1098
 
 
1099
 
                path = g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
1100
 
                if (path) {
1101
 
                        GdkPixbuf *pixbuf;
1102
 
 
1103
 
                        pixbuf = gdk_pixbuf_new_from_file (path, NULL);
1104
 
                        if (pixbuf) {
1105
 
                                g_file_info_set_attribute_object (info,
1106
 
                                                                  BRASERO_IO_THUMBNAIL,
1107
 
                                                                  G_OBJECT (pixbuf));
1108
 
                                g_object_unref (pixbuf);
1109
 
                        }
1110
 
                        else
1111
 
                                should_thumbnail = TRUE;
1112
 
                }
1113
 
                else if (!g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED))
1114
 
                        should_thumbnail = TRUE;
1115
 
        }
1116
 
 
1117
 
        /* see if we are supposed to get metadata for this file (provided it's
1118
 
         * an audio file of course). */
1119
 
        if ((g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR
1120
 
        ||   g_file_info_get_file_type (info) == G_FILE_TYPE_SYMBOLIC_LINK)
1121
 
        &&  (options & BRASERO_IO_INFO_METADATA)) {
1122
 
                BraseroMetadataInfo metadata = { NULL };
1123
 
                BraseroMetadataFlag flags;
1124
 
                gboolean result;
1125
 
                gchar *uri;
1126
 
 
1127
 
                flags = ((options & BRASERO_IO_INFO_METADATA_MISSING_CODEC) ? BRASERO_METADATA_FLAG_MISSING : 0)|
1128
 
                        ((should_thumbnail) ? BRASERO_METADATA_FLAG_THUMBNAIL : 0);
1129
 
 
1130
 
                uri = g_file_get_uri (file);
1131
 
                result = brasero_io_get_metadata_info (BRASERO_IO (manager),
1132
 
                                                       cancel,
1133
 
                                                       uri,
1134
 
                                                       info,
1135
 
                                                       flags,
1136
 
                                                       &metadata);
1137
 
                g_free (uri);
1138
 
 
1139
 
                if (result)
1140
 
                        brasero_io_set_metadata_attributes (info, &metadata);
1141
 
 
1142
 
                brasero_metadata_info_clear (&metadata);
1143
 
        }
1144
 
 
1145
 
        return info;
1146
 
}
1147
 
 
1148
 
static BraseroAsyncTaskResult
1149
 
brasero_io_get_file_info_thread (BraseroAsyncTaskManager *manager,
1150
 
                                 GCancellable *cancel,
1151
 
                                 gpointer callback_data)
1152
 
{
1153
 
        BraseroIOJob *job = callback_data;
1154
 
        gchar *file_uri = NULL;
1155
 
        GError *error = NULL;
1156
 
        GFileInfo *info;
1157
 
        GFile *file;
1158
 
 
1159
 
        if (job->options & BRASERO_IO_INFO_CHECK_PARENT_SYMLINK) {
1160
 
                /* If we want to make sure a directory is not added twice we have to make sure
1161
 
                 * that it doesn't have a symlink as parent otherwise "/home/Foo/Bar" with Foo
1162
 
                 * as a symlink pointing to /tmp would be seen as a different file from /tmp/Bar 
1163
 
                 * It would be much better if we could use the inode numbers provided by gnome_vfs
1164
 
                 * unfortunately they are guint64 and can't be used in hash tables as keys.
1165
 
                 * Therefore we check parents up to root to see if there are symlinks and if so
1166
 
                 * we get a path without symlinks in it. This is done only for local file */
1167
 
                file_uri = brasero_io_check_for_parent_symlink (job->uri, cancel);
1168
 
        }
1169
 
 
1170
 
        if (g_cancellable_is_cancelled (cancel)) {
1171
 
                g_free (file_uri);
1172
 
                return BRASERO_ASYNC_TASK_FINISHED;
1173
 
        }
1174
 
 
1175
 
        file = g_file_new_for_uri (file_uri?file_uri:job->uri);
1176
 
        info = brasero_io_get_file_info_thread_real (manager,
1177
 
                                                     cancel,
1178
 
                                                     file,
1179
 
                                                     job->options,
1180
 
                                                     &error);
1181
 
 
1182
 
        /* do this to have a very nice URI:
1183
 
         * for example: file://pouet instead of file://../directory/pouet */
1184
 
        g_free (file_uri);
1185
 
        file_uri = g_file_get_uri (file);
1186
 
        g_object_unref (file);
1187
 
 
1188
 
        brasero_io_return_result (BRASERO_IO (manager),
1189
 
                                  job->base,
1190
 
                                  file_uri,
1191
 
                                  info,
1192
 
                                  error,
1193
 
                                  job->callback_data);
1194
 
 
1195
 
        g_free (file_uri);
1196
 
        return BRASERO_ASYNC_TASK_FINISHED;
1197
 
}
1198
 
 
1199
 
static const BraseroAsyncTaskType info_type = {
1200
 
        brasero_io_get_file_info_thread,
1201
 
        brasero_io_job_destroy
1202
 
};
1203
 
 
1204
 
static void
1205
 
brasero_io_new_file_info_job (BraseroIO *self,
1206
 
                              const gchar *uri,
1207
 
                              const BraseroIOJobBase *base,
1208
 
                              BraseroIOFlags options,
1209
 
                              BraseroIOResultCallbackData *callback_data)
1210
 
{
1211
 
        BraseroIOJob *job;
1212
 
 
1213
 
        job = g_new0 (BraseroIOJob, 1);
1214
 
        brasero_io_set_job (job,
1215
 
                            base,
1216
 
                            uri,
1217
 
                            options,
1218
 
                            callback_data);
1219
 
 
1220
 
        brasero_io_push_job (self, job, &info_type);
1221
 
}
1222
 
 
1223
 
void
1224
 
brasero_io_get_file_info (BraseroIO *self,
1225
 
                          const gchar *uri,
1226
 
                          const BraseroIOJobBase *base,
1227
 
                          BraseroIOFlags options,
1228
 
                          gpointer user_data)
1229
 
{
1230
 
        BraseroIOResultCallbackData *callback_data = NULL;
1231
 
 
1232
 
        if (user_data) {
1233
 
                callback_data = g_new0 (BraseroIOResultCallbackData, 1);
1234
 
                callback_data->callback_data = user_data;
1235
 
        }
1236
 
 
1237
 
        brasero_io_new_file_info_job (self, uri, base, options, callback_data);
1238
 
}
1239
 
 
1240
 
/**
1241
 
 * Used to parse playlists
1242
 
 */
1243
 
 
1244
 
#ifdef BUILD_PLAYLIST
1245
 
 
1246
 
struct _BraseroIOPlaylist {
1247
 
        gchar *title;
1248
 
        GSList *uris;
1249
 
};
1250
 
typedef struct _BraseroIOPlaylist BraseroIOPlaylist;
1251
 
 
1252
 
static void
1253
 
brasero_io_playlist_clear (BraseroIOPlaylist *data)
1254
 
{
1255
 
        g_slist_foreach (data->uris, (GFunc) g_free, NULL);
1256
 
        g_slist_free (data->uris);
1257
 
 
1258
 
        g_free (data->title);
1259
 
}
1260
 
 
1261
 
static void
1262
 
brasero_io_add_playlist_entry_parsed_cb (TotemPlParser *parser,
1263
 
                                         const gchar *uri,
1264
 
                                         GHashTable *metadata,
1265
 
                                         BraseroIOPlaylist *data)
1266
 
{
1267
 
        data->uris = g_slist_prepend (data->uris, g_strdup (uri));
1268
 
}
1269
 
 
1270
 
static void
1271
 
brasero_io_start_playlist_cb (TotemPlParser *parser,
1272
 
                                  const gchar *uri,
1273
 
                                  GHashTable *metadata,
1274
 
                                  BraseroIOPlaylist *data)
1275
 
{
1276
 
        const gchar *title;
1277
 
 
1278
 
        title = g_hash_table_lookup (metadata, TOTEM_PL_PARSER_FIELD_TITLE);
1279
 
        if (!title)
1280
 
                return;
1281
 
 
1282
 
        if (!data->title)
1283
 
                data->title = g_strdup (title);
1284
 
}
1285
 
 
1286
 
static gboolean
1287
 
brasero_io_parse_playlist_get_uris (const gchar *uri,
1288
 
                                    BraseroIOPlaylist *playlist,
1289
 
                                    GError **error)
1290
 
{
1291
 
        gboolean result;
1292
 
        TotemPlParser *parser;
1293
 
 
1294
 
        parser = totem_pl_parser_new ();
1295
 
        g_signal_connect (parser,
1296
 
                          "playlist-started",
1297
 
                          G_CALLBACK (brasero_io_start_playlist_cb),
1298
 
                          playlist);
1299
 
        g_signal_connect (parser,
1300
 
                          "entry-parsed",
1301
 
                          G_CALLBACK (brasero_io_add_playlist_entry_parsed_cb),
1302
 
                          playlist);
1303
 
 
1304
 
        if (g_object_class_find_property (G_OBJECT_GET_CLASS (parser), "recurse"))
1305
 
                g_object_set (G_OBJECT (parser), "recurse", FALSE, NULL);
1306
 
 
1307
 
        result = totem_pl_parser_parse (parser, uri, TRUE);
1308
 
        g_object_unref (parser);
1309
 
 
1310
 
        if (!result) {
1311
 
                g_set_error (error,
1312
 
                             BRASERO_ERROR,
1313
 
                             BRASERO_ERROR_GENERAL,
1314
 
                             _("The file does not appear to be a playlist"));
1315
 
 
1316
 
                return FALSE;
1317
 
        }
1318
 
 
1319
 
        return TRUE;
1320
 
}
1321
 
 
1322
 
static BraseroAsyncTaskResult
1323
 
brasero_io_parse_playlist_thread (BraseroAsyncTaskManager *manager,
1324
 
                                  GCancellable *cancel,
1325
 
                                  gpointer callback_data)
1326
 
{
1327
 
        GSList *iter;
1328
 
        gboolean result;
1329
 
        GFileInfo *info;
1330
 
        GError *error = NULL;
1331
 
        BraseroIOJob *job = callback_data;
1332
 
        BraseroIOPlaylist data = { NULL, };
1333
 
 
1334
 
        result = brasero_io_parse_playlist_get_uris (job->uri, &data, &error);
1335
 
        if (!result) {
1336
 
                brasero_io_return_result (BRASERO_IO (manager),
1337
 
                                          job->base,
1338
 
                                          job->uri,
1339
 
                                          NULL,
1340
 
                                          error,
1341
 
                                          job->callback_data);
1342
 
                return BRASERO_ASYNC_TASK_FINISHED;
1343
 
        }
1344
 
 
1345
 
        if (g_cancellable_is_cancelled (cancel))
1346
 
                return BRASERO_ASYNC_TASK_FINISHED;
1347
 
 
1348
 
        /* that's finished; Send the title */
1349
 
        info = g_file_info_new ();
1350
 
        g_file_info_set_attribute_boolean (info,
1351
 
                                           BRASERO_IO_IS_PLAYLIST,
1352
 
                                           TRUE);
1353
 
        g_file_info_set_attribute_uint32 (info,
1354
 
                                          BRASERO_IO_PLAYLIST_ENTRIES_NUM,
1355
 
                                          g_slist_length (data.uris));
1356
 
        if (data.title)
1357
 
                g_file_info_set_attribute_string (info,
1358
 
                                                  BRASERO_IO_PLAYLIST_TITLE,
1359
 
                                                  data.title);
1360
 
 
1361
 
        brasero_io_return_result (BRASERO_IO (manager),
1362
 
                                  job->base,
1363
 
                                  job->uri,
1364
 
                                  info,
1365
 
                                  NULL,
1366
 
                                  job->callback_data);
1367
 
 
1368
 
        /* Now get information about each file in the list. 
1369
 
         * Reverse order of list to get a correct order for entries. */
1370
 
        data.uris = g_slist_reverse (data.uris);
1371
 
        for (iter = data.uris; iter; iter = iter->next) {
1372
 
                GFile *file;
1373
 
                gchar *child;
1374
 
                GFileInfo *child_info;
1375
 
 
1376
 
                child = iter->data;
1377
 
                if (g_cancellable_is_cancelled (cancel))
1378
 
                        break;
1379
 
 
1380
 
                file = g_file_new_for_uri (child);
1381
 
                child_info = brasero_io_get_file_info_thread_real (manager,
1382
 
                                                                   cancel,
1383
 
                                                                   file,
1384
 
                                                                   job->options,
1385
 
                                                                   NULL);
1386
 
                g_object_unref (file);
1387
 
 
1388
 
                if (!child_info)
1389
 
                        continue;
1390
 
 
1391
 
                brasero_io_return_result (BRASERO_IO (manager),
1392
 
                                          job->base,
1393
 
                                          child,
1394
 
                                          child_info,
1395
 
                                          NULL,
1396
 
                                          job->callback_data);
1397
 
        }
1398
 
 
1399
 
        brasero_io_playlist_clear (&data);
1400
 
        return BRASERO_ASYNC_TASK_FINISHED;
1401
 
}
1402
 
 
1403
 
static const BraseroAsyncTaskType playlist_type = {
1404
 
        brasero_io_parse_playlist_thread,
1405
 
        brasero_io_job_destroy
1406
 
};
1407
 
 
1408
 
void
1409
 
brasero_io_parse_playlist (BraseroIO *self,
1410
 
                           const gchar *uri,
1411
 
                           const BraseroIOJobBase *base,
1412
 
                           BraseroIOFlags options,
1413
 
                           gpointer user_data)
1414
 
{
1415
 
        BraseroIOJob *job;
1416
 
        BraseroIOResultCallbackData *callback_data = NULL;
1417
 
 
1418
 
        if (user_data) {
1419
 
                callback_data = g_new0 (BraseroIOResultCallbackData, 1);
1420
 
                callback_data->callback_data = user_data;
1421
 
        }
1422
 
 
1423
 
        job = g_new0 (BraseroIOJob, 1);
1424
 
        brasero_io_set_job (job,
1425
 
                            base,
1426
 
                            uri,
1427
 
                            options,
1428
 
                            callback_data);
1429
 
 
1430
 
        brasero_io_push_job (self, job, &playlist_type);
1431
 
}
1432
 
 
1433
 
#endif
1434
 
 
1435
 
/**
1436
 
 * Used to count the number of files under a directory and the children size
1437
 
 */
1438
 
 
1439
 
struct _BraseroIOCountData {
1440
 
        BraseroIOJob job;
1441
 
 
1442
 
        GSList *uris;
1443
 
        GSList *children;
1444
 
 
1445
 
        guint files_num;
1446
 
        guint files_invalid;
1447
 
 
1448
 
        guint64 total_b;
1449
 
        gboolean progress_started;
1450
 
};
1451
 
typedef struct _BraseroIOCountData BraseroIOCountData;
1452
 
 
1453
 
static void
1454
 
brasero_io_get_file_count_destroy (BraseroAsyncTaskManager *manager,
1455
 
                                   gboolean cancelled,
1456
 
                                   gpointer callback_data)
1457
 
{
1458
 
        BraseroIOCountData *data = callback_data;
1459
 
 
1460
 
        g_slist_foreach (data->uris, (GFunc) g_free, NULL);
1461
 
        g_slist_free (data->uris);
1462
 
 
1463
 
        g_slist_foreach (data->children, (GFunc) g_object_unref, NULL);
1464
 
        g_slist_free (data->children);
1465
 
 
1466
 
        brasero_io_job_progress_report_stop (BRASERO_IO (manager), callback_data);
1467
 
 
1468
 
        brasero_io_job_free (BRASERO_IO (manager), cancelled, callback_data);
1469
 
}
1470
 
 
1471
 
#ifdef BUILD_PLAYLIST
1472
 
 
1473
 
static gboolean
1474
 
brasero_io_get_file_count_process_playlist (BraseroIO *self,
1475
 
                                            GCancellable *cancel,
1476
 
                                            BraseroIOCountData *data,
1477
 
                                            const gchar *uri)
1478
 
{
1479
 
        BraseroIOPlaylist playlist = {NULL, };
1480
 
        GSList *iter;
1481
 
 
1482
 
        if (!brasero_io_parse_playlist_get_uris (uri, &playlist, NULL))
1483
 
                return FALSE;
1484
 
 
1485
 
        for (iter = playlist.uris; iter; iter = iter->next) {
1486
 
                gboolean result;
1487
 
                GFileInfo *info;
1488
 
                gchar *child_uri;
1489
 
                BraseroMetadataInfo metadata = { NULL, };
1490
 
 
1491
 
                child_uri = iter->data;
1492
 
                data->files_num ++;
1493
 
 
1494
 
                info = g_file_info_new ();
1495
 
                result = brasero_io_get_metadata_info (self,
1496
 
                                                       cancel,
1497
 
                                                       child_uri,
1498
 
                                                       info,
1499
 
                                                       ((data->job.options & BRASERO_IO_INFO_METADATA_MISSING_CODEC) ? BRASERO_METADATA_FLAG_MISSING : 0) |
1500
 
                                                       ((data->job.options & BRASERO_IO_INFO_METADATA_THUMBNAIL) ? BRASERO_METADATA_FLAG_THUMBNAIL : 0),
1501
 
                                                       &metadata);
1502
 
 
1503
 
                if (result)
1504
 
                        data->total_b += metadata.len;
1505
 
                else
1506
 
                        data->files_invalid ++;
1507
 
 
1508
 
                brasero_metadata_info_clear (&metadata);
1509
 
                g_object_unref (info);
1510
 
        }
1511
 
 
1512
 
        brasero_io_playlist_clear (&playlist);
1513
 
        return TRUE;
1514
 
}
1515
 
 
1516
 
#endif 
1517
 
 
1518
 
static void
1519
 
brasero_io_get_file_count_process_file (BraseroIO *self,
1520
 
                                        GCancellable *cancel,
1521
 
                                        BraseroIOCountData *data,
1522
 
                                        GFile *file,
1523
 
                                        GFileInfo *info)
1524
 
{
1525
 
        if (data->job.options & BRASERO_IO_INFO_METADATA) {
1526
 
                BraseroMetadataInfo metadata = { NULL, };
1527
 
                gboolean result = FALSE;
1528
 
                gchar *child_uri;
1529
 
 
1530
 
                child_uri = g_file_get_uri (file);
1531
 
                result = brasero_io_get_metadata_info (self,
1532
 
                                                       cancel,
1533
 
                                                       child_uri,
1534
 
                                                       info,
1535
 
                                                       ((data->job.options & BRASERO_IO_INFO_METADATA_MISSING_CODEC) ? BRASERO_METADATA_FLAG_MISSING : 0) |
1536
 
                                                       ((data->job.options & BRASERO_IO_INFO_METADATA_THUMBNAIL) ? BRASERO_METADATA_FLAG_THUMBNAIL : 0),
1537
 
                                                       &metadata);
1538
 
                if (result)
1539
 
                        data->total_b += metadata.len;
1540
 
 
1541
 
#ifdef BUILD_PLAYLIST
1542
 
 
1543
 
                /* see if that's a playlist (and if we have recursive on). */
1544
 
                else if (data->job.options & BRASERO_IO_INFO_RECURSIVE) {
1545
 
                        const gchar *mime;
1546
 
 
1547
 
                        mime = g_file_info_get_content_type (info);
1548
 
                        if (mime
1549
 
                        && (!strcmp (mime, "audio/x-scpls")
1550
 
                        ||  !strcmp (mime, "audio/x-ms-asx")
1551
 
                        ||  !strcmp (mime, "audio/x-mp3-playlist")
1552
 
                        ||  !strcmp (mime, "audio/x-mpegurl"))) {
1553
 
                                if (!brasero_io_get_file_count_process_playlist (self, cancel, data, child_uri))
1554
 
                                        data->files_invalid ++;
1555
 
                        }
1556
 
 
1557
 
                        data->files_invalid ++;
1558
 
                }
1559
 
 
1560
 
#endif
1561
 
 
1562
 
                else
1563
 
                        data->files_invalid ++;
1564
 
 
1565
 
                brasero_metadata_info_clear (&metadata);
1566
 
                g_free (child_uri);
1567
 
                return;
1568
 
        }
1569
 
 
1570
 
        data->total_b += g_file_info_get_size (info);
1571
 
}
1572
 
 
1573
 
static void
1574
 
brasero_io_get_file_count_process_directory (BraseroIO *self,
1575
 
                                             GCancellable *cancel,
1576
 
                                             BraseroIOCountData *data)
1577
 
{
1578
 
        GFile *file;
1579
 
        GFileInfo *info;
1580
 
        GError *error = NULL;
1581
 
        GFileEnumerator *enumerator;
1582
 
        gchar attributes [512] = {G_FILE_ATTRIBUTE_STANDARD_NAME "," 
1583
 
                                  G_FILE_ATTRIBUTE_STANDARD_SIZE "," 
1584
 
                                  G_FILE_ATTRIBUTE_STANDARD_TYPE };
1585
 
 
1586
 
        if ((data->job.options & BRASERO_IO_INFO_METADATA)
1587
 
        &&  (data->job.options & BRASERO_IO_INFO_RECURSIVE))
1588
 
                strcat (attributes, "," G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
1589
 
 
1590
 
        file = data->children->data;
1591
 
        data->children = g_slist_remove (data->children, file);
1592
 
 
1593
 
        enumerator = g_file_enumerate_children (file,
1594
 
                                                attributes,
1595
 
                                                (data->job.options & BRASERO_IO_INFO_FOLLOW_SYMLINK)?G_FILE_QUERY_INFO_NONE:G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,        /* follow symlinks by default*/
1596
 
                                                cancel,
1597
 
                                                NULL);
1598
 
        if (!enumerator) {
1599
 
                g_object_unref (file);
1600
 
                return;
1601
 
        }
1602
 
 
1603
 
        while ((info = g_file_enumerator_next_file (enumerator, cancel, &error)) || error) {
1604
 
                GFile *child;
1605
 
 
1606
 
                if (g_cancellable_is_cancelled (cancel)) {
1607
 
                        g_object_unref (info);
1608
 
                        break;
1609
 
                }
1610
 
 
1611
 
                data->files_num ++;
1612
 
 
1613
 
                if (error) {
1614
 
                        g_error_free (error);
1615
 
                        error = NULL;
1616
 
 
1617
 
                        data->files_invalid ++;
1618
 
                        continue;
1619
 
                }
1620
 
 
1621
 
                child = g_file_get_child (file, g_file_info_get_name (info));
1622
 
 
1623
 
                if (g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR
1624
 
                ||  g_file_info_get_file_type (info) == G_FILE_TYPE_SYMBOLIC_LINK) {
1625
 
                        brasero_io_get_file_count_process_file (self, cancel, data, child, info);
1626
 
                        g_object_unref (child);
1627
 
                }
1628
 
                else if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
1629
 
                        data->children = g_slist_prepend (data->children, child);
1630
 
                else
1631
 
                        g_object_unref (child);
1632
 
 
1633
 
                g_object_unref (info);
1634
 
        }
1635
 
 
1636
 
        g_file_enumerator_close (enumerator, cancel, NULL);
1637
 
        g_object_unref (enumerator);
1638
 
        g_object_unref (file);
1639
 
}
1640
 
 
1641
 
static gboolean
1642
 
brasero_io_get_file_count_start (BraseroIO *self,
1643
 
                                 GCancellable *cancel,
1644
 
                                 BraseroIOCountData *data,
1645
 
                                 const gchar *uri)
1646
 
{
1647
 
        GFile *file;
1648
 
        GFileInfo *info;
1649
 
        gchar attributes [512] = {G_FILE_ATTRIBUTE_STANDARD_NAME "," 
1650
 
                                  G_FILE_ATTRIBUTE_STANDARD_SIZE "," 
1651
 
                                  G_FILE_ATTRIBUTE_STANDARD_TYPE };
1652
 
 
1653
 
        if ((data->job.options & BRASERO_IO_INFO_METADATA)
1654
 
        &&  (data->job.options & BRASERO_IO_INFO_RECURSIVE))
1655
 
                strcat (attributes, "," G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
1656
 
 
1657
 
        file = g_file_new_for_uri (uri);
1658
 
        info = g_file_query_info (file,
1659
 
                                  attributes,
1660
 
                                  (data->job.options & BRASERO_IO_INFO_FOLLOW_SYMLINK)?G_FILE_QUERY_INFO_NONE:G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,      /* follow symlinks by default*/
1661
 
                                  cancel,
1662
 
                                  NULL);
1663
 
 
1664
 
        data->files_num ++;
1665
 
        if (!info) {
1666
 
                g_object_unref (file);
1667
 
                data->files_invalid ++;
1668
 
                return FALSE;
1669
 
        }
1670
 
 
1671
 
        if (g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR
1672
 
        ||  g_file_info_get_file_type (info) == G_FILE_TYPE_SYMBOLIC_LINK) {
1673
 
                brasero_io_get_file_count_process_file (self, cancel, data, file, info);
1674
 
                g_object_unref (file);
1675
 
        }
1676
 
        else if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
1677
 
                if (data->job.options & BRASERO_IO_INFO_RECURSIVE)
1678
 
                        data->children = g_slist_prepend (data->children, file);
1679
 
                else
1680
 
                        g_object_unref (file);
1681
 
        }
1682
 
        else
1683
 
                g_object_unref (file);
1684
 
 
1685
 
        g_object_unref (info);
1686
 
        return TRUE;
1687
 
}
1688
 
 
1689
 
static void
1690
 
brasero_io_get_file_count_progress_cb (BraseroIOJob *job,
1691
 
                                       BraseroIOJobProgress *progress)
1692
 
{
1693
 
        BraseroIOCountData *data = (BraseroIOCountData *) job;
1694
 
 
1695
 
        progress->read_b = data->total_b;
1696
 
        progress->total_b = data->total_b;
1697
 
        progress->files_num = data->files_num;
1698
 
        progress->files_invalid = data->files_invalid;
1699
 
}
1700
 
 
1701
 
static BraseroAsyncTaskResult
1702
 
brasero_io_get_file_count_thread (BraseroAsyncTaskManager *manager,
1703
 
                                  GCancellable *cancel,
1704
 
                                  gpointer callback_data)
1705
 
{
1706
 
        BraseroIOCountData *data = callback_data;
1707
 
        GFileInfo *info;
1708
 
        gchar *uri;
1709
 
 
1710
 
        if (data->children) {
1711
 
                brasero_io_get_file_count_process_directory (BRASERO_IO (manager), cancel, data);
1712
 
                return BRASERO_ASYNC_TASK_RESCHEDULE;
1713
 
        }
1714
 
        else if (!data->uris) {
1715
 
                info = g_file_info_new ();
1716
 
 
1717
 
                /* set GFileInfo information */
1718
 
                g_file_info_set_attribute_uint32 (info, BRASERO_IO_COUNT_INVALID, data->files_invalid);
1719
 
                g_file_info_set_attribute_uint64 (info, BRASERO_IO_COUNT_SIZE, data->total_b);
1720
 
                g_file_info_set_attribute_uint32 (info, BRASERO_IO_COUNT_NUM, data->files_num);
1721
 
 
1722
 
                brasero_io_return_result (BRASERO_IO (manager),
1723
 
                                          data->job.base,
1724
 
                                          NULL,
1725
 
                                          info,
1726
 
                                          NULL,
1727
 
                                          data->job.callback_data);
1728
 
 
1729
 
                return BRASERO_ASYNC_TASK_FINISHED;
1730
 
        }
1731
 
 
1732
 
        if (!data->progress_started) {
1733
 
                brasero_io_job_progress_report_start (BRASERO_IO (manager),
1734
 
                                                      &data->job,
1735
 
                                                      brasero_io_get_file_count_progress_cb);
1736
 
                data->progress_started = 1;
1737
 
        }
1738
 
 
1739
 
        uri = data->uris->data;
1740
 
        data->uris = g_slist_remove (data->uris, uri);
1741
 
 
1742
 
        brasero_io_get_file_count_start (BRASERO_IO (manager), cancel, data, uri);
1743
 
        g_free (uri);
1744
 
 
1745
 
        return BRASERO_ASYNC_TASK_RESCHEDULE;
1746
 
}
1747
 
 
1748
 
static const BraseroAsyncTaskType count_type = {
1749
 
        brasero_io_get_file_count_thread,
1750
 
        brasero_io_get_file_count_destroy
1751
 
};
1752
 
 
1753
 
void
1754
 
brasero_io_get_file_count (BraseroIO *self,
1755
 
                           GSList *uris,
1756
 
                           const BraseroIOJobBase *base,
1757
 
                           BraseroIOFlags options,
1758
 
                           gpointer user_data)
1759
 
{
1760
 
        BraseroIOCountData *data;
1761
 
        BraseroIOResultCallbackData *callback_data = NULL;
1762
 
 
1763
 
        if (user_data) {
1764
 
                callback_data = g_new0 (BraseroIOResultCallbackData, 1);
1765
 
                callback_data->callback_data = user_data;
1766
 
        }
1767
 
 
1768
 
        data = g_new0 (BraseroIOCountData, 1);
1769
 
 
1770
 
        for (; uris; uris = uris->next)
1771
 
                data->uris = g_slist_prepend (data->uris, g_strdup (uris->data));
1772
 
 
1773
 
        brasero_io_set_job (BRASERO_IO_JOB (data),
1774
 
                            base,
1775
 
                            NULL,
1776
 
                            options,
1777
 
                            callback_data);
1778
 
 
1779
 
        brasero_io_push_job (self, BRASERO_IO_JOB (data), &count_type);
1780
 
}
1781
 
 
1782
 
/**
1783
 
 * Used to explore directories
1784
 
 */
1785
 
 
1786
 
struct _BraseroIOContentsData {
1787
 
        BraseroIOJob job;
1788
 
        GSList *children;
1789
 
};
1790
 
typedef struct _BraseroIOContentsData BraseroIOContentsData;
1791
 
 
1792
 
static void
1793
 
brasero_io_load_directory_destroy (BraseroAsyncTaskManager *manager,
1794
 
                                   gboolean cancelled,
1795
 
                                   gpointer callback_data)
1796
 
{
1797
 
        BraseroIOContentsData *data = callback_data;
1798
 
 
1799
 
        g_slist_foreach (data->children, (GFunc) g_object_unref, NULL);
1800
 
        g_slist_free (data->children);
1801
 
 
1802
 
        brasero_io_job_free (BRASERO_IO (manager), cancelled, BRASERO_IO_JOB (data));
1803
 
}
1804
 
 
1805
 
#ifdef BUILD_PLAYLIST
1806
 
 
1807
 
static gboolean
1808
 
brasero_io_load_directory_playlist (BraseroIO *self,
1809
 
                                    GCancellable *cancel,
1810
 
                                    BraseroIOContentsData *data,
1811
 
                                    const gchar *uri,
1812
 
                                    const gchar *attributes)
1813
 
{
1814
 
        BraseroIOPlaylist playlist = {NULL, };
1815
 
        GSList *iter;
1816
 
 
1817
 
        if (!brasero_io_parse_playlist_get_uris (uri, &playlist, NULL))
1818
 
                return FALSE;
1819
 
 
1820
 
        for (iter = playlist.uris; iter; iter = iter->next) {
1821
 
                GFile *file;
1822
 
                gboolean result;
1823
 
                GFileInfo *info;
1824
 
                gchar *child_uri;
1825
 
                BraseroMetadataInfo metadata = { NULL, };
1826
 
 
1827
 
                child_uri = iter->data;
1828
 
 
1829
 
                file = g_file_new_for_uri (child_uri);
1830
 
                info = g_file_query_info (file,
1831
 
                                          attributes,
1832
 
                                          G_FILE_QUERY_INFO_NONE,               /* follow symlinks */
1833
 
                                          cancel,
1834
 
                                          NULL);
1835
 
                if (!info) {
1836
 
                        g_object_unref (file);
1837
 
                        continue;
1838
 
                }
1839
 
 
1840
 
                result = brasero_io_get_metadata_info (self,
1841
 
                                                       cancel,
1842
 
                                                       child_uri,
1843
 
                                                       info,
1844
 
                                                       ((data->job.options & BRASERO_IO_INFO_METADATA_MISSING_CODEC) ? BRASERO_METADATA_FLAG_MISSING : 0) |
1845
 
                                                       ((data->job.options & BRASERO_IO_INFO_METADATA_THUMBNAIL) ? BRASERO_METADATA_FLAG_THUMBNAIL : 0),
1846
 
                                                       &metadata);
1847
 
 
1848
 
                if (result) {
1849
 
                        brasero_io_set_metadata_attributes (info, &metadata);
1850
 
                        brasero_io_return_result (self,
1851
 
                                                  data->job.base,
1852
 
                                                  child_uri,
1853
 
                                                  info,
1854
 
                                                  NULL,
1855
 
                                                  data->job.callback_data);
1856
 
                }
1857
 
                else
1858
 
                        g_object_unref (info);
1859
 
 
1860
 
                brasero_metadata_info_clear (&metadata);
1861
 
 
1862
 
                g_object_unref (file);
1863
 
        }
1864
 
 
1865
 
        brasero_io_playlist_clear (&playlist);
1866
 
        return TRUE;
1867
 
}
1868
 
 
1869
 
#endif
1870
 
 
1871
 
static BraseroAsyncTaskResult
1872
 
brasero_io_load_directory_thread (BraseroAsyncTaskManager *manager,
1873
 
                                  GCancellable *cancel,
1874
 
                                  gpointer callback_data)
1875
 
{
1876
 
        gchar attributes [512] = {G_FILE_ATTRIBUTE_STANDARD_NAME "," 
1877
 
                                  G_FILE_ATTRIBUTE_STANDARD_SIZE ","
1878
 
                                  G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK ","
1879
 
                                  G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET ","
1880
 
                                  G_FILE_ATTRIBUTE_STANDARD_TYPE };
1881
 
        BraseroIOContentsData *data = callback_data;
1882
 
        GFileEnumerator *enumerator;
1883
 
        GError *error = NULL;
1884
 
        GFileInfo *info;
1885
 
        GFile *file;
1886
 
 
1887
 
        if (data->job.options & BRASERO_IO_INFO_PERM)
1888
 
                strcat (attributes, "," G_FILE_ATTRIBUTE_ACCESS_CAN_READ);
1889
 
 
1890
 
        if (data->job.options & BRASERO_IO_INFO_MIME)
1891
 
                strcat (attributes, "," G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
1892
 
        else if ((data->job.options & BRASERO_IO_INFO_METADATA)
1893
 
             &&  (data->job.options & BRASERO_IO_INFO_RECURSIVE))
1894
 
                strcat (attributes, "," G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
1895
 
 
1896
 
        if (data->job.options & BRASERO_IO_INFO_ICON)
1897
 
                strcat (attributes, "," G_FILE_ATTRIBUTE_STANDARD_ICON);
1898
 
 
1899
 
        if (data->children) {
1900
 
                file = data->children->data;
1901
 
                data->children = g_slist_remove (data->children, file);
1902
 
        }
1903
 
        else
1904
 
                file = g_file_new_for_uri (data->job.uri);
1905
 
 
1906
 
        enumerator = g_file_enumerate_children (file,
1907
 
                                                attributes,
1908
 
                                                (data->job.options & BRASERO_IO_INFO_FOLLOW_SYMLINK)?G_FILE_QUERY_INFO_NONE:G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,        /* follow symlinks by default*/
1909
 
                                                cancel,
1910
 
                                                &error);
1911
 
 
1912
 
        if (!enumerator) {
1913
 
                gchar *directory_uri;
1914
 
 
1915
 
                directory_uri = g_file_get_uri (file);
1916
 
                brasero_io_return_result (BRASERO_IO (manager),
1917
 
                                          data->job.base,
1918
 
                                          directory_uri,
1919
 
                                          NULL,
1920
 
                                          error,
1921
 
                                          data->job.callback_data);
1922
 
                g_free (directory_uri);
1923
 
                g_object_unref (file);
1924
 
 
1925
 
                if (data->children)
1926
 
                        return BRASERO_ASYNC_TASK_RESCHEDULE;
1927
 
 
1928
 
                return BRASERO_ASYNC_TASK_FINISHED;
1929
 
        }
1930
 
 
1931
 
        while ((info = g_file_enumerator_next_file (enumerator, cancel, NULL))) {
1932
 
                const gchar *name;
1933
 
                gchar *child_uri;
1934
 
                GFile *child;
1935
 
 
1936
 
                name = g_file_info_get_name (info);
1937
 
                if (g_cancellable_is_cancelled (cancel)) {
1938
 
                        g_object_unref (info);
1939
 
                        break;
1940
 
                }
1941
 
 
1942
 
                if (name [0] == '.'
1943
 
                && (name [1] == '\0'
1944
 
                || (name [1] == '.' && name [2] == '\0'))) {
1945
 
                        g_object_unref (info);
1946
 
                        continue;
1947
 
                }
1948
 
 
1949
 
                child = g_file_get_child (file, name);
1950
 
                if (!child)
1951
 
                        continue;
1952
 
 
1953
 
                child_uri = g_file_get_uri (child);
1954
 
 
1955
 
                /* special case for symlinks */
1956
 
                if (g_file_info_get_is_symlink (info)) {
1957
 
                        if (!brasero_io_check_symlink_target (file, info)) {
1958
 
                                error = g_error_new (BRASERO_ERROR,
1959
 
                                                     BRASERO_ERROR_SYMLINK_LOOP,
1960
 
                                                     _("Recursive symbolic link"));
1961
 
 
1962
 
                                /* since we checked for the existence of the file
1963
 
                                 * an error means a looping symbolic link */
1964
 
                                brasero_io_return_result (BRASERO_IO (manager),
1965
 
                                                          data->job.base,
1966
 
                                                          child_uri,
1967
 
                                                          NULL,
1968
 
                                                          error,
1969
 
                                                          data->job.callback_data);
1970
 
 
1971
 
                                g_free (child_uri);
1972
 
                                g_object_unref (info);
1973
 
                                g_object_unref (child);
1974
 
                                continue;
1975
 
                        }
1976
 
                }
1977
 
 
1978
 
                if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
1979
 
                        brasero_io_return_result (BRASERO_IO (manager),
1980
 
                                                  data->job.base,
1981
 
                                                  child_uri,
1982
 
                                                  info,
1983
 
                                                  NULL,
1984
 
                                                  data->job.callback_data);
1985
 
 
1986
 
                        if (data->job.options & BRASERO_IO_INFO_RECURSIVE)
1987
 
                                data->children = g_slist_prepend (data->children, child);
1988
 
                        else
1989
 
                                g_object_unref (child);
1990
 
 
1991
 
                        g_free (child_uri);
1992
 
                        continue;
1993
 
                }
1994
 
 
1995
 
                if (data->job.options & BRASERO_IO_INFO_METADATA) {
1996
 
                        BraseroMetadataInfo metadata = {NULL, };
1997
 
                        gboolean result;
1998
 
 
1999
 
                        /* add metadata information to this file */
2000
 
                        result = brasero_io_get_metadata_info (BRASERO_IO (manager),
2001
 
                                                               cancel,
2002
 
                                                               child_uri,
2003
 
                                                               info,
2004
 
                                                               ((data->job.options & BRASERO_IO_INFO_METADATA_MISSING_CODEC) ? BRASERO_METADATA_FLAG_MISSING : 0) |
2005
 
                                                               ((data->job.options & BRASERO_IO_INFO_METADATA_THUMBNAIL) ? BRASERO_METADATA_FLAG_THUMBNAIL : 0),
2006
 
                                                               &metadata);
2007
 
 
2008
 
                        if (result)
2009
 
                                brasero_io_set_metadata_attributes (info, &metadata);
2010
 
 
2011
 
#ifdef BUILD_PLAYLIST
2012
 
 
2013
 
                        else if (data->job.options & BRASERO_IO_INFO_RECURSIVE) {
2014
 
                                const gchar *mime;
2015
 
 
2016
 
                                mime = g_file_info_get_content_type (info);
2017
 
                                if (mime
2018
 
                                && (!strcmp (mime, "audio/x-scpls")
2019
 
                                ||  !strcmp (mime, "audio/x-ms-asx")
2020
 
                                ||  !strcmp (mime, "audio/x-mp3-playlist")
2021
 
                                ||  !strcmp (mime, "audio/x-mpegurl")))
2022
 
                                        brasero_io_load_directory_playlist (BRASERO_IO (manager),
2023
 
                                                                            cancel,
2024
 
                                                                            data,
2025
 
                                                                            child_uri,
2026
 
                                                                            attributes);
2027
 
                        }
2028
 
 
2029
 
#endif
2030
 
 
2031
 
                        brasero_metadata_info_clear (&metadata);
2032
 
                }
2033
 
 
2034
 
                brasero_io_return_result (BRASERO_IO (manager),
2035
 
                                          data->job.base,
2036
 
                                          child_uri,
2037
 
                                          info,
2038
 
                                          NULL,
2039
 
                                          data->job.callback_data);
2040
 
                g_free (child_uri);
2041
 
                g_object_unref (child);
2042
 
        }
2043
 
 
2044
 
        g_file_enumerator_close (enumerator, NULL, NULL);
2045
 
        g_object_unref (enumerator);
2046
 
        g_object_unref (file);
2047
 
 
2048
 
        if (data->children)
2049
 
                return BRASERO_ASYNC_TASK_RESCHEDULE;
2050
 
 
2051
 
        return BRASERO_ASYNC_TASK_FINISHED;
2052
 
}
2053
 
 
2054
 
static const BraseroAsyncTaskType contents_type = {
2055
 
        brasero_io_load_directory_thread,
2056
 
        brasero_io_load_directory_destroy
2057
 
};
2058
 
 
2059
 
void
2060
 
brasero_io_load_directory (BraseroIO *self,
2061
 
                           const gchar *uri,
2062
 
                           const BraseroIOJobBase *base,
2063
 
                           BraseroIOFlags options,
2064
 
                           gpointer user_data)
2065
 
{
2066
 
        BraseroIOContentsData *data;
2067
 
        BraseroIOResultCallbackData *callback_data = NULL;
2068
 
 
2069
 
        if (user_data) {
2070
 
                callback_data = g_new0 (BraseroIOResultCallbackData, 1);
2071
 
                callback_data->callback_data = user_data;
2072
 
        }
2073
 
 
2074
 
        data = g_new0 (BraseroIOContentsData, 1);
2075
 
        brasero_io_set_job (BRASERO_IO_JOB (data),
2076
 
                            base,
2077
 
                            uri,
2078
 
                            options,
2079
 
                            callback_data);
2080
 
 
2081
 
        brasero_io_push_job (self, BRASERO_IO_JOB (data), &contents_type);
2082
 
}
2083
 
 
2084
 
/**
2085
 
 * to evaluate the contents of a medium or image async
2086
 
 */
2087
 
struct _BraseroIOImageContentsData {
2088
 
        BraseroIOJob job;
2089
 
        gchar *dev_image;
2090
 
 
2091
 
        gint64 session_block;
2092
 
        gint64 block;
2093
 
};
2094
 
typedef struct _BraseroIOImageContentsData BraseroIOImageContentsData;
2095
 
 
2096
 
static void
2097
 
brasero_io_image_directory_contents_destroy (BraseroAsyncTaskManager *manager,
2098
 
                                             gboolean cancelled,
2099
 
                                             gpointer callback_data)
2100
 
{
2101
 
        BraseroIOImageContentsData *data = callback_data;
2102
 
 
2103
 
        g_free (data->dev_image);
2104
 
        brasero_io_job_free (BRASERO_IO (manager), cancelled, BRASERO_IO_JOB (data));
2105
 
}
2106
 
 
2107
 
static BraseroAsyncTaskResult
2108
 
brasero_io_image_directory_contents_thread (BraseroAsyncTaskManager *manager,
2109
 
                                            GCancellable *cancel,
2110
 
                                            gpointer callback_data)
2111
 
{
2112
 
        BraseroIOImageContentsData *data = callback_data;
2113
 
        BraseroDeviceHandle *handle;
2114
 
        GList *children, *iter;
2115
 
        GError *error = NULL;
2116
 
        BraseroVolSrc *vol;
2117
 
 
2118
 
        handle = brasero_device_handle_open (data->job.uri, FALSE, NULL);
2119
 
        vol = brasero_volume_source_open_device_handle (handle, &error);
2120
 
        if (!vol) {
2121
 
                brasero_device_handle_close (handle);
2122
 
                brasero_io_return_result (BRASERO_IO (manager),
2123
 
                                          data->job.base,
2124
 
                                          data->job.uri,
2125
 
                                          NULL,
2126
 
                                          error,
2127
 
                                          data->job.callback_data);
2128
 
                return BRASERO_ASYNC_TASK_FINISHED;
2129
 
        }
2130
 
 
2131
 
        children = brasero_volume_load_directory_contents (vol,
2132
 
                                                           data->session_block,
2133
 
                                                           data->block,
2134
 
                                                           &error);
2135
 
        brasero_volume_source_close (vol);
2136
 
        brasero_device_handle_close (handle);
2137
 
 
2138
 
        for (iter = children; iter; iter = iter->next) {
2139
 
                BraseroVolFile *file;
2140
 
                GFileInfo *info;
2141
 
 
2142
 
                file = iter->data;
2143
 
 
2144
 
                info = g_file_info_new ();
2145
 
                g_file_info_set_file_type (info, file->isdir? G_FILE_TYPE_DIRECTORY:G_FILE_TYPE_REGULAR);
2146
 
                g_file_info_set_name (info, BRASERO_VOLUME_FILE_NAME (file));
2147
 
 
2148
 
                if (file->isdir)
2149
 
                        g_file_info_set_attribute_int64 (info,
2150
 
                                                         BRASERO_IO_DIR_CONTENTS_ADDR,
2151
 
                                                         file->specific.dir.address);
2152
 
                else
2153
 
                        g_file_info_set_size (info, BRASERO_VOLUME_FILE_SIZE (file));
2154
 
 
2155
 
                brasero_io_return_result (BRASERO_IO (manager),
2156
 
                                          data->job.base,
2157
 
                                          data->job.uri,
2158
 
                                          info,
2159
 
                                          NULL,
2160
 
                                          data->job.callback_data);
2161
 
        }
2162
 
 
2163
 
        g_list_foreach (children, (GFunc) brasero_volume_file_free, NULL);
2164
 
        g_list_free (children);
2165
 
 
2166
 
        return BRASERO_ASYNC_TASK_FINISHED;
2167
 
}
2168
 
 
2169
 
static const BraseroAsyncTaskType image_contents_type = {
2170
 
        brasero_io_image_directory_contents_thread,
2171
 
        brasero_io_image_directory_contents_destroy
2172
 
};
2173
 
 
2174
 
void
2175
 
brasero_io_load_image_directory (BraseroIO *self,
2176
 
                                 const gchar *dev_image,
2177
 
                                 gint64 session_block,
2178
 
                                 gint64 block,
2179
 
                                 const BraseroIOJobBase *base,
2180
 
                                 BraseroIOFlags options,
2181
 
                                 gpointer user_data)
2182
 
{
2183
 
        BraseroIOImageContentsData *data;
2184
 
        BraseroIOResultCallbackData *callback_data = NULL;
2185
 
 
2186
 
        if (user_data) {
2187
 
                callback_data = g_new0 (BraseroIOResultCallbackData, 1);
2188
 
                callback_data->callback_data = user_data;
2189
 
        }
2190
 
 
2191
 
        data = g_new0 (BraseroIOImageContentsData, 1);
2192
 
        data->block = block;
2193
 
        data->session_block = session_block;
2194
 
 
2195
 
        brasero_io_set_job (BRASERO_IO_JOB (data),
2196
 
                            base,
2197
 
                            dev_image,
2198
 
                            options,
2199
 
                            callback_data);
2200
 
 
2201
 
        brasero_io_push_job (self,
2202
 
                             BRASERO_IO_JOB (data),
2203
 
                             &image_contents_type);
2204
 
 
2205
 
}
2206
 
                                         
2207
 
/**
2208
 
 * That's for file transfer
2209
 
 */
2210
 
 
2211
 
struct _BraseroIOXferPair {
2212
 
        GFile *src;
2213
 
        gchar *dest;
2214
 
};
2215
 
typedef struct _BraseroIOXferPair BraseroIOXferPair;
2216
 
 
2217
 
struct _BraseroIOXferData {
2218
 
        BraseroIOCountData count;
2219
 
        BraseroIOJobProgress *progress;
2220
 
 
2221
 
        gchar *dest_path;
2222
 
 
2223
 
        guint64 current_read_b;
2224
 
        guint64 current_total_b;
2225
 
        guint64 read_b;
2226
 
 
2227
 
        GFile *current;
2228
 
        GMutex *current_lock;
2229
 
 
2230
 
        GFileInfo *info;
2231
 
        GSList *pairs;
2232
 
};
2233
 
typedef struct _BraseroIOXferData BraseroIOXferData;
2234
 
 
2235
 
static void
2236
 
brasero_io_xfer_pair_free (BraseroIOXferPair *pair)
2237
 
{
2238
 
        g_object_unref (pair->src);
2239
 
        g_free (pair->dest);
2240
 
 
2241
 
        g_free (pair);
2242
 
}
2243
 
 
2244
 
static void
2245
 
brasero_io_xfer_destroy (BraseroAsyncTaskManager *manager,
2246
 
                         gboolean cancelled,
2247
 
                         gpointer callback_data)
2248
 
{
2249
 
        BraseroIOXferData *data = callback_data;
2250
 
 
2251
 
        g_slist_foreach (data->pairs, (GFunc) brasero_io_xfer_pair_free, NULL);
2252
 
        g_slist_free (data->pairs);
2253
 
        g_free (data->dest_path);
2254
 
 
2255
 
        g_mutex_free (data->current_lock);
2256
 
 
2257
 
        /* no need to stop progress report as the following function will do it */
2258
 
        brasero_io_get_file_count_destroy (manager, cancelled, callback_data);
2259
 
}
2260
 
 
2261
 
static void
2262
 
brasero_io_xfer_progress_cb (goffset current_num_bytes,
2263
 
                             goffset total_num_bytes,
2264
 
                             gpointer callback_data)
2265
 
{
2266
 
        BraseroIOXferData *data = callback_data;
2267
 
 
2268
 
        data->current_read_b = current_num_bytes;
2269
 
        data->current_total_b = total_num_bytes;
2270
 
}
2271
 
 
2272
 
static BraseroAsyncTaskResult
2273
 
brasero_io_xfer_file_thread (BraseroIOXferData *data,
2274
 
                             GCancellable *cancel,
2275
 
                             GFile *src,
2276
 
                             const gchar *dest_path,
2277
 
                             GError **error)
2278
 
{
2279
 
        GFile *dest;
2280
 
        gboolean result;
2281
 
 
2282
 
        g_mutex_lock (data->current_lock);
2283
 
        data->current = src;
2284
 
        g_mutex_unlock (data->current_lock);
2285
 
 
2286
 
        dest = g_file_new_for_path (dest_path);
2287
 
        result = g_file_copy (src,
2288
 
                              dest,
2289
 
                              G_FILE_COPY_ALL_METADATA,
2290
 
                              cancel,
2291
 
                              brasero_io_xfer_progress_cb,
2292
 
                              data,
2293
 
                              error);
2294
 
        g_object_unref (dest);
2295
 
 
2296
 
        data->read_b += data->current_total_b;
2297
 
        data->current_read_b = 0;
2298
 
 
2299
 
        g_mutex_lock (data->current_lock);
2300
 
        data->current = NULL;
2301
 
        g_mutex_unlock (data->current_lock);
2302
 
 
2303
 
        return result;
2304
 
}
2305
 
 
2306
 
static gboolean
2307
 
brasero_io_xfer_recursive_thread (BraseroIOXferData *data,
2308
 
                                  GCancellable *cancel,
2309
 
                                  GFile *src,
2310
 
                                  const gchar *dest_path,
2311
 
                                  GError **error)
2312
 
{
2313
 
        GFileInfo *info;
2314
 
        GFileEnumerator *enumerator;
2315
 
 
2316
 
        enumerator = g_file_enumerate_children (src,
2317
 
                                                G_FILE_ATTRIBUTE_STANDARD_TYPE ","
2318
 
                                                G_FILE_ATTRIBUTE_STANDARD_NAME,
2319
 
                                                G_FILE_QUERY_INFO_NONE, /* follow symlinks by default */
2320
 
                                                cancel,
2321
 
                                                error);
2322
 
        if (!enumerator)
2323
 
                return FALSE;
2324
 
 
2325
 
        while ((info = g_file_enumerator_next_file (enumerator, cancel, NULL))) {
2326
 
                gboolean result;
2327
 
                GFile *src_child;
2328
 
                gchar *dest_child;
2329
 
 
2330
 
                if (g_cancellable_is_cancelled (cancel)) {
2331
 
                        result = FALSE;
2332
 
                        break;
2333
 
                }
2334
 
 
2335
 
                if (!info)
2336
 
                        continue;
2337
 
 
2338
 
                src_child = g_file_get_child (src, g_file_info_get_name (info));
2339
 
                dest_child = g_build_path (G_DIR_SEPARATOR_S,
2340
 
                                           dest_path,
2341
 
                                           g_file_info_get_name (info),
2342
 
                                           NULL);
2343
 
 
2344
 
                if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
2345
 
                        /* Create a directory with the same name and keep it for
2346
 
                         * later.
2347
 
                         * Note: if that fails don't bother. */
2348
 
                        if (!g_mkdir (dest_child, 700)) {
2349
 
                                BraseroIOXferPair *new_pair;
2350
 
 
2351
 
                                new_pair = g_new0 (BraseroIOXferPair, 1);
2352
 
                                new_pair->src = src_child;
2353
 
                                new_pair->dest = dest_child;
2354
 
                                data->pairs = g_slist_prepend (data->pairs, new_pair);
2355
 
                        }
2356
 
                }
2357
 
                else {
2358
 
                        result = brasero_io_xfer_file_thread (data,
2359
 
                                                              cancel,
2360
 
                                                              src_child,
2361
 
                                                              dest_child,
2362
 
                                                              NULL);
2363
 
 
2364
 
                        g_free (dest_child);
2365
 
                        g_object_unref (src_child);
2366
 
                }
2367
 
 
2368
 
                g_object_unref (info);
2369
 
        }
2370
 
 
2371
 
        g_file_enumerator_close (enumerator, cancel, NULL);
2372
 
        g_object_unref (enumerator);
2373
 
 
2374
 
        return TRUE;
2375
 
}
2376
 
 
2377
 
static gboolean
2378
 
brasero_io_xfer_start (BraseroIO *self,
2379
 
                       GCancellable *cancel,
2380
 
                       BraseroIOXferData *data,
2381
 
                       GError **error)
2382
 
{
2383
 
        GFile *file;
2384
 
        gboolean result;
2385
 
 
2386
 
        /* retrieve some information about the file we have to copy */
2387
 
        file = g_file_new_for_uri (data->count.job.uri);
2388
 
        data->info = g_file_query_info (file,
2389
 
                                        G_FILE_ATTRIBUTE_STANDARD_TYPE","
2390
 
                                        G_FILE_ATTRIBUTE_STANDARD_SIZE,
2391
 
                                        G_FILE_QUERY_INFO_NONE, /* follow symlinks by default*/
2392
 
                                        cancel,
2393
 
                                        error);
2394
 
        if (!data->info || error) {
2395
 
                g_object_unref (file);
2396
 
                return FALSE;
2397
 
        }
2398
 
 
2399
 
        g_file_info_set_attribute_string (data->info,
2400
 
                                          BRASERO_IO_XFER_DESTINATION,
2401
 
                                          data->dest_path);
2402
 
 
2403
 
        /* see if we should explore it beforehand to report progress */
2404
 
        if (data->count.job.base->progress) {
2405
 
                data->count.files_num = 1;
2406
 
                if (g_file_info_get_file_type (data->info) == G_FILE_TYPE_REGULAR
2407
 
                ||  g_file_info_get_file_type (data->info) == G_FILE_TYPE_SYMBOLIC_LINK)
2408
 
                        brasero_io_get_file_count_process_file (self, cancel, &data->count, file, data->info);
2409
 
                else if (g_file_info_get_file_type (data->info) == G_FILE_TYPE_DIRECTORY)
2410
 
                        brasero_io_get_file_count_process_directory (self, cancel, &data->count);
2411
 
        }
2412
 
 
2413
 
        /* start the downloading */
2414
 
        if (g_file_info_get_file_type (data->info) == G_FILE_TYPE_DIRECTORY) {
2415
 
                if (g_mkdir_with_parents (data->dest_path, 700)) {
2416
 
                        int errsv = errno;
2417
 
 
2418
 
                        g_object_unref (file);
2419
 
 
2420
 
                        g_set_error (error,
2421
 
                                     BRASERO_ERROR,
2422
 
                                     BRASERO_ERROR_GENERAL,
2423
 
                                     _("A directory could not be created (%s)"),
2424
 
                                     g_strerror (errsv));
2425
 
                        return FALSE;
2426
 
                }
2427
 
 
2428
 
                if (data->count.job.options & BRASERO_IO_INFO_RECURSIVE)
2429
 
                        brasero_io_xfer_recursive_thread (data,
2430
 
                                                          cancel,
2431
 
                                                          file,
2432
 
                                                          data->dest_path,
2433
 
                                                          NULL);
2434
 
        }
2435
 
        else if (g_file_info_get_file_type (data->info) == G_FILE_TYPE_REGULAR
2436
 
             ||  g_file_info_get_file_type (data->info) == G_FILE_TYPE_SYMBOLIC_LINK)
2437
 
                result = brasero_io_xfer_file_thread (data,
2438
 
                                                      cancel,
2439
 
                                                      file,
2440
 
                                                      data->dest_path,
2441
 
                                                      error);
2442
 
 
2443
 
        g_object_unref (file);
2444
 
        return result;
2445
 
}
2446
 
 
2447
 
static void
2448
 
brasero_io_xfer_get_progress_cb (BraseroIOJob *job,
2449
 
                                 BraseroIOJobProgress *progress)
2450
 
{
2451
 
        BraseroIOXferData *data = (BraseroIOXferData *) job;
2452
 
 
2453
 
        if (progress->current)
2454
 
                g_free (progress->current);
2455
 
 
2456
 
        g_mutex_lock (data->current_lock);
2457
 
        progress->current = g_file_get_basename (data->current);
2458
 
        g_mutex_unlock (data->current_lock);
2459
 
 
2460
 
        progress->total_b = data->count.total_b;
2461
 
        progress->read_b = data->current_read_b + data->read_b;
2462
 
        progress->files_num = data->count.files_num - data->count.files_invalid;
2463
 
}
2464
 
 
2465
 
static BraseroAsyncTaskResult
2466
 
brasero_io_xfer_thread (BraseroAsyncTaskManager *manager,
2467
 
                        GCancellable *cancel,
2468
 
                        gpointer callback_data)
2469
 
{
2470
 
        BraseroIOXferPair *pair;
2471
 
        BraseroIOXferData *data = callback_data;
2472
 
 
2473
 
        if (!data->info) {
2474
 
                GError *error = NULL;
2475
 
 
2476
 
                brasero_io_job_progress_report_start (BRASERO_IO (manager),
2477
 
                                                      callback_data,
2478
 
                                                      brasero_io_xfer_get_progress_cb);
2479
 
 
2480
 
                if (!brasero_io_xfer_start (BRASERO_IO (manager), cancel, data, &error)) {
2481
 
                        brasero_io_return_result (BRASERO_IO (manager),
2482
 
                                                  data->count.job.base,
2483
 
                                                  data->count.job.uri,
2484
 
                                                  NULL,
2485
 
                                                  error,
2486
 
                                                  data->count.job.callback_data);
2487
 
                        return BRASERO_ASYNC_TASK_FINISHED;
2488
 
                }
2489
 
 
2490
 
                if (data->pairs)
2491
 
                        return BRASERO_ASYNC_TASK_RESCHEDULE;
2492
 
 
2493
 
                brasero_io_return_result (BRASERO_IO (manager),
2494
 
                                          data->count.job.base,
2495
 
                                          data->count.job.uri,
2496
 
                                          data->info,
2497
 
                                          NULL,
2498
 
                                          data->count.job.callback_data);
2499
 
                data->info = NULL;
2500
 
                return BRASERO_ASYNC_TASK_FINISHED;
2501
 
        }
2502
 
 
2503
 
        /* If there is a progress callback, retrieve the size of all the data. */
2504
 
        if (data->count.children) {
2505
 
                brasero_io_get_file_count_process_directory (BRASERO_IO (manager), cancel, &data->count);
2506
 
                return BRASERO_ASYNC_TASK_RESCHEDULE;
2507
 
        }
2508
 
 
2509
 
        pair = data->pairs->data;
2510
 
        data->pairs = g_slist_remove (data->pairs, pair);
2511
 
 
2512
 
        brasero_io_xfer_recursive_thread (data,
2513
 
                                          cancel,
2514
 
                                          pair->src,
2515
 
                                          pair->dest,
2516
 
                                          NULL);
2517
 
 
2518
 
        brasero_io_xfer_pair_free (pair);
2519
 
 
2520
 
        if (data->pairs)
2521
 
                return BRASERO_ASYNC_TASK_RESCHEDULE;
2522
 
 
2523
 
        brasero_io_return_result (BRASERO_IO (manager),
2524
 
                                  data->count.job.base,
2525
 
                                  data->count.job.uri,
2526
 
                                  data->info,
2527
 
                                  NULL,
2528
 
                                  data->count.job.callback_data);
2529
 
        data->info = NULL;
2530
 
 
2531
 
        return BRASERO_ASYNC_TASK_FINISHED;
2532
 
}
2533
 
 
2534
 
static const BraseroAsyncTaskType xfer_type = {
2535
 
        brasero_io_xfer_thread,
2536
 
        brasero_io_xfer_destroy
2537
 
};
2538
 
 
2539
 
void
2540
 
brasero_io_xfer (BraseroIO *self,
2541
 
                 const gchar *uri,
2542
 
                 const gchar *dest_path,
2543
 
                 const BraseroIOJobBase *base,
2544
 
                 BraseroIOFlags options,
2545
 
                 gpointer user_data)
2546
 
{
2547
 
        BraseroIOXferData *data;
2548
 
        BraseroIOResultCallbackData *callback_data = NULL;
2549
 
 
2550
 
        if (user_data) {
2551
 
                callback_data = g_new0 (BraseroIOResultCallbackData, 1);
2552
 
                callback_data->callback_data = user_data;
2553
 
        }
2554
 
 
2555
 
        data = g_new0 (BraseroIOXferData, 1);
2556
 
        data->dest_path = g_strdup (dest_path);
2557
 
        data->current_lock = g_mutex_new ();
2558
 
 
2559
 
        brasero_io_set_job (BRASERO_IO_JOB (data),
2560
 
                            base,
2561
 
                            uri,
2562
 
                            options,
2563
 
                            callback_data);
2564
 
 
2565
 
        brasero_io_push_job (self, BRASERO_IO_JOB (data), &xfer_type);
2566
 
}
2567
 
 
2568
 
static void
2569
 
brasero_io_cancel_result (BraseroIO *self,
2570
 
                          BraseroIOJobResult *result)
2571
 
{
2572
 
        BraseroIOResultCallbackData *data;
2573
 
        BraseroIOPrivate *priv;
2574
 
 
2575
 
        priv = BRASERO_IO_PRIVATE (self);
2576
 
 
2577
 
        g_mutex_lock (priv->lock);
2578
 
        priv->results = g_slist_remove (priv->results, result);
2579
 
        g_mutex_unlock (priv->lock);
2580
 
 
2581
 
        data = result->callback_data;
2582
 
        brasero_io_unref_result_callback_data (data,
2583
 
                                               result->base->object,
2584
 
                                               result->base->destroy,
2585
 
                                               TRUE);
2586
 
        brasero_io_job_result_free (result);
2587
 
}
2588
 
 
2589
 
static gboolean
2590
 
brasero_io_cancel_tasks_by_base_cb (BraseroAsyncTaskManager *manager,
2591
 
                                    gpointer callback_data,
2592
 
                                    gpointer user_data)
2593
 
{
2594
 
        BraseroIOJob *job = callback_data;
2595
 
        BraseroIOJobBase *base = user_data;
2596
 
 
2597
 
        if (job->base != base)
2598
 
                return FALSE;
2599
 
 
2600
 
        return TRUE;
2601
 
}
2602
 
 
2603
 
void
2604
 
brasero_io_cancel_by_base (BraseroIO *self,
2605
 
                           BraseroIOJobBase *base)
2606
 
{
2607
 
        GSList *iter;
2608
 
        GSList *next;
2609
 
        BraseroIOPrivate *priv;
2610
 
 
2611
 
        priv = BRASERO_IO_PRIVATE (self);
2612
 
 
2613
 
        brasero_async_task_manager_foreach_unprocessed_remove (BRASERO_ASYNC_TASK_MANAGER (self),
2614
 
                                                               brasero_io_cancel_tasks_by_base_cb,
2615
 
                                                               base);
2616
 
 
2617
 
        brasero_async_task_manager_foreach_active_remove (BRASERO_ASYNC_TASK_MANAGER (self),
2618
 
                                                          brasero_io_cancel_tasks_by_base_cb,
2619
 
                                                          base);
2620
 
 
2621
 
        /* do it afterwards in case some results slipped through */
2622
 
        for (iter = priv->results; iter; iter = next) {
2623
 
                BraseroIOJobResult *result;
2624
 
 
2625
 
                result = iter->data;
2626
 
                next = iter->next;
2627
 
 
2628
 
                if (result->base != base)
2629
 
                        continue;
2630
 
 
2631
 
                brasero_io_cancel_result (self, result);
2632
 
        }
2633
 
}
2634
 
 
2635
 
static gboolean
2636
 
brasero_io_cancel_tasks_by_data_cb (BraseroAsyncTaskManager *manager,
2637
 
                                    gpointer callback_data,
2638
 
                                    gpointer user_data)
2639
 
{
2640
 
        BraseroIOJob *job = callback_data;
2641
 
 
2642
 
        if (job->callback_data && job->callback_data->callback_data != user_data)
2643
 
                return FALSE;
2644
 
 
2645
 
        return TRUE;
2646
 
}
2647
 
 
2648
 
void
2649
 
brasero_io_cancel_by_data (BraseroIO *self,
2650
 
                           gpointer callback_data)
2651
 
{
2652
 
        GSList *iter;
2653
 
        GSList *next;
2654
 
        BraseroIOPrivate *priv;
2655
 
 
2656
 
        priv = BRASERO_IO_PRIVATE (self);
2657
 
 
2658
 
        brasero_async_task_manager_foreach_unprocessed_remove (BRASERO_ASYNC_TASK_MANAGER (self),
2659
 
                                                               brasero_io_cancel_tasks_by_data_cb,
2660
 
                                                               callback_data);
2661
 
 
2662
 
        brasero_async_task_manager_foreach_active_remove (BRASERO_ASYNC_TASK_MANAGER (self),
2663
 
                                                          brasero_io_cancel_tasks_by_data_cb,
2664
 
                                                          callback_data);
2665
 
 
2666
 
        /* do it afterwards in case some results slipped through */
2667
 
        for (iter = priv->results; iter; iter = next) {
2668
 
                BraseroIOJobResult *result;
2669
 
 
2670
 
                result = iter->data;
2671
 
                next = iter->next;
2672
 
 
2673
 
                if (result->callback_data != callback_data)
2674
 
                        continue;
2675
 
 
2676
 
                brasero_io_cancel_result (self, result);
2677
 
        }
2678
 
}
2679
 
 
2680
 
struct _BraseroIOJobCompareData {
2681
 
        BraseroIOCompareCallback func;
2682
 
        const BraseroIOJobBase *base;
2683
 
        gpointer user_data;
2684
 
};
2685
 
typedef struct _BraseroIOJobCompareData BraseroIOJobCompareData;
2686
 
 
2687
 
static gboolean
2688
 
brasero_io_compare_unprocessed_task (BraseroAsyncTaskManager *manager,
2689
 
                                     gpointer task,
2690
 
                                     gpointer callback_data)
2691
 
{
2692
 
        BraseroIOJob *job = task;
2693
 
        BraseroIOJobCompareData *data = callback_data;
2694
 
 
2695
 
        if (job->base == data->base)
2696
 
                return FALSE;
2697
 
 
2698
 
        if (!job->callback_data)
2699
 
                return FALSE;
2700
 
 
2701
 
        return data->func (job->callback_data->callback_data, data->user_data);
2702
 
}
2703
 
 
2704
 
void
2705
 
brasero_io_find_urgent (BraseroIO *self,
2706
 
                        const BraseroIOJobBase *base,
2707
 
                        BraseroIOCompareCallback callback,
2708
 
                        gpointer user_data)
2709
 
{
2710
 
        BraseroIOJobCompareData callback_data;
2711
 
 
2712
 
        callback_data.func = callback;
2713
 
        callback_data.base = base;
2714
 
        callback_data.user_data = user_data;
2715
 
 
2716
 
        brasero_async_task_manager_find_urgent_task (BRASERO_ASYNC_TASK_MANAGER (self),
2717
 
                                                     brasero_io_compare_unprocessed_task,
2718
 
                                                     &callback_data);
2719
 
                                                     
2720
 
}
2721
 
 
2722
 
BraseroIOJobBase *
2723
 
brasero_io_register (GObject *object,
2724
 
                     BraseroIOResultCallback callback,
2725
 
                     BraseroIODestroyCallback destroy,
2726
 
                     BraseroIOProgressCallback progress)
2727
 
{
2728
 
        BraseroIOJobBase *base;
2729
 
 
2730
 
        base = g_new0 (BraseroIOJobBase, 1);
2731
 
        base->object = object;
2732
 
        base->callback = callback;
2733
 
        base->destroy = destroy;
2734
 
        base->progress = progress;
2735
 
 
2736
 
        return base;
2737
 
}
2738
 
 
2739
 
static void
2740
 
brasero_io_init (BraseroIO *object)
2741
 
{
2742
 
        BraseroIOPrivate *priv;
2743
 
        BraseroMetadata *metadata;
2744
 
        priv = BRASERO_IO_PRIVATE (object);
2745
 
 
2746
 
        priv->lock = g_mutex_new ();
2747
 
        priv->lock_metadata = g_mutex_new ();
2748
 
 
2749
 
        priv->meta_buffer = g_queue_new ();
2750
 
 
2751
 
        /* create metadatas now since it doesn't work well when it's created in 
2752
 
         * a thread. */
2753
 
        metadata = brasero_metadata_new ();
2754
 
        priv->metadatas = g_slist_prepend (priv->metadatas, metadata);
2755
 
        metadata = brasero_metadata_new ();
2756
 
        priv->metadatas = g_slist_prepend (priv->metadatas, metadata);
2757
 
}
2758
 
 
2759
 
static gboolean
2760
 
brasero_io_free_async_queue (BraseroAsyncTaskManager *manager,
2761
 
                             gpointer callback_data,
2762
 
                             gpointer NULL_data)
2763
 
{
2764
 
        BraseroIOJob *job = callback_data;
2765
 
 
2766
 
        brasero_io_job_free (BRASERO_IO (manager), TRUE, job);
2767
 
        return TRUE;
2768
 
}
2769
 
 
2770
 
static void
2771
 
brasero_io_finalize (GObject *object)
2772
 
{
2773
 
        BraseroIOPrivate *priv;
2774
 
        GSList *iter;
2775
 
 
2776
 
        priv = BRASERO_IO_PRIVATE (object);
2777
 
 
2778
 
        brasero_async_task_manager_foreach_unprocessed_remove (BRASERO_ASYNC_TASK_MANAGER (object),
2779
 
                                                               brasero_io_free_async_queue,
2780
 
                                                               NULL);
2781
 
 
2782
 
        brasero_async_task_manager_foreach_active_remove (BRASERO_ASYNC_TASK_MANAGER (object),
2783
 
                                                          brasero_io_free_async_queue,
2784
 
                                                          NULL);
2785
 
 
2786
 
        g_slist_foreach (priv->metadatas, (GFunc) g_object_unref, NULL);
2787
 
        g_slist_free (priv->metadatas);
2788
 
        priv->metadatas = NULL;
2789
 
 
2790
 
        if (priv->meta_buffer) {
2791
 
                BraseroIOMetadataCached *cached;
2792
 
 
2793
 
                while ((cached = g_queue_pop_head (priv->meta_buffer)) != NULL)
2794
 
                        brasero_io_metadata_cached_free (cached);
2795
 
 
2796
 
                g_queue_free (priv->meta_buffer);
2797
 
                priv->meta_buffer = NULL;
2798
 
        }
2799
 
 
2800
 
        if (priv->results_id) {
2801
 
                g_source_remove (priv->results_id);
2802
 
                priv->results_id = 0;
2803
 
        }
2804
 
 
2805
 
        for (iter = priv->results; iter; iter = iter->next) {
2806
 
                BraseroIOJobResult *result;
2807
 
 
2808
 
                result = iter->data;
2809
 
                brasero_io_job_result_free (result);
2810
 
        }
2811
 
        g_slist_free (priv->results);
2812
 
        priv->results = NULL;
2813
 
 
2814
 
        if (priv->progress_id) {
2815
 
                g_source_remove (priv->progress_id);
2816
 
                priv->progress_id = 0;
2817
 
        }
2818
 
 
2819
 
        if (priv->progress) {
2820
 
                g_slist_foreach (priv->progress, (GFunc) g_free, NULL);
2821
 
                g_slist_free (priv->progress);
2822
 
                priv->progress = NULL;
2823
 
        }
2824
 
 
2825
 
        if (priv->lock) {
2826
 
                g_mutex_free (priv->lock);
2827
 
                priv->lock = NULL;
2828
 
        }
2829
 
 
2830
 
        if (priv->lock_metadata) {
2831
 
                g_mutex_free (priv->lock_metadata);
2832
 
                priv->lock_metadata = NULL;
2833
 
        }
2834
 
 
2835
 
        if (priv->mounted) {
2836
 
                GSList *iter;
2837
 
 
2838
 
                /* unmount all volumes we mounted ourselves */
2839
 
                for (iter = priv->mounted; iter; iter = iter->next) {
2840
 
                        GMount *mount;
2841
 
 
2842
 
                        mount = iter->data;
2843
 
 
2844
 
                        BRASERO_BURN_LOG ("Unmountin volume");
2845
 
                        g_mount_unmount (mount,
2846
 
                                         G_MOUNT_UNMOUNT_NONE,
2847
 
                                         NULL,
2848
 
                                         NULL,
2849
 
                                         NULL);
2850
 
                        g_object_unref (mount);
2851
 
                }
2852
 
        }
2853
 
 
2854
 
        G_OBJECT_CLASS (brasero_io_parent_class)->finalize (object);
2855
 
}
2856
 
 
2857
 
static void
2858
 
brasero_io_class_init (BraseroIOClass *klass)
2859
 
{
2860
 
        GObjectClass* object_class = G_OBJECT_CLASS (klass);
2861
 
 
2862
 
        g_type_class_add_private (klass, sizeof (BraseroIOPrivate));
2863
 
 
2864
 
        object_class->finalize = brasero_io_finalize;
2865
 
}
2866
 
 
2867
 
static BraseroIO *singleton = NULL;
2868
 
 
2869
 
static void
2870
 
brasero_io_last_reference_cb (gpointer null_data,
2871
 
                              GObject *object,
2872
 
                              gboolean is_last_ref)
2873
 
{
2874
 
        if (is_last_ref) {
2875
 
                singleton = NULL;
2876
 
                g_object_remove_toggle_ref (object,
2877
 
                                            brasero_io_last_reference_cb,
2878
 
                                            null_data);
2879
 
        }
2880
 
}
2881
 
 
2882
 
BraseroIO *
2883
 
brasero_io_get_default ()
2884
 
{
2885
 
        if (singleton) {
2886
 
                g_object_ref (singleton);
2887
 
                return singleton;
2888
 
        }
2889
 
 
2890
 
        singleton = g_object_new (BRASERO_TYPE_IO, NULL);
2891
 
        g_object_add_toggle_ref (G_OBJECT (singleton),
2892
 
                                 brasero_io_last_reference_cb,
2893
 
                                 NULL);
2894
 
        return singleton;
2895
 
}