1 #include "../../include/nntp/nntpconnection.h"
\r
2 #include "../../include/nntp/uwildmat.h"
\r
3 #include "../../include/stringfunctions.h"
\r
4 #include "../../include/boardlist.h"
\r
5 #include "../../include/message.h"
\r
6 #include "../../include/messagelist.h"
\r
7 #include "../../include/option.h"
\r
8 #include "../../include/nntp/extensiontrust.h"
\r
9 #include "../../include/threadwrapper/cancelablethread.h"
\r
11 #include <algorithm>
\r
12 #include <Poco/DateTime.h>
\r
13 #include <Poco/DateTimeFormatter.h>
\r
14 #include <Poco/Timestamp.h>
\r
20 NNTPConnection::NNTPConnection(SOCKET sock)
\r
22 std::string tempval;
\r
25 m_tempbuffer.resize(32768);
\r
27 m_status.m_isposting=false;
\r
28 m_status.m_allowpost=false;
\r
29 m_status.m_boardid=-1;
\r
30 m_status.m_messageid=-1;
\r
31 m_status.m_mode=MODE_NONE;
\r
32 m_status.m_authenticated=false;
\r
34 Option::Instance()->Get("NNTPAllowPost",tempval);
\r
37 m_status.m_allowpost=true;
\r
42 NNTPConnection::~NNTPConnection()
\r
47 void NNTPConnection::Disconnect()
\r
49 if(m_socket!=INVALID_SOCKET)
\r
52 closesocket(m_socket);
\r
56 m_socket=INVALID_SOCKET;
\r
60 std::vector<char>::iterator NNTPConnection::Find(std::vector<char> &buffer, const std::string &val)
\r
62 return std::search(buffer.begin(),buffer.end(),val.begin(),val.end());
\r
65 const bool NNTPConnection::HandleArticleCommand(const NNTPCommand &command)
\r
68 SendArticleParts(command);
\r
73 const bool NNTPConnection::HandleAuthInfoCommand(const NNTPCommand &command)
\r
75 if(command.m_arguments.size()<2)
\r
77 SendBufferedLine("501 Syntax error");
\r
79 else if(m_status.m_authenticated==true)
\r
81 SendBufferedLine("502 Command unavailable"); // not available when already authenticated
\r
85 std::string arg=command.m_arguments[0];
\r
86 StringFunctions::UpperCase(arg,arg);
\r
87 std::string name="";
\r
88 // get remaining args as part of the name since a name might have a space and the args are split on spaces
\r
89 for(std::vector<std::string>::const_iterator i=command.m_arguments.begin()+1; i!=command.m_arguments.end(); i++)
\r
91 // we split on the space, so add it back
\r
92 if(i!=command.m_arguments.begin()+1)
\r
100 LocalIdentity localid;
\r
101 if(localid.Load(name))
\r
103 m_status.m_authuser=localid;
\r
104 m_status.m_authenticated=true;
\r
105 SendBufferedLine("281 Authentication accepted");
\r
109 SendBufferedLine("481 Authentication failed");
\r
112 else if(arg=="PASS")
\r
114 SendBufferedLine("482 Authentication commands issued out of sequence"); // only require username
\r
118 SendBufferedLine("501 Syntax error");
\r
125 const bool NNTPConnection::HandleBodyCommand(const NNTPCommand &command)
\r
127 SendArticleParts(command);
\r
132 const bool NNTPConnection::HandleCapabilitiesCommand(const NNTPCommand &command)
\r
135 SendBufferedLine("101 Capability list :");
\r
136 SendBufferedLine("VERSION 2");
\r
137 if(m_status.m_authenticated==false) // RFC 4643 2.2 0 - don't advertise MODE-READER after authentication
\r
139 SendBufferedLine("MODE-READER");
\r
141 SendBufferedLine("READER");
\r
142 SendBufferedLine("LIST OVERVIEW.FMT");
\r
143 SendBufferedLine("OVER MSGID");
\r
144 if(m_status.m_allowpost==true)
\r
146 SendBufferedLine("POST");
\r
148 if(m_status.m_authenticated==false)
\r
150 SendBufferedLine("AUTHINFO USER");
\r
152 SendBufferedLine("XFMSTRUST");
\r
153 SendBufferedLine(".");
\r
158 const bool NNTPConnection::HandleCommand(const NNTPCommand &command)
\r
160 if(command.m_command=="QUIT")
\r
162 return HandleQuitCommand(command);
\r
164 if(command.m_command=="MODE")
\r
166 return HandleModeCommand(command);
\r
168 if(command.m_command=="CAPABILITIES")
\r
170 return HandleCapabilitiesCommand(command);
\r
172 if(command.m_command=="HELP")
\r
174 return HandleHelpCommand(command);
\r
176 if(command.m_command=="DATE")
\r
178 return HandleDateCommand(command);
\r
180 if(command.m_command=="LIST")
\r
182 return HandleListCommand(command);
\r
184 if(command.m_command=="GROUP")
\r
186 return HandleGroupCommand(command);
\r
188 if(command.m_command=="LISTGROUP")
\r
190 return HandleListGroupCommand(command);
\r
192 if(command.m_command=="LAST")
\r
194 return HandleLastCommand(command);
\r
196 if(command.m_command=="NEXT")
\r
198 return HandleNextCommand(command);
\r
200 if(command.m_command=="ARTICLE")
\r
202 return HandleArticleCommand(command);
\r
204 if(command.m_command=="HEAD")
\r
206 return HandleHeadCommand(command);
\r
208 if(command.m_command=="BODY")
\r
210 return HandleBodyCommand(command);
\r
212 if(command.m_command=="STAT")
\r
214 return HandleStatCommand(command);
\r
216 if(command.m_command=="NEWGROUPS")
\r
218 return HandleNewGroupsCommand(command);
\r
220 if(command.m_command=="POST")
\r
222 return HandlePostCommand(command);
\r
224 if(command.m_command=="OVER" || command.m_command=="XOVER")
\r
226 return HandleOverCommand(command);
\r
228 if(command.m_command=="AUTHINFO")
\r
230 return HandleAuthInfoCommand(command);
\r
232 if(command.m_command=="XGETTRUST")
\r
234 return HandleGetTrustCommand(command);
\r
236 if(command.m_command=="XSETTRUST")
\r
238 return HandleSetTrustCommand(command);
\r
240 if(command.m_command=="XGETTRUSTLIST")
\r
242 return HandleGetTrustListCommand(command);
\r
248 const bool NNTPConnection::HandleDateCommand(const NNTPCommand &command)
\r
250 Poco::DateTime now;
\r
251 SendBufferedLine("111 "+Poco::DateTimeFormatter::format(now,"%Y%m%d%H%M%S"));
\r
255 const bool NNTPConnection::HandleGetTrustCommand(const NNTPCommand &command)
\r
257 if(command.m_arguments.size()>=2)
\r
259 std::string type=command.m_arguments[0];
\r
260 StringFunctions::UpperCase(type,type);
\r
261 if(type=="MESSAGE" || type=="TRUSTLIST" || type=="PEERMESSAGE" || type=="PEERTRUSTLIST")
\r
263 if(m_status.m_authenticated)
\r
267 std::string nntpname="";
\r
268 for(int i=1; i<command.m_arguments.size(); i++)
\r
274 nntpname+=command.m_arguments[i];
\r
277 TrustExtension tr(m_status.m_authuser.GetID());
\r
279 if(type=="MESSAGE")
\r
281 if(tr.GetMessageTrust(nntpname,trust))
\r
286 else if(type=="TRUSTLIST")
\r
288 if(tr.GetTrustListTrust(nntpname,trust))
\r
293 else if(type=="PEERMESSAGE")
\r
295 if(tr.GetPeerMessageTrust(nntpname,trust))
\r
300 else if(type=="PEERTRUSTLIST")
\r
302 if(tr.GetPeerTrustListTrust(nntpname,trust))
\r
308 if(trust>=0 && found)
\r
310 std::string truststr="";
\r
311 StringFunctions::Convert(trust,truststr);
\r
312 SendBufferedLine("280 "+truststr);
\r
316 SendBufferedLine("281 null");
\r
320 SendBufferedLine("480 Identity not found");
\r
326 SendBufferedLine("480 User not authenticated");
\r
331 SendBufferedLine("501 Syntax error");
\r
336 SendBufferedLine("501 Syntax error");
\r
341 const bool NNTPConnection::HandleGetTrustListCommand(const NNTPCommand &command)
\r
343 if(m_status.m_authenticated)
\r
345 TrustExtension tr(m_status.m_authuser.GetID());
\r
346 std::map<std::string,TrustExtension::trust> trustlist;
\r
347 if(tr.GetTrustList(trustlist))
\r
349 SendBufferedLine("280 Trust list follows");
\r
350 for(std::map<std::string,TrustExtension::trust>::iterator i=trustlist.begin(); i!=trustlist.end(); i++)
\r
352 std::ostringstream tempstr;
\r
353 tempstr << (*i).first << "\t";
\r
354 if((*i).second.m_localmessagetrust>-1)
\r
356 tempstr << (*i).second.m_localmessagetrust;
\r
363 if((*i).second.m_localtrustlisttrust>-1)
\r
365 tempstr << (*i).second.m_localtrustlisttrust;
\r
372 if((*i).second.m_peermessagetrust>-1)
\r
374 tempstr << (*i).second.m_peermessagetrust;
\r
381 if((*i).second.m_peertrustlisttrust>-1)
\r
383 tempstr << (*i).second.m_peertrustlisttrust;
\r
390 tempstr << (*i).second.m_messagetrustcomment;
\r
392 tempstr << (*i).second.m_trustlisttrustcomment;
\r
394 SendBufferedLine(tempstr.str());
\r
396 SendBufferedLine(".");
\r
400 SendBufferedLine("501 Syntax error");
\r
405 SendBufferedLine("480 User not authenticated");
\r
410 const bool NNTPConnection::HandleGroupCommand(const NNTPCommand &command)
\r
412 if(command.m_arguments.size()==1)
\r
415 if(board.Load(command.m_arguments[0])==true)
\r
417 std::ostringstream tempstr;
\r
419 tempstr << "211 " << board.GetMessageCount() << " " << board.GetLowMessageID() << " " << board.GetHighMessageID() << " " << board.GetBoardName();
\r
421 SendBufferedLine(tempstr.str());
\r
423 // set the current boardid to this one
\r
424 m_status.m_boardid=board.GetBoardID();
\r
425 //set the first message id, -1 if there are no messages
\r
426 board.GetLowMessageID()!=0 ? m_status.m_messageid=board.GetLowMessageID() : m_status.m_messageid=-1;
\r
431 SendBufferedLine("411 No such newsgroup");
\r
436 SendBufferedLine("501 Syntax error");
\r
437 m_log->debug("NNTPConnection::HandleGroupCommand syntax error");
\r
443 const bool NNTPConnection::HandleHeadCommand(const NNTPCommand &command)
\r
446 SendArticleParts(command);
\r
451 const bool NNTPConnection::HandleHelpCommand(const NNTPCommand &command)
\r
453 SendBufferedLine("100 Help text follows");
\r
454 SendBufferedLine("There is no help text");
\r
455 SendBufferedLine(".");
\r
460 const bool NNTPConnection::HandleLastCommand(const NNTPCommand &command)
\r
462 if(m_status.m_boardid!=-1)
\r
464 if(m_status.m_messageid!=-1)
\r
468 if(mess.LoadPrevious(m_status.m_messageid,m_status.m_boardid))
\r
470 std::ostringstream tempstr;
\r
472 m_status.m_messageid=mess.GetMessageID();
\r
474 tempstr << "223 " << mess.GetMessageID() << " " << mess.GetNNTPArticleID();
\r
476 SendBufferedLine(tempstr.str());
\r
481 SendBufferedLine("422 No previous article in this group");
\r
486 SendBufferedLine("420 Current article number is invalid");
\r
491 SendBufferedLine("412 No newsgroup selected");
\r
497 const bool NNTPConnection::HandleListCommand(const NNTPCommand &command)
\r
500 int type=1; // default LIST type is active
\r
501 std::string arg1="";
\r
502 std::string arg2="";
\r
505 if(command.m_arguments.size()>0)
\r
507 StringFunctions::UpperCase(command.m_arguments[0],arg1);
\r
512 else if(arg1=="NEWSGROUPS")
\r
516 else if(arg1=="OVERVIEW.FMT")
\r
526 if(command.m_arguments.size()>1)
\r
528 arg2=command.m_arguments[1];
\r
531 // LIST ACTIVE [wildmat]
\r
535 std::ostringstream tempstr;
\r
539 SendBufferedLine("215 list of newsgroups follows");
\r
541 for(BoardList::iterator i=bl.begin(); i!=bl.end(); i++)
\r
546 // check wilmat match
\r
549 show=uwildmat((*i).GetBoardName().c_str(),arg2.c_str());
\r
552 if(show==true && (*i).GetSaveReceivedMessages()==true)
\r
554 tempstr << (*i).GetBoardName() << " " << (*i).GetHighMessageID() << " " << (*i).GetLowMessageID() << " " << (m_status.m_allowpost ? "y" : "n");
\r
555 SendBufferedLine(tempstr.str());
\r
559 SendBufferedLine(".");
\r
566 std::ostringstream tempstr;
\r
570 SendBufferedLine("215 list of newsgroups follows");
\r
572 for(BoardList::iterator i=bl.begin(); i!=bl.end(); i++)
\r
577 // check wilmat match
\r
580 show=uwildmat((*i).GetBoardName().c_str(),arg2.c_str());
\r
583 if(show==true && (*i).GetSaveReceivedMessages()==true)
\r
585 tempstr << (*i).GetBoardName() << "\t" << (*i).GetBoardDescription();
\r
586 SendBufferedLine(tempstr.str());
\r
590 SendBufferedLine(".");
\r
593 // LIST OVERVIEW.FMT
\r
596 SendBufferedLine("215 Order of fields in overview database.");
\r
597 SendBufferedLine("Subject:");
\r
598 SendBufferedLine("From:");
\r
599 SendBufferedLine("Date:");
\r
600 SendBufferedLine("Message-ID:");
\r
601 SendBufferedLine("References:");
\r
602 SendBufferedLine(":bytes");
\r
603 SendBufferedLine(":lines");
\r
604 SendBufferedLine(".");
\r
609 SendBufferedLine("501 Syntax error");
\r
610 m_log->debug("NNTPConnection::HandleListCommand unhandled LIST variant");
\r
616 const bool NNTPConnection::HandleListGroupCommand(const NNTPCommand &command)
\r
619 std::ostringstream tempstr;
\r
621 bool validgroup=false;
\r
625 // no args and invalid boardid
\r
626 if(command.m_arguments.size()==0 && m_status.m_boardid==-1)
\r
628 SendBufferedLine("412 No newsgroup selected");
\r
630 else if(command.m_arguments.size()==0)
\r
632 validgroup=board.Load(m_status.m_boardid);
\r
634 else if(command.m_arguments.size()==1)
\r
636 validgroup=board.Load(command.m_arguments[0]);
\r
639 lownum=board.GetLowMessageID();
\r
640 highnum=board.GetHighMessageID();
\r
644 SendBufferedLine("411 No such newsgroup");
\r
647 else if(command.m_arguments.size()==2)
\r
649 validgroup=board.Load(command.m_arguments[0]);
\r
650 std::vector<std::string> rangeparts;
\r
651 StringFunctions::Split(command.m_arguments[1],"-",rangeparts);
\r
653 if(rangeparts.size()>0)
\r
655 StringFunctions::Convert(rangeparts[0],lownum);
\r
657 if(rangeparts.size()>1)
\r
659 StringFunctions::Convert(rangeparts[1],highnum);
\r
666 SendBufferedLine("501 Syntax error");
\r
667 m_log->debug("NNTPConnection::HandleListGroupCommand unknown arguments");
\r
673 // set boardid and messageid
\r
674 m_status.m_boardid=board.GetBoardID();
\r
675 board.GetLowMessageID()!=0 ? m_status.m_messageid=board.GetLowMessageID() : m_status.m_messageid=-1;
\r
679 lownum=board.GetLowMessageID();
\r
683 highnum=board.GetHighMessageID();
\r
686 tempstr << "211 " << board.GetMessageCount() << " " << board.GetLowMessageID() << " " << board.GetHighMessageID() << " " << board.GetBoardName();
\r
687 SendBufferedLine(tempstr.str());
\r
690 ml.LoadRange(lownum,highnum,board.GetBoardID());
\r
692 for(std::vector<Message>::iterator i=ml.begin(); i!=ml.end(); i++)
\r
695 tempstr << (*i).GetMessageID();
\r
697 SendBufferedLine(tempstr.str());
\r
700 // end of multi-line response
\r
701 SendBufferedLine(".");
\r
708 const bool NNTPConnection::HandleModeCommand(const NNTPCommand &command)
\r
710 if(command.m_arguments.size()>0)
\r
712 std::string arg=command.m_arguments[0];
\r
713 StringFunctions::UpperCase(arg,arg);
\r
716 m_status.m_mode=MODE_READER;
\r
717 if(m_status.m_allowpost==true)
\r
719 SendBufferedLine("200 Posting allowed");
\r
723 SendBufferedLine("201 Posting prohibited");
\r
726 m_log->debug("NNTPConnection::HandleModeCommand set mode to reader");
\r
730 SendBufferedLine("501 Syntax error");
\r
731 m_log->debug("NNTPConnection::HandleModeCommand unknown MODE argument : "+arg);
\r
736 SendBufferedLine("501 Syntax error");
\r
737 m_log->debug("NNTPConnection::HandleModeCommand no argument supplied for MODE");
\r
743 const bool NNTPConnection::HandleNewGroupsCommand(const NNTPCommand &command)
\r
745 if(command.m_arguments.size()>=2)
\r
747 Poco::DateTime date;
\r
751 if(command.m_arguments[0].size()==8)
\r
753 StringFunctions::Convert(command.m_arguments[0].substr(0,4),tempyear);
\r
754 StringFunctions::Convert(command.m_arguments[0].substr(4,2),tempmonth);
\r
755 StringFunctions::Convert(command.m_arguments[0].substr(6,2),tempday);
\r
758 date.assign(tempyear,tempmonth,tempday,date.hour(),date.minute(),date.second());
\r
762 m_log->fatal("NNTPConnection::HandleNewGroupsCommand error assigning date");
\r
769 If the first two digits of the year are not specified
\r
770 (this is supported only for backward compatibility), the year is to
\r
771 be taken from the current century if yy is smaller than or equal to
\r
772 the current year, and the previous century otherwise.
\r
775 Poco::DateTime now;
\r
776 century=now.year()-(now.year()%100);
\r
778 StringFunctions::Convert(command.m_arguments[0].substr(0,2),tempyear);
\r
779 tempyear<=now.year()-century ? tempyear+=century : tempyear+=(century-100);
\r
781 //tempint > 50 ? tempint+=1900 : tempint+=2000;
\r
783 StringFunctions::Convert(command.m_arguments[0].substr(2,2),tempmonth);
\r
784 StringFunctions::Convert(command.m_arguments[0].substr(4,2),tempday);
\r
787 date.assign(tempyear,tempmonth,tempday);
\r
791 m_log->fatal("NNTPConnection::HandleNewGroupsCommand error assigning date");
\r
797 bl.LoadNew(Poco::DateTimeFormatter::format(date,"%Y-%m-%d %H:%M:%S"));
\r
799 SendBufferedLine("231 List of new newsgroups follows");
\r
801 for(BoardList::iterator i=bl.begin(); i!=bl.end(); i++)
\r
803 if((*i).GetSaveReceivedMessages()==true)
\r
805 std::ostringstream tempstr;
\r
806 tempstr << (*i).GetBoardName() << " " << (*i).GetHighMessageID() << " " << (*i).GetLowMessageID() << " " << m_status.m_allowpost ? "y" : "n";
\r
807 SendBufferedLine(tempstr.str());
\r
811 SendBufferedLine(".");
\r
816 SendBufferedLine("501 Syntax error");
\r
817 m_log->debug("NNTPConnection::HandleNewGroupsCommand syntax error");
\r
824 const bool NNTPConnection::HandleNextCommand(const NNTPCommand &command)
\r
826 if(m_status.m_boardid!=-1)
\r
828 if(m_status.m_messageid!=-1)
\r
832 if(mess.LoadNext(m_status.m_messageid,m_status.m_boardid))
\r
834 std::ostringstream tempstr;
\r
836 m_status.m_messageid=mess.GetMessageID();
\r
838 tempstr << "223 " << mess.GetMessageID() << " " << mess.GetNNTPArticleID();
\r
840 SendBufferedLine(tempstr.str());
\r
845 SendBufferedLine("421 No next article in this group");
\r
850 SendBufferedLine("420 Current article number is invalid");
\r
855 SendBufferedLine("412 No newsgroup selected");
\r
862 const bool NNTPConnection::HandleOverCommand(const NNTPCommand &command)
\r
864 long lowmessageid,highmessageid;
\r
865 std::string messageuuid="";
\r
867 lowmessageid=highmessageid=-2;
\r
869 if(command.m_arguments.size()==0)
\r
871 lowmessageid=m_status.m_messageid;
\r
872 highmessageid=m_status.m_messageid;
\r
877 if(command.m_arguments.size()>0 && command.m_arguments[0].find("<")==0 && command.m_arguments[0].find(">")>0)
\r
879 messageuuid=command.m_arguments[0];
\r
880 messageuuid=StringFunctions::Replace(messageuuid,"<","");
\r
881 messageuuid=StringFunctions::Replace(messageuuid,">","");
\r
883 // get rid of @ and everything after
\r
884 if(messageuuid.find("@")!=std::string::npos)
\r
886 messageuuid.erase(messageuuid.find("@"));
\r
890 // single article or range
\r
894 if(command.m_arguments[0].find("-")!=std::string::npos)
\r
896 std::vector<std::string> rangeparts;
\r
897 StringFunctions::Split(command.m_arguments[0],"-",rangeparts);
\r
899 if(rangeparts.size()>0)
\r
901 StringFunctions::Convert(rangeparts[0],lowmessageid);
\r
905 else if(rangeparts.size()>1)
\r
907 StringFunctions::Convert(rangeparts[1],highmessageid);
\r
913 StringFunctions::Convert(command.m_arguments[0],lowmessageid);
\r
918 if(messageuuid!="")
\r
921 if(mess.Load(messageuuid))
\r
923 SendBufferedLine("224 Overview information follows");
\r
924 SendArticleOverInfo(mess);
\r
925 SendBufferedLine(".");
\r
929 SendBufferedLine("423 No such article");
\r
935 if(m_status.m_boardid!=-1 && bd.Load(m_status.m_boardid))
\r
938 if(highmessageid==-2)
\r
941 if(mess.Load(lowmessageid,m_status.m_boardid))
\r
943 SendBufferedLine("224 Overview information follows");
\r
944 SendArticleOverInfo(mess);
\r
945 SendBufferedLine(".");
\r
949 SendBufferedLine("423 No such article in this group");
\r
952 // range with no upper bound
\r
953 else if(highmessageid==-1)
\r
956 ml.LoadRange(lowmessageid,bd.GetHighMessageID(),m_status.m_boardid);
\r
959 SendBufferedLine("224 Overview information follows");
\r
960 for(MessageList::iterator i=ml.begin(); i!=ml.end(); i++)
\r
962 SendArticleOverInfo((*i));
\r
964 SendBufferedLine(".");
\r
968 SendBufferedLine("423 Empty range");
\r
971 // range with upper and lower bound
\r
972 else if(highmessageid>=lowmessageid)
\r
975 ml.LoadRange(lowmessageid,highmessageid,m_status.m_boardid);
\r
978 SendBufferedLine("224 Overview information follows");
\r
979 for(MessageList::iterator i=ml.begin(); i!=ml.end(); i++)
\r
981 SendArticleOverInfo((*i));
\r
983 SendBufferedLine(".");
\r
987 SendBufferedLine("423 Empty range");
\r
993 SendBufferedLine("423 Empty range");
\r
998 SendBufferedLine("423 No newsgroup selected");
\r
1006 const bool NNTPConnection::HandlePostCommand(const NNTPCommand &command)
\r
1008 if(m_status.m_allowpost==true)
\r
1010 SendBufferedLine("340 Send article to be posted");
\r
1011 m_status.m_isposting=true;
\r
1015 SendBufferedLine("440 Posting not permitted");
\r
1021 void NNTPConnection::HandlePostedMessage(const std::string &message)
\r
1025 if(mess.ParseNNTPMessage(message))
\r
1027 // if we authenticated, set the username to the authenticated user
\r
1028 if(m_status.m_authenticated)
\r
1030 mess.SetFromName(m_status.m_authuser.GetName());
\r
1032 // handle a messages posted to an adminboard
\r
1033 if(mess.PostedToAdministrationBoard()==true)
\r
1035 mess.HandleAdministrationMessage();
\r
1037 if(mess.StartFreenetInsert())
\r
1039 SendBufferedLine("240 Article received OK");
\r
1043 SendBufferedLine("441 Posting failed. Make sure the identity you are sending with exists!");
\r
1048 SendBufferedLine("441 Posting failed");
\r
1052 void NNTPConnection::HandleReceivedData()
\r
1054 if(m_status.m_isposting==false)
\r
1056 // get end of command line
\r
1057 std::vector<char>::iterator endpos=Find(m_receivebuffer,"\r\n");
\r
1059 // we got a command
\r
1060 if(endpos!=m_receivebuffer.end())
\r
1062 NNTPCommand command;
\r
1063 std::string commandline(m_receivebuffer.begin(),endpos);
\r
1065 // remove command from receive buffer
\r
1066 m_receivebuffer.erase(m_receivebuffer.begin(),endpos+2);
\r
1068 // remove any leading/trailing whitespace
\r
1069 commandline=StringFunctions::TrimWhitespace(commandline);
\r
1071 // split out command and arguments separated by space or tab
\r
1072 StringFunctions::SplitMultiple(commandline," \t",command.m_arguments);
\r
1074 // command is first element in argument vector
\r
1075 command.m_command=command.m_arguments[0];
\r
1076 // erase command from argument vector and make it upper case
\r
1077 command.m_arguments.erase(command.m_arguments.begin());
\r
1078 StringFunctions::UpperCase(command.m_command,command.m_command);
\r
1080 if(HandleCommand(command)==true)
\r
1086 SendBufferedLine("500 Unknown command");
\r
1088 m_log->debug("NNTPConnection::HandleReceivedData received unhandled NNTP command : "+commandline);
\r
1096 // check for end of post
\r
1097 std::vector<char>::iterator endpos=Find(m_receivebuffer,"\r\n.\r\n");
\r
1099 if(endpos!=m_receivebuffer.end())
\r
1101 // get the message
\r
1102 std::string message(m_receivebuffer.begin(),endpos);
\r
1103 // remove from receive buffer
\r
1104 m_receivebuffer.erase(m_receivebuffer.begin(),endpos+5);
\r
1106 // get rid of dot stuffing ( 2 dots on start of a line - used to prevent premature message end in NNTP)
\r
1107 message=StringFunctions::Replace(message,"\r\n..","\r\n.");
\r
1109 HandlePostedMessage(message);
\r
1111 // message was received, so posting is completed
\r
1112 m_status.m_isposting=false;
\r
1118 const bool NNTPConnection::HandleSetTrustCommand(const NNTPCommand &command)
\r
1120 if(command.m_arguments.size()>=3)
\r
1122 std::string type=command.m_arguments[0];
\r
1123 StringFunctions::UpperCase(type,type);
\r
1124 if(type=="MESSAGE" || type=="TRUSTLIST" || type=="MESSAGECOMMENT" || type=="TRUSTLISTCOMMENT")
\r
1126 if(m_status.m_authenticated)
\r
1131 std::string comment="";
\r
1132 std::string nntpname="";
\r
1134 if(type=="MESSAGE" || type=="TRUSTLIST")
\r
1136 for(int i=1; i<command.m_arguments.size()-1; i++)
\r
1142 nntpname+=command.m_arguments[i];
\r
1145 if(command.m_arguments[command.m_arguments.size()-1]!="null")
\r
1147 StringFunctions::Convert(command.m_arguments[command.m_arguments.size()-1],trust);
\r
1150 if(trust>=-1 && trust<=100)
\r
1159 for(int i=1; i<command.m_arguments.size() && startpos==-1; i++)
\r
1161 if(command.m_arguments[i].size()>0 && command.m_arguments[i][0]!='\"')
\r
1167 nntpname+=command.m_arguments[i];
\r
1176 for(int i=startpos; i<command.m_arguments.size(); i++)
\r
1182 comment+=command.m_arguments[i];
\r
1184 // strip " from comment beginning and end
\r
1185 if(comment.size()>0 && comment[0]=='\"')
\r
1187 comment.erase(0,1);
\r
1189 if(comment.size()>0 && comment[comment.size()-1]=='\"')
\r
1191 comment.erase(comment.size()-1);
\r
1197 TrustExtension tr(m_status.m_authuser.GetID());
\r
1199 if(type=="MESSAGE")
\r
1201 if(tr.SetMessageTrust(nntpname,trust))
\r
1206 if(type=="TRUSTLIST")
\r
1208 if(tr.SetTrustListTrust(nntpname,trust))
\r
1213 if(type=="MESSAGECOMMENT")
\r
1215 if(tr.SetMessageTrustComment(nntpname,comment))
\r
1220 if(type=="TRUSTLISTCOMMENT")
\r
1222 if(tr.SetTrustListTrustComment(nntpname,comment))
\r
1228 if(found && valid)
\r
1230 SendBufferedLine("280 Trust Set");
\r
1232 else if(found==false)
\r
1234 SendBufferedLine("480 Identity not found");
\r
1238 SendBufferedLine("501 Syntax error");
\r
1244 SendBufferedLine("480 User not authenticated");
\r
1249 SendBufferedLine("501 Syntax error");
\r
1254 SendBufferedLine("501 Syntax error");
\r
1259 const bool NNTPConnection::HandleStatCommand(const NNTPCommand &command)
\r
1261 SendArticleParts(command);
\r
1266 const bool NNTPConnection::HandleQuitCommand(const NNTPCommand &command)
\r
1268 SendBufferedLine("205 Connection Closing");
\r
1271 m_log->information("NNTPConnection::HandleQuitCommand client closed connection");
\r
1275 void NNTPConnection::run()
\r
1277 struct timeval tv;
\r
1278 fd_set writefs,readfs;
\r
1281 // seed random number generater for this thread
\r
1282 srand(time(NULL));
\r
1284 if(m_status.m_allowpost==true)
\r
1286 SendBufferedLine("200 Service available, posting allowed");
\r
1290 SendBufferedLine("201 Service available, posting prohibited");
\r
1296 FD_ZERO(&writefs);
\r
1298 FD_SET(m_socket,&readfs);
\r
1299 if(m_sendbuffer.size()>0)
\r
1301 FD_SET(m_socket,&writefs);
\r
1307 rval=select(m_socket+1,&readfs,&writefs,0,&tv);
\r
1311 if(FD_ISSET(m_socket,&readfs))
\r
1314 HandleReceivedData();
\r
1316 if(m_socket!=INVALID_SOCKET && FD_ISSET(m_socket,&writefs))
\r
1321 else if(rval==SOCKET_ERROR)
\r
1323 m_log->error("NNTPConnection::run select returned -1 : "+GetSocketErrorMessage());
\r
1326 }while(!Disconnected() && !IsCancelled());
\r
1332 void NNTPConnection::SendArticleOverInfo(Message &message)
\r
1334 std::string tempval;
\r
1336 std::map<long,std::string> references;
\r
1338 StringFunctions::Convert(message.GetMessageID(),tempval);
\r
1339 line=tempval+"\t";
\r
1340 line+=message.GetSubject()+"\t";
\r
1341 line+=message.GetFromName()+"\t";
\r
1342 line+=Poco::DateTimeFormatter::format(message.GetDateTime(),"%w, %d %b %y %H:%M:%S -0000")+"\t";
\r
1343 line+=message.GetNNTPArticleID()+"\t";
\r
1344 references=message.GetInReplyTo();
\r
1345 if(references.size()>0)
\r
1347 for(std::map<long,std::string>::reverse_iterator i=references.rbegin(); i!=references.rend(); i++)
\r
1349 if(i!=references.rbegin())
\r
1353 line+="<"+(*i).second+">"; //+"@freenetproject.org>";
\r
1363 SendBufferedLine(line);
\r
1366 void NNTPConnection::SendArticleParts(const NNTPConnection::NNTPCommand &command)
\r
1368 bool sendheaders,sendbody;
\r
1369 std::string successcode;
\r
1371 if(command.m_command=="ARTICLE")
\r
1375 successcode="220";
\r
1377 else if(command.m_command=="HEAD")
\r
1381 successcode="221";
\r
1383 else if(command.m_command=="BODY")
\r
1385 sendheaders=false;
\r
1387 successcode="222";
\r
1389 else if(command.m_command=="STAT")
\r
1391 sendheaders=false;
\r
1393 successcode="223";
\r
1397 int messageid=m_status.m_messageid;
\r
1398 std::string articleid="";
\r
1399 int type=0; // default to current messageid, 1=messageid, 2=articleid
\r
1401 if(command.m_arguments.size()==1 && command.m_arguments[0].size()>0)
\r
1403 if(command.m_arguments[0].find("<")==std::string::npos)
\r
1405 StringFunctions::Convert(command.m_arguments[0],messageid);
\r
1406 message.Load(messageid,m_status.m_boardid);
\r
1407 m_status.m_messageid=message.GetMessageID();
\r
1412 articleid=command.m_arguments[0];
\r
1413 //strip off < and > and everthing after @
\r
1414 if(articleid.size()>0 && articleid[0]=='<')
\r
1416 articleid.erase(0,1);
\r
1418 if(articleid.size()>0 && articleid[articleid.size()-1]=='>')
\r
1420 articleid.erase(articleid.size()-1);
\r
1423 if(articleid.size()>0 && articleid.find('@')!=std::string::npos)
\r
1425 articleid.erase(articleid.find('@'));
\r
1428 message.Load(articleid);
\r
1434 message.Load(m_status.m_messageid,m_status.m_boardid);
\r
1440 if(m_status.m_boardid!=-1)
\r
1442 if(m_status.m_messageid!=-1)
\r
1444 std::ostringstream tempstr;
\r
1445 std::string article;
\r
1446 if(sendheaders&&sendbody)
\r
1448 article=message.GetNNTPHeaders()+"\r\n"+message.GetNNTPBody();
\r
1450 else if(sendheaders && !sendbody)
\r
1452 article=message.GetNNTPHeaders();
\r
1453 // strip off final \r\n from headers
\r
1454 if(article.rfind("\r\n")==article.size()-2)
\r
1456 article.erase(article.size()-2);
\r
1461 article=message.GetNNTPBody();
\r
1463 // dot stuff article
\r
1464 article=StringFunctions::Replace(article,"\r\n.","\r\n..");
\r
1466 tempstr << successcode << " " << message.GetMessageID() << " " << message.GetNNTPArticleID();
\r
1468 SendBufferedLine(tempstr.str());
\r
1469 if(sendheaders || sendbody)
\r
1471 SendBufferedLine(article);
\r
1472 SendBufferedLine(".");
\r
1478 SendBufferedLine("420 Current article number is invalid");
\r
1483 SendBufferedLine("412 No newsgroup selected");
\r
1487 if(m_status.m_boardid!=-1)
\r
1489 if(message.GetMessageID()!=-1)
\r
1491 std::ostringstream tempstr;
\r
1492 std::string article;
\r
1493 if(sendheaders&&sendbody)
\r
1495 article=message.GetNNTPHeaders()+"\r\n"+message.GetNNTPBody();
\r
1497 else if(sendheaders && !sendbody)
\r
1499 article=message.GetNNTPHeaders();
\r
1500 // strip off final \r\n from headers
\r
1501 if(article.rfind("\r\n")==article.size()-2)
\r
1503 article.erase(article.size()-2);
\r
1508 article=message.GetNNTPBody();
\r
1510 // dot stuff article
\r
1511 article=StringFunctions::Replace(article,"\r\n.","\r\n..");
\r
1513 tempstr << successcode << " " << message.GetMessageID() << " " << message.GetNNTPArticleID();
\r
1515 SendBufferedLine(tempstr.str());
\r
1516 if(sendheaders || sendbody)
\r
1518 SendBufferedLine(article);
\r
1519 SendBufferedLine(".");
\r
1524 SendBufferedLine("423 No article with that number");
\r
1529 SendBufferedLine("412 No newsgroup selected");
\r
1533 if(message.GetMessageID()!=-1)
\r
1535 std::string article;
\r
1536 if(sendheaders&&sendbody)
\r
1538 article=message.GetNNTPHeaders()+"\r\n"+message.GetNNTPBody();
\r
1540 else if(sendheaders && !sendbody)
\r
1542 article=message.GetNNTPHeaders();
\r
1543 // strip off final \r\n from headers
\r
1544 if(article.rfind("\r\n")==article.size()-2)
\r
1546 article.erase(article.size()-2);
\r
1551 article=message.GetNNTPBody();
\r
1553 // dot stuff article
\r
1554 article=StringFunctions::Replace(article,"\r\n.","\r\n..");
\r
1556 SendBufferedLine(successcode+" 0 "+message.GetNNTPArticleID());
\r
1557 if(sendheaders || sendbody)
\r
1559 SendBufferedLine(article);
\r
1560 SendBufferedLine(".");
\r
1565 SendBufferedLine("430 No article with that message-id");
\r
1572 void NNTPConnection::SendBuffered(const std::string &data)
\r
1574 m_sendbuffer.insert(m_sendbuffer.end(),data.begin(),data.end());
\r
1577 void NNTPConnection::SocketReceive()
\r
1579 int rval=recv(m_socket,&m_tempbuffer[0],m_tempbuffer.size(),0);
\r
1582 m_receivebuffer.insert(m_receivebuffer.end(),m_tempbuffer.begin(),m_tempbuffer.begin()+rval);
\r
1587 m_log->information("NNTPConnection::SocketReceive remote host closed connection");
\r
1591 std::string errnostr;
\r
1592 StringFunctions::Convert(GetSocketErrorNumber(),errnostr);
\r
1593 // error on receive - close the connection
\r
1595 m_log->error("NNTPConnection::SocketReceive recv returned -1 : "+errnostr+" - "+GetSocketErrorMessage());
\r
1599 void NNTPConnection::SocketSend()
\r
1601 if(m_sendbuffer.size()>0 && m_socket!=INVALID_SOCKET)
\r
1603 int rval=send(m_socket,&m_sendbuffer[0],m_sendbuffer.size(),0);
\r
1606 m_sendbuffer.erase(m_sendbuffer.begin(),m_sendbuffer.begin()+rval);
\r
1610 std::string errnostr;
\r
1611 StringFunctions::Convert(GetSocketErrorNumber(),errnostr);
\r
1612 m_log->error("NNTPConnection::SocketSend returned -1 : "+errnostr+" - "+GetSocketErrorMessage());
\r