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; } }
/** * @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; }
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; }
/** * @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; }
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; }
/** * @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; }
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; }
/** * @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; }
/** * @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; }
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; } }
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); }
/** * @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; }
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; }
/** * @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; }
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__); } } }
/** * @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; }
/** * @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; }
/** * @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; }
/** * @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; }
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; }
/** * @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)); }
/** * @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; }
/** * @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; }
/** * @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; }
/** * @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); }
/** * @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; }
/** * @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); }
/** * @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); }
/** * @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; }
/** * @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); }