/** * @brief Update a mail message's parent folder in the database. * @brief usernum the numerical id of the user to whom the mail message belongs. * @brief messagenum the numerical id of the target mail message of the operation. * @brief source the numerical id of the parent folder in which the mail message currently resides. * @brief target the numerical id of the destination folder which is to be the new parent of the mail message. * @brief transaction a transaction id for the database operation, in case the caller needs to roll back changes on failure. * @return -1 on failure, 0 if the target message could not be located in the database, or 1 on success. */ int_t mail_db_update_message_folder(uint64_t usernum, uint64_t messagenum, uint64_t source, uint64_t target, int64_t transaction) { uint64_t result; MYSQL_BIND parameters[4]; if (!usernum || !messagenum || !source || !target || transaction < 0) { log_pedantic("Passed an invalid message parameter."); return -1; } mm_wipe(parameters, sizeof(parameters)); // Target Folder parameters[0].buffer_type = MYSQL_TYPE_LONGLONG; parameters[0].buffer_length = sizeof(uint64_t); parameters[0].buffer = ⌖ parameters[0].is_unsigned = true; // Messagenum parameters[1].buffer_type = MYSQL_TYPE_LONGLONG; parameters[1].buffer_length = sizeof(uint64_t); parameters[1].buffer = &messagenum; parameters[1].is_unsigned = true; // Usernum parameters[2].buffer_type = MYSQL_TYPE_LONGLONG; parameters[2].buffer_length = sizeof(uint64_t); parameters[2].buffer = &usernum; parameters[2].is_unsigned = true; // Source Folder parameters[3].buffer_type = MYSQL_TYPE_LONGLONG; parameters[3].buffer_length = sizeof(uint64_t); parameters[3].buffer = &source; parameters[3].is_unsigned = true; // Since the result is unsigned, an error is indicated by a return value of (my_ulonglong)-1. if ((result = stmt_exec_affected_conn(stmts.update_message_folder, parameters, transaction)) != 1 && result == (my_ulonglong)-1) { log_pedantic("An error occurred while trying to move a message into a different folder. { user = %lu / message = %lu / source = %lu / " "target = %lu / query = -1 / error = %u = %s }", usernum, messagenum, source, target, mysql_stmt_errno_d(pool_get_obj(sql_pool, transaction)), mysql_stmt_error_d(pool_get_obj(sql_pool, transaction))); return -1; } #ifdef MAGMA_PEDANTIC else if (result > 1) { log_pedantic("The message folder update affected more than one database row. Since we supplied the primary key this should never happen! " "{ user = %lu / message = %lu / source = %lu / target = %lu }", usernum, messagenum, source, target); return -1; } #endif // If the message couldn't be located we return zero. if (!result) { return 0; } return 1; }
/// 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; }
/** * @note The disposition parameter marks whether or not the signature is currently marked as junk. * So training the signature means that it will toggle its status. * @param usernum the numerical id of the user making the spam training request. * @param disposition if 0, dspam will mark the signature as junk; otherwise, mark as OK. * @param signature a managed string containing the spam signature to be trained. * @return true on success or false on failure. */ bool_t dspam_train(uint64_t usernum, int_t disposition, stringer_t *signature) { int_t ret; DSPAM_CTX *ctx; chr_t unum[20]; stringer_t *tmpdir; uint32_t connection; struct _mysql_drv_dbh dbh; struct _ds_spam_signature sig; // 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 false; } // 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 false; } st_free(tmpdir); // Get a DB connection. if (pool_pull(sql_pool, &connection) != PL_RESERVED) { log_info("Unable to get an available connection for the query."); dspam_destroy_d(ctx); return false; } 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 false; } // 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 false; } // Tokenization method and statistical algorithm. ctx->algorithms = DSA_GRAHAM | DSA_BURTON | DSP_GRAHAM; ctx->tokenizer = DSZ_CHAIN; // Setup the classification as opposite of what the original was. ctx->classification = disposition ? DSR_ISINNOCENT : DSR_ISSPAM; // Set up the context for error correction. ctx->source = DSS_ERROR; // Setup the signature. sig.length = st_length_get(signature); sig.data = st_char_get(signature); ctx->signature = &sig; // Call DSPAM, and then destroy the context. ret = dspam_process_d(ctx, NULL); dspam_detach_d(ctx); dspam_destroy_d(ctx); pool_release(sql_pool, connection); if (ret) { log_pedantic("An error occurred while training message signature. {dspam_process = %i}", ret); return false; } return true; }