/* GIO - GLib Input, Output and Streaming Library * * SPDX-FileCopyrightText: 2009 Red Hat, Inc. * SPDX-FileCopyrightText: 2010 Christian Persch * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include "gbase64encoder.h" #include #include #include "gioenums.h" #include "gioenumtypes.h" #include "gioerror.h" #include "glib.h" #include "glibintl.h" /* See g_base64_encode_step docs in ../glib/gbase64.c */ #define BASE64_ENCODING_OUTPUT_SIZE(len, break_lines) \ (((len) / 3 + 1) * 4 + 4 + ((break_lines) ? ((((len) / 3 + 1) * 4 + 4) / 72 + 1) : 0)) /** * GBase64Encoder: * * `GBase64Encoder` is an implementation of [iface@Gio.Converter] * that converts data to base64 encoding. * * Since: 2.82 */ struct _GBase64Encoder { GObject parent_instance; gboolean break_lines; int state[2]; }; typedef enum { PROP_BREAK_LINES = 1, } GBase64EncoderProperty; static void g_base64_encoder_reset (GConverter *converter) { GBase64Encoder *encoder = G_BASE64_ENCODER (converter); encoder->state[0] = encoder->state[1] = 0; } static GConverterResult g_base64_encoder_convert (GConverter *converter, const void *inbuf, gsize inbuf_size, void *outbuf, gsize outbuf_size, GConverterFlags flags, gsize *bytes_read, gsize *bytes_written, GError **error) { GBase64Encoder *encoder = G_BASE64_ENCODER (converter); if (inbuf_size == 0 || (flags & G_CONVERTER_FLUSH)) { if (outbuf_size < (encoder->break_lines ? 5 : 4)) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NO_SPACE, "Need more output space"); return G_CONVERTER_ERROR; } *bytes_read = 0; *bytes_written = g_base64_encode_close (encoder->break_lines, outbuf, &encoder->state[0], &encoder->state[1]); if (flags & G_CONVERTER_FLUSH) return G_CONVERTER_FLUSHED; if (flags & G_CONVERTER_INPUT_AT_END) return G_CONVERTER_FINISHED; return G_CONVERTER_CONVERTED; } if (outbuf_size < BASE64_ENCODING_OUTPUT_SIZE (inbuf_size, encoder->break_lines)) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NO_SPACE, "Need more output space"); return G_CONVERTER_ERROR; } *bytes_read = inbuf_size; *bytes_written = g_base64_encode_step (inbuf, inbuf_size, encoder->break_lines, outbuf, &encoder->state[0], &encoder->state[1]); if (flags & G_CONVERTER_INPUT_AT_END) return G_CONVERTER_FINISHED; return G_CONVERTER_CONVERTED; } static void g_base64_encoder_iface_init (GConverterIface *iface) { iface->convert = g_base64_encoder_convert; iface->reset = g_base64_encoder_reset; } G_DEFINE_TYPE_WITH_CODE (GBase64Encoder, g_base64_encoder, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_CONVERTER, g_base64_encoder_iface_init)) static void g_base64_encoder_init (GBase64Encoder *encoder) { encoder->break_lines = FALSE; encoder->state[0] = encoder->state[1] = 0; } static void g_base64_encoder_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GBase64Encoder *encoder = G_BASE64_ENCODER (object); switch ((GBase64EncoderProperty) prop_id) { case PROP_BREAK_LINES: encoder->break_lines = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void g_base64_encoder_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GBase64Encoder *encoder = G_BASE64_ENCODER (object); switch ((GBase64EncoderProperty) prop_id) { case PROP_BREAK_LINES: g_value_set_boolean (value, encoder->break_lines); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void g_base64_encoder_class_init (GBase64EncoderClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->get_property = g_base64_encoder_get_property; gobject_class->set_property = g_base64_encoder_set_property; /** * GBase64Encoder:break-lines: * * Whether to break lines. * * This is typically used when putting base64-encoded data in emails. * It breaks the lines at 72 columns instead of putting all of the text on * the same line. This avoids problems with long lines in the email system. * * Since: 2.82 */ g_object_class_install_property (gobject_class, PROP_BREAK_LINES, g_param_spec_boolean ("break-lines", NULL, NULL, FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); } /** * g_base64_encoder_new: * @break_lines: whether to break long lines * * Creates a new `GBase64Encoder`. * * Setting @break_lines to `TRUE` is typically used when putting * base64-encoded data in emails. It breaks the lines at 72 columns * instead of putting all of the text on the same line. This avoids * problems with long lines in the email system. * * Returns: (transfer full): a new `GBase64Encoder` * * Since: 2.82 */ GConverter * g_base64_encoder_new (gboolean break_lines) { return g_object_new (G_TYPE_BASE64_ENCODER, "break-lines", break_lines, NULL); }