version 0.1.0
[fms.git] / src / nntp / nntpconnection.cpp
diff --git a/src/nntp/nntpconnection.cpp b/src/nntp/nntpconnection.cpp
new file mode 100644 (file)
index 0000000..5ee9776
--- /dev/null
@@ -0,0 +1,1186 @@
+#include "../../include/nntp/nntpconnection.h"\r
+#include "../../include/nntp/uwildmat.h"\r
+#include "../../include/stringfunctions.h"\r
+#include "../../include/datetime.h"\r
+#include "../../include/boardlist.h"\r
+#include "../../include/message.h"\r
+#include "../../include/messagelist.h"\r
+#include "../../include/option.h"\r
+\r
+#include <zthread/Thread.h>\r
+\r
+#ifdef XMEM\r
+       #include <xmem.h>\r
+#endif\r
+\r
+NNTPConnection::NNTPConnection(SOCKET sock)\r
+{\r
+       std::string tempval;\r
+\r
+       m_socket=sock;\r
+       m_tempbuffer.resize(32768);\r
+       \r
+       m_status.m_isposting=false;\r
+       m_status.m_allowpost=false;\r
+       m_status.m_boardid=-1;\r
+       m_status.m_messageid=-1;\r
+       m_status.m_mode=ClientMode::MODE_NONE;\r
+\r
+       Option::instance()->Get("NNTPAllowPost",tempval);\r
+       if(tempval=="true")\r
+       {\r
+               m_status.m_allowpost=true;\r
+       }\r
+\r
+}\r
+\r
+NNTPConnection::~NNTPConnection()\r
+{\r
+\r
+}\r
+\r
+void NNTPConnection::Disconnect()\r
+{\r
+       if(m_socket!=INVALID_SOCKET)\r
+       {\r
+       #ifdef _WIN32\r
+               closesocket(m_socket);\r
+       #else\r
+               close(m_socket);\r
+       #endif\r
+               m_socket=INVALID_SOCKET;\r
+       }\r
+}\r
+\r
+std::vector<char>::iterator NNTPConnection::Find(std::vector<char> &buffer, const std::string &val)\r
+{\r
+       return std::search(buffer.begin(),buffer.end(),val.begin(),val.end());\r
+}\r
+\r
+const bool NNTPConnection::HandleArticleCommand(const NNTPCommand &command)\r
+{\r
+\r
+       SendArticleParts(command);\r
+       \r
+       return true;\r
+}\r
+\r
+const bool NNTPConnection::HandleBodyCommand(const NNTPCommand &command)\r
+{\r
+       SendArticleParts(command);\r
+\r
+       return true;\r
+}\r
+\r
+const bool NNTPConnection::HandleCapabilitiesCommand(const NNTPCommand &command)\r
+{\r
+       \r
+       SendBufferedLine("101 Capability list :");\r
+       SendBufferedLine("VERSION 2");\r
+       SendBufferedLine("MODE-READER");\r
+       SendBufferedLine("READER");\r
+       SendBufferedLine("LIST OVERVIEW.FMT");\r
+       SendBufferedLine("OVER MSGID");\r
+       if(m_status.m_allowpost==true)\r
+       {\r
+               SendBufferedLine("POST");\r
+       }\r
+       SendBufferedLine(".");\r
+       \r
+       return true;\r
+}\r
+\r
+const bool NNTPConnection::HandleCommand(const NNTPCommand &command)\r
+{\r
+       if(command.m_command=="QUIT")\r
+       {\r
+               return HandleQuitCommand(command);\r
+       }\r
+       if(command.m_command=="MODE")\r
+       {\r
+               return HandleModeCommand(command);\r
+       }\r
+       if(command.m_command=="CAPABILITIES")\r
+       {\r
+               return HandleCapabilitiesCommand(command);\r
+       }\r
+       if(command.m_command=="HELP")\r
+       {\r
+               return HandleHelpCommand(command);\r
+       }\r
+       if(command.m_command=="DATE")\r
+       {\r
+               return HandleDateCommand(command);\r
+       }\r
+       if(command.m_command=="LIST")\r
+       {\r
+               return HandleListCommand(command);\r
+       }\r
+       if(command.m_command=="GROUP")\r
+       {\r
+               return HandleGroupCommand(command);\r
+       }\r
+       if(command.m_command=="LISTGROUP")\r
+       {\r
+               return HandleListGroupCommand(command);\r
+       }\r
+       if(command.m_command=="LAST")\r
+       {\r
+               return HandleLastCommand(command);\r
+       }\r
+       if(command.m_command=="NEXT")\r
+       {\r
+               return HandleNextCommand(command);\r
+       }\r
+       if(command.m_command=="ARTICLE")\r
+       {\r
+               return HandleArticleCommand(command);\r
+       }\r
+       if(command.m_command=="HEAD")\r
+       {\r
+               return HandleHeadCommand(command);\r
+       }\r
+       if(command.m_command=="BODY")\r
+       {\r
+               return HandleBodyCommand(command);\r
+       }\r
+       if(command.m_command=="STAT")\r
+       {\r
+               return HandleStatCommand(command);\r
+       }\r
+       if(command.m_command=="NEWGROUPS")\r
+       {\r
+               return HandleNewGroupsCommand(command);\r
+       }\r
+       if(command.m_command=="POST")\r
+       {\r
+               return HandlePostCommand(command);\r
+       }\r
+       if(command.m_command=="OVER" || command.m_command=="XOVER")\r
+       {\r
+               return HandleOverCommand(command);\r
+       }\r
+\r
+       return false;\r
+}\r
+\r
+const bool NNTPConnection::HandleDateCommand(const NNTPCommand &command)\r
+{\r
+       DateTime now;\r
+       now.SetToGMTime();\r
+       SendBufferedLine("111 "+now.Format("%Y%m%d%H%M%S"));\r
+       return true;\r
+}\r
+\r
+const bool NNTPConnection::HandleGroupCommand(const NNTPCommand &command)\r
+{\r
+       if(command.m_arguments.size()==1)\r
+       {\r
+               Board board;\r
+               if(board.Load(command.m_arguments[0])==true)\r
+               {\r
+                       std::ostringstream tempstr;\r
+\r
+                       tempstr << "211 " << board.GetMessageCount() << " " << board.GetLowMessageID() << " " << board.GetHighMessageID() << " " << board.GetBoardName();\r
+\r
+                       SendBufferedLine(tempstr.str());\r
+\r
+                       // set the current boardid to this one\r
+                       m_status.m_boardid=board.GetBoardID();\r
+                       //set the first message id, -1 if there are no messages\r
+                       board.GetLowMessageID()!=0 ? m_status.m_messageid=board.GetLowMessageID() : m_status.m_messageid=-1;\r
+\r
+               }\r
+               else\r
+               {\r
+                       SendBufferedLine("411 No such newsgroup");\r
+               }\r
+       }\r
+       else\r
+       {\r
+               SendBufferedLine("501 Syntax error");\r
+               m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleGroupCommand syntax error");\r
+       }\r
+\r
+       return true;\r
+}\r
+\r
+const bool NNTPConnection::HandleHeadCommand(const NNTPCommand &command)\r
+{\r
+       \r
+       SendArticleParts(command);\r
+\r
+       return true;\r
+}\r
+\r
+const bool NNTPConnection::HandleHelpCommand(const NNTPCommand &command)\r
+{\r
+       SendBufferedLine("100 Help text follows");\r
+       SendBufferedLine("There is no help text");\r
+       SendBufferedLine(".");\r
+\r
+       return true;\r
+}\r
+\r
+const bool NNTPConnection::HandleLastCommand(const NNTPCommand &command)\r
+{\r
+       if(m_status.m_boardid!=-1)\r
+       {\r
+               if(m_status.m_messageid!=-1)\r
+               {\r
+                       Message mess;\r
+\r
+                       if(mess.Load(m_status.m_messageid,m_status.m_boardid))\r
+                       {\r
+                               std::ostringstream tempstr;\r
+\r
+                               m_status.m_messageid=mess.GetMessageID();\r
+\r
+                               tempstr << "223 " << mess.GetMessageID() << " " << mess.GetNNTPArticleID();\r
+\r
+                               SendBufferedLine(tempstr.str());\r
+\r
+                       }\r
+                       else\r
+                       {\r
+                               SendBufferedLine("422 No previous article in this group");\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       SendBufferedLine("420 Current article number is invalid");\r
+               }\r
+       }\r
+       else\r
+       {\r
+               SendBufferedLine("412 No newsgroup selected");\r
+       }\r
+\r
+       return true;\r
+}\r
+\r
+const bool NNTPConnection::HandleListCommand(const NNTPCommand &command)\r
+{\r
+\r
+       int type=1;     // default LIST type is active\r
+       std::string arg1="";\r
+       std::string arg2="";\r
+\r
+       // type of LIST\r
+       if(command.m_arguments.size()>0)\r
+       {\r
+               StringFunctions::UpperCase(command.m_arguments[0],arg1);\r
+               if(arg1=="ACTIVE")\r
+               {\r
+                       type=1;\r
+               }\r
+               else if(arg1=="NEWSGROUPS")\r
+               {\r
+                       type=2;\r
+               }\r
+               else if(arg1=="OVERVIEW.FMT")\r
+               {\r
+                       type=3;\r
+               }\r
+               else\r
+               {\r
+                       type=0;\r
+               }\r
+       }\r
+       // wildmat\r
+       if(command.m_arguments.size()>1)\r
+       {\r
+               arg2=command.m_arguments[1];\r
+       }\r
+\r
+       // LIST ACTIVE [wildmat]\r
+       if(type==1)\r
+       {\r
+               bool show;\r
+               std::ostringstream tempstr;\r
+               BoardList bl;\r
+               bl.Load();\r
+\r
+               SendBufferedLine("215 list of newsgroups follows");\r
+\r
+               for(BoardList::iterator i=bl.begin(); i!=bl.end(); i++)\r
+               {\r
+                       show=true;\r
+                       tempstr.str("");\r
+\r
+                       // check wilmat match\r
+                       if(arg2!="")\r
+                       {\r
+                               show=uwildmat((*i).GetBoardName().c_str(),arg2.c_str());\r
+                       }\r
+\r
+                       if(show==true)\r
+                       {\r
+                               tempstr << (*i).GetBoardName() << "\t" << (*i).GetHighMessageID() << "\t" << (*i).GetLowMessageID() << "\t" << (m_status.m_allowpost ? "y" : "n");\r
+                               SendBufferedLine(tempstr.str());\r
+                       }\r
+               }\r
+\r
+               SendBufferedLine(".");\r
+\r
+       }\r
+       // LIST NEWSGROUPS\r
+       else if(type==2)\r
+       {\r
+               bool show;\r
+               std::ostringstream tempstr;\r
+               BoardList bl;\r
+               bl.Load();\r
+\r
+               SendBufferedLine("215 list of newsgroups follows");\r
+\r
+               for(BoardList::iterator i=bl.begin(); i!=bl.end(); i++)\r
+               {\r
+                       show=true;\r
+                       tempstr.str("");\r
+\r
+                       // check wilmat match\r
+                       if(arg2!="")\r
+                       {\r
+                               show=uwildmat((*i).GetBoardName().c_str(),arg2.c_str());\r
+                       }\r
+\r
+                       if(show==true)\r
+                       {\r
+                               tempstr << (*i).GetBoardName() << "\t" << (*i).GetBoardDescription();\r
+                               SendBufferedLine(tempstr.str());\r
+                       }\r
+               }\r
+\r
+               SendBufferedLine(".");\r
+\r
+       }\r
+       // LIST OVERVIEW.FMT\r
+       else if(type==3)\r
+       {\r
+               SendBufferedLine("215 Order of fields in overview database.");\r
+               SendBufferedLine("Subject:");\r
+               SendBufferedLine("From:");\r
+               SendBufferedLine("Date:");\r
+               SendBufferedLine("Message-ID:");\r
+               SendBufferedLine("References:");\r
+               SendBufferedLine(":bytes");\r
+               SendBufferedLine(":lines");\r
+               SendBufferedLine(".");\r
+       }\r
+       else\r
+       {\r
+               // unknown arg\r
+               SendBufferedLine("501 Syntax error");\r
+               m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleListCommand unhandled LIST variant");\r
+       }\r
+\r
+       return true;\r
+}\r
+\r
+const bool NNTPConnection::HandleListGroupCommand(const NNTPCommand &command)\r
+{\r
+\r
+       std::ostringstream tempstr;\r
+       Board board;\r
+       bool validgroup=false;\r
+       int tempint;\r
+       int lownum=-1;\r
+       int highnum=-1;\r
+\r
+       // no args and invalid boardid\r
+       if(command.m_arguments.size()==0 && m_status.m_boardid==-1)\r
+       {\r
+               SendBufferedLine("412 No newsgroup selected");\r
+       }\r
+       else if(command.m_arguments.size()==0)\r
+       {\r
+               validgroup=board.Load(m_status.m_boardid);\r
+       }\r
+       else if(command.m_arguments.size()==1)\r
+       {\r
+               validgroup=board.Load(command.m_arguments[0]);\r
+               if(validgroup)\r
+               {\r
+                       lownum=board.GetLowMessageID();\r
+                       highnum=board.GetHighMessageID();\r
+               }\r
+               else\r
+               {\r
+                       SendBufferedLine("411 No such newsgroup");\r
+               }\r
+       }\r
+       else if(command.m_arguments.size()==2)\r
+       {\r
+               validgroup=board.Load(command.m_arguments[0]);\r
+               std::vector<std::string> rangeparts;\r
+               StringFunctions::Split(command.m_arguments[1],"-",rangeparts);\r
+\r
+               if(rangeparts.size()>0)\r
+               {\r
+                       StringFunctions::Convert(rangeparts[0],lownum);\r
+               }\r
+               if(rangeparts.size()>1)\r
+               {\r
+                       StringFunctions::Convert(rangeparts[1],highnum);\r
+               }\r
+\r
+       }\r
+       else\r
+       {\r
+               // unknown arg\r
+               SendBufferedLine("501 Syntax error");\r
+               m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleListGroupCommand unknown arguments");\r
+       }\r
+\r
+       if(validgroup)\r
+       {\r
+\r
+               // set boardid and messageid\r
+               m_status.m_boardid=board.GetBoardID();\r
+               board.GetLowMessageID()!=0 ? m_status.m_messageid=board.GetLowMessageID() : m_status.m_messageid=-1;\r
+\r
+               if(lownum==-1)\r
+               {\r
+                       lownum=board.GetLowMessageID();\r
+               }\r
+               if(highnum==-1)\r
+               {\r
+                       highnum=board.GetHighMessageID();\r
+               }\r
+\r
+               tempstr << "211 " << board.GetMessageCount() << " " << board.GetLowMessageID() << " " << board.GetHighMessageID() << " " << board.GetBoardName();\r
+               SendBufferedLine(tempstr.str());\r
+\r
+               MessageList ml;\r
+               ml.LoadRange(lownum,highnum,board.GetBoardID());\r
+\r
+               for(std::vector<Message>::iterator i=ml.begin(); i!=ml.end(); i++)\r
+               {\r
+                       tempstr.str("");\r
+                       tempstr << (*i).GetMessageID();\r
+\r
+                       SendBufferedLine(tempstr.str());\r
+               }\r
+\r
+               // end of multi-line response\r
+               SendBufferedLine(".");\r
+\r
+       }\r
+\r
+       return true;\r
+}\r
+\r
+const bool NNTPConnection::HandleModeCommand(const NNTPCommand &command)\r
+{\r
+       if(command.m_arguments.size()>0)\r
+       {\r
+               std::string arg=command.m_arguments[0];\r
+               StringFunctions::UpperCase(arg,arg);\r
+               if(arg=="READER")\r
+               {\r
+                       m_status.m_mode=ClientMode::MODE_READER;\r
+                       if(m_status.m_allowpost==true)\r
+                       {\r
+                               SendBufferedLine("200 Posting allowed");\r
+                       }\r
+                       else\r
+                       {\r
+                               SendBufferedLine("201 Posting prohibited");\r
+                       }\r
+                       \r
+                       m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleModeCommand set mode to reader");\r
+               }\r
+               else\r
+               {\r
+                       SendBufferedLine("501 Syntax error");\r
+                       m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleModeCommand unknown MODE argument : "+arg);\r
+               }\r
+       }\r
+       else\r
+       {\r
+               SendBufferedLine("501 Syntax error");\r
+               m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleModeCommand no argument supplied for MODE");     \r
+       }\r
+\r
+       return true;\r
+}\r
+\r
+const bool NNTPConnection::HandleNewGroupsCommand(const NNTPCommand &command)\r
+{\r
+       if(command.m_arguments.size()>=2)\r
+       {\r
+               DateTime date;\r
+               int tempint;\r
+               if(command.m_arguments[0].size()==8)\r
+               {\r
+                       StringFunctions::Convert(command.m_arguments[0].substr(0,4),tempint);\r
+                       date.SetYear(tempint);\r
+                       StringFunctions::Convert(command.m_arguments[0].substr(4,2),tempint);\r
+                       date.SetMonth(tempint);\r
+                       StringFunctions::Convert(command.m_arguments[0].substr(6,2),tempint);\r
+                       date.SetDay(tempint);\r
+               }\r
+               else\r
+               {\r
+                       /*\r
+                       from RFC 3977\r
+                       If the first two digits of the year are not specified\r
+                       (this is supported only for backward compatibility), the year is to\r
+                       be taken from the current century if yy is smaller than or equal to\r
+                       the current year, and the previous century otherwise.\r
+                       */\r
+                       int century;\r
+                       DateTime now;\r
+                       now.SetToGMTime();\r
+                       century=now.GetYear()-(now.GetYear()%100);\r
+\r
+                       StringFunctions::Convert(command.m_arguments[0].substr(0,2),tempint);\r
+                       tempint<=now.GetYear()-century ? tempint+=century : tempint+=(century-100);\r
+                       \r
+                       //tempint > 50 ? tempint+=1900 : tempint+=2000;\r
+                       \r
+                       date.SetYear(tempint);\r
+                       StringFunctions::Convert(command.m_arguments[0].substr(2,2),tempint);\r
+                       date.SetMonth(tempint);\r
+                       StringFunctions::Convert(command.m_arguments[0].substr(4,2),tempint);\r
+                       date.SetDay(tempint);\r
+               }\r
+\r
+               date.Normalize();\r
+\r
+               BoardList bl;\r
+\r
+               bl.LoadNew(date.Format("%Y-%m-%d %H:%M:%S"));\r
+\r
+               SendBufferedLine("231 List of new newsgroups follows");\r
+\r
+               for(BoardList::iterator i=bl.begin(); i!=bl.end(); i++)\r
+               {\r
+                       std::ostringstream tempstr;\r
+                       tempstr << (*i).GetBoardName() << " " << (*i).GetHighMessageID() << " " << (*i).GetLowMessageID() << " " << m_status.m_allowpost ? "y" : "n";\r
+                       SendBufferedLine(tempstr.str());\r
+               }\r
+\r
+               SendBufferedLine(".");\r
+\r
+       }\r
+       else\r
+       {\r
+               SendBufferedLine("501 Syntax error");\r
+               m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleNewGroupsCommand syntax error");\r
+       }\r
+\r
+       return true;\r
+\r
+}\r
+\r
+const bool NNTPConnection::HandleNextCommand(const NNTPCommand &command)\r
+{\r
+       if(m_status.m_boardid!=-1)\r
+       {\r
+               if(m_status.m_messageid!=-1)\r
+               {\r
+                       Message mess;\r
+\r
+                       if(mess.Load(m_status.m_messageid,m_status.m_boardid))\r
+                       {\r
+                               std::ostringstream tempstr;\r
+\r
+                               m_status.m_messageid=mess.GetMessageID();\r
+\r
+                               tempstr << "223 " << mess.GetMessageID() << " " << mess.GetNNTPArticleID();\r
+\r
+                               SendBufferedLine(tempstr.str());\r
+\r
+                       }\r
+                       else\r
+                       {\r
+                               SendBufferedLine("421 No next article in this group");\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       SendBufferedLine("420 Current article number is invalid");\r
+               }\r
+       }\r
+       else\r
+       {\r
+               SendBufferedLine("412 No newsgroup selected");\r
+       }\r
+\r
+       return true;\r
+\r
+}\r
+\r
+const bool NNTPConnection::HandleOverCommand(const NNTPCommand &command)\r
+{\r
+       long lowmessageid,highmessageid;\r
+       std::string messageuuid="";\r
+\r
+       lowmessageid=highmessageid=-2;\r
+\r
+       if(command.m_arguments.size()==0)\r
+       {\r
+               lowmessageid=m_status.m_messageid;\r
+               highmessageid=m_status.m_messageid;\r
+       }\r
+       else\r
+       {\r
+               // Message-ID\r
+               if(command.m_arguments.size()>0 && command.m_arguments[0].find("<")==0 && command.m_arguments[0].find(">")>0)\r
+               {\r
+                       messageuuid=command.m_arguments[0];\r
+                       messageuuid=StringFunctions::Replace(messageuuid,"<","");\r
+                       messageuuid=StringFunctions::Replace(messageuuid,">","");\r
+               }\r
+               // single article or range\r
+               else\r
+               {\r
+                       // range\r
+                       if(command.m_arguments[0].find("-")!=std::string::npos)\r
+                       {\r
+                               std::vector<std::string> rangeparts;\r
+                               StringFunctions::Split(command.m_arguments[0],"-",rangeparts);\r
+                               // no upper bound\r
+                               if(rangeparts.size()>0)\r
+                               {\r
+                                       StringFunctions::Convert(rangeparts[0],lowmessageid);\r
+                                       highmessageid=-1;\r
+                               }\r
+                               //upper bound\r
+                               else if(rangeparts.size()>1)\r
+                               {\r
+                                       StringFunctions::Convert(rangeparts[1],highmessageid);\r
+                               }\r
+                       }\r
+                       // single\r
+                       else\r
+                       {\r
+                               StringFunctions::Convert(command.m_arguments[0],lowmessageid);\r
+                       }\r
+               }\r
+       }\r
+\r
+       if(messageuuid!="")\r
+       {\r
+               Message mess;\r
+               if(mess.Load(messageuuid))\r
+               {\r
+                       SendBufferedLine("224 Overview information follows");\r
+                       SendArticleOverInfo(mess);\r
+                       SendBufferedLine(".");\r
+               }\r
+               else\r
+               {\r
+                       SendBufferedLine("423 No such article");\r
+               }\r
+       }\r
+       else\r
+       {\r
+               Board bd;\r
+               if(m_status.m_boardid!=-1 && bd.Load(m_status.m_boardid))\r
+               {\r
+                       // single message\r
+                       if(highmessageid==-2)\r
+                       {\r
+                               Message mess;\r
+                               if(mess.Load(lowmessageid,m_status.m_boardid))\r
+                               {\r
+                                       SendBufferedLine("224 Overview information follows");\r
+                                       SendArticleOverInfo(mess);\r
+                                       SendBufferedLine(".");\r
+                               }\r
+                               else\r
+                               {\r
+                                       SendBufferedLine("423 No such article in this group");\r
+                               }\r
+                       }\r
+                       // range with no upper bound\r
+                       else if(highmessageid==-1)\r
+                       {\r
+                               MessageList ml;\r
+                               ml.LoadRange(lowmessageid,bd.GetHighMessageID(),m_status.m_boardid);\r
+                               if(ml.size()>0)\r
+                               {\r
+                                       SendBufferedLine("224 Overview information follows");\r
+                                       for(MessageList::iterator i=ml.begin(); i!=ml.end(); i++)\r
+                                       {\r
+                                               SendArticleOverInfo((*i));\r
+                                       }\r
+                                       SendBufferedLine(".");\r
+                               }\r
+                               else\r
+                               {\r
+                                       SendBufferedLine("423 Empty range");\r
+                               }\r
+                       }\r
+                       // range with upper and lower bound\r
+                       else if(highmessageid>=lowmessageid)\r
+                       {\r
+                               MessageList ml;\r
+                               ml.LoadRange(lowmessageid,highmessageid,m_status.m_boardid);\r
+                               if(ml.size()>0)\r
+                               {\r
+                                       SendBufferedLine("224 Overview information follows");\r
+                                       for(MessageList::iterator i=ml.begin(); i!=ml.end(); i++)\r
+                                       {\r
+                                               SendArticleOverInfo((*i));\r
+                                       }\r
+                                       SendBufferedLine(".");\r
+                               }\r
+                               else\r
+                               {\r
+                                       SendBufferedLine("423 Empty range");\r
+                               }\r
+                       }\r
+                       // invalid range\r
+                       else\r
+                       {\r
+                               SendBufferedLine("423 Empty range");\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       SendBufferedLine("423 No newsgroup selected");\r
+               }\r
+       }\r
+\r
+       return true;\r
+\r
+}\r
+\r
+const bool NNTPConnection::HandlePostCommand(const NNTPCommand &command)\r
+{\r
+       if(m_status.m_allowpost==true)\r
+       {\r
+               SendBufferedLine("340 Send article to be posted");\r
+               m_status.m_isposting=true;\r
+       }\r
+       else\r
+       {\r
+               SendBufferedLine("440 Posting not permitted");\r
+       }\r
+\r
+       return true;\r
+}\r
+\r
+void NNTPConnection::HandlePostedMessage(const std::string &message)\r
+{\r
+       Message mess;\r
+\r
+       if(mess.ParseNNTPMessage(message))\r
+       {\r
+               mess.StartFreenetInsert();\r
+               SendBufferedLine("240 Article received OK");\r
+       }\r
+       else\r
+       {\r
+               SendBufferedLine("441 Posting failed");\r
+       }\r
+}\r
+\r
+void NNTPConnection::HandleReceivedData()\r
+{\r
+       if(m_status.m_isposting==false)\r
+       {\r
+               // get end of command line\r
+               std::vector<char>::iterator endpos=Find(m_receivebuffer,"\r\n");\r
+               \r
+               // we got a command\r
+               if(endpos!=m_receivebuffer.end())\r
+               {\r
+                       NNTPCommand command;\r
+                       std::string commandline(m_receivebuffer.begin(),endpos);\r
+\r
+                       // remove command from receive buffer\r
+                       m_receivebuffer.erase(m_receivebuffer.begin(),endpos+2);\r
+\r
+                       // remove any leading/trailing whitespace\r
+                       commandline=StringFunctions::TrimWhitespace(commandline);\r
+\r
+                       // split out command and arguments separated by space or tab\r
+                       StringFunctions::SplitMultiple(commandline," \t",command.m_arguments);\r
+\r
+                       // command is first element in argument vector\r
+                       command.m_command=command.m_arguments[0];\r
+                       // erase command from argument vector and make it upper case\r
+                       command.m_arguments.erase(command.m_arguments.begin());\r
+                       StringFunctions::UpperCase(command.m_command,command.m_command);\r
+\r
+                       if(HandleCommand(command)==true)\r
+                       {\r
+                               \r
+                       }\r
+                       else\r
+                       {\r
+                               SendBufferedLine("500 Unknown command");\r
+\r
+                               m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleReceivedData received unhandled NNTP command : "+commandline);\r
+                       }\r
+\r
+               }\r
+\r
+       }\r
+       else\r
+       {\r
+               // check for end of post\r
+               std::vector<char>::iterator endpos=Find(m_receivebuffer,"\r\n.\r\n");\r
+\r
+               if(endpos!=m_receivebuffer.end())\r
+               {\r
+                       // get the message\r
+                       std::string message(m_receivebuffer.begin(),endpos);\r
+                       // remove from receive buffer\r
+                       m_receivebuffer.erase(m_receivebuffer.begin(),endpos+5);\r
+\r
+                       // get rid of dot stuffing ( 2 dots on start of a line - used to prevent premature message end in NNTP)\r
+                       message=StringFunctions::Replace(message,"\r\n..","\r\n.");\r
+\r
+                       HandlePostedMessage(message);\r
+\r
+                       // message was received, so posting is completed\r
+                       m_status.m_isposting=false;\r
+\r
+               }\r
+       }\r
+}\r
+\r
+const bool NNTPConnection::HandleStatCommand(const NNTPCommand &command)\r
+{\r
+       SendArticleParts(command);\r
+\r
+       return true;\r
+}\r
+\r
+const bool NNTPConnection::HandleQuitCommand(const NNTPCommand &command)\r
+{\r
+       SendBufferedLine("205 Connection Closing");\r
+       SocketSend();\r
+       Disconnect();\r
+       m_log->WriteLog(LogFile::LOGLEVEL_INFO,"NNTPConnection::HandleQuitCommand client closed connection");\r
+       return true;\r
+}\r
+\r
+void NNTPConnection::run()\r
+{\r
+       struct timeval tv;\r
+       FD_SET writefs,readfs;\r
+       int rval;\r
+\r
+       // seed random number generater for this thread\r
+       srand(time(NULL));\r
+       \r
+       if(m_status.m_allowpost==true)\r
+       {\r
+               SendBufferedLine("200 Service available, posting allowed");\r
+       }\r
+       else\r
+       {\r
+               SendBufferedLine("201 Service available, posting prohibited");\r
+       }\r
+\r
+       do\r
+       {\r
+               FD_ZERO(&readfs);\r
+               FD_ZERO(&writefs);\r
+               \r
+               FD_SET(m_socket,&readfs);\r
+               if(m_sendbuffer.size()>0)\r
+               {\r
+                       FD_SET(m_socket,&writefs);\r
+               }\r
+               \r
+               tv.tv_sec=1;\r
+               tv.tv_usec=0;\r
+               \r
+               rval=select(m_socket+1,&readfs,&writefs,0,&tv);\r
+               \r
+               if(rval>0)\r
+               {\r
+                       if(FD_ISSET(m_socket,&readfs))\r
+                       {\r
+                               SocketReceive();\r
+                               HandleReceivedData();\r
+                       }\r
+                       if(m_socket!=INVALID_SOCKET && FD_ISSET(m_socket,&writefs))\r
+                       {\r
+                               SocketSend();\r
+                       }\r
+               }\r
+               else if(rval==-1)\r
+               {\r
+                       m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"NNTPConnection::run select returned -1 : "+GetSocketErrorMessage());   \r
+               }\r
+\r
+       }while(!Disconnected() && !ZThread::Thread::interrupted());\r
+       \r
+       Disconnect();\r
+       \r
+}\r
+\r
+void NNTPConnection::SendArticleOverInfo(const Message &message)\r
+{\r
+       std::string tempval;\r
+       std::string line;\r
+       std::map<long,std::string> references;\r
+\r
+       StringFunctions::Convert(message.GetMessageID(),tempval);\r
+       line=tempval+"\t";\r
+       line+=message.GetSubject()+"\t";\r
+       line+=message.GetFromName()+"\t";\r
+       line+=message.GetDateTime().Format("%a, %d %b %y %H:%M:%S -0000")+"\t";\r
+       line+=message.GetNNTPArticleID()+"\t";\r
+       references=message.GetInReplyTo();\r
+       if(references.size()>0)\r
+       {\r
+               for(std::map<long,std::string>::const_reverse_iterator i=references.rbegin(); i!=references.rend(); i++)\r
+               {\r
+                       if(i!=references.rbegin())\r
+                       {\r
+                               line+=" ";\r
+                       }\r
+                       line+="<"+(*i).second+">";\r
+               }\r
+               line+="\t";\r
+       }\r
+       else\r
+       {\r
+               line+="\t";\r
+       }\r
+       line+="\t";\r
+\r
+       SendBufferedLine(line);\r
+}\r
+\r
+void NNTPConnection::SendArticleParts(const NNTPConnection::NNTPCommand &command)\r
+{\r
+       bool sendheaders,sendbody;\r
+       std::string successcode;\r
+\r
+       if(command.m_command=="ARTICLE")\r
+       {\r
+               sendheaders=true;\r
+               sendbody=true;\r
+               successcode="220";\r
+       }\r
+       else if(command.m_command=="HEAD")\r
+       {\r
+               sendheaders=true;\r
+               sendbody=false;\r
+               successcode="221";\r
+       }\r
+       else if(command.m_command=="BODY")\r
+       {\r
+               sendheaders=false;\r
+               sendbody=true;\r
+               successcode="222";\r
+       }\r
+       else if(command.m_command=="STAT")\r
+       {\r
+               sendheaders=false;\r
+               sendbody=false;\r
+               successcode="223";\r
+       }\r
+\r
+       Message message;\r
+       int messageid=m_status.m_messageid;\r
+       std::string articleid="";\r
+       int type=0;     // default to current messageid, 1=messageid, 2=articleid\r
+\r
+       if(command.m_arguments.size()==1 && command.m_arguments[0].size()>0)\r
+       {\r
+               if(command.m_arguments[0].find("<")==std::string::npos)\r
+               {\r
+                       StringFunctions::Convert(command.m_arguments[0],messageid);\r
+                       message.Load(messageid,m_status.m_boardid);\r
+                       m_status.m_messageid=message.GetMessageID();\r
+                       type=1;\r
+               }\r
+               else\r
+               {\r
+                       articleid=command.m_arguments[0];\r
+                       message.Load(articleid);\r
+                       type=2;\r
+               }\r
+       }\r
+       else\r
+       {\r
+               message.Load(m_status.m_messageid,m_status.m_boardid);\r
+       }\r
+\r
+       switch(type)\r
+       {\r
+       case 0:\r
+               if(m_status.m_boardid!=-1)\r
+               {\r
+                       if(m_status.m_messageid!=-1)\r
+                       {\r
+                               std::ostringstream tempstr;\r
+                               std::string article;\r
+                               if(sendheaders&&sendbody)\r
+                               {\r
+                                       article=message.GetNNTPHeaders()+"\r\n"+message.GetNNTPBody();\r
+                               }\r
+                               else if(sendheaders && !sendbody)\r
+                               {\r
+                                       article=message.GetNNTPHeaders();\r
+                                       // strip off final \r\n from headers\r
+                                       if(article.rfind("\r\n")==article.size()-2)\r
+                                       {\r
+                                               article.erase(article.size()-2);\r
+                                       }\r
+                               }\r
+                               else\r
+                               {\r
+                                       article=message.GetNNTPBody();\r
+                               }\r
+                               // dot stuff article\r
+                               article=StringFunctions::Replace(article,"\r\n.","\r\n..");\r
+\r
+                               tempstr << successcode << " " << message.GetMessageID() << " " << message.GetNNTPArticleID();\r
+\r
+                               SendBufferedLine(tempstr.str());\r
+                               if(sendheaders || sendbody)\r
+                               {\r
+                                       SendBufferedLine(article);\r
+                                       SendBufferedLine(".");\r
+                               }\r
+\r
+                       }\r
+                       else\r
+                       {\r
+                               SendBufferedLine("420 Current article number is invalid");\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       SendBufferedLine("412 No newsgroup selected");\r
+               }\r
+               break;\r
+       case 1:\r
+               if(m_status.m_boardid!=-1)\r
+               {\r
+                       if(message.GetMessageID()!=-1)\r
+                       {\r
+                               std::ostringstream tempstr;\r
+                               std::string article;\r
+                               if(sendheaders&&sendbody)\r
+                               {\r
+                                       article=message.GetNNTPHeaders()+"\r\n"+message.GetNNTPBody();\r
+                               }\r
+                               else if(sendheaders && !sendbody)\r
+                               {\r
+                                       article=message.GetNNTPHeaders();\r
+                                       // strip off final \r\n from headers\r
+                                       if(article.rfind("\r\n")==article.size()-2)\r
+                                       {\r
+                                               article.erase(article.size()-2);\r
+                                       }\r
+                               }\r
+                               else\r
+                               {\r
+                                       article=message.GetNNTPBody();\r
+                               }\r
+                               // dot stuff article\r
+                               article=StringFunctions::Replace(article,"\r\n.","\r\n..");\r
+\r
+                               tempstr << successcode << " " << message.GetMessageID() << " " << message.GetNNTPArticleID();\r
+\r
+                               SendBufferedLine(tempstr.str());\r
+                               if(sendheaders || sendbody)\r
+                               {\r
+                                       SendBufferedLine(article);\r
+                                       SendBufferedLine(".");\r
+                               }\r
+                       }\r
+                       else\r
+                       {\r
+                               SendBufferedLine("423 No article with that number");\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       SendBufferedLine("412 No newsgroup selected");\r
+               }\r
+               break;\r
+       case 2:\r
+               if(message.GetMessageID()!=-1)\r
+               {\r
+                       std::string article;\r
+                       if(sendheaders&&sendbody)\r
+                       {\r
+                               article=message.GetNNTPHeaders()+"\r\n"+message.GetNNTPBody();\r
+                       }\r
+                       else if(sendheaders && !sendbody)\r
+                       {\r
+                               article=message.GetNNTPHeaders();\r
+                               // strip off final \r\n from headers\r
+                               if(article.rfind("\r\n")==article.size()-2)\r
+                               {\r
+                                       article.erase(article.size()-2);\r
+                               }\r
+                       }\r
+                       else\r
+                       {\r
+                               article=message.GetNNTPBody();\r
+                       }\r
+                       // dot stuff article\r
+                       article=StringFunctions::Replace(article,"\r\n.","\r\n..");\r
+\r
+                       SendBufferedLine(successcode+" 0 "+message.GetNNTPArticleID());\r
+                       if(sendheaders || sendbody)\r
+                       {\r
+                               SendBufferedLine(article);\r
+                               SendBufferedLine(".");\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       SendBufferedLine("430 No article with that message-id");\r
+               }\r
+               break;\r
+       }\r
+\r
+}\r
+\r
+void NNTPConnection::SendBuffered(const std::string &data)\r
+{\r
+       m_sendbuffer.insert(m_sendbuffer.end(),data.begin(),data.end());\r
+}\r
+\r
+void NNTPConnection::SocketReceive()\r
+{\r
+       int rval=recv(m_socket,&m_tempbuffer[0],m_tempbuffer.size(),0);\r
+       if(rval>0)\r
+       {\r
+               m_receivebuffer.insert(m_receivebuffer.end(),m_tempbuffer.begin(),m_tempbuffer.begin()+rval);\r
+       }\r
+       else if(rval==0)\r
+       {\r
+               Disconnect();\r
+               m_log->WriteLog(LogFile::LOGLEVEL_INFO,"NNTPConnection::SocketReceive remote host closed connection");\r
+       }\r
+       else if(rval==-1)\r
+       {\r
+               // error on receive - close the connection\r
+               Disconnect();\r
+               m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"NNTPConnection::SocketReceive recv returned -1 : "+GetSocketErrorMessage());\r
+       }\r
+}\r
+\r
+void NNTPConnection::SocketSend()\r
+{\r
+       if(m_sendbuffer.size()>0 && m_socket!=INVALID_SOCKET)\r
+       {\r
+               int rval=send(m_socket,&m_sendbuffer[0],m_sendbuffer.size(),0);\r
+               if(rval>0)\r
+               {       \r
+                       m_sendbuffer.erase(m_sendbuffer.begin(),m_sendbuffer.begin()+rval);\r
+               }\r
+               else if(rval==-1)\r
+               {\r
+                       m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"NNTPConnection::SocketSend returned -1 : "+GetSocketErrorMessage());\r
+               }\r
+       }\r
+}\r