1 | /* poppler-attachment.cc: glib wrapper for poppler |
2 | * Copyright (C) 2006, Red Hat, Inc. |
3 | * |
4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License as published by |
6 | * the Free Software Foundation; either version 2, or (at your option) |
7 | * any later version. |
8 | * |
9 | * This program is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | * GNU General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU General Public License |
15 | * along with this program; if not, write to the Free Software |
16 | * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. |
17 | */ |
18 | |
19 | #include "config.h" |
20 | #include <cerrno> |
21 | |
22 | #include <goo/gfile.h> |
23 | |
24 | #include "poppler.h" |
25 | #include "poppler-private.h" |
26 | |
27 | #include <new> |
28 | |
29 | /** |
30 | * SECTION:poppler-attachment |
31 | * @short_description: Attachments |
32 | * @title: PopplerAttachment |
33 | */ |
34 | |
35 | /* FIXME: We need to add gettext support sometime */ |
36 | #define _(x) (x) |
37 | |
38 | struct PopplerAttachmentPrivate |
39 | { |
40 | Object obj_stream {}; |
41 | GDateTime *mtime; |
42 | GDateTime *ctime; |
43 | }; |
44 | |
45 | static void poppler_attachment_finalize(GObject *obj); |
46 | |
47 | G_DEFINE_TYPE_WITH_PRIVATE(PopplerAttachment, poppler_attachment, G_TYPE_OBJECT) |
48 | |
49 | #define GET_PRIVATE(obj) ((PopplerAttachmentPrivate *)poppler_attachment_get_instance_private(obj)) |
50 | |
51 | static void poppler_attachment_init(PopplerAttachment *attachment) |
52 | { |
53 | void *place; |
54 | |
55 | place = GET_PRIVATE(attachment); |
56 | new (place) PopplerAttachmentPrivate(); |
57 | } |
58 | |
59 | static void poppler_attachment_class_init(PopplerAttachmentClass *klass) |
60 | { |
61 | G_OBJECT_CLASS(klass)->finalize = poppler_attachment_finalize; |
62 | } |
63 | |
64 | static void poppler_attachment_finalize(GObject *obj) |
65 | { |
66 | PopplerAttachment *attachment; |
67 | PopplerAttachmentPrivate *priv; |
68 | |
69 | attachment = (PopplerAttachment *)obj; |
70 | priv = GET_PRIVATE(attachment); |
71 | |
72 | if (attachment->name) { |
73 | g_free(mem: attachment->name); |
74 | } |
75 | attachment->name = nullptr; |
76 | |
77 | if (attachment->description) { |
78 | g_free(mem: attachment->description); |
79 | } |
80 | attachment->description = nullptr; |
81 | |
82 | if (attachment->checksum) { |
83 | g_string_free(string: attachment->checksum, TRUE); |
84 | } |
85 | attachment->checksum = nullptr; |
86 | |
87 | g_clear_pointer(&priv->mtime, g_date_time_unref); |
88 | g_clear_pointer(&priv->ctime, g_date_time_unref); |
89 | |
90 | priv->~PopplerAttachmentPrivate(); |
91 | |
92 | G_OBJECT_CLASS(poppler_attachment_parent_class)->finalize(obj); |
93 | } |
94 | |
95 | /* Public functions */ |
96 | |
97 | PopplerAttachment *_poppler_attachment_new(FileSpec *emb_file) |
98 | { |
99 | PopplerAttachment *attachment; |
100 | PopplerAttachmentPrivate *priv; |
101 | EmbFile *embFile; |
102 | |
103 | g_assert(emb_file != nullptr); |
104 | |
105 | attachment = (PopplerAttachment *)g_object_new(POPPLER_TYPE_ATTACHMENT, first_property_name: nullptr); |
106 | priv = GET_PRIVATE(attachment); |
107 | |
108 | if (emb_file->getFileName()) { |
109 | attachment->name = _poppler_goo_string_to_utf8(s: emb_file->getFileName()); |
110 | } |
111 | if (emb_file->getDescription()) { |
112 | attachment->description = _poppler_goo_string_to_utf8(s: emb_file->getDescription()); |
113 | } |
114 | |
115 | embFile = emb_file->getEmbeddedFile(); |
116 | if (embFile != nullptr && embFile->streamObject()->isStream()) { |
117 | attachment->size = embFile->size(); |
118 | |
119 | if (embFile->createDate()) { |
120 | priv->ctime = _poppler_convert_pdf_date_to_date_time(date: embFile->createDate()); |
121 | G_GNUC_BEGIN_IGNORE_DEPRECATIONS |
122 | /* This will overflow on dates from after 2038. This field is |
123 | * deprecated, only kept for backward compatibility. */ |
124 | attachment->ctime = (GTime)g_date_time_to_unix(datetime: priv->ctime); |
125 | G_GNUC_END_IGNORE_DEPRECATIONS |
126 | } |
127 | if (embFile->modDate()) { |
128 | priv->mtime = _poppler_convert_pdf_date_to_date_time(date: embFile->modDate()); |
129 | G_GNUC_BEGIN_IGNORE_DEPRECATIONS |
130 | /* This will overflow on dates from after 2038. This field is |
131 | * deprecated, only kept for backward compatibility. */ |
132 | attachment->mtime = (GTime)g_date_time_to_unix(datetime: priv->mtime); |
133 | G_GNUC_END_IGNORE_DEPRECATIONS |
134 | } |
135 | |
136 | if (embFile->checksum() && embFile->checksum()->getLength() > 0) { |
137 | attachment->checksum = g_string_new_len(init: embFile->checksum()->c_str(), len: embFile->checksum()->getLength()); |
138 | } |
139 | priv->obj_stream = embFile->streamObject()->copy(); |
140 | } else { |
141 | g_warning("Missing stream object for embedded file" ); |
142 | g_clear_object(&attachment); |
143 | } |
144 | |
145 | return attachment; |
146 | } |
147 | |
148 | /** |
149 | * poppler_attachment_get_checksum: |
150 | * @attachment: a #PopplerAttachment |
151 | * |
152 | * Returns: The attachment's checksum. |
153 | * |
154 | * Since: 20.09.0 |
155 | */ |
156 | const GString *poppler_attachment_get_checksum(PopplerAttachment *attachment) |
157 | { |
158 | return attachment->checksum; |
159 | } |
160 | |
161 | /** |
162 | * poppler_attachment_get_ctime: |
163 | * @attachment: a #PopplerAttachment |
164 | * |
165 | * Returns: (transfer none) (nullable): The attachment's creation date and time |
166 | * as a #GDateTime, or %NULL if the creation date and time is not available. |
167 | * |
168 | * Since: 20.09.0 |
169 | */ |
170 | GDateTime *poppler_attachment_get_ctime(PopplerAttachment *attachment) |
171 | { |
172 | return GET_PRIVATE(attachment)->ctime; |
173 | } |
174 | |
175 | /** |
176 | * poppler_attachment_get_description: |
177 | * @attachment: a #PopplerAttachment |
178 | * |
179 | * Returns: The attachment's descriptive text. |
180 | * |
181 | * Since: 20.09.0 |
182 | */ |
183 | const gchar *poppler_attachment_get_description(PopplerAttachment *attachment) |
184 | { |
185 | return attachment->description; |
186 | } |
187 | |
188 | /** |
189 | * poppler_attachment_get_mtime: |
190 | * @attachment: a #PopplerAttachment |
191 | * |
192 | * Returns: (transfer none) (nullable): The attachment's modification date and |
193 | * time as a #GDateTime, or %NULL if the modification date and time is not |
194 | * available. |
195 | * |
196 | * Since: 20.09.0 |
197 | */ |
198 | GDateTime *poppler_attachment_get_mtime(PopplerAttachment *attachment) |
199 | { |
200 | return GET_PRIVATE(attachment)->mtime; |
201 | } |
202 | |
203 | /** |
204 | * poppler_attachment_get_name: |
205 | * @attachment: a #PopplerAttachment |
206 | * |
207 | * Returns: The attachment's name. |
208 | * |
209 | * Since: 20.09.0 |
210 | */ |
211 | const gchar *poppler_attachment_get_name(PopplerAttachment *attachment) |
212 | { |
213 | return attachment->name; |
214 | } |
215 | |
216 | /** |
217 | * poppler_attachment_get_size: |
218 | * @attachment: a #PopplerAttachment |
219 | * |
220 | * Returns: The attachment's size. |
221 | * |
222 | * Since: 20.09.0 |
223 | */ |
224 | gsize poppler_attachment_get_size(PopplerAttachment *attachment) |
225 | { |
226 | return attachment->size; |
227 | } |
228 | |
229 | static gboolean save_helper(const gchar *buf, gsize count, gpointer data, GError **error) |
230 | { |
231 | FILE *f = (FILE *)data; |
232 | gsize n; |
233 | |
234 | n = fwrite(ptr: buf, size: 1, n: count, s: f); |
235 | if (n != count) { |
236 | int errsv = errno; |
237 | g_set_error(err: error, G_FILE_ERROR, code: g_file_error_from_errno(err_no: errsv), _("Error writing to image file: %s" ), g_strerror(errnum: errsv)); |
238 | return FALSE; |
239 | } |
240 | |
241 | return TRUE; |
242 | } |
243 | |
244 | /** |
245 | * poppler_attachment_save: |
246 | * @attachment: A #PopplerAttachment. |
247 | * @filename: name of file to save |
248 | * @error: (allow-none): return location for error, or %NULL. |
249 | * |
250 | * Saves @attachment to a file indicated by @filename. If @error is set, %FALSE |
251 | * will be returned. Possible errors include those in the #G_FILE_ERROR domain |
252 | * and whatever the save function generates. |
253 | * |
254 | * Return value: %TRUE, if the file successfully saved |
255 | **/ |
256 | gboolean poppler_attachment_save(PopplerAttachment *attachment, const char *filename, GError **error) |
257 | { |
258 | gboolean result; |
259 | FILE *f; |
260 | |
261 | g_return_val_if_fail(POPPLER_IS_ATTACHMENT(attachment), FALSE); |
262 | |
263 | f = openFile(path: filename, mode: "wb" ); |
264 | |
265 | if (f == nullptr) { |
266 | gchar *display_name = g_filename_display_name(filename); |
267 | g_set_error(err: error, G_FILE_ERROR, code: g_file_error_from_errno(errno), _("Failed to open '%s' for writing: %s" ), display_name, g_strerror(errno)); |
268 | g_free(mem: display_name); |
269 | return FALSE; |
270 | } |
271 | |
272 | result = poppler_attachment_save_to_callback(attachment, save_func: save_helper, user_data: f, error); |
273 | |
274 | if (fclose(stream: f) < 0) { |
275 | gchar *display_name = g_filename_display_name(filename); |
276 | g_set_error(err: error, G_FILE_ERROR, code: g_file_error_from_errno(errno), _("Failed to close '%s', all data may not have been saved: %s" ), display_name, g_strerror(errno)); |
277 | g_free(mem: display_name); |
278 | return FALSE; |
279 | } |
280 | |
281 | return result; |
282 | } |
283 | |
284 | #ifndef G_OS_WIN32 |
285 | |
286 | /** |
287 | * poppler_attachment_save_to_fd: |
288 | * @attachment: A #PopplerAttachment. |
289 | * @fd: a valid file descriptor open for writing |
290 | * @error: (allow-none): return location for error, or %NULL. |
291 | * |
292 | * Saves @attachment to a file referred to by @fd. If @error is set, %FALSE |
293 | * will be returned. Possible errors include those in the #G_FILE_ERROR domain |
294 | * and whatever the save function generates. |
295 | * Note that this function takes ownership of @fd; you must not operate on it |
296 | * again, nor close it. |
297 | * |
298 | * Return value: %TRUE, if the file successfully saved |
299 | * |
300 | * Since: 21.12.0 |
301 | **/ |
302 | gboolean poppler_attachment_save_to_fd(PopplerAttachment *attachment, int fd, GError **error) |
303 | { |
304 | gboolean result; |
305 | FILE *f; |
306 | |
307 | g_return_val_if_fail(POPPLER_IS_ATTACHMENT(attachment), FALSE); |
308 | g_return_val_if_fail(fd != -1, FALSE); |
309 | g_return_val_if_fail(error == nullptr || *error == nullptr, FALSE); |
310 | |
311 | f = fdopen(fd: fd, modes: "wb" ); |
312 | if (f == nullptr) { |
313 | int errsv = errno; |
314 | g_set_error(err: error, G_FILE_ERROR, code: g_file_error_from_errno(err_no: errsv), _("Failed to open FD %d for writing: %s" ), fd, g_strerror(errnum: errsv)); |
315 | close(fd: fd); |
316 | return FALSE; |
317 | } |
318 | |
319 | result = poppler_attachment_save_to_callback(attachment, save_func: save_helper, user_data: f, error); |
320 | |
321 | if (fclose(stream: f) < 0) { |
322 | int errsv = errno; |
323 | g_set_error(err: error, G_FILE_ERROR, code: g_file_error_from_errno(err_no: errsv), _("Failed to close FD %d, all data may not have been saved: %s" ), fd, g_strerror(errnum: errsv)); |
324 | return FALSE; |
325 | } |
326 | |
327 | return result; |
328 | } |
329 | |
330 | #endif /* !G_OS_WIN32 */ |
331 | |
332 | #define BUF_SIZE 1024 |
333 | |
334 | /** |
335 | * poppler_attachment_save_to_callback: |
336 | * @attachment: A #PopplerAttachment. |
337 | * @save_func: (scope call): a function that is called to save each block of data that the save routine generates. |
338 | * @user_data: user data to pass to the save function. |
339 | * @error: (allow-none): return location for error, or %NULL. |
340 | * |
341 | * Saves @attachment by feeding the produced data to @save_func. Can be used |
342 | * when you want to store the attachment to something other than a file, such as |
343 | * an in-memory buffer or a socket. If @error is set, %FALSE will be |
344 | * returned. Possible errors include those in the #G_FILE_ERROR domain and |
345 | * whatever the save function generates. |
346 | * |
347 | * Return value: %TRUE, if the save successfully completed |
348 | **/ |
349 | gboolean poppler_attachment_save_to_callback(PopplerAttachment *attachment, PopplerAttachmentSaveFunc save_func, gpointer user_data, GError **error) |
350 | { |
351 | PopplerAttachmentPrivate *priv; |
352 | Stream *stream; |
353 | gchar buf[BUF_SIZE]; |
354 | int i; |
355 | gboolean eof_reached = FALSE; |
356 | |
357 | g_return_val_if_fail(POPPLER_IS_ATTACHMENT(attachment), FALSE); |
358 | priv = GET_PRIVATE(attachment); |
359 | |
360 | stream = priv->obj_stream.getStream(); |
361 | stream->reset(); |
362 | |
363 | do { |
364 | int data; |
365 | |
366 | for (i = 0; i < BUF_SIZE; i++) { |
367 | data = stream->getChar(); |
368 | if (data == EOF) { |
369 | eof_reached = TRUE; |
370 | break; |
371 | } |
372 | buf[i] = data; |
373 | } |
374 | |
375 | if (i > 0) { |
376 | if (!(save_func)(buf, i, user_data, error)) { |
377 | return FALSE; |
378 | } |
379 | } |
380 | } while (!eof_reached); |
381 | |
382 | return TRUE; |
383 | } |
384 | |