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