/* * bdat.c, SJ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void reset_bdat_counters(struct smtp_session *session){ session->bdat_rounds = 0; session->bdat_last_round = 0; session->bdat_bytes_to_read = 0; session->bad = 0; } void get_bdat_size_to_read(struct smtp_session *session, char *buf){ char *p; session->bdat_rounds++; session->bdat_bytes_to_read = 0; session->protocol_state = SMTP_STATE_BDAT; // determine if this is the last BDAT command p = strcasestr(buf, " LAST"); if(p){ session->bdat_last_round = 1; syslog(LOG_INFO, "%s: BDAT LAST", session->ttmpfile); *p = '\0'; } // determine the size to be read p = strchr(buf, ' '); if(p){ session->bdat_bytes_to_read = atoi(p); if(session->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_INFO, "%s: BDAT len=%d", session->ttmpfile, session->bdat_bytes_to_read); } if(!p || session->bdat_bytes_to_read <= 0){ session->bdat_bytes_to_read = 0; syslog(LOG_INFO, "%s: malformed BDAT command", session->ttmpfile); } } void process_bdat(struct smtp_session *session, char *readbuf, int readlen){ int i; char buf[SMALLBUFSIZE]; if(readlen <= 0) return; //printf("readbuf in process_bdat (%d): *%s*\n", readlen, readbuf); if(session->bdat_rounds == 1){ session->fd = open(session->ttmpfile, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP); if(session->fd == -1){ syslog(LOG_PRIORITY, "%s: %s", ERR_OPEN_TMP_FILE, session->ttmpfile); } } session->bdat_bytes_to_read -= readlen; if(session->fd != -1){ write(session->fd, readbuf, readlen); session->tot_len += readlen; if(session->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_INFO, "%s: wrote %d bytes, %d bytes to go", session->ttmpfile, readlen, session->bdat_bytes_to_read); } if(session->bdat_bytes_to_read < 0){ // malformed data from client: we got more data then had been told in BDAT argument syslog(LOG_PRIORITY, "ERROR: invalid BDAT data. Expected %d, got %d bytes", session->bdat_bytes_to_read + readlen, readlen); session->bad = 1; close(session->fd); unlink(session->ttmpfile); } if(session->bdat_bytes_to_read == 0){ if(session->bdat_last_round == 1){ if(session->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_INFO, "%s: read all bdat data in %d rounds", session->ttmpfile, session->bdat_rounds); // send back the smtp answers for(i=0; ibdat_rounds; i++){ if(session->fd == -1){ send_smtp_response(session, SMTP_RESP_421_ERR_WRITE_FAILED); } else { if(i == 0){ fsync(session->fd); close(session->fd); move_email(session); snprintf(buf, sizeof(buf)-1, "250 OK <%s>\r\n", session->ttmpfile); send_smtp_response(session, buf); syslog(LOG_PRIORITY, "received: %s, from=%s, size=%d", session->ttmpfile, session->mailfrom, session->tot_len); } else send_smtp_response(session, SMTP_RESP_250_BDAT); } } // technically we are not in the PERIOD state, but it's good enough // to quit the BDAT processing state session->protocol_state = SMTP_STATE_PERIOD; session->fd = -1; } else { // this is not the last BDAT round, let's go back // after the rcpt state, and wait for the next // BDAT command session->protocol_state = SMTP_STATE_RCPT_TO; } } }