summaryrefslogtreecommitdiff
path: root/src/conversion.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/conversion.c')
-rw-r--r--src/conversion.c414
1 files changed, 414 insertions, 0 deletions
diff --git a/src/conversion.c b/src/conversion.c
new file mode 100644
index 0000000..356200c
--- /dev/null
+++ b/src/conversion.c
@@ -0,0 +1,414 @@
+/* conversion.c - String conversion helper functions.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004, 2007 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_SYS_TYPES_H
+ /* Solaris 8 needs sys/types.h before time.h. */
+# include <sys/types.h>
+#endif
+#include <time.h>
+#include <errno.h>
+
+#include "gpgme.h"
+#include "util.h"
+#include "debug.h"
+
+#define atoi_1(p) (*(p) - '0' )
+#define atoi_2(p) ((atoi_1(p) * 10) + atoi_1((p)+1))
+#define atoi_4(p) ((atoi_2(p) * 100) + atoi_2((p)+2))
+
+
+
+/* Convert two hexadecimal digits from STR to the value they
+ represent. Returns -1 if one of the characters is not a
+ hexadecimal digit. */
+int
+_gpgme_hextobyte (const char *str)
+{
+ int val = 0;
+ int i;
+
+#define NROFHEXDIGITS 2
+ for (i = 0; i < NROFHEXDIGITS; i++)
+ {
+ if (*str >= '0' && *str <= '9')
+ val += *str - '0';
+ else if (*str >= 'A' && *str <= 'F')
+ val += 10 + *str - 'A';
+ else if (*str >= 'a' && *str <= 'f')
+ val += 10 + *str - 'a';
+ else
+ return -1;
+ if (i < NROFHEXDIGITS - 1)
+ val *= 16;
+ str++;
+ }
+ return val;
+}
+
+
+/* Decode the C formatted string SRC and store the result in the
+ buffer *DESTP which is LEN bytes long. If LEN is zero, then a
+ large enough buffer is allocated with malloc and *DESTP is set to
+ the result. Currently, LEN is only used to specify if allocation
+ is desired or not, the caller is expected to make sure that *DESTP
+ is large enough if LEN is not zero. */
+gpgme_error_t
+_gpgme_decode_c_string (const char *src, char **destp, size_t len)
+{
+ char *dest;
+
+ /* Set up the destination buffer. */
+ if (len)
+ {
+ if (len < strlen (src) + 1)
+ return gpg_error (GPG_ERR_INTERNAL);
+
+ dest = *destp;
+ }
+ else
+ {
+ /* The converted string will never be larger than the original
+ string. */
+ dest = malloc (strlen (src) + 1);
+ if (!dest)
+ return gpg_error_from_syserror ();
+
+ *destp = dest;
+ }
+
+ /* Convert the string. */
+ while (*src)
+ {
+ if (*src != '\\')
+ {
+ *(dest++) = *(src++);
+ continue;
+ }
+
+ switch (src[1])
+ {
+#define DECODE_ONE(match,result) \
+ case match: \
+ src += 2; \
+ *(dest++) = result; \
+ break;
+
+ DECODE_ONE ('\'', '\'');
+ DECODE_ONE ('\"', '\"');
+ DECODE_ONE ('\?', '\?');
+ DECODE_ONE ('\\', '\\');
+ DECODE_ONE ('a', '\a');
+ DECODE_ONE ('b', '\b');
+ DECODE_ONE ('f', '\f');
+ DECODE_ONE ('n', '\n');
+ DECODE_ONE ('r', '\r');
+ DECODE_ONE ('t', '\t');
+ DECODE_ONE ('v', '\v');
+
+ case 'x':
+ {
+ int val = _gpgme_hextobyte (&src[2]);
+
+ if (val == -1)
+ {
+ /* Should not happen. */
+ *(dest++) = *(src++);
+ *(dest++) = *(src++);
+ if (*src)
+ *(dest++) = *(src++);
+ if (*src)
+ *(dest++) = *(src++);
+ }
+ else
+ {
+ if (!val)
+ {
+ /* A binary zero is not representable in a C
+ string. */
+ *(dest++) = '\\';
+ *(dest++) = '0';
+ }
+ else
+ *((unsigned char *) dest++) = val;
+ src += 4;
+ }
+ }
+ break;
+
+ default:
+ {
+ /* Should not happen. */
+ *(dest++) = *(src++);
+ *(dest++) = *(src++);
+ }
+ }
+ }
+ *(dest++) = 0;
+
+ return 0;
+}
+
+
+/* Decode the percent escaped string SRC and store the result in the
+ buffer *DESTP which is LEN bytes long. If LEN is zero, then a
+ large enough buffer is allocated with malloc and *DESTP is set to
+ the result. Currently, LEN is only used to specify if allocation
+ is desired or not, the caller is expected to make sure that *DESTP
+ is large enough if LEN is not zero. If BINARY is 1, then '\0'
+ characters are allowed in the output. */
+gpgme_error_t
+_gpgme_decode_percent_string (const char *src, char **destp, size_t len,
+ int binary)
+{
+ char *dest;
+
+ /* Set up the destination buffer. */
+ if (len)
+ {
+ if (len < strlen (src) + 1)
+ return gpg_error (GPG_ERR_INTERNAL);
+
+ dest = *destp;
+ }
+ else
+ {
+ /* The converted string will never be larger than the original
+ string. */
+ dest = malloc (strlen (src) + 1);
+ if (!dest)
+ return gpg_error_from_syserror ();
+
+ *destp = dest;
+ }
+
+ /* Convert the string. */
+ while (*src)
+ {
+ if (*src != '%')
+ {
+ *(dest++) = *(src++);
+ continue;
+ }
+ else
+ {
+ int val = _gpgme_hextobyte (&src[1]);
+
+ if (val == -1)
+ {
+ /* Should not happen. */
+ *(dest++) = *(src++);
+ if (*src)
+ *(dest++) = *(src++);
+ if (*src)
+ *(dest++) = *(src++);
+ }
+ else
+ {
+ if (!val && !binary)
+ {
+ /* A binary zero is not representable in a C
+ string. */
+ *(dest++) = '\\';
+ *(dest++) = '0';
+ }
+ else
+ *((unsigned char *) dest++) = val;
+ src += 3;
+ }
+ }
+ }
+ *(dest++) = 0;
+
+ return 0;
+}
+
+
+/* Encode the string SRC with percent escaping and store the result in
+ the buffer *DESTP which is LEN bytes long. If LEN is zero, then a
+ large enough buffer is allocated with malloc and *DESTP is set to
+ the result. Currently, LEN is only used to specify if allocation
+ is desired or not, the caller is expected to make sure that *DESTP
+ is large enough if LEN is not zero. If BINARY is 1, then '\0'
+ characters are allowed in the output. */
+gpgme_error_t
+_gpgme_encode_percent_string (const char *src, char **destp, size_t len)
+{
+ size_t destlen;
+ char *dest;
+ const char *str;
+
+ destlen = 0;
+ str = src;
+ /* We percent-escape the + character because the user might need a
+ "percent plus" escaped string (special gpg format). But we
+ percent-escape the space character, which works with and without
+ the special plus format. */
+ while (*str)
+ {
+ if (*str == '+' || *str == '\"' || *str == '%'
+ || *(const unsigned char *)str <= 0x20)
+ destlen += 3;
+ else
+ destlen++;
+ str++;
+ }
+ /* Terminating nul byte. */
+ destlen++;
+
+ /* Set up the destination buffer. */
+ if (len)
+ {
+ if (len < destlen);
+ return gpg_error (GPG_ERR_INTERNAL);
+
+ dest = *destp;
+ }
+ else
+ {
+ /* The converted string will never be larger than the original
+ string. */
+ dest = malloc (destlen);
+ if (!dest)
+ return gpg_error_from_syserror ();
+
+ *destp = dest;
+ }
+
+ /* Convert the string. */
+ while (*src)
+ {
+ if (*src == '+' || *src == '\"' || *src == '%'
+ || *(const unsigned char *)src <= 0x20)
+ {
+ snprintf (dest, 4, "%%%02X", *(unsigned char *)src);
+ dest += 3;
+ }
+ else
+ *(dest++) = *src;
+ src++;
+ }
+ *(dest++) = 0;
+
+ return 0;
+}
+
+
+#ifdef HAVE_W32_SYSTEM
+static time_t
+_gpgme_timegm (struct tm *tm)
+{
+ /* This one is thread safe. */
+ SYSTEMTIME st;
+ FILETIME ft;
+ unsigned long long cnsecs;
+
+ st.wYear = tm->tm_year + 1900;
+ st.wMonth = tm->tm_mon + 1;
+ st.wDay = tm->tm_mday;
+ st.wHour = tm->tm_hour;
+ st.wMinute = tm->tm_min;
+ st.wSecond = tm->tm_sec;
+ st.wMilliseconds = 0; /* Not available. */
+ st.wDayOfWeek = 0; /* Ignored. */
+
+ /* System time is UTC thus the conversion is pretty easy. */
+ if (!SystemTimeToFileTime (&st, &ft))
+ {
+ gpg_err_set_errno (EINVAL);
+ return (time_t)(-1);
+ }
+
+ cnsecs = (((unsigned long long)ft.dwHighDateTime << 32)
+ | ft.dwLowDateTime);
+ cnsecs -= 116444736000000000ULL; /* The filetime epoch is 1601-01-01. */
+ return (time_t)(cnsecs / 10000000ULL);
+}
+#endif
+
+
+/* Parse the string TIMESTAMP into a time_t. The string may either be
+ seconds since Epoch or in the ISO 8601 format like
+ "20390815T143012". Returns 0 for an empty string or seconds since
+ Epoch. Leading spaces are skipped. If ENDP is not NULL, it will
+ point to the next non-parsed character in TIMESTRING. */
+time_t
+_gpgme_parse_timestamp (const char *timestamp, char **endp)
+{
+ /* Need to skip leading spaces, because that is what strtoul does
+ but not our ISO 8601 checking code. */
+ while (*timestamp && *timestamp== ' ')
+ timestamp++;
+ if (!*timestamp)
+ return 0;
+
+ if (strlen (timestamp) >= 15 && timestamp[8] == 'T')
+ {
+ struct tm buf;
+ int year;
+
+ year = atoi_4 (timestamp);
+ if (year < 1900)
+ return (time_t)(-1);
+
+ if (endp)
+ *endp = (char*)(timestamp + 15);
+
+ /* Fixme: We would better use a configure test to see whether
+ mktime can handle dates beyond 2038. */
+ if (sizeof (time_t) <= 4 && year >= 2038)
+ return (time_t)2145914603; /* 2037-12-31 23:23:23 */
+
+ memset (&buf, 0, sizeof buf);
+ buf.tm_year = year - 1900;
+ buf.tm_mon = atoi_2 (timestamp+4) - 1;
+ buf.tm_mday = atoi_2 (timestamp+6);
+ buf.tm_hour = atoi_2 (timestamp+9);
+ buf.tm_min = atoi_2 (timestamp+11);
+ buf.tm_sec = atoi_2 (timestamp+13);
+
+#ifdef HAVE_W32_SYSTEM
+ return _gpgme_timegm (&buf);
+#else
+#ifdef HAVE_TIMEGM
+ return timegm (&buf);
+#else
+ {
+ time_t tim;
+
+ putenv ("TZ=UTC");
+ tim = mktime (&buf);
+#ifdef __GNUC__
+#warning fixme: we must somehow reset TZ here. It is not threadsafe anyway.
+#endif
+ return tim;
+ }
+#endif /* !HAVE_TIMEGM */
+#endif /* !HAVE_W32_SYSTEM */
+ }
+ else
+ return (time_t)strtoul (timestamp, endp, 10);
+}