Newer
Older
uint32_t msgnum;
ulong bytes;
SOCKET socket;
smb_t smb;
smbmsg_t msg;
user_t user;
client_t client;
mail_t* mail;
pop3_t pop3=*(pop3_t*)arg;
CRYPT_SESSION session = -1;
BOOL nodelay=TRUE;
ulong nb = 0;
char *estr;
int level;
int stat;
SetThreadName("sbbs/pop3");
thread_up(TRUE /* setuid */);
free(arg);
socket=pop3.socket;
client.protocol = pop3.tls_port ? "POP3S" : "POP3";
if(startup->options&MAIL_OPT_DEBUG_POP3)
lprintf(LOG_DEBUG,"%04d %s session thread started", socket, client.protocol);
if(startup->pop3_sound[0] && !(startup->options&MAIL_OPT_MUTE))
PlaySound(startup->pop3_sound, NULL, SND_ASYNC|SND_FILENAME);
if(startup->options&MAIL_OPT_DEBUG_POP3)
lprintf(LOG_INFO,"%04d %s connection accepted from: %s port %u"
,socket, client.protocol, host_ip, inet_addrport(&pop3.client_addr));
SAFECOPY(host_name, STR_NO_HOSTNAME);
if(!(startup->options&MAIL_OPT_NO_HOST_LOOKUP)) {
getnameinfo(&pop3.client_addr.addr, pop3.client_addr_len, host_name, sizeof(host_name), NULL, 0, NI_NAMEREQD);
if(startup->options&MAIL_OPT_DEBUG_POP3)
lprintf(LOG_INFO,"%04d %s Hostname: %s [%s]", socket, client.protocol, host_name, host_ip);
if (pop3.tls_port) {
if (get_ssl_cert(&scfg, &estr, &level) == -1) {
if (estr) {
lprintf(level, "%04d %s !Failure getting certificate: %s", socket, client.protocol, estr);
free_crypt_attrstr(estr);
mail_close_socket(&socket, &session);
thread_down();
return;
}
if ((stat=cryptCreateSession(&session, CRYPT_UNUSED, CRYPT_SESSION_SSL_SERVER)) != CRYPT_OK) {
GCESH(stat, client.protocol, socket, host_ip, CRYPT_UNUSED, "creating session");
mail_close_socket(&socket, &session);
thread_down();
return;
}
if ((stat=cryptSetAttribute(session, CRYPT_SESSINFO_SSL_OPTIONS, CRYPT_SSLOPTION_DISABLE_CERTVERIFY)) != CRYPT_OK) {
GCESH(stat, client.protocol, socket, host_ip, session, "disabling certificate verification");
mail_close_socket(&socket, &session);
thread_down();
return;
}
lock_ssl_cert();
if ((stat=cryptSetAttribute(session, CRYPT_SESSINFO_PRIVATEKEY, scfg.tls_certificate)) != CRYPT_OK) {
unlock_ssl_cert();
GCESH(stat, client.protocol, socket, host_ip, session, "setting private key");
mail_close_socket(&socket, &session);
thread_down();
return;
}
nodelay = TRUE;
setsockopt(socket,IPPROTO_TCP,TCP_NODELAY,(char*)&nodelay,sizeof(nodelay));
nb=0;
ioctlsocket(socket,FIONBIO,&nb);
if ((stat = cryptSetAttribute(session, CRYPT_SESSINFO_NETWORKSOCKET, socket)) != CRYPT_OK) {
unlock_ssl_cert();
GCESH(stat, client.protocol, socket, host_ip, session, "setting session socket");
mail_close_socket(&socket, &session);
thread_down();
return;
}
if ((stat = cryptSetAttribute(session, CRYPT_SESSINFO_ACTIVE, 1)) != CRYPT_OK) {
unlock_ssl_cert();
GCESH(stat, client.protocol, socket, host_ip, session, "setting session active");
mail_close_socket(&socket, &session);
thread_down();
return;
}
unlock_ssl_cert();
if (startup->max_inactivity) {
if (cryptSetAttribute(session, CRYPT_OPTION_NET_READTIMEOUT, startup->max_inactivity) != CRYPT_OK) {
GCESH(stat, client.protocol, socket, host_ip, session, "setting read timeout");
mail_close_socket(&socket, &session);
thread_down();
return;
}
}
}
ulong banned = loginBanned(&scfg, startup->login_attempt_list, socket, host_name, startup->login_attempt, &attempted);
if(banned || trashcan(&scfg,host_ip,"ip")) {
if(banned) {
char ban_duration[128];
lprintf(LOG_NOTICE, "%04d %s !TEMPORARY BAN of %s (%lu login attempts, last: %s) - remaining: %s"
,socket, client.protocol, host_ip, attempted.count-attempted.dupes, attempted.user, seconds_to_str(banned, ban_duration));
lprintf(LOG_NOTICE,"%04d %s !CLIENT IP ADDRESS BLOCKED: %s",socket, client.protocol, host_ip);
sockprintf(socket,client.protocol,session,"-ERR Access denied.");
mail_close_socket(&socket, &session);
thread_down();
return;
}
if(trashcan(&scfg,host_name,"host")) {
lprintf(LOG_NOTICE,"%04d %s !CLIENT HOSTNAME BLOCKED: %s"
,socket, client.protocol, host_name);
sockprintf(socket,client.protocol,session,"-ERR Access denied.");
mail_close_socket(&socket, &session);
thread_down();
return;
}
protected_uint32_adjust(&active_clients, 1);
update_clients();
/* Initialize client display */
client.size=sizeof(client);
client.time=time32(NULL);
SAFECOPY(client.addr,host_ip);
SAFECOPY(client.host,host_name);
client.user=STR_UNKNOWN_USER;
client.usernum = 0;
client_on(socket,&client,FALSE /* update */);
SAFEPRINTF2(str,"%s: %s", client.protocol, host_ip);
&& (login_attempts=loginAttempts(startup->login_attempt_list, &pop3.client_addr)) > 1) {
lprintf(LOG_DEBUG,"%04d %s Throttling suspicious connection from: %s (%lu login attempts)"
,socket, client.protocol, host_ip, login_attempts);
mswait(login_attempts*startup->login_attempt.throttle);
}
do {
memset(&smb,0,sizeof(smb));
memset(&msg,0,sizeof(msg));

rswindell
committed
memset(&user,0,sizeof(user));
password[0]=0;
srand((unsigned int)(time(NULL) ^ (time_t)GetCurrentThreadId())); /* seed random number generator */
rand(); /* throw-away first result */
safe_snprintf(challenge,sizeof(challenge),"<%x%x%lx%lx@%.128s>"
,rand(),socket,(ulong)time(NULL),(ulong)clock(), server_host_name());
sockprintf(socket,client.protocol,session,"+OK Synchronet %s Server %s-%s Ready %s"
,client.protocol, revision,PLATFORM_DESC,challenge);
/* Requires USER or APOP command first */
for(i=5;i;i--) {
if(!sockgetrsp(socket,client.protocol,session,NULL,buf,sizeof(buf)))
break;
if(!strnicmp(buf,"USER ",5))
break;
break;
else if (!stricmp(buf, "CAPA")) {
// Capabilities
sockprintf(socket,client.protocol,session, "+OK Capability list follows");
sockprintf(socket,client.protocol,session, "TOP\r\nUSER\r\nPIPELINING\r\nUIDL\r\nIMPLEMENTATION Synchronet POP3 Server %s-%s\r\n%s.", revision, PLATFORM_DESC, (session != -1 || get_ssl_cert(&scfg, NULL, NULL) == -1) ? "" : "STLS\r\n");
i++;
}
else if (!stricmp(buf, "STLS")) {
if (get_ssl_cert(&scfg, &estr, &level) == -1) {
if (estr) {
lprintf(level, "%04d %s !TLS Failure getting certificate: %s", socket, client.protocol, estr);
free_crypt_attrstr(estr);
}
sockprintf(socket,client.protocol,session,"-ERR STLS command not supported");
sockprintf(socket,client.protocol,session,"+OK Begin TLS negotiation");
if ((stat=cryptCreateSession(&session, CRYPT_UNUSED, CRYPT_SESSION_SSL_SERVER)) != CRYPT_OK) {
GCESH(stat, client.protocol, socket, host_ip, CRYPT_UNUSED, "creating session");
if ((stat=cryptSetAttribute(session, CRYPT_SESSINFO_SSL_OPTIONS, CRYPT_SSLOPTION_DISABLE_CERTVERIFY)) != CRYPT_OK) {
GCESH(stat, client.protocol, socket, host_ip, session, "disabling certificate verification");
lock_ssl_cert();
if ((stat=cryptSetAttribute(session, CRYPT_SESSINFO_PRIVATEKEY, scfg.tls_certificate)) != CRYPT_OK) {
unlock_ssl_cert();
GCESH(stat, client.protocol, socket, host_ip, session, "setting private key");
buf[0] = 0;
break;
}
nodelay = TRUE;
setsockopt(socket,IPPROTO_TCP,TCP_NODELAY,(char*)&nodelay,sizeof(nodelay));
nb=0;
ioctlsocket(socket,FIONBIO,&nb);
if ((stat = cryptSetAttribute(session, CRYPT_SESSINFO_NETWORKSOCKET, socket)) != CRYPT_OK) {
unlock_ssl_cert();
GCESH(stat, client.protocol, socket, host_ip, session, "setting network socket");
if ((stat=cryptSetAttribute(session, CRYPT_SESSINFO_ACTIVE, 1)) != CRYPT_OK) {
unlock_ssl_cert();
GCESH(stat, client.protocol, socket, host_ip, session, "setting session active");
unlock_ssl_cert();
if ((stat=cryptSetAttribute(session, CRYPT_OPTION_NET_READTIMEOUT, startup->max_inactivity)) != CRYPT_OK) {
GCESH(stat, client.protocol, socket, host_ip, session, "setting read timeout");
buf[0] = 0;
break;
}
}
i++;
client.protocol = "POP3S";
sockprintf(socket,client.protocol,session,"-ERR USER, APOP, CAPA, or STLS command expected");
if(!i || buf[0]==0) /* no USER or APOP command received */
break;
SKIP_WHITESPACE(p);
if(apop) {
if((response=strrchr(p,' '))!=NULL)
*(response++)=0;
else
response=p;
}
if((p = strstr(username, NO_SPAM)) != NULL) {
*p = 0;
lm_mode = LM_NOSPAM;
} else
lm_mode = 0;
sockprintf(socket,client.protocol,session,"+OK");
if(!sockgetrsp(socket,client.protocol,session,"PASS ",buf,sizeof(buf))) {
sockprintf(socket,client.protocol,session,"-ERR PASS command expected");
SKIP_WHITESPACE(p);
user.number=matchuser(&scfg,username,FALSE /*sysop_alias*/);

rswindell
committed
if(scfg.sys_misc&SM_ECHO_PW)
lprintf(LOG_NOTICE,"%04d %s [%s] !UNKNOWN USER: '%s' (password: %s)"
,socket, client.protocol, host_ip, username, password);

rswindell
committed
else
lprintf(LOG_NOTICE,"%04d %s [%s] !UNKNOWN USER: '%s'"
,socket, client.protocol, host_ip, username);
badlogin(socket, session, client.protocol, pop_auth_error, username, password, host_name, &pop3.client_addr);
break;
}
if((i=getuserdat(&scfg, &user))!=0) {
lprintf(LOG_ERR,"%04d %s [%s] !ERROR %d getting data on user (%s)"
,socket, client.protocol, host_ip, i, username);
break;
}
if(user.misc&(DELETED|INACTIVE)) {
lprintf(LOG_NOTICE,"%04d %s [%s] !DELETED or INACTIVE user #%u (%s)"
,socket, client.protocol, host_ip, user.number, username);
badlogin(socket, session, client.protocol, pop_auth_error, username, password, NULL, NULL);
if(apop) {
strlwr(user.pass); /* this is case-sensitive, so convert to lowercase */
strcat(challenge,user.pass);
MD5_calc(digest,challenge,strlen(challenge));
MD5_hex((BYTE*)str,digest);
lprintf(LOG_NOTICE,"%04d %s [%s] !FAILED APOP authentication: %s"
,socket, client.protocol, host_ip, username);
lprintf(LOG_DEBUG,"%04d !POP3 digest data: %s",socket,challenge);
lprintf(LOG_DEBUG,"%04d !POP3 calc digest: %s",socket,str);
lprintf(LOG_DEBUG,"%04d !POP3 resp digest: %s",socket,response);
badlogin(socket, session, client.protocol, pop_auth_error, username, response, host_name, &pop3.client_addr);
break;
}
} else if(stricmp(password,user.pass)) {

rswindell
committed
if(scfg.sys_misc&SM_ECHO_PW)
lprintf(LOG_NOTICE,"%04d %s [%s] !FAILED Password attempt for user %s: '%s' expected '%s'"
,socket, client.protocol, host_ip, username, password, user.pass);

rswindell
committed
else
lprintf(LOG_NOTICE,"%04d %s [%s] !FAILED Password attempt for user %s"
,socket, client.protocol, host_ip, username);
badlogin(socket, session, client.protocol, pop_auth_error, username, password, host_name, &pop3.client_addr);
if(user.pass[0]) {
loginSuccess(startup->login_attempt_list, &pop3.client_addr);
listAddNodeData(¤t_logins, client.addr, strlen(client.addr) + 1, socket, LAST_NODE);
}
putuserrec(&scfg,user.number,U_COMP,LEN_COMP,host_name);
/* Update client display */
client.user=user.alias;
client.usernum = user.number;
client_on(socket,&client,TRUE /* update */);
if(startup->options&MAIL_OPT_DEBUG_POP3)
lprintf(LOG_INFO,"%04d %s [%s] %s logged in %s", socket, client.protocol, host_ip, user.alias, apop ? "via APOP":"");
SAFEPRINTF2(str,"%s: %s", client.protocol, user.alias);
SAFEPRINTF(smb.file,"%smail",scfg.data_dir);
if(smb_islocked(&smb)) {
lprintf(LOG_WARNING,"%04d %s <%s> !MAIL BASE LOCKED: %s",socket, client.protocol, user.alias, smb.last_error);
sockprintf(socket,client.protocol,session,"-ERR database locked, try again later");
break;
}
smb.retry_time=scfg.smb_retry_time;
smb.subnum=INVALID_SUB;
if((i=smb_open(&smb))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) opening %s", socket, client.protocol, user.alias, i, smb.last_error,smb.file);
sockprintf(socket,client.protocol,session,"-ERR %d opening %s",i,smb.file);
break;
}
mail=loadmail(&smb,&msgs,user.number,MAIL_YOUR,lm_mode);
for(l=bytes=0;l<msgs;l++) {
msg.hdr.number=mail[l].number;
if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) getting message index"
,socket, client.protocol, user.alias, i, smb.last_error);
if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_WARNING,"%04d %s <%s> !ERROR %d (%s) locking message header #%u"
,socket, client.protocol, user.alias, i, smb.last_error, msg.hdr.number);
break;
}
i=smb_getmsghdr(&smb,&msg);
smb_unlockmsghdr(&smb,&msg);
if(i!=0) {
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) line %u, msg #%u"
,socket, client.protocol, user.alias, i, smb.last_error, __LINE__, msg.idx.number);
bytes+=smb_getmsgtxtlen(&msg);
sockprintf(socket,client.protocol,session,"-ERR message #%d: %d (%s)"
,mail[l].number,i,smb.last_error);
break;
}
sockprintf(socket,client.protocol,session,"+OK %u messages (%lu bytes)",msgs,bytes);
while(1) { /* TRANSACTION STATE */
rd = sockreadline(socket, client.protocol, session, buf, sizeof(buf));
if(rd<0)
truncsp(buf);
if(startup->options&MAIL_OPT_DEBUG_POP3)
lprintf(LOG_DEBUG,"%04d %s RX: %s", socket, client.protocol, buf);
sockprintf(socket,client.protocol,session,"+OK");
continue;
}
if(!stricmp(buf, "QUIT")) {
sockprintf(socket,client.protocol,session,"+OK");
break;
}
if(!stricmp(buf, "STAT")) {
sockprintf(socket,client.protocol,session,"+OK %u %lu",msgs,bytes);
continue;
}
if(!stricmp(buf, "RSET")) {
if((i=smb_locksmbhdr(&smb))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) locking message base"
,socket, client.protocol, user.alias, i, smb.last_error);
sockprintf(socket,client.protocol,session,"-ERR %d locking message base",i);
continue;
}
for(l=0;l<msgs;l++) {
msg.hdr.number=mail[l].number;
if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) getting message index"
,socket, client.protocol, user.alias, i, smb.last_error);
if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_WARNING,"%04d %s <%s> !ERROR %d (%s) locking message header #%u"
,socket, client.protocol, user.alias, i, smb.last_error, msg.hdr.number);
if((i=smb_getmsghdr(&smb,&msg))!=SMB_SUCCESS) {
smb_unlockmsghdr(&smb,&msg);
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) line %u, msg #%u"
,socket, client.protocol, user.alias, i, smb.last_error, __LINE__, msg.idx.number);
break;
}
msg.hdr.attr=mail[l].attr;
if((i=smb_putmsg(&smb,&msg))!=SMB_SUCCESS)
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) updating message index"
,socket, client.protocol, user.alias, i, smb.last_error);
smb_unlockmsghdr(&smb,&msg);
smb_freemsgmem(&msg);
}
smb_unlocksmbhdr(&smb);
sockprintf(socket,client.protocol,session,"-ERR %d messages reset (ERROR: %d)",l,i);
sockprintf(socket,client.protocol,session,"+OK %u messages (%lu bytes)",msgs,bytes);
continue;
}
if(!strnicmp(buf, "LIST",4) || !strnicmp(buf,"UIDL",4)) {
p=buf+4;
SKIP_WHITESPACE(p);
if(isdigit((uchar)*p)) {
msgnum=strtoul(p, NULL, 10);
if(msgnum<1 || msgnum>msgs) {
lprintf(LOG_NOTICE,"%04d %s <%s> !INVALID message #%" PRIu32
,socket, client.protocol, user.alias, msgnum);
sockprintf(socket,client.protocol,session,"-ERR no such message");
continue;
}
msg.hdr.number=mail[msgnum-1].number;
if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) getting message index"
,socket, client.protocol, user.alias, i, smb.last_error);
sockprintf(socket,client.protocol,session,"-ERR %d getting message index",i);
break;
}
if(msg.idx.attr&MSG_DELETE) {
lprintf(LOG_NOTICE,"%04d %s <%s> !ATTEMPT to list DELETED message"
,socket, client.protocol, user.alias);
sockprintf(socket,client.protocol,session,"-ERR message deleted");
if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_WARNING,"%04d %s <%s> !ERROR %d (%s) locking message header #%u"
,socket, client.protocol, user.alias, i, smb.last_error, msg.hdr.number);
sockprintf(socket,client.protocol,session,"-ERR %d locking message header",i);
continue;
}
i=smb_getmsghdr(&smb,&msg);
smb_unlockmsghdr(&smb,&msg);
if(i!=0) {
smb_freemsgmem(&msg);
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) line %u, msg #%u"
,socket, client.protocol, user.alias, i, smb.last_error, __LINE__, msg.idx.number);
sockprintf(socket,client.protocol,session,"-ERR %d getting message header",i);
continue;
}
if(!strnicmp(buf, "LIST",4)) {
sockprintf(socket,client.protocol,session,"+OK %" PRIu32 " %lu",msgnum,smb_getmsgtxtlen(&msg));
sockprintf(socket,client.protocol,session,"+OK %" PRIu32 " %u",msgnum,msg.hdr.number);
smb_freemsgmem(&msg);
continue;
}
/* List ALL messages */
sockprintf(socket,client.protocol,session,"+OK %u messages (%lu bytes)",msgs,bytes);
for(l=0;l<msgs;l++) {
msg.hdr.number=mail[l].number;
if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) getting message index"
,socket, client.protocol, user.alias, i, smb.last_error);
break;
}
if(msg.idx.attr&MSG_DELETE)
continue;
if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_WARNING,"%04d %s <%s> !ERROR %d (%s) locking message header #%u"
,socket, client.protocol, user.alias, i, smb.last_error, msg.hdr.number);
break;
}
i=smb_getmsghdr(&smb,&msg);
smb_unlockmsghdr(&smb,&msg);
if(i!=0) {
smb_freemsgmem(&msg);
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) line %u, msg #%u"
,socket, client.protocol, user.alias, i, smb.last_error, __LINE__, msg.idx.number);
break;
}
if(!strnicmp(buf, "LIST",4)) {
sockprintf(socket,client.protocol,session,"%u %lu",l+1,smb_getmsgtxtlen(&msg));
sockprintf(socket,client.protocol,session,"%u %u",l+1,msg.hdr.number);
smb_freemsgmem(&msg);
}
sockprintf(socket,client.protocol,session,".");
activity=TRUE;
if(!strnicmp(buf, "RETR ",5) || !strnicmp(buf,"TOP ",4)) {
SAFEPRINTF2(str,"%s: %s", client.protocol, user.alias);
status(str);
lines=-1;
p=buf+4;
SKIP_WHITESPACE(p);
msgnum=strtoul(p, NULL, 10);
if(!strnicmp(buf,"TOP ",4)) {
SKIP_DIGIT(p);
SKIP_WHITESPACE(p);
lines=atol(p);
}
if(msgnum<1 || msgnum>msgs) {
lprintf(LOG_NOTICE,"%04d %s <%s> !ATTEMPTED to retrieve an INVALID message #%" PRIu32
,socket, client.protocol, user.alias, msgnum);
sockprintf(socket,client.protocol,session,"-ERR no such message");
continue;
}
msg.hdr.number=mail[msgnum-1].number;
lprintf(LOG_INFO,"%04d %s <%s> retrieving message #%u with command: %s"
,socket, client.protocol, user.alias, msg.hdr.number, buf);
if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) getting message index"
,socket, client.protocol, user.alias, i, smb.last_error);
sockprintf(socket,client.protocol,session,"-ERR %d getting message index",i);
continue;
}
if(msg.idx.attr&MSG_DELETE) {
lprintf(LOG_NOTICE,"%04d %s <%s> !ATTEMPT to retrieve DELETED message"
,socket, client.protocol, user.alias);
sockprintf(socket,client.protocol,session,"-ERR message deleted");
if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_WARNING,"%04d %s <%s> !ERROR %d (%s) locking message header #%u"
,socket, client.protocol, user.alias, i, smb.last_error, msg.hdr.number);
sockprintf(socket,client.protocol,session,"-ERR %d locking message header",i);
i=smb_getmsghdr(&smb,&msg);
smb_unlockmsghdr(&smb,&msg);
if(i!=0) {
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) line %u, msg #%u"
,socket, client.protocol, user.alias, i, smb.last_error, __LINE__, msg.idx.number);
sockprintf(socket,client.protocol,session,"-ERR %d getting message header",i);
if((msgtxt=smb_getmsgtxt(&smb,&msg,GETMSGTXT_ALL))==NULL) {
lprintf(LOG_ERR,"%04d %s <%s> !ERROR (%s) retrieving message #%u text"
,socket, client.protocol, user.alias, smb.last_error, msg.hdr.number);
sockprintf(socket,client.protocol,session,"-ERR retrieving message text");
remove_ctrl_a(msgtxt, msgtxt);
if(lines > 0 /* Works around BlackBerry mail server */
&& lines >= strlen(msgtxt)) /* which requests the number of bytes (instead of lines) using TOP */
lines=-1;
sockprintf(socket,client.protocol,session,"+OK message follows");
lprintf(LOG_DEBUG,"%04d %s <%s> sending message text (%lu bytes)"
,socket, client.protocol, user.alias, (ulong)strlen(msgtxt));
lines_sent=sockmsgtxt(socket, client.protocol, session, &msg, msgtxt, lines);
/* if(startup->options&MAIL_OPT_DEBUG_POP3) */
if(lines!=-1 && lines_sent<lines) /* could send *more* lines */
lprintf(LOG_WARNING,"%04d %s <%s> !ERROR sending message text (sent %ld of %ld lines)"
,socket, client.protocol, user.alias, lines_sent, lines);
lprintf(LOG_DEBUG,"%04d %s <%s> message transfer complete (%lu lines)"
,socket, client.protocol, user.alias, lines_sent);
if((i=smb_locksmbhdr(&smb))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) locking message base"
,socket, client.protocol, user.alias, i, smb.last_error);
} else {
if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) getting message index"
,socket, client.protocol, user.alias, i, smb.last_error);
} else {
msg.hdr.attr|=MSG_READ;
if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS)
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) locking message header #%u"
,socket, client.protocol, user.alias, i, smb.last_error, msg.hdr.number);
if((i=smb_putmsg(&smb,&msg))!=SMB_SUCCESS)
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) marking message #%u as read"
,socket, client.protocol, user.alias, i, smb.last_error, msg.hdr.number);
smb_unlockmsghdr(&smb,&msg);
}
smb_unlocksmbhdr(&smb);
}
smb_freemsgmem(&msg);
smb_freemsgtxt(msgtxt);
continue;
}
if(!strnicmp(buf, "DELE ",5)) {
p=buf+5;
SKIP_WHITESPACE(p);
msgnum=strtoul(p, NULL, 10);
if(msgnum<1 || msgnum>msgs) {
lprintf(LOG_NOTICE,"%04d %s <%s> !ATTEMPTED to delete an INVALID message #%" PRIu32
,socket, client.protocol, user.alias, msgnum);
sockprintf(socket,client.protocol,session,"-ERR no such message");
continue;
}
msg.hdr.number=mail[msgnum-1].number;
lprintf(LOG_INFO,"%04d %s <%s> deleting message #%u"
,socket, client.protocol, user.alias, msg.hdr.number);
if((i=smb_locksmbhdr(&smb))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) locking message base"
,socket, client.protocol, user.alias, i, smb.last_error);
sockprintf(socket,client.protocol,session,"-ERR %d locking message base",i);
continue;
}
if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
smb_unlocksmbhdr(&smb);
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) getting message index"
,socket, client.protocol, user.alias, i, smb.last_error);
sockprintf(socket,client.protocol,session,"-ERR %d getting message index",i);
if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) {
smb_unlocksmbhdr(&smb);
lprintf(LOG_WARNING,"%04d %s <%s> !ERROR %d (%s) locking message header #%u"
,socket, client.protocol, user.alias, i, smb.last_error, msg.hdr.number);
sockprintf(socket,client.protocol,session,"-ERR %d locking message header",i);
if((i=smb_getmsghdr(&smb,&msg))!=SMB_SUCCESS) {
smb_unlocksmbhdr(&smb);
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) line %u, msg #%u"
,socket, client.protocol, user.alias, i, smb.last_error, __LINE__, msg.idx.number);
sockprintf(socket,client.protocol,session,"-ERR %d getting message header",i);
continue;
}
msg.hdr.attr|=MSG_DELETE;
if((i=smb_putmsg(&smb,&msg))==SMB_SUCCESS && msg.hdr.auxattr&MSG_FILEATTACH)
delfattach(&scfg,&msg);
smb_unlockmsghdr(&smb,&msg);
smb_unlocksmbhdr(&smb);
smb_freemsgmem(&msg);
if(i!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d %s <%s> !ERROR %d (%s) marking message for deletion"
, socket, client.protocol, user.alias, i, smb.last_error);
sockprintf(socket,client.protocol,session,"-ERR %d marking message for deletion",i);
sockprintf(socket,client.protocol,session,"+OK");
if(startup->options&MAIL_OPT_DEBUG_POP3)
lprintf(LOG_INFO,"%04d %s <%s> message deleted", socket, client.protocol, user.alias);
lprintf(LOG_NOTICE,"%04d %s <%s> !UNSUPPORTED COMMAND: '%s'"
,socket, client.protocol, user.alias, buf);
sockprintf(socket,client.protocol,session,"-ERR UNSUPPORTED COMMAND: %s",buf);
if(user.number) {
if(!logoutuserdat(&scfg,&user,time(NULL),client.time))
lprintf(LOG_ERR,"%04d %s <%s> !ERROR in logoutuserdat", socket, client.protocol, user.alias);
if(activity) {
if(user.number)
lprintf(LOG_INFO,"%04d %s <%s> logged out from port %u on %s [%s]"
,socket, client.protocol, user.alias, inet_addrport(&pop3.client_addr), host_name, host_ip);
lprintf(LOG_INFO,"%04d %s client disconnected from port %u on %s [%s]"
,socket, client.protocol, inet_addrport(&pop3.client_addr), host_name, host_ip);
status(STATUS_WFC);
/* Free up resources here */
if(mail!=NULL)
freemail(mail);
smb_freemsgmem(&msg);
smb_close(&smb);
listRemoveTaggedNode(¤t_logins, socket, /* free_data */TRUE);
protected_uint32_adjust(&active_clients, -1);
update_clients();
{
int32_t remain = thread_down();
if(startup->options&MAIL_OPT_DEBUG_POP3)
lprintf(LOG_DEBUG,"%04d %s session thread terminated (%u threads remain, %lu clients served)"
,socket, client.protocol, remain, ++stats.pop3_served);
/* Must be last */
mail_close_socket(&socket, &session);
static ulong rblchk(SOCKET sock, const char* prot, union xp_sockaddr *addr, const char* rbl_addr)
{
char name[256];
DWORD mail_addr;
HOSTENT* host;
struct in_addr dnsbl_result;
unsigned char *addr6;
switch(addr->addr.sa_family) {
case AF_INET:
mail_addr=ntohl(addr->in.sin_addr.s_addr);
safe_snprintf(name,sizeof(name),"%lu.%lu.%lu.%lu.%.128s"
,(ulong)(mail_addr&0xff)
,(ulong)(mail_addr>>8)&0xff
,(ulong)(mail_addr>>16)&0xff
,(ulong)(mail_addr>>24)&0xff
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
,rbl_addr
);
break;
case AF_INET6:
addr6 = (unsigned char *)&addr->in6.sin6_addr;
safe_snprintf(name,sizeof(name),"%1x.%1x.%1x.%1x.%1x.%1x.%1x.%1x."
"%1x.%1x.%1x.%1x.%1x.%1x.%1x.%1x."
"%1x.%1x.%1x.%1x.%1x.%1x.%1x.%1x."
"%1x.%1x.%1x.%1x.%1x.%1x.%1x.%1x.%.128s"
,addr6[15]&0x0f
,addr6[15]>>4
,addr6[14]&0x0f
,addr6[14]>>4
,addr6[13]&0x0f
,addr6[13]>>4
,addr6[12]&0x0f
,addr6[12]>>4
,addr6[11]&0x0f
,addr6[11]>>4
,addr6[10]&0x0f
,addr6[10]>>4
,addr6[9]&0x0f
,addr6[9]>>4
,addr6[8]&0x0f
,addr6[8]>>4
,addr6[7]&0x0f
,addr6[7]>>4
,addr6[6]&0x0f
,addr6[6]>>4
,addr6[5]&0x0f
,addr6[5]>>4
,addr6[4]&0x0f
,addr6[4]>>4
,addr6[3]&0x0f
,addr6[3]>>4
,addr6[2]&0x0f
,addr6[2]>>4
,addr6[1]&0x0f
,addr6[1]>>4
,addr6[0]&0x0f
,addr6[0]>>4
,rbl_addr
);
break;
default:
return 0;
}
lprintf(LOG_DEBUG,"%04d %s DNSBL Query: %s",sock,prot,name);
if((host=gethostbyname(name))==NULL)
return(0);
dnsbl_result.s_addr = *((ulong*)host->h_addr_list[0]);
lprintf(LOG_INFO,"%04d %s DNSBL Query: %s resolved to: %s"
,sock,prot,name,inet_ntoa(dnsbl_result));
return(dnsbl_result.s_addr);
static ulong dns_blacklisted(SOCKET sock, const char* prot, union xp_sockaddr *addr, char* host_name, char* list, char* dnsbl_ip)
{
char fname[MAX_PATH+1];
char str[256];
char* p;
char* tp;
FILE* fp;
ulong found=0;
SAFEPRINTF(fname,"%sdnsbl_exempt.cfg",scfg.ctrl_dir);
if(findstr(host_name,fname))
return(FALSE);
SAFEPRINTF(fname,"%sdns_blacklist.cfg", scfg.ctrl_dir);
if((fp=fopen(fname,"r"))==NULL)
return(FALSE);
while(!feof(fp) && !found) {
if(fgets(str,sizeof(str),fp)==NULL)
break;
truncsp(str);
p=str;
SKIP_WHITESPACE(p);
if(*p==';' || *p==0) /* comment or blank line */
continue;
sprintf(list,"%.100s",p);
/* terminate */
tp = p;
FIND_WHITESPACE(tp);
*tp=0;
found = rblchk(sock, prot, addr, p);
}
fclose(fp);
if(found)
return(found);
}
static BOOL chk_email_addr(SOCKET socket, const char* prot, char* p, char* host_name, char* host_ip
,char* to, char* from, char* source)
{
char addr[64];
char tmp[128];
SKIP_WHITESPACE(p);
char* lt = strchr(p, '<');
if(lt!= NULL)
p = lt+1;
truncstr(addr,">( ");
if(!trashcan(&scfg,addr,"email"))
return(TRUE);
lprintf(LOG_NOTICE,"%04d %s !BLOCKED %s e-mail address: %s"
,socket, prot, source, addr);
SAFEPRINTF2(tmp,"Blocked %s e-mail address: %s", source, addr);
spamlog(&scfg, (char*)prot, "REFUSED", tmp, host_name, host_ip, to, from);
static BOOL email_addr_is_exempt(const char* addr)
{
char fname[MAX_PATH+1];
char netmail[128];
char* p;
if(*addr==0 || strcmp(addr,"<>")==0)
return FALSE;
SAFEPRINTF(fname,"%sdnsbl_exempt.cfg",scfg.ctrl_dir);
if(findstr((char*)addr,fname))
return TRUE;
SAFECOPY(netmail, addr);
if(*(p=netmail)=='<')
p++;
truncstr(p,">");
return userdatdupe(&scfg, 0, U_NETMAIL, LEN_NETMAIL, p, /* del */FALSE, /* next */FALSE, NULL, NULL);
}
static void exempt_email_addr(const char* comment
,const char* sender_info
,const char* toaddr)
{
char fname[MAX_PATH+1];
char to[128];
char tmp[128];
FILE* fp;
if(*toaddr == '<')
SAFECOPY(to, toaddr);
else
SAFEPRINTF(to,"<%s>",toaddr);
if(!email_addr_is_exempt(to)) {
SAFEPRINTF(fname,"%sdnsbl_exempt.cfg",scfg.ctrl_dir);
if((fp=fopen(fname,"a"))==NULL)
lprintf(LOG_ERR,"0000 !Error opening file: %s", fname);
else {
lprintf(LOG_INFO,"0000 %s: %s", comment, to);
fprintf(fp,"\n;%s from %s on %s\n%s\n"
,comment, sender_info
,timestr(&scfg,time32(NULL),tmp), to);
fclose(fp);
}
}
}
{
int file;
if(scfg.smtpmail_sem[0]==0)
return; /* do nothing */
if((file=open(scfg.smtpmail_sem,O_WRONLY|O_CREAT|O_TRUNC,DEFFILEMODE))!=-1)
close(file);
}
/*****************************************************************************/
/* Returns command line generated from instr with %c replacements */
/*****************************************************************************/
static char* mailcmdstr(char* instr, char* msgpath, char* newpath, char* logpath
,char* lstpath, char* errpath
,char* host, char* ip, uint usernum
,char* rcpt_addr
,char* sender, char* sender_addr, char* reverse_path, char* cmd)
{
char str[1024];
int i,j,len;
len=strlen(instr);
for(i=j=0;i<len;i++) {
if(instr[i]=='%') {
i++;
cmd[j]=0;
switch(toupper(instr[i])) {
case 'D':
strcat(cmd,logpath);
break;
case 'E':
strcat(cmd,errpath);
break;
case 'H':
strcat(cmd,host);
break;
case 'I':
strcat(cmd,ip);
break;
case 'G': /* Temp directory */
strcat(cmd,scfg.temp_dir);
break;
case 'J':
strcat(cmd,scfg.data_dir);
break;
case 'K':
strcat(cmd,scfg.ctrl_dir);
break;
case 'L':
strcat(cmd,lstpath);
break;
case 'F':
case 'M':
strcat(cmd,msgpath);
break;
case 'N':
strcat(cmd,newpath);
break;
case 'O': /* SysOp */
strcat(cmd,scfg.sys_op);
break;
case 'Q': /* QWK ID */
strcat(cmd,scfg.sys_id);
break;
case 'R': /* reverse path */
strcat(cmd,reverse_path);
break;
case 'S': /* sender name */
strcat(cmd,sender);
break;
case 'T': /* recipient */
strcat(cmd,rcpt_addr);