/** * @brief Insert a spam signature training link into a plain text message part. * @see mail_discover_insertion_point() * @param server the server object of the web server where the teacher application is hosted. * @param message a managed string containing the message body to be parsed. * @param part a managed string (placer) containing the part of the message where the signature should be inserted. * @param signum the spam signature number referenced by the teacher url. * @param sigkey the spam signature key for client verification in the teacher app. * @param disposition if 0, the message disposition is "innocent"; otherwise, the disposition specifies "spam". * @param type the encoding type of the message (MESSAGE_TYPE_HTML or other). * @param encoding if MESSAGE_ENCODING_QUOTED_PRINTABLE, set the encoding type to qp. * @return NULL on failure or a managed string containing the message with the inserted signature training link on success. */ stringer_t * mail_insert_chunk_text(server_t *server, stringer_t *message, stringer_t *part, uint64_t signum, uint64_t sigkey, int_t disposition, int_t type, int_t encoding) { size_t point; stringer_t *pretext; stringer_t *posttext; stringer_t *result; stringer_t *signature; if (st_empty(part)) { return NULL; } // Start at the end of the chunk and search for where to insert the signature. if (!(signature = mail_build_signature(server, type, encoding, signum, sigkey, disposition))) { return NULL; } // Figure out the insertion point_t and create the new message. point = mail_discover_insertion_point(message, part, type); pretext = PLACER(st_char_get(message), point); posttext = PLACER(st_char_get(message) + point, st_length_get(message) - point); result = st_merge("sss", pretext, signature, posttext); st_free(signature); if (!result) { log_pedantic("Unable to merge the strings together."); } return result; }
/** * @brief Serialize an unsigned 16-bit integer into a managed string. * @note This function will reallocate the output managed string if necessary, and append the serialized data, updating the length field to reflect the changes. * @param data a pointer to the address of a managed string to receive the output, which will be allocated for the caller if NULL is passed. * @param number the value of the unsigned 16-bit integer to be serialized. * @return true if the specified value was serialized and stored successfully, or false on failure. */ bool_t serialize_uint16(stringer_t **data, uint16_t number) { stringer_t *holder = NULL; size_t length = uint16_digits(number); // Make sure we have room to write the number. if (!data) { return false; } else if (!*data && (*data = st_alloc(1024)) == NULL) { return false; } else if (st_avail_get(*data) - st_length_get(*data) < (length+1) && (holder = st_realloc(*data, st_avail_get(*data) + 1024)) == NULL) { return false; } else if (holder) { *data = holder; } if (length != snprintf(st_char_get(*data) + st_length_get(*data), st_avail_get(*data) - st_length_get(*data), "%hu", number)) { return false; } *(st_char_get(*data) + st_length_get(*data) + length) = ';'; st_length_set(*data, st_length_get(*data) + length + 1); return true; }
bool_t check_string_dupe(char *type, uint32_t check) { size_t len; stringer_t *s, *d; if (!(s = st_alloc(check, st_length_int(constant)))) { return false; } len = snprintf(st_char_get(s), st_length_int(constant) + 1, "%.*s", st_length_int(constant), st_char_get(constant)); if (check & MAPPED_T || check & MANAGED_T) { st_length_set(s, len); } if (!(d = st_dupe(s))) { st_free(s); return false; } log_print("%28.28s = %.*s", type, st_length_int(d), st_char_get(d)); if (memcmp(st_char_get(d), st_char_get(s), st_length_get(s))) { st_free(s); st_free(d); return false; } st_free(s); st_free(d); return true; }
bool_t check_string_dupe(uint32_t check) { size_t len; stringer_t *s, *d; if (!(s = st_alloc_opts(check, st_length_int(string_check_constant)))) { return false; } len = snprintf(st_char_get(s), st_length_int(string_check_constant) + 1, "%.*s", st_length_int(string_check_constant), st_char_get(string_check_constant)); if ((check & MAPPED_T) || (check & MANAGED_T)) { st_length_set(s, len); } if (!(d = st_dupe(s))) { st_free(s); return false; } if (memcmp(st_char_get(d), st_char_get(s), st_length_get(s))) { st_free(s); st_free(d); return false; } st_free(s); st_free(d); return true; }
/** * @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; }
/** * @brief Get a placer pointing to a mime header in a mime part. * @param part a managed string containing the mime part text to be parsed. * @return a placer pointing to the mime header at the start of the specified mime part. */ placer_t mail_mime_header(stringer_t *part) { chr_t *stream; size_t length; int_t header = 0; length = st_length_get(part); stream = st_data_get(part); for (size_t i = 0; i < length && header != 3; i++) { if (header == 0 && *stream == '\n') { header++; } else if (header == 1 && *stream == '\n') { header += 2; } else if (header == 1 && *stream == '\r') { header++; } else if (header == 2 && *stream == '\n') { header++; } else if (header != 0) { header = 0; } stream++; } return pl_init(st_data_get(part), stream - st_char_get(part)); }
/** * @brief Validate an email address. * @param email a managed string containing the email address to be validated. * @return true if the specified email was valid and false if it was not. */ bool_t contact_business_valid_email(stringer_t *email) { chr_t *holder; size_t length, increment; if (!email) { return false; } // Lower case the string. lower_st(email); holder = st_char_get(email); length = st_length_get(email); if (!length) { return false; } // Catch leading at symbols. if (*holder == '@') { return false; } // The address. for (increment = 0; increment < length && *(holder + increment) != '@'; increment++) { if (*(holder + increment) < '!' || *(holder + increment) > '~') { return false; } } // Make sure we have an at symbol. if (increment == length || *(holder + increment++) != '@' || *(holder + increment) == '.' || increment == length) { return false; } // Look for the TLD seperator. while (increment < length && *(holder + increment) != '.') { if ((*(holder + increment) < 'a' || *(holder + increment) > 'z') && *(holder + increment) != '-') { return false; } increment++; } // Make sure we have a domain name. if (increment == length || *(holder + increment++) != '.' || increment == length) { return false; } // Go through the rest. while (increment < length) { if ((*(holder + increment) < 'a' || *(holder + increment) > 'z') && *(holder + increment) != '-' && *(holder + increment) != '.') { return false; } increment++; } return true; }
/** * @brief Get the number of bytes taken up by a mail header. * @param message a managed string containing the full smtp mail message. * @return 0 on failure, or the number of bytes occupied by the mail header. */ size_t mail_header_end(stringer_t *message) { const chr_t *msg; size_t msglen; if (!message) { log_pedantic("Received a NULL message pointer."); return 0; } msglen = st_length_get(message); msg = st_char_get(message); for (size_t i = 0; i < msglen; i++) { if (msg[i] == '\n') { if (i + 1 < msglen && msg[i + 1] == '\n') { return i + 2; } if (i + 2 < msglen && msg[i + 1] == '\r' && msg[i + 2] == '\n') { return i + 3; } } } return msglen; }
/** * @brief Insert a new contact entry into the database. * @param usernum the numerical id of the user to whom the contact entry belongs. * @param foldernum the numerical id of the parent folder to contain the contact entry. * @param name a pointer to a managed string containing the name of the new contact entry. * @return -1 on failure, 0 if no item was inserted into the database, or the id of the newly inserted contact entry in the database on success. */ uint64_t contact_insert(uint64_t usernum, uint64_t foldernum, stringer_t *name) { MYSQL_BIND parameters[3]; mm_wipe(parameters, sizeof(parameters)); // Usernum parameters[0].buffer_type = MYSQL_TYPE_LONGLONG; parameters[0].buffer_length = sizeof(uint64_t); parameters[0].buffer = &usernum; parameters[0].is_unsigned = true; // Folder parameters[1].buffer_type = MYSQL_TYPE_LONGLONG; parameters[1].buffer_length = sizeof(uint64_t); parameters[1].buffer = &foldernum; parameters[1].is_unsigned = true; // Name parameters[2].buffer_type = MYSQL_TYPE_STRING; parameters[2].buffer_length = st_length_get(name); parameters[2].buffer = st_char_get(name); return stmt_insert(stmts.insert_contact, parameters); }
/** * @brief Delete the specified contact detail of a contact entry from the database. * @param contactnum the numerical id of the contact entry to have the specified detail removed. * @param key a managed string containing the name of the contact detail to be removed from the entry. * @return -1 on error, 0 if no matching detail was found in the database, or 1 if the delete operation was successful. */ int_t contact_detail_delete(uint64_t contactnum, stringer_t *key) { int64_t affected; MYSQL_BIND parameters[4]; mm_wipe(parameters, sizeof(parameters)); // Contact Number parameters[0].buffer_type = MYSQL_TYPE_LONGLONG; parameters[0].buffer_length = sizeof(uint64_t); parameters[0].buffer = &contactnum; parameters[0].is_unsigned = true; // Key parameters[1].buffer_type = MYSQL_TYPE_STRING; parameters[1].buffer_length = st_length_get(key); parameters[1].buffer = st_char_get(key); if ((affected = stmt_exec_affected(stmts.delete_contact_details, parameters)) == -1) { log_pedantic("The contact detail deletion request triggered an error. { contact = %lu / key = %.*s }", contactnum, st_length_int(key), st_char_get(key)); return -1; } log_check(affected > 2); return (int_t)affected; }
/** * @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 Delete a user config entry from the database by key. * @param usernum the numerical userid of the user to whom the requested config key belongs. * @param key a managed string containing the name of the config key to be deleted. * @return 1 if the config option was successfully deleted, 0 if the config key couldn't be found in the database, or -1 on general failure. */ int_t user_config_delete(uint64_t usernum, stringer_t *key) { uint64_t affected; MYSQL_BIND parameters[4]; mm_wipe(parameters, sizeof(parameters)); // User Number parameters[0].buffer_type = MYSQL_TYPE_LONGLONG; parameters[0].buffer_length = sizeof(uint64_t); parameters[0].buffer = &usernum; parameters[0].is_unsigned = true; // Key parameters[1].buffer_type = MYSQL_TYPE_STRING; parameters[1].buffer_length = st_length_get(key); parameters[1].buffer = st_char_get(key); if (!(affected = stmt_exec_affected(stmts.delete_user_config, parameters)) == (my_ulonglong)-1) { log_pedantic("The user config deletion request triggered an error. { user = %lu / key = %.*s }", usernum, st_length_int(key), st_char_get(key)); return -1; } log_check(affected > 2); return (int_t)affected; }
/** * @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; }
/** * @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); }
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; }
/** * @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; }
/** * @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 Generate a MIME boundary string that is unique to a collection of content. * @param parts a pointer to an array of managed strings containing the MIME children data to be separated by the boundary. * @return NULL on failure, or a pointer to a managed string containing the generated boundary on success. */ stringer_t * mail_mime_generate_boundary (array_t *parts) { stringer_t *result, *cmp; chr_t *ptr; size_t blen = 16; int_t rnd; if (!parts) { log_pedantic("Cannot generate mime boundary with empty input data."); return NULL; } if (!(result = st_alloc(blen))) { log_error("Unable to allocate space for boundary string."); return NULL; } // Keep generating boundary strings until one of them is unique... we don't expect collisions to happen that often. while (1) { srand(rand_get_uint64()); ptr = st_char_get (result); // Generate blen's worth of random bytes, each being either a random digit or lowercase letter. for (size_t i = 0; i < blen; i++) { rnd = rand(); if (rnd % 2) { *ptr++ = '0' + (rnd % 10); } else { *ptr ++ = 'a' + (rnd % 26); } } st_length_set(result, blen); // Now make sure it's not found in any of the parts. for (size_t i = 0; i < ar_length_get(parts); i++) { if (!(cmp = (stringer_t *) ar_field_ptr(parts, i))) { log_pedantic("Could not generate mime boundary for null content."); st_free(result); return NULL; } if (st_search_ci(cmp, result, NULL)) { continue; } } // If we made it this far, the boundary string was not found in any of the supplied content. break; } return result; }
bool_t check_string_print(void) { uint64_t total; bool_t result = true; stringer_t *strings[14]; mm_set(strings, 0, sizeof(strings)); strings[0] = st_print(NULLER_T | CONTIGUOUS | HEAP, "%.*s", st_length_int(constant), st_char_get(constant)); strings[1] = st_print(NULLER_T | JOINTED | HEAP, "%.*s", st_length_int(strings[0]), st_char_get(strings[0])); strings[2] = st_print(BLOCK_T | CONTIGUOUS | HEAP, "%.*s", st_length_int(strings[1]), st_char_get(strings[1])); strings[3] = st_print(BLOCK_T | JOINTED | HEAP, "%.*s", st_length_int(strings[2]), st_char_get(strings[2])); strings[4] = st_print(MANAGED_T | CONTIGUOUS | HEAP, "%.*s", st_length_int(strings[3]), st_char_get(strings[3])); strings[5] = st_print(MANAGED_T | JOINTED | HEAP, "%.*s", st_length_int(strings[4]), st_char_get(strings[4])); strings[6] = st_print(MAPPED_T | JOINTED | HEAP, "%.*s", st_length_int(strings[5]), st_char_get(strings[5])); strings[7] = st_print(NULLER_T | CONTIGUOUS | SECURE, "%.*s", st_length_int(strings[6]), st_char_get(strings[6])); strings[8] = st_print(NULLER_T | JOINTED | SECURE, "%.*s", st_length_int(strings[7]), st_char_get(strings[7])); strings[9] = st_print(BLOCK_T | CONTIGUOUS | SECURE, "%.*s", st_length_int(strings[8]), st_char_get(strings[8])); strings[10] = st_print(BLOCK_T | JOINTED | SECURE, "%.*s", st_length_int(strings[9]), st_char_get(strings[9])); strings[11] = st_print(MANAGED_T | CONTIGUOUS | SECURE, "%.*s", st_length_int(strings[10]), st_char_get(strings[10])); strings[12] = st_print(MANAGED_T | JOINTED | SECURE, "%.*s", st_length_int(strings[11]), st_char_get(strings[11])); strings[13] = st_print(MAPPED_T | JOINTED | SECURE, "%.*s", st_length_int(strings[12]), st_char_get(strings[12])); for (int i = 0; i < 14 && strings[i]; i++) { for (unsigned int j = total = 0; strings[i] && j < st_length_get(strings[i]); j++) { total += *(st_char_get(strings[i]) + j); } if (total != 5366) { result = false; } } log_print("%28.28s = %s", "print", result ? "passed" : "failed"); for (int i = 0; i < 14; i++) { if (strings[i]) st_free(strings[i]); } return result; }
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; }
void tok_pop_init_st(tok_state_t *state, stringer_t *string, char token) { if (!state) { return; } state->token = token; state->block = state->position = (string == NULL ? NULL : st_char_get(string)); state->length = state->remaining = (string == NULL ? 0 : st_length_get(string)); }
/** * @brief Insert a spam signature training link into a mail message. * @see mail_modify_part() * @param message the mail message object of the message to be modified. * @param server the server object of the web server where the teacher application is hosted. * @param signum the spam signature number referenced by the teacher url. * @param sigkey the spam signature key for client verification in the teacher app. * @param disposition if 0, the message disposition is "innocent"; otherwise, the disposition specifies "spam". * @return This function returns no value. */ void mail_signature_add(mail_message_t *message, server_t *server, uint64_t signum, uint64_t sigkey, int_t disposition) { stringer_t *part; part = PLACER(st_char_get(message->text), st_length_get(message->text)); if ((mail_modify_part(server, message, part, signum, sigkey, disposition, 0)) == 0) { log_pedantic("------ MESSAGE ---------\n%.*s------------------", st_length_int(message->text), st_char_get(message->text)); } return; }
// QUESTION: Why are we using managed strings here? It makes no sense, especially since we have expectations as to where they point. // QUESTION: And why even have message? It seems like we could make do with just having part. size_t mail_discover_insertion_point(stringer_t *message, stringer_t *part, int_t type) { chr_t *stream; size_t length; stringer_t *tag; // If the message is not HTML, return the end of the part as the insertion point. if (type != MESSAGE_TYPE_HTML) { return st_char_get(part) - st_char_get(message) + st_length_get(part); } // Get setup. length = st_length_get(part); stream = st_data_get(part) + length - 1; while (length) { // Reverse until we find a character that is not whitespace. while (length && (*stream == ' ' || *stream == '\r' || *stream == '\n' || *stream == '\t')) { length--; stream--; } if (!(tag = mail_extract_tag(stream, length))) { return (st_char_get(part) - st_char_get(message)) + length; } // What tag is it? if (st_cmp_ci_eq(tag, PLACER("</body>", 7)) != 0 && st_cmp_ci_eq(tag, PLACER("</html>", 7)) != 0) { st_free(tag); return (st_char_get(part) - st_char_get(message)) + length; } st_free(tag); // Advance past this tag. while (length && *stream != '<') { length--; stream--; } if (length && *stream == '<') { length--; stream--; } } return (st_char_get(part) - st_char_get(message)) + length; }
placer_t imap_parse_address_breaker(stringer_t *address, uint32_t part) { size_t length; int_t status = 0; uint32_t position = 0; placer_t output = pl_null(); chr_t *holder, *start = NULL, *end = NULL; if (address == NULL) { return pl_null(); } length = st_length_get(address); holder = st_char_get(address); while (length > 0 && end == NULL) { if (position == part) { start = holder; position++; } // Inside quotes, nothing counts. if (status == 0 && *holder == '\"') { status = 1; } else if (status == 1 && *holder == '\"') { status = 0; } // Outside quotes, and we hit the break character. else if (status == 0 && position > part && *holder == ',') { end = holder; } else if (status == 0 && position < part && *holder == ',') { position++; } length--; holder++; } // If we hit the end. if (end == NULL) { end = holder; } if (start != NULL && end != NULL) { output = pl_init(start, end - start); } return output; }
// QUESTION: Should this function always return 1?? int_t mail_mime_update(mail_message_t *message) { placer_t part; if (message->mime) { mail_mime_free(message->mime); } part = pl_init(st_char_get(message->text), st_length_get(message->text)); message->mime = mail_mime_part(&part, 1); return 1; }
/** * @brief Get a specified chunk (mime part) of a multipart mime message. * @param message a managed string containing the mime message to be parsed. * @param boundary a managed string containing the boundary used to split the multipart mime message. * @param chunk the one-index based chunk to be retrieved from the multipart message * @return NULL on failure or a placer containing the specified chunk on success. */ stringer_t * mail_get_chunk(stringer_t *message, stringer_t *boundary, int_t chunk) { int_t found = 0; stringer_t *result; size_t start = 0, length = 0, input = 0; while (chunk != 0) { // So on repeats we don't have to start all over again. if (length != 0) { start += length - 1; } found = 0; while (found == 0) { // Get the start of the MIME message part. if (!st_search_cs(PLACER(st_char_get(message) + start, st_length_get(message) - start), boundary, &input)) { log_pedantic("The boundary doesn't appear to be part of this message."); return NULL; } // Skip the boundary before searching again. start += input + st_length_get(boundary); // This will detect the section ending. if (st_length_get(message) - start >= 2 && mm_cmp_cs_eq(st_char_get(message) + start, "--", 2) == 1) { return NULL; } // Some broken implementations use similar boundaries. This should detect those. else if (st_length_get(message) - start > 0 && (*(st_char_get(message) + start) < '!' || *(st_char_get(message) + start) > '~')) { found = 1; } } found = 0; while (found == 0) { // Get the end. if (!st_search_cs(PLACER(st_char_get(message) + start, st_length_get(message) - start), boundary, &length)) { length = st_length_get(message) - start; found = 1; } else if (st_length_get(message) - start - length > 0 && (*(st_char_get(message) + start) < '!' || *(st_char_get(message) + start) > '~')) { found = 1; } } chunk--; } // Setup a placer with the chunk. result = PLACER(st_char_get(message) + start, length); return result; }
/** * @brief Serialize the contents of a managed string into another managed string. * @note This function will reallocate the output managed string if necessary, and append the serialized data, updating the length field to reflect the changes. * @param data a pointer to the address of a managed string to receive the output, which will be allocated for the caller if NULL is passed. * @param string a pointer to a managed string containing the data to be serialized. * @return true if the specified value was serialized and stored successfully, or false on failure. */ bool_t serialize_st(stringer_t **data, stringer_t *string) { size_t length; stringer_t *holder = NULL; // Make sure we have room to write the number. if (!data) { return false; } // Check for NULLs. if (!string || !(length = st_length_get(string))) { if (!serialize_sz(data, 0)) { return false; } return true; } // Record the length. else if (!serialize_sz(data, length)) { return false; } // Previous line ensures that *data should never be NULL. /*else if (!*data && (*data = st_alloc(length + (length % 1024))) == NULL) { return false; }*/ else if (st_avail_get(*data) - st_length_get(*data) < length && (holder = st_realloc(*data, st_avail_get(*data) + length + (length % 1024))) == NULL) { return false; } else if (holder) { *data = holder; } mm_copy(st_char_get(*data) + st_length_get(*data), st_char_get(string), length); *(st_char_get(*data) + st_length_get(*data) + length) = ';'; st_length_set(*data, st_length_get(*data) + length + 1); return true; }
END_TEST START_TEST (check_mail_headers_s) { log_disable(); bool_t result = true; stringer_t *errmsg = MANAGEDBUF(1024); if (status()) result = check_mail_headers_sthread(errmsg); log_test("MAIL / HEADERS / SINGLE THREADED:", errmsg); ck_assert_msg(result, st_char_get(errmsg)); }
/** * @brief Update a specified contact detail in the database, or insert it if it does not exist. * @param contactnum the numerical id of the contact entry to be modified. * @param key a managed string containing the name of the contact detail to be updated. * @param value a managed string containing the new value of the specified contact detail. * @param flags a bitmask of flags to be associated with the specified contact entry detail. * @return -1 on error, 0 if no update was necessary, 1 if a new contact detail was inserted into the database, or 2 if the * specified contact detail was updated successfully. */ int_t contact_detail_upsert(uint64_t contactnum, stringer_t *key, stringer_t *value, uint64_t flags) { int64_t affected; MYSQL_BIND parameters[4]; mm_wipe(parameters, sizeof(parameters)); // Contact Number parameters[0].buffer_type = MYSQL_TYPE_LONGLONG; parameters[0].buffer_length = sizeof(uint64_t); parameters[0].buffer = &contactnum; parameters[0].is_unsigned = true; // Key parameters[1].buffer_type = MYSQL_TYPE_STRING; parameters[1].buffer_length = st_length_get(key); parameters[1].buffer = st_char_get(key); // Value parameters[2].buffer_type = MYSQL_TYPE_STRING; parameters[2].buffer_length = st_length_get(value); parameters[2].buffer = st_char_get(value); // Flags parameters[3].buffer_type = MYSQL_TYPE_LONGLONG; parameters[3].buffer_length = sizeof(uint64_t); parameters[3].buffer = &flags; parameters[3].is_unsigned = true; if ((affected = stmt_exec_affected(stmts.upsert_contact_detail, parameters)) == -1) { log_pedantic("The contact detail upsert triggered an error. { contact = %lu / key = %.*s }", contactnum, st_length_int(key), st_char_get(key)); return -1; } log_check(affected > 2); return (int_t)affected; }
END_TEST START_TEST (check_mail_store_s) { log_disable(); bool_t result = true; stringer_t *errmsg = MANAGEDBUF(1024); if (status()) result = check_mail_store_plaintext_sthread(errmsg); if (status() && result) result = check_mail_store_encrypted_sthread(errmsg); log_test("MAIL / STORE / SINGLE THREADED:", errmsg); ck_assert_msg(result, st_char_get(errmsg)); }