int message_tell()

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;
}