Example #1
0
/**
 * @brief	Insert a spam signature training link into a plain text message part.
 * @see		mail_discover_insertion_point()
 * @param	server		the server object of the web server where the teacher application is hosted.
 * @param	message		a managed string containing the message body to be parsed.
 * @param	part		a managed string (placer) containing the part of the message where the signature should be inserted.
 * @param	signum		the spam signature number referenced by the teacher url.
 * @param	sigkey		the spam signature key for client verification in the teacher app.
 * @param	disposition	if 0, the message disposition is "innocent"; otherwise, the disposition specifies "spam".
 * @param	type		the encoding type of the message (MESSAGE_TYPE_HTML or other).
 * @param	encoding	if MESSAGE_ENCODING_QUOTED_PRINTABLE, set the encoding type to qp.
 * @return	NULL on failure or a managed string containing the message with the inserted signature training link on success.
 */
stringer_t * mail_insert_chunk_text(server_t *server, stringer_t *message, stringer_t *part, uint64_t signum, uint64_t sigkey, int_t disposition, int_t type, int_t encoding) {

	size_t point;
	stringer_t *pretext;
	stringer_t *posttext;
	stringer_t *result;
	stringer_t *signature;

	if (st_empty(part)) {
		return NULL;
	}

	// Start at the end of the chunk and search for where to insert the signature.
	if (!(signature = mail_build_signature(server, type, encoding, signum, sigkey, disposition))) {
		return NULL;
	}

	// Figure out the insertion point_t and create the new message.
	point = mail_discover_insertion_point(message, part, type);
	pretext = PLACER(st_char_get(message), point);
	posttext = PLACER(st_char_get(message) + point, st_length_get(message) - point);
	result = st_merge("sss", pretext, signature, posttext);

	st_free(signature);

	if (!result) {
		log_pedantic("Unable to merge the strings together.");
	}

	return result;
}
/**
 * @brief	Serialize an unsigned 16-bit integer into a managed string.
 * @note	This function will reallocate the output managed string if necessary, and append the serialized data, updating the length field to reflect the changes.
 * @param	data	a pointer to the address of a managed string to receive the output, which will be allocated for the caller if NULL is passed.
 * @param	number	the value of the unsigned 16-bit integer to be serialized.
 * @return	true if the specified value was serialized and stored successfully, or false on failure.
 */
bool_t serialize_uint16(stringer_t **data, uint16_t number) {
	
	stringer_t *holder = NULL;
	size_t length = uint16_digits(number);

	// Make sure we have room to write the number.
	if (!data) {
		return false;
	}
	else if (!*data && (*data = st_alloc(1024)) == NULL) {
		return false;
	}
	else if (st_avail_get(*data) - st_length_get(*data) < (length+1) && (holder = st_realloc(*data, st_avail_get(*data) + 1024)) == NULL) {
		return false;
	}
	else if (holder) {
		*data = holder;
	}
	
	if (length != snprintf(st_char_get(*data) + st_length_get(*data), st_avail_get(*data) - st_length_get(*data), "%hu", number)) {
		return false;
	}
	
	*(st_char_get(*data) + st_length_get(*data) + length) = ';';
	st_length_set(*data, st_length_get(*data) + length + 1);
	
	return true;
}
Example #3
0
bool_t check_string_dupe(char *type, uint32_t check) {

	size_t len;
	stringer_t *s, *d;

	if (!(s = st_alloc(check, st_length_int(constant)))) {
		return false;
	}

	len = snprintf(st_char_get(s), st_length_int(constant) + 1, "%.*s", st_length_int(constant), st_char_get(constant));
	if (check & MAPPED_T || check & MANAGED_T) {
		st_length_set(s, len);
	}

	if (!(d = st_dupe(s))) {
		st_free(s);
		return false;
	}

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

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

	st_free(s);
	st_free(d);

	return true;
}
Example #4
0
bool_t check_string_dupe(uint32_t check) {

	size_t len;
	stringer_t *s, *d;

	if (!(s = st_alloc_opts(check, st_length_int(string_check_constant)))) {
		return false;
	}

	len = snprintf(st_char_get(s), st_length_int(string_check_constant) + 1, "%.*s", st_length_int(string_check_constant), st_char_get(string_check_constant));
	if ((check & MAPPED_T) || (check & MANAGED_T)) {
		st_length_set(s, len);
	}

	if (!(d = st_dupe(s))) {
		st_free(s);
		return false;
	}

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

	st_free(s);
	st_free(d);

	return true;
}
Example #5
0
 /**
  * @brief	Acquire a named lock, with synchronization provided via memcached.
  * @see	cache_silent_add()
  * @note	The lock will be held for a maximum of 10 minutes, and failed locking attempts will be retried
  * 		periodically for a maxmimum of 1 minute before returing failure.
  * @param	key		a managed string containing the name of the lock to be acquired.
  * @return	-1 on general failure, 0 on memcached failure, or 1 on success.
  */
int_t lock_get(stringer_t *key) {

	uint64_t value;
	stringer_t *lock = MANAGEDBUF(128);
	int_t success, iterations = MAGMA_LOCK_TIMEOUT;
//	const struct timespec delay = { .tv_sec = 0, .tv_nsec = 1000000000 };
	const struct timespec delay = { .tv_sec = 1, .tv_nsec = 0 };

	// Build the key.
	if (st_empty(key) || st_sprint(lock, "%.*s.lock", st_length_int(key), st_char_get(key)) <= 0) {
		log_pedantic("Unable generate the accessor for the cluster lock.");
		return -1;
	}

	// Build the lock value.
	value = time(NULL);

	do {

		// Keep the lock for ten minutes.
		if ((success = cache_silent_add(lock, PLACER(&value, sizeof(uint64_t)), MAGMA_LOCK_EXPIRATION)) != 1) {
			nanosleep(&delay, NULL);
		}

	} while (success != 1 && iterations--);

#ifdef MAGMA_PEDANTIC
	if (success != 1) log_pedantic("Unable to obtain a cluster lock for %.*s.", st_length_int(lock), st_char_get(lock));
#endif

	return success;
}

/**
  * @brief	Release a named lock, with synchronization provided via memcached.
  * @see	cache_delete()
  * @note	The lock will be held for 10 seconds, and locking attempts will occur periodically for 60 seconds prior to failure.
  * @param	key		a managed string containing the name of the lock to be released.
  * @return	-1 on general failure, 0 on memcached failure, or 1 on success.
  */
void lock_release(stringer_t *key) {

	stringer_t *lock = MANAGEDBUF(128);

	// Build the key.
	if (st_empty(key) || st_sprint(lock, "%.*s.lock", st_length_int(key), st_char_get(key)) <= 0) {
		log_pedantic("Unable generate the accessor for the cluster lock.");
		return;
	}

	/// LOW: At some point we should add logic to check whether this cluster node even owns the lock before
	/// 	blindly deleting the lock.
	cache_delete(lock);
	return;
}
Example #6
0
/**
 * @brief	Get a placer pointing to a mime header in a mime part.
 * @param	part	a managed string containing the mime part text to be parsed.
 * @return	a placer pointing to the mime header at the start of the specified mime part.
 */
placer_t mail_mime_header(stringer_t *part) {

	chr_t *stream;
	size_t length;
	int_t header = 0;

	length = st_length_get(part);
	stream = st_data_get(part);

	for (size_t i = 0; i < length && header != 3; i++) {

		if (header == 0 && *stream == '\n') {
			header++;
		}
		else if (header == 1 && *stream == '\n') {
			header += 2;
		}
		else if (header == 1 && *stream == '\r') {
			header++;
		}
		else if (header == 2 && *stream == '\n') {
			header++;
		}
		else if (header != 0) {
			header = 0;
		}

		stream++;
	}

	return pl_init(st_data_get(part), stream - st_char_get(part));
}
Example #7
0
/**
 * @brief	Validate an email address.
 * @param	email	a managed string containing the email address to be validated.
 * @return	true if the specified email was valid and false if it was not.
 */
bool_t contact_business_valid_email(stringer_t *email) {

	chr_t *holder;
	size_t length, increment;


	if (!email) {
		return false;
	}

	// Lower case the string.
	lower_st(email);

	holder = st_char_get(email);
	length = st_length_get(email);

	if (!length) {
		return false;
	}

	// Catch leading at symbols.
	if (*holder == '@') {
		return false;
	}

	// The address.
	for (increment = 0; increment < length && *(holder + increment) != '@'; increment++) {
		if (*(holder + increment) < '!' || *(holder + increment) > '~') {
			return false;
		}
	}

	// Make sure we have an at symbol.
	if (increment == length || *(holder + increment++) != '@' || *(holder + increment) == '.' || increment == length) {
		return false;
	}

	// Look for the TLD seperator.
	while (increment < length && *(holder + increment) != '.') {
		if ((*(holder + increment) < 'a' || *(holder + increment) > 'z') && *(holder + increment) != '-') {
			return false;
		}
		increment++;
	}

	// Make sure we have a domain name.
	if (increment == length || *(holder + increment++) != '.' || increment == length) {
		return false;
	}

	// Go through the rest.
	while (increment < length) {
		if ((*(holder + increment) < 'a' || *(holder + increment) > 'z') && *(holder + increment) != '-' && *(holder + increment) != '.') {
			return false;
		}
		increment++;
	}

	return true;
}
Example #8
0
/**
 * @brief	Get the number of bytes taken up by a mail header.
 * @param	message		a managed string containing the full smtp mail message.
 * @return	0 on failure, or the number of bytes occupied by the mail header.
 */
size_t mail_header_end(stringer_t *message) {

	const chr_t *msg;
	size_t msglen;

	if (!message) {
		log_pedantic("Received a NULL message pointer.");
		return 0;
	}

	msglen = st_length_get(message);
	msg = st_char_get(message);

	for (size_t i = 0; i < msglen; i++) {
		if (msg[i] == '\n') {
			if (i + 1 < msglen && msg[i + 1] == '\n') {
				return i + 2;
			}
			if (i + 2 < msglen && msg[i + 1] == '\r' && msg[i + 2] == '\n') {
				return i + 3;
			}
		}
	}

	return msglen;
}
Example #9
0
/**
 * @brief	Insert a new contact entry into the database.
 * @param	usernum		the numerical id of the user to whom the contact entry belongs.
 * @param	foldernum	the numerical id of the parent folder to contain the contact entry.
 * @param	name		a pointer to a managed string containing the name of the new contact entry.
 * @return	-1 on failure, 0 if no item was inserted into the database, or the id of the newly inserted contact entry in the database on success.
 */
uint64_t contact_insert(uint64_t usernum, uint64_t foldernum, stringer_t *name) {

	MYSQL_BIND parameters[3];

	mm_wipe(parameters, sizeof(parameters));

	// Usernum
	parameters[0].buffer_type = MYSQL_TYPE_LONGLONG;
	parameters[0].buffer_length = sizeof(uint64_t);
	parameters[0].buffer = &usernum;
	parameters[0].is_unsigned = true;

	// Folder
	parameters[1].buffer_type = MYSQL_TYPE_LONGLONG;
	parameters[1].buffer_length = sizeof(uint64_t);
	parameters[1].buffer = &foldernum;
	parameters[1].is_unsigned = true;

	// Name
	parameters[2].buffer_type = MYSQL_TYPE_STRING;
	parameters[2].buffer_length = st_length_get(name);
	parameters[2].buffer = st_char_get(name);

	return stmt_insert(stmts.insert_contact, parameters);
}
Example #10
0
/**
 * @brief	Delete the specified contact detail of a contact entry from the database.
 * @param	contactnum	the numerical id of the contact entry to have the specified detail removed.
 * @param	key			a managed string containing the name of the contact detail to be removed from the entry.
 * @return	-1 on error, 0 if no matching detail was found in the database, or 1 if the delete operation was successful.
 */
int_t contact_detail_delete(uint64_t contactnum, stringer_t *key) {

	int64_t affected;
	MYSQL_BIND parameters[4];

	mm_wipe(parameters, sizeof(parameters));

	// Contact Number
	parameters[0].buffer_type = MYSQL_TYPE_LONGLONG;
	parameters[0].buffer_length = sizeof(uint64_t);
	parameters[0].buffer = &contactnum;
	parameters[0].is_unsigned = true;

	// Key
	parameters[1].buffer_type = MYSQL_TYPE_STRING;
	parameters[1].buffer_length = st_length_get(key);
	parameters[1].buffer = st_char_get(key);

	if ((affected = stmt_exec_affected(stmts.delete_contact_details, parameters)) == -1) {
		log_pedantic("The contact detail deletion request triggered an error. { contact = %lu / key = %.*s }", contactnum, st_length_int(key), st_char_get(key));
		return -1;
	}

	log_check(affected > 2);

	return (int_t)affected;
}
Example #11
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);
}
Example #12
0
/**
 * @brief	Delete a user config entry from the database by key.
 * @param	usernum		the numerical userid of the user to whom the requested config key belongs.
 * @param	key			a managed string containing the name of the config key to be deleted.
 * @return	1 if the config option was successfully deleted, 0 if the config key couldn't be found in the database, or -1 on general failure.
 */
int_t user_config_delete(uint64_t usernum, stringer_t *key) {

	uint64_t affected;
	MYSQL_BIND parameters[4];

	mm_wipe(parameters, sizeof(parameters));

	// User Number
	parameters[0].buffer_type = MYSQL_TYPE_LONGLONG;
	parameters[0].buffer_length = sizeof(uint64_t);
	parameters[0].buffer = &usernum;
	parameters[0].is_unsigned = true;

	// Key
	parameters[1].buffer_type = MYSQL_TYPE_STRING;
	parameters[1].buffer_length = st_length_get(key);
	parameters[1].buffer = st_char_get(key);

	if (!(affected = stmt_exec_affected(stmts.delete_user_config, parameters)) == (my_ulonglong)-1) {
		log_pedantic("The user config deletion request triggered an error. { user = %lu / key = %.*s }", usernum, st_length_int(key), st_char_get(key));
		return -1;
	}

	log_check(affected > 2);

	return (int_t)affected;
}
Example #13
0
/**
 * @brief	Count the number of instances of a boundary string inside a MIME body.
 * @note	The search is terminated if "--" is found right after the boundary string.
 * @param	body		a placer containing the body text to be parsed.
 * @param	boundary	a pointer to a managed string containing the boundary string for the MIME content.
 * @return	0 on failure, or the number of times the boundary string was located in the MIME body on success.
 */
uint32_t mail_mime_count(placer_t body, stringer_t *boundary) {

	uint32_t result = 0;
	chr_t *stream, *bounddata;
	size_t increment = 0, length, boundlen;

	if (pl_empty(body) || st_empty(boundary)) {
		return 0;
	}

	// Figure out the lengths.
	if (!(length = st_length_get(&body))) {
		log_pedantic("Cannot count boundary marker in zero-length MIME body..");
		return 0;
	}
	else if (!(boundlen = st_length_get(boundary))) {
		log_pedantic("Cannot count zero-length MIME boundary.");
		return 0;
	}

	// Setup.
	stream = st_char_get(&body);
	bounddata = st_char_get(boundary);

	// Find the start of the first part.
	while (increment + boundlen <= length) {

		if (mm_cmp_cs_eq(stream, bounddata, boundlen) == 0) {
			stream += boundlen + 1;
			increment += boundlen + 1;

			// Two dashes indicate the end of this mime sections.
			if (increment + 1 <= length && mm_cmp_cs_eq(stream, "--", 2) == 0) {
				increment = length + 1;
			}
			else {
				result++;
			}
		}
		else {
			stream++;
			increment++;
		}
	}

	return result;
}
Example #14
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);
}
Example #15
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;
}
Example #16
0
/**
 * @brief	Update a user contact entry in the database.
 * @param	contactnum		the numerical id of the contact entry to be modified.
 * @param	usernum			the numerical id of the user to whom the specified contact entry belongs.
 * @param	cur_folder		the numerical id of the parent contact containing the specified contact entry.
 * @param	target_folder	if not 0, sets the new parent contact folder to which the specified contact entry will belong.
 * @param	name			if not NULL, sets the new name of the specified contact entry.
 * @return	-1 on error, 0 if the specified contact entry was not found in the database, or 1 if the contact entry was successfully updated.
 */
int_t contact_update(uint64_t contactnum, uint64_t usernum, uint64_t cur_folder, uint64_t target_folder, stringer_t *name) {

	int64_t affected;
	MYSQL_BIND parameters[5];

	mm_wipe(parameters, sizeof(parameters));

	// Destination Folder
	if (target_folder) {
		parameters[0].buffer_type = MYSQL_TYPE_LONGLONG;
		parameters[0].buffer_length = sizeof(uint64_t);
		parameters[0].buffer = &target_folder;
		parameters[0].is_unsigned = true;
	}
	else {
		parameters[0].buffer_type = MYSQL_TYPE_LONGLONG;
		parameters[0].is_null = ISNULL(true);
	}

	// Name
	if (!st_empty(name)) {
		parameters[1].buffer_type = MYSQL_TYPE_STRING;
		parameters[1].buffer_length = st_length_get(name);
		parameters[1].buffer = st_char_get(name);
	}
	else {
		parameters[1].buffer_type = MYSQL_TYPE_LONGLONG;
		parameters[1].is_null = ISNULL(true);
	}

	// Contact Number
	parameters[2].buffer_type = MYSQL_TYPE_LONGLONG;
	parameters[2].buffer_length = sizeof(uint64_t);
	parameters[2].buffer = &contactnum;
	parameters[2].is_unsigned = true;

	// User Number
	parameters[3].buffer_type = MYSQL_TYPE_LONGLONG;
	parameters[3].buffer_length = sizeof(uint64_t);
	parameters[3].buffer = &usernum;
	parameters[3].is_unsigned = true;

	// Current Folder
	parameters[4].buffer_type = MYSQL_TYPE_LONGLONG;
	parameters[4].buffer_length = sizeof(uint64_t);
	parameters[4].buffer = &cur_folder;
	parameters[4].is_unsigned = true;

	// Since the updated column is always updated this function should only return 0 if the query doesn't match any rows, otherwise 1 to indicate success.
	if ((affected = stmt_exec_affected(stmts.update_contact, parameters)) == -1) {
		log_pedantic("The contact entry update triggered an error. { usernum = %lu / foldernum = %lu / contact = %lu }", usernum, cur_folder, contactnum);
		return -1;
	}

	log_check(affected > 2);

	return (int_t)affected;
}
Example #17
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;
}
Example #18
0
/**
 * @brief	Generate a MIME boundary string that is unique to a collection of content.
 * @param	parts	a pointer to an array of managed strings containing the MIME children data to be separated by the boundary.
 * @return	NULL on failure, or a pointer to a managed string containing the generated boundary on success.
 */
stringer_t * mail_mime_generate_boundary (array_t *parts) {

	stringer_t *result, *cmp;
	chr_t *ptr;
	size_t blen = 16;
	int_t rnd;

	if (!parts) {
		log_pedantic("Cannot generate mime boundary with empty input data.");
		return NULL;
	}

	if (!(result = st_alloc(blen))) {
		log_error("Unable to allocate space for boundary string.");
		return NULL;
	}

	// Keep generating boundary strings until one of them is unique... we don't expect collisions to happen that often.
	while (1) {
		srand(rand_get_uint64());
		ptr = st_char_get (result);

		// Generate blen's worth of random bytes, each being either a random digit or lowercase letter.
		for (size_t i = 0; i < blen; i++) {
			rnd = rand();

			if (rnd % 2) {
				*ptr++ = '0' + (rnd % 10);
			} else {
				*ptr ++ = 'a' + (rnd % 26);
			}

		}

		st_length_set(result, blen);

		// Now make sure it's not found in any of the parts.
		for (size_t i = 0; i < ar_length_get(parts); i++) {

			if (!(cmp = (stringer_t *) ar_field_ptr(parts, i))) {
				log_pedantic("Could not generate mime boundary for null content.");
				st_free(result);
				return NULL;
			}

			if (st_search_ci(cmp, result, NULL)) {
				continue;
			}

		}

		// If we made it this far, the boundary string was not found in any of the supplied content.
		break;
	}

	return result;
}
Example #19
0
bool_t check_string_print(void) {

	uint64_t total;
	bool_t result = true;
	stringer_t *strings[14];

	mm_set(strings, 0, sizeof(strings));

	strings[0] = st_print(NULLER_T | CONTIGUOUS | HEAP, "%.*s", st_length_int(constant), st_char_get(constant));
	strings[1] = st_print(NULLER_T | JOINTED | HEAP, "%.*s", st_length_int(strings[0]), st_char_get(strings[0]));
	strings[2] = st_print(BLOCK_T | CONTIGUOUS | HEAP, "%.*s", st_length_int(strings[1]), st_char_get(strings[1]));
	strings[3] = st_print(BLOCK_T | JOINTED | HEAP, "%.*s", st_length_int(strings[2]), st_char_get(strings[2]));
	strings[4] = st_print(MANAGED_T | CONTIGUOUS | HEAP, "%.*s", st_length_int(strings[3]), st_char_get(strings[3]));
	strings[5] = st_print(MANAGED_T | JOINTED | HEAP, "%.*s", st_length_int(strings[4]), st_char_get(strings[4]));
	strings[6] = st_print(MAPPED_T | JOINTED | HEAP, "%.*s", st_length_int(strings[5]), st_char_get(strings[5]));

	strings[7] = st_print(NULLER_T | CONTIGUOUS | SECURE, "%.*s", st_length_int(strings[6]), st_char_get(strings[6]));
	strings[8] = st_print(NULLER_T | JOINTED | SECURE, "%.*s", st_length_int(strings[7]), st_char_get(strings[7]));
	strings[9] = st_print(BLOCK_T | CONTIGUOUS | SECURE, "%.*s", st_length_int(strings[8]), st_char_get(strings[8]));
	strings[10] = st_print(BLOCK_T | JOINTED | SECURE, "%.*s", st_length_int(strings[9]), st_char_get(strings[9]));
	strings[11] = st_print(MANAGED_T | CONTIGUOUS | SECURE, "%.*s", st_length_int(strings[10]), st_char_get(strings[10]));
	strings[12] = st_print(MANAGED_T | JOINTED | SECURE, "%.*s", st_length_int(strings[11]), st_char_get(strings[11]));
	strings[13] = st_print(MAPPED_T | JOINTED | SECURE, "%.*s", st_length_int(strings[12]), st_char_get(strings[12]));

	for (int i = 0; i < 14 && strings[i]; i++) {
		for (unsigned int j = total = 0; strings[i] && j < st_length_get(strings[i]); j++) {
			total += *(st_char_get(strings[i]) + j);
		}

		if (total != 5366) {
			result = false;
		}
	}

	log_print("%28.28s = %s", "print", result ? "passed" : "failed");

	for (int i = 0; i < 14; i++) {
		if (strings[i])
			st_free(strings[i]);
	}

	return result;

}
Example #20
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;
}
Example #21
0
void tok_pop_init_st(tok_state_t *state, stringer_t *string, char token) {

	if (!state) {
		return;
	}

	state->token = token;
	state->block = state->position = (string == NULL ? NULL : st_char_get(string));
	state->length = state->remaining = (string == NULL ? 0 : st_length_get(string));
}
Example #22
0
/**
 * @brief	Insert a spam signature training link into a mail message.
 * @see		mail_modify_part()
 * @param	message			the mail message object of the message to be modified.
 * @param	server			the server object of the web server where the teacher application is hosted.
 * @param	signum			the spam signature number referenced by the teacher url.
 * @param	sigkey			the spam signature key for client verification in the teacher app.
 * @param	disposition		if 0, the message disposition is "innocent"; otherwise, the disposition specifies "spam".
 * @return	This function returns no value.
 */
void mail_signature_add(mail_message_t *message, server_t *server, uint64_t signum, uint64_t sigkey, int_t disposition) {

	stringer_t *part;

	part = PLACER(st_char_get(message->text), st_length_get(message->text));

	if ((mail_modify_part(server, message, part, signum, sigkey, disposition, 0)) == 0) {
		log_pedantic("------ MESSAGE ---------\n%.*s------------------", st_length_int(message->text), st_char_get(message->text));
	}

	return;
}
Example #23
0
// QUESTION: Why are we using managed strings here? It makes no sense, especially since we have expectations as to where they point.
// QUESTION: And why even have message? It seems like we could make do with just having part.
size_t mail_discover_insertion_point(stringer_t *message, stringer_t *part, int_t type) {

	chr_t *stream;
	size_t length;
	stringer_t *tag;

	// If the message is not HTML, return the end of the part as the insertion point.
	if (type != MESSAGE_TYPE_HTML) {
		return st_char_get(part) - st_char_get(message) + st_length_get(part);
	}

	// Get setup.
	length = st_length_get(part);
	stream = st_data_get(part) + length - 1;

	while (length) {

		// Reverse until we find a character that is not whitespace.
		while (length && (*stream == ' ' || *stream == '\r' || *stream == '\n' || *stream == '\t')) {
			length--;
			stream--;
		}

		if (!(tag = mail_extract_tag(stream, length))) {
			return (st_char_get(part) - st_char_get(message)) + length;
		}

		// What tag is it?
		if (st_cmp_ci_eq(tag, PLACER("</body>", 7)) != 0 && st_cmp_ci_eq(tag, PLACER("</html>", 7)) != 0) {
			st_free(tag);
			return (st_char_get(part) - st_char_get(message)) + length;
		}

		st_free(tag);

		// Advance past this tag.
		while (length && *stream != '<') {
			length--;
			stream--;
		}

		if (length && *stream == '<') {
			length--;
			stream--;
		}

	}

	return (st_char_get(part) - st_char_get(message)) + length;
}
placer_t imap_parse_address_breaker(stringer_t *address, uint32_t part) {

	size_t length;
	int_t status = 0;
	uint32_t position = 0;
	placer_t output = pl_null();
	chr_t *holder, *start = NULL, *end = NULL;

	if (address == NULL) {
		return pl_null();
	}

	length = st_length_get(address);
	holder = st_char_get(address);

	while (length > 0 && end == NULL) {

		if (position == part) {
			start = holder;
			position++;
		}

		// Inside quotes, nothing counts.
		if (status == 0 && *holder == '\"') {
			status = 1;
		}
		else if (status == 1 && *holder == '\"') {
			status = 0;
		}
		// Outside quotes, and we hit the break character.
		else if (status == 0 && position > part && *holder == ',') {
			end = holder;
		}
		else if (status == 0 && position < part && *holder == ',') {
			position++;
		}

		length--;
		holder++;
	}

	// If we hit the end.
	if (end == NULL) {
		end = holder;
	}

	if (start != NULL && end != NULL) {
		output = pl_init(start, end - start);
	}

	return output;
}
Example #25
0
// QUESTION: Should this function always return 1??
int_t mail_mime_update(mail_message_t *message) {

	placer_t part;

	if (message->mime) {
		mail_mime_free(message->mime);
	}

	part = pl_init(st_char_get(message->text), st_length_get(message->text));
	message->mime = mail_mime_part(&part, 1);

	return 1;
}
Example #26
0
/**
 * @brief	Get a specified chunk (mime part) of a multipart mime message.
 * @param	message		a managed string containing the mime message to be parsed.
 * @param	boundary	a managed string containing the boundary used to split the multipart mime message.
 * @param	chunk		the one-index based chunk to be retrieved from the multipart message
 * @return	NULL on failure or a placer containing the specified chunk on success.
 */
stringer_t * mail_get_chunk(stringer_t *message, stringer_t *boundary, int_t chunk) {

	int_t found = 0;
	stringer_t *result;
	size_t start = 0, length = 0, input = 0;

	while (chunk != 0) {

		// So on repeats we don't have to start all over again.
		if (length != 0) {
			start += length - 1;
		}

		found = 0;

		while (found == 0) {

			// Get the start of the MIME message part.
			if (!st_search_cs(PLACER(st_char_get(message) + start, st_length_get(message) - start), boundary, &input)) {
				log_pedantic("The boundary doesn't appear to be part of this message.");
				return NULL;
			}

			// Skip the boundary before searching again.
			start += input + st_length_get(boundary);

			// This will detect the section ending.
			if (st_length_get(message) - start >= 2 && mm_cmp_cs_eq(st_char_get(message) + start, "--", 2) == 1) {
				return NULL;
			}
			// Some broken implementations use similar boundaries. This should detect those.
			else if (st_length_get(message) - start > 0 && (*(st_char_get(message) + start) < '!' || *(st_char_get(message) + start) > '~')) {
				found = 1;
			}
		}

		found = 0;

		while (found == 0) {

			// Get the end.
			if (!st_search_cs(PLACER(st_char_get(message) + start, st_length_get(message) - start), boundary, &length)) {
				length = st_length_get(message) - start;
				found = 1;
			}
			else if (st_length_get(message) - start - length > 0 && (*(st_char_get(message) + start) < '!' || *(st_char_get(message) + start) > '~')) {
				found = 1;
			}

		}

		chunk--;
	}

	// Setup a placer with the chunk.
	result = PLACER(st_char_get(message) + start, length);

	return result;
}
/**
 * @brief	Serialize the contents of a managed string into another managed string.
 * @note	This function will reallocate the output managed string if necessary, and append the serialized data, updating the length field to reflect the changes.
 * @param	data	a pointer to the address of a managed string to receive the output, which will be allocated for the caller if NULL is passed.
 * @param	string	a pointer to a managed string containing the data to be serialized.
 * @return	true if the specified value was serialized and stored successfully, or false on failure.
 */
bool_t serialize_st(stringer_t **data, stringer_t *string) {
	
	size_t length;
	stringer_t *holder = NULL;
	
	// Make sure we have room to write the number.
	if (!data) {
		return false;
	}
	// Check for NULLs.
	if (!string || !(length = st_length_get(string))) {

		if (!serialize_sz(data, 0)) {
			return false;
		}

		return true;
	}
	// Record the length.
	else if (!serialize_sz(data, length)) {
		return false;
	}
	// Previous line ensures that *data should never be NULL.
	/*else if (!*data && (*data = st_alloc(length + (length % 1024))) == NULL) {
		return false;
	}*/
	else if (st_avail_get(*data) - st_length_get(*data) < length && (holder = st_realloc(*data, st_avail_get(*data) + length + (length % 1024))) == NULL) {
		return false;
	}
	else if (holder) {
		*data = holder;
	}
	
	mm_copy(st_char_get(*data) + st_length_get(*data), st_char_get(string), length);
	*(st_char_get(*data) + st_length_get(*data) + length) = ';';
	st_length_set(*data, st_length_get(*data) + length + 1);
	
	return true;
}
Example #28
0
END_TEST

START_TEST (check_mail_headers_s) {

	log_disable();
	bool_t result = true;
	stringer_t *errmsg = MANAGEDBUF(1024);

	if (status()) result = check_mail_headers_sthread(errmsg);

	log_test("MAIL / HEADERS / SINGLE THREADED:", errmsg);
	ck_assert_msg(result, st_char_get(errmsg));
}
Example #29
0
/**
 * @brief	Update a specified contact detail in the database, or insert it if it does not exist.
 * @param	contactnum	the numerical id of the contact entry to be modified.
 * @param	key			a managed string containing the name of the contact detail to be updated.
 * @param	value		a managed string containing the new value of the specified contact detail.
 * @param	flags		a bitmask of flags to be associated with the specified contact entry detail.
 * @return	-1 on error, 0 if no update was necessary, 1 if a new contact detail was inserted into the database, or 2 if the
 * 			specified contact detail was updated successfully.
 */
int_t contact_detail_upsert(uint64_t contactnum, stringer_t *key, stringer_t *value, uint64_t flags) {

	int64_t affected;
	MYSQL_BIND parameters[4];

	mm_wipe(parameters, sizeof(parameters));

	// Contact Number
	parameters[0].buffer_type = MYSQL_TYPE_LONGLONG;
	parameters[0].buffer_length = sizeof(uint64_t);
	parameters[0].buffer = &contactnum;
	parameters[0].is_unsigned = true;

	// Key
	parameters[1].buffer_type = MYSQL_TYPE_STRING;
	parameters[1].buffer_length = st_length_get(key);
	parameters[1].buffer = st_char_get(key);

	// Value
	parameters[2].buffer_type = MYSQL_TYPE_STRING;
	parameters[2].buffer_length = st_length_get(value);
	parameters[2].buffer = st_char_get(value);

	// Flags
	parameters[3].buffer_type = MYSQL_TYPE_LONGLONG;
	parameters[3].buffer_length = sizeof(uint64_t);
	parameters[3].buffer = &flags;
	parameters[3].is_unsigned = true;

	if ((affected = stmt_exec_affected(stmts.upsert_contact_detail, parameters)) == -1) {
		log_pedantic("The contact detail upsert triggered an error. { contact = %lu / key = %.*s }", contactnum, st_length_int(key), st_char_get(key));
		return -1;
	}

	log_check(affected > 2);

	return (int_t)affected;
}
Example #30
0
END_TEST

START_TEST (check_mail_store_s) {

	log_disable();
	bool_t result = true;
	stringer_t *errmsg = MANAGEDBUF(1024);

	if (status()) result = check_mail_store_plaintext_sthread(errmsg);
	if (status() && result) result = check_mail_store_encrypted_sthread(errmsg);

	log_test("MAIL / STORE / SINGLE THREADED:", errmsg);
	ck_assert_msg(result, st_char_get(errmsg));
}