diff options
| author | David Bremner <bremner@debian.org> | 2019-02-17 07:30:33 -0400 |
|---|---|---|
| committer | David Bremner <bremner@debian.org> | 2019-02-17 07:30:33 -0400 |
| commit | f7130468d27c4f37d45e6aa60baacfc3329ccff4 (patch) | |
| tree | f26a901f6e28185d60200c9111de30e1c15b4996 /util | |
Import notmuch_0.28.2.orig.tar.gz
[dgit import orig notmuch_0.28.2.orig.tar.gz]
Diffstat (limited to 'util')
| -rw-r--r-- | util/Makefile | 5 | ||||
| -rw-r--r-- | util/Makefile.local | 16 | ||||
| -rw-r--r-- | util/crypto.c | 221 | ||||
| -rw-r--r-- | util/crypto.h | 37 | ||||
| -rw-r--r-- | util/error_util.c | 40 | ||||
| -rw-r--r-- | util/error_util.h | 47 | ||||
| -rw-r--r-- | util/gmime-extra.c | 227 | ||||
| -rw-r--r-- | util/gmime-extra.h | 103 | ||||
| -rw-r--r-- | util/hex-escape.c | 159 | ||||
| -rw-r--r-- | util/hex-escape.h | 41 | ||||
| -rw-r--r-- | util/string-util.c | 270 | ||||
| -rw-r--r-- | util/string-util.h | 86 | ||||
| -rw-r--r-- | util/talloc-extra.c | 14 | ||||
| -rw-r--r-- | util/talloc-extra.h | 18 | ||||
| -rw-r--r-- | util/util.c | 24 | ||||
| -rw-r--r-- | util/util.h | 29 | ||||
| -rw-r--r-- | util/xutil.c | 139 | ||||
| -rw-r--r-- | util/xutil.h | 52 | ||||
| -rw-r--r-- | util/zlib-extra.c | 85 | ||||
| -rw-r--r-- | util/zlib-extra.h | 25 |
20 files changed, 1638 insertions, 0 deletions
diff --git a/util/Makefile b/util/Makefile new file mode 100644 index 00000000..fa25832e --- /dev/null +++ b/util/Makefile @@ -0,0 +1,5 @@ +all: + $(MAKE) -C .. all + +.DEFAULT: + $(MAKE) -C .. $@ diff --git a/util/Makefile.local b/util/Makefile.local new file mode 100644 index 00000000..ba03230e --- /dev/null +++ b/util/Makefile.local @@ -0,0 +1,16 @@ +# -*- makefile -*- + +dir := util +extra_cflags += -I$(srcdir)/$(dir) + +libnotmuch_util_c_srcs := $(dir)/xutil.c $(dir)/error_util.c $(dir)/hex-escape.c \ + $(dir)/string-util.c $(dir)/talloc-extra.c $(dir)/zlib-extra.c \ + $(dir)/util.c $(dir)/gmime-extra.c $(dir)/crypto.c + +libnotmuch_util_modules := $(libnotmuch_util_c_srcs:.c=.o) + +$(dir)/libnotmuch_util.a: $(libnotmuch_util_modules) + $(call quiet,AR) rcs $@ $^ + +SRCS := $(SRCS) $(libnotmuch_util_c_srcs) +CLEAN := $(CLEAN) $(libnotmuch_util_modules) $(dir)/libnotmuch_util.a diff --git a/util/crypto.c b/util/crypto.c new file mode 100644 index 00000000..9d3b6dad --- /dev/null +++ b/util/crypto.c @@ -0,0 +1,221 @@ +/* notmuch - Not much of an email program, (just index and search) + * + * Copyright © 2012 Jameson Rollins + * + * 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 3 of the License, 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, see https://www.gnu.org/licenses/ . + * + * Authors: Jameson Rollins <jrollins@finestructure.net> + */ + +#include "crypto.h" +#include <strings.h> +#define unused(x) x __attribute__ ((unused)) + +#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr[0])) + +#if (GMIME_MAJOR_VERSION < 3) +/* Create or pass on a GPG context (GMime 2.6) */ +static notmuch_status_t +get_gpg_context (_notmuch_crypto_t *crypto, GMimeCryptoContext **ctx) +{ + if (ctx == NULL || crypto == NULL) + return NOTMUCH_STATUS_NULL_POINTER; + + if (crypto->gpgctx) { + *ctx = crypto->gpgctx; + return NOTMUCH_STATUS_SUCCESS; + } + + /* TODO: GMimePasswordRequestFunc */ + crypto->gpgctx = g_mime_gpg_context_new (NULL, crypto->gpgpath ? crypto->gpgpath : "gpg"); + if (! crypto->gpgctx) { + return NOTMUCH_STATUS_FAILED_CRYPTO_CONTEXT_CREATION; + } + + g_mime_gpg_context_set_use_agent ((GMimeGpgContext *) crypto->gpgctx, true); + g_mime_gpg_context_set_always_trust ((GMimeGpgContext *) crypto->gpgctx, false); + + *ctx = crypto->gpgctx; + return NOTMUCH_STATUS_SUCCESS; +} + +/* Create or pass on a PKCS7 context (GMime 2.6) */ +static notmuch_status_t +get_pkcs7_context (_notmuch_crypto_t *crypto, GMimeCryptoContext **ctx) +{ + if (ctx == NULL || crypto == NULL) + return NOTMUCH_STATUS_NULL_POINTER; + + if (crypto->pkcs7ctx) { + *ctx = crypto->pkcs7ctx; + return NOTMUCH_STATUS_SUCCESS; + } + + /* TODO: GMimePasswordRequestFunc */ + crypto->pkcs7ctx = g_mime_pkcs7_context_new (NULL); + if (! crypto->pkcs7ctx) { + return NOTMUCH_STATUS_FAILED_CRYPTO_CONTEXT_CREATION; + } + + g_mime_pkcs7_context_set_always_trust ((GMimePkcs7Context *) crypto->pkcs7ctx, + false); + + *ctx = crypto->pkcs7ctx; + return NOTMUCH_STATUS_SUCCESS; +} +static const struct { + const char *protocol; + notmuch_status_t (*get_context) (_notmuch_crypto_t *crypto, GMimeCryptoContext **ctx); +} protocols[] = { + { + .protocol = "application/pgp-signature", + .get_context = get_gpg_context, + }, + { + .protocol = "application/pgp-encrypted", + .get_context = get_gpg_context, + }, + { + .protocol = "application/pkcs7-signature", + .get_context = get_pkcs7_context, + }, + { + .protocol = "application/x-pkcs7-signature", + .get_context = get_pkcs7_context, + }, +}; + +/* for the specified protocol return the context pointer (initializing + * if needed) */ +notmuch_status_t +_notmuch_crypto_get_gmime_ctx_for_protocol (_notmuch_crypto_t *crypto, + const char *protocol, + GMimeCryptoContext **ctx) +{ + if (! protocol) + return NOTMUCH_STATUS_MALFORMED_CRYPTO_PROTOCOL; + + /* As per RFC 1847 section 2.1: "the [protocol] value token is + * comprised of the type and sub-type tokens of the Content-Type". + * As per RFC 1521 section 2: "Content-Type values, subtypes, and + * parameter names as defined in this document are + * case-insensitive." Thus, we use strcasecmp for the protocol. + */ + for (size_t i = 0; i < ARRAY_SIZE (protocols); i++) { + if (strcasecmp (protocol, protocols[i].protocol) == 0) + return protocols[i].get_context (crypto, ctx); + } + + return NOTMUCH_STATUS_UNKNOWN_CRYPTO_PROTOCOL; +} + +void +_notmuch_crypto_cleanup (_notmuch_crypto_t *crypto) +{ + if (crypto->gpgctx) { + g_object_unref (crypto->gpgctx); + crypto->gpgctx = NULL; + } + + if (crypto->pkcs7ctx) { + g_object_unref (crypto->pkcs7ctx); + crypto->pkcs7ctx = NULL; + } +} +#else +void _notmuch_crypto_cleanup (unused(_notmuch_crypto_t *crypto)) +{ +} +#endif + +GMimeObject * +_notmuch_crypto_decrypt (bool *attempted, + notmuch_decryption_policy_t decrypt, + notmuch_message_t *message, + g_mime_3_unused(GMimeCryptoContext* crypto_ctx), + GMimeMultipartEncrypted *part, + GMimeDecryptResult **decrypt_result, + GError **err) +{ + GMimeObject *ret = NULL; + if (decrypt == NOTMUCH_DECRYPT_FALSE) + return NULL; + + /* the versions of notmuch that can support session key decryption */ +#if HAVE_GMIME_SESSION_KEYS + if (message) { + notmuch_message_properties_t *list = NULL; + + for (list = notmuch_message_get_properties (message, "session-key", TRUE); + notmuch_message_properties_valid (list); notmuch_message_properties_move_to_next (list)) { + if (err && *err) { + g_error_free (*err); + *err = NULL; + } + if (attempted) + *attempted = true; +#if (GMIME_MAJOR_VERSION < 3) + ret = g_mime_multipart_encrypted_decrypt_session (part, + crypto_ctx, + notmuch_message_properties_value (list), + decrypt_result, err); +#else + ret = g_mime_multipart_encrypted_decrypt (part, + GMIME_DECRYPT_NONE, + notmuch_message_properties_value (list), + decrypt_result, err); +#endif + if (ret) + break; + } + if (list) + notmuch_message_properties_destroy (list); + if (ret) + return ret; + } +#endif + + if (err && *err) { + g_error_free (*err); + *err = NULL; + } + + if (decrypt == NOTMUCH_DECRYPT_AUTO) + return ret; + + if (attempted) + *attempted = true; +#if (GMIME_MAJOR_VERSION < 3) +#if HAVE_GMIME_SESSION_KEYS + gboolean oldgetsk = g_mime_crypto_context_get_retrieve_session_key (crypto_ctx); + gboolean newgetsk = (decrypt == NOTMUCH_DECRYPT_TRUE && decrypt_result); + if (newgetsk != oldgetsk) + /* This could return an error, but we can't do anything about it, so ignore it */ + g_mime_crypto_context_set_retrieve_session_key (crypto_ctx, newgetsk, NULL); +#endif + ret = g_mime_multipart_encrypted_decrypt(part, crypto_ctx, + decrypt_result, err); +#if HAVE_GMIME_SESSION_KEYS + if (newgetsk != oldgetsk) + g_mime_crypto_context_set_retrieve_session_key (crypto_ctx, oldgetsk, NULL); +#endif +#else + GMimeDecryptFlags flags = GMIME_DECRYPT_NONE; + if (decrypt == NOTMUCH_DECRYPT_TRUE && decrypt_result) + flags |= GMIME_DECRYPT_EXPORT_SESSION_KEY; + ret = g_mime_multipart_encrypted_decrypt(part, flags, NULL, + decrypt_result, err); +#endif + return ret; +} diff --git a/util/crypto.h b/util/crypto.h new file mode 100644 index 00000000..c384601c --- /dev/null +++ b/util/crypto.h @@ -0,0 +1,37 @@ +#ifndef _CRYPTO_H +#define _CRYPTO_H + +#include <stdbool.h> +#include "gmime-extra.h" +#include "notmuch.h" + +typedef struct _notmuch_crypto { + bool verify; + notmuch_decryption_policy_t decrypt; +#if (GMIME_MAJOR_VERSION < 3) + GMimeCryptoContext* gpgctx; + GMimeCryptoContext* pkcs7ctx; + const char *gpgpath; +#endif +} _notmuch_crypto_t; + +GMimeObject * +_notmuch_crypto_decrypt (bool *attempted, + notmuch_decryption_policy_t decrypt, + notmuch_message_t *message, + GMimeCryptoContext* crypto_ctx, + GMimeMultipartEncrypted *part, + GMimeDecryptResult **decrypt_result, + GError **err); + +#if (GMIME_MAJOR_VERSION < 3) +notmuch_status_t +_notmuch_crypto_get_gmime_ctx_for_protocol (_notmuch_crypto_t *crypto, + const char *protocol, + GMimeCryptoContext **ctx); +#endif + +void +_notmuch_crypto_cleanup (_notmuch_crypto_t *crypto); + +#endif diff --git a/util/error_util.c b/util/error_util.c new file mode 100644 index 00000000..e64162c7 --- /dev/null +++ b/util/error_util.c @@ -0,0 +1,40 @@ +/* error_util.c - internal error utilities for notmuch. + * + * Copyright © 2009 Carl Worth + * + * 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 3 of the License, 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, see https://www.gnu.org/licenses/ . + * + * Author: Carl Worth <cworth@cworth.org> + */ + +#include <stdlib.h> +#include <stdarg.h> +#include <stdio.h> + +#include "error_util.h" + +void +_internal_error (const char *format, ...) +{ + va_list va_args; + + va_start (va_args, format); + + fprintf (stderr, "Internal error: "); + vfprintf (stderr, format, va_args); + + va_end (va_args); + exit (1); +} + diff --git a/util/error_util.h b/util/error_util.h new file mode 100644 index 00000000..4bb338a2 --- /dev/null +++ b/util/error_util.h @@ -0,0 +1,47 @@ +/* error_util.h - Provide the INTERNAL_ERROR macro + * + * Copyright © 2009 Carl Worth + * + * 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 3 of the License, 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, see https://www.gnu.org/licenses/ . + * + * Author: Carl Worth <cworth@cworth.org> + */ + +#ifndef ERROR_UTIL_H +#define ERROR_UTIL_H + +#include <talloc.h> + +#include "function-attributes.h" + +/* There's no point in continuing when we've detected that we've done + * something wrong internally (as opposed to the user passing in a + * bogus value). + * + * Note that PRINTF_ATTRIBUTE comes from talloc.h + */ +void +_internal_error (const char *format, ...) PRINTF_ATTRIBUTE (1, 2) NORETURN_ATTRIBUTE; + +/* There's no point in continuing when we've detected that we've done + * something wrong internally (as opposed to the user passing in a + * bogus value). + * + * Note that __location__ comes from talloc.h. + */ +#define INTERNAL_ERROR(format, ...) \ + _internal_error (format " (%s).\n", \ + ##__VA_ARGS__, __location__) + +#endif diff --git a/util/gmime-extra.c b/util/gmime-extra.c new file mode 100644 index 00000000..bc1e3c4d --- /dev/null +++ b/util/gmime-extra.c @@ -0,0 +1,227 @@ +#include "gmime-extra.h" +#include <string.h> + +GMimeStream * +g_mime_stream_stdout_new() +{ + GMimeStream *stream_stdout = NULL; + GMimeStream *stream_buffered = NULL; + + stream_stdout = g_mime_stream_pipe_new (STDOUT_FILENO); + if (!stream_stdout) + return NULL; + + g_mime_stream_pipe_set_owner (GMIME_STREAM_PIPE (stream_stdout), FALSE); + + stream_buffered = g_mime_stream_buffer_new (stream_stdout, GMIME_STREAM_BUFFER_BLOCK_WRITE); + + g_object_unref (stream_stdout); + + return stream_buffered; +} + +/** + * copy a glib string into a talloc context, and free it. + */ +static char* +g_string_talloc_strdup (void *ctx, char *g_string) +{ + char *new_str = talloc_strdup (ctx, g_string); + g_free (g_string); + return new_str; +} + +#if (GMIME_MAJOR_VERSION < 3) + +const char * +g_mime_certificate_get_valid_userid (GMimeCertificate *cert) +{ + /* output user id only if validity is FULL or ULTIMATE. */ + /* note that gmime 2.6 is using the term "trust" here, which + * is WRONG. It's actually user id "validity". */ + const char *name = g_mime_certificate_get_name (cert); + if (name == NULL) + return name; + GMimeCertificateTrust trust = g_mime_certificate_get_trust (cert); + if (trust == GMIME_CERTIFICATE_TRUST_FULLY || trust == GMIME_CERTIFICATE_TRUST_ULTIMATE) + return name; + return NULL; +} + +char * +g_mime_message_get_address_string (GMimeMessage *message, GMimeRecipientType type) +{ + InternetAddressList *list = g_mime_message_get_recipients (message, type); + return internet_address_list_to_string (list, 0); +} + +inline InternetAddressList * +g_mime_message_get_addresses (GMimeMessage *message, GMimeRecipientType type) +{ + return g_mime_message_get_recipients (message, type); +} + +char * +g_mime_message_get_date_string (void *ctx, GMimeMessage *message) +{ + char *date = g_mime_message_get_date_as_string (message); + return g_string_talloc_strdup (ctx, date); +} + +InternetAddressList * +g_mime_message_get_from (GMimeMessage *message) +{ + return internet_address_list_parse_string (g_mime_message_get_sender (message)); +} + +const char * +g_mime_message_get_from_string (GMimeMessage *message) { + return g_mime_message_get_sender (message); +} + +InternetAddressList * +g_mime_message_get_reply_to_list (GMimeMessage *message) +{ + const char *reply_to; + + reply_to = g_mime_message_get_reply_to (message); + if (reply_to && *reply_to) + return internet_address_list_parse_string (reply_to); + else + return NULL; +} + +/** + * return talloc allocated reply-to string + */ +char * +g_mime_message_get_reply_to_string (void *ctx, GMimeMessage *message) +{ + return talloc_strdup(ctx, g_mime_message_get_reply_to (message)); +} + +gboolean +g_mime_signature_status_good (GMimeSignatureStatus status) { + return (status == GMIME_SIGNATURE_STATUS_GOOD); +} + +gboolean +g_mime_signature_status_bad (GMimeSignatureStatus status) { + return (status == GMIME_SIGNATURE_STATUS_BAD); +} + +gboolean +g_mime_signature_status_error (GMimeSignatureError error) { + return (error != GMIME_SIGNATURE_ERROR_NONE); +} + +gint64 +g_mime_utils_header_decode_date_unix (const char *date) { + return (gint64) g_mime_utils_header_decode_date (date, NULL); +} + +#else /* GMime >= 3.0 */ + +const char * +g_mime_certificate_get_valid_userid (GMimeCertificate *cert) +{ + /* output user id only if validity is FULL or ULTIMATE. */ + const char *uid = g_mime_certificate_get_user_id (cert); + if (uid == NULL) + return uid; + GMimeValidity validity = g_mime_certificate_get_id_validity (cert); + if (validity == GMIME_VALIDITY_FULL || validity == GMIME_VALIDITY_ULTIMATE) + return uid; + return NULL; +} + +const char* +g_mime_certificate_get_fpr16 (GMimeCertificate *cert) { + const char *fpr = g_mime_certificate_get_fingerprint (cert); + if (!fpr || strlen (fpr) < 16) + return fpr; + + return fpr + (strlen (fpr) - 16); +} + +char * +g_mime_message_get_address_string (GMimeMessage *message, GMimeAddressType type) +{ + InternetAddressList *list = g_mime_message_get_addresses (message, type); + return internet_address_list_to_string (list, NULL, 0); +} + +char * +g_mime_message_get_date_string (void *ctx, GMimeMessage *message) +{ + GDateTime* parsed_date = g_mime_message_get_date (message); + if (parsed_date) { + char *date = g_mime_utils_header_format_date (parsed_date); + return g_string_talloc_strdup (ctx, date); + } else { + return talloc_strdup(ctx, "Thu, 01 Jan 1970 00:00:00 +0000"); + } +} + +InternetAddressList * +g_mime_message_get_reply_to_list(GMimeMessage *message) +{ + return g_mime_message_get_reply_to (message); +} + +const char * +g_mime_message_get_from_string (GMimeMessage *message) +{ + return g_mime_object_get_header (GMIME_OBJECT (message), "From"); +} + +char * +g_mime_message_get_reply_to_string (void *ctx, GMimeMessage *message) +{ + InternetAddressList *list = g_mime_message_get_reply_to (message); + return g_string_talloc_strdup (ctx, internet_address_list_to_string (list, NULL, 0)); +} + +void +g_mime_parser_set_scan_from (GMimeParser *parser, gboolean flag) +{ + g_mime_parser_set_format (parser, flag ? GMIME_FORMAT_MBOX : GMIME_FORMAT_MESSAGE); +} + +/* In GMime 3.0, status GOOD and VALID both imply something about the + * validity of the UIDs attached to the signing key. This forces us to + * use following somewhat relaxed definition of a "good" signature to + * preserve current notmuch semantics. + */ + +gboolean +g_mime_signature_status_good (GMimeSignatureStatus status) { + return ((status & (GMIME_SIGNATURE_STATUS_RED | GMIME_SIGNATURE_STATUS_ERROR_MASK)) == 0); +} + +gboolean +g_mime_signature_status_bad (GMimeSignatureStatus status) { + return (status & GMIME_SIGNATURE_STATUS_RED); +} + +gboolean +g_mime_signature_status_error (GMimeSignatureStatus status) { + return (status & GMIME_SIGNATURE_STATUS_ERROR_MASK); +} + +gint64 +g_mime_utils_header_decode_date_unix (const char *date) { + GDateTime* parsed_date = g_mime_utils_header_decode_date (date); + time_t ret; + + if (parsed_date) { + ret = g_date_time_to_unix (parsed_date); + g_date_time_unref (parsed_date); + } else { + ret = 0; + } + + return ret; +} + +#endif diff --git a/util/gmime-extra.h b/util/gmime-extra.h new file mode 100644 index 00000000..ca822b8c --- /dev/null +++ b/util/gmime-extra.h @@ -0,0 +1,103 @@ +#ifndef _GMIME_EXTRA_H +#define _GMIME_EXTRA_H +#include <gmime/gmime.h> + +GMimeStream *g_mime_stream_stdout_new(void); + +#include <talloc.h> + + +#if (GMIME_MAJOR_VERSION < 3) + +#define GMIME_ADDRESS_TYPE_TO GMIME_RECIPIENT_TYPE_TO +#define GMIME_ADDRESS_TYPE_CC GMIME_RECIPIENT_TYPE_CC +#define GMIME_ADDRESS_TYPE_BCC GMIME_RECIPIENT_TYPE_BCC + +#define g_mime_2_6_unref(obj) g_object_unref (obj) +#define g_mime_3_unused(arg) arg +#define g_mime_certificate_get_fpr16(cert) g_mime_certificate_get_key_id (cert) +#else /* GMime >= 3.0 */ + +#define GMIME_ENABLE_RFC_2047_WORKAROUNDS 0xdeadbeef +#define g_mime_content_type_to_string(c) g_mime_content_type_get_mime_type (c) +#define g_mime_filter_crlf_new(encode,dots) g_mime_filter_dos2unix_new (FALSE) +#define g_mime_gpg_context_new(func,path) g_mime_gpg_context_new () +#define g_mime_gpg_context_set_use_agent(ctx,val) /*ignore*/ +#define g_mime_gpg_context_set_always_trust(ctx,val) /*ignore*/ +#define g_mime_init(flags) g_mime_init() +#define g_mime_message_add_recipient(m,t,n,a) g_mime_message_add_mailbox (m,t,n,a) +#define g_mime_message_set_subject(m,s) g_mime_message_set_subject(m,s,NULL) +#define g_mime_multipart_signed_verify(mps,ctx,err) g_mime_multipart_signed_verify(mps, GMIME_ENCRYPT_NONE, err) +#define g_mime_object_write_to_stream(o,s) g_mime_object_write_to_stream (o,NULL,s) +#define g_mime_object_set_header(o,h,v) g_mime_object_set_header (o,h,v,NULL) +#define g_mime_parser_construct_message(p) g_mime_parser_construct_message (p, g_mime_parser_options_get_default ()) +#define g_mime_part_get_content_object(p) g_mime_part_get_content (p) +#define g_mime_pkcs7_context_new(arg) g_mime_pkcs7_context_new() +#define g_mime_pkcs7_context_set_always_trust(ctx,val) /*ignore*/ +#define g_mime_signature_get_errors(sig) g_mime_signature_get_status (sig) +#define g_mime_utils_header_decode_text(txt) g_mime_utils_header_decode_text (NULL, txt) +#define internet_address_to_string(ia,encode) internet_address_to_string (ia,NULL,encode) +#define internet_address_list_parse_string(str) internet_address_list_parse (NULL,str) + +typedef GMimeAddressType GMimeRecipientType; + +typedef GMimeSignatureStatus GMimeSignatureError; + +#define g_mime_2_6_unref(obj) /*ignore*/ +#define g_mime_3_unused(arg) unused(arg) +#endif + +/** + * Get last 16 hex digits of fingerprint ("keyid") + */ +const char *g_mime_certificate_get_fpr16 (GMimeCertificate *cert); +/** + * Return the contents of the appropriate address header as a string + * Should be freed using g_free + */ +char *g_mime_message_get_address_string (GMimeMessage *message, GMimeRecipientType type); + +InternetAddressList * g_mime_message_get_addresses (GMimeMessage *message, GMimeRecipientType type); + +/** + * return talloc allocated date string + */ + +char *g_mime_message_get_date_string (void *ctx, GMimeMessage *message); + +/** + * glib allocated list of From: addresses + */ + +InternetAddressList * g_mime_message_get_from (GMimeMessage *message); + + +/** + * return string for From: address + * (owned by gmime) + */ +const char * g_mime_message_get_from_string (GMimeMessage *message); + +InternetAddressList * g_mime_message_get_reply_to_list (GMimeMessage *message); + +/** + * return talloc allocated reply-to string + */ +char * g_mime_message_get_reply_to_string (void *ctx, GMimeMessage *message); + +void g_mime_parser_set_scan_from (GMimeParser *parser, gboolean flag); + +gboolean g_mime_signature_status_good (GMimeSignatureStatus status); + +gboolean g_mime_signature_status_bad (GMimeSignatureStatus status); + +gboolean g_mime_signature_status_error (GMimeSignatureError status); + +gint64 g_mime_utils_header_decode_date_unix (const char *date); + +/** + * Return string for valid User ID (or NULL if no valid User ID exists) + */ +const char * g_mime_certificate_get_valid_userid (GMimeCertificate *cert); + +#endif diff --git a/util/hex-escape.c b/util/hex-escape.c new file mode 100644 index 00000000..8883ff90 --- /dev/null +++ b/util/hex-escape.c @@ -0,0 +1,159 @@ +/* hex-escape.c - Manage encoding and decoding of byte strings into path names + * + * Copyright (c) 2011 David Bremner + * + * 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 3 of the License, 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, see https://www.gnu.org/licenses/ . + * + * Author: David Bremner <david@tethera.net> + */ + +#include <assert.h> +#include <string.h> +#include <talloc.h> +#include <ctype.h> +#include "error_util.h" +#include "hex-escape.h" + +static const char *output_charset = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-_@=.,"; + +static const char escape_char = '%'; + +static int +is_output (char c) +{ + return (strchr (output_charset, c) != NULL); +} + +static int +maybe_realloc (void *ctx, size_t needed, char **out, size_t *out_size) +{ + if (*out_size < needed) { + + if (*out == NULL) + *out = talloc_size (ctx, needed); + else + *out = talloc_realloc (ctx, *out, char, needed); + + if (*out == NULL) + return 0; + + *out_size = needed; + } + return 1; +} + +hex_status_t +hex_encode (void *ctx, const char *in, char **out, size_t *out_size) +{ + + const char *p; + char *q; + + size_t needed = 1; /* for the NUL */ + + assert (ctx); assert (in); assert (out); assert (out_size); + + for (p = in; *p; p++) { + needed += is_output (*p) ? 1 : 3; + } + + if (*out == NULL) + *out_size = 0; + + if (!maybe_realloc (ctx, needed, out, out_size)) + return HEX_OUT_OF_MEMORY; + + q = *out; + p = in; + + while (*p) { + if (is_output (*p)) { + *q++ = *p++; + } else { + sprintf (q, "%%%02x", (unsigned char)*p++); + q += 3; + } + } + + *q = '\0'; + return HEX_SUCCESS; +} + +/* Hex decode 'in' to 'out'. + * + * This must succeed for in == out to support hex_decode_inplace(). + */ +static hex_status_t +hex_decode_internal (const char *in, unsigned char *out) +{ + char buf[3]; + + while (*in) { + if (*in == escape_char) { + char *endp; + + /* This also handles unexpected end-of-string. */ + if (!isxdigit ((unsigned char) in[1]) || + !isxdigit ((unsigned char) in[2])) + return HEX_SYNTAX_ERROR; + + buf[0] = in[1]; + buf[1] = in[2]; + buf[2] = '\0'; + + *out = strtoul (buf, &endp, 16); + + if (endp != buf + 2) + return HEX_SYNTAX_ERROR; + + in += 3; + out++; + } else { + *out++ = *in++; + } + } + + *out = '\0'; + + return HEX_SUCCESS; +} + +hex_status_t +hex_decode_inplace (char *s) +{ + /* A decoded string is never longer than the encoded one, so it is + * safe to decode a string onto itself. */ + return hex_decode_internal (s, (unsigned char *) s); +} + +hex_status_t +hex_decode (void *ctx, const char *in, char **out, size_t * out_size) +{ + const char *p; + size_t needed = 1; /* for the NUL */ + + assert (ctx); assert (in); assert (out); assert (out_size); + + for (p = in; *p; p++) + if ((p[0] == escape_char) && isxdigit (p[1]) && isxdigit (p[2])) + needed -= 1; + else + needed += 1; + + if (!maybe_realloc (ctx, needed, out, out_size)) + return HEX_OUT_OF_MEMORY; + + return hex_decode_internal (in, (unsigned char *) *out); +} diff --git a/util/hex-escape.h b/util/hex-escape.h new file mode 100644 index 00000000..5182042e --- /dev/null +++ b/util/hex-escape.h @@ -0,0 +1,41 @@ +#ifndef _HEX_ESCAPE_H +#define _HEX_ESCAPE_H + +typedef enum hex_status { + HEX_SUCCESS = 0, + HEX_SYNTAX_ERROR, + HEX_OUT_OF_MEMORY +} hex_status_t; + +/* + * The API for hex_encode() and hex_decode() is modelled on that for + * getline. + * + * If 'out' points to a NULL pointer a char array of the appropriate + * size is allocated using talloc, and out_size is updated. + * + * If 'out' points to a non-NULL pointer, it assumed to describe an + * existing char array, with the size given in *out_size. This array + * may be resized by talloc_realloc if needed; in this case *out_size + * will also be updated. + * + * Note that it is an error to pass a NULL pointer for any parameter + * of these routines. + */ + +hex_status_t +hex_encode (void *talloc_ctx, const char *in, char **out, + size_t *out_size); + +hex_status_t +hex_decode (void *talloc_ctx, const char *in, char **out, + size_t *out_size); + +/* + * Non-allocating hex decode to decode 's' in-place. The length of the + * result is always equal to or shorter than the length of the + * original. + */ +hex_status_t +hex_decode_inplace (char *s); +#endif diff --git a/util/string-util.c b/util/string-util.c new file mode 100644 index 00000000..fc2058e0 --- /dev/null +++ b/util/string-util.c @@ -0,0 +1,270 @@ +/* string-util.c - Extra or enhanced routines for null terminated strings. + * + * Copyright (c) 2012 Jani Nikula + * + * 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 3 of the License, 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, see https://www.gnu.org/licenses/ . + * + * Author: Jani Nikula <jani@nikula.org> + */ + + +#include "string-util.h" +#include "talloc.h" + +#include <ctype.h> +#include <errno.h> + +char * +strtok_len (char *s, const char *delim, size_t *len) +{ + /* skip initial delims */ + s += strspn (s, delim); + + /* length of token */ + *len = strcspn (s, delim); + + return *len ? s : NULL; +} + +const char * +strtok_len_c (const char *s, const char *delim, size_t *len) +{ + /* strtok_len is already const-safe, but we can't express both + * versions in the C type system. */ + return strtok_len ((char*)s, delim, len); +} + +char * +sanitize_string (const void *ctx, const char *str) +{ + char *out, *loop; + + if (! str) + return NULL; + + out = talloc_strdup (ctx, str); + if (! out) + return NULL; + + for (loop = out; *loop; loop++) { + if (*loop == '\t' || *loop == '\n') + *loop = ' '; + else if ((unsigned char)(*loop) < 32) + *loop = '?'; + } + + return out; +} + +static int +is_unquoted_terminator (unsigned char c) +{ + return c == 0 || c <= ' ' || c == ')'; +} + +int +make_boolean_term (void *ctx, const char *prefix, const char *term, + char **buf, size_t *len) +{ + const char *in; + char *out; + size_t needed = 3; + int need_quoting = 0; + + /* Do we need quoting? To be paranoid, we quote anything + * containing a quote or '(', even though these only matter at the + * beginning, and anything containing non-ASCII text. */ + if (! term[0]) + need_quoting = 1; + for (in = term; *in && !need_quoting; in++) + if (is_unquoted_terminator (*in) || *in == '"' || *in == '(' + || (unsigned char)*in > 127) + need_quoting = 1; + + if (need_quoting) + for (in = term; *in; in++) + needed += (*in == '"') ? 2 : 1; + else + needed = strlen (term) + 1; + + /* Reserve space for the prefix */ + if (prefix) + needed += strlen (prefix) + 1; + + if ((*buf == NULL) || (needed > *len)) { + *len = 2 * needed; + *buf = talloc_realloc (ctx, *buf, char, *len); + } + + if (! *buf) { + errno = ENOMEM; + return -1; + } + + out = *buf; + + /* Copy in the prefix */ + if (prefix) { + strcpy (out, prefix); + out += strlen (prefix); + *out++ = ':'; + } + + if (! need_quoting) { + strcpy (out, term); + return 0; + } + + /* Quote term by enclosing it in double quotes and doubling any + * internal double quotes. */ + *out++ = '"'; + in = term; + while (*in) { + if (*in == '"') + *out++ = '"'; + *out++ = *in++; + } + *out++ = '"'; + *out = '\0'; + + return 0; +} + +const char* +skip_space (const char *str) +{ + while (*str && isspace ((unsigned char) *str)) + ++str; + return str; +} + +int +parse_boolean_term (void *ctx, const char *str, + char **prefix_out, char **term_out) +{ + int err = EINVAL; + *prefix_out = *term_out = NULL; + + /* Parse prefix */ + str = skip_space (str); + const char *pos = strchr (str, ':'); + if (! pos || pos == str) + goto FAIL; + *prefix_out = talloc_strndup (ctx, str, pos - str); + if (! *prefix_out) { + err = ENOMEM; + goto FAIL; + } + ++pos; + + /* Implement de-quoting compatible with make_boolean_term. */ + if (*pos == '"') { + char *out = talloc_array (ctx, char, strlen (pos)); + int closed = 0; + if (! out) { + err = ENOMEM; + goto FAIL; + } + *term_out = out; + /* Skip the opening quote, find the closing quote, and + * un-double doubled internal quotes. */ + for (++pos; *pos; ) { + if (*pos == '"') { + ++pos; + if (*pos != '"') { + /* Found the closing quote. */ + closed = 1; + pos = skip_space (pos); + break; + } + } + *out++ = *pos++; + } + /* Did the term terminate without a closing quote or is there + * trailing text after the closing quote? */ + if (!closed || *pos) + goto FAIL; + *out = '\0'; + } else { + const char *start = pos; + /* Check for text after the boolean term. */ + while (! is_unquoted_terminator (*pos)) + ++pos; + if (*skip_space (pos)) { + err = EINVAL; + goto FAIL; + } + /* No trailing text; dup the string so the caller can free + * it. */ + *term_out = talloc_strndup (ctx, start, pos - start); + if (! *term_out) { + err = ENOMEM; + goto FAIL; + } + } + return 0; + + FAIL: + talloc_free (*prefix_out); + talloc_free (*term_out); + errno = err; + return -1; +} + +int +strcmp_null (const char *s1, const char *s2) +{ + if (s1 && s2) + return strcmp (s1, s2); + else if (! s1 && ! s2) + return 0; + else if (s1) + return 1; /* s1 (non-NULL) is greater than s2 (NULL) */ + else + return -1; /* s1 (NULL) is less than s2 (non-NULL) */ +} + +int +strcase_equal (const void *a, const void *b) +{ + return strcasecmp (a, b) == 0; +} + +unsigned int +strcase_hash (const void *ptr) +{ + const char *s = ptr; + + /* This is the djb2 hash. */ + unsigned int hash = 5381; + while (s && *s) { + hash = ((hash << 5) + hash) + tolower (*s); + s++; + } + + return hash; +} + +void +strip_trailing (char *str, char ch) +{ + int i; + + for (i = strlen (str) - 1; i >= 0; i--) { + if (str[i] == ch) + str[i] = '\0'; + else + break; + } +} diff --git a/util/string-util.h b/util/string-util.h new file mode 100644 index 00000000..4c110a20 --- /dev/null +++ b/util/string-util.h @@ -0,0 +1,86 @@ +#ifndef _STRING_UTIL_H +#define _STRING_UTIL_H + +#include <string.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* like strtok(3), but without state, and doesn't modify s. Return + * value is indicated by pointer and length, not null terminator. + * + * Usage pattern: + * + * const char *tok = input; + * const char *delim = " \t"; + * size_t tok_len = 0; + * + * while ((tok = strtok_len (tok + tok_len, delim, &tok_len)) != NULL) { + * // do stuff with string tok of length tok_len + * } + */ + +char *strtok_len (char *s, const char *delim, size_t *len); + +/* Const version of strtok_len. */ +const char *strtok_len_c (const char *s, const char *delim, size_t *len); + +/* Return a talloced string with str sanitized. + * + * Whitespace characters (tabs and newlines) are replaced with spaces, + * non-printable characters with question marks. + */ +char *sanitize_string (const void *ctx, const char *str); + +/* Construct a boolean term query with the specified prefix (e.g., + * "id") and search term, quoting term as necessary. Specifically, if + * term contains any non-printable ASCII characters, non-ASCII + * characters, close parenthesis or double quotes, it will be enclosed + * in double quotes and any internal double quotes will be doubled + * (e.g. a"b -> "a""b"). The result will be a valid notmuch query and + * can be parsed by parse_boolean_term. + * + * Output is into buf; it may be talloc_realloced. + * Return: 0 on success, -1 on error. errno will be set to ENOMEM if + * there is an allocation failure. + */ +int make_boolean_term (void *talloc_ctx, const char *prefix, const char *term, + char **buf, size_t *len); + +/* Parse a boolean term query consisting of a prefix, a colon, and a + * term that may be quoted as described for make_boolean_term. If the + * term is not quoted, then it ends at the first whitespace or close + * parenthesis. str may containing leading or trailing whitespace, + * but anything else is considered a parse error. This is compatible + * with anything produced by make_boolean_term, and supports a subset + * of the quoting styles supported by Xapian (and hence notmuch). + * *prefix_out and *term_out will be talloc'd with context ctx. + * + * Return: 0 on success, -1 on error. errno will be set to EINVAL if + * there is a parse error or ENOMEM if there is an allocation failure. + */ +int +parse_boolean_term (void *ctx, const char *str, + char **prefix_out, char **term_out); + +/* strcmp that handles NULL strings; in strcmp terms a NULL string is + * considered to be less than a non-NULL string. + */ +int strcmp_null (const char *s1, const char *s2); + +/* GLib GEqualFunc compatible strcasecmp wrapper */ +int strcase_equal (const void *a, const void *b); + +/* GLib GHashFunc compatible case insensitive hash function */ +unsigned int strcase_hash (const void *ptr); + +void strip_trailing (char *str, char ch); + +const char* skip_space (const char *str); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/util/talloc-extra.c b/util/talloc-extra.c new file mode 100644 index 00000000..96262470 --- /dev/null +++ b/util/talloc-extra.c @@ -0,0 +1,14 @@ +#include <string.h> +#include "talloc-extra.h" + +char * +talloc_strndup_named_const (void *ctx, const char *str, + size_t len, const char *name) +{ + char *ptr = talloc_strndup (ctx, str, len); + + if (ptr) + talloc_set_name_const (ptr, name); + + return ptr; +} diff --git a/util/talloc-extra.h b/util/talloc-extra.h new file mode 100644 index 00000000..eac5dc05 --- /dev/null +++ b/util/talloc-extra.h @@ -0,0 +1,18 @@ +#ifndef _TALLOC_EXTRA_H +#define _TALLOC_EXTRA_H + +#include <talloc.h> + +/* Like talloc_strndup, but take an extra parameter for the internal talloc + * name (for debugging) */ + +char * +talloc_strndup_named_const (void *ctx, const char *str, + size_t len, const char *name); + +/* use the __location__ macro from talloc.h to name a string according to its + * source location */ + +#define talloc_strndup_debug(ctx, str, len) talloc_strndup_named_const (ctx, str, len, __location__) + +#endif diff --git a/util/util.c b/util/util.c new file mode 100644 index 00000000..06659b35 --- /dev/null +++ b/util/util.c @@ -0,0 +1,24 @@ +#include "util.h" +#include "error_util.h" +#include <string.h> +#include <errno.h> + +const char * +util_error_string (util_status_t errnum) +{ + switch (errnum) { + case UTIL_SUCCESS: + return "success"; + case UTIL_OUT_OF_MEMORY: + return "out of memory"; + case UTIL_EOF: + return "end of file"; + case UTIL_ERRNO: + return strerror (errno); + case UTIL_GZERROR: + /* we lack context to be more informative here */ + return "zlib error"; + default: + INTERNAL_ERROR("unexpected error status %d", errnum); + } +} diff --git a/util/util.h b/util/util.h new file mode 100644 index 00000000..b24860af --- /dev/null +++ b/util/util.h @@ -0,0 +1,29 @@ +#ifndef _UTIL_H +#define _UTIL_H + +typedef enum util_status { + /** + * No error occurred. + */ + UTIL_SUCCESS = 0, + /** + * Out of memory. + */ + UTIL_OUT_OF_MEMORY, + /** + * End of stream reached while attempting to read. + */ + UTIL_EOF, + /** + * Low level error occurred, consult errno. + */ + UTIL_ERRNO, + /** + * Zlib error occurred, call gzerror for details. + */ + UTIL_GZERROR +} util_status_t; + +const char * +util_error_string (util_status_t status); +#endif diff --git a/util/xutil.c b/util/xutil.c new file mode 100644 index 00000000..f211eaaa --- /dev/null +++ b/util/xutil.c @@ -0,0 +1,139 @@ +/* xutil.c - Various wrapper functions to abort on error. + * + * Copyright © 2009 Carl Worth + * + * 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 3 of the License, 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, see https://www.gnu.org/licenses/ . + * + * Author: Carl Worth <cworth@cworth.org> + */ + +#include <stdio.h> +#include <string.h> + +#include "xutil.h" +#include "error_util.h" + +void * +xcalloc (size_t nmemb, size_t size) +{ + void *ret; + + ret = calloc (nmemb, size); + if (ret == NULL) { + fprintf (stderr, "Out of memory.\n"); + exit (1); + } + + return ret; +} + +void * +xmalloc (size_t size) +{ + void *ret; + + ret = malloc (size); + if (ret == NULL) { + fprintf (stderr, "Out of memory.\n"); + exit (1); + } + + return ret; +} + +void * +xrealloc (void *ptr, size_t size) +{ + void *ret; + + ret = realloc (ptr, size); + if (ret == NULL) { + fprintf (stderr, "Out of memory.\n"); + exit (1); + } + + return ret; +} + +char * +xstrdup (const char *s) +{ + char *ret; + + ret = strdup (s); + if (ret == NULL) { + fprintf (stderr, "Out of memory.\n"); + exit (1); + } + + return ret; +} + +char * +xstrndup (const char *s, size_t n) +{ + char *ret; + + if (strlen (s) <= n) + n = strlen (s); + + ret = malloc (n + 1); + if (ret == NULL) { + fprintf (stderr, "Out of memory.\n"); + exit (1); + } + memcpy (ret, s, n); + ret[n] = '\0'; + + return ret; +} + +int +xregcomp (regex_t *preg, const char *regex, int cflags) +{ + int rerr; + + rerr = regcomp (preg, regex, cflags); + if (rerr) { + size_t error_size = regerror (rerr, preg, NULL, 0); + char *error = xmalloc (error_size); + + regerror (rerr, preg, error, error_size); + fprintf (stderr, "compiling regex %s: %s\n", + regex, error); + free (error); + return 1; + } + return 0; +} + +int +xregexec (const regex_t *preg, const char *string, + size_t nmatch, regmatch_t pmatch[], int eflags) +{ + unsigned int i; + int rerr; + + rerr = regexec (preg, string, nmatch, pmatch, eflags); + if (rerr) + return rerr; + + for (i = 0; i < nmatch; i++) { + if (pmatch[i].rm_so == -1) + INTERNAL_ERROR ("matching regex against %s: Sub-match %d not found\n", + string, i); + } + + return 0; +} diff --git a/util/xutil.h b/util/xutil.h new file mode 100644 index 00000000..4829f33c --- /dev/null +++ b/util/xutil.h @@ -0,0 +1,52 @@ +/* xutil.h - Various wrapper functions to abort on error. + * + * Copyright © 2009 Carl Worth + * + * 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 3 of the License, 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, see https://www.gnu.org/licenses/ . + * + * Author: Carl Worth <cworth@cworth.org> + */ + +#ifndef NOTMUCH_XUTIL_H +#define NOTMUCH_XUTIL_H + +#include <stdlib.h> +#include <sys/types.h> +#include <regex.h> + +/* xutil.c */ +void * +xcalloc (size_t nmemb, size_t size); + +void * +xmalloc (size_t size); + +void * +xrealloc (void *ptrr, size_t size); + +char * +xstrdup (const char *s); + +char * +xstrndup (const char *s, size_t n); + +/* Returns 0 for successful compilation, 1 otherwise */ +int +xregcomp (regex_t *preg, const char *regex, int cflags); + +int +xregexec (const regex_t *preg, const char *string, + size_t nmatch, regmatch_t pmatch[], int eflags); + +#endif diff --git a/util/zlib-extra.c b/util/zlib-extra.c new file mode 100644 index 00000000..2b2cd8f9 --- /dev/null +++ b/util/zlib-extra.c @@ -0,0 +1,85 @@ +/* zlib-extra.c - Extra or enhanced routines for compressed I/O. + * + * Copyright (c) 2014 David Bremner + * + * 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 3 of the License, 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, see https://www.gnu.org/licenses/ . + * + * Author: David Bremner <david@tethera.net> + */ + +#include "zlib-extra.h" +#include <talloc.h> +#include <stdio.h> +#include <string.h> + +/* mimic POSIX/glibc getline, but on a zlib gzFile stream, and using talloc */ +util_status_t +gz_getline (void *talloc_ctx, char **bufptr, ssize_t *bytes_read, gzFile stream) +{ + char *buf = *bufptr; + unsigned int len; + size_t offset = 0; + + if (buf) { + len = talloc_array_length (buf); + } else { + /* same as getdelim from gnulib */ + len = 120; + buf = talloc_array (talloc_ctx, char, len); + if (buf == NULL) + return UTIL_OUT_OF_MEMORY; + } + + while (1) { + if (! gzgets (stream, buf + offset, len - offset)) { + /* Null indicates EOF or error */ + int zlib_status = 0; + (void) gzerror (stream, &zlib_status); + switch (zlib_status) { + case Z_OK: + /* no data read before EOF */ + if (offset == 0) + return UTIL_EOF; + else + goto SUCCESS; + case Z_ERRNO: + return UTIL_ERRNO; + default: + return UTIL_GZERROR; + } + } + + offset += strlen (buf + offset); + + if (buf[offset - 1] == '\n') + goto SUCCESS; + + len *= 2; + buf = talloc_realloc (talloc_ctx, buf, char, len); + if (buf == NULL) + return UTIL_OUT_OF_MEMORY; + } + SUCCESS: + *bufptr = buf; + *bytes_read = offset; + return UTIL_SUCCESS; +} + +const char *gz_error_string (util_status_t status, gzFile file) +{ + if (status == UTIL_GZERROR) + return gzerror (file, NULL); + else + return util_error_string (status); +} diff --git a/util/zlib-extra.h b/util/zlib-extra.h new file mode 100644 index 00000000..aedfd48f --- /dev/null +++ b/util/zlib-extra.h @@ -0,0 +1,25 @@ +#ifndef _ZLIB_EXTRA_H +#define _ZLIB_EXTRA_H + +#include "util.h" +#include <zlib.h> + +/* Like getline, but read from a gzFile. Allocation is with talloc. + * Returns: + * + * UTIL_SUCCESS, UTIL_OUT_OF_MEMORY, UTIL_ERRNO, UTIL_GZERROR + * Consult util.h for description + * + * UTIL_EOF End of file encountered before + * any characters read + */ +util_status_t +gz_getline (void *ctx, char **lineptr, ssize_t *bytes_read, gzFile stream); + +/* return a suitable error string based on the return status + * from gz_readline + */ + +const char * +gz_error_string (util_status_t status, gzFile stream); +#endif |
