1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /*
3  *  Copyright © 2020 Jan-Michael Brummer <jan.brummer@tabos.org>
4  *
5  *  This file is part of Epiphany.
6  *
7  *  Epiphany is free software: you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation, either version 3 of the License, or
10  *  (at your option) any later version.
11  *
12  *  Epiphany is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with Epiphany.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "config.h"
22 #include "ephy-pdf-handler.h"
23 
24 #include "ephy-embed-container.h"
25 #include "ephy-embed-shell.h"
26 #include "ephy-web-view.h"
27 
28 #include <gio/gio.h>
29 #include <glib/gi18n.h>
30 #include <string.h>
31 
32 struct _EphyPDFHandler {
33   GObject parent_instance;
34 
35   GList *outstanding_requests;
36 };
37 
38 G_DEFINE_TYPE (EphyPDFHandler, ephy_pdf_handler, G_TYPE_OBJECT)
39 
40 typedef struct {
41   EphyPDFHandler *source_handler;
42   WebKitURISchemeRequest *scheme_request;
43   GCancellable *cancellable;
44   EphyDownload *download;
45   char *file_name;
46 } EphyPdfRequest;
47 
48 static EphyPdfRequest *
ephy_pdf_request_new(EphyPDFHandler * handler,WebKitURISchemeRequest * request)49 ephy_pdf_request_new (EphyPDFHandler         *handler,
50                       WebKitURISchemeRequest *request)
51 {
52   EphyPdfRequest *pdf_request;
53 
54   pdf_request = g_new0 (EphyPdfRequest, 1);
55   pdf_request->source_handler = g_object_ref (handler);
56   pdf_request->scheme_request = g_object_ref (request);
57   pdf_request->cancellable = g_cancellable_new ();
58 
59   return pdf_request;
60 }
61 
62 static void
ephy_pdf_request_free(EphyPdfRequest * request)63 ephy_pdf_request_free (EphyPdfRequest *request)
64 {
65   if (request->download) {
66     g_signal_handlers_disconnect_by_data (request->download, request);
67 
68     if (ephy_download_is_active (request->download))
69       ephy_download_cancel (request->download);
70   }
71 
72   g_object_unref (request->source_handler);
73   g_object_unref (request->scheme_request);
74   g_clear_pointer (&request->file_name, g_free);
75 
76   g_cancellable_cancel (request->cancellable);
77   g_object_unref (request->cancellable);
78 
79   g_free (request);
80 }
81 
82 static void
finish_uri_scheme_request(EphyPdfRequest * request,gchar * data,GError * error)83 finish_uri_scheme_request (EphyPdfRequest *request,
84                            gchar          *data,
85                            GError         *error)
86 {
87   GInputStream *stream;
88   gssize data_length;
89 
90   g_assert ((data && !error) || (!data && error));
91 
92   if (error) {
93     webkit_uri_scheme_request_finish_error (request->scheme_request, error);
94   } else {
95     data_length = MIN (strlen (data), G_MAXSSIZE);
96     stream = g_memory_input_stream_new_from_data (data, data_length, g_free);
97     webkit_uri_scheme_request_finish (request->scheme_request, stream, data_length, "text/html");
98     g_object_unref (stream);
99   }
100 
101   request->source_handler->outstanding_requests =
102     g_list_remove (request->source_handler->outstanding_requests,
103                    request);
104 
105   ephy_pdf_request_free (request);
106 }
107 
108 static void
pdf_file_deleted(GObject * source,GAsyncResult * res,gpointer user_data)109 pdf_file_deleted (GObject      *source,
110                   GAsyncResult *res,
111                   gpointer      user_data)
112 {
113   g_autoptr (GError) error = NULL;
114   if (!g_file_delete_finish (G_FILE (source), res, &error))
115     g_warning ("Could not delete temporary PDF file: %s", error->message);
116 }
117 
118 static void
pdf_file_loaded(GObject * source,GAsyncResult * res,gpointer user_data)119 pdf_file_loaded (GObject      *source,
120                  GAsyncResult *res,
121                  gpointer      user_data)
122 {
123   EphyPdfRequest *self = user_data;
124   GBytes *html_file;
125   g_autoptr (GError) error = NULL;
126   g_autoptr (GString) html = NULL;
127   g_autofree gchar *b64 = NULL;
128   g_autofree char *file_data = NULL;
129   gsize len = 0;
130 
131   if (!g_file_load_contents_finish (G_FILE (source), res, &file_data, &len, NULL, &error)) {
132     if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
133       g_warning ("Could not read PDF file content: %s", error->message);
134     return;
135   }
136 
137   html_file = g_resources_lookup_data ("/org/gnome/epiphany/pdfjs/web/viewer.html", 0, NULL);
138 
139   b64 = g_base64_encode ((const guchar *)file_data, len);
140   g_file_delete_async (G_FILE (source), G_PRIORITY_DEFAULT, NULL, pdf_file_deleted, NULL);
141 
142   html = g_string_new ("");
143   g_string_printf (html, g_bytes_get_data (html_file, NULL), b64, self->file_name ? self->file_name : "");
144 
145   finish_uri_scheme_request (self, g_strdup (html->str), NULL);
146 }
147 
148 static void
download_completed_cb(EphyDownload * download,EphyPdfRequest * self)149 download_completed_cb (EphyDownload   *download,
150                        EphyPdfRequest *self)
151 {
152   g_assert (download);
153   g_assert (self);
154 
155   g_signal_handlers_disconnect_by_data (download, self);
156 
157   if (g_strcmp0 ("application/pdf", ephy_download_get_content_type (download)) == 0) {
158     g_autoptr (GFile) file = NULL;
159     const char *document_uri = webkit_download_get_destination (ephy_download_get_webkit_download (download));
160 
161     file = g_file_new_for_uri (document_uri);
162 
163     g_file_load_contents_async (file, self->cancellable, pdf_file_loaded, self);
164   } else {
165     g_warning ("PDF %s has invalid MIME type: %s",
166                ephy_download_get_destination_uri (download),
167                ephy_download_get_content_type (download));
168   }
169 
170   g_clear_object (&self->download);
171 }
172 
173 static void
download_errored_cb(EphyDownload * download,GError * error,EphyPdfRequest * self)174 download_errored_cb (EphyDownload   *download,
175                      GError         *error,
176                      EphyPdfRequest *self)
177 {
178   g_assert (download);
179   g_assert (error);
180   g_assert (self);
181 
182   g_signal_handlers_disconnect_by_data (download, self);
183 
184   if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
185     WebKitURIRequest *request = webkit_download_get_request (ephy_download_get_webkit_download (download));
186 
187     g_warning ("Cannot fetch pdf from <%s>: %s",
188                webkit_uri_request_get_uri (request),
189                error ? error->message : "Unknown error");
190   }
191 
192   g_clear_object (&self->download);
193 }
194 
195 static gboolean
decide_destination_cb(WebKitDownload * wk_download,const gchar * suggested_filename,gpointer user_data)196 decide_destination_cb (WebKitDownload *wk_download,
197                        const gchar    *suggested_filename,
198                        gpointer        user_data)
199 {
200   EphyPdfRequest *request = user_data;
201   g_autofree gchar *tmp_file = NULL;
202   g_autofree gchar *file_uri = NULL;
203 
204   tmp_file = g_strdup_printf ("%s/%s", g_get_tmp_dir (), g_path_get_basename (suggested_filename));
205   file_uri = g_filename_to_uri (tmp_file, NULL, NULL);
206   ephy_download_set_destination_uri (request->download, file_uri);
207 
208   g_clear_pointer (&request->file_name, g_free);
209   request->file_name = g_path_get_basename (suggested_filename);
210 
211   return TRUE;
212 }
213 
214 static void
ephy_pdf_request_start(EphyPdfRequest * request)215 ephy_pdf_request_start (EphyPdfRequest *request)
216 {
217   const char *modified_uri;
218   const char *original_uri;
219 
220   request->source_handler->outstanding_requests =
221     g_list_prepend (request->source_handler->outstanding_requests, request);
222 
223   original_uri = webkit_uri_scheme_request_get_uri (request->scheme_request);
224   g_assert (g_str_has_prefix (original_uri, "ephy-pdf:"));
225   modified_uri = original_uri + strlen ("ephy-pdf:");
226 
227   request->download = ephy_download_new_for_uri_internal (modified_uri);
228   ephy_download_disable_desktop_notification (request->download);
229   webkit_download_set_allow_overwrite (ephy_download_get_webkit_download (request->download), TRUE);
230 
231   g_signal_connect (request->download, "completed", G_CALLBACK (download_completed_cb), request);
232   g_signal_connect (request->download, "error", G_CALLBACK (download_errored_cb), request);
233   g_signal_connect (ephy_download_get_webkit_download (request->download), "decide-destination", G_CALLBACK (decide_destination_cb), request);
234 }
235 
236 static void
cancel_outstanding_request(EphyPdfRequest * request)237 cancel_outstanding_request (EphyPdfRequest *request)
238 {
239   g_cancellable_cancel (request->cancellable);
240 }
241 
242 static void
ephy_pdf_handler_dispose(GObject * object)243 ephy_pdf_handler_dispose (GObject *object)
244 {
245   EphyPDFHandler *handler = EPHY_PDF_HANDLER (object);
246 
247   if (handler->outstanding_requests) {
248     g_list_foreach (handler->outstanding_requests, (GFunc)cancel_outstanding_request, NULL);
249     g_list_free (handler->outstanding_requests);
250     handler->outstanding_requests = NULL;
251   }
252 
253   G_OBJECT_CLASS (ephy_pdf_handler_parent_class)->dispose (object);
254 }
255 
256 static void
ephy_pdf_handler_init(EphyPDFHandler * handler)257 ephy_pdf_handler_init (EphyPDFHandler *handler)
258 {
259 }
260 
261 static void
ephy_pdf_handler_class_init(EphyPDFHandlerClass * klass)262 ephy_pdf_handler_class_init (EphyPDFHandlerClass *klass)
263 {
264   GObjectClass *object_class = G_OBJECT_CLASS (klass);
265 
266   object_class->dispose = ephy_pdf_handler_dispose;
267 }
268 
269 EphyPDFHandler *
ephy_pdf_handler_new(void)270 ephy_pdf_handler_new (void)
271 {
272   return EPHY_PDF_HANDLER (g_object_new (EPHY_TYPE_PDF_HANDLER, NULL));
273 }
274 
275 void
ephy_pdf_handler_handle_request(EphyPDFHandler * handler,WebKitURISchemeRequest * scheme_request)276 ephy_pdf_handler_handle_request (EphyPDFHandler         *handler,
277                                  WebKitURISchemeRequest *scheme_request)
278 {
279   EphyPdfRequest *pdf_request;
280 
281   pdf_request = ephy_pdf_request_new (handler, scheme_request);
282   ephy_pdf_request_start (pdf_request);
283 }
284 
285 void
ephy_pdf_handler_stop(EphyPDFHandler * handler,WebKitWebView * web_view)286 ephy_pdf_handler_stop (EphyPDFHandler *handler,
287                        WebKitWebView  *web_view)
288 {
289   GList *list;
290 
291   for (list = handler->outstanding_requests; list; list = list->next) {
292     EphyPdfRequest *request = list->data;
293     WebKitWebView *request_web_view = webkit_uri_scheme_request_get_web_view (request->scheme_request);
294 
295     if (request_web_view == web_view) {
296       ephy_pdf_request_free (request);
297       return;
298     }
299   }
300 }
301