1 /*
2 * pluma-document-loader.c
3 * This file is part of pluma
4 *
5 * Copyright (C) 2005 - Paolo Maggi
6 * Copyright (C) 2007 - Paolo Maggi, Steve Frécinaux
7 * Copyright (C) 2012-2021 MATE Developers
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 */
24
25 /*
26 * Modified by the pluma Team, 2005-2007. See the AUTHORS file for a
27 * list of people on the pluma Team.
28 * See the ChangeLog files for a list of changes.
29 *
30 * $Id$
31 */
32
33 #ifdef HAVE_CONFIG_H
34 #include <config.h>
35 #endif
36
37 #include <glib/gi18n.h>
38 #include <glib/gstdio.h>
39 #include <gio/gio.h>
40
41 #include "pluma-document-loader.h"
42 #include "pluma-document-output-stream.h"
43 #include "pluma-smart-charset-converter.h"
44 #include "pluma-debug.h"
45 #include "pluma-metadata-manager.h"
46 #include "pluma-utils.h"
47 #include "pluma-enum-types.h"
48 #include "pluma-settings.h"
49
50 #ifndef ENABLE_GVFS_METADATA
51 #include "pluma-metadata-manager.h"
52 #endif
53
54 typedef struct
55 {
56 PlumaDocumentLoader *loader;
57 GCancellable *cancellable;
58 gssize read;
59 gboolean tried_mount;
60 } AsyncData;
61
62 /* Signals */
63
64 enum {
65 LOADING,
66 LAST_SIGNAL
67 };
68
69 static guint signals[LAST_SIGNAL] = { 0 };
70
71 /* Properties */
72
73 enum
74 {
75 PROP_0,
76 PROP_DOCUMENT,
77 PROP_URI,
78 PROP_ENCODING,
79 PROP_NEWLINE_TYPE
80 };
81
82 #define READ_CHUNK_SIZE 8192
83 #define REMOTE_QUERY_ATTRIBUTES G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE "," \
84 G_FILE_ATTRIBUTE_STANDARD_TYPE "," \
85 G_FILE_ATTRIBUTE_TIME_MODIFIED "," \
86 G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC "," \
87 G_FILE_ATTRIBUTE_STANDARD_SIZE "," \
88 G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE "," \
89 PLUMA_METADATA_ATTRIBUTE_ENCODING
90
91 static void open_async_read (AsyncData *async);
92
93 struct _PlumaDocumentLoaderPrivate
94 {
95 GSettings *enc_settings;
96
97 PlumaDocument *document;
98 gboolean used;
99
100 /* Info on the current file */
101 GFileInfo *info;
102 gchar *uri;
103 const PlumaEncoding *encoding;
104 const PlumaEncoding *auto_detected_encoding;
105 PlumaDocumentNewlineType auto_detected_newline_type;
106 GFile *gfile;
107 goffset bytes_read;
108
109 /* Handle for remote files */
110 GCancellable *cancellable;
111 GInputStream *stream;
112 GOutputStream *output;
113 PlumaSmartCharsetConverter *converter;
114
115 gchar buffer[READ_CHUNK_SIZE];
116
117 GError *error;
118 };
119
G_DEFINE_TYPE_WITH_PRIVATE(PlumaDocumentLoader,pluma_document_loader,G_TYPE_OBJECT)120 G_DEFINE_TYPE_WITH_PRIVATE (PlumaDocumentLoader, pluma_document_loader, G_TYPE_OBJECT)
121
122 static void
123 pluma_document_loader_set_property (GObject *object,
124 guint prop_id,
125 const GValue *value,
126 GParamSpec *pspec)
127 {
128 PlumaDocumentLoader *loader = PLUMA_DOCUMENT_LOADER (object);
129
130 switch (prop_id)
131 {
132 case PROP_DOCUMENT:
133 g_return_if_fail (loader->priv->document == NULL);
134 loader->priv->document = g_value_get_object (value);
135 break;
136 case PROP_URI:
137 g_return_if_fail (loader->priv->uri == NULL);
138 loader->priv->uri = g_value_dup_string (value);
139 break;
140 case PROP_ENCODING:
141 g_return_if_fail (loader->priv->encoding == NULL);
142 loader->priv->encoding = g_value_get_boxed (value);
143 break;
144 case PROP_NEWLINE_TYPE:
145 loader->priv->auto_detected_newline_type = g_value_get_enum (value);
146 break;
147 default:
148 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
149 break;
150 }
151 }
152
153 static void
pluma_document_loader_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)154 pluma_document_loader_get_property (GObject *object,
155 guint prop_id,
156 GValue *value,
157 GParamSpec *pspec)
158 {
159 PlumaDocumentLoader *loader = PLUMA_DOCUMENT_LOADER (object);
160
161 switch (prop_id)
162 {
163 case PROP_DOCUMENT:
164 g_value_set_object (value, loader->priv->document);
165 break;
166 case PROP_URI:
167 g_value_set_string (value, loader->priv->uri);
168 break;
169 case PROP_ENCODING:
170 g_value_set_boxed (value, pluma_document_loader_get_encoding (loader));
171 break;
172 case PROP_NEWLINE_TYPE:
173 g_value_set_enum (value, loader->priv->auto_detected_newline_type);
174 break;
175 default:
176 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
177 break;
178 }
179 }
180
181 static void
pluma_document_loader_finalize(GObject * object)182 pluma_document_loader_finalize (GObject *object)
183 {
184 PlumaDocumentLoaderPrivate *priv = pluma_document_loader_get_instance_private (PLUMA_DOCUMENT_LOADER(object));
185
186 g_free (priv->uri);
187
188 G_OBJECT_CLASS (pluma_document_loader_parent_class)->finalize (object);
189 }
190
191 static void
pluma_document_loader_dispose(GObject * object)192 pluma_document_loader_dispose (GObject *object)
193 {
194 PlumaDocumentLoaderPrivate *priv = pluma_document_loader_get_instance_private (PLUMA_DOCUMENT_LOADER(object));
195
196 if (priv->cancellable != NULL)
197 {
198 g_cancellable_cancel (priv->cancellable);
199 g_object_unref (priv->cancellable);
200 priv->cancellable = NULL;
201 }
202
203 if (priv->stream != NULL)
204 {
205 g_object_unref (priv->stream);
206 priv->stream = NULL;
207 }
208
209 if (priv->output != NULL)
210 {
211 g_object_unref (priv->output);
212 priv->output = NULL;
213 }
214
215 if (priv->converter != NULL)
216 {
217 g_object_unref (priv->converter);
218 priv->converter = NULL;
219 }
220
221 if (priv->gfile != NULL)
222 {
223 g_object_unref (priv->gfile);
224 priv->gfile = NULL;
225 }
226
227 g_clear_error (&priv->error);
228
229 if (priv->info != NULL)
230 {
231 g_object_unref (priv->info);
232 priv->info = NULL;
233 }
234
235 g_clear_object (&priv->enc_settings);
236
237 G_OBJECT_CLASS (pluma_document_loader_parent_class)->dispose (object);
238 }
239
240 static void
pluma_document_loader_class_init(PlumaDocumentLoaderClass * klass)241 pluma_document_loader_class_init (PlumaDocumentLoaderClass *klass)
242 {
243 GObjectClass *object_class = G_OBJECT_CLASS (klass);
244
245 object_class->dispose = pluma_document_loader_dispose;
246 object_class->finalize = pluma_document_loader_finalize;
247 object_class->get_property = pluma_document_loader_get_property;
248 object_class->set_property = pluma_document_loader_set_property;
249
250 g_object_class_install_property (object_class,
251 PROP_DOCUMENT,
252 g_param_spec_object ("document",
253 "Document",
254 "The PlumaDocument this PlumaDocumentLoader is associated with",
255 PLUMA_TYPE_DOCUMENT,
256 G_PARAM_READWRITE |
257 G_PARAM_CONSTRUCT_ONLY |
258 G_PARAM_STATIC_STRINGS));
259
260 g_object_class_install_property (object_class,
261 PROP_URI,
262 g_param_spec_string ("uri",
263 "URI",
264 "The URI this PlumaDocumentLoader loads the document from",
265 "",
266 G_PARAM_READWRITE |
267 G_PARAM_CONSTRUCT_ONLY));
268
269 g_object_class_install_property (object_class,
270 PROP_ENCODING,
271 g_param_spec_boxed ("encoding",
272 "Encoding",
273 "The encoding of the saved file",
274 PLUMA_TYPE_ENCODING,
275 G_PARAM_READWRITE |
276 G_PARAM_CONSTRUCT_ONLY |
277 G_PARAM_STATIC_STRINGS));
278
279 g_object_class_install_property (object_class,
280 PROP_NEWLINE_TYPE,
281 g_param_spec_enum ("newline-type",
282 "Newline type",
283 "The accepted types of line ending",
284 PLUMA_TYPE_DOCUMENT_NEWLINE_TYPE,
285 PLUMA_DOCUMENT_NEWLINE_TYPE_LF,
286 G_PARAM_READWRITE |
287 G_PARAM_STATIC_NAME |
288 G_PARAM_STATIC_BLURB));
289
290 signals[LOADING] =
291 g_signal_new ("loading",
292 G_OBJECT_CLASS_TYPE (object_class),
293 G_SIGNAL_RUN_LAST,
294 G_STRUCT_OFFSET (PlumaDocumentLoaderClass, loading),
295 NULL, NULL, NULL,
296 G_TYPE_NONE,
297 2,
298 G_TYPE_BOOLEAN,
299 G_TYPE_POINTER);
300 }
301
302 static void
pluma_document_loader_init(PlumaDocumentLoader * loader)303 pluma_document_loader_init (PlumaDocumentLoader *loader)
304 {
305 loader->priv = pluma_document_loader_get_instance_private (loader);
306
307 loader->priv->used = FALSE;
308 loader->priv->auto_detected_newline_type = PLUMA_DOCUMENT_NEWLINE_TYPE_DEFAULT;
309 loader->priv->converter = NULL;
310 loader->priv->error = NULL;
311 loader->priv->enc_settings = g_settings_new (PLUMA_SCHEMA_ID);
312 }
313
314 PlumaDocumentLoader *
pluma_document_loader_new(PlumaDocument * doc,const gchar * uri,const PlumaEncoding * encoding)315 pluma_document_loader_new (PlumaDocument *doc,
316 const gchar *uri,
317 const PlumaEncoding *encoding)
318 {
319 g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), NULL);
320
321 return PLUMA_DOCUMENT_LOADER (g_object_new (PLUMA_TYPE_DOCUMENT_LOADER,
322 "document", doc,
323 "uri", uri,
324 "encoding", encoding,
325 NULL));
326 }
327
328 static AsyncData *
async_data_new(PlumaDocumentLoader * loader)329 async_data_new (PlumaDocumentLoader *loader)
330 {
331 AsyncData *async;
332
333 async = g_slice_new (AsyncData);
334 async->loader = loader;
335 async->cancellable = g_object_ref (loader->priv->cancellable);
336 async->tried_mount = FALSE;
337
338 return async;
339 }
340
341 static void
async_data_free(AsyncData * async)342 async_data_free (AsyncData *async)
343 {
344 g_object_unref (async->cancellable);
345 g_slice_free (AsyncData, async);
346 }
347
348 static const PlumaEncoding *
get_metadata_encoding(PlumaDocumentLoader * loader)349 get_metadata_encoding (PlumaDocumentLoader *loader)
350 {
351 const PlumaEncoding *enc = NULL;
352
353 #ifndef ENABLE_GVFS_METADATA
354 gchar *charset;
355 const gchar *uri;
356
357 uri = pluma_document_loader_get_uri (loader);
358
359 charset = pluma_metadata_manager_get (uri, "encoding");
360
361 if (charset == NULL)
362 return NULL;
363
364 enc = pluma_encoding_get_from_charset (charset);
365
366 g_free (charset);
367 #else
368 GFileInfo *info;
369
370 info = pluma_document_loader_get_info (loader);
371
372 /* check if the encoding was set in the metadata */
373 if (g_file_info_has_attribute (info, PLUMA_METADATA_ATTRIBUTE_ENCODING))
374 {
375 const gchar *charset;
376
377 charset = g_file_info_get_attribute_string (info, PLUMA_METADATA_ATTRIBUTE_ENCODING);
378
379 if (charset == NULL)
380 return NULL;
381
382 enc = pluma_encoding_get_from_charset (charset);
383 }
384 #endif
385
386 return enc;
387 }
388
389 static void
remote_load_completed_or_failed(PlumaDocumentLoader * loader,AsyncData * async)390 remote_load_completed_or_failed (PlumaDocumentLoader *loader, AsyncData *async)
391 {
392 pluma_document_loader_loading (loader,
393 TRUE,
394 loader->priv->error);
395
396 if (async)
397 async_data_free (async);
398 }
399
400 static void
async_failed(AsyncData * async,GError * error)401 async_failed (AsyncData *async, GError *error)
402 {
403 g_propagate_error (&async->loader->priv->error, error);
404 remote_load_completed_or_failed (async->loader, async);
405 }
406
407 static void
close_input_stream_ready_cb(GInputStream * stream,GAsyncResult * res,AsyncData * async)408 close_input_stream_ready_cb (GInputStream *stream,
409 GAsyncResult *res,
410 AsyncData *async)
411 {
412 GError *error = NULL;
413
414 pluma_debug (DEBUG_LOADER);
415
416 /* check cancelled state manually */
417 if (g_cancellable_is_cancelled (async->cancellable))
418 {
419 async_data_free (async);
420 return;
421 }
422
423 pluma_debug_message (DEBUG_SAVER, "Finished closing input stream");
424
425 if (!g_input_stream_close_finish (stream, res, &error))
426 {
427 pluma_debug_message (DEBUG_SAVER, "Closing input stream error: %s", error->message);
428
429 async_failed (async, error);
430 return;
431 }
432
433 pluma_debug_message (DEBUG_SAVER, "Close output stream");
434 if (!g_output_stream_close (async->loader->priv->output, async->cancellable, &error))
435 {
436 async_failed (async, error);
437 return;
438 }
439
440 remote_load_completed_or_failed (async->loader, async);
441 }
442
443 static void
write_complete(AsyncData * async)444 write_complete (AsyncData *async)
445 {
446 if (async->loader->priv->stream)
447 g_input_stream_close_async (G_INPUT_STREAM (async->loader->priv->stream),
448 G_PRIORITY_HIGH,
449 async->cancellable,
450 (GAsyncReadyCallback)close_input_stream_ready_cb,
451 async);
452 }
453
454 /* prototype, because they call each other... isn't C lovely */
455 static void read_file_chunk (AsyncData *async);
456
457 static void
write_file_chunk(AsyncData * async)458 write_file_chunk (AsyncData *async)
459 {
460 PlumaDocumentLoader *loader;
461 gssize bytes_written;
462 GError *error = NULL;
463
464 loader = async->loader;
465
466 /* we use sync methods on doc stream since it is in memory. Using async
467 would be racy and we can endup with invalidated iters */
468 bytes_written = g_output_stream_write (G_OUTPUT_STREAM (loader->priv->output),
469 loader->priv->buffer,
470 async->read,
471 async->cancellable,
472 &error);
473
474 pluma_debug_message (DEBUG_SAVER, "Written: %" G_GSSIZE_FORMAT, bytes_written);
475 if (bytes_written == -1)
476 {
477 pluma_debug_message (DEBUG_SAVER, "Write error: %s", error->message);
478 async_failed (async, error);
479 return;
480 }
481
482 /* note that this signal blocks the read... check if it isn't
483 * a performance problem
484 */
485 pluma_document_loader_loading (loader, FALSE, NULL);
486
487 read_file_chunk (async);
488 }
489
490 static void
async_read_cb(GInputStream * stream,GAsyncResult * res,AsyncData * async)491 async_read_cb (GInputStream *stream,
492 GAsyncResult *res,
493 AsyncData *async)
494 {
495 pluma_debug (DEBUG_LOADER);
496 PlumaDocumentLoader *loader;
497 GError *error = NULL;
498
499 pluma_debug (DEBUG_LOADER);
500
501 /* manually check cancelled state */
502 if (g_cancellable_is_cancelled (async->cancellable))
503 {
504 async_data_free (async);
505 return;
506 }
507
508 loader = async->loader;
509
510 async->read = g_input_stream_read_finish (stream, res, &error);
511
512 /* error occurred */
513 if (async->read == -1)
514 {
515 async_failed (async, error);
516 return;
517 }
518
519 /* Check for the extremely unlikely case where the file size overflows. */
520 if (loader->priv->bytes_read + async->read < loader->priv->bytes_read)
521 {
522 g_set_error (&loader->priv->error,
523 PLUMA_DOCUMENT_ERROR,
524 PLUMA_DOCUMENT_ERROR_TOO_BIG,
525 "File too big");
526
527 async_failed (async, loader->priv->error);
528 return;
529 }
530
531 /* Bump the size. */
532 loader->priv->bytes_read += async->read;
533
534 /* end of the file, we are done! */
535 if (async->read == 0)
536 {
537 g_output_stream_flush (loader->priv->output,
538 NULL,
539 &loader->priv->error);
540
541 loader->priv->auto_detected_encoding =
542 pluma_smart_charset_converter_get_guessed (loader->priv->converter);
543
544 loader->priv->auto_detected_newline_type =
545 pluma_document_output_stream_detect_newline_type (PLUMA_DOCUMENT_OUTPUT_STREAM (loader->priv->output));
546
547 /* Check if we needed some fallback char, if so, check if there was
548 a previous error and if not set a fallback used error */
549 /* FIXME Uncomment this when we want to manage conversion fallback */
550 /*if ((pluma_smart_charset_converter_get_num_fallbacks (loader->priv->converter) != 0) &&
551 loader->priv->error == NULL)
552 {
553 g_set_error_literal (&loader->priv->error,
554 PLUMA_DOCUMENT_ERROR,
555 PLUMA_DOCUMENT_ERROR_CONVERSION_FALLBACK,
556 "There was a conversion error and it was "
557 "needed to use a fallback char");
558 }*/
559
560 write_complete (async);
561
562 return;
563 }
564
565 write_file_chunk (async);
566 }
567
568 static void
read_file_chunk(AsyncData * async)569 read_file_chunk (AsyncData *async)
570 {
571 PlumaDocumentLoader *loader;
572
573 loader = async->loader;
574
575 g_input_stream_read_async (G_INPUT_STREAM (loader->priv->stream),
576 loader->priv->buffer,
577 READ_CHUNK_SIZE,
578 G_PRIORITY_HIGH,
579 async->cancellable,
580 (GAsyncReadyCallback) async_read_cb,
581 async);
582 }
583
584 static GSList *
get_candidate_encodings(PlumaDocumentLoader * loader)585 get_candidate_encodings (PlumaDocumentLoader *loader)
586 {
587 const PlumaEncoding *metadata;
588 GSList *encodings;
589 gchar **enc_strv;
590
591 enc_strv = g_settings_get_strv (loader->priv->enc_settings,
592 PLUMA_SETTINGS_ENCODING_AUTO_DETECTED);
593
594 encodings = _pluma_encoding_strv_to_list ((const gchar * const *)enc_strv);
595 g_strfreev (enc_strv);
596
597 metadata = get_metadata_encoding (loader);
598 if (metadata != NULL)
599 {
600 encodings = g_slist_prepend (encodings, (gpointer)metadata);
601 }
602
603 return encodings;
604 }
605
606 static void
finish_query_info(AsyncData * async)607 finish_query_info (AsyncData *async)
608 {
609 PlumaDocumentLoader *loader;
610 GInputStream *conv_stream;
611 GFileInfo *info;
612 GSList *candidate_encodings;
613
614 loader = async->loader;
615 info = loader->priv->info;
616
617 /* if it's not a regular file, error out... */
618 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_TYPE) &&
619 g_file_info_get_file_type (info) != G_FILE_TYPE_REGULAR)
620 {
621 g_set_error (&loader->priv->error,
622 G_IO_ERROR,
623 G_IO_ERROR_NOT_REGULAR_FILE,
624 "Not a regular file");
625
626 remote_load_completed_or_failed (loader, async);
627
628 return;
629 }
630
631 /* Get the candidate encodings */
632 if (loader->priv->encoding == NULL)
633 {
634 candidate_encodings = get_candidate_encodings (loader);
635 }
636 else
637 {
638 candidate_encodings = g_slist_prepend (NULL, (gpointer) loader->priv->encoding);
639 }
640
641 loader->priv->converter = pluma_smart_charset_converter_new (candidate_encodings);
642 g_slist_free (candidate_encodings);
643
644 conv_stream = g_converter_input_stream_new (loader->priv->stream,
645 G_CONVERTER (loader->priv->converter));
646
647 g_object_unref (loader->priv->stream);
648
649 loader->priv->stream = conv_stream;
650
651 /* Output stream */
652 loader->priv->output = pluma_document_output_stream_new (loader->priv->document);
653
654 /* start reading */
655 read_file_chunk (async);
656 }
657
658 static void
query_info_cb(GFile * source,GAsyncResult * res,AsyncData * async)659 query_info_cb (GFile *source,
660 GAsyncResult *res,
661 AsyncData *async)
662 {
663 GFileInfo *info;
664 GError *error = NULL;
665
666 pluma_debug (DEBUG_LOADER);
667
668 /* manually check the cancelled state */
669 if (g_cancellable_is_cancelled (async->cancellable))
670 {
671 async_data_free (async);
672 return;
673 }
674
675 /* finish the info query */
676 info = g_file_query_info_finish (async->loader->priv->gfile,
677 res,
678 &error);
679
680 if (info == NULL)
681 {
682 /* propagate the error and clean up */
683 async_failed (async, error);
684 return;
685 }
686
687 async->loader->priv->info = info;
688
689 finish_query_info (async);
690 }
691
692 static void
mount_ready_callback(GFile * file,GAsyncResult * res,AsyncData * async)693 mount_ready_callback (GFile *file,
694 GAsyncResult *res,
695 AsyncData *async)
696 {
697 GError *error = NULL;
698 gboolean mounted;
699
700 pluma_debug (DEBUG_LOADER);
701
702 /* manual check for cancelled state */
703 if (g_cancellable_is_cancelled (async->cancellable))
704 {
705 async_data_free (async);
706 return;
707 }
708
709 mounted = g_file_mount_enclosing_volume_finish (file, res, &error);
710
711 if (!mounted)
712 {
713 async_failed (async, error);
714 }
715 else
716 {
717 /* try again to open the file for reading */
718 open_async_read (async);
719 }
720 }
721
722 static void
recover_not_mounted(AsyncData * async)723 recover_not_mounted (AsyncData *async)
724 {
725 PlumaDocument *doc;
726 GMountOperation *mount_operation;
727
728 pluma_debug (DEBUG_LOADER);
729
730 doc = pluma_document_loader_get_document (async->loader);
731 mount_operation = _pluma_document_create_mount_operation (doc);
732
733 async->tried_mount = TRUE;
734 g_file_mount_enclosing_volume (async->loader->priv->gfile,
735 G_MOUNT_MOUNT_NONE,
736 mount_operation,
737 async->cancellable,
738 (GAsyncReadyCallback) mount_ready_callback,
739 async);
740
741 g_object_unref (mount_operation);
742 }
743
744 static void
async_read_ready_callback(GObject * source,GAsyncResult * res,AsyncData * async)745 async_read_ready_callback (GObject *source,
746 GAsyncResult *res,
747 AsyncData *async)
748 {
749 GError *error = NULL;
750 PlumaDocumentLoader *loader;
751
752 pluma_debug (DEBUG_LOADER);
753
754 /* manual check for cancelled state */
755 if (g_cancellable_is_cancelled (async->cancellable))
756 {
757 async_data_free (async);
758 return;
759 }
760
761 loader = async->loader;
762
763 loader->priv->stream = G_INPUT_STREAM (g_file_read_finish (loader->priv->gfile,
764 res, &error));
765
766 if (!loader->priv->stream)
767 {
768 if (error->code == G_IO_ERROR_NOT_MOUNTED && !async->tried_mount)
769 {
770 recover_not_mounted (async);
771 g_error_free (error);
772 return;
773 }
774
775 /* Propagate error */
776 g_propagate_error (&loader->priv->error, error);
777 pluma_document_loader_loading (loader,
778 TRUE,
779 loader->priv->error);
780
781 async_data_free (async);
782 return;
783 }
784
785 /* get the file info: note we cannot use
786 * g_file_input_stream_query_info_async since it is not able to get the
787 * content type etc, beside it is not supported by gvfs.
788 * Using the file instead of the stream is slightly racy, but for
789 * loading this is not too bad...
790 */
791 g_file_query_info_async (loader->priv->gfile,
792 REMOTE_QUERY_ATTRIBUTES,
793 G_FILE_QUERY_INFO_NONE,
794 G_PRIORITY_HIGH,
795 async->cancellable,
796 (GAsyncReadyCallback) query_info_cb,
797 async);
798 }
799
800 static void
open_async_read(AsyncData * async)801 open_async_read (AsyncData *async)
802 {
803 g_file_read_async (async->loader->priv->gfile,
804 G_PRIORITY_HIGH,
805 async->cancellable,
806 (GAsyncReadyCallback) async_read_ready_callback,
807 async);
808 }
809
810 void
pluma_document_loader_loading(PlumaDocumentLoader * loader,gboolean completed,GError * error)811 pluma_document_loader_loading (PlumaDocumentLoader *loader,
812 gboolean completed,
813 GError *error)
814 {
815 /* the object will be unrefed in the callback of the loading signal
816 * (when completed == TRUE), so we need to prevent finalization.
817 */
818 if (completed)
819 {
820 g_object_ref (loader);
821 }
822
823 g_signal_emit (loader, signals[LOADING], 0, completed, error);
824
825 if (completed)
826 {
827 if (error == NULL)
828 pluma_debug_message (DEBUG_LOADER, "load completed");
829 else
830 pluma_debug_message (DEBUG_LOADER, "load failed");
831
832 g_object_unref (loader);
833 }
834 }
835
836 /* If enconding == NULL, the encoding will be autodetected */
837 void
pluma_document_loader_load(PlumaDocumentLoader * loader)838 pluma_document_loader_load (PlumaDocumentLoader *loader)
839 {
840 AsyncData *async;
841
842 pluma_debug (DEBUG_LOADER);
843
844 g_return_if_fail (PLUMA_IS_DOCUMENT_LOADER (loader));
845
846 /* the loader can be used just once, then it must be thrown away */
847 g_return_if_fail (loader->priv->used == FALSE);
848 loader->priv->used = TRUE;
849
850 /* make sure no load operation is currently running */
851 g_return_if_fail (loader->priv->cancellable == NULL);
852
853 loader->priv->gfile = g_file_new_for_uri (loader->priv->uri);
854
855 /* loading start */
856 pluma_document_loader_loading (PLUMA_DOCUMENT_LOADER (loader),
857 FALSE,
858 NULL);
859
860 loader->priv->cancellable = g_cancellable_new ();
861 async = async_data_new (loader);
862
863 open_async_read (async);
864 }
865
866 gboolean
pluma_document_loader_cancel(PlumaDocumentLoader * loader)867 pluma_document_loader_cancel (PlumaDocumentLoader *loader)
868 {
869 pluma_debug (DEBUG_LOADER);
870
871 g_return_val_if_fail (PLUMA_IS_DOCUMENT_LOADER (loader), FALSE);
872
873 if (loader->priv->cancellable == NULL)
874 return FALSE;
875
876 g_cancellable_cancel (loader->priv->cancellable);
877
878 g_set_error (&loader->priv->error,
879 G_IO_ERROR,
880 G_IO_ERROR_CANCELLED,
881 "Operation cancelled");
882
883 remote_load_completed_or_failed (loader, NULL);
884
885 return TRUE;
886 }
887
888 PlumaDocument *
pluma_document_loader_get_document(PlumaDocumentLoader * loader)889 pluma_document_loader_get_document (PlumaDocumentLoader *loader)
890 {
891 g_return_val_if_fail (PLUMA_IS_DOCUMENT_LOADER (loader), NULL);
892
893 return loader->priv->document;
894 }
895
896 /* Returns STDIN_URI if loading from stdin */
897 const gchar *
pluma_document_loader_get_uri(PlumaDocumentLoader * loader)898 pluma_document_loader_get_uri (PlumaDocumentLoader *loader)
899 {
900 g_return_val_if_fail (PLUMA_IS_DOCUMENT_LOADER (loader), NULL);
901
902 return loader->priv->uri;
903 }
904
905 goffset
pluma_document_loader_get_bytes_read(PlumaDocumentLoader * loader)906 pluma_document_loader_get_bytes_read (PlumaDocumentLoader *loader)
907 {
908 g_return_val_if_fail (PLUMA_IS_DOCUMENT_LOADER (loader), 0);
909
910 return loader->priv->bytes_read;
911 }
912
913 const PlumaEncoding *
pluma_document_loader_get_encoding(PlumaDocumentLoader * loader)914 pluma_document_loader_get_encoding (PlumaDocumentLoader *loader)
915 {
916 g_return_val_if_fail (PLUMA_IS_DOCUMENT_LOADER (loader), NULL);
917
918 if (loader->priv->encoding != NULL)
919 return loader->priv->encoding;
920
921 g_return_val_if_fail (loader->priv->auto_detected_encoding != NULL,
922 pluma_encoding_get_current ());
923
924 return loader->priv->auto_detected_encoding;
925 }
926
927 PlumaDocumentNewlineType
pluma_document_loader_get_newline_type(PlumaDocumentLoader * loader)928 pluma_document_loader_get_newline_type (PlumaDocumentLoader *loader)
929 {
930 g_return_val_if_fail (PLUMA_IS_DOCUMENT_LOADER (loader),
931 PLUMA_DOCUMENT_NEWLINE_TYPE_LF);
932
933 return loader->priv->auto_detected_newline_type;
934 }
935
936 GFileInfo *
pluma_document_loader_get_info(PlumaDocumentLoader * loader)937 pluma_document_loader_get_info (PlumaDocumentLoader *loader)
938 {
939 g_return_val_if_fail (PLUMA_IS_DOCUMENT_LOADER (loader), NULL);
940
941 return loader->priv->info;
942 }
943