in spamc/libspamc.c [1711:1953]
int message_tell(struct transport *tp, const char *username, int flags,
struct message *m, int msg_class,
unsigned int tellflags, unsigned int *didtellflags)
{
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];
float version;
int response;
int failureval;
SSL_CTX *ctx = NULL;
SSL *ssl = NULL;
assert(tp != NULL);
assert(m != NULL);
if (flags & SPAMC_USE_SSL) {
#ifdef SPAMC_SSL
ctx = _try_ssl_ctx_init(flags);
if (ctx == NULL) {
failureval = EX_OSERR;
goto failure;
}
#else
UNUSED_VARIABLE(ssl);
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 != NULL)
free(m->outbuf);
m->priv->alloced_size = m->max_len + EXPANSION_ALLOWANCE + 1;
if ((m->outbuf = malloc(m->priv->alloced_size)) == NULL) {
failureval = EX_OSERR;
goto failure;
}
m->out = m->outbuf;
m->out_len = 0;
/* Build spamd protocol header */
strcpy(buf, "TELL ");
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 (msg_class != 0) {
strcpy(buf + len, "Message-class: ");
if (msg_class == SPAMC_MESSAGE_CLASS_SPAM) {
strcat(buf + len, "spam\r\n");
}
else {
strcat(buf + len, "ham\r\n");
}
len += strlen(buf + len);
}
if ((tellflags & SPAMC_SET_LOCAL) || (tellflags & SPAMC_SET_REMOTE)) {
int needs_comma_p = 0;
strcat(buf + len, "Set: ");
if (tellflags & SPAMC_SET_LOCAL) {
strcat(buf + len, "local");
needs_comma_p = 1;
}
if (tellflags & SPAMC_SET_REMOTE) {
if (needs_comma_p == 1) {
strcat(buf + len, ",");
}
strcat(buf + len, "remote");
}
strcat(buf + len, "\r\n");
len += strlen(buf + len);
}
if ((tellflags & SPAMC_REMOVE_LOCAL) || (tellflags & SPAMC_REMOVE_REMOTE)) {
int needs_comma_p = 0;
strcat(buf + len, "Remove: ");
if (tellflags & SPAMC_REMOVE_LOCAL) {
strcat(buf + len, "local");
needs_comma_p = 1;
}
if (tellflags & SPAMC_REMOVE_REMOTE) {
if (needs_comma_p == 1) {
strcat(buf + len, ",");
}
strcat(buf + len, "remote");
}
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 > SPAMC_MAX_MESSAGE_LEN) || ((len + 27) >= (bufsiz - len))) {
_use_msg_for_out(m);
return EX_DATAERR;
}
len += sprintf(buf + len, "Content-length: %d\r\n\r\n", (int) m->msg_len);
if (m->priv->spamc_header_callback != NULL) {
char buf2[1024];
m->priv->spamc_header_callback(m, flags, buf2, 1024);
strncat(buf, buf2, bufsiz - len);
}
libspamc_timeout = m->timeout;
libspamc_connect_timeout = m->connect_timeout; /* Sep 8, 2008 mrgus: separate connect 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
rc = _try_ssl_connect(ctx, tp, &ssl, flags, sock);
if (rc != EX_OK) {
failureval = rc;
goto failure;
}
#endif
}
/* Send to spamd */
if (flags & SPAMC_USE_SSL) {
#ifdef SPAMC_SSL
rc = SSL_write(ssl, buf, len);
if (rc <= 0) {
libspamc_log(flags, LOG_ERR, "SSL write failed (%d)",
SSL_get_error(ssl, rc));
failureval = EX_IOERR;
goto failure;
}
rc = SSL_write(ssl, m->msg, m->msg_len);
if (rc <= 0) {
libspamc_log(flags, LOG_ERR, "SSL write failed (%d)",
SSL_get_error(ssl, rc));
failureval = EX_IOERR;
goto failure;
}
SSL_shutdown(ssl);
shutdown(sock, SHUT_WR);
#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;
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, didtellflags) < 0) {
failureval = EX_PROTOCOL;
goto failure;
}
}
len = 0; /* overwrite those headers */
shutdown(sock, SHUT_RD);
closesocket(sock);
sock = -1;
libspamc_timeout = 0;
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;
}