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
38struct PopplerAttachmentPrivate
39{
40 Object obj_stream {};
41 GDateTime *mtime;
42 GDateTime *ctime;
43};
44
45static void poppler_attachment_finalize(GObject *obj);
46
47G_DEFINE_TYPE_WITH_PRIVATE(PopplerAttachment, poppler_attachment, G_TYPE_OBJECT)
48
49#define GET_PRIVATE(obj) ((PopplerAttachmentPrivate *)poppler_attachment_get_instance_private(obj))
50
51static void poppler_attachment_init(PopplerAttachment *attachment)
52{
53 void *place;
54
55 place = GET_PRIVATE(attachment);
56 new (place) PopplerAttachmentPrivate();
57}
58
59static void poppler_attachment_class_init(PopplerAttachmentClass *klass)
60{
61 G_OBJECT_CLASS(klass)->finalize = poppler_attachment_finalize;
62}
63
64static 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
97PopplerAttachment *_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 */
156const 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 */
170GDateTime *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 */
183const 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 */
198GDateTime *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 */
211const 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 */
224gsize poppler_attachment_get_size(PopplerAttachment *attachment)
225{
226 return attachment->size;
227}
228
229static 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 **/
256gboolean 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 **/
302gboolean 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 **/
349gboolean 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

source code of poppler/glib/poppler-attachment.cc