Exemple #1
0
/**
 * @brief	Get the subtype of the Content-Type header value from a mime header.
 * @note	For example in the case of a Content-Type of 'text/plain', "plain" would be the subtype.
 * @param	header	a placer pointing to the mime header to be parsed.
 * @return	a managed string containing the content subtype of the header, with "plain" as the default.
 */
stringer_t * mail_mime_type_sub(placer_t header) {

	chr_t *stream;
	stringer_t *line, *result;
	size_t remaining, characters = 0;

	if (!(line = mail_header_fetch_cleaned(&header, PLACER("Content-Type", 12)))) {
		return st_import("plain", 5);
	}

	stream = st_char_get(line);
	remaining = st_length_get(line);

	// Advance past any garbage.
	while (remaining && (*stream == '\n' || *stream == '\r' || *stream == ' ' || *stream == '\t' || *stream == '"')) {
		stream++;
		remaining--;
	}

	// Count how many characters are in the group. Based on RFC4288, section 4.2.
	while (remaining && ((*stream >= 'a' && *stream <= 'z') || (*stream >= 'A' && *stream <= 'Z') || (*stream >= '0' && *stream <= '9') ||
		*stream == '!' || *stream == '#' || *stream == '$' || *stream == '&' || *stream == '.' || *stream == '+' || *stream == '-' ||
		*stream == '^' || *stream == '_')) {
		stream++;
		remaining--;
	}

	// Make sure we got something back. Use a default value of plain.
	if (!remaining || *stream != '/') {
		st_free(line);
		return st_import("plain", 5);
	}

	// Advance past the separator.
	stream++;
	remaining--;

	// Count how many characters are in the subtype. Based on RFC4288, section 4.2.
	while (remaining != 0 && ((*stream >= 'a' && *stream <= 'z') || (*stream >= 'A' && *stream <= 'Z') || (*stream >= '0' && *stream <= '9') ||
		*stream == '!' || *stream == '#' || *stream == '$' || *stream == '&' || *stream == '.' || *stream == '+' || *stream == '-' ||
		*stream == '^' || *stream == '_')) {
		stream++;
		remaining--;
		characters++;
	}

	// Make sure we got something back. Use a default value of text.
	if (!characters) {
		st_free(line);
		return st_import("plain", 5);
	}

	result = st_import(stream - characters, characters);
	st_free(line);

	return result;
}
Exemple #2
0
/**
 * @brief	Get the key name of a mime header line parameter.
 * @param	parameter	a managed string containing the complete parameter (key/value pair) of the mime header line.
 * @return	NULL on failure or a managed string containing the mime header parameter key name on success.
 */
stringer_t * mail_mime_type_parameters_key(stringer_t *parameter) {

	chr_t *stream;
	size_t length, characters = 0;

	if (!parameter) {
		return NULL;
	}

	length = st_length_get(parameter);
	stream = st_char_get(parameter);

	// Skip the garbage.
	while (length != 0 && *stream == ' ') {
		stream++;
		length--;
	}

	// Find the end of the key.
	while (length > 0 && *stream != '=') {
		characters++;
		length--;
		stream++;
	}

	// Make sure we got something back.
	if (!characters) {
		return NULL;
	}

	return st_import(stream - characters, characters);
}
Exemple #3
0
/**
 * @brief	Return an ECIES public key as a null-terminated hex string.
 * @param	key	the input ECIES key pair.
 * @return	NULL on failure, or the hex-formatted public key as a null-terminated string.
 */
stringer_t * deprecated_ecies_key_public_hex(EC_KEY *key) {

	char *hex;
	const EC_POINT *point;
	const EC_GROUP *group;
	stringer_t *result = NULL;

	if (!(point = EC_KEY_get0_public_key_d(key))) {
		log_info("No public key available. {%s}", ssl_error_string(MEMORYBUF(256), 256));
		return NULL;
	} else if (!(group = EC_KEY_get0_group_d(key))) {
		log_info("No group available. {%s}", ssl_error_string(MEMORYBUF(256), 256));
		return NULL;
	} else if (!(hex = EC_POINT_point2hex_d(group, point, POINT_CONVERSION_COMPRESSED, NULL))) {
		log_info("Unable to serialize the public key into hex. {%s}", ssl_error_string(MEMORYBUF(256), 256));
		return NULL;
	}

	if (!(result = st_import(hex, ns_length_get(hex) + 1))) {
		log_info("Unable to make copy of ECIES public key.");
	}

	OPENSSL_free_d(hex);

	return result;
}
Exemple #4
0
/**
 * @brief	Get the value of a mime header line parameter.
 * @param	parameter	a managed string containing the complete parameter (key/value pair) of the mime header line.
 * @return	NULL on failure or a managed string containing the mime header parameter value on success.
 */
stringer_t * mail_mime_type_parameters_value(stringer_t *parameter) {

	chr_t *stream;
	size_t length, characters = 0;

	if (!parameter) {
		return NULL;
	}

	length = st_length_get(parameter);
	stream = st_char_get(parameter);

	// Skip the garbage.
	while (length && *stream == ' ') {
		stream++;
		length--;
	}

	// Find the end of the key.
	while (length && *stream != '=') {
		length--;
		stream++;
	}

	if (!length || *stream != '=') {
		return NULL;
	}

	// Skip any remaining garbage.
	while (length && (*stream == ' ' || *stream == '=' || *stream == '\"')) {
		stream++;
		length--;
	}

	// Find the length of the value.
	while (length && *stream != '"') {
		characters++;
		length--;
		stream++;
	}

	// Make sure we got something back.
	if (!characters) {
		return NULL;
	}

	// Remove the trailing space. The header cleanup function checks for consecutive spaces.
	if (*stream == ' ') {
		stream--;
		characters--;
	}

	// Make sure we got something back.
	if (!characters) {
		return NULL;
	}

	return st_import(stream - characters, characters);
}
Exemple #5
0
/**
 * @brief	Get the content encoding type from a mime header.
 * @note	This function parses the Content-Transfer-Encoding field of the header, returning "7bit" by default.
 * @param	header	 a placer pointing to the mime header to be parsed.
 * @return	a managed string containing the content encoding type value of the header, with "7bit" as default.
 */
stringer_t * mail_mime_content_encoding(placer_t header) {

	chr_t *stream;
	stringer_t *holder, *result = NULL;
	size_t remaining, characters = 0;

	if (!(holder = mail_header_fetch_cleaned(&header, PLACER("Content-Transfer-Encoding", 25)))) {
		return st_import("7bit", 4);
	}

	remaining = st_length_get(holder);
	stream = st_char_get(holder);

	// Advance past any garbage.
	while (remaining != 0 && (*stream == '\n' || *stream == '\r' || *stream == ' ' || *stream == '\t')) {
		stream++;
		remaining--;
	}

	// Count how many characters are in the group. Based on RFC2045, section 6.1.
	while (remaining != 0 && ((*stream >= 'a' && *stream <= 'z') || (*stream >= 'A' && *stream <= 'Z') || (*stream >= '0' && *stream <= '9') ||
		*stream == '-')) {
		stream++;
		remaining--;
		characters++;
	}

	// Make sure we got something back. Use a default value of 7bit.
	if (characters != 0) {
		result = st_import(stream - characters, characters);
	}
	else {
		result = st_import("7bit", 4);
	}

	st_free(holder);

	return result;
}
Exemple #6
0
bool_t check_string_import(void) {

	stringer_t *s;

	if (!(s = st_import(st_data_get(string_check_constant), st_length_int(string_check_constant)))) {
		return false;
	}

	if (memcmp(st_char_get(s), st_char_get(string_check_constant), st_length_get(string_check_constant))) {
		st_free(s);
		return false;
	}

	st_free(s);

	return true;
}
Exemple #7
0
bool_t check_string_import(void) {

	stringer_t *s;

	if (!(s = st_import(st_data_get(constant), st_length_int(constant)))) {
		return false;
	}

	log_print("%28.28s = %.*s", "duplicate", st_length_int(s), st_char_get(s));

	if (memcmp(st_char_get(s), st_char_get(constant), st_length_get(constant))) {
		st_free(s);
		return false;
	}

	st_free(s);

	return true;
}
Exemple #8
0
// Extracts an nil string. Not quite as liberal as the atomic string.
int_t imap_parse_nstring(stringer_t **output, chr_t **start, size_t *length, chr_t type) {

	chr_t *holder;
	size_t left;
	stringer_t *result;

	// Get setup.
	holder = *start;
	left = *length;
	*output = NULL;

	// Advance until we have a break character.
	while (left != 0 && *holder >= '!' && *holder <= '~' && *holder != '"' && *holder != '[' && *holder != type) {
		holder++;
		left--;
	}

	// There was nothing to process or we hit an invalid character.
	if (*start == holder || (*holder != ' ' && *holder != '\r' && *holder != '\n' && *holder != '[' && *holder != type)) {
		return -1;
	}

	// Create a stringer with the result.
	if (!(result = st_import(*start, holder - *start))) {
		log_error("Unable to extract the nil string.");
		return -1;
	}

	// Advance to the next argument.
	if (left != 0 && *holder != type && *holder != '[') {
		holder++;
		left--;
	}

	// Update
	*start = holder;
	*length = left;
	*output = result;

	return 1;
}
END_TEST

START_TEST(check_parser_header) {

	dmime_common_headers_t *header1, *header2;
	int res = 0;
	size_t outsize;
	unsigned char *formatted;

	header1 = dime_prsr_headers_create();
	header1->headers[HEADER_TYPE_DATE] = st_import("11:34:12 AM March 12, 2004", 26);
	header1->headers[HEADER_TYPE_TO] = st_import("*****@*****.**", 13);
	header1->headers[HEADER_TYPE_CC] = st_import("*****@*****.**", 16);
	header1->headers[HEADER_TYPE_FROM] = st_import("*****@*****.**", 22);
	header1->headers[HEADER_TYPE_ORGANIZATION] = st_import("Cool people organization", 24);
	header1->headers[HEADER_TYPE_SUBJECT] = st_import("here's stuff", 12);

	formatted = dime_prsr_headers_format(header1, &outsize);
	ck_assert_msg(formatted != NULL, "Failed to format common headers.\n");

	header2 = dime_prsr_headers_parse(formatted, outsize);
	ck_assert_msg(header2 != NULL, "Failed to parse common headers.\n");

	res = st_cmp_cs_eq(header1->headers[HEADER_TYPE_DATE], header2->headers[HEADER_TYPE_DATE]);
	ck_assert_msg(res == 0, "Date header was corrupted.\n");

	res = st_cmp_cs_eq(header1->headers[HEADER_TYPE_TO], header2->headers[HEADER_TYPE_TO]);
	ck_assert_msg(res == 0, "To header was corrupted.\n");

	res = st_cmp_cs_eq(header1->headers[HEADER_TYPE_CC], header2->headers[HEADER_TYPE_CC]);
	ck_assert_msg(res == 0, "CC header was corrupted.\n");

	res = st_cmp_cs_eq(header1->headers[HEADER_TYPE_FROM], header2->headers[HEADER_TYPE_FROM]);
	ck_assert_msg(res == 0, "From header was corrupted.\n");

	res = st_cmp_cs_eq(header1->headers[HEADER_TYPE_ORGANIZATION], header2->headers[HEADER_TYPE_ORGANIZATION]);
	ck_assert_msg(res == 0, "Organization header was corrupted.\n");

	res = st_cmp_cs_eq(header1->headers[HEADER_TYPE_SUBJECT], header2->headers[HEADER_TYPE_SUBJECT]);
	ck_assert_msg(res == 0, "Subject header was corrupted.\n");

	dime_prsr_headers_destroy(header1);
	dime_prsr_headers_destroy(header2);
	free(formatted);

	fprintf(stderr, "DMIME common header parsing complete.\n");
}
Exemple #10
0
/**
 * @brief	Get the content id from a mime header.
 * @note	This function parses the Content-Id field of the header, returning "7bit" by default.
 * @param	header	 a placer pointing to the mime header to be parsed.
 * @return	NULL on failure, or a managed string containing the content id value of the header.
 */
stringer_t * mail_mime_content_id(placer_t header) {

	chr_t *stream;
	stringer_t *holder, *result = NULL;
	size_t remaining, characters = 0;

	if (!(holder = mail_header_fetch_cleaned(&header, PLACER("Content-Id", 10)))) {
		return NULL;
	}

	remaining = st_length_get(holder);
	stream = st_char_get(holder);

	// Advance past any garbage.
	while (remaining != 0 && (*stream == '\n' || *stream == '\r' || *stream == ' ' || *stream == '\t')) {
		stream++;
		remaining--;
	}

	// Count how many characters are in the group. Based on RFC2822, section 3.6.4.
	while (remaining != 0 && ((*stream >= 'a' && *stream <= 'z') || (*stream >= 'A' && *stream <= 'Z') || (*stream >= '0' && *stream <= '9') ||
		*stream == '<' || *stream == '@' || *stream == '!' || *stream == '#' || *stream == '$' || *stream == '%' || *stream == '&' || *stream == '\'' ||
		*stream == '*' || *stream == '+' || *stream == '-' || *stream == '/' || *stream == '=' || *stream == '?' || *stream == '^' || *stream == '_' ||
		*stream == '`' || *stream == '{' || *stream == '|' || *stream == '}' || *stream == '~' || *stream == '[' || *stream == ']' || *stream == '.' || *stream == '>')) {
		stream++;
		remaining--;
		characters++;
	}

	// Make sure we got something back.
	if (characters != 0) {
		result = st_import(stream - characters, characters);
	}

	st_free(holder);

	return result;
}
static stringer_t *mkstringer(const char *s) {

	stringer_t *st = st_import(s, strlen(s) + 1);
	ck_assert_ptr_ne(NULL, st);
	return st;
}
Exemple #12
0
/// HIGH: Return a result structure with the disposition, confidence, probability and signature data. Then store the analysis result with the message. Either in the DB or
/// by adding a custom header to the message. Return codes should become 0 for success, or a negative integer for errors. Add statistical updates to check and train functions.
/// Result: result=\"%s\"; class=\"%s\"; probability=%01.4f; confidence=%02.2f; signature=%lu; key=%lu;
/// Layman's terms: How spammy is this message
int_t dspam_check(uint64_t usernum, stringer_t *message, stringer_t **signature) {

	DSPAM_CTX *ctx;
	chr_t unum[20];
	uint32_t connection;
	int_t result = 2, ret;
	struct _mysql_drv_dbh dbh;
	stringer_t *tmpdir, *output;

	// Generate a string version of the dispatch number.
	if (snprintf(unum, 20, "%lu", usernum) <= 0 || !(tmpdir = spool_path(MAGMA_SPOOL_DATA))) {
		log_pedantic("Context setup error.");
		return -1;
	}
	// Initialize the DSPAM context.
	else if (!(ctx = dspam_create_d(unum, NULL, st_char_get(tmpdir), DSM_PROCESS, DSF_SIGNATURE | DSF_NOISE | DSF_WHITELIST))) {
		log_pedantic("An error occurred inside the DSPAM library. {dspam_create = NULL}");
		st_free(tmpdir);
    return -1;
 	}

	st_free(tmpdir);

	if (pool_pull(sql_pool, &connection) != PL_RESERVED) {
		log_info("Unable to get an available connection for the query.");
		dspam_destroy_d(ctx);
		return -1;
	}
	else if (sql_ping(connection) < 0 || !stmt_rebuild(connection)) {
		log_info("The database connection has been lost and the reconnection attempt failed.");
		dspam_destroy_d(ctx);
		return -1;
	}

	// Setup the database handle in a structure format.
	dbh.dbh_read = pool_get_obj(sql_pool, connection);
	dbh.dbh_write = pool_get_obj(sql_pool, connection);

	if ((ret = dspam_attach_d(ctx, &dbh))) {
   	if (dspam_detach_d(ctx) != 0) {
			log_pedantic("Could not detach the DB connection.");
		}
		pool_release(sql_pool, connection);
		log_pedantic("An error occurred while attaching to the statistical database. {dspam_attach = %i}", ret);
		dspam_destroy_d(ctx);
		return -1;
  }

	// Tokenization method and statistical algorithm.
	ctx->algorithms = DSA_GRAHAM | DSA_BURTON | DSP_GRAHAM;
	ctx->tokenizer = DSZ_CHAIN;

	// To prevent the message tokens from being stored in the database we disable training.
	ctx->training_mode = DST_NOTRAIN;

	// This actually processes the message.
	if ((ret = dspam_process_d(ctx, st_char_get(message)))) {
		if (dspam_detach_d(ctx) != 0) {
			log_pedantic("Could not detach the DB connection.");
		}
		pool_release(sql_pool, connection);
		log_pedantic("An error occurred while analyzing an email with DSPAM. {dspam_process = %i}", ret);
		dspam_destroy_d(ctx);
    return -1;
	}

	// We assume that the SQL connection will no longer be needed.
	if ((ret = dspam_detach_d(ctx))) {
		log_pedantic("Could not detach the DB connection. {dspam_detach = %i}", ret);
		pool_release(sql_pool, connection);
		dspam_destroy_d(ctx);
    return -1;
	}

	// Return the connection to our pool.
	pool_release(sql_pool, connection);

	// Check to see if the message is junk mail.
	if (ctx->result == DSR_ISSPAM) {
		result = -2;
	}
	else {
		result = 1;
	}

	//log_pedantic("Probability: %2.4f Confidence: %2.4f, Result: %s", ctx->probability, ctx->confidence,
 	//	(ctx->result == DSR_ISSPAM) ? "JUNK" : "INNOCENT");

	// See what happens if we don't get a signature back.
	if (ctx->signature == NULL) {
		log_error("DSPAM did not return a signature. {ctx->signature = NULL}");
		dspam_destroy_d(ctx);
    return result;
  }

	// Copy over the signature.
	if (!(output = st_import(ctx->signature->data, ctx->signature->length))) {
		log_pedantic("Could not import the statistical signature. {length = %lu}", ctx->signature->length);
		dspam_destroy_d(ctx);
		return result;
  }

	if (signature) {
		*signature = output;
	}

	// Destroy the context and return the result.
	dspam_destroy_d(ctx);
	return result;
}
Exemple #13
0
/**
 * @brief	Generate a captcha image for a specified character string.
 * @param	value	a managed string containing the text that is to become the basis of the captcha challenge.
 * @return	NULL on failure, or a managed string containing the path to the image file containing the captcha graphic on success.
 */
stringer_t * register_captcha_generate(stringer_t *value) {

	gdImagePtr image;
	chr_t string[2], *holder = NULL;
	double font_size = 40.0, angle;
	stringer_t *output = NULL, *font_path = NULL;
	chr_t *gderr;
	int_t brect[8], characters, color, increment;
	// int_t white;

	if (!(characters = st_length_get(value))) {
		log_pedantic("Zero length value passed in.");
		return NULL;
	}

	// We need a font when initializing the image.
	if (!(font_path = register_captcha_random_font())) {
		log_pedantic("Could not pick a random font.");
		return NULL;
	}

	mm_wipe(string, 2);

	if ((gderr = gdImageStringFT_d(NULL, &brect[0], 0, st_char_get(font_path), font_size, 0.0, 0, 0, string))) {
		log_pedantic("Could not initialize the rectangle: %s", gderr);
		return NULL;
	}

	// Creates an image that is 36 pixels wide for each character (+24 for the margin), and 47 pixels high.
	if (!(image = gdImageCreate_d((characters * 36) + 11, 47))) {
		log_pedantic("Could not create the image.");
		return NULL;
	}

	// The first color you allocate is used for the background.
	//white = gdImageColorResolve_d(image, 255, 255, 255);
	gdImageColorResolve_d(image, 255, 255, 255);

	// Write a bunch of randomly colored pixels onto the background.
	register_captcha_write_noise(image, (characters * 36) + 11, 47);

	mm_wipe(string, 2);

	// Write the string.
	for (increment = 0; increment < characters; increment++) {

		// Randomize the font size. By default it goes between 22px and 38px.
		font_size = 38.0 - (rand_get_uint32() % 16);

		// Pick a random angle.
		angle = 0.0;
		if ((rand_get_uint32() % 2) == 0) {
			angle -= rand_get_uint32() % 15;
		}
		else {
			angle += rand_get_uint32() % 25;
		}

		// Change the angle to radians.
		angle /= 360.0;
		angle *= (22.000/7.000);

		// Use the appropriate character.
		string[0] = *(st_char_get(value) + increment);

		// The character i needs to always be large, so people can see the upper dot.
		if (string[0] == 'i') {
			font_size = 38;
		}

		// Select a random font.
		if (!(font_path = register_captcha_random_font())) {
			gdImageDestroy_d(image);
			log_pedantic("Could not pick a random font.");
			return NULL;
		}

		// Select a random color, biased towards red.
		color = gdImageColorResolve_d(image, rand_get_uint32() % 256, rand_get_uint32() % 50, rand_get_uint32() % 50);

		// Write the character to the image.
		if (gdImageStringFT_d(image, &brect[0], color, st_char_get(font_path), font_size, angle, (36 * increment) + 12, 43 - ((40 - font_size) /2), string) != NULL) {
			gdImageDestroy_d(image);
			log_pedantic("Could not writer characters into the image.");
			return NULL;
		}
	}

	// Output Use gdImageJpegPtr_d to produce the output in JPEG.
	if (!(holder = gdImageGifPtr_d(image, &increment)) || !increment) {
		log_pedantic("Could not output the image to a buffer.");
	}
	else if (!(output = st_import(holder, increment))) {
		log_pedantic("Could not move the image into the output buffer.");
		gdFree_d(holder);
	}
	else {
		gdFree_d(holder);
	}

	gdImageDestroy_d(image);

	return output;
}
Exemple #14
0
prime_org_signet_t * org_signet_set(stringer_t *org) {

	prime_field_t *field = NULL;
	prime_object_t *object = NULL;
	prime_org_signet_t *result = NULL;

	if (!(object = prime_unpack(org))) {
		log_pedantic("Unable to parse the PRIME organizational signet.");
		return NULL;
	}
	else if (object->type != PRIME_ORG_SIGNET) {
		log_pedantic("The object passed in was not an organizational signet.");
		prime_object_free(object);
		return NULL;
	}

	else if (!(result = org_signet_alloc())) {
		log_pedantic("Unable to allocate a PRIME organizational signet.");
		prime_object_free(object);
		return NULL;
	}

	// Public signing key, verify the length and import the ed25519 public key.
	else if (!(field = prime_field_get(object, 1)) || st_length_get(&(field->payload)) != 32 ||
		!(result->signing = ed25519_public_set(&(field->payload)))) {
		log_pedantic("Unable to parse the PRIME organizational signing key.");
		prime_object_free(object);
		org_signet_free(result);
		return NULL;
	}

	// Public encryption key, verify the length and import the compressed secp256k1 public key.
	else if (!(field = prime_field_get(object, 3)) || st_length_get(&(field->payload)) != 33 ||
		!(result->encryption = secp256k1_public_set(&(field->payload)))) {
		log_pedantic("Unable to parse the PRIME organizational encryption key.");
		prime_object_free(object);
		org_signet_free(result);
		return NULL;
	}

	// Self signature taken over the cryptographic fields, verify the length and import the ed25519 signature.
	else if (!(field = prime_field_get(object, 4)) || st_length_get(&(field->payload)) != 64 ||
		!(result->signature = st_import(pl_data_get(field->payload), pl_length_get(field->payload)))) {
		log_pedantic("Unable to parse the PRIME organizational signet signature.");
		prime_object_free(object);
		org_signet_free(result);
		return NULL;
	}

	// We don't need the packed object context any more.
	prime_object_free(object);

	// Verify the signature.
	if (!org_signet_verify(result)) {
		log_pedantic("The PRIME organizational signet signature is invalid.");
		org_signet_free(result);
		return NULL;
	}

	return result;
}
Exemple #15
0
/**
 * @brief	Parse a json string array into a linked list of managed strings.
 * @param	json	a json object containing an array of strings.
 * @param	nout	if not NULL, an optional pointer to a size_t to receive the item count of the json string array.
 * @return	NULL on failure, or a pointer to an inx holder containing the specified json array contents as a collection of managed strings.
 */
inx_t * portal_parse_json_str_array (json_t *json, size_t *nout) {

	inx_t *result;
	stringer_t *istr;
	const chr_t *str;
	size_t count;
	multi_t key = { .type = M_TYPE_UINT64, .val.u64 = 0 };

	if (!json) {
		log_pedantic("Portal cannot parse null json array.");
		return NULL;
	// The json object must be an array.
	} else if (!json_is_array(json)) {
		log_pedantic("Portal cannot parse mistyped json array.");
		return NULL;
	} else if (!(result = inx_alloc(M_INX_LINKED, st_free))) {
		log_error("Portal could not allocate space to parse json array.");
		return NULL;
	}

	// If it's an empty array, return right away.
	if (!(count = json_array_size_d(json))) {

		if (nout) {
			*nout = 0;
		}

		return result;
	}

	// Before starting a SQL transaction check that all of the array values are positive integer values.
	for (size_t i = 0; i < count; i++) {

		if (!json_is_string(json_array_get_d(json, i))) {
			log_pedantic("Portal cannot parse json string array with non-string element.");
			inx_free(result);
			return NULL;
		}

		if (!(str = json_string_value_d(json_array_get_d(json, i)))) {
			log_pedantic("Portal encountered json string array with NULL element.");
			inx_free(result);
			return NULL;
		}

		if (!(istr = st_import(str, ns_length_get(str)))) {
			log_error("Portal could not import string from json array.");
			inx_free(result);
			return NULL;
		}

		key.val.u64 = i+1;

		if (!inx_insert(result, key, istr)) {
			log_error("Portal could not prepare data from json array.");
			inx_free(result);
			return NULL;
		}

	}

	if (nout) {
		*nout = count;
	}

	return result;
}

/**
 * @brief	Parse the context of a requested folder.
 * @note	If no context is specified, the "mail" context is assumed (PORTAL_ENDPOINT_CONTEXT_MAIL).
 * @param	context		a managed string containing the context (supported values are "mail", "contacts", "settings", and "help").
 * @return	PORTAL_ENDPOINT_CONTEXT_INVALID on failure, or the flag of the determined context on success.
 */
int_t portal_parse_context(stringer_t *context) {

	int_t result = PORTAL_ENDPOINT_CONTEXT_MAIL;

	if (!context) {
		return result;
	}

	// Parse the context.
	if (!st_cmp_ci_eq(context, PLACER("mail", 4))) {
		result = PORTAL_ENDPOINT_CONTEXT_MAIL;
	}
	else if (!st_cmp_ci_eq(context, PLACER("contacts", 8))) {
		result = PORTAL_ENDPOINT_CONTEXT_CONTACTS;
	}
	else if (!st_cmp_ci_eq(context, PLACER("settings", 8))) {
		result = PORTAL_ENDPOINT_CONTEXT_SETTINGS;
	}
	else if (!st_cmp_ci_eq(context, PLACER("help", 4))) {
		result = PORTAL_ENDPOINT_CONTEXT_HELP;
	}
	else {
		result = PORTAL_ENDPOINT_CONTEXT_INVALID;
	}

	return result;
}