gpgme/src/conversion.c
Werner Koch 293d173691
core: Add gpgme_data_set_flag to add more meta data to data objects.
* src/gpgme.h.in (gpgme_data_set_flag): New public function.
* src/data.c (gpgme_data_set_flag): New.
(_gpgme_data_get_size_hint): New.
* src/data.h (strucy gpgme_data): Add field 'size_hint'.
* src/gpgme.def, src/libgpgme.vers: Add new function.
* src/conversion.c (_gpgme_string_to_off): New.

Signed-off-by: Werner Koch <wk@gnupg.org>
2016-08-12 15:21:42 +02:00

503 lines
12 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* 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;
}
/* Split a string into space delimited fields and remove leading and
* trailing spaces from each field. A pointer to the each field is
* stored in ARRAY. Stop splitting at ARRAYSIZE fields. The function
* modifies STRING. The number of parsed fields is returned.
*/
int
_gpgme_split_fields (char *string, char **array, int arraysize)
{
int n = 0;
char *p, *pend;
for (p = string; *p == ' '; p++)
;
do
{
if (n == arraysize)
break;
array[n++] = p;
pend = strchr (p, ' ');
if (!pend)
break;
*pend++ = 0;
for (p = pend; *p == ' '; p++)
;
}
while (*p);
return n;
}
/* Convert the field STRING into an unsigned long value. Check for
* trailing garbage. */
gpgme_error_t
_gpgme_strtoul_field (const char *string, unsigned long *result)
{
char *endp;
gpg_err_set_errno (0);
*result = strtoul (string, &endp, 0);
if (errno)
return gpg_error_from_syserror ();
if (endp == string || *endp)
return gpg_error (GPG_ERR_INV_VALUE);
return 0;
}
/* Convert STRING into an offset value. Note that this functions only
* allows for a base-10 length. This function is similar to atoi()
* and thus there is no error checking. */
gpgme_off_t
_gpgme_string_to_off (const char *string)
{
gpgme_off_t value = 0;
while (*string == ' ' || *string == '\t')
string++;
for (; *string >= '0' && *string <= '9'; string++)
{
value *= 10;
value += atoi_1 (string);
}
return value;
}
#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);
}
/* The GPG backend uses OpenPGP algorithm numbers which we need to map
to our algorithm numbers. This function MUST not change ERRNO. */
int
_gpgme_map_pk_algo (int algo, gpgme_protocol_t protocol)
{
if (protocol == GPGME_PROTOCOL_OPENPGP)
{
switch (algo)
{
case 1: case 2: case 3: case 16: case 17: break;
case 18: algo = GPGME_PK_ECDH; break;
case 19: algo = GPGME_PK_ECDSA; break;
case 20: break;
case 22: algo = GPGME_PK_EDDSA; break;
default: algo = 0; break; /* Unknown. */
}
}
return algo;
}