/*
DBOX Module
Copyright (C) 1994-2000 Daniel Kroening <kroening@handshake.de>
Purpose:
*/
#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <signal.h>
#include <string>
#include <tools.h>
#include <folder.h>
#include <stringtools.h>
#include <logf.h>
#include <dboxversion.h>
#include <messageid.h>
#ifndef DBOX_STANDALONE
#include <md5.h>
#endif
#include "pop.h"
#include "dboxpop3.h"
void pop3alarm(int signal)
{
cout << "-ERR logout, timeout\r\n";
exit(0);
}
class dboxpop3t:public dboxpopt
{
public:
std::string command, args;
std::basic_string<dword> messages;
std::basic_string<bool> deleted;
dword lastmsg;
std::string timestamp;
void dolist(dword arg, bool only_parameter, bool uidl);
bool logged_in();
void dopop3();
void parse();
void retr_top(dword nr, const char *toparg);
dboxpop3t();
};
dboxpop3t::dboxpop3t():dboxpopt()
{
protocol="pop3";
lastmsg=0;
}
void dboxpop3t::parse()
{
const char *tptr=commandline.c_str();
while(*tptr==' ' || *tptr=='\t') tptr++;
command="";
while(*tptr!=0 && *tptr!=' ' && *tptr!='\t')
command+=*(tptr++);
if(*tptr==' ' || *tptr=='\t')
{
while(*tptr==' ' || *tptr=='\t') tptr++;
args=tptr;
}
else
args="";
}
void dboxpop3t::retr_top(dword nr, const char *toparg)
{
dword lines;
//
// get header and, if necessary, body
//
if(toparg==NULL)
lines=0xffffffff;
else
lines=atol(toparg);
if(toparg==NULL)
cout << "+OK message follows\r\n";
else
cout << "+OK top of message follows\r\n";
if(lines!=0)
get_body(nr);
else
get_header(nr);
// output header
cout.write(rfcm.data, rfcm.headerlen);
// output body
if(lines!=0)
{
const char *ptr=rfcm.data+rfcm.headerlen;
char data=0, lastchar=0;
byte escapestatus=1; // 0=nix, 1=Zeilenanfang, 2=Zeilenanfang+'.'
while(lines!=0 && ptr<rfcm.endofmail)
{
lastchar=data;
data=*(ptr++);
switch(escapestatus)
{
case 0:
if(data=='\r' || data=='\n')
escapestatus=1;
break;
case 1:
if(data=='.')
{
escapestatus=2;
cout << '.'; // Hm. Netscape macht aus ... => ..
}
else if(data=='\r' || data=='\n')
escapestatus=1;
else
escapestatus=0;
break;
case 2:
if(data=='\r' || data=='\n')
escapestatus=1;
else
escapestatus=0;
break;
}
if(data=='\n' && lastchar!='\r')
cout << '\r';
cout << data;
if(data=='\n') lines--;
}
if(data!='\n')
cout << "\r\n";
}
cout << ".\r\n";
}
void dboxpop3t::dolist(dword arg, bool only_parameter, bool uidl)
{
if(only_parameter)
{
if(deleted[arg-1])
{
cout << "-ERR message is deleted\r\n";
return;
}
cout << "+OK ";
}
else
{
if(deleted[arg-1])
return;
}
dword nr=messages[arg-1];
get_folderindex(nr);
cout << arg << " ";
if(uidl)
{
cout << fi.id << "."
<< str2crc(fi.mid) << "."
<< fi.received;
}
else
{
if(!fi.flags.binary)
cout << (fi.len+fi.headerlen);
else
cout << (fi.headerlen+fi.len*1377/1000);
}
cout << "\r\n";
}
bool dboxpop3t::logged_in()
{
if(open_mail(up.name, ""))
{
cout << "-ERR internal error: failed to open mail folder\r\n";
return TRUE;
}
if(index>=0 && header>=0)
{
dword idx=0;
while(read(index, &fi, sizeof fi)==sizeof fi)
{
if(fi.check_version())
{
logf.printf("dboxpopt::login", "mailbox %s corrupted",
up.name);
cout << "-ERR sorry, mailbox is corrupted\r\n";
abort();
}
if(!fi.flags.deleted)
messages.append(1, idx);
idx++;
}
deleted.append(messages.size(), FALSE);
}
return FALSE;
}
#ifndef DBOX_STANDALONE
std::string correct_digest(const std::string ×tamp, const std::string &password)
{
std::string hash_input=timestamp+password;
unsigned char md5hash[16];
md5_calc(md5hash, (unsigned char *)hash_input.c_str(), hash_input.size());
std::string digest;
for(int i=0; i<16; i++)
{
char hex_s[4];
snprintf(hex_s, 3, "%02x", (unsigned char)md5hash[i]);
digest+=hex_s;
}
return digest;
}
#endif
void dboxpop3t::dopop3()
{
signal(SIGALRM, pop3alarm);
timestamp="<"+gen_message_id("pop3")+">";
cout << "+OK DBOX POP3 Server " DBOXVERSIONNR " ready";
#ifndef DBOX_STANDALONE
cout << " " << timestamp;
#endif
cout << "\r\n";
getpeer();
while(TRUE)
{
if(getcommand()) return;
parse();
if(stringcasecmp(command, "QUIT")==0)
{
if(fp.lock())
cout << "-ERR sorry, failed to get a lock\r\n";
else
{
bool crunch=FALSE;
for(unsigned i=0; i<messages.size(); i++)
if(deleted[i])
{
dword nr=messages[i];
lseek(index, nr*sizeof(fi), SEEK_SET);
read(index, &fi, sizeof fi);
if(!fi.flags.deleted)
{
fi.flags.deleted=TRUE;
lseek(index, nr*sizeof(fi), SEEK_SET);
write(index, &fi, sizeof fi);
crunch=TRUE;
}
}
fp.unlock();
cout << "+OK " << config.boxname
<< " DBOX POP Server (Version vom " DBOXVERSION ") shutdown.\r\n";
#ifndef DBOX_STANDALONE // TODO
if(crunch)
docrunch(up.name);
#endif
return;
}
}
else if(stringcasecmp(command, "USER")==0 && !transfer)
{
up.setname(args);
if(up.name[0]==0)
cout << "-ERR invalid user name\r\n";
else
{
dnstring(up.name);
cout << "+OK please send PASS command\r\n";
#ifndef DBOX_STANDALONE
snprintf(userlogfile, DIRLEN, "%slogfile", up.getpath());
#endif
}
}
else if(stringcasecmp(command, "PASS")==0 && !transfer)
{
if(up.name[0]==0)
{
cout << "-ERR please provide an user name first\r\n";
}
else
{
if(login(args))
{
cout << "-ERR invalid password, bye\r\n";
return;
}
if(logged_in())
return;
movemail();
cout << "+OK Welcome\r\n";
}
}
else if(stringcasecmp(command, "STAT")==0 && transfer)
{
if(index<0)
cout << "+OK 0 0\r\n";
else
{
dword total_size=0; // estimated
for(dword arg=1; arg<=messages.size(); arg++)
{
if(!deleted[arg-1])
{
get_folderindex(messages[arg-1]);
if(!fi.flags.binary)
total_size+=fi.len+fi.headerlen;
else
total_size+=fi.headerlen+fi.len*1377/1000; // uuencode
}
}
cout << "+OK " << messages.size() << " "
<< total_size << "\r\n";
}
}
else if(stringcasecmp(command, "LIST")==0 && transfer)
{
if(args=="")
{
cout << "+OK " << messages.size() << " messages\r\n";
for(dword arg=1; arg<=messages.size(); arg++)
dolist(arg, FALSE, FALSE);
cout << ".\r\n";
}
else
{
dword arg=atoi(args.c_str());
if(arg==0 || arg>messages.size())
cout << "-ERR invalid message\r\n";
else
dolist(arg, TRUE, FALSE);
}
}
else if(stringcasecmp(command, "UIDL")==0 && transfer)
{
if(args=="")
{
cout << "+OK " << messages.size() << " messages\r\n";
for(dword arg=1; arg<=messages.size(); arg++)
dolist(arg, FALSE, TRUE);
cout << ".\r\n";
}
else
{
dword arg=atoi(args.c_str());
if(arg==0 || arg>messages.size())
cout << "-ERR invalid message\r\n";
else
dolist(arg, TRUE, TRUE);
}
}
else if((stringcasecmp(command, "RETR")==0 ||
stringcasecmp(command, "TOP")==0) && transfer)
{
const char *toparg=(char *)NULL;
if(stringcasecmp(command, "TOP")==0)
{
toparg=strrchr(args.c_str(), ' ');
if(toparg!=NULL) toparg++;
}
if(args=="")
cout << "-ERR message number required!\r\n";
else
{
dword arg, nr;
arg=atoi(args.c_str());
if(arg==0 || arg>messages.size())
cout << "-ERR invalid message\r\n";
else if(deleted[arg-1])
cout << "-ERR message is deleted\r\n";
else
{
if(arg>lastmsg) lastmsg=arg;
nr=messages[arg-1];
retr_top(nr, toparg);
}
}
}
else if(stringcasecmp(command, "DELE")==0 && transfer)
{
if(args=="")
cout << "-ERR message number required!\r\n";
else
{
dword arg=atoi(args.c_str());
if(arg==0 || arg>messages.size())
cout << "-ERR invalid message\r\n";
else if(deleted[arg-1])
cout << "-ERR message is deleted\r\n";
else
{
if(arg>lastmsg) lastmsg=arg;
deleted[arg-1]=TRUE;
cout << "+OK message deleted.\r\n";
}
}
}
else if(stringcasecmp(command, "NOOP")==0 && transfer)
{
if(args!="")
cout << "-ERR Uh?\r\n";
else
cout << "+OK Gaehn.\r\n";
}
else if(stringcasecmp(command, "APOP")==0)
{
#ifdef DBOX_STANDALONE
cout << "-ERR sorry, no APOP supported\r\n";
#else
char mailbox[TEXTLEN], *tptr;
strmaxcpy(mailbox, args.c_str(), TEXTLEN-1);
tptr=strchr(mailbox, ' ');
if(tptr==NULL)
cout << "-ERR invalid paramters\r\n";
else
{
*tptr=0;
const char *digest=strchr(args.c_str(), ' ');
if(digest!=NULL) digest++; else digest="";
dnstring(mailbox);
up.setname(mailbox);
if(up.read())
{
logf.printf("login", "Invalid user name %s from %s", up.name, peer);
cout << "-ERR invalid user name\r\n";
}
else
{
snprintf(userlogfile, DIRLEN, "%slogfile", up.getpath());
const char *cryptedpassword=up|"cryptedpassword";
if(cryptedpassword[0]==0) cryptedpassword=up|"cryptedpasswort";
if(cryptedpassword[0]==0 || strcmp(cryptedpassword, "locked")==0)
{
logf.printf("login", "apop: account %s locked from %s", up.name, peer);
cout << "-ERR account locked\r\n";
}
else
{
char password[TEXTLEN];
uncryptpassword(cryptedpassword, password);
if(correct_digest(timestamp, password)==digest)
{
logf.printf("login", "%s: correct APOP digest from %s", up.name, peer);
if(login2())
cout << "-ERR permission denied\r\n";
else if(logged_in())
return;
else
cout << "+OK Welcome\r\n";
}
else
{
logf.fileprintf(userlogfile, "login",
"wrong %s APOP digest from %s", protocol.c_str(), peer);
logf.printf("login", "%s: wrong APOP digest from %s", up.name, peer);
cout << "-ERR invalid password\r\n";
}
}
}
}
#endif
}
else if(stringcasecmp(command, "LAST")==0 && transfer)
{
if(args!="")
cout << "-ERR Uh?\r\n";
else
cout << "+OK " << lastmsg << "\r\n";
}
else if(stringcasecmp(command, "RSET")==0 && transfer)
{
if(args!="")
cout << "-ERR Uh?\r\n";
else
{
for(unsigned i=0; i<deleted.size(); i++)
deleted[i]=FALSE;
cout << "+OK\r\n";
}
}
else
cout << "-ERR Invalid command: \"" << command << "\"\r\n";
cout.flush();
}
}
void dopop3()
{
dboxpop3t dboxpop3;
dboxpop3.dopop3();
#if 0
// APOP Example from rfc1939
cout <<
correct_digest("<1896.697170952@dbc.mtview.ca.us>", "tanstaaf")
<< "\n";
#endif
}
syntax highlighted by Code2HTML, v. 0.9.1