/* poppler-attachment.cc: glib wrapper for poppler * Copyright (C) 2006, Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ #include "config.h" #include #include #include "poppler.h" #include "poppler-private.h" #include /** * SECTION:poppler-attachment * @short_description: Attachments * @title: PopplerAttachment */ /* FIXME: We need to add gettext support sometime */ #define _(x) (x) struct PopplerAttachmentPrivate { Object obj_stream {}; GDateTime *mtime; GDateTime *ctime; }; static void poppler_attachment_finalize(GObject *obj); G_DEFINE_TYPE_WITH_PRIVATE(PopplerAttachment, poppler_attachment, G_TYPE_OBJECT) #define GET_PRIVATE(obj) ((PopplerAttachmentPrivate *)poppler_attachment_get_instance_private(obj)) static void poppler_attachment_init(PopplerAttachment *attachment) { void *place; place = GET_PRIVATE(attachment); new (place) PopplerAttachmentPrivate(); } static void poppler_attachment_class_init(PopplerAttachmentClass *klass) { G_OBJECT_CLASS(klass)->finalize = poppler_attachment_finalize; } static void poppler_attachment_finalize(GObject *obj) { PopplerAttachment *attachment; PopplerAttachmentPrivate *priv; attachment = (PopplerAttachment *)obj; priv = GET_PRIVATE(attachment); if (attachment->name) { g_free(attachment->name); } attachment->name = nullptr; if (attachment->description) { g_free(attachment->description); } attachment->description = nullptr; if (attachment->checksum) { g_string_free(attachment->checksum, TRUE); } attachment->checksum = nullptr; g_clear_pointer(&priv->mtime, g_date_time_unref); g_clear_pointer(&priv->ctime, g_date_time_unref); priv->~PopplerAttachmentPrivate(); G_OBJECT_CLASS(poppler_attachment_parent_class)->finalize(obj); } /* Public functions */ PopplerAttachment *_poppler_attachment_new(FileSpec *emb_file) { PopplerAttachment *attachment; PopplerAttachmentPrivate *priv; EmbFile *embFile; g_assert(emb_file != nullptr); attachment = (PopplerAttachment *)g_object_new(POPPLER_TYPE_ATTACHMENT, nullptr); priv = GET_PRIVATE(attachment); if (emb_file->getFileName()) { attachment->name = _poppler_goo_string_to_utf8(emb_file->getFileName()); } if (emb_file->getDescription()) { attachment->description = _poppler_goo_string_to_utf8(emb_file->getDescription()); } embFile = emb_file->getEmbeddedFile(); if (embFile != nullptr && embFile->streamObject()->isStream()) { attachment->size = embFile->size(); if (embFile->createDate()) { priv->ctime = _poppler_convert_pdf_date_to_date_time(embFile->createDate()); G_GNUC_BEGIN_IGNORE_DEPRECATIONS /* This will overflow on dates from after 2038. This field is * deprecated, only kept for backward compatibility. */ attachment->ctime = (GTime)g_date_time_to_unix(priv->ctime); G_GNUC_END_IGNORE_DEPRECATIONS } if (embFile->modDate()) { priv->mtime = _poppler_convert_pdf_date_to_date_time(embFile->modDate()); G_GNUC_BEGIN_IGNORE_DEPRECATIONS /* This will overflow on dates from after 2038. This field is * deprecated, only kept for backward compatibility. */ attachment->mtime = (GTime)g_date_time_to_unix(priv->mtime); G_GNUC_END_IGNORE_DEPRECATIONS } if (embFile->checksum() && embFile->checksum()->getLength() > 0) { attachment->checksum = g_string_new_len(embFile->checksum()->c_str(), embFile->checksum()->getLength()); } priv->obj_stream = embFile->streamObject()->copy(); } else { g_warning("Missing stream object for embedded file"); g_clear_object(&attachment); } return attachment; } /** * poppler_attachment_get_checksum: * @attachment: a #PopplerAttachment * * Returns: The attachment's checksum. * * Since: 20.09.0 */ const GString *poppler_attachment_get_checksum(PopplerAttachment *attachment) { return attachment->checksum; } /** * poppler_attachment_get_ctime: * @attachment: a #PopplerAttachment * * Returns: (transfer none) (nullable): The attachment's creation date and time * as a #GDateTime, or %NULL if the creation date and time is not available. * * Since: 20.09.0 */ GDateTime *poppler_attachment_get_ctime(PopplerAttachment *attachment) { return GET_PRIVATE(attachment)->ctime; } /** * poppler_attachment_get_description: * @attachment: a #PopplerAttachment * * Returns: The attachment's descriptive text. * * Since: 20.09.0 */ const gchar *poppler_attachment_get_description(PopplerAttachment *attachment) { return attachment->description; } /** * poppler_attachment_get_mtime: * @attachment: a #PopplerAttachment * * Returns: (transfer none) (nullable): The attachment's modification date and * time as a #GDateTime, or %NULL if the modification date and time is not * available. * * Since: 20.09.0 */ GDateTime *poppler_attachment_get_mtime(PopplerAttachment *attachment) { return GET_PRIVATE(attachment)->mtime; } /** * poppler_attachment_get_name: * @attachment: a #PopplerAttachment * * Returns: The attachment's name. * * Since: 20.09.0 */ const gchar *poppler_attachment_get_name(PopplerAttachment *attachment) { return attachment->name; } /** * poppler_attachment_get_size: * @attachment: a #PopplerAttachment * * Returns: The attachment's size. * * Since: 20.09.0 */ gsize poppler_attachment_get_size(PopplerAttachment *attachment) { return attachment->size; } static gboolean save_helper(const gchar *buf, gsize count, gpointer data, GError **error) { FILE *f = (FILE *)data; gsize n; n = fwrite(buf, 1, count, f); if (n != count) { int errsv = errno; g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errsv), _("Error writing to image file: %s"), g_strerror(errsv)); return FALSE; } return TRUE; } /** * poppler_attachment_save: * @attachment: A #PopplerAttachment. * @filename: name of file to save * @error: (allow-none): return location for error, or %NULL. * * Saves @attachment to a file indicated by @filename. If @error is set, %FALSE * will be returned. Possible errors include those in the #G_FILE_ERROR domain * and whatever the save function generates. * * Return value: %TRUE, if the file successfully saved **/ gboolean poppler_attachment_save(PopplerAttachment *attachment, const char *filename, GError **error) { gboolean result; FILE *f; g_return_val_if_fail(POPPLER_IS_ATTACHMENT(attachment), FALSE); f = openFile(filename, "wb"); if (f == nullptr) { gchar *display_name = g_filename_display_name(filename); g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errno), _("Failed to open '%s' for writing: %s"), display_name, g_strerror(errno)); g_free(display_name); return FALSE; } result = poppler_attachment_save_to_callback(attachment, save_helper, f, error); if (fclose(f) < 0) { gchar *display_name = g_filename_display_name(filename); g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errno), _("Failed to close '%s', all data may not have been saved: %s"), display_name, g_strerror(errno)); g_free(display_name); return FALSE; } return result; } #ifndef G_OS_WIN32 /** * poppler_attachment_save_to_fd: * @attachment: A #PopplerAttachment. * @fd: a valid file descriptor open for writing * @error: (allow-none): return location for error, or %NULL. * * Saves @attachment to a file referred to by @fd. If @error is set, %FALSE * will be returned. Possible errors include those in the #G_FILE_ERROR domain * and whatever the save function generates. * Note that this function takes ownership of @fd; you must not operate on it * again, nor close it. * * Return value: %TRUE, if the file successfully saved * * Since: 21.12.0 **/ gboolean poppler_attachment_save_to_fd(PopplerAttachment *attachment, int fd, GError **error) { gboolean result; FILE *f; g_return_val_if_fail(POPPLER_IS_ATTACHMENT(attachment), FALSE); g_return_val_if_fail(fd != -1, FALSE); g_return_val_if_fail(error == nullptr || *error == nullptr, FALSE); f = fdopen(fd, "wb"); if (f == nullptr) { int errsv = errno; g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errsv), _("Failed to open FD %d for writing: %s"), fd, g_strerror(errsv)); close(fd); return FALSE; } result = poppler_attachment_save_to_callback(attachment, save_helper, f, error); if (fclose(f) < 0) { int errsv = errno; g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errsv), _("Failed to close FD %d, all data may not have been saved: %s"), fd, g_strerror(errsv)); return FALSE; } return result; } #endif /* !G_OS_WIN32 */ #define BUF_SIZE 1024 /** * poppler_attachment_save_to_callback: * @attachment: A #PopplerAttachment. * @save_func: (scope call): a function that is called to save each block of data that the save routine generates. * @user_data: user data to pass to the save function. * @error: (allow-none): return location for error, or %NULL. * * Saves @attachment by feeding the produced data to @save_func. Can be used * when you want to store the attachment to something other than a file, such as * an in-memory buffer or a socket. If @error is set, %FALSE will be * returned. Possible errors include those in the #G_FILE_ERROR domain and * whatever the save function generates. * * Return value: %TRUE, if the save successfully completed **/ gboolean poppler_attachment_save_to_callback(PopplerAttachment *attachment, PopplerAttachmentSaveFunc save_func, gpointer user_data, GError **error) { PopplerAttachmentPrivate *priv; Stream *stream; gchar buf[BUF_SIZE]; int i; gboolean eof_reached = FALSE; g_return_val_if_fail(POPPLER_IS_ATTACHMENT(attachment), FALSE); priv = GET_PRIVATE(attachment); stream = priv->obj_stream.getStream(); stream->reset(); do { int data; for (i = 0; i < BUF_SIZE; i++) { data = stream->getChar(); if (data == EOF) { eof_reached = TRUE; break; } buf[i] = data; } if (i > 0) { if (!(save_func)(buf, i, user_data, error)) { return FALSE; } } } while (!eof_reached); return TRUE; }