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