Пример #1
0
void
st_free(struct stack *stp)
{
  struct st_node *np;

  assert(stp != NULL);

  if (st_empty(stp)) {
	free(stp);
	stp = NULL;
	return;
  }

  do {
	np = st_pop(stp);
	if (np != NULL) {
	  free(np);
	  np = NULL;
	}
  } while (st_empty(stp) != 1);

  if (stp != NULL) {
	free(stp);
	stp = NULL;
  }
}
Пример #2
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;
}
Пример #3
0
bool_t check_inx_append_mthread(MAGMA_INDEX inx_type, stringer_t *errmsg) {

	void *result;
	inx_t *inx = NULL;
	bool_t outcome = true;
	pthread_t *threads = NULL;

	if (status() && (!(inx = inx_alloc(inx_type | M_INX_LOCK_MANUAL, &ns_free)))) {
		st_sprint(errmsg, "An error occured during initial allocation in the inx check append multi-threaded test.");
		outcome = false;
	}
	else {

		if (!INX_CHECK_MTHREADS || !(threads = mm_alloc(sizeof(pthread_t) * INX_CHECK_MTHREADS))) {
			outcome = false;
		}
		else {

			for (uint64_t counter = 0; counter < INX_CHECK_MTHREADS; counter++) {
				if (thread_launch(threads + counter, &check_inx_append_mthread_test, inx)) {
					st_sprint(errmsg, "An error occured when launching a thread.");
					outcome = false;
				}
			}

			for (uint64_t counter = 0; counter < INX_CHECK_MTHREADS; counter++) {
				if (thread_result(*(threads + counter), &result) || !result || !*(bool_t *)result) {
					if (st_empty(errmsg)) st_sprint(errmsg, "One of the append check threads returned false.");
					outcome = false;
				}

				mm_cleanup(result);
			}

			mm_free(threads);
		}

		if (inx_count(inx) != 0 && st_empty(errmsg)) {
			st_sprint(errmsg, "The index was not properly cleared.");
			outcome = false;
		}

	}

	if (inx) {
		inx_cleanup(inx);
	}

	return outcome;
}
Пример #4
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;
}
Пример #5
0
static void _rt_type_id(struct _rt_invocation* inv, struct _rt_stack* sp)
{
	st_ptr result,result_w;
	st_empty(inv->t,&result);
	result_w = result;

	switch(sp->type)
	{
	case STACK_OBJ:
		{
			oid_str buffer;
			if(cle_get_oid_str(inv->hdl->inst,sp->obj,&buffer) == 0)
				st_insert(inv->t,&result_w,(cdat)&buffer,sizeof(buffer));
		}
		break;
	case STACK_PTR:
	case STACK_RO_PTR:
		st_insert(inv->t,&result_w,"[struct]",9);
		break;
	case STACK_NUM:
		st_insert(inv->t,&result_w,"[num]",6);
		break;
	case STACK_CODE:
		st_insert(inv->t,&result_w,"[code]",7);
		break;
	case STACK_NULL:
		st_insert(inv->t,&result_w,"[null]",7);
		break;
	case STACK_PROP:
		//cle_get_property_ref() and out id
		st_insert(inv->t,&result_w,"[property]",13);
	}
	sp->type = STACK_PTR;
}
Пример #6
0
/**
 * @brief	Get the fully expanded name of a folder.
 * @note	The folder hierarchy will be delimited with a "." character.
 * @param	folders		the inx object holding all of a user's magma folders.
 * @param	target		a pointer to the magma folder object to have its name expanded.
 * @return	NULL on failure or a pointer to a managed string with the fully expanded name of the specified folder.
 */
stringer_t * magma_folder_name(inx_t *folders, magma_folder_t *target) {

	stringer_t *result;
	int_t recursion = 0;

	if (!folders || !target || !target->foldernum || st_empty(target->name)) {
		log_pedantic("Invalid folder context. Unable to construct the name.");
		return NULL;
	}

	else if (!(result = st_dupe_opts(MANAGED_T | JOINTED | HEAP, target->name))) {
		log_pedantic("We were unable to duplicate the folder name.");
		return NULL;
	}

	while (target && target->parent != 0 && recursion++ < FOLDER_RECURSION_LIMIT) {

		// Get the parent target.
		if ((target = magma_folder_find_number(folders, target->parent)) == NULL) {
			log_pedantic("There appears to be a folder with an invalid parent.");
			return result;
		}

		// Append the seperator and then the parent name.
		result = st_append(result, PLACER(".", 1));
		result = st_append(result, target->name);
	}

	return result;
}
Пример #7
0
int main(void)
{
    size_t max = 100;
    size_t n = 20;
    char buf[strsiz];
    size_t i;
    st_t st = st_init(max);

    for (i = 0; i < n; i++) {
        item_t t = newitem(randkey(), randstr(buf, strsiz));
        st_insert(st, t);
    }

    st_sort(st, print);
    printf("\nThe item with key 11 is: ");
    print(st_search(st, 11));
    printf("\nThe 4th smallest key is: ");
    print(st_select(st, 4));
    st_delete(st, st_search(st, 11));
    printf("\n delete the item with key 11.\n");
    st_sort(st, print);

    /* delete all item */
    while (!st_empty(st)) {
        item_t x = st_select(st, 0);
        printf("delete item: ");
        print(x);
        st_delete(st, st_select(st, 0));
    }

    st_finalize(&st);
    return 0;
}
Пример #8
0
/**
 * @brief	Allocates an output string of appropriate size with specified opts for hex decoding of input
 * @param	input	Input stringer to be decoded.
 * @return	NULL on failure, otherwise allocated stringer with decoded data.
 */
stringer_t * hex_decode_opts(stringer_t *input, uint32_t opts) {

	stringer_t *result = NULL;
	size_t insize;

	if(st_empty(input)) {
		log_pedantic("Empty stringer was passed in.");
	}

	if(!opts) {
		log_pedantic("Invalid stringer options were passed in.");
		goto error;
	}

	insize = st_length_get(input);

	if(!(result = st_alloc_opts(opts, (insize % 2) ? ((insize + 1) / 2) : (insize / 2) ))) {
		log_error("Failed to allocate memory for hex-encoded output.");
		goto error;
	}

	if(result != hex_decode_st(input, result)) {
		log_error("Failed to encode data.");
		goto cleanup_result;
	}

	return result;

cleanup_result:
	st_free(result);
error:
	return NULL;
}
Пример #9
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;
}
Пример #10
0
void
st_foreach(struct stack *stp, void (*func_p) (struct st_node *np))  
{
  struct st_node *np;
  
  assert((stp != NULL) &&
		 (func_p != NULL) &&
		 !st_empty(stp));
  
  np = stp->top;
 
  while (np != NULL) {
	func_p(np);
	np = np->next;
  }
}
Пример #11
0
struct st_node *
st_pop(struct stack *stp)
{
  struct st_node *np;

  assert(stp != NULL);
  
  if (st_empty(stp))
	return (NULL);

  np = stp->top;
  stp->top = np->next;
  np->next = NULL;
  stp->size--;
  return (np);
}
Пример #12
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;
}
Пример #13
0
static uint _rt_ref_out(struct _rt_invocation* inv, struct _rt_stack** sp, struct _rt_stack* to)
{
	st_ptr pt;
	st_empty(inv->t,&pt);

	if(to->var->type != STACK_NULL)
	{
		struct _rt_stack target;
		target.type = STACK_PTR;
		target.single_ptr_w = pt;

		if(_rt_out(inv,sp,&target,to->var))
			return 1;
	}

	to->var->single_ptr_w = to->var->single_ptr = pt;
	to->var->type = STACK_PTR;
	*to = *to->var;
	return 0;
}
Пример #14
0
/**
 * @brief	Split a mime body into an array of children by a 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 to split the mime content.
 * @return	NULL on failure, or a pointer to an array of mime children on success.
 */
array_t * mail_mime_split(placer_t body, stringer_t *boundary) {

	array_t *result;
	uint32_t parts;
	stringer_t *item;

	// Figure out how many children this body part has.
	if (!(parts = mail_mime_count(body, boundary))) {
		return NULL;
	}

	// Allocate an array to hold all of the children.
	if (!(result = ar_alloc(parts))) {
		log_pedantic("Could not allocate an array of %i elements for the MIME parts.", parts);
		return NULL;
	}

	// Build an array that contains all of the children.
	for (uint32_t i = 1; i <= parts; i++) {

		if ((item = st_alloc_opts(PLACER_T | JOINTED | HEAP | FOREIGNDATA, 0))) {

			// Get the part and clean it up.
			*((placer_t *)item) = pl_set(*((placer_t *)item), mail_mime_child(body, boundary, i));

			/// TODO: This is ugly. Because the array gets a placer pointer we need to free it when done. But that means differentiating between
			/// these placers and what were usually passed which will likely stack allocated placers. We could probably just change it to a stringer
			/// now that its going to st_free(), but that would mean lots of updates all over the place.
			if (st_empty(item) || ar_append(&result, ARRAY_TYPE_STRINGER, item) != 1) {
				st_free(item);
			}

		}
	}

	return result;
}
Пример #15
0
static void _rt_run(struct _rt_invocation* inv)
{
	struct _rt_stack* sp = inv->top->sp;
	while(1)
	{
		int tmp;
		switch(*inv->top->pc++)
		{
		case OP_NOOP:
			break;
		case OP_DEBUG:
			break;
		case OP_POP:
			sp++;
			break;
		case OP_ADD:
			if(_rt_num(inv,sp) || _rt_num(inv,sp + 1))
				break;
			sp[1].num += sp[0].num;
			sp++;
			break;
		case OP_SUB:
			if(_rt_num(inv,sp) || _rt_num(inv,sp + 1))
				break;
			sp[1].num -= sp[0].num;
			sp++;
			break;
		case OP_MUL:
			if(_rt_num(inv,sp) || _rt_num(inv,sp + 1))
				break;
			sp[1].num *= sp[0].num;
			sp++;
			break;
		case OP_DIV:
			if(_rt_num(inv,sp) || _rt_num(inv,sp + 1) || sp->num == 0)
				break;
			sp[1].num /= sp[0].num;
			sp++;
			break;
		case OP_NOT:
			sp->num = _rt_test(sp) == 0;
			sp->type = STACK_NUM;
			break;
		case OP_NEG:
			if(_rt_num(inv,sp))
				break;
			sp->num *= -1;
			break;

		case OP_EQ:
			sp[1].num = _rt_equal(inv,sp,sp + 1);
			sp[1].type = STACK_NUM;
			sp++;
			break;
		case OP_NE:
			sp[1].num = _rt_equal(inv,sp,sp + 1) == 0;
			sp[1].type = STACK_NUM;
			sp++;
			break;
		case OP_GE:
			sp[1].num = _rt_compare(inv,sp,sp + 1) >= 0;
			sp++;
			break;
		case OP_GT:
			sp[1].num = _rt_compare(inv,sp,sp + 1) > 0;
			sp++;
			break;
		case OP_LE:
			sp[1].num = _rt_compare(inv,sp,sp + 1) <= 0;
			sp++;
			break;
		case OP_LT:
			sp[1].num = _rt_compare(inv,sp,sp + 1) < 0;
			sp++;
			break;

		case OP_BNZ:
			// emit Is (branch forward conditional)
			tmp = *((ushort*)inv->top->pc);
			inv->top->pc += sizeof(ushort);
			if(_rt_test(sp))
				inv->top->pc += tmp;
			sp++;
		case OP_BZ:
			// emit Is (branch forward conditional)
			tmp = *((ushort*)inv->top->pc);
			inv->top->pc += sizeof(ushort);
			if(_rt_test(sp) == 0)
				inv->top->pc += tmp;
			sp++;
			break;
		case OP_BR:
			// emit Is (branch forward)
			tmp = *((ushort*)inv->top->pc);
			inv->top->pc += tmp + sizeof(ushort);
			break;

		case OP_LOOP:	// JIT-HOOK 
			// emit Is (branch back)
			tmp = *((ushort*)inv->top->pc);
			inv->top->pc -= tmp - sizeof(ushort);
			break;
		case OP_ZLOOP:
			tmp = *((ushort*)inv->top->pc);
			inv->top->pc += sizeof(ushort);
			if(_rt_test(sp) == 0)
				inv->top->pc -= tmp;
			sp++;
			break;
		case OP_NZLOOP:
			tmp = *((ushort*)inv->top->pc);
			inv->top->pc += sizeof(ushort);
			if(_rt_test(sp))
				inv->top->pc -= tmp;
			sp++;
			break;

		case OP_FREE:
			// emit Ic2 (byte,byte)
			tmp = *inv->top->pc++;
			while(tmp-- > 0)
				_rt_free(inv,&inv->top->vars[tmp + *inv->top->pc]);
			inv->top->pc++;
			break;

		case OP_NULL:		// remove
			sp--;
			sp->type = STACK_NULL;
			break;
		case OP_IMM:
			// emit II (imm short)
			sp--;
			sp->type = STACK_NUM;
			sp->num = *((short*)inv->top->pc);
			inv->top->pc += sizeof(short);
			break;
		case OP_STR:
			// emit Is
			sp--;
			sp->type = STACK_RO_PTR;
			sp->single_ptr = inv->top->code->strings;
			st_move(inv->t,&sp->single_ptr,inv->top->pc,sizeof(ushort));
			inv->top->pc += sizeof(ushort);
			break;
		case OP_OBJ:
			sp--;
			sp->type = STACK_OBJ;
			sp->ptr = sp->obj = inv->top->object;
			cle_skip_header(inv->hdl->inst,&sp->ptr);
			break;

		case OP_NEW:
			tmp = *((ushort*)inv->top->pc);
			inv->top->pc += sizeof(ushort);
			sp--;
//			if(cle_goto_object_cdat(inv->hdl->inst,inv->top->pc,tmp,&sp->obj))
				if (1)
				_rt_error(inv,__LINE__);
			else
			{
				inv->top->pc += tmp;
				if(_rt_new_obj(inv,sp))
					_rt_error(inv,__LINE__);
			}
			break;
		case OP_CLONE:
			if(sp->type != STACK_OBJ)
				_rt_error(inv,__LINE__);
			else if(_rt_new_obj(inv,sp))
				_rt_error(inv,__LINE__);
			break;
		case OP_ID:
			sp--;
			sp->type = STACK_OBJ;
			sp->obj = inv->top->object;
			_rt_type_id(inv,sp);
			break;
		case OP_IDO:
			_rt_type_id(inv,sp);
			break;
		case OP_FIND:
			inv->top->pc++;
			if(sp->type == STACK_PTR || sp->type == STACK_RO_PTR)
			{
				if(cle_goto_object(inv->hdl->inst,sp->single_ptr,&sp->obj))
					sp->type = STACK_NULL;
				else
				{
					sp->ptr = sp->obj;
					cle_skip_header(inv->hdl->inst,&sp->ptr);
					sp->type = STACK_OBJ;
				}
			}
			else
				_rt_error(inv,__LINE__);
			break;

		case OP_IT:
			inv->top->pc++;
			sp--;
			switch(sp[1].type)
			{
			case STACK_PROP:
				// try to start iterator on value
				break;
			case STACK_COLLECTION:
				it_create(inv->t,&sp->it,&sp[1].ptr);
				sp->type = STACK_ITERATOR_COL;
				break;
			case STACK_PTR:
			case STACK_RO_PTR:
				it_create(inv->t,&sp->it,&sp[1].single_ptr);
				sp->type = STACK_ITERATOR;
				break;
			default:
				_rt_error(inv,__LINE__);
			}
			break;
		case OP_INEXT:
			tmp = *inv->top->pc++;
			switch(sp->type)
			{
			case STACK_ITERATOR:
				sp->num = it_next(inv->t,0,&sp->it,0);
				break;
			case STACK_ITERATOR_COL:
				sp->num = it_next(inv->t,0,&sp->it,sizeof(oid));
				break;
			default:
				_rt_error(inv,__LINE__);
			}
			sp->type = STACK_NUM;
			break;
		case OP_IPREV:
			tmp = *inv->top->pc++;
			switch(sp->type)
			{
			case STACK_ITERATOR:
				sp->num = it_prev(inv->t,0,&sp->it,0);
				break;
			case STACK_ITERATOR_COL:
				sp->num = it_prev(inv->t,0,&sp->it,sizeof(oid));
				break;
			default:
				_rt_error(inv,__LINE__);
			}
			sp->type = STACK_NUM;
			break;
		case OP_IKEY:
			inv->top->pc++;
			if(sp->type != STACK_ITERATOR && sp->type != STACK_ITERATOR_COL)
				_rt_error(inv,__LINE__);
			else
			{
				st_ptr pt;
				st_empty(inv->t,&pt);
				st_append(inv->t,&pt,sp->it.kdata,sp->it.kused);
				sp->single_ptr = sp->single_ptr_w = pt;
				sp->type = STACK_PTR;
			}
			break;
		case OP_IVAL:
			inv->top->pc++;
			if(sp->type == STACK_ITERATOR)
			{
				st_ptr pt;
				if(it_current(inv->t,&sp->it,&pt) == 0)
				{
					sp->single_ptr = sp->single_ptr_w = pt;
					sp->type = STACK_PTR;
				}
				else
					sp->type = STACK_NULL;
			}
			else if(sp->type == STACK_ITERATOR_COL)
			{
				if(sp->it.kused == sizeof(oid))
				{
					// TODO: move to object.c
					st_ptr pt = inv->hdl->inst.root;
					st_move(inv->t,&pt,sp->it.kdata,sizeof(oid));
					sp->ptr = sp->obj = pt;
					cle_skip_header(inv->hdl->inst,&sp->ptr);
					sp->type = STACK_OBJ;
				}
				else
					sp->type = STACK_NULL;
			}
			else
				_rt_error(inv,__LINE__);
			break;

		case OP_OMV:
			tmp = *((ushort*)inv->top->pc);
			inv->top->pc += sizeof(ushort);
			sp--;
			sp->obj = inv->top->object;
			if(cle_get_property_host(inv->hdl->inst,&sp->ptr,inv->top->pc,tmp) < 0)
			{
				sp->type = STACK_NULL;
				inv->top->pc += tmp;
			}
			else
			{
				inv->top->pc += tmp;
				sp->ptr = sp->obj;
				cle_skip_header(inv->hdl->inst,&sp->ptr);
				_rt_get(inv,&sp);
			}
			break;
		case OP_MV:
			tmp = *((ushort*)inv->top->pc);
			inv->top->pc += sizeof(ushort);
			if(_rt_move(inv,&sp,inv->top->pc,tmp))
				sp->type = STACK_NULL;
			inv->top->pc += tmp;
			break;
		case OP_RIDX:
			if(sp->type == STACK_NUM)
			{
				char buffer[sizeof(rt_number) + HEAD_SIZE];
				buffer[0] = 0;
				buffer[1] = 'N';
				memcpy(buffer + 2,&sp->num,sizeof(rt_number));
				sp++;
				if(_rt_move(inv,&sp,buffer,sizeof(buffer)))
					sp->type = STACK_NULL;
			}
			else if(sp->type == STACK_PTR ||
				sp->type == STACK_RO_PTR)
			{
				st_ptr mv = sp->single_ptr;
				sp++;
				if(_rt_move_st(inv,&sp,&mv))
					sp->type = STACK_NULL;
			}
			break;
		case OP_LVAR:
			sp--;
			*sp = inv->top->vars[*inv->top->pc++];
			break;

		// writer
		case OP_POPW:
			if(sp->type == STACK_OUTPUT)
				sp->out->pop(sp->outdata);
			else
				sp++;
			break;
		case OP_DMVW:
			tmp = *((ushort*)inv->top->pc);
			inv->top->pc += sizeof(ushort);
			switch(sp->type)
			{
			case STACK_REF:
				if(sp->var->type == STACK_NULL)
				{
					st_empty(inv->t,&sp->var->single_ptr_w);
					sp->var->single_ptr = sp->var->single_ptr_w;
				}
				else if(sp->var->type != STACK_PTR)
				{
					_rt_error(inv,__LINE__);
					return;
				}
				sp->single_ptr_w = sp->var->single_ptr;
				sp->single_ptr = sp->single_ptr_w;
				sp->type = STACK_PTR;
			case STACK_PTR:
				sp--;
				sp[0] = sp[1];
				st_insert(inv->t,&sp->single_ptr_w,inv->top->pc,tmp);
				break;
			case STACK_OUTPUT:
				if(inv->response_started == 0)
				{
					sp->out->start(sp->outdata);
					inv->response_started = 1;
				}
				else inv->response_started = 2;
				sp->out->push(sp->outdata);
				sp->out->data(sp->outdata,inv->top->pc,tmp);
			}
			inv->top->pc += tmp;
			break;
		case OP_MVW:
			tmp = *((ushort*)inv->top->pc);
			inv->top->pc += sizeof(ushort);
			switch(sp->type)
			{
			case STACK_PTR:
				st_insert(inv->t,&sp->single_ptr_w,inv->top->pc,tmp);
				break;
			case STACK_OUTPUT:
				if(inv->response_started == 0)
				{
					sp->out->start(sp->outdata);
					inv->response_started = 1;
				}
				else inv->response_started = 2;
				sp->out->data(sp->outdata,inv->top->pc,tmp);
			}
			inv->top->pc += tmp;
			break;
		case OP_WIDX:	// replace by OP_OUT ?
			_rt_out(inv,&sp,sp,sp + 1);
			sp++;
			break;

		case OP_OPEN:
			_rt_do_open(inv,&sp);
			break;
		case OP_OPEN_POP:
			// unfinished output
			if(inv->response_started == 2)
				sp->out->next(sp->outdata);
			sp->out->end(sp->outdata,0,0);
			inv->response_started = 1;
			tk_commit_task(sp->outtask);	// well, what if something went wrong??
			sp++;
			break;
		// receive input
		case OP_RECV:
			sp += *inv->top->pc++;
			inv->top->sp = sp;
			return;

		case OP_SET:
			if(inv->top->is_expr != 0)
				_rt_error(inv,__LINE__);
			else if(sp[1].type != STACK_PROP)
				_rt_error(inv,__LINE__);
			else
			{
				switch(sp->type)
				{
				case STACK_PROP:
					if(cle_identity_value(inv->hdl->inst,sp->prop_id,sp->prop_obj,&sp->ptr))
						_rt_error(inv,__LINE__);
					if(cle_set_property_ptr(inv->hdl->inst,sp[1].prop_obj,sp[1].prop_id,&sp[1].ptr))
						_rt_error(inv,__LINE__);
					// might not be a good idea if prop-val is a mem-ref
					st_copy_st(inv->t,&sp[1].ptr,&sp->ptr);
					break;
				case STACK_RO_PTR:
					// link
					if(cle_set_property_ptr(inv->hdl->inst,sp[1].prop_obj,sp[1].prop_id,&sp[1].ptr))
						_rt_error(inv,__LINE__);
					st_link(inv->t,&sp[1].ptr,&sp->single_ptr);
					break;
				case STACK_PTR:
					// copy 
					if(cle_set_property_ptr(inv->hdl->inst,sp[1].prop_obj,sp[1].prop_id,&sp[1].ptr))
						_rt_error(inv,__LINE__);
					st_copy_st(inv->t,&sp[1].ptr,&sp->single_ptr);
					break;
				case STACK_NUM:
					// bin-num
					if(cle_set_property_num(inv->hdl->inst,sp[1].prop_obj,sp[1].prop_id,sp->num))
						_rt_error(inv,__LINE__);
					break;
				case STACK_OBJ:
					// obj-ref
					if(cle_set_property_ref(inv->hdl->inst,sp[1].prop_obj,sp[1].prop_id,sp->obj))
						_rt_error(inv,__LINE__);
					break;
				case STACK_CODE:
					// write out path/event to method/handler
					if(st_move(inv->t,&sp->single_ptr,"p",1) == 0)
					{
						if(cle_set_property_ptr(inv->hdl->inst,sp[1].prop_obj,sp[1].prop_id,&sp[1].ptr))
							_rt_error(inv,__LINE__);
						st_copy_st(inv->t,&sp[1].ptr,&sp->single_ptr);
						break;
					}	// or null
				default:
					// empty / null
					if(cle_set_property_ptr(inv->hdl->inst,sp[1].prop_obj,sp[1].prop_id,&sp[1].ptr))
						_rt_error(inv,__LINE__);
				}
			}
			sp += 2;
			break;
		case OP_MERGE:
			switch(sp->type)
			{
			case STACK_PROP:
				if(inv->top->is_expr != 0)
					_rt_error(inv,__LINE__);
				else if(cle_set_property_ptr(inv->hdl->inst,sp->prop_obj,sp->prop_id,&sp->single_ptr))
					_rt_error(inv,__LINE__);
				else
				{
					sp->single_ptr_w = sp->single_ptr;
					sp->type = STACK_PTR;
				}
				break;
			case STACK_REF:
				if(sp->var->type == STACK_NULL)
				{
					st_empty(inv->t,&sp->var->single_ptr);
					sp->var->single_ptr_w = sp->var->single_ptr;
					sp->var->type = STACK_PTR;
				}
				else if(sp->var->type != STACK_PTR)
					_rt_error(inv,__LINE__);
			case STACK_PTR:
			case STACK_OUTPUT:
				break;
			default:
				_rt_error(inv,__LINE__);
			}
			break;
		case OP_2STR:
			tmp = *inv->top->pc++;	// vars
			if(tmp != 1)
			{
				_rt_error(inv,__LINE__);
				break;
			}
			// fall throu
		case OP_CAT:
			{
				struct _rt_stack to;
				st_empty(inv->t,&to.single_ptr);
				to.single_ptr_w = to.single_ptr;
				to.type = STACK_PTR;
				_rt_out(inv,&sp,&to,sp);
				*sp = to;
			}
			break;
		case OP_NEXT:	// non-string (concat) out-ing [OUT Last Tree]
			if(sp[1].type == STACK_REF)
			{
				if(sp[1].var->type == STACK_NULL)
					*sp[1].var = *sp;
				else _rt_ref_out(inv,&sp,sp + 1);
			}
			else
			{
				if(_rt_out(inv,&sp,sp + 1,sp) == 0)
				{
					sp++;
					if(inv->response_started == 2 && sp->type == STACK_OUTPUT)
					{
						inv->response_started = 1;
						sp->out->next(sp->outdata);
					}
				}
			}
			break;
		case OP_OUTL:
			// TODO: stream out structures
		case OP_OUT:	// stream out string
			if(sp[1].type == STACK_REF)
				_rt_ref_out(inv,&sp,sp + 1);
			else
				_rt_out(inv,&sp,sp + 1,sp);
			sp++;
			break;

		case OP_AVAR:
			inv->top->vars[*inv->top->pc++] = *sp;
			sp++;
			break;
		case OP_DEFP:
			// emit Is2 (branch forward)
			tmp = *inv->top->pc++;	// var
			if(inv->top->vars[tmp].type == STACK_NULL)
			{
				sp--;
				inv->top->vars[tmp].var = sp;
				inv->top->vars[tmp].type = STACK_REF;
				inv->top->pc += sizeof(ushort);
			}
			else
			{
				tmp = *((ushort*)inv->top->pc);
				inv->top->pc += tmp + sizeof(ushort);
			}
			break;
		case OP_END:
			tmp = inv->top->code->body.maxparams;
			while(tmp-- > 0)
				_rt_free(inv,&inv->top->vars[tmp]);

			if(inv->top->parent == 0)
			{
				// unfinished output? -> next
				if(inv->response_started == 2)
					inv->hdl->response->next(inv->hdl->respdata);

				cle_stream_end(inv->hdl);
				return;
			}
			else
			{
				struct _rt_callframe* cf = inv->top;
				inv->top = inv->top->parent;
				sp = inv->top->sp;

				// unref page of origin
				tk_unref(inv->t,cf->pg);
			}
			break;
		case OP_DOCALL:
			tmp = *inv->top->pc++;	// params
			if(_rt_call(inv,sp,tmp) == 0)
			{
				inv->top->parent->sp = sp + 1 + tmp;	// return-stack
				*(--inv->top->sp) = *(sp + 1 + tmp);	// copy output-target
				sp = inv->top->sp;		// set new stack
			}
			break;
		case OP_DOCALL_N:
			tmp = *inv->top->pc++;	// params
			if(_rt_call(inv,sp,tmp) == 0)
			{
				inv->top->parent->sp = sp + tmp;	// return-stack
				inv->top->sp--;
				inv->top->sp->type = STACK_REF;	// ref to sp-top
				inv->top->sp->var = sp + tmp;
				inv->top->sp->var->type = STACK_NULL;
				sp = inv->top->sp;		// set new stack
			}
			break;
		case OP_ERROR:	// system exception
			return;
		default:
			_rt_error(inv,__LINE__);
		}
	}
}
Пример #16
0
/**
 * @brief	Log the contents of a magma configuration option.
 * @param	key		a pointer to the magma configuration key to be dumped.
 * @return	This function returns no value.
 */
void config_output_value(magma_keys_t *key) {

	switch (key->norm.type) {
	case (M_TYPE_NULLER):
		if (ns_empty(*((char **)(key->store))))
			log_info("%s = NULL", key->name);
		else
			log_info("%s = %s", key->name, *((char **)(key->store)));
		break;

	case (M_TYPE_STRINGER):
		// Intercept the blacklist config key->
		if (!st_cmp_cs_eq(NULLER(key->name), PLACER("magma.smtp.blacklist", 20))) {

			if (!magma.smtp.blacklists.count) {
				log_info("%s = NULL",key->name);
			}

			for (uint32_t j = 0; j < magma.smtp.blacklists.count; j++) {
				log_info("%s = %.*s",key->name, st_length_int(magma.smtp.blacklists.domain[j]), st_char_get(magma.smtp.blacklists.domain[j]));
			}
		}

		else if (st_empty(*((stringer_t **)(key->store))))
			log_info("%s = NULL", key->name);
		else
			log_info("%s = %.*s", key->name, st_length_int(*((stringer_t **)(key->store))), st_char_get(*((stringer_t **)(key->store))));
		break;

	case (M_TYPE_BOOLEAN):
		log_info("%s = %s", key->name, (*((bool_t *)(key->store)) ? "true" : "false"));
		break;

	case (M_TYPE_INT8):
		log_info("%s = %hhi", key->name, *((int8_t *)(key->store)));
		break;
	case (M_TYPE_INT16):
		log_info("%s = %hi", key->name, *((int16_t *)(key->store)));
		break;

	case (M_TYPE_INT32):
		log_info("%s = %i", key->name, *((int32_t *)(key->store)));
		break;

	case (M_TYPE_INT64):
		log_info("%s = %li", key->name, *((int64_t *)(key->store)));
		break;

	case (M_TYPE_UINT8):
		log_info("%s = %hhu", key->name, *((uint8_t *)(key->store)));
		break;
	case (M_TYPE_UINT16):
		log_info("%s = %hu", key->name, *((uint16_t *)(key->store)));
		break;
	case (M_TYPE_UINT32):
		log_info("%s = %u", key->name, *((uint32_t *)(key->store)));
		break;
	case (M_TYPE_UINT64):
		log_info("%s = %lu", key->name, *((uint64_t *)(key->store)));
		break;
	default:
		log_pedantic("Unexpected type. {type = %u}", key->norm.type);
		break;
	}

	return;
}
Пример #17
0
/**
 * @brief	Accept and verify a password for POP3 authentication.
 * @note	This command is only allowed for sessions which have not yet been authenticated, but which have already supplied a username.
 *			If the username/password combo was validated, the account information is retrieved and checked to see if it is locked.
 *			After successful authentication, this function will prohibit insecure connections for any user configured to use SSL only,
 *			and enforce the existence of only one POP3 session at a time.
 *			Finally, the database Log table for this user's POP3 access is updated, and all the user's messages are retrieved.
 * @param	con		the POP3 client connection issuing the command.
 * @return	This function returns no value.
 */
void pop_pass(connection_t *con) {

	int_t state;
	credential_t *cred;
	stringer_t *password, *username;

	if (con->pop.session_state != 0) {
		pop_invalid(con);
		return;
	}

	// The user must come before the PASS command.
	if (st_empty(con->pop.username)) {
		con_write_bl(con, "-ERR You must supply a username first.\r\n", 40);
		return;
	}

	// If they didn't pass in a valid password.
	if (!(password = pop_pass_parse(con))) {
		con_write_bl(con, "-ERR Invalid PASS command.\r\n", 28);
		return;
	}

	// Hash the password.
	// First we need to get the regular username from the fully qualified one.
	if (!(username = credential_username(con->pop.username))) {
		con_write_bl(con, "-ERR Internal server error. Please try again later.\r\n", 53);
		st_wipe(password);
		st_free(password);
	}

	st_free(con->pop.username);
	con->pop.username = username;

	if (!(cred = credential_alloc_auth(con->pop.username, password))) {
		con_write_bl(con, "-ERR Internal server error. Please try again later.\r\n", 53);
		st_wipe(password);
		st_free(password);
		return;
	}

	st_wipe(password);
	st_free(password);

	// Pull the user info out.
	state = meta_get(con->pop.username, cred->auth.domain, cred->auth.password, cred->auth.key, META_PROT_POP, META_GET_MESSAGES, &(con->pop.user));

	// Securely delete this information, as these are the keys to the castle.
	credential_free(cred);

	// Not found, or invalid password.
	if (state == 0) {
		con_write_bl(con,  "-ERR The username and password combination is invalid.\r\n", 56);
		return;
	}
	// Internal error.
	else if (state < 0 || !con->pop.user) {
		con_write_bl(con, "-ERR [SYS/TEMP] Internal server error. Please try again later.\r\n", 64);
		return;
	}

	// Locks
	else if (con->pop.user->lock_status != 0) {
		// What type of lock is it.
		if (con->pop.user->lock_status == 1) {
			con_write_bl(con, "-ERR [SYS/PERM] This account has been administratively locked.\r\n", 64);
		}
		else if (con->pop.user->lock_status == 2) {
			con_write_bl(con, "-ERR [SYS/PERM] This account has been locked for inactivity.\r\n", 62);
		}
		else if (con->pop.user->lock_status == 3) {
			con_write_bl(con, "-ERR [SYS/PERM] This account has been locked on suspicion of abuse.\r\n", 69);
		}
		else if (con->pop.user->lock_status == 4) {
			con_write_bl(con, "-ERR [SYS/PERM] This account has been locked at the request of the user.\r\n", 74);
		}
		else {
			con_write_bl(con, "-ERR [SYS/PERM] This account has been locked.\r\n", 47);
		}

		con->pop.user = NULL;
		meta_remove(con->pop.username, META_PROT_POP);
		return;
	}

	// SSL check.
	else if ((con->pop.user->flags & META_USER_SSL) == META_USER_SSL && con_secure(con) != 1) {
		con->pop.user = NULL;
		meta_remove(con->pop.username, META_PROT_POP);
		con_write_bl(con, "-ERR [SYS/PERM] This user account is configured to require that all POP sessions be connected over SSL.\r\n", 105);
		return;
	}

	// Single session check.
	else if (con->pop.user->refs.pop != 1) {
		con->pop.user = NULL;
		meta_remove(con->pop.username, META_PROT_POP);
		con_write_bl(con, "-ERR [IN-USE] This account is being used by another session. Please try again in a few minutes.\r\n", 97);
		return;
	}

	// Debug logging.
	log_pedantic("User %.*s logged in from %s via POP. {poprefs = %lu, imaprefs = %lu, messages = %lu}",
		st_length_int(con->pop.username), st_char_get(con->pop.username), st_char_get(con_addr_presentation(con, MANAGEDBUF(256))),
		con->pop.user->refs.pop, con->pop.user->refs.imap, con->pop.user->messages ? inx_count(con->pop.user->messages) : 0);

	// Update the log and unlock the session.
	meta_data_update_log(con->pop.user, META_PROT_POP);

	meta_user_wlock(con->pop.user);
	meta_messages_login_update(con->pop.user, META_LOCKED);
	meta_user_unlock(con->pop.user);

	// Update session state.
	con->pop.session_state = 1;

	// Tell the client everything worked.
	con_write_bl(con, "+OK Password accepted.\r\n", 24);

	return;
}
Пример #18
0
/**
 * @brief	Calculates the legacy symmetric encryption key and authentication token.
 * @param	username	a managed string holding the sanitzed username.
 * @param	password	a managed string holding the plain text user password.
 * @return	an auth_legacy_t structure is returned upon success, and NULL upon failure.
 **/
auth_legacy_t * auth_legacy(stringer_t *username, stringer_t *password) {

	auth_legacy_t *legacy = NULL;
	stringer_t *input = NULL, *intermediate = MANAGEDBUF(64);

	// Make sure all three required inputs are valid pointers and hold at least one character.
	if (st_empty(username) || st_empty(password) || st_empty(magma.secure.salt)) {
		log_error("A variable needed to calculate the legacy authentication and encryption values was invalid.");
		return NULL;
	}
	else if (!(legacy = auth_legacy_alloc())) {
		log_error("We were unable to allocate a buffer to hold the legacy hash values.");
		return NULL;
	}

	// Combine the three inputs into a single buffer.
	else if (!(input = st_merge("sss", username, magma.secure.salt, password))) {
		log_error("Unable to combine the three input values needed to calculate the legacy authentication and encryption values.");
		auth_legacy_free(legacy);
		return NULL;
	}

	// Hash the inputs together and we'll get the legacy symmetric encryption key.
	else if (!(legacy->key = st_alloc_opts(MANAGED_T | CONTIGUOUS | SECURE, 64)) || !(hash_sha512(input, legacy->key))) {
		log_error("Unable to calculate the legacy hash values.");
		auth_legacy_free(legacy);
		st_free(input);
		return NULL;
	}

	// Free and reuse the holder variable.
	st_free(input);

	// Prepare the inputs for the intermediary hash.
	if (!(input = st_merge("ss", password, legacy->key))) {
		log_error("Failed to merge the legacy authentication inputs for the intermediate hash round.");
		auth_legacy_free(legacy);
		return NULL;
	}

	// Hash the password with the output of the first round. Note that if the hash function returns NULL and overwrites
	// the intermediate string pointer, the buffer will be freed automatically because it was allocated off the stack.
	else if (!(hash_sha512(input, intermediate))) {
		log_error("Unable to calculate the legacy hash values.");
		auth_legacy_free(legacy);
		st_free(input);
		return NULL;
	}

	// Free and reuse the holder variable.
	st_free(input);

	// Prepare the inputs for the intermediary hash.
	if (!(input = st_merge("ss", password, intermediate))) {
		log_error("Failed to merge the legacy authentication inputs for the final hash round.");
		auth_legacy_free(legacy);
		return NULL;
	}

	// Hash the inputs together and we'll get the legacy authentication token.
	if (!(legacy->token = st_alloc(64)) || !(hash_sha512(input, legacy->token))) {
		log_error("Failed to merge the legacy authentication inputs for the final hash round.");
		auth_legacy_free(legacy);
		st_free(input);
		return NULL;
	}

	// Free the inputs.
	st_free(input);

	// And return success.
	return legacy;
}
Пример #19
0
/**
 * @brief	Load all magma configuration options present in the database.
 * @note	Each key/value pair extracted from the database is submitted to the following logic:
 *	 			If a config option was loaded from the database, the key must allow it to be configurable via the database.
 *	 			Check to see that any key that has previously been set is allowed to be overwritten.
 * 				If the key is required, it may not contain an empty value.
 * 			Finally, this function sets the appropriate magma key corresponding to the config key.
 * 			All leftover keys not matched to global magma keys will be configured via servers, relay, and cache server options.
 * @return	true if all database config options were parsed and evaluated successfully, or false on failure.
 */
bool_t config_load_database_settings(void) {

	row_t *row;
	uint64_t rows;
	magma_keys_t *key;
	table_t *database_pairs;
	stringer_t *value, *name;

	if (!(magma.host.number = config_fetch_host_number()) || !(database_pairs = config_fetch_settings())) {
		return false;
	}

	// Loop through each of the row returned.
	rows = res_row_count(database_pairs);
	for (uint64_t i = 0; i < rows && (row = res_row_get(database_pairs, i)); i++) {

			name = PLACER(res_field_block(row, 0), res_field_length(row, 0));
			value = PLACER(res_field_block(row, 1), res_field_length(row, 1));

			if ((key = config_key_lookup(name))) {
				// Make sure the setting can be provided via the database.
				if (!key->database) {
					log_critical("%s cannot be changed using the database.", key->name);
					res_table_free(database_pairs);
					return false;
				}

				// Make sure the setting can be provided via the database.
				else if (key->set && !key->overwrite) {
					log_critical("%s has already been set and cannot be overwritten.", key->name);
					res_table_free(database_pairs);
					return false;
				}

				// Make sure the required magma_keys are not set to NULL.
				else if (key->required && st_empty(value)) {
					log_critical("%s requires a legal value.", key->name);
					res_table_free(database_pairs);
					return false;
				}

				// Attempt to set the value.
				else if (!config_value_set(key, value)) {
					res_table_free(database_pairs);
					return false;
				}

				// Record that we've set this parameter.
				key->set = true;
			}

			// If we haven't had a match yet, check if its a server param.
			else if (!st_cmp_ci_starts(name, CONSTANT("magma.servers"))) {
				servers_config(name, value);
			}

			// If we haven't had a match yet, check if its a relay instance.
			else if (!st_cmp_ci_starts(name, CONSTANT("magma.relay"))) {
				relay_config(name, value);
			}

			else if (!st_cmp_ci_starts(name, CONSTANT("magma.iface.cache.host"))) {
				cache_config(name, value);
			}

			// Otherwise if we still haven't matched a value, report an error.
			else {
				log_critical("%.*s is not a valid setting.", st_length_int(name), st_char_get(name));
				res_table_free(database_pairs);
				return false;
			}

	}

	res_table_free(database_pairs);
	return true;
}
Пример #20
0
stringer_t * hash_digest(digest_t *digest, stringer_t *s, stringer_t *output) {

	int_t olen;
	uint_t rlen;
	uint32_t opts = 0;
	EVP_MD_CTX ctx;
	stringer_t *result = NULL;

	// Ensure a digest pointer was passed in and that we can retrieve the output length.
	if (!digest || (olen = EVP_MD_size_d((const EVP_MD *)digest)) <= 0) {
		log_pedantic("The hash algorithm is missing or invalid.");
		return NULL;
	}
	else if (output && !st_valid_destination((opts = *((uint32_t *)output)))) {
		log_pedantic("An output string was supplied but it does not represent a buffer capable of holding a result.");
		return NULL;
	}
	else if (st_empty(s)) {
		log_pedantic("The input string does not appear to have any data ready for encoding. {%slen = %zu}", s ? "" : "s = NULL / ",	s ? st_length_get(s) : 0);
		return NULL;
	}

	// Make sure the output buffer is large enough or if output was passed in as NULL we'll attempt the allocation of our own buffer.
	else if ((result = output) && ((st_valid_avail(opts) && st_avail_get(output) < olen) || (!st_valid_avail(opts) && st_length_get(output) < olen))) {
		log_pedantic("The output buffer supplied is not large enough to hold the result. {avail = %zu / required = %i}",
				st_valid_avail(opts) ? st_avail_get(output) : st_length_get(output), olen);
		return NULL;
	}
	else if (!output && !(result = st_alloc(olen))) {
		log_pedantic("The output buffer memory allocation request failed. {requested = %i}", olen);
		return NULL;
	}

	// Initialize the context.
	EVP_MD_CTX_init_d(&ctx);
	rlen = olen;

	// Setup the digest algorithm.
	if (EVP_DigestInit_ex_d(&ctx, (const EVP_MD *)digest, NULL) != 1) {
		log_pedantic("An error occurred while trying to initialize the hash context. {%s}",	ssl_error_string(MEMORYBUF(256), 256));
		EVP_MD_CTX_cleanup_d(&ctx);
		if (!output) {
			st_free(result);
		}
		return NULL;
	}

	// Process the input data.
	else if (EVP_DigestUpdate_d(&ctx, st_data_get(s), st_length_get(s)) != 1) {
		log_pedantic("An error occurred while trying to process the input data. {%s}", ssl_error_string(MEMORYBUF(256), 256));
		EVP_MD_CTX_cleanup_d(&ctx);
		if (!output) {
			st_free(result);
		}
		return NULL;
	}

	// Retrieve the hash output.
	else if (EVP_DigestFinal_d(&ctx, st_data_get(result), &rlen) != 1) {
		log_pedantic("An error occurred while trying to retrieve the hash result. {%s}", ssl_error_string(MEMORYBUF(256), 256));
		EVP_MD_CTX_cleanup_d(&ctx);
		if (!output) {
			st_free(result);
		}
		return NULL;
	}

	// Cleanup.
	EVP_MD_CTX_cleanup_d(&ctx);

	if (!output || st_valid_tracked(opts)) {
		st_length_set(result, rlen);
	}
	return result;
}
Пример #21
0
/**
 * @brief	Return a zero-length placer pointing to NULL data.
 * @return	a zero-length placer pointing to NULL data.
 */
placer_t pl_null(void) {

	return (placer_t){ .opts = PLACER_T | JOINTED | STACK | FOREIGNDATA, .data = NULL, .length = 0 };
}

/**
 * @brief	Return a placer wrapping a data buffer of given size.
 * @param	data	a pointer to the data to be wrapped.
 * @param	len		the length, in bytes, of the data.
 * @return	a placer pointing to the specified data.
 */
placer_t pl_init(void *data, size_t len) {

	return (placer_t){ .opts = PLACER_T | JOINTED | STACK | FOREIGNDATA, .data = data, .length = len };
}

placer_t pl_clone(placer_t place) {
	return (pl_init(place.data, place.length));
}

placer_t pl_set(placer_t place, placer_t set) {

	return (placer_t){ .opts = place.opts, .data = set.data, .length = set.length };
}

/**
 * @brief	Get a pointer to the data referenced by a placer.
 * @param	place	the input placer.
 * @return	NULL on failure or a pointer to the block of data associated with the specified placer on success.
 */
void * pl_data_get(placer_t place) {

	return st_data_get((stringer_t *)&place);
}

/**
 * @brief	Get a character pointer to the data referenced by a placer.
 * @param	place	the input placer.
 * @return	NULL on failure or a a character pointer to the block of data associated with the specified placer on success.
 */
chr_t * pl_char_get(placer_t place) {

	return st_char_get((stringer_t *)&place);
}

/**
 * @brief	Get the length, in bytes, of a placer as an integer.
 * @param	place	the input placer.
 * @return	the size, in bytes, of the specified placer.
 */
int_t pl_length_int(placer_t place) {

	return st_length_int((stringer_t *)&place);
}

/**
 * @brief	Get the length, in bytes, of a placer.
 * @param	place	the input placer.
 * @return	the size, in bytes, of the specified placer.
 */
size_t pl_length_get(placer_t place) {

	return st_length_get((stringer_t *)&place);
}

/**
 * @brief	Determine whether or not the specified placer is empty.
 * @param	place	the input placer.
 * @return	true if the placer is empty or zero-length, or false otherwise.
 */
bool_t pl_empty(placer_t place) {

	return st_empty((stringer_t *)&place);
}

/**
 * @brief	Determine if a placer begins with a specified character.
 * @param	place	the input placer.
 * @param	c		the character to be compared with the first byte of the placer's data.
 * @return	true if the placer begins with the given character or false otherwise.
 */
bool_t pl_starts_with_char(placer_t place, chr_t c) {

	if (pl_empty(place)) {
		return false;
	}

	if (*(pl_char_get(place)) == c) {
		return true;
	}

	return false;
}

/**
 * @brief	Advance the placer one character forward beyond an expected character.
 * @param	place	the input placer.
 * @param	more	if true, the placer must contain more data, and vice versa.
 * @return	true if more was true and the placer contains more data, or if more was false and the placer ended; false otherwise.
 */
bool_t pl_inc(placer_t *place, bool_t more) {

	if (pl_empty(*place)) {
		return false;
	}

	place->length--;
	place->data = (chr_t *)place->data + 1;

	return (more == (place->length > 0));
}
Пример #22
0
/**
 * @brief	Lookup user and return their meta user object.
 *
 * @note	If the user is not found in the local session cache, the session will be constructed using the database, and then cached.
 *
 * @param 	usernum			the numeric identifier for the user account.
 * @param 	username		the official username stored in the database.
 * @param	salt			the user specific salt value.
 * @param	master			the user account's master encryption key which will be used to unlock the private storage key.
 * @param 	verification	the verification token.
 * @param	protocol			a set of protocol specifying the protocol used by the calling function. Values can be META_PROT_NONE,
 * 							META_PROT_SMTP, META_PROT_POP, META_PROT_IMAP, META_PROT_WEB, or META_PROT_GENERIC.
 * @param	get				a set of protocol specifying the data to be retrieved (META_GET_NONE, META_GET_MESSAGES,
 * 							META_GET_FOLDERS, or META_GET_CONTACTS)
 * @param	output			the address of a meta user object that will store a pointer to the result of the lookup.
 *
 * @return	-1 on error, 0 on success, 1 for an authentication issue.
 */
int_t meta_get(uint64_t usernum, stringer_t *username, stringer_t *salt, stringer_t *master, stringer_t *verification, META_PROTOCOL protocol, META_GET get, meta_user_t **output) {

	int_t state;
	meta_user_t *user = NULL;

	// If the auth structure is empty, or the usernum is invalid, return an error immediately.
	if (!usernum || !st_populated(username, master, verification)) {
		log_pedantic("Invalid parameters were used to get the meta data object.");
		return -1;
	}

	// Pull the user context using the usernum, or add an empty context if it doesn't exist.
	if (!(user = meta_inx_find(usernum, protocol))) {
		log_pedantic("Could not find an existing user object, nor could we create one.");
		return -1;
	}

	meta_user_wlock(user);

	// Pull the user information.
	if ((state = meta_update_user(user, META_LOCKED)) < 0) {
		meta_user_unlock(user);
		meta_inx_remove(usernum, protocol);
		return state;
	}

	// The auth_t object should have checked the verification token already, but we check here just to be sure.
	else if (st_empty(user->verification) || st_cmp_cs_eq(verification, user->verification)) {
		meta_user_unlock(user);
		meta_inx_remove(usernum, protocol);
		return 1;
	}

	// Are we supposed to get the realm keys.
	if ((get & META_GET_KEYS) && meta_update_realms(user, salt, master, META_LOCKED) < 0) {
		meta_user_unlock(user);
		meta_inx_remove(usernum, protocol);
		return -1;
	}

	// Are we supposed to get the mailbox keys.
	if ((get & META_GET_KEYS) && meta_update_keys(user, META_LOCKED) < 0) {

		// If key decryption fails, then the master key is likely invalid, so we need to ensure we don't cache an invalid
		// realm key as a result.
		st_cleanup(user->realm.mail);
		user->realm.mail = NULL;
		meta_user_unlock(user);
		meta_inx_remove(usernum, protocol);
		return -1;
	}

 	 // Are we supposed to get the mailbox aliases.
	if ((get & META_GET_ALIASES) && meta_update_aliases(user, META_LOCKED) < 0) {
		meta_user_unlock(user);
		meta_inx_remove(usernum, protocol);
		return -1;
	}

	// Are we supposed to get the messages.
	if ((get & META_GET_MESSAGES) && meta_messages_update(user, META_LOCKED) < 0) {
		meta_user_unlock(user);
		meta_inx_remove(usernum, protocol);
		return -1;
	}

	if ((get & META_GET_FOLDERS) && meta_update_message_folders(user, META_LOCKED) < 0) {
		meta_user_unlock(user);
		meta_inx_remove(usernum, protocol);
		return -1;
	}

	// Are we supposed to update the folders.
	if ((get & META_GET_FOLDERS) && meta_update_folders(user, META_LOCKED) < 0) {
		meta_user_unlock(user);
		meta_inx_remove(usernum, protocol);
		return -1;
	}

	// Are we supposed to update the folders.
	if ((get & META_GET_CONTACTS) && meta_update_contacts(user, META_LOCKED) < 0) {
		meta_user_unlock(user);
		meta_inx_remove(usernum, protocol);
		return -1;
	}

	*output = user;
	meta_user_unlock(user);

	return 0;
}
Пример #23
0
/**
 * @brief	Load all magma configuration options specified by the user on the command line.
 * @note	Each key/value pair extracted from the database is submitted to the following logic:
 *	 			If a config option was loaded from the database, the key must allow it to be configurable via the database.
 *	 			Check to see that any key that has previously been set is allowed to be overwritten.
 * 				If the key is required, it may not contain an empty value.
 * 			Finally, this function sets the appropriate magma key corresponding to the config key.
 * 			All leftover keys not matched to global magma keys will be configured via servers, relay, and cache server options.
 * @return	true if all database config options were parsed and evaluated successfully, or false on failure.
 */
bool_t config_load_cmdline_settings(void) {
	multi_t name;
	magma_keys_t *key;
	inx_cursor_t *cursor;
	nvp_t *config_pairs = NULL;
	stringer_t *value;

	// If not set, then bail out.
	if (!cmdline_config_data)
		return true;

	// Load the command line options and convert them into a name/value pair structure.
	if (!(config_pairs = nvp_alloc())) {
		st_free(cmdline_config_data);
		return false;
	}
	else if (nvp_parse(config_pairs, cmdline_config_data) < 0) {
		nvp_free(config_pairs);
		st_free(cmdline_config_data);
		return false;
	}
	else if (!(cursor = inx_cursor_alloc(config_pairs->pairs))) {
		nvp_free(config_pairs);
		st_free(cmdline_config_data);
		return false;
	}

	// Our command line config data won't be necessary anymore.
	st_free(cmdline_config_data);

	// Run through all of the magma_keys and see if there is a matching name/value pair.
	while (!mt_is_empty(name = inx_cursor_key_next(cursor))) {

		value = inx_cursor_value_active(cursor);

		if ((key = config_key_lookup(name.val.st))) {

			// Make sure the setting can be provided via the configuration file.
			if (!key->file && value) {
					log_critical("%s cannot be changed using command line option.", key->name);
					inx_cursor_free(cursor);
					nvp_free(config_pairs);
					return false;
			}
			// Make sure the required magma_keys are not set to NULL.
			else if (key->required && st_empty(value)) {
				log_critical("%s requires a legal value.", key->name);
				inx_cursor_free(cursor);
				nvp_free(config_pairs);
				return false;
			}

			// Attempt to set the value.
			else if (!config_value_set(key, value)) {
				inx_cursor_free(cursor);
				nvp_free(config_pairs);
				return false;
			}

			// If a legit value was provided, then record that we've set this parameter.
			key->set = true;
		}

		// If we haven't had a match yet, check if its a server param.
		else if (name.val.st && !st_cmp_ci_starts(name.val.st, CONSTANT("magma.servers"))) {
			servers_config(name.val.st, value);
		}

		// If we haven't had a match yet, check if its a relay instance.
		else if (name.val.st && !st_cmp_ci_starts(name.val.st, CONSTANT("magma.relay"))) {
			relay_config(name.val.st, value);
		}

		else if (name.val.st && !st_cmp_ci_starts(name.val.st, CONSTANT("magma.iface.cache.host"))) {
			cache_config(name.val.st, value);
		}

		else {
			log_critical("%.*s is not a valid setting.", st_length_int(name.val.st), st_char_get(name.val.st));
			inx_cursor_free(cursor);
			nvp_free(config_pairs);
			return false;
		}
	}

	inx_cursor_free(cursor);
	nvp_free(config_pairs);

	return true;
}
Пример #24
0
/**
 * @brief	Set the value of a global config key.
 * @note	This function will also free the value of the global config key if it has already previously been set.
 * @param	setting		a pointer to the global key to have its value adjusted.
 * @param	value		a managed string containing the new key value, or if NULL, the key's default value will be used.
 * @return	true if the specified key's value was set successfully, or false on failure.
 */
bool_t config_value_set(magma_keys_t *setting, stringer_t *value) {

	bool_t result = true;

	/// LOW: Realtime blacklist domains are handled using custom code because we don't yet have a generic type to store lists.
	if (!st_cmp_cs_eq(NULLER(setting->name), PLACER("magma.smtp.blacklist", 20))) {

		// When the default values are assigned an empty string is passed in. Returning false then tricks the system into
		// thinking the default value is wrong, so we just return true to avoid the issue.
		if (st_empty(value)) {
			return true;
		}

		// Were using a fixed array, so if we run out of room we have to reject config.
		if (magma.smtp.blacklists.count >= MAGMA_BLACKLIST_INSTANCES) {
			log_critical("magma.smtp.blacklist is limited to %u %s and the configuration currently contains more than %u %s.",
				MAGMA_BLACKLIST_INSTANCES, MAGMA_BLACKLIST_INSTANCES == 1 ? "domain" : "domains", MAGMA_BLACKLIST_INSTANCES,
				MAGMA_BLACKLIST_INSTANCES == 1 ? "domain" : "domains");
			return false;
		}

		// Make sure the targeted array slot is empty, and the string passed to us has data in it. If so duplicate the value and
		// and store it in the array. If anything fails, return false.
		else if (magma.smtp.blacklists.domain[magma.smtp.blacklists.count] || !(magma.smtp.blacklists.domain[magma.smtp.blacklists.count] =
			st_dupe_opts(MANAGED_T | CONTIGUOUS | HEAP, value))) {
			return false;
		}

		// Track the number of blacklist domains we have.
		magma.smtp.blacklists.count++;

		// Return true so the switch statement below doesn't corrupt the array.
		return true;
	}
	else if (!st_cmp_cs_eq(NULLER(setting->name), NULLER("magma.smtp.bypass_addr"))) {

			// When the default values are assigned an empty string is passed in. Returning false then tricks the system into
			// thinking the default value is wrong, so we just return true to avoid the issue.
			if (st_empty(value)) {
				return true;
			}

			if (!smtp_add_bypass_entry(value)) {
				log_critical("Unable to add smtp bypass entry { entry = %s }", st_char_get(value));
				return false;
			}

			// Return true so the switch statement below doesn't corrupt the array.
			return true;
		}


	switch (setting->norm.type) {

	// Strings
	case (M_TYPE_NULLER):
		if (!ns_empty(*((char **)(setting->store)))) {
			ns_free(*((char **)(setting->store)));
			*((char **)(setting->store)) = NULL;
		}
		if (!st_empty(value))
			*((char **)(setting->store)) = ns_import(st_char_get(value), st_length_get(value));
		else if (!ns_empty(setting->norm.val.ns))
			*((char **)(setting->store)) = ns_dupe(setting->norm.val.ns);
		break;

	case (M_TYPE_STRINGER):
		if (!st_empty(*((stringer_t **)(setting->store)))) {
			st_free(*((stringer_t **)(setting->store)));
			*((stringer_t **)(setting->store)) = NULL;
		}
		if (!st_empty(value))
			*((stringer_t **)(setting->store)) = st_dupe_opts(MANAGED_T | CONTIGUOUS | HEAP, value);
		else if (!st_empty(setting->norm.val.st))
			*((stringer_t **)(setting->store)) = st_dupe_opts(MANAGED_T | CONTIGUOUS | HEAP, setting->norm.val.st);
		break;

		// Booleans
	case (M_TYPE_BOOLEAN):
		if (!st_empty(value)) {
			if (!st_cmp_ci_eq(value, CONSTANT("true")))
				*((bool_t *)(setting->store)) = true;
			else if (!st_cmp_ci_eq(value, CONSTANT("false")))
				*((bool_t *)(setting->store)) = false;
			else {
				log_critical("Invalid value for %s.", setting->name);
				result = false;
			}
		} else
			*((bool_t *)(setting->store)) = setting->norm.val.binary;
		break;

		// Integers
	case (M_TYPE_INT8):
		if (!st_empty(value)) {
			if (!int8_conv_st(value, (int8_t *)(setting->store))) {
				log_critical("Invalid value for %s.", setting->name);
				result = false;
			}
		} else
			*((int8_t *)(setting->store)) = setting->norm.val.i8;
		break;

	case (M_TYPE_INT16):
		if (!st_empty(value)) {
			if (!uint16_conv_st(value, (uint16_t *)(setting->store))) {
				log_critical("Invalid value for %s.", setting->name);
				result = false;
			}
		} else
			*((int16_t *)(setting->store)) = setting->norm.val.u16;
		break;

	case (M_TYPE_INT32):
		if (!st_empty(value)) {
			if (!int32_conv_st(value, (int32_t *)(setting->store))) {
				log_critical("Invalid value for %s.", setting->name);
				result = false;
			}
		} else
			*((int32_t *)(setting->store)) = setting->norm.val.i32;
		break;

	case (M_TYPE_INT64):
		if (!st_empty(value)) {
			if (!int64_conv_st(value, (int64_t *)(setting->store))) {
				log_critical("Invalid value for %s.", setting->name);
				result = false;
			}
		} else
			*((int64_t *)(setting->store)) = setting->norm.val.i64;
		break;

		// Unsigned Integers
	case (M_TYPE_UINT8):
		if (!st_empty(value)) {
			if (!uint8_conv_st(value, (uint8_t *)(setting->store))) {
				log_critical("Invalid value for %s.", setting->name);
				result = false;
			}
		} else
			*((uint8_t *)(setting->store)) = setting->norm.val.u8;
		break;

	case (M_TYPE_UINT16):
		if (!st_empty(value)) {
			if (!uint16_conv_st(value, (uint16_t *)(setting->store))) {
				log_critical("Invalid value for %s.", setting->name);
				result = false;
			}
		} else
			*((uint16_t *)(setting->store)) = setting->norm.val.u16;
		break;

	case (M_TYPE_UINT32):
		if (!st_empty(value)) {
			if (!uint32_conv_st(value, (uint32_t *)(setting->store))) {
				log_critical("Invalid value for %s.", setting->name);
				result = false;
			}
		} else
			*((uint32_t *)(setting->store)) = setting->norm.val.u32;
		break;

	case (M_TYPE_UINT64):
		if (!st_empty(value)) {
			if (!uint64_conv_st(value, (uint64_t *)(setting->store))) {
				log_critical("Invalid value for %s.", setting->name);
				result = false;
			}
		} else
			*((uint64_t *)(setting->store)) = setting->norm.val.u64;
		break;

	default:
		log_critical("The %s setting definition is using an invalid type.", setting->name);
		result = false;
		break;
	}
	return result;
}
Пример #25
0
/**
 * @brief	Read a line of input from a network client session.
 * @return	-1 on general failure, -2 if the connection was reset, or the length of the current line of input, including the trailing new line character.
 */
int64_t client_read_line(client_t *client) {

	ssize_t bytes = 0;
	int_t counter = 0;
	stringer_t *error = NULL;
	bool_t blocking = true, line = false;

#ifdef MAGMA_PEDANTIC
	int_t local = 0;
	stringer_t *ip = NULL, *cipher = NULL;
#endif

	if (!client || client->sockd == -1) {
		if (client) client->status = 1;
		return -1;
	}

	// Check for data past the current line buffer.
	else if (pl_length_get(client->line) && st_length_get(client->buffer) > pl_length_get(client->line)) {

		// Move the unused data to the front of the buffer.
		mm_move(st_data_get(client->buffer), st_data_get(client->buffer) + pl_length_get(client->line), st_length_get(client->buffer) - pl_length_get(client->line));

		// Update the length.
		st_length_set(client->buffer, st_length_get(client->buffer) - pl_length_get(client->line));

		// Check whether the data we just moved contains a complete line.
		if (!pl_empty((client->line = line_pl_st(client->buffer, 0)))) {
			client->status = 1;
			return pl_length_get(client->line);
		}
	}
	// Otherwise reset the buffer and line lengths to zero.
	else {
		st_length_set(client->buffer, 0);
		client->line = pl_null();
	}

	// Loop until we get a complete line, an error, or the buffer is filled.
	do {

		// Read bytes off the network. Skip past any existing data in the buffer.
		if (client->tls) {

			// If bytes is zero or below and the library isn't asking for another read, then an error occurred.
			bytes = tls_read(client->tls, st_char_get(client->buffer) + st_length_get(client->buffer),
				st_avail_get(client->buffer) - st_length_get(client->buffer), blocking);

			// If zero bytes were read, or a negative value was returned to indicate an error, call tls_erorr(), which will return
			// NULL if the error can be safely ignored. Otherwise log the output for debug purposes.
			if (bytes <= 0 && (error = tls_error(client->tls, bytes, MANAGEDBUF(512)))) {
#ifdef MAGMA_PEDANTIC
				cipher = tls_cipher(client->tls, MANAGEDBUF(128));
				ip = ip_presentation(client->ip, MANAGEDBUF(INET6_ADDRSTRLEN));

				log_pedantic("TLS client read operation failed. { ip = %.*s / %.*s / result = %zi%s%.*s }",
					st_length_int(ip), st_char_get(ip), st_length_int(cipher), st_char_get(cipher),
					bytes, (error ? " / " : ""), st_length_int(error), st_char_get(error));
#endif
				client->status = -1;
				return -1;
			}
			// This will occur when the read operation results in a 0, or negative value, but TLS error returns NULL to
			// indicate it was a transient error. For transient errors we simply set bytes equal to 0 so the read call gets retried.
			else if (bytes <= 0) {
				bytes = 0;
			}
		}
		else {

			errno = 0;

			bytes = recv(client->sockd, st_char_get(client->buffer) + st_length_get(client->buffer),
				st_avail_get(client->buffer) - st_length_get(client->buffer), (blocking ? 0 : MSG_DONTWAIT));

			// Check for errors on non-SSL reads in the traditional way.
			if (bytes <= 0 && tcp_status(client->sockd)) {
#ifdef MAGMA_PEDANTIC
				local = errno;
				ip = ip_presentation(client->ip, MANAGEDBUF(INET6_ADDRSTRLEN));

				log_pedantic("TCP client read operation failed. { ip = %.*s / result = %zi / error = %i / message = %s }",
					st_length_int(ip), st_char_get(ip), bytes, local, strerror_r(local, MEMORYBUF(1024), 1024));
#endif
				client->status = -1;
				return -1;
			}

		}

		// We actually read in data, so we need to update the buffer to reflect the amount of data it currently holds.
		if (bytes > 0) {
			st_length_set(client->buffer, st_length_get(client->buffer) + bytes);
		}

		// Check whether we have a complete line before checking whether the connection was closed.
		if (!st_empty(client->buffer) && !pl_empty((client->line = line_pl_st(client->buffer, 0)))) {
			line = true;
		}

	} while (!line && counter++ < 128 && st_length_get(client->buffer) != st_avail_get(client->buffer) && status());

	if (st_length_get(client->buffer) > 0) {
		client->status = 1;
	}

	return pl_length_get(client->line);
}
Пример #26
0
/**
 * @brief	Output a key name and value in a generic way.
 * @param	prefix		a pointer to a null-terminated string containing an optional prefix to be printed before the supplied key name.
 * @param	name		a pointer to a null-terminated string containing the name of the key being output.
 * @param	type		an M_TYPE value specifying the multi-type of the key value.
 * @param	val			a void pointer to the value of the specified key, which will be printed in accordance with the supplied multi-type.
 * @param	required	a boolean value specifying whether the specified key is a required configuration option.
 * @return	This function returns no value.
 */
void config_output_value_generic(chr_t *prefix, chr_t *name, M_TYPE type, void *val, bool_t required) {

	chr_t *reqstr = "";

	if (!prefix) {
		prefix = "";
	}

	if (required) {
		reqstr = "*";
	}

	switch (type) {
	case (M_TYPE_NULLER):
		if (ns_empty(*((char **)(val))))
			log_info("%s%s%s = NULL", prefix, name, reqstr);
		else
			log_info("%s%s%s = %s", prefix, name, reqstr, *((char **)(val)));
		break;

	case (M_TYPE_STRINGER):
		// Intercept the blacklist config key->
		if (!st_cmp_cs_eq(NULLER(name), PLACER("magma.smtp.blacklist", 20))) {

			if (!magma.smtp.blacklists.count) {
				log_info("%s%s%s = NULL", prefix, name, reqstr);
			}

			for (uint32_t j = 0; j < magma.smtp.blacklists.count; j++) {
				log_info("%s%s%s = %.*s", prefix, name, reqstr, st_length_int(magma.smtp.blacklists.domain[j]), st_char_get(magma.smtp.blacklists.domain[j]));
			}
		}

		else if (st_empty(*((stringer_t **)(val))))
			log_info("%s%s%s = NULL", prefix, name, reqstr);
		else
			log_info("%s%s%s = %.*s", prefix, name, reqstr, st_length_int(*((stringer_t **)(val))), st_char_get(*((stringer_t **)(val))));
		break;

	case (M_TYPE_ENUM):

			if (!st_cmp_cs_eq(NULLER(name), CONSTANT(".protocol"))) {
				if (*((M_PROTOCOL *)((char *)val)) == MOLTEN)
					log_info("%s%s%s = MOLTEN", prefix, name, reqstr);
				else if (*((M_PROTOCOL *)((char *)val)) == HTTP)
					log_info("%s%s%s = HTTP", prefix, name, reqstr);
				else if (*((M_PROTOCOL *)((char *)val)) == POP)
					log_info("%s%s%s = POP", prefix, name, reqstr);
				else if (*((M_PROTOCOL *)((char *)val)) == IMAP)
					log_info("%s%s%s = IMAP", prefix, name, reqstr);
				else if (*((M_PROTOCOL *)((char *)val)) == SMTP)
					log_info("%s%s%s = SMTP", prefix, name, reqstr);
				else if (*((M_PROTOCOL *)((char *)val)) == SUBMISSION)
					log_info("%s%s%s = SUBMISSION", prefix, name, reqstr);
				else if (*((M_PROTOCOL *)((char *)val)) == EMPTY)
						log_info("%s%s%s = EMPTY", prefix, name, reqstr);
				else
					log_info("%s%s%s = [UNKNOWN]", prefix, name, reqstr);
			} else if (!st_cmp_cs_eq(NULLER(name), CONSTANT(".network.type"))) {
				if (*((M_PORT *)((char *)val)) == TCP_PORT)
					log_info("%s%s%s = TCP", prefix, name, reqstr);
				else if (*((M_PORT *)((char *)val)) == SSL_PORT)
					log_info("%s%s%s = SSL", prefix, name, reqstr);
				else
					log_info("%s%s%s = [UNKNOWN]", prefix, name, reqstr);
			}
		break;

	case (M_TYPE_BOOLEAN):
		log_info("%s%s%s = %s", prefix, name, reqstr, (*((bool_t *)(val)) ? "true" : "false"));
		break;

	case (M_TYPE_INT8):
		log_info("%s%s%s = %hhi", prefix, name, reqstr, *((int8_t *)(val)));
		break;
	case (M_TYPE_INT16):
		log_info("%s%s%s = %hi", prefix, name, reqstr, *((int16_t *)(val)));
		break;

	case (M_TYPE_INT32):
		log_info("%s%s%s = %i", prefix, name, reqstr, *((int32_t *)(val)));
		break;

	case (M_TYPE_INT64):
		log_info("%s%s%s = %li", prefix, name, reqstr, *((int64_t *)(val)));
		break;

	case (M_TYPE_UINT8):
		log_info("%s%s%s = %hhu", prefix, name, reqstr, *((uint8_t *)(val)));
		break;
	case (M_TYPE_UINT16):
		log_info("%s%s%s = %hu", prefix, name, reqstr, *((uint16_t *)(val)));
		break;
	case (M_TYPE_UINT32):
		log_info("%s%s%s = %u", prefix, name, reqstr, *((uint32_t *)(val)));
		break;
	case (M_TYPE_UINT64):
		log_info("%s%s%s = %lu", prefix, name, reqstr, *((uint64_t *)(val)));
		break;
	default:
		log_pedantic("Unexpected type. {type = %u}", type);
		break;
	}

	return;
}
Пример #27
0
/**
 * @brief	Get a placer pointing to the specified child inside a MIME body.
 * @param	body		a placer containing the body text to be parsed.
 * @param	boundary	a pointer to a managed string containing the boundary string to split the MIME content.
 * @param	child		the zero-based index of the MIME child to be located in the body text.
 * @return	pl_null() on failure, or a placer containing the specified MIME child on success.
 */
placer_t mail_mime_child(placer_t body, stringer_t *boundary, uint32_t child) {

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

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

	// Figure out the lengths.
	if (!(length = st_length_get(&body))) {
		log_pedantic("Cannot parse children from zero-length MIME body..");
		return pl_null();
	}
	else if (!(boundlen = st_length_get(boundary))) {
		log_pedantic("Cannot parse children from MIME body with zero-length boundary.");
		return pl_null();;
	}

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

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

		if (mm_cmp_cs_eq(stream, bounddata, boundlen) == 0 && (increment + boundlen == length || *(stream + boundlen) < '!' || *(stream + boundlen) > '~')) {
			stream += boundlen;
			increment += boundlen;

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

		}
		else {
			stream++;
			increment++;
		}

	}

	// The requested child wasn't found.
	if (increment + boundlen >= length) {
		return pl_null();
	}

	// This will skip a line break after the boundary marker.
	if (length - increment > 0 && *stream == '\r') {
		stream++;
		increment++;
	}

	if (length - increment > 0 && *stream == '\n') {
		stream++;
		increment++;
	}

	// Store the start position.
	start = stream;

	// Find the end.
	while (increment < length) {

		if (increment + boundlen < length && mm_cmp_cs_eq(stream, bounddata, boundlen) == 0) {
			increment = length;
		}
		else {
			stream++;
			increment++;
		}
	}

	// Make sure we advanced.
	if (stream == start) {
		return pl_null();
	}

	return pl_init(start, stream - start);
}
Пример #28
0
/**
 * @brief	Read a line of input from a network connection.
 * @note	This function handles reading data from both regular and ssl connections.
 * 			This function continually attempts to read incoming data from the specified connection until a \n terminated line of input is received.
 * 			If a new line is read, the length of that line is returned to the caller, including the trailing \n.
 * 			If the read returns -1 and wasn't caused by a syscall interruption or blocking error, -1 is returned, and the connection status is set to -1.
 * 			If the read returns 0 and wasn't caused by a syscall interruption or blocking error, -2 is returned, and the connection status is set to 2.
 * 			Once a \n character is reached, the length of the current line of input is returned to the user, and the connection status is set to 1.
 * @param	con		the network connection across which the line of data will be read.
 * @return	-1 on general failure, -2 if the connection was reset, or the length of the current line of input, including the trailing new line character.
 */
int64_t con_read_line(connection_t *con, bool_t block) {

	ssize_t bytes = 0;
	int_t counter = 0;
	bool_t line = false;

	if (!con || con->network.sockd == -1 || con_status(con) < 0) {
		if (con) con->network.status = -1;
		return -1;
	}

	// Check for an existing network buffer. If there isn't one, try creating it.
	else if (!con->network.buffer && !con_init_network_buffer(con)) {
		con->network.status = -1;
		return -1;
	}

	// Check if we have received more data than just what is in the current line of input.
	else if (pl_length_get(con->network.line) && st_length_get(con->network.buffer) > pl_length_get(con->network.line)) {

		// If so, move the unused "new" data after the current line marker to the front of the buffer.
		mm_move(st_data_get(con->network.buffer), st_data_get(con->network.buffer) + pl_length_get(con->network.line),
			st_length_get(con->network.buffer) - pl_length_get(con->network.line));

		// Update the buffer length.
		st_length_set(con->network.buffer, st_length_get(con->network.buffer) - pl_length_get(con->network.line));

		// Check whether the data we just moved contains a complete line.
		if (!pl_empty((con->network.line = line_pl_st(con->network.buffer, 0)))) {
			con->network.status = 1;
			return pl_length_get(con->network.line);
		}

	}
	// Otherwise reset the buffer and line lengths to zero.
	else {
		st_length_set(con->network.buffer, 0);
		con->network.line = pl_null();
	}

	// Loop until we get a complete line, an error, or the buffer is filled.
	do {
//		blocking = st_length_get(con->network.buffer) ? false : true;
		block = true;

		if (con->network.tls) {
			bytes = tls_read(con->network.tls, st_char_get(con->network.buffer) + st_length_get(con->network.buffer),
				st_avail_get(con->network.buffer) - st_length_get(con->network.buffer), block);
		}
		else {
			bytes = tcp_read(con->network.sockd, st_char_get(con->network.buffer) + st_length_get(con->network.buffer),
				st_avail_get(con->network.buffer) - st_length_get(con->network.buffer), block);
		}

		// We actually read in data, so we need to update the buffer to reflect the amount of unprocessed data it currently holds.
		if (bytes > 0) {
			st_length_set(con->network.buffer, st_length_get(con->network.buffer) + bytes);
		}
		else if (bytes == 0) {
			usleep(1000);
		}
		else {
			con->network.status = -1;
			return -1;
		}

		// Check whether we have a complete line before checking whether the connection was closed.
		if (!st_empty(con->network.buffer) && !pl_empty((con->network.line = line_pl_st(con->network.buffer, 0)))) {
			line = true;
		}

	} while (!line && block && counter++ < 128 && st_length_get(con->network.buffer) != st_avail_get(con->network.buffer) && status());

	if (st_length_get(con->network.buffer) > 0) {
		con->network.status = 1;
	}

	return pl_length_get(con->network.line);
}
Пример #29
0
/**
 * @brief	Parse a block of data into a mail mime object.
 * @note	By parsing the specified mime part, this function fills in the content type and encoding of the resulting mail mime object.
 * 			If the message is multipart, the boundary string is determined and then used to split the body into children;
 * 			then each child part is passed to mail_mime_part() to be parsed likewise, recursively.
 * @param	part		a managed string containing the mime part data to be parsed.
 * @param	recursion	an incremented recursion level tracker for calling this function, to prevent an overflow from occurring.
 * @return	NULL on failure or a pointer to a newly allocated and updated mail mime object parsed from the part data on success.
 */
mail_mime_t * mail_mime_part(stringer_t *part, uint32_t recursion) {

	array_t *holder;
	size_t elements, increment;
	mail_mime_t *result, *subpart;

	// Recursion limiter.
	if (recursion >= MAIL_MIME_RECURSION_LIMIT) {
		log_pedantic("Recursion limit hit.");
		return NULL;
	}

	if (st_empty(part)) {
		log_pedantic("Passed an empty placer_t.");
		return NULL;
	}

	if (!(result = mm_alloc(sizeof(mail_mime_t)))) {
		log_pedantic("Could not allocate %zu bytes for the MIME structure.", sizeof(mail_mime_t));
		return NULL;
	}

	// Store the entire part, and figure out the length of the header.
	result->entire = pl_init(st_data_get(part), st_length_get(part));
	result->header = mail_mime_header(part);

	// Check to make sure the header doesn't take up the entire part.
	if (st_length_get(&(result->header)) != st_length_get(part)) {
		result->body = pl_init(st_char_get(part) + st_length_get(&(result->header)), st_length_get(part) - st_length_get(&(result->header)));
	}

	// Determine the content type.
	result->type = mail_mime_type(result->header);
	result->encoding = mail_mime_encoding(result->header);

	// If were dealing with a multipart message, get the boundary.
	if ((result->type == MESSAGE_TYPE_MULTI_ALTERNATIVE || result->type == MESSAGE_TYPE_MULTI_MIXED || result->type == MESSAGE_TYPE_MULTI_RELATED ||
		result->type == MESSAGE_TYPE_MULTI_RFC822 || result->type == MESSAGE_TYPE_MULTI_UNKOWN) && (result->boundary = mail_mime_boundary(result->header))) {

		// Get an array of message parts.
		if ((holder = mail_mime_split(result->body, result->boundary)) && (elements = ar_length_get(holder))) {

			if ((result->children = ar_alloc(elements))) {

				for (increment = 0; increment < elements; increment++) {

					if ((subpart = mail_mime_part(ar_field_st(holder, increment), recursion + 1))) {

						if (ar_append(&(result->children), ARRAY_TYPE_POINTER, subpart) != 1) {
							mail_mime_free(subpart);
						}

					}

				}

			}

		}

		if (holder) {
			ar_free(holder);
		}

	}

	return result;
}
Пример #30
0
/**
 * @brief	Read a line of input from a network connection.
 * @note	This function handles reading data from both regular and ssl connections.
 * 			This function continually attempts to read incoming data from the specified connection until a \n terminated line of input is received.
 * 			If a new line is read, the length of that line is returned to the caller, including the trailing \n.
 * 			If the read returns -1 and wasn't caused by a syscall interruption or blocking error, -1 is returned, and the connection status is set to -1.
 * 			If the read returns 0 and wasn't caused by a syscall interruption or blocking error, -2 is returned, and the connection status is set to 2.
 * 			Once a \n character is reached, the length of the current line of input is returned to the user, and the connection status is set to 1.
 * @param	con		the network connection across which the line of data will be read.
 * @return	-1 on general failure, -2 if the connection was reset, or the length of the current line of input, including the trailing \n.
 */
int64_t con_read_line(connection_t *con, bool_t block) {

	ssize_t bytes;
	bool_t line = false;

	if (!con || con->network.sockd == -1) {
		con->network.status = -1;
		return -1;
	}

	// Check for an existing network buffer. If there isn't one, try creating it.
	if (!con->network.buffer && !con_init_network_buffer(con)) {
		con->network.status = -1;
		return -1;
	}

	// Check if we have received more data than just what is in the current line of input.
	if (pl_length_get(con->network.line) && st_length_get(con->network.buffer) > pl_length_get(con->network.line)) {

		// If so, move the unused "new" data after the current line marker to the front of the buffer.
		mm_move(st_data_get(con->network.buffer), st_data_get(con->network.buffer) + pl_length_get(con->network.line),
			st_length_get(con->network.buffer) - pl_length_get(con->network.line));

		// Update the buffer length.
		st_length_set(con->network.buffer, st_length_get(con->network.buffer) - pl_length_get(con->network.line));

		// Check whether the data we just moved contains a complete line.
		if (!pl_empty((con->network.line = line_pl_st(con->network.buffer, 0)))) {
			con->network.status = 1;
			return pl_length_get(con->network.line);
		}

	}
	// Otherwise reset the buffer and line lengths to zero.
	else {
		st_length_set(con->network.buffer, 0);
		con->network.line = pl_null();
	}

	// Loop until we get a complete line, an error, or the buffer is filled.
	do {

		// Read bytes off the network. Skip past any existing data in the buffer.
		if (con->network.ssl) {

			// If bytes is zero or below and the library isn't asking for another read, then an error occurred.
			bytes = ssl_read(con->network.ssl, st_char_get(con->network.buffer) + st_length_get(con->network.buffer),
				st_avail_get(con->network.buffer) - st_length_get(con->network.buffer), block);

			if (bytes <= 0 && bytes != SSL_ERROR_WANT_READ) {
				con->network.status = -1;
				return -1;
			}
			else if (bytes <= 0) {
				return 0;
			}
		}
		else {
			bytes = recv(con->network.sockd, st_char_get(con->network.buffer) + st_length_get(con->network.buffer),
				st_avail_get(con->network.buffer) - st_length_get(con->network.buffer), (block ? 0 : MSG_DONTWAIT));

			// Check for errors on non-SSL reads in the traditional way.
			if (bytes <= 0 && errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) {
				con->network.status = -1;
				return -1;
			}
			else if (!bytes) {
				con->network.status = 2;
				return -2;
			}

		}

		if (bytes > 0) {
			st_length_set(con->network.buffer, st_length_get(con->network.buffer) + bytes);
		}

		// Check whether we have a complete line before checking whether the connection was closed.
		if (!st_empty(con->network.buffer) && !pl_empty((con->network.line = line_pl_st(con->network.buffer, 0)))) {
			line = true;
		}

	} while (status() && !line && st_length_get(con->network.buffer) != st_avail_get(con->network.buffer));

	if (st_length_get(con->network.buffer) > 0) {
		con->network.status = 1;
	}

	return pl_length_get(con->network.line);
}