/** * @brief Get the subtype of the Content-Type header value from a mime header. * @note For example in the case of a Content-Type of 'text/plain', "plain" would be the subtype. * @param header a placer pointing to the mime header to be parsed. * @return a managed string containing the content subtype of the header, with "plain" as the default. */ stringer_t * mail_mime_type_sub(placer_t header) { chr_t *stream; stringer_t *line, *result; size_t remaining, characters = 0; if (!(line = mail_header_fetch_cleaned(&header, PLACER("Content-Type", 12)))) { return st_import("plain", 5); } stream = st_char_get(line); remaining = st_length_get(line); // Advance past any garbage. while (remaining && (*stream == '\n' || *stream == '\r' || *stream == ' ' || *stream == '\t' || *stream == '"')) { stream++; remaining--; } // Count how many characters are in the group. Based on RFC4288, section 4.2. while (remaining && ((*stream >= 'a' && *stream <= 'z') || (*stream >= 'A' && *stream <= 'Z') || (*stream >= '0' && *stream <= '9') || *stream == '!' || *stream == '#' || *stream == '$' || *stream == '&' || *stream == '.' || *stream == '+' || *stream == '-' || *stream == '^' || *stream == '_')) { stream++; remaining--; } // Make sure we got something back. Use a default value of plain. if (!remaining || *stream != '/') { st_free(line); return st_import("plain", 5); } // Advance past the separator. stream++; remaining--; // Count how many characters are in the subtype. Based on RFC4288, section 4.2. while (remaining != 0 && ((*stream >= 'a' && *stream <= 'z') || (*stream >= 'A' && *stream <= 'Z') || (*stream >= '0' && *stream <= '9') || *stream == '!' || *stream == '#' || *stream == '$' || *stream == '&' || *stream == '.' || *stream == '+' || *stream == '-' || *stream == '^' || *stream == '_')) { stream++; remaining--; characters++; } // Make sure we got something back. Use a default value of text. if (!characters) { st_free(line); return st_import("plain", 5); } result = st_import(stream - characters, characters); st_free(line); return result; }
/** * @brief Get the key name of a mime header line parameter. * @param parameter a managed string containing the complete parameter (key/value pair) of the mime header line. * @return NULL on failure or a managed string containing the mime header parameter key name on success. */ stringer_t * mail_mime_type_parameters_key(stringer_t *parameter) { chr_t *stream; size_t length, characters = 0; if (!parameter) { return NULL; } length = st_length_get(parameter); stream = st_char_get(parameter); // Skip the garbage. while (length != 0 && *stream == ' ') { stream++; length--; } // Find the end of the key. while (length > 0 && *stream != '=') { characters++; length--; stream++; } // Make sure we got something back. if (!characters) { return NULL; } return st_import(stream - characters, characters); }
/** * @brief Return an ECIES public key as a null-terminated hex string. * @param key the input ECIES key pair. * @return NULL on failure, or the hex-formatted public key as a null-terminated string. */ stringer_t * deprecated_ecies_key_public_hex(EC_KEY *key) { char *hex; const EC_POINT *point; const EC_GROUP *group; stringer_t *result = NULL; if (!(point = EC_KEY_get0_public_key_d(key))) { log_info("No public key available. {%s}", ssl_error_string(MEMORYBUF(256), 256)); return NULL; } else if (!(group = EC_KEY_get0_group_d(key))) { log_info("No group available. {%s}", ssl_error_string(MEMORYBUF(256), 256)); return NULL; } else if (!(hex = EC_POINT_point2hex_d(group, point, POINT_CONVERSION_COMPRESSED, NULL))) { log_info("Unable to serialize the public key into hex. {%s}", ssl_error_string(MEMORYBUF(256), 256)); return NULL; } if (!(result = st_import(hex, ns_length_get(hex) + 1))) { log_info("Unable to make copy of ECIES public key."); } OPENSSL_free_d(hex); return result; }
/** * @brief Get the value of a mime header line parameter. * @param parameter a managed string containing the complete parameter (key/value pair) of the mime header line. * @return NULL on failure or a managed string containing the mime header parameter value on success. */ stringer_t * mail_mime_type_parameters_value(stringer_t *parameter) { chr_t *stream; size_t length, characters = 0; if (!parameter) { return NULL; } length = st_length_get(parameter); stream = st_char_get(parameter); // Skip the garbage. while (length && *stream == ' ') { stream++; length--; } // Find the end of the key. while (length && *stream != '=') { length--; stream++; } if (!length || *stream != '=') { return NULL; } // Skip any remaining garbage. while (length && (*stream == ' ' || *stream == '=' || *stream == '\"')) { stream++; length--; } // Find the length of the value. while (length && *stream != '"') { characters++; length--; stream++; } // Make sure we got something back. if (!characters) { return NULL; } // Remove the trailing space. The header cleanup function checks for consecutive spaces. if (*stream == ' ') { stream--; characters--; } // Make sure we got something back. if (!characters) { return NULL; } return st_import(stream - characters, characters); }
/** * @brief Get the content encoding type from a mime header. * @note This function parses the Content-Transfer-Encoding field of the header, returning "7bit" by default. * @param header a placer pointing to the mime header to be parsed. * @return a managed string containing the content encoding type value of the header, with "7bit" as default. */ stringer_t * mail_mime_content_encoding(placer_t header) { chr_t *stream; stringer_t *holder, *result = NULL; size_t remaining, characters = 0; if (!(holder = mail_header_fetch_cleaned(&header, PLACER("Content-Transfer-Encoding", 25)))) { return st_import("7bit", 4); } remaining = st_length_get(holder); stream = st_char_get(holder); // Advance past any garbage. while (remaining != 0 && (*stream == '\n' || *stream == '\r' || *stream == ' ' || *stream == '\t')) { stream++; remaining--; } // Count how many characters are in the group. Based on RFC2045, section 6.1. while (remaining != 0 && ((*stream >= 'a' && *stream <= 'z') || (*stream >= 'A' && *stream <= 'Z') || (*stream >= '0' && *stream <= '9') || *stream == '-')) { stream++; remaining--; characters++; } // Make sure we got something back. Use a default value of 7bit. if (characters != 0) { result = st_import(stream - characters, characters); } else { result = st_import("7bit", 4); } st_free(holder); return result; }
bool_t check_string_import(void) { stringer_t *s; if (!(s = st_import(st_data_get(string_check_constant), st_length_int(string_check_constant)))) { return false; } if (memcmp(st_char_get(s), st_char_get(string_check_constant), st_length_get(string_check_constant))) { st_free(s); return false; } st_free(s); return true; }
bool_t check_string_import(void) { stringer_t *s; if (!(s = st_import(st_data_get(constant), st_length_int(constant)))) { return false; } log_print("%28.28s = %.*s", "duplicate", st_length_int(s), st_char_get(s)); if (memcmp(st_char_get(s), st_char_get(constant), st_length_get(constant))) { st_free(s); return false; } st_free(s); return true; }
// Extracts an nil string. Not quite as liberal as the atomic string. int_t imap_parse_nstring(stringer_t **output, chr_t **start, size_t *length, chr_t type) { chr_t *holder; size_t left; stringer_t *result; // Get setup. holder = *start; left = *length; *output = NULL; // Advance until we have a break character. while (left != 0 && *holder >= '!' && *holder <= '~' && *holder != '"' && *holder != '[' && *holder != type) { holder++; left--; } // There was nothing to process or we hit an invalid character. if (*start == holder || (*holder != ' ' && *holder != '\r' && *holder != '\n' && *holder != '[' && *holder != type)) { return -1; } // Create a stringer with the result. if (!(result = st_import(*start, holder - *start))) { log_error("Unable to extract the nil string."); return -1; } // Advance to the next argument. if (left != 0 && *holder != type && *holder != '[') { holder++; left--; } // Update *start = holder; *length = left; *output = result; return 1; }
END_TEST START_TEST(check_parser_header) { dmime_common_headers_t *header1, *header2; int res = 0; size_t outsize; unsigned char *formatted; header1 = dime_prsr_headers_create(); header1->headers[HEADER_TYPE_DATE] = st_import("11:34:12 AM March 12, 2004", 26); header1->headers[HEADER_TYPE_TO] = st_import("*****@*****.**", 13); header1->headers[HEADER_TYPE_CC] = st_import("*****@*****.**", 16); header1->headers[HEADER_TYPE_FROM] = st_import("*****@*****.**", 22); header1->headers[HEADER_TYPE_ORGANIZATION] = st_import("Cool people organization", 24); header1->headers[HEADER_TYPE_SUBJECT] = st_import("here's stuff", 12); formatted = dime_prsr_headers_format(header1, &outsize); ck_assert_msg(formatted != NULL, "Failed to format common headers.\n"); header2 = dime_prsr_headers_parse(formatted, outsize); ck_assert_msg(header2 != NULL, "Failed to parse common headers.\n"); res = st_cmp_cs_eq(header1->headers[HEADER_TYPE_DATE], header2->headers[HEADER_TYPE_DATE]); ck_assert_msg(res == 0, "Date header was corrupted.\n"); res = st_cmp_cs_eq(header1->headers[HEADER_TYPE_TO], header2->headers[HEADER_TYPE_TO]); ck_assert_msg(res == 0, "To header was corrupted.\n"); res = st_cmp_cs_eq(header1->headers[HEADER_TYPE_CC], header2->headers[HEADER_TYPE_CC]); ck_assert_msg(res == 0, "CC header was corrupted.\n"); res = st_cmp_cs_eq(header1->headers[HEADER_TYPE_FROM], header2->headers[HEADER_TYPE_FROM]); ck_assert_msg(res == 0, "From header was corrupted.\n"); res = st_cmp_cs_eq(header1->headers[HEADER_TYPE_ORGANIZATION], header2->headers[HEADER_TYPE_ORGANIZATION]); ck_assert_msg(res == 0, "Organization header was corrupted.\n"); res = st_cmp_cs_eq(header1->headers[HEADER_TYPE_SUBJECT], header2->headers[HEADER_TYPE_SUBJECT]); ck_assert_msg(res == 0, "Subject header was corrupted.\n"); dime_prsr_headers_destroy(header1); dime_prsr_headers_destroy(header2); free(formatted); fprintf(stderr, "DMIME common header parsing complete.\n"); }
/** * @brief Get the content id from a mime header. * @note This function parses the Content-Id field of the header, returning "7bit" by default. * @param header a placer pointing to the mime header to be parsed. * @return NULL on failure, or a managed string containing the content id value of the header. */ stringer_t * mail_mime_content_id(placer_t header) { chr_t *stream; stringer_t *holder, *result = NULL; size_t remaining, characters = 0; if (!(holder = mail_header_fetch_cleaned(&header, PLACER("Content-Id", 10)))) { return NULL; } remaining = st_length_get(holder); stream = st_char_get(holder); // Advance past any garbage. while (remaining != 0 && (*stream == '\n' || *stream == '\r' || *stream == ' ' || *stream == '\t')) { stream++; remaining--; } // Count how many characters are in the group. Based on RFC2822, section 3.6.4. while (remaining != 0 && ((*stream >= 'a' && *stream <= 'z') || (*stream >= 'A' && *stream <= 'Z') || (*stream >= '0' && *stream <= '9') || *stream == '<' || *stream == '@' || *stream == '!' || *stream == '#' || *stream == '$' || *stream == '%' || *stream == '&' || *stream == '\'' || *stream == '*' || *stream == '+' || *stream == '-' || *stream == '/' || *stream == '=' || *stream == '?' || *stream == '^' || *stream == '_' || *stream == '`' || *stream == '{' || *stream == '|' || *stream == '}' || *stream == '~' || *stream == '[' || *stream == ']' || *stream == '.' || *stream == '>')) { stream++; remaining--; characters++; } // Make sure we got something back. if (characters != 0) { result = st_import(stream - characters, characters); } st_free(holder); return result; }
static stringer_t *mkstringer(const char *s) { stringer_t *st = st_import(s, strlen(s) + 1); ck_assert_ptr_ne(NULL, st); return st; }
/// HIGH: Return a result structure with the disposition, confidence, probability and signature data. Then store the analysis result with the message. Either in the DB or /// by adding a custom header to the message. Return codes should become 0 for success, or a negative integer for errors. Add statistical updates to check and train functions. /// Result: result=\"%s\"; class=\"%s\"; probability=%01.4f; confidence=%02.2f; signature=%lu; key=%lu; /// Layman's terms: How spammy is this message int_t dspam_check(uint64_t usernum, stringer_t *message, stringer_t **signature) { DSPAM_CTX *ctx; chr_t unum[20]; uint32_t connection; int_t result = 2, ret; struct _mysql_drv_dbh dbh; stringer_t *tmpdir, *output; // Generate a string version of the dispatch number. if (snprintf(unum, 20, "%lu", usernum) <= 0 || !(tmpdir = spool_path(MAGMA_SPOOL_DATA))) { log_pedantic("Context setup error."); return -1; } // Initialize the DSPAM context. else if (!(ctx = dspam_create_d(unum, NULL, st_char_get(tmpdir), DSM_PROCESS, DSF_SIGNATURE | DSF_NOISE | DSF_WHITELIST))) { log_pedantic("An error occurred inside the DSPAM library. {dspam_create = NULL}"); st_free(tmpdir); return -1; } st_free(tmpdir); if (pool_pull(sql_pool, &connection) != PL_RESERVED) { log_info("Unable to get an available connection for the query."); dspam_destroy_d(ctx); return -1; } else if (sql_ping(connection) < 0 || !stmt_rebuild(connection)) { log_info("The database connection has been lost and the reconnection attempt failed."); dspam_destroy_d(ctx); return -1; } // Setup the database handle in a structure format. dbh.dbh_read = pool_get_obj(sql_pool, connection); dbh.dbh_write = pool_get_obj(sql_pool, connection); if ((ret = dspam_attach_d(ctx, &dbh))) { if (dspam_detach_d(ctx) != 0) { log_pedantic("Could not detach the DB connection."); } pool_release(sql_pool, connection); log_pedantic("An error occurred while attaching to the statistical database. {dspam_attach = %i}", ret); dspam_destroy_d(ctx); return -1; } // Tokenization method and statistical algorithm. ctx->algorithms = DSA_GRAHAM | DSA_BURTON | DSP_GRAHAM; ctx->tokenizer = DSZ_CHAIN; // To prevent the message tokens from being stored in the database we disable training. ctx->training_mode = DST_NOTRAIN; // This actually processes the message. if ((ret = dspam_process_d(ctx, st_char_get(message)))) { if (dspam_detach_d(ctx) != 0) { log_pedantic("Could not detach the DB connection."); } pool_release(sql_pool, connection); log_pedantic("An error occurred while analyzing an email with DSPAM. {dspam_process = %i}", ret); dspam_destroy_d(ctx); return -1; } // We assume that the SQL connection will no longer be needed. if ((ret = dspam_detach_d(ctx))) { log_pedantic("Could not detach the DB connection. {dspam_detach = %i}", ret); pool_release(sql_pool, connection); dspam_destroy_d(ctx); return -1; } // Return the connection to our pool. pool_release(sql_pool, connection); // Check to see if the message is junk mail. if (ctx->result == DSR_ISSPAM) { result = -2; } else { result = 1; } //log_pedantic("Probability: %2.4f Confidence: %2.4f, Result: %s", ctx->probability, ctx->confidence, // (ctx->result == DSR_ISSPAM) ? "JUNK" : "INNOCENT"); // See what happens if we don't get a signature back. if (ctx->signature == NULL) { log_error("DSPAM did not return a signature. {ctx->signature = NULL}"); dspam_destroy_d(ctx); return result; } // Copy over the signature. if (!(output = st_import(ctx->signature->data, ctx->signature->length))) { log_pedantic("Could not import the statistical signature. {length = %lu}", ctx->signature->length); dspam_destroy_d(ctx); return result; } if (signature) { *signature = output; } // Destroy the context and return the result. dspam_destroy_d(ctx); return result; }
/** * @brief Generate a captcha image for a specified character string. * @param value a managed string containing the text that is to become the basis of the captcha challenge. * @return NULL on failure, or a managed string containing the path to the image file containing the captcha graphic on success. */ stringer_t * register_captcha_generate(stringer_t *value) { gdImagePtr image; chr_t string[2], *holder = NULL; double font_size = 40.0, angle; stringer_t *output = NULL, *font_path = NULL; chr_t *gderr; int_t brect[8], characters, color, increment; // int_t white; if (!(characters = st_length_get(value))) { log_pedantic("Zero length value passed in."); return NULL; } // We need a font when initializing the image. if (!(font_path = register_captcha_random_font())) { log_pedantic("Could not pick a random font."); return NULL; } mm_wipe(string, 2); if ((gderr = gdImageStringFT_d(NULL, &brect[0], 0, st_char_get(font_path), font_size, 0.0, 0, 0, string))) { log_pedantic("Could not initialize the rectangle: %s", gderr); return NULL; } // Creates an image that is 36 pixels wide for each character (+24 for the margin), and 47 pixels high. if (!(image = gdImageCreate_d((characters * 36) + 11, 47))) { log_pedantic("Could not create the image."); return NULL; } // The first color you allocate is used for the background. //white = gdImageColorResolve_d(image, 255, 255, 255); gdImageColorResolve_d(image, 255, 255, 255); // Write a bunch of randomly colored pixels onto the background. register_captcha_write_noise(image, (characters * 36) + 11, 47); mm_wipe(string, 2); // Write the string. for (increment = 0; increment < characters; increment++) { // Randomize the font size. By default it goes between 22px and 38px. font_size = 38.0 - (rand_get_uint32() % 16); // Pick a random angle. angle = 0.0; if ((rand_get_uint32() % 2) == 0) { angle -= rand_get_uint32() % 15; } else { angle += rand_get_uint32() % 25; } // Change the angle to radians. angle /= 360.0; angle *= (22.000/7.000); // Use the appropriate character. string[0] = *(st_char_get(value) + increment); // The character i needs to always be large, so people can see the upper dot. if (string[0] == 'i') { font_size = 38; } // Select a random font. if (!(font_path = register_captcha_random_font())) { gdImageDestroy_d(image); log_pedantic("Could not pick a random font."); return NULL; } // Select a random color, biased towards red. color = gdImageColorResolve_d(image, rand_get_uint32() % 256, rand_get_uint32() % 50, rand_get_uint32() % 50); // Write the character to the image. if (gdImageStringFT_d(image, &brect[0], color, st_char_get(font_path), font_size, angle, (36 * increment) + 12, 43 - ((40 - font_size) /2), string) != NULL) { gdImageDestroy_d(image); log_pedantic("Could not writer characters into the image."); return NULL; } } // Output Use gdImageJpegPtr_d to produce the output in JPEG. if (!(holder = gdImageGifPtr_d(image, &increment)) || !increment) { log_pedantic("Could not output the image to a buffer."); } else if (!(output = st_import(holder, increment))) { log_pedantic("Could not move the image into the output buffer."); gdFree_d(holder); } else { gdFree_d(holder); } gdImageDestroy_d(image); return output; }
prime_org_signet_t * org_signet_set(stringer_t *org) { prime_field_t *field = NULL; prime_object_t *object = NULL; prime_org_signet_t *result = NULL; if (!(object = prime_unpack(org))) { log_pedantic("Unable to parse the PRIME organizational signet."); return NULL; } else if (object->type != PRIME_ORG_SIGNET) { log_pedantic("The object passed in was not an organizational signet."); prime_object_free(object); return NULL; } else if (!(result = org_signet_alloc())) { log_pedantic("Unable to allocate a PRIME organizational signet."); prime_object_free(object); return NULL; } // Public signing key, verify the length and import the ed25519 public key. else if (!(field = prime_field_get(object, 1)) || st_length_get(&(field->payload)) != 32 || !(result->signing = ed25519_public_set(&(field->payload)))) { log_pedantic("Unable to parse the PRIME organizational signing key."); prime_object_free(object); org_signet_free(result); return NULL; } // Public encryption key, verify the length and import the compressed secp256k1 public key. else if (!(field = prime_field_get(object, 3)) || st_length_get(&(field->payload)) != 33 || !(result->encryption = secp256k1_public_set(&(field->payload)))) { log_pedantic("Unable to parse the PRIME organizational encryption key."); prime_object_free(object); org_signet_free(result); return NULL; } // Self signature taken over the cryptographic fields, verify the length and import the ed25519 signature. else if (!(field = prime_field_get(object, 4)) || st_length_get(&(field->payload)) != 64 || !(result->signature = st_import(pl_data_get(field->payload), pl_length_get(field->payload)))) { log_pedantic("Unable to parse the PRIME organizational signet signature."); prime_object_free(object); org_signet_free(result); return NULL; } // We don't need the packed object context any more. prime_object_free(object); // Verify the signature. if (!org_signet_verify(result)) { log_pedantic("The PRIME organizational signet signature is invalid."); org_signet_free(result); return NULL; } return result; }
/** * @brief Parse a json string array into a linked list of managed strings. * @param json a json object containing an array of strings. * @param nout if not NULL, an optional pointer to a size_t to receive the item count of the json string array. * @return NULL on failure, or a pointer to an inx holder containing the specified json array contents as a collection of managed strings. */ inx_t * portal_parse_json_str_array (json_t *json, size_t *nout) { inx_t *result; stringer_t *istr; const chr_t *str; size_t count; multi_t key = { .type = M_TYPE_UINT64, .val.u64 = 0 }; if (!json) { log_pedantic("Portal cannot parse null json array."); return NULL; // The json object must be an array. } else if (!json_is_array(json)) { log_pedantic("Portal cannot parse mistyped json array."); return NULL; } else if (!(result = inx_alloc(M_INX_LINKED, st_free))) { log_error("Portal could not allocate space to parse json array."); return NULL; } // If it's an empty array, return right away. if (!(count = json_array_size_d(json))) { if (nout) { *nout = 0; } return result; } // Before starting a SQL transaction check that all of the array values are positive integer values. for (size_t i = 0; i < count; i++) { if (!json_is_string(json_array_get_d(json, i))) { log_pedantic("Portal cannot parse json string array with non-string element."); inx_free(result); return NULL; } if (!(str = json_string_value_d(json_array_get_d(json, i)))) { log_pedantic("Portal encountered json string array with NULL element."); inx_free(result); return NULL; } if (!(istr = st_import(str, ns_length_get(str)))) { log_error("Portal could not import string from json array."); inx_free(result); return NULL; } key.val.u64 = i+1; if (!inx_insert(result, key, istr)) { log_error("Portal could not prepare data from json array."); inx_free(result); return NULL; } } if (nout) { *nout = count; } return result; } /** * @brief Parse the context of a requested folder. * @note If no context is specified, the "mail" context is assumed (PORTAL_ENDPOINT_CONTEXT_MAIL). * @param context a managed string containing the context (supported values are "mail", "contacts", "settings", and "help"). * @return PORTAL_ENDPOINT_CONTEXT_INVALID on failure, or the flag of the determined context on success. */ int_t portal_parse_context(stringer_t *context) { int_t result = PORTAL_ENDPOINT_CONTEXT_MAIL; if (!context) { return result; } // Parse the context. if (!st_cmp_ci_eq(context, PLACER("mail", 4))) { result = PORTAL_ENDPOINT_CONTEXT_MAIL; } else if (!st_cmp_ci_eq(context, PLACER("contacts", 8))) { result = PORTAL_ENDPOINT_CONTEXT_CONTACTS; } else if (!st_cmp_ci_eq(context, PLACER("settings", 8))) { result = PORTAL_ENDPOINT_CONTEXT_SETTINGS; } else if (!st_cmp_ci_eq(context, PLACER("help", 4))) { result = PORTAL_ENDPOINT_CONTEXT_HELP; } else { result = PORTAL_ENDPOINT_CONTEXT_INVALID; } return result; }