Beispiel #1
0
static void qmqpd_write_content(QMQPD_STATE *state)
{
    char   *start;
    char   *next;
    int     len;
    int     rec_type;
    int     first = 1;
    int     ch;

    /*
     * Start the message content segment. Prepend our own Received: header to
     * the message content. List the recipient only when a message has one
     * recipient. Otherwise, don't list the recipient to avoid revealing Bcc:
     * recipients that are supposed to be invisible.
     */
    rec_fputs(state->cleanup, REC_TYPE_MESG, "");
    rec_fprintf(state->cleanup, REC_TYPE_NORM, "Received: from %s (%s [%s])",
		state->name, state->name, state->rfc_addr);
    if (state->rcpt_count == 1 && state->recipient) {
	rec_fprintf(state->cleanup, REC_TYPE_NORM,
		    "\tby %s (%s) with %s id %s",
		    var_myhostname, var_mail_name,
		    state->protocol, state->queue_id);
	quote_822_local(state->buf, state->recipient);
	rec_fprintf(state->cleanup, REC_TYPE_NORM,
		    "\tfor <%s>; %s", STR(state->buf),
		    mail_date(state->arrival_time.tv_sec));
    } else {
	rec_fprintf(state->cleanup, REC_TYPE_NORM,
		    "\tby %s (%s) with %s",
		    var_myhostname, var_mail_name, state->protocol);
	rec_fprintf(state->cleanup, REC_TYPE_NORM,
		    "\tid %s; %s", state->queue_id,
		    mail_date(state->arrival_time.tv_sec));
    }
#ifdef RECEIVED_ENVELOPE_FROM
    quote_822_local(state->buf, state->sender);
    rec_fprintf(state->cleanup, REC_TYPE_NORM,
		"\t(envelope-from <%s>)", STR(state->buf));
#endif

    /*
     * Write the message content.
     * 
     * XXX Force an empty record when the queue file content begins with
     * whitespace, so that it won't be considered as being part of our own
     * Received: header. What an ugly Kluge.
     * 
     * XXX Deal with UNIX-style From_ lines at the start of message content just
     * in case.
     */
    for (next = STR(state->message); /* void */ ; /* void */ ) {
	if ((ch = qmqpd_next_line(state->message, &start, &len, &next)) < 0)
	    break;
	if (ch == '\n')
	    rec_type = REC_TYPE_NORM;
	else
	    rec_type = REC_TYPE_CONT;
	if (first) {
	    if (strncmp(start + strspn(start, ">"), "From ", 5) == 0) {
		rec_fprintf(state->cleanup, rec_type,
			    "X-Mailbox-Line: %.*s", len, start);
		continue;
	    }
	    first = 0;
	    if (len > 0 && IS_SPACE_TAB(start[0]))
		rec_put(state->cleanup, REC_TYPE_NORM, "", 0);
	}
	if (rec_put(state->cleanup, rec_type, start, len) < 0) {
	    state->err = CLEANUP_STAT_WRITE;
	    return;
	}
    }
}
Beispiel #2
0
Variant f_imap_headerinfo(const Resource& imap_stream, int64_t msg_number,
                          int64_t fromlength /* = 0 */,
                          int64_t subjectlength /* = 0 */,
                          const String& defaulthost /* = "" */) {
  ImapStream *obj = imap_stream.getTyped<ImapStream>();
  if (fromlength < 0 || fromlength > MAILTMPLEN) {
    Logger::Warning("From length has to be between 0 and %d", MAILTMPLEN);
    return false;
  }
  if (subjectlength < 0 || subjectlength > MAILTMPLEN) {
    Logger::Warning("Subject length has to be between 0 and %d", MAILTMPLEN);
    return false;
  }
  if (!obj->checkMsgNumber(msg_number)) {
    return false;
  }
  if (!mail_fetchstructure(obj->m_stream, msg_number, NIL)) {
    return false;
  }

  MESSAGECACHE *cache = mail_elt(obj->m_stream, msg_number);
  ENVELOPE *en = mail_fetchenvelope(obj->m_stream, msg_number);

  /* call a function to parse all the text, so that we can use the
     same function to parse text from other sources */
  Object ret = _php_make_header_object(en);

  /* now run through properties that are only going to be returned
     from a server, not text headers */
  ret.o_set("Recent",   cache->recent ? (cache->seen ? "R": "N") : " ");
  ret.o_set("Unseen",   (cache->recent | cache->seen) ? " " : "U");
  ret.o_set("Flagged",  cache->flagged  ? "F" : " ");
  ret.o_set("Answered", cache->answered ? "A" : " ");
  ret.o_set("Deleted",  cache->deleted  ? "D" : " ");
  ret.o_set("Draft",    cache->draft    ? "X" : " ");

  char dummy[2000], fulladdress[MAILTMPLEN + 1];
  snprintf(dummy, sizeof(dummy), "%4ld", cache->msgno);
  ret.o_set("Msgno", String(dummy, CopyString));

  mail_date(dummy, cache);
  ret.o_set("MailDate", String(dummy, CopyString));

  snprintf(dummy, sizeof(dummy), "%ld", cache->rfc822_size);
  ret.o_set("Size", String(dummy, CopyString));

  ret.o_set("udate", (int64_t)mail_longdate(cache));

  if (en->from && fromlength) {
    fulladdress[0] = 0x00;
    mail_fetchfrom(fulladdress, obj->m_stream, msg_number, fromlength);
    ret.o_set("fetchfrom", String(fulladdress, CopyString));
  }
  if (en->subject && subjectlength) {
    fulladdress[0] = 0x00;
    mail_fetchsubject(fulladdress, obj->m_stream, msg_number, subjectlength);
    ret.o_set("fetchsubject", String(fulladdress, CopyString));
  }

  return ret;
}
Beispiel #3
0
static int pickup_copy(VSTREAM *qfile, VSTREAM *cleanup,
		               PICKUP_INFO *info, VSTRING *buf)
{
    time_t  now = time((time_t *) 0);
    int     status;
    char   *name;

    /*
     * Protect against time-warped time stamps. Warn about mail that has been
     * queued for an excessive amount of time. Allow for some time drift with
     * network clients that mount the maildrop remotely - especially clients
     * that can't get their daylight savings offsets right.
     */
#define DAY_SECONDS 86400
#define HOUR_SECONDS 3600

    if (info->st.st_mtime > now + 2 * HOUR_SECONDS) {
	msg_warn("%s: message dated %ld seconds into the future",
		 info->id, (long) (info->st.st_mtime - now));
	info->st.st_mtime = now;
    } else if (info->st.st_mtime < now - DAY_SECONDS) {
	msg_warn("%s: message has been queued for %d days",
		 info->id, (int) ((now - info->st.st_mtime) / DAY_SECONDS));
    }

    /*
     * Add content inspection transport. See also postsuper(1).
     */
    if (*var_filter_xport)
	rec_fprintf(cleanup, REC_TYPE_FILT, "%s", var_filter_xport);

    /*
     * Copy the message envelope segment. Allow only those records that we
     * expect to see in the envelope section. The envelope segment must
     * contain an envelope sender address.
     */
    if ((status = copy_segment(qfile, cleanup, info, buf, REC_TYPE_ENVELOPE)) != 0)
	return (status);
    if (info->sender == 0) {
	msg_warn("%s: uid=%ld: no envelope sender",
		 info->id, (long) info->st.st_uid);
	return (REMOVE_MESSAGE_FILE);
    }

    /*
     * For messages belonging to $mail_owner also log the maildrop queue id.
     * This supports message tracking for mail requeued via "postsuper -r".
     */
#define MAIL_IS_REQUEUED(info) \
    ((info)->st.st_uid == var_owner_uid && ((info)->st.st_mode & S_IROTH) == 0)

    if (MAIL_IS_REQUEUED(info)) {
	msg_info("%s: uid=%d from=<%s> orig_id=%s", info->id,
		 (int) info->st.st_uid, info->sender,
		 ((name = strrchr(info->path, '/')) != 0 ?
		  name + 1 : info->path));
    } else {
	msg_info("%s: uid=%d from=<%s>", info->id,
		 (int) info->st.st_uid, info->sender);
    }

    /*
     * Message content segment. Send a dummy message length. Prepend a
     * Received: header to the message contents. For tracing purposes,
     * include the message file ownership, without revealing the login name.
     */
    rec_fputs(cleanup, REC_TYPE_MESG, "");
    rec_fprintf(cleanup, REC_TYPE_NORM, "Received: by %s (%s, from userid %ld)",
		var_myhostname, var_mail_name, (long) info->st.st_uid);
    rec_fprintf(cleanup, REC_TYPE_NORM, "\tid %s; %s", info->id,
		mail_date(info->st.st_mtime));

    /*
     * Copy the message content segment. Allow only those records that we
     * expect to see in the message content section.
     */
    if ((status = copy_segment(qfile, cleanup, info, buf, REC_TYPE_CONTENT)) != 0)
	return (status);

    /*
     * Send the segment with information extracted from message headers.
     * Permit a non-empty extracted segment, so that list manager software
     * can to output recipients after the message, and so that sysadmins can
     * re-inject messages after a change of configuration.
     */
    rec_fputs(cleanup, REC_TYPE_XTRA, "");
    if ((status = copy_segment(qfile, cleanup, info, buf, REC_TYPE_EXTRACT)) != 0)
	return (status);

    /*
     * There are no errors. Send the end-of-data marker, and get the cleanup
     * service completion status. XXX Since the pickup service is unable to
     * bounce, the cleanup service can report only soft errors here.
     */
    rec_fputs(cleanup, REC_TYPE_END, "");
    if (attr_scan(cleanup, ATTR_FLAG_MISSING,
		  RECV_ATTR_INT(MAIL_ATTR_STATUS, &status),
		  RECV_ATTR_STR(MAIL_ATTR_WHY, buf),
		  ATTR_TYPE_END) != 2)
	return (cleanup_service_error(info, CLEANUP_STAT_WRITE));

    /*
     * Depending on the cleanup service completion status, delete the message
     * file, or try again later. Bounces are dealt with by the cleanup
     * service itself. The master process wakes up the cleanup service every
     * now and then.
     */
    if (status) {
	return (cleanup_service_error_reason(info, status, vstring_str(buf)));
    } else {
	return (REMOVE_MESSAGE_FILE);
    }
}
Beispiel #4
0
int     main(void)
{
    vstream_printf("%s\n", mail_date(time((time_t *) 0)));
    vstream_fflush(VSTREAM_OUT);
    return (0);
}
Beispiel #5
0
static int forward_send(FORWARD_INFO *info, DELIVER_REQUEST *request,
			        DELIVER_ATTR attr, char *delivered)
{
    const char *myname = "forward_send";
    VSTRING *buffer = vstring_alloc(100);
    VSTRING *folded;
    int     status;
    int     rec_type = 0;

    /*
     * Start the message content segment. Prepend our Delivered-To: header to
     * the message data. Stop at the first error. XXX Rely on the front-end
     * services to enforce record size limits.
     */
    rec_fputs(info->cleanup, REC_TYPE_MESG, "");
    vstring_strcpy(buffer, delivered);
    rec_fprintf(info->cleanup, REC_TYPE_NORM, "Received: by %s (%s)",
		var_myhostname, var_mail_name);
    rec_fprintf(info->cleanup, REC_TYPE_NORM, "\tid %s; %s",
		info->queue_id, mail_date(info->posting_time.tv_sec));
    if (local_deliver_hdr_mask & DELIVER_HDR_FWD) {
	folded = vstring_alloc(100);
	rec_fprintf(info->cleanup, REC_TYPE_NORM, "Delivered-To: %s",
		    casefold(folded, (STR(buffer))));
	vstring_free(folded);
    }
    if ((status = vstream_ferror(info->cleanup)) == 0)
	if (vstream_fseek(attr.fp, attr.offset, SEEK_SET) < 0)
	    msg_fatal("%s: seek queue file %s: %m:",
		      myname, VSTREAM_PATH(attr.fp));
    while (status == 0 && (rec_type = rec_get(attr.fp, buffer, 0)) > 0) {
	if (rec_type != REC_TYPE_CONT && rec_type != REC_TYPE_NORM)
	    break;
	status = (REC_PUT_BUF(info->cleanup, rec_type, buffer) != rec_type);
    }
    if (status == 0 && rec_type != REC_TYPE_XTRA) {
	msg_warn("%s: bad record type: %d in message content",
		 info->queue_id, rec_type);
	status |= mark_corrupt(attr.fp);
    }

    /*
     * Send the end-of-data marker only when there were no errors.
     */
    if (status == 0) {
	rec_fputs(info->cleanup, REC_TYPE_XTRA, "");
	rec_fputs(info->cleanup, REC_TYPE_END, "");
    }

    /*
     * Retrieve the cleanup service completion status only if there are no
     * problems.
     */
    if (status == 0)
	if (vstream_fflush(info->cleanup)
	    || attr_scan(info->cleanup, ATTR_FLAG_MISSING,
			 RECV_ATTR_INT(MAIL_ATTR_STATUS, &status),
			 ATTR_TYPE_END) != 1)
	    status = 1;

    /*
     * Log successful forwarding.
     * 
     * XXX DSN alias and .forward expansion already report SUCCESS, so don't do
     * it again here.
     */
    if (status == 0) {
	attr.rcpt.dsn_notify =
	    (attr.rcpt.dsn_notify == DSN_NOTIFY_SUCCESS ?
	     DSN_NOTIFY_NEVER : attr.rcpt.dsn_notify & ~DSN_NOTIFY_SUCCESS);
	dsb_update(attr.why, "2.0.0", "relayed", DSB_SKIP_RMTA, DSB_SKIP_REPLY,
		   "forwarded as %s", info->queue_id);
	status = sent(BOUNCE_FLAGS(request), SENT_ATTR(attr));
    }

    /*
     * Cleanup.
     */
    vstring_free(buffer);
    return (status);
}
Beispiel #6
0
int     main(int argc, char **argv)
{
    SESSION *session;
    char   *host;
    char   *port;
    char   *path;
    int     path_len;
    int     sessions = 1;
    int     ch;
    ssize_t len;
    int     n;
    int     i;
    char   *buf;
    const char *parse_err;
    struct addrinfo *res;
    int     aierr;
    const char *protocols = INET_PROTO_NAME_ALL;
    INET_PROTO_INFO *proto_info;

    /*
     * Fingerprint executables and core dumps.
     */
    MAIL_VERSION_STAMP_ALLOCATE;

    signal(SIGPIPE, SIG_IGN);
    msg_vstream_init(argv[0], VSTREAM_ERR);

    /*
     * Parse JCL.
     */
    while ((ch = GETOPT(argc, argv, "46cC:f:l:m:M:r:R:s:t:vw:")) > 0) {
	switch (ch) {
	case '4':
	    protocols = INET_PROTO_NAME_IPV4;
	    break;
	case '6':
	    protocols = INET_PROTO_NAME_IPV6;
	    break;
	case 'c':
	    count++;
	    break;
	case 'C':
	    if ((connect_count = atoi(optarg)) <= 0)
		usage(argv[0]);
	    break;
	case 'f':
	    sender = optarg;
	    break;
	case 'l':
	    if ((message_length = atoi(optarg)) <= 0)
		usage(argv[0]);
	    break;
	case 'm':
	    if ((message_count = atoi(optarg)) <= 0)
		usage(argv[0]);
	    break;
	case 'M':
	    if (*optarg == '[') {
		if (!valid_mailhost_literal(optarg, DO_GRIPE))
		    msg_fatal("bad address literal: %s", optarg);
	    } else {
		if (!valid_hostname(optarg, DO_GRIPE))
		    msg_fatal("bad hostname: %s", optarg);
	    }
	    var_myhostname = optarg;
	    break;
	case 'r':
	    if ((recipients = atoi(optarg)) <= 0)
		usage(argv[0]);
	    break;
	case 'R':
	    if (fixed_delay > 0 || (random_delay = atoi(optarg)) <= 0)
		usage(argv[0]);
	    break;
	case 's':
	    if ((sessions = atoi(optarg)) <= 0)
		usage(argv[0]);
	    break;
	case 't':
	    recipient = optarg;
	    break;
	case 'v':
	    msg_verbose++;
	    break;
	case 'w':
	    if (random_delay > 0 || (fixed_delay = atoi(optarg)) <= 0)
		usage(argv[0]);
	    break;
	default:
	    usage(argv[0]);
	}
    }
    if (argc - optind != 1)
	usage(argv[0]);

    if (random_delay > 0)
	srand(getpid());

    /*
     * Translate endpoint address to internal form.
     */
    proto_info = inet_proto_init("protocols", protocols);
    if (strncmp(argv[optind], "unix:", 5) == 0) {
	path = argv[optind] + 5;
	path_len = strlen(path);
	if (path_len >= (int) sizeof(sun.sun_path))
	    msg_fatal("unix-domain name too long: %s", path);
	memset((char *) &sun, 0, sizeof(sun));
	sun.sun_family = AF_UNIX;
#ifdef HAS_SUN_LEN
	sun.sun_len = path_len + 1;
#endif
	memcpy(sun.sun_path, path, path_len);
	sa = (struct sockaddr *) & sun;
	sa_length = sizeof(sun);
    } else {
	if (strncmp(argv[optind], "inet:", 5) == 0)
	    argv[optind] += 5;
	buf = mystrdup(argv[optind]);
	if ((parse_err = host_port(buf, &host, (char *) 0, &port, "628")) != 0)
	    msg_fatal("%s: %s", argv[optind], parse_err);
	if ((aierr = hostname_to_sockaddr(host, port, SOCK_STREAM, &res)) != 0)
	    msg_fatal("%s: %s", argv[optind], MAI_STRERROR(aierr));
	myfree(buf);
	sa = (struct sockaddr *) & ss;
	if (res->ai_addrlen > sizeof(ss))
	    msg_fatal("address length %d > buffer length %d",
		      (int) res->ai_addrlen, (int) sizeof(ss));
	memcpy((char *) sa, res->ai_addr, res->ai_addrlen);
	sa_length = res->ai_addrlen;
#ifdef HAS_SA_LEN
	sa->sa_len = sa_length;
#endif
	freeaddrinfo(res);
    }

    /*
     * Allocate space for temporary buffer.
     */
    buffer = vstring_alloc(100);

    /*
     * Make sure we have sender and recipient addresses.
     */
    if (var_myhostname == 0)
	var_myhostname = get_hostname();
    if (sender == 0 || recipient == 0) {
	vstring_sprintf(buffer, "foo@%s", var_myhostname);
	defaddr = mystrdup(vstring_str(buffer));
	if (sender == 0)
	    sender = defaddr;
	if (recipient == 0)
	    recipient = defaddr;
    }

    /*
     * Prepare some results that may be used multiple times: the message
     * content netstring, the sender netstring, and the recipient netstrings.
     */
    mydate = mail_date(time((time_t *) 0));
    mypid = getpid();

    message_buffer = vstring_alloc(message_length + 200);
    vstring_sprintf(buffer,
		  "From: <%s>\nTo: <%s>\nDate: %s\nMessage-Id: <%d@%s>\n\n",
		    sender, recipient, mydate, mypid, var_myhostname);
    for (n = 1; LEN(buffer) < message_length; n++) {
	for (i = 0; i < n && i < 79; i++)
	    VSTRING_ADDCH(buffer, 'X');
	VSTRING_ADDCH(buffer, '\n');
    }
    STR(buffer)[message_length - 1] = '\n';
    netstring_memcpy(message_buffer, STR(buffer), message_length);

    len = strlen(sender);
    sender_buffer = vstring_alloc(len);
    netstring_memcpy(sender_buffer, sender, len);

    if (recipients == 1) {
	len = strlen(recipient);
	recipient_buffer = vstring_alloc(len);
	netstring_memcpy(recipient_buffer, recipient, len);
    } else {
	recipient_buffer = vstring_alloc(100);
	for (n = 0; n < recipients; n++) {
	    vstring_sprintf(buffer, "%d%s", n, recipient);
	    netstring_memcat(recipient_buffer, STR(buffer), LEN(buffer));
	}
    }

    /*
     * Start sessions.
     */
    while (sessions-- > 0) {
	session = (SESSION *) mymalloc(sizeof(*session));
	session->stream = 0;
	session->xfer_count = 0;
	session->connect_count = connect_count;
	session->next = 0;
	session_count++;
	startup(session);
    }
    for (;;) {
	event_loop(-1);
	if (session_count <= 0 && message_count <= 0) {
	    if (count) {
		VSTREAM_PUTC('\n', VSTREAM_OUT);
		vstream_fflush(VSTREAM_OUT);
	    }
	    exit(0);
	}
    }
}
Beispiel #7
0
void server( vsock_t *ssrv ) {

    struct smtp_resp *resp;
    struct smtp_cmd  *cmd;

    char *mail_from = NULL;

    vsock_t *sclnt;

    if ( (sclnt = vsock_connect(connect_to, 1500, timeout)) == NULL ) {
        error( "Can't forward connection" );

        smtp_putreply( ssrv, 554, "Sorry, can't forward connection", 0 );

        while( (cmd = smtp_readcmd(ssrv)) != NULL ) {
            if ( strcasecmp(cmd->command, "QUIT") == NULL ) 
                break;
            free_smtp_cmd( cmd );
            smtp_putreply( ssrv, 503, "bad sequence of commands", 0 );
        }
        free_smtp_cmd( cmd );
        smtp_putreply( ssrv, 221, "Bye", 0 );
        vsock_close( ssrv );

        return ;
    }

    resp = smtp_readreply( sclnt );

    if ( resp->code != 220 ) {
        error( "forward server not ready: %d %s", resp->code, resp->text  );

        while( resp != NULL && resp->cont ) {
            smtp_putreply(  ssrv, resp->code, resp->text, resp->cont );
            free_smtp_resp( resp );
            resp = smtp_readreply( sclnt );
        }
        
        smtp_putreply( ssrv, resp->code, resp->text, resp->cont );
        free_smtp_resp( resp );

        while( 1 ) {
            if ( (cmd = smtp_readcmd( ssrv )) == NULL ) {
                break;
            }
            if ( smtp_putcmd( sclnt, cmd ) ) {
                free_smtp_cmd( cmd );
                break;
            }

            resp = smtp_readreply(sclnt);
            while( resp != NULL && resp->cont ) {
                smtp_putreply(  ssrv, resp->code, resp->text, resp->cont );
                free_smtp_resp( resp );
                resp = smtp_readreply( sclnt );
            }
            if ( resp == NULL ) {
                break;
            }

            if ( smtp_putreply( ssrv, resp->code, resp->text, resp->cont )) {
                free_smtp_resp( resp );
                break;
            }
            free_smtp_resp( resp );
        }

        vsock_close( ssrv  );
        vsock_close( sclnt );
        
        return ;
    } 
    free_smtp_resp( resp );

    smtp_putreply( ssrv, 220, "localhost SMTP AV-filter " VERSION, 0 );

    while( 1 ) {
        int rc;

        cmd = smtp_readcmd( ssrv );
        if ( cmd == NULL ) {
            break;
        }
        if ( !strcasecmp(cmd->command, "MAIL") ) {
            if ( mail_from != NULL ) {
                free( mail_from );
                mail_from = NULL;
            }
            if ( cmd->argv[0] != NULL) {
                char *p;
                if ( (p = strchr(cmd->argv[0], ':')) != NULL ) { 
                    mail_from = strdup( p + 1 );
                }
            }
        }
        if ( strcasecmp(cmd->command, "DATA") == NULL ) {
            struct mem_chunk *data;

            smtp_putreply( ssrv, 354, "End data with <CR><LF>.<CR><LF>", 0 );
            debug( "enter DATA" );
            data = smtp_readdata( ssrv );
            if ( check_data( data ) ) {
                free_mem_chunks( data );
                error("message from %s infected", mail_from == NULL ?  "<unknown user>" : mail_from );
                cmd->command = "RSET";
                cmd->argc = 0;
                smtp_putcmd( sclnt, cmd );
                resp = smtp_readreply( sclnt );
                free_smtp_resp( resp );
                smtp_putreply(  ssrv, 550, "Content rejected (Message infected with virus)", 0 );
            }
            else { 
                notice( "message from %s passed virus check", 
                        mail_from == NULL ?  "<unknown user>" : mail_from );

                rc = smtp_putcmd( sclnt, cmd );
                free_smtp_cmd( cmd );
                if ( rc ) { 
                    debug( "smtp_putcmd failed, breaking loop" );
                    break;
                }
                debug( "loop: rr1-1" );
                resp = smtp_readreply( sclnt );
                if ( resp == NULL ) {
                    debug( "smtp_readreply failed, breaking loop" );
                    break;
                }
                if ( resp->code != 354 ) {
                    error("DATA failed: %d %s", resp->code, resp->text);
                    cmd->command = "DATA";
                    cmd->argc = 0;
                    /* FIXME */
                    smtp_putcmd( sclnt, cmd );
                    smtp_readreply( sclnt );
                }
                free_smtp_resp( resp );

                smtp_printf( sclnt, "Received: from %s by %s (AV-filter %s) ;" ,
                        "localhost", "localhost", VERSION  );
                smtp_printf( sclnt, "\t%s",  mail_date( time( NULL )));

                smtp_putdata( sclnt, data );
                free_mem_chunks( data );
                resp = smtp_readreply( sclnt );
                smtp_putreply(  ssrv, resp->code, resp->text, resp->cont );
                free_smtp_resp( resp );
            }
        } 
        else {
            rc = smtp_putcmd( sclnt, cmd );
            free_smtp_cmd( cmd ); 
            if ( rc ) { 
                debug( "smtp_putcmd failed, breaking loop" );
                break;
            }
            resp = smtp_readreply( sclnt );
            while( resp != NULL && resp->cont ) {
#if 0                
                if ( strcasecmp(resp->text, "PIPELINING") != NULL )
#endif
                    smtp_putreply(  ssrv, resp->code, resp->text, resp->cont );
                free_smtp_resp( resp );
                resp = smtp_readreply( sclnt );
            }
            if ( resp == NULL ) {
                debug( "smtp_readreply failed, breaking loop" );
                break;
            }
            rc = smtp_putreply( ssrv, resp->code, resp->text, resp->cont );
            if ( rc ) {
                free_smtp_resp( resp );
                debug( "smtp_putreply failed, breaking loop" );
                break;
            }
            if ( resp->code == 221 ) {
                free_smtp_resp( resp );
                debug( "221 response received" );
                break;
            }
            free_smtp_resp( resp );
        }

    }
    vsock_close( ssrv  );
    vsock_close( sclnt );
}