373 lines
9.0 KiB
C
373 lines
9.0 KiB
C
/*
|
|
* Copyright 2008 Arsen Chaloyan
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <apr_uuid.h>
|
|
#include "apt_text_stream.h"
|
|
|
|
#define TOKEN_TRUE "true"
|
|
#define TOKEN_FALSE "false"
|
|
#define TOKEN_TRUE_LENGTH (sizeof(TOKEN_TRUE)-1)
|
|
#define TOKEN_FALSE_LENGTH (sizeof(TOKEN_FALSE)-1)
|
|
|
|
|
|
/** Navigate through the lines of the text stream (message) */
|
|
APT_DECLARE(apt_bool_t) apt_text_line_read(apt_text_stream_t *stream, apt_str_t *line)
|
|
{
|
|
char *pos = stream->pos;
|
|
const char *end = stream->text.buf + stream->text.length;
|
|
apt_bool_t eol = FALSE;
|
|
line->length = 0;
|
|
line->buf = pos;
|
|
/* while not end of stream */
|
|
while(pos < end) {
|
|
if(*pos == APT_TOKEN_CR) {
|
|
/* end of line detected */
|
|
line->length = pos - line->buf;
|
|
pos++;
|
|
if(pos < end && *pos == APT_TOKEN_LF) {
|
|
pos++;
|
|
}
|
|
eol = TRUE;
|
|
break;
|
|
}
|
|
else if(*pos == APT_TOKEN_LF) {
|
|
/* end of line detected */
|
|
line->length = pos - line->buf;
|
|
pos++;
|
|
eol = TRUE;
|
|
break;
|
|
}
|
|
pos++;
|
|
}
|
|
|
|
stream->pos = pos;
|
|
return eol;
|
|
}
|
|
|
|
/** Navigate through the headers (name:value pairs) of the text stream (message)
|
|
Valid headers are:
|
|
name:value<CRLF>
|
|
name: value<CRLF>
|
|
name: value<CRLF>
|
|
name: value<LF>
|
|
name:<CRLF> (only name, no value)
|
|
<CRLF> (empty header)
|
|
Malformed headers are:
|
|
name:value (missing end of line <CRLF>)
|
|
name<CRLF> (missing separator ':')
|
|
*/
|
|
APT_DECLARE(apt_bool_t) apt_text_header_read(apt_text_stream_t *stream, apt_pair_t *pair)
|
|
{
|
|
char *pos = stream->pos;
|
|
const char *end = stream->text.buf + stream->text.length;
|
|
apt_bool_t eol = FALSE;
|
|
apt_string_reset(&pair->name);
|
|
apt_string_reset(&pair->value);
|
|
/* while not end of stream */
|
|
while(pos < end) {
|
|
if(*pos == APT_TOKEN_CR) {
|
|
/* end of line detected */
|
|
if(pair->value.buf) {
|
|
/* set length of the value */
|
|
pair->value.length = pos - pair->value.buf;
|
|
}
|
|
pos++;
|
|
if(pos < end && *pos == APT_TOKEN_LF) {
|
|
pos++;
|
|
}
|
|
eol = TRUE;
|
|
break;
|
|
}
|
|
else if(*pos == APT_TOKEN_LF) {
|
|
/* end of line detected */
|
|
if(pair->value.buf) {
|
|
/* set length of the value */
|
|
pair->value.length = pos - pair->value.buf;
|
|
}
|
|
pos++;
|
|
eol = TRUE;
|
|
break;
|
|
}
|
|
else if(!pair->name.length) {
|
|
/* skip initial spaces and read name */
|
|
if(!pair->name.buf && *pos != APT_TOKEN_SP) {
|
|
pair->name.buf = pos;
|
|
}
|
|
if(*pos == ':') {
|
|
/* set length of the name */
|
|
pair->name.length = pos - pair->name.buf;
|
|
}
|
|
}
|
|
else if(!pair->value.length) {
|
|
/* skip initial spaces and read value */
|
|
if(!pair->value.buf && *pos != APT_TOKEN_SP) {
|
|
pair->value.buf = pos;
|
|
}
|
|
}
|
|
pos++;
|
|
}
|
|
|
|
stream->pos = pos;
|
|
/* if length == 0 && buf -> header is malformed */
|
|
return (eol && (pair->name.length || !pair->name.buf));
|
|
}
|
|
|
|
|
|
/** Navigate through the fields of the line */
|
|
APT_DECLARE(apt_bool_t) apt_text_field_read(apt_text_stream_t *stream, char separator, apt_bool_t skip_spaces, apt_str_t *field)
|
|
{
|
|
char *pos = stream->pos;
|
|
const char *end = stream->text.buf + stream->text.length;
|
|
if(skip_spaces == TRUE) {
|
|
while(pos < end && *pos == APT_TOKEN_SP) pos++;
|
|
}
|
|
|
|
field->buf = pos;
|
|
field->length = 0;
|
|
while(pos < end && *pos != separator) pos++;
|
|
|
|
field->length = pos - field->buf;
|
|
if(pos < end) {
|
|
/* skip the separator */
|
|
pos++;
|
|
}
|
|
|
|
stream->pos = pos;
|
|
return field->length ? TRUE : FALSE;
|
|
}
|
|
|
|
/** Scroll text stream */
|
|
APT_DECLARE(apt_bool_t) apt_text_stream_scroll(apt_text_stream_t *stream)
|
|
{
|
|
apr_size_t remaining_length = stream->text.buf + stream->text.length - stream->pos;
|
|
if(!remaining_length || remaining_length == stream->text.length) {
|
|
stream->pos = stream->text.buf + remaining_length;
|
|
return FALSE;
|
|
}
|
|
memmove(stream->text.buf,stream->pos,remaining_length);
|
|
stream->pos = stream->text.buf + remaining_length;
|
|
stream->text.length = remaining_length;
|
|
*stream->pos = '\0';
|
|
return TRUE;
|
|
}
|
|
|
|
/** Parse id@resource string */
|
|
APT_DECLARE(apt_bool_t) apt_id_resource_parse(const apt_str_t *str, char separator, apt_str_t *id, apt_str_t *resource, apr_pool_t *pool)
|
|
{
|
|
apt_str_t field = *str;
|
|
const char *pos = strchr(str->buf,separator);
|
|
if(!pos) {
|
|
return FALSE;
|
|
}
|
|
|
|
field.length = pos - field.buf;
|
|
if(field.length >= str->length) {
|
|
return FALSE;
|
|
}
|
|
apt_string_copy(id,&field,pool);
|
|
field.buf += field.length + 1;
|
|
field.length = str->length - (field.length + 1);
|
|
apt_string_copy(resource,&field,pool);
|
|
return TRUE;
|
|
}
|
|
|
|
/** Generate id@resource string */
|
|
APT_DECLARE(apt_bool_t) apt_id_resource_generate(const apt_str_t *id, const apt_str_t *resource, char separator, apt_str_t *str, apr_pool_t *pool)
|
|
{
|
|
apr_size_t length = id->length+resource->length+1;
|
|
char *buf = apr_palloc(pool,length+1);
|
|
memcpy(buf,id->buf,id->length);
|
|
buf[id->length] = separator;
|
|
memcpy(buf+id->length+1,resource->buf,resource->length);
|
|
buf[length] = '\0';
|
|
str->buf = buf;
|
|
str->length = length;
|
|
return TRUE;
|
|
}
|
|
|
|
/** Generate only the name ("name":) of the header */
|
|
APT_DECLARE(apt_bool_t) apt_text_header_name_generate(const apt_str_t *name, apt_text_stream_t *stream)
|
|
{
|
|
char *pos = stream->pos;
|
|
memcpy(pos,name->buf,name->length);
|
|
pos += name->length;
|
|
*pos++ = ':';
|
|
*pos++ = ' ';
|
|
stream->pos = pos;
|
|
return TRUE;
|
|
}
|
|
|
|
/** Parse name=value pair */
|
|
static apt_bool_t apt_pair_parse(apt_pair_t *pair, const apt_str_t *field, apr_pool_t *pool)
|
|
{
|
|
apt_text_stream_t stream;
|
|
stream.text = *field;
|
|
stream.pos = stream.text.buf;
|
|
|
|
/* read name */
|
|
if(apt_text_field_read(&stream,'=',TRUE,&pair->name) == FALSE) {
|
|
return FALSE;
|
|
}
|
|
|
|
/* read value */
|
|
apt_text_field_read(&stream,';',TRUE,&pair->value);
|
|
return TRUE;
|
|
}
|
|
|
|
/** Parse array of name-value pairs */
|
|
APT_DECLARE(apt_bool_t) apt_pair_array_parse(apt_pair_arr_t *arr, const apt_str_t *value, apr_pool_t *pool)
|
|
{
|
|
apt_str_t field;
|
|
apt_pair_t *pair;
|
|
apt_text_stream_t stream;
|
|
if(!arr || !value) {
|
|
return FALSE;
|
|
}
|
|
|
|
stream.text = *value;
|
|
stream.pos = stream.text.buf;
|
|
/* read name-value pairs */
|
|
while(apt_text_field_read(&stream,';',TRUE,&field) == TRUE) {
|
|
pair = apr_array_push(arr);
|
|
apt_pair_parse(pair,&field,pool);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/** Generate array of name-value pairs */
|
|
APT_DECLARE(apt_bool_t) apt_pair_array_generate(apt_pair_arr_t *arr, apt_text_stream_t *stream)
|
|
{
|
|
int i;
|
|
apt_pair_t *pair;
|
|
char *pos = stream->pos;
|
|
if(!arr) {
|
|
return FALSE;
|
|
}
|
|
|
|
for(i=0; i<arr->nelts; i++) {
|
|
pair = (apt_pair_t*)arr->elts + i;
|
|
if(i != 0) {
|
|
*pos++ = ';';
|
|
}
|
|
if(pair->name.length) {
|
|
memcpy(pos,pair->name.buf,pair->name.length);
|
|
pos += pair->name.length;
|
|
if(pair->value.length) {
|
|
*pos++ = '=';
|
|
memcpy(pos,pair->value.buf,pair->value.length);
|
|
pos += pair->value.length;
|
|
}
|
|
}
|
|
}
|
|
stream->pos = pos;
|
|
return TRUE;
|
|
}
|
|
|
|
/** Parse boolean-value */
|
|
APT_DECLARE(apt_bool_t) apt_boolean_value_parse(const apt_str_t *str, apt_bool_t *value)
|
|
{
|
|
if(!str->buf) {
|
|
return FALSE;
|
|
}
|
|
if(strncasecmp(str->buf,TOKEN_TRUE,TOKEN_TRUE_LENGTH) == 0) {
|
|
*value = TRUE;
|
|
return TRUE;
|
|
}
|
|
if(strncasecmp(str->buf,TOKEN_FALSE,TOKEN_FALSE_LENGTH) == 0) {
|
|
*value = FALSE;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/** Generate boolean-value */
|
|
APT_DECLARE(apt_bool_t) apt_boolean_value_generate(apt_bool_t value, apt_text_stream_t *stream)
|
|
{
|
|
if(value == TRUE) {
|
|
memcpy(stream->pos,TOKEN_TRUE,TOKEN_TRUE_LENGTH);
|
|
stream->pos += TOKEN_TRUE_LENGTH;
|
|
}
|
|
else {
|
|
memcpy(stream->pos,TOKEN_FALSE,TOKEN_FALSE_LENGTH);
|
|
stream->pos += TOKEN_FALSE_LENGTH;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/** Generate value plus the length (number of digits) of the value itself. */
|
|
APT_DECLARE(apt_bool_t) apt_var_length_value_generate(apr_size_t *value, apr_size_t max_count, apt_str_t *str)
|
|
{
|
|
/* (N >= (10^M-M)) ? N+M+1 : N+M */
|
|
apr_size_t temp;
|
|
apr_size_t count; /* M */
|
|
apr_size_t bounds; /* 10^M */
|
|
int length;
|
|
|
|
/* calculate count */
|
|
temp = *value;
|
|
count = 0;
|
|
do{count++; temp /= 10;} while(temp);
|
|
|
|
/* calculate bounds */
|
|
temp = count;
|
|
bounds = 1;
|
|
do{bounds *= 10; temp--;} while(temp);
|
|
|
|
if(*value >= bounds - count) {
|
|
count++;
|
|
}
|
|
|
|
*value += count;
|
|
if(count > max_count) {
|
|
return FALSE;
|
|
}
|
|
|
|
str->length = 0;
|
|
length = sprintf(str->buf, "%"APR_SIZE_T_FMT, *value);
|
|
if(length <= 0) {
|
|
return FALSE;
|
|
}
|
|
str->length = length;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/** Generate unique identifier (hex string) */
|
|
APT_DECLARE(apt_bool_t) apt_unique_id_generate(apt_str_t *id, apr_size_t length, apr_pool_t *pool)
|
|
{
|
|
char *hex_str;
|
|
apr_size_t i;
|
|
apr_size_t count;
|
|
apr_uuid_t uuid;
|
|
apr_uuid_get(&uuid);
|
|
|
|
hex_str = apr_palloc(pool,length+1);
|
|
|
|
count = length / 2;
|
|
if(count > sizeof(uuid)) {
|
|
count = sizeof(uuid);
|
|
}
|
|
for(i=0; i<count; i++) {
|
|
sprintf(hex_str+i*2,"%02x",uuid.data[i]);
|
|
}
|
|
hex_str[length] = '\0';
|
|
|
|
id->buf = hex_str;
|
|
id->length = length;
|
|
return TRUE;
|
|
}
|