version 0.3.33
[fms.git] / src / freenet / frostmessagerequester.cpp
diff --git a/src/freenet/frostmessagerequester.cpp b/src/freenet/frostmessagerequester.cpp
new file mode 100644 (file)
index 0000000..9b933cf
--- /dev/null
@@ -0,0 +1,351 @@
+#include "../../include/freenet/frostmessagerequester.h"\r
+#include "../../include/freenet/frostidentity.h"\r
+#include "../../include/freenet/frostmessagexml.h"\r
+\r
+#include <Poco/DateTime.h>\r
+#include <Poco/DateTimeParser.h>\r
+#include <Poco/DateTimeFormatter.h>\r
+#include <Poco/Timespan.h>\r
+\r
+FrostMessageRequester::FrostMessageRequester(SQLite3DB::DB *db):IIndexRequester<std::string>(db)\r
+{\r
+       Initialize();\r
+}\r
+\r
+FrostMessageRequester::FrostMessageRequester(SQLite3DB::DB *db, FCPv2::Connection *fcp):IIndexRequester<std::string>(db,fcp)\r
+{\r
+       Initialize();\r
+}\r
+\r
+const bool FrostMessageRequester::HandleAllData(FCPv2::Message &message)\r
+{\r
+       std::vector<std::string> idparts;\r
+       std::vector<char> data;\r
+       long datalength=0;\r
+       FrostMessageXML xml;\r
+       FrostIdentity frostid;\r
+       bool validmessage=true;\r
+       bool inserted=false;\r
+\r
+       StringFunctions::Split(message["Identifier"],"|",idparts);\r
+       StringFunctions::Convert(message["DataLength"],datalength);\r
+\r
+       // wait for all data to be received from connection\r
+       m_fcp->WaitForBytes(1000,datalength);\r
+\r
+       // if we got disconnected- return immediately\r
+       if(m_fcp->IsConnected()==false)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       // receive the file\r
+       m_fcp->Receive(data,datalength);\r
+\r
+       // mark this index as received\r
+       SQLite3DB::Statement st=m_db->Prepare("INSERT INTO tblFrostMessageRequests(BoardID,Day,RequestIndex,Found) VALUES(?,?,?,'true');");\r
+       st.Bind(0,idparts[1]);\r
+       st.Bind(1,idparts[3]);\r
+       st.Bind(2,idparts[2]);\r
+       st.Step();\r
+\r
+       if(data.size()>0 && xml.ParseXML(std::string(data.begin(),data.end()))==true)\r
+       {\r
+               std::vector<std::string> boards=xml.GetBoards();\r
+               std::map<long,std::string> replyto=xml.GetInReplyTo();\r
+\r
+               if(xml.GetFrostAuthor()!="Anonymous" && frostid.FromPublicKey(xml.GetFrostPublicKey())==false)\r
+               {\r
+                       m_log->debug("FrostMessageRequester::HandleAllData error with public key "+xml.GetFrostPublicKey());\r
+                       validmessage=false;\r
+               }\r
+\r
+               if(validmessage && xml.GetFrostAuthor()!="Anonymous" && frostid.VerifyAuthor(xml.GetFrostAuthor())==false)\r
+               {\r
+                       m_log->debug("FrostMessageRequester::HandleAllData error with author "+xml.GetFrostAuthor());\r
+                       validmessage=false;\r
+               }\r
+\r
+               std::string contentv2=xml.GetSignableContentV2();\r
+               if(validmessage && xml.GetFrostAuthor()!="Anonymous" && frostid.VerifySignature(std::vector<unsigned char>(contentv2.begin(),contentv2.end()),xml.GetFrostSignatureV2())==false)\r
+               {\r
+                       m_log->debug("FrostMessageRequester::HandleAllData error with signature "+xml.GetFrostSignatureV2());\r
+                       validmessage=false;\r
+               }\r
+\r
+               if(xml.GetFrostAuthor()=="Anonymous" && m_saveanonymous==false)\r
+               {\r
+                       validmessage=false;\r
+               }\r
+\r
+               if(validmessage==true)\r
+               {\r
+                       std::string nntpbody="";\r
+                       nntpbody=xml.GetBody();\r
+\r
+                       //add file keys/sizes to body\r
+                       std::vector<MessageXML::fileattachment> fileattachments=xml.GetFileAttachments();\r
+                       if(fileattachments.size()>0)\r
+                       {\r
+                               nntpbody+="\r\nAttachments";\r
+                       }\r
+                       for(std::vector<MessageXML::fileattachment>::iterator i=fileattachments.begin(); i!=fileattachments.end(); i++)\r
+                       {\r
+                               std::string sizestr="0";\r
+                               StringFunctions::Convert((*i).m_size,sizestr);\r
+\r
+                               nntpbody+="\r\n"+(*i).m_key;\r
+                               nntpbody+="\r\n"+sizestr+" bytes";\r
+                               nntpbody+="\r\n";\r
+                       }\r
+\r
+                       m_db->Execute("BEGIN;");\r
+\r
+                       st=m_db->Prepare("INSERT INTO tblMessage(FromName,MessageDate,MessageTime,Subject,MessageUUID,ReplyBoardID,Body,InsertDate,MessageIndex) VALUES(?,?,?,?,?,?,?,?,?);");\r
+                       st.Bind(0,xml.GetFrostAuthor());\r
+                       st.Bind(1,xml.GetDate());\r
+                       st.Bind(2,xml.GetTime());\r
+                       st.Bind(3,xml.GetSubject());\r
+                       st.Bind(4,xml.GetMessageID());\r
+                       st.Bind(5,idparts[1]);\r
+                       st.Bind(6,nntpbody);\r
+                       st.Bind(7,idparts[3]);\r
+                       st.Bind(8,idparts[2]);\r
+                       inserted=st.Step(true);\r
+                       long messageid=st.GetLastInsertRowID();\r
+\r
+                       if(inserted==true)\r
+                       {\r
+\r
+                               st=m_db->Prepare("INSERT INTO tblMessageBoard(MessageID,BoardID) VALUES(?,?);");\r
+                               for(std::vector<std::string>::iterator i=boards.begin(); i!=boards.end(); i++)\r
+                               {\r
+                                       st.Bind(0,messageid);\r
+                                       st.Bind(1,idparts[1]);\r
+                                       st.Step();\r
+                                       st.Reset();\r
+                               }\r
+                               st.Finalize();\r
+\r
+                               st=m_db->Prepare("INSERT INTO tblMessageReplyTo(MessageID,ReplyToMessageUUID,ReplyOrder) VALUES(?,?,?);");\r
+                               for(std::map<long,std::string>::iterator j=replyto.begin(); j!=replyto.end(); j++)\r
+                               {\r
+                                       st.Bind(0,messageid);\r
+                                       st.Bind(1,(*j).second);\r
+                                       st.Bind(2,(*j).first);\r
+                                       st.Step();\r
+                                       st.Reset();\r
+                               }\r
+                               st.Finalize();\r
+\r
+                               m_log->debug("MessageRequester::HandleAllData parsed Message XML file : "+message["Identifier"]);\r
+\r
+                       }\r
+                       else    // couldn't insert - was already in database\r
+                       {\r
+                               std::string errmsg;\r
+                               m_db->GetLastError(errmsg);\r
+                               m_log->debug("FrostMessageRequester::HandleAllData could not insert message into database.  SQLite error "+errmsg);\r
+                       }\r
+\r
+                       st.Finalize();\r
+                       m_db->Execute("COMMIT;");\r
+\r
+               }       // if validmessage\r
+               else\r
+               {\r
+                       m_log->debug("FrostMessageRequester::HandleAllData invalid message "+message["Identifier"]);\r
+               }\r
+\r
+       }\r
+       else\r
+       {\r
+               m_log->error("FrostMessageRequester::HandleAllData error parsing FrostMessage XML file : "+message["Identifier"]);\r
+       }\r
+\r
+       RemoveFromRequestList(idparts[0]+"|"+idparts[1]+"|"+idparts[2]);\r
+\r
+       return true;\r
+\r
+}\r
+\r
+const bool FrostMessageRequester::HandleGetFailed(FCPv2::Message &message)\r
+{\r
+       std::vector<std::string> idparts;\r
+       StringFunctions::Split(message["Identifier"],"|",idparts);\r
+\r
+       if(message["Fatal"]=="true")\r
+       {\r
+               // insert index so we won't try it again\r
+               SQLite3DB::Statement st=m_db->Prepare("INSERT INTO tblFrostMessageRequests(BoardID,Day,RequestIndex,Found) VALUES(?,?,?,'true');");\r
+               st.Bind(0,idparts[0]);\r
+               st.Bind(1,idparts[2]);\r
+               st.Bind(2,idparts[1]);\r
+               st.Step();\r
+       }\r
+\r
+       m_log->debug("FrostMessageRequester::HandleGetFailed handled failure "+message["Code"]+" of "+message["Identifier"]);\r
+\r
+       return true;\r
+\r
+}\r
+\r
+void FrostMessageRequester::Initialize()\r
+{\r
+       m_fcpuniquename="FrostMessageRequester";\r
+       std::string tempval("");\r
+       m_maxrequests=0;\r
+       Option option(m_db);\r
+\r
+       option.GetInt("FrostMaxMessageRequests",m_maxrequests);\r
+       if(m_maxrequests<1)\r
+       {\r
+               m_maxrequests=1;\r
+               m_log->error("Option FrostMaxMessageRequests is currently set at "+tempval+".  It must be 1 or greater.");\r
+       }\r
+       if(m_maxrequests>100)\r
+       {\r
+               m_log->warning("Option FrostMaxMessageRequests is currently set at "+tempval+".  This value might be incorrectly configured.");\r
+       }\r
+\r
+       m_maxdaysbackward=0;\r
+       option.GetInt("FrostMessageMaxDaysBackward",m_maxdaysbackward);\r
+       if(m_maxdaysbackward<0)\r
+       {\r
+               m_maxdaysbackward=0;\r
+               m_log->error("Option FrostMessageMaxDaysBackward is currently set at "+tempval+".  It must be 0 or greater.");\r
+       }\r
+       if(m_maxdaysbackward>30)\r
+       {\r
+               m_log->warning("Option FrostMessageMaxDaysBackward is currently set at "+tempval+".  This value might be incorrectly configured.");\r
+       }\r
+\r
+       m_boardprefix="";\r
+       option.Get("FrostBoardPrefix",m_boardprefix);\r
+\r
+       m_frostmessagebase="";\r
+       option.Get("FrostMessageBase",m_frostmessagebase);\r
+\r
+       m_saveanonymous=false;\r
+       option.Get("FrostSaveAnonymousMessages",tempval);\r
+       if(tempval=="true")\r
+       {\r
+               m_saveanonymous=true;\r
+       }\r
+\r
+       m_maxindexesforward=4;\r
+\r
+}\r
+\r
+void FrostMessageRequester::PopulateIDList()\r
+{\r
+\r
+       Poco::DateTime pastdate;\r
+       long expectedindex;\r
+       SQLite3DB::Statement st=m_db->Prepare("SELECT BoardID, BoardName FROM tblBoard WHERE BoardName LIKE ? || '%' AND SaveReceivedMessages='true';");\r
+       SQLite3DB::Statement st2=m_db->Prepare("SELECT Day, RequestIndex FROM tblFrostMessageRequests WHERE BoardID=? AND Day=? ORDER BY RequestIndex ASC;");\r
+\r
+       st.Bind(0,m_boardprefix);\r
+       st.Step();\r
+\r
+       while(st.RowReturned())\r
+       {\r
+               int boardid=-1;\r
+               std::string boardidstr="";\r
+               std::string boardname="";\r
+\r
+               st.ResultInt(0,boardid);\r
+               st.ResultText(1,boardname);\r
+\r
+               StringFunctions::Convert(boardid,boardidstr);\r
+\r
+               for(long backdays=0; backdays<=m_maxdaysbackward; backdays++)\r
+               {\r
+\r
+                       pastdate=Poco::DateTime()-Poco::Timespan(backdays,0,0,0,0);\r
+                       std::string day=Poco::DateTimeFormatter::format(pastdate,"%Y-%m-%d");\r
+\r
+                       st2.Bind(0,boardid);\r
+                       st2.Bind(1,day);\r
+                       st2.Step();\r
+\r
+                       expectedindex=0;\r
+                       while(st2.RowReturned())\r
+                       {\r
+                               int thisindex=-1;\r
+                               st2.ResultText(0,day);\r
+                               st2.ResultInt(1,thisindex);\r
+\r
+                               // fill in indexes we haven't downloaded yet\r
+                               if(expectedindex<thisindex)\r
+                               {\r
+                                       for(long i=expectedindex; i<thisindex; i++)\r
+                                       {\r
+                                               std::string istr="";\r
+                                               StringFunctions::Convert(i,istr);\r
+                                               m_ids[boardidstr+"|"+istr+"|"+day]=false;\r
+                                       }\r
+                               }\r
+\r
+                               expectedindex=thisindex+1;\r
+\r
+                               st2.Step();\r
+\r
+                       }\r
+                       st2.Reset();\r
+\r
+                       // fill in remaining indexes\r
+                       for(long i=expectedindex; i<=expectedindex+m_maxindexesforward; i++)\r
+                       {\r
+                               std::string istr="";\r
+                               StringFunctions::Convert(i,istr);       \r
+                               m_ids[boardidstr+"|"+istr+"|"+day]=false;\r
+                       }\r
+\r
+               }\r
+\r
+               st.Step();\r
+       }\r
+\r
+}\r
+\r
+void FrostMessageRequester::StartRequest(const std::string &id)\r
+{\r
+       Poco::DateTime date;\r
+       int tz=0;\r
+       FCPv2::Message message;\r
+       std::vector<std::string> idparts;\r
+\r
+       StringFunctions::Split(id,"|",idparts);\r
+\r
+       Poco::DateTimeParser::tryParse(idparts[2],date,tz);\r
+\r
+       SQLite3DB::Statement st=m_db->Prepare("SELECT BoardName FROM tblBoard WHERE BoardID=?;");\r
+       st.Bind(0,idparts[0]);\r
+       st.Step();\r
+\r
+       if(st.RowReturned())\r
+       {\r
+               std::string boardname="";\r
+\r
+               st.ResultText(0,boardname);\r
+               // erase prefix from the board name\r
+               if(m_boardprefix.size()>0)\r
+               {\r
+                       boardname.erase(0,m_boardprefix.size());\r
+               }\r
+\r
+               message.SetName("ClientGet");\r
+               message["URI"]="KSK@"+m_frostmessagebase+"|message|news|"+Poco::DateTimeFormatter::format(date,"%Y.%n.%e")+"-"+boardname+"-"+idparts[1]+".xml";\r
+               message["Identifier"]=m_fcpuniquename+"|"+id+"|"+message["URI"];\r
+               message["ReturnType"]="direct";\r
+               message["MaxSize"]="1000000";           // 1 MB\r
+\r
+               m_fcp->Send(message);\r
+\r
+               m_requesting.push_back(id);\r
+       }\r
+\r
+       m_ids[id]=true;\r
+\r
+}\r