/*
** (c) Copyright 2003-2005 Matt Messier and John Viega
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**
** Redistributions of source code must retain the above copyright notice,
** this list of conditions and the following disclaimer.
**
** Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
**
** Neither the name of the primary nor the names of the contributors may
** be used to endorse or promote products derived from this software
** without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "isafestr.h"
#ifdef HAVE_TERMIOS_H
#include <termios.h>
#endif
#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif
#ifdef HAVE_PATHS_H
#include <paths.h>
#endif
#ifdef WIN32
#include <io.h>
#define _PATH_TTY "CONIN$"
#else
#ifdef HAVE_PTHREAD
#include <pthread.h>
#endif
#endif
#ifndef _PATH_TTY
#define _PATH_TTY "/dev/tty"
#endif
#define SAFESTR_READLINE_SIZE (128 - (sizeof(small_isafestr_t) + 1))
typedef union safestr_integer_t
{
int32_t s32;
int64_t s64;
u_int32_t u32;
u_int64_t u64;
} safestr_integer_t;
static char ascii_values[64] =
{
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
-1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
};
static int alpha_bytes[256] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - 15 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16 - 31 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 32 - 47 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 48 - 63 */
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 64 - 79 */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 80 - 95 */
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 96 - 111 */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 112 - 127 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 128 - 143 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 144 - 159 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 160 - 175 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 176 - 191 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 192 - 207 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 208 - 223 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 224 - 239 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 240 - 255 */
};
static int space_bytes[256] =
{
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0 - 15 */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 16 - 31 */
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 32 - 47 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 48 - 63 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 64 - 79 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 95 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 96 - 111 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 112 - 127 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 128 - 143 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 144 - 159 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 160 - 175 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 176 - 191 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 192 - 207 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 208 - 223 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 224 - 239 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 240 - 255 */
};
static void cleanup_safestr(safestr_t, void *);
static void parse_integer(isafestr_t, safestr_integer_t *, int, int, int);
#ifndef WIN32
static void restore_signals(sigset_t *, void *);
static void restore_terminal(struct termios *, FILE *);
#endif
static void
cleanup_safestr(safestr_t str, void *arg)
{
safestr_free(str);
}
static void
parse_integer(isafestr_t istr, safestr_integer_t *result, int base, int size, int sign)
{
int negative = 0, value;
char *cptr;
unsigned char c;
if (size != 32 && size != 64)
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_PARAMETER, NULL);
if (base && (base < 2 || base > 36))
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_PARAMETER, NULL);
if (size == 32 && sign) result->s32 = 0;
else if (size == 32) result->u32 = 0;
else if (size == 64 && sign) result->s64 = 0;
else if (size == 64) result->u64 = 0;
cptr = istr->str;
while (space_bytes[(int)(unsigned char)*cptr])
cptr++;
if (*cptr == '+')
cptr++;
else if (*cptr == '-' && sign)
{
cptr++;
negative = 1;
}
if (*cptr == '0' && (*(cptr + 1) == 'b' || *(cptr + 1) == 'B'))
{
if (base && base != 2)
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT, NULL);
base = 2;
cptr += 2;
}
else if (*cptr == '0' && *(cptr + 1) >= '0' && *(cptr + 1) <= '7')
{
if (base && base != 8)
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT, NULL);
base = 8;
cptr++;
}
else if (*cptr == '0' && (*(cptr + 1) == 'x' || *(cptr + 1) == 'X'))
{
if (base && base != 16)
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT, NULL);
base = 16;
cptr += 2;
}
else
{
base = 10;
}
for (; *cptr; cptr++)
{
c = safestr_casemap_upper[(int)*cptr & 0xff];
if (c < 32 || c > sizeof(ascii_values) + 32)
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT, NULL);
if ((value = ascii_values[c - 32]) == -1)
{
if (space_bytes[(int)c])
break;
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT, NULL);
}
if (value >= base)
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT, NULL);
if (size == 32 && sign) result->s32 = (result->s32 * base) + value;
else if (size == 32) result->u32 = (result->u32 * base) + value;
else if (size == 64 && sign) result->s64 = (result->s64 * base) + value;
else if (size == 64) result->u64 = (result->u64 * base) + value;
}
if (sign && negative)
{
if (size == 32) result->s32 *= -1;
else if (size == 64) result->s64 *= -1;
}
}
#ifndef WIN32
static void
restore_signals(sigset_t *saved_signals, void *arg)
{
#ifdef HAVE_PTHREAD
pthread_sigmask(SIG_SETMASK, saved_signals, NULL);
#endif
}
static void
restore_terminal(struct termios *saved_term, FILE *term)
{
tcsetattr(fileno(term), TCSAFLUSH, saved_term);
}
#endif
void
safestr_convert(safestr_t str, u_int32_t flags)
{
char *cptr;
u_int32_t i = 0;
isafestr_t istr;
XXL_ASSET_BLOCK_BEGIN
{
istr = safestr_get(str, SAFESTR_GET_WRITABLE);
if (flags & SAFESTR_CONVERT_UPPERCASE)
{
for (cptr = istr->str; i < istr->hdr.length; i++, cptr++)
*cptr = (char)safestr_casemap_upper[(int)(unsigned char)*cptr];
}
else if (flags & SAFESTR_CONVERT_LOWERCASE)
{
for (cptr = istr->str; i < istr->hdr.length; i++, cptr++)
*cptr = (char)safestr_casemap_lower[(int)(unsigned char)*cptr];
}
else if (flags & SAFESTR_CONVERT_TITLECASE)
{
for (cptr = istr->str; i < istr->hdr.length; i++, cptr++)
{
if (i + 2 < istr->hdr.length &&
safestr_casemap_lower[(int)(unsigned char)*cptr] == 'm' &&
safestr_casemap_lower[(int)(unsigned char)*(cptr + 1)] == 'c')
{
*cptr = (char)safestr_casemap_upper[(int)(unsigned char)*cptr];
i++; cptr++;
*cptr = (char)safestr_casemap_lower[(int)(unsigned char)*cptr];
i++; cptr++;
*cptr = (char)safestr_casemap_upper[(int)(unsigned char)*cptr];
}
else if (cptr == istr->str)
{
*cptr = (char)safestr_casemap_upper[(int)(unsigned char)*cptr];
}
else if (i + 1 < istr->hdr.length && !alpha_bytes[(int)(unsigned char)*cptr])
{
i++, cptr++;
*cptr = (char)safestr_casemap_upper[(int)(unsigned char)*cptr];
}
else
{
/* Last resort ... lowercase it */
*cptr = (char)safestr_casemap_lower[(int)(unsigned char)*cptr];
}
}
}
}
XXL_ASSET_BLOCK_END;
}
safestr_t
safestr_do_getpassword(FILE *term, safestr_t prompt, const char *file, unsigned int lineno)
{
int done = 0, termfd;
char ch;
#ifndef WIN32
sigset_t saved_signals, set_signals;
#endif
safestr_t str;
u_int32_t actual_length;
isafestr_t iorig, iprompt, istr;
#ifndef WIN32
struct termios saved_term, set_term;
#endif
XXL_ASSET_BLOCK_BEGIN
{
if (!term && !(term = xxl_fopen(_PATH_TTY, "r+", XXL_ASSET_TEMPORARY)))
XXL_THROW_ERROR(errno, 0);
termfd = fileno(term);
iprompt = safestr_get(prompt, SAFESTR_GET_READONLY);
fprintf(term, "%s", iprompt->str);
fflush(term);
#ifndef WIN32
/* Defer interrupt when echo is turned off */
sigemptyset(&set_signals);
sigaddset(&set_signals, SIGINT);
sigaddset(&set_signals, SIGTSTP);
#ifdef HAVE_PTHREAD
pthread_sigmask(SIG_BLOCK, &set_signals, &saved_signals);
#endif
XXL_ASSET_SAVE(&saved_signals, restore_signals, NULL, XXL_ASSET_TEMPORARY);
/* Set the terminal to not echo */
tcgetattr(termfd, &saved_term);
set_term = saved_term;
set_term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
tcsetattr(termfd, TCSAFLUSH, &set_term);
XXL_ASSET_SAVE(&saved_term, restore_terminal, term, XXL_ASSET_TEMPORARY);
#endif
/* Allocate a string to hold the user input */
str = safestr_do_alloc(SAFESTR_READLINE_SIZE, 0, file, lineno);
XXL_ASSET_SAVE(str, cleanup_safestr, NULL, XXL_ASSET_PROMOTE);
istr = iorig = safestr_get(str, SAFESTR_GET_WRITABLE);
/* Read the password from the terminal user */
while (!done)
{
switch (read(termfd, &ch, 1))
{
case 1:
if (ch != '\n')
break;
/* FALL THROUGH */
case 0:
istr->str[istr->hdr.length] = '\0';
done = 1;
break;
case -1:
XXL_THROW_ERROR(errno, NULL);
break;
}
istr->str[istr->hdr.length++] = ch;
if (istr->hdr.length == istr->hdr.size)
{
actual_length = istr->hdr.length;
istr = safestr_resize(istr, actual_length + SAFESTR_READLINE_SIZE);
istr->hdr.length = actual_length;
}
}
if (istr->str[istr->hdr.length - 1] == '\n')
istr->str[--istr->hdr.length] = '\0';
putc('\n', term);
str = safestr_complete(iorig, istr);
}
XXL_ASSET_BLOCK_END;
return str;
}
safestr_t
safestr_do_readline(FILE *f, const char *file, unsigned int lineno)
{
int eof_reached = 0;
safestr_t str;
u_int32_t actual_length;
isafestr_t iorig, istr;
XXL_ASSET_BLOCK_BEGIN
{
str = safestr_do_alloc(SAFESTR_READLINE_SIZE, 0, file, lineno);
XXL_ASSET_SAVE(str, cleanup_safestr, NULL, XXL_ASSET_PROMOTE);
istr = iorig = safestr_get(str, SAFESTR_GET_WRITABLE);
while (!eof_reached)
{
if (!fgets(istr->str + istr->hdr.length,
istr->hdr.size - istr->hdr.length + 1, f))
{
if (!feof(f))
XXL_THROW_ERROR(errno, NULL);
eof_reached = 1;
break;
}
/* we have to play some games with string length here, because
* safestr_resize() sets the length to the requested length, but
* we don't actually want that.
*/
actual_length = istr->hdr.length + strlen(istr->str + istr->hdr.length);
if (istr->str[actual_length - 1] == '\n')
{
istr->str[--actual_length] = '\0';
if (istr->str[actual_length - 1] == '\r')
istr->str[--actual_length] = '\0';
istr->hdr.length = actual_length;
break;
}
istr->hdr.length = actual_length;
istr = safestr_resize(istr, actual_length + SAFESTR_READLINE_SIZE);
istr->hdr.length = actual_length;
}
str = safestr_complete(iorig, istr);
}
XXL_ASSET_BLOCK_END;
if (eof_reached)
{
safestr_free(str);
return NULL;
}
return str;
}
double
safestr_todouble(safestr_t str)
{
char *end;
double result;
isafestr_t istr;
XXL_ASSET_BLOCK_BEGIN
{
istr = safestr_get(str, SAFESTR_GET_READONLY);
result = strtod(istr->str, &end);
if (end && (*end || end == istr->str))
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT, end);
}
XXL_ASSET_BLOCK_END;
return result;
}
int32_t
safestr_toint32(safestr_t str, int base)
{
safestr_integer_t result;
XXL_ASSET_BLOCK_BEGIN
{
parse_integer(safestr_get(str, SAFESTR_GET_READONLY), &result, base, 32, 1);
}
XXL_ASSET_BLOCK_END;
return result.s32;
}
int64_t
safestr_toint64(safestr_t str, int base)
{
safestr_integer_t result;
XXL_ASSET_BLOCK_BEGIN
{
parse_integer(safestr_get(str, SAFESTR_GET_READONLY), &result, base, 64, 1);
}
XXL_ASSET_BLOCK_END;
return result.s64;
}
u_int32_t
safestr_touint32(safestr_t str, int base)
{
safestr_integer_t result;
XXL_ASSET_BLOCK_BEGIN
{
parse_integer(safestr_get(str, SAFESTR_GET_READONLY), &result, base, 32, 0);
}
XXL_ASSET_BLOCK_END;
return result.u32;
}
u_int64_t
safestr_touint64(safestr_t str, int base)
{
safestr_integer_t result;
XXL_ASSET_BLOCK_BEGIN
{
parse_integer(safestr_get(str, SAFESTR_GET_READONLY), &result, base, 64, 0);
}
XXL_ASSET_BLOCK_END;
return result.u64;
}
void
safestr_trim(safestr_t str, u_int32_t flags)
{
char *cptr;
u_int32_t length;
isafestr_t istr;
XXL_ASSET_BLOCK_BEGIN
{
istr = safestr_get(str, SAFESTR_GET_WRITABLE);
length = istr->hdr.length;
if (!(flags & (SAFESTR_TRIM_BOTH)))
flags = SAFESTR_TRIM_BOTH;
if (flags & SAFESTR_TRIM_LEADING)
{
for (cptr = istr->str;
length > 0 && space_bytes[(int)(unsigned char)*cptr];
cptr++)
{
length--;
}
memmove(istr->str, cptr, length + 1);
}
if (flags & SAFESTR_TRIM_TRAILING)
{
while (length > 0 && space_bytes[(int)(unsigned char)istr->str[length - 1]])
length--;
istr->str[length] = '\0';
}
istr->hdr.length = length;
}
XXL_ASSET_BLOCK_END;
}
syntax highlighted by Code2HTML, v. 0.9.1