--- spamc/libspamc.h (revision 158731) +++ spamc/libspamc.h (working copy) @@ -139,8 +139,6 @@ int out_len; /* Output from spamd. Either the filtered message, or the check-only response. Or else, a pointer to msg above. */ - int is_learned; /* Output from spamd. Gives state - about learn resp. unlearn process */ /* these members added in SpamAssassin version 2.60: */ struct libspamc_private_message *priv; @@ -210,9 +208,17 @@ * failover, more than one host is defined, but if there is only one there, * no failover is done. */ -int message_filter(struct transport *tp, const char *username, int learntype, +int message_filter(struct transport *tp, const char *username, int flags, struct message *m); +/* Process the message through the spamd learn, making as many connection + * attempts as are implied by the transport structure. To make this do + * failover, more than one host is defined, but if there is only one there, + * no failover is done. + */ +int message_learn(struct transport *tp, const char *username, int flags, + struct message *m, int learntype, int *islearned); + /* Dump the message. If there is any data in the message (typically, m->type * will be MESSAGE_ERROR) it will be message_writed. Then, fd_in will be piped * to fd_out intol EOF. This is particularly useful if you get back an @@ -222,7 +228,7 @@ /* Do a message_read->message_filter->message_write sequence, handling errors * appropriately with dump_message or appropriate CHECK_ONLY output. Returns * EX_OK or EX_ISSPAM/EX_NOTSPAM on success, some error EX on error. */ -int message_process(struct transport *trans, char *username, int learntype, int max_size, +int message_process(struct transport *trans, char *username, int max_size, int in_fd, int out_fd, const int flags); /* Cleanup the resources we allocated for storing the message. Call after @@ -230,7 +236,7 @@ void message_cleanup(struct message *m); /* Aug 14, 2002 bj: This is now legacy, don't use it. */ -int process_message(struct transport *tp, char *username, int learntype, +int process_message(struct transport *tp, char *username, int max_size, int in_fd, int out_fd, const int check_only, const int safe_fallback); --- spamc/spamc.c (revision 158731) +++ spamc/spamc.c (working copy) @@ -501,6 +501,7 @@ int result; int ret; int learntype = 0; + int islearned = 0; transport_init(&trans); @@ -560,7 +561,12 @@ if (ret == EX_OK) { - ret = message_filter(&trans, username, learntype, flags, &m); + if (flags & SPAMC_LEARN) { + ret = message_learn(&trans, username, flags, &m, learntype, &islearned); + } + else { + ret = message_filter(&trans, username, flags, &m); + } free(username); username = NULL; @@ -569,7 +575,7 @@ get_output_fd(&out_fd); if (flags & SPAMC_LEARN) { - if (m.is_learned == 1) { + if (islearned == 1) { printf("Message successfully un/learned\n"); } else { --- spamc/libspamc.c (revision 158731) +++ spamc/libspamc.c (working copy) @@ -765,7 +765,8 @@ } static int -_handle_spamd_header(struct message *m, int flags, char *buf, int len) +_handle_spamd_header(struct message *m, int flags, char *buf, int len, + int *islearned) { char is_spam[6]; char s_str[21], t_str[21]; @@ -818,10 +819,10 @@ } else if (sscanf(buf, "Learned: %3s", is_learned) == 1) { if(strcmp(is_learned, "yes") == 0 || strcmp(is_learned, "Yes") == 0) { - m->is_learned = 1; + *islearned = 1; } else if(strcmp(is_learned, "no") == 0 || strcmp(is_learned, "No") == 0) { - m->is_learned = 0; + *islearned = 0; } else { libspamc_log(flags, LOG_ERR, "spamd responded with bad Learned-state '%s'", @@ -835,8 +836,8 @@ return EX_PROTOCOL; } -int message_filter(struct transport *tp, const char *username, int learntype, - int flags, struct message *m) +int message_filter(struct transport *tp, const char *username, + int flags, struct message *m) { char buf[8192]; size_t bufsiz = (sizeof(buf) / sizeof(*buf)) - 4; /* bit of breathing room */ @@ -844,7 +845,6 @@ int sock = -1; int rc; char versbuf[20]; - char strlearntype[1]; float version; int response; int failureval; @@ -876,7 +876,7 @@ m->out_len = 0; /* Build spamd protocol header */ - if(flags & SPAMC_CHECK_ONLY) + if (flags & SPAMC_CHECK_ONLY) strcpy(buf, "CHECK "); else if (flags & SPAMC_REPORT_IFSPAM) strcpy(buf, "REPORT_IFSPAM "); @@ -884,8 +884,6 @@ strcpy(buf, "REPORT "); else if (flags & SPAMC_SYMBOLS) strcpy(buf, "SYMBOLS "); - else if (flags & SPAMC_LEARN ) - strcpy(buf, "LEARN "); else strcpy(buf, "PROCESS "); @@ -899,21 +897,6 @@ strcat(buf, "\r\n"); len = strlen(buf); - - if (flags & SPAMC_LEARN) { - if ((learntype > 2) | (learntype < 0 )) { - free(m->out); - m->out = m->msg; - m->out_len = m->msg_len; - return EX_OSERR; - } - sprintf(strlearntype,"%d",learntype); - strcpy(buf + len, "Learn-type: "); - strcat(buf + len, strlearntype); - strcat(buf + len, "\r\n"); - len += strlen(buf + len); - } - if (username != NULL) { if (strlen(username) + 8 >= (bufsiz - len)) { _use_msg_for_out(m); @@ -988,7 +971,6 @@ m->score = 0; m->threshold = 0; m->is_spam = EX_TOOBIG; - m->is_learned = 0; while (1) { failureval = _spamc_read_full_line(m, flags, ssl, sock, buf, &len, bufsiz); @@ -999,8 +981,8 @@ if (len == 0 && buf[0] == '\0') { break; /* end of headers */ } - - if (_handle_spamd_header(m, flags, buf, len) < 0) { + int throwaway; + if (_handle_spamd_header(m, flags, buf, len, &throwaway) < 0) { failureval = EX_PROTOCOL; goto failure; } @@ -1018,12 +1000,6 @@ } return EX_OK; } - else if (flags & SPAMC_LEARN) { - shutdown(sock, SHUT_RD); - closesocket(sock); - sock = -1; - return EX_OK; - } else { if (m->content_length < 0) { /* should have got a length too. */ @@ -1093,7 +1069,7 @@ } -int message_process(struct transport *trans, char *username, int learntype, int max_size, +int message_process(struct transport *trans, char *username, int max_size, int in_fd, int out_fd, const int flags) { int ret; @@ -1105,7 +1081,7 @@ ret = message_read(in_fd, flags, &m); if (ret != EX_OK) goto FAIL; - ret = message_filter(trans, username, learntype, flags, &m); + ret = message_filter(trans, username, flags, &m); if (ret != EX_OK) goto FAIL; if (message_write(out_fd, &m) < 0) @@ -1130,7 +1106,198 @@ } } +int message_learn(struct transport *tp, const char *username, int flags, + struct message *m, int learntype, int *islearned) +{ + char buf[8192]; + size_t bufsiz = (sizeof(buf) / sizeof(*buf)) - 4; /* bit of breathing room */ + size_t len; + int sock = -1; + int rc; + char versbuf[20]; + char strlearntype[1]; + float version; + int response; + int failureval; + SSL_CTX *ctx = NULL; + SSL *ssl = NULL; + SSL_METHOD *meth; + if (flags & SPAMC_USE_SSL) { +#ifdef SPAMC_SSL + SSLeay_add_ssl_algorithms(); + meth = SSLv2_client_method(); + SSL_load_error_strings(); + ctx = SSL_CTX_new(meth); +#else + UNUSED_VARIABLE(ssl); + UNUSED_VARIABLE(meth); + UNUSED_VARIABLE(ctx); + libspamc_log(flags, LOG_ERR, "spamc not built with SSL support"); + return EX_SOFTWARE; +#endif + } + + m->is_spam = EX_TOOBIG; + if ((m->outbuf = malloc(m->max_len + EXPANSION_ALLOWANCE + 1)) == NULL) { + failureval = EX_OSERR; + goto failure; + } + m->out = m->outbuf; + m->out_len = 0; + + /* Build spamd protocol header */ + strcpy(buf, "LEARN "); + + len = strlen(buf); + if (len + strlen(PROTOCOL_VERSION) + 2 >= bufsiz) { + _use_msg_for_out(m); + return EX_OSERR; + } + + strcat(buf, PROTOCOL_VERSION); + strcat(buf, "\r\n"); + len = strlen(buf); + + if ((learntype > 2) | (learntype < 0 )) { + free(m->out); + m->out = m->msg; + m->out_len = m->msg_len; + return EX_OSERR; + } + sprintf(strlearntype,"%d",learntype); + strcpy(buf + len, "Learn-type: "); + strcat(buf + len, strlearntype); + strcat(buf + len, "\r\n"); + len += strlen(buf + len); + + if (username != NULL) { + if (strlen(username) + 8 >= (bufsiz - len)) { + _use_msg_for_out(m); + return EX_OSERR; + } + strcpy(buf + len, "User: "); + strcat(buf + len, username); + strcat(buf + len, "\r\n"); + len += strlen(buf + len); + } + if ((m->msg_len > 9999999) || ((len + 27) >= (bufsiz - len))) { + _use_msg_for_out(m); + return EX_OSERR; + } + len += sprintf(buf + len, "Content-length: %d\r\n\r\n", m->msg_len); + + libspamc_timeout = m->timeout; + + if (tp->socketpath) + rc = _try_to_connect_unix(tp, &sock); + else + rc = _try_to_connect_tcp(tp, &sock); + + if (rc != EX_OK) { + _use_msg_for_out(m); + return rc; /* use the error code try_to_connect_*() gave us. */ + } + + if (flags & SPAMC_USE_SSL) { +#ifdef SPAMC_SSL + ssl = SSL_new(ctx); + SSL_set_fd(ssl, sock); + SSL_connect(ssl); +#endif + } + + /* Send to spamd */ + if (flags & SPAMC_USE_SSL) { +#ifdef SPAMC_SSL + SSL_write(ssl, buf, len); + SSL_write(ssl, m->msg, m->msg_len); +#endif + } + else { + full_write(sock, 0, buf, len); + full_write(sock, 0, m->msg, m->msg_len); + shutdown(sock, SHUT_WR); + } + + /* ok, now read and parse it. SPAMD/1.2 line first... */ + failureval = + _spamc_read_full_line(m, flags, ssl, sock, buf, &len, bufsiz); + if (failureval != EX_OK) { + goto failure; + } + + if (sscanf(buf, "SPAMD/%18s %d %*s", versbuf, &response) != 2) { + libspamc_log(flags, LOG_ERR, "spamd responded with bad string '%s'", buf); + failureval = EX_PROTOCOL; + goto failure; + } + + versbuf[19] = '\0'; + version = _locale_safe_string_to_float(versbuf, 20); + if (version < 1.0) { + libspamc_log(flags, LOG_ERR, "spamd responded with bad version string '%s'", + versbuf); + failureval = EX_PROTOCOL; + goto failure; + } + + m->score = 0; + m->threshold = 0; + m->is_spam = EX_TOOBIG; + *islearned = 0; + while (1) { + failureval = + _spamc_read_full_line(m, flags, ssl, sock, buf, &len, bufsiz); + if (failureval != EX_OK) { + goto failure; + } + + if (len == 0 && buf[0] == '\0') { + break; /* end of headers */ + } + + if (_handle_spamd_header(m, flags, buf, len, islearned) < 0) { + failureval = EX_PROTOCOL; + goto failure; + } + } + + len = 0; /* overwrite those headers */ + + shutdown(sock, SHUT_RD); + closesocket(sock); + sock = -1; + return EX_OK; + + libspamc_timeout = 0; + + if (m->out_len != m->content_length) { + libspamc_log(flags, LOG_ERR, + "failed sanity check, %d bytes claimed, %d bytes seen", + m->content_length, m->out_len); + failureval = EX_PROTOCOL; + goto failure; + } + + return EX_OK; + + failure: + _use_msg_for_out(m); + if (sock != -1) { + closesocket(sock); + } + libspamc_timeout = 0; + + if (flags & SPAMC_USE_SSL) { +#ifdef SPAMC_SSL + SSL_free(ssl); + SSL_CTX_free(ctx); +#endif + } + return failureval; +} + void message_cleanup(struct message *m) { if (m->outbuf) @@ -1143,9 +1310,9 @@ } /* Aug 14, 2002 bj: Obsolete! */ -int process_message(struct transport *tp, char *username, int learntype, - int max_size, int in_fd, int out_fd, - const int my_check_only, const int my_safe_fallback) +int process_message(struct transport *tp, char *username, int max_size, + int in_fd, int out_fd, const int my_check_only, + const int my_safe_fallback) { int flags; @@ -1155,7 +1322,7 @@ if (my_safe_fallback) flags |= SPAMC_SAFE_FALLBACK; - return message_process(tp, username, learntype, max_size, in_fd, out_fd, flags); + return message_process(tp, username, max_size, in_fd, out_fd, flags); } /*