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; } } }
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; }
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); } }
int main(void) { vstream_printf("%s\n", mail_date(time((time_t *) 0))); vstream_fflush(VSTREAM_OUT); return (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); }
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); } } }
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 ); }