--- /dev/null
+#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