/*
** Copyright (c) 2005-2007 Sendmail, Inc. and its suppliers.
** All rights reserved.
*/
#ifndef lint
static char util_c_id[] = "@(#)$Id: util.c,v 1.15 2007/12/17 23:59:01 msk Exp $";
#endif /* !lint */
/* system includes */
#include <sys/types.h>
#include <ctype.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
/* libsm includes */
#include <sm/string.h>
/* libdkim includes */
#include "dkim.h"
#include "util.h"
/*
** DKIM_COLLAPSE -- remove spaces from a string
**
** Parameters:
** str -- string to process
**
** Return value:
** None.
*/
void
dkim_collapse(u_char *str)
{
u_char *q;
u_char *r;
assert(str != NULL);
for (q = str, r = str; *q != '\0'; q++)
{
if (!isspace(*q))
{
if (q != r)
*r = *q;
r++;
}
}
*r = '\0';
}
/*
** DKIM_ISLWSP -- return true iff a character is some kind of linear
** whitespace
**
** Parameters:
** c -- character being examined
**
** Return value:
** TRUE iff 'c' refers to something considered linear whitespace.
*/
bool
dkim_islwsp(u_int c)
{
return (isascii(c) && isspace(c) && c != '\n' && c != '\r');
}
/*
** DKIM_ADDRCMP -- like strcmp(), except for e-mail addresses (i.e. local-part
** is case-sensitive, while the domain part is not)
**
** Parameters:
** s1, s2 -- the strings
**
** Return value:
** Like strcmp(). However, if "s2" contains only a domain name,
** don't bother comparing the local-parts.
*/
int
dkim_addrcmp(u_char *s1, u_char *s2)
{
int ret;
u_char *at1, *at2;
assert(s1 != NULL);
assert(s2 != NULL);
at1 = (u_char *) strchr(s1, '@');
at2 = (u_char *) strchr(s2, '@');
/* if one or both contain no "@"s, just be strcmp() */
if (at1 == NULL || at2 == NULL)
return strcmp(s1, s2);
/* first check the local-parts */
*at1 = '\0';
*at2 = '\0';
/* if "s2" contains only a domain name, skip the local-part check */
if (at2 != s2)
{
/* compare the local-parts, case-sensitive */
ret = strcmp(s1, s2);
if (ret != 0)
{
*at1 = '@';
*at2 = '@';
return ret;
}
}
/* now compare the domains, case-insensitive */
ret = strcasecmp(at1 + 1, at2 + 1);
*at1 = '@';
*at2 = '@';
return ret;
}
/*
** DKIM_HDRLIST -- build up a header list for use in a regexp
**
** Parameters:
** buf -- where to write
** buflen -- bytes at "buf"
** hdrlist -- array of header names
** first -- first call
**
** Return value:
** TRUE iff everything fit.
*/
bool
dkim_hdrlist(u_char *buf, size_t buflen, u_char **hdrlist, bool first)
{
int c;
int len;
char *p;
char *q;
char *end;
assert(buf != NULL);
assert(hdrlist != NULL);
for (c = 0; ; c++)
{
if (hdrlist[c] == NULL)
break;
if (!first)
{
len = sm_strlcat(buf, "|", buflen);
if (len >= buflen)
return FALSE;
}
else
{
len = strlen(buf);
}
first = FALSE;
q = &buf[len];
end = &buf[buflen - 1];
for (p = hdrlist[c]; *p != '\0'; p++)
{
if (q >= end)
return FALSE;
switch (*p)
{
case '*':
*q = '.';
q++;
if (q >= end)
return FALSE;
*q = '*';
q++;
break;
case '.':
*q = '\\';
q++;
if (q >= end)
return FALSE;
*q = '.';
q++;
break;
default:
*q = *p;
q++;
break;
}
}
}
return TRUE;
}
/*
** DKIM_LOWERHDR -- convert a string (presumably a header) to all lowercase,
** but only up to a colon
**
** Parameters:
** str -- string to modify
**
** Return value:
** None.
*/
void
dkim_lowerhdr(unsigned char *str)
{
unsigned char *p;
assert(str != NULL);
for (p = str; *p != '\0'; p++)
{
if (*p == ':')
return;
if (isascii(*p) && isupper(*p))
*p = tolower(*p);
}
}
/*
** DKIM_HEXCHAR -- translate a hexadecimal character
**
** Parameters:
** c -- character to translate
**
** Return value:
** Decimal equivalent.
*/
int
dkim_hexchar(int c)
{
switch (c)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return c - '0';
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
return 10 + c - 'A';
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
return 10 + c - 'a';
default:
assert(0);
return -1;
}
}
/*
** DKIM_QP_DECODE -- decode a quoted-printable string
**
** Parameters:
** in -- input
** out -- output
** outlen -- bytes available at "out"
**
** Return value:
** >= 0 -- number of bytes in output
** -1 -- parse error
*/
int
dkim_qp_decode(unsigned char *in, unsigned char *out, int outlen)
{
unsigned char next1;
unsigned char next2 = 0;
int xl;
unsigned char const *p;
unsigned char *q;
unsigned char *pos;
unsigned char const *start;
unsigned char const *stop;
unsigned char *end;
unsigned char const *hexdigits = "0123456789ABCDEF";
assert(in != NULL);
assert(out != NULL);
start = NULL;
stop = NULL;
end = out + outlen;
for (p = in, q = out; *p != '\0' && q <= end; p++)
{
switch (*p)
{
case '=':
next1 = *(p + 1);
if (next1 != '\0')
next2 = *(p + 2);
/* = at EOL */
if (next1 == '\n' ||
(next1 == '\r' && next2 == '\n'))
{
stop = p;
if (start != NULL)
{
unsigned char const *x;
for (x = start; x <= stop; x++)
{
if (q <= end)
{
*q = *x;
q++;
}
}
}
start = NULL;
stop = NULL;
p++;
if (next2 == '\n')
p++;
break;
}
/* = elsewhere */
pos = (unsigned char *) strchr(hexdigits, next1);
if (pos == NULL)
return -1;
xl = (pos - (unsigned char *) hexdigits) * 16;
pos = (unsigned char *) strchr(hexdigits, next2);
if (pos == NULL)
return -1;
xl += (pos - (unsigned char *) hexdigits);
stop = p;
if (start != NULL)
{
unsigned char const *x;
for (x = start; x < stop; x++)
{
if (q <= end)
{
*q = *x;
q++;
}
}
}
start = NULL;
stop = NULL;
*q = xl;
q++;
p += 2;
break;
case ' ':
case '\t':
if (start == NULL)
start = p;
break;
case '\r':
break;
case '\n':
if (stop == NULL)
stop = p;
if (start != NULL)
{
unsigned char const *x;
for (x = start; x <= stop; x++)
{
if (q <= end)
{
*q = *x;
q++;
}
}
}
if (p > in && *(p - 1) != '\r')
{
if (q <= end)
{
*q = '\n';
q++;
}
}
else
{
if (q <= end)
{
*q = '\r';
q++;
}
if (q <= end)
{
*q = '\n';
q++;
}
}
start = NULL;
stop = NULL;
break;
default:
if (start == NULL)
start = p;
stop = p;
break;
}
}
if (start != NULL)
{
unsigned char const *x;
for (x = start; x < p; x++)
{
if (q <= end)
{
*q = *x;
q++;
}
}
}
return q - out;
}
/*
** DKIM_STRTOUL -- convert string to unsigned long
**
** Parameters:
** str -- string to convert
** endptr -- pointer to store string after value
** base -- base to convert from
**
** Return value:
** Value of string as unsigned long
*/
unsigned long
dkim_strtoul(const char *str, char **endptr, int base)
{
char start = '+';
static char cutlim = ULONG_MAX % 10;
char c;
bool erange = FALSE;
static unsigned long cutoff = ULONG_MAX / 10;
unsigned long value = 0;
const char *subj;
const char *cur;
if (base != 10)
return strtoul(str, endptr, base);
if (str == NULL)
{
errno = EINVAL;
return value;
}
subj = str;
while (*subj != '\0' && isspace(*subj))
subj++;
if (*subj == '-' || *subj == '+')
start = *subj++;
for (cur = subj; *cur >= '0' && *cur <= '9'; cur++)
{
if (erange)
continue;
c = *cur - '0';
if ((value > cutoff) || (value == cutoff && c > cutlim))
{
erange = TRUE;
continue;
}
value = (value * 10) + c;
}
if (cur == subj)
{
if (endptr != NULL)
*endptr = (char *) str;
errno = EINVAL;
return 0;
}
if (endptr != NULL)
*endptr = (char *) cur;
if (erange)
{
errno = ERANGE;
return ULONG_MAX;
}
if (start == '-')
return -value;
else
return value;
}
/*
** DKIM_STRTOULL -- convert string to unsigned long long
**
** Parameters:
** str -- string to convert
** endptr -- pointer to store string after value
** base -- base to convert from
**
** Return value:
** Value of string as unsigned long long
*/
unsigned long long
dkim_strtoull(const char *str, char **endptr, int base)
{
char start = '+';
char c;
bool erange = FALSE;
static char cutlim = ULLONG_MAX % 10;
static unsigned long long cutoff = ULLONG_MAX / 10;
unsigned long long value = 0;
const char *subj;
const char *cur;
if (base != 10)
return strtoull(str, endptr, base);
if (str == NULL)
{
errno = EINVAL;
return value;
}
subj = str;
while (*subj && isspace(*subj))
subj++;
if (*subj == '-' || *subj == '+')
start = *subj++;
for (cur = subj; *cur >= '0' && *cur <= '9'; cur++)
{
if (erange)
continue;
c = *cur - '0';
if ((value > cutoff) || (value == cutoff && c > cutlim))
{
erange = 1;
continue;
}
value = (value * 10) + c;
}
if (cur == subj)
{
if (endptr != NULL)
*endptr = (char *) str;
errno = EINVAL;
return 0;
}
if (endptr != NULL)
*endptr = (char *) cur;
if (erange != 0)
{
errno = ERANGE;
return ULLONG_MAX;
}
if (start == '-')
return -value;
else
return value;
}
syntax highlighted by Code2HTML, v. 0.9.1