version 0.3.33
[fms.git] / src / freenet / frostmessagerequester.cpp
1 #include "../../include/freenet/frostmessagerequester.h"\r
2 #include "../../include/freenet/frostidentity.h"\r
3 #include "../../include/freenet/frostmessagexml.h"\r
4 \r
5 #include <Poco/DateTime.h>\r
6 #include <Poco/DateTimeParser.h>\r
7 #include <Poco/DateTimeFormatter.h>\r
8 #include <Poco/Timespan.h>\r
9 \r
10 FrostMessageRequester::FrostMessageRequester(SQLite3DB::DB *db):IIndexRequester<std::string>(db)\r
11 {\r
12         Initialize();\r
13 }\r
14 \r
15 FrostMessageRequester::FrostMessageRequester(SQLite3DB::DB *db, FCPv2::Connection *fcp):IIndexRequester<std::string>(db,fcp)\r
16 {\r
17         Initialize();\r
18 }\r
19 \r
20 const bool FrostMessageRequester::HandleAllData(FCPv2::Message &message)\r
21 {\r
22         std::vector<std::string> idparts;\r
23         std::vector<char> data;\r
24         long datalength=0;\r
25         FrostMessageXML xml;\r
26         FrostIdentity frostid;\r
27         bool validmessage=true;\r
28         bool inserted=false;\r
29 \r
30         StringFunctions::Split(message["Identifier"],"|",idparts);\r
31         StringFunctions::Convert(message["DataLength"],datalength);\r
32 \r
33         // wait for all data to be received from connection\r
34         m_fcp->WaitForBytes(1000,datalength);\r
35 \r
36         // if we got disconnected- return immediately\r
37         if(m_fcp->IsConnected()==false)\r
38         {\r
39                 return false;\r
40         }\r
41 \r
42         // receive the file\r
43         m_fcp->Receive(data,datalength);\r
44 \r
45         // mark this index as received\r
46         SQLite3DB::Statement st=m_db->Prepare("INSERT INTO tblFrostMessageRequests(BoardID,Day,RequestIndex,Found) VALUES(?,?,?,'true');");\r
47         st.Bind(0,idparts[1]);\r
48         st.Bind(1,idparts[3]);\r
49         st.Bind(2,idparts[2]);\r
50         st.Step();\r
51 \r
52         if(data.size()>0 && xml.ParseXML(std::string(data.begin(),data.end()))==true)\r
53         {\r
54                 std::vector<std::string> boards=xml.GetBoards();\r
55                 std::map<long,std::string> replyto=xml.GetInReplyTo();\r
56 \r
57                 if(xml.GetFrostAuthor()!="Anonymous" && frostid.FromPublicKey(xml.GetFrostPublicKey())==false)\r
58                 {\r
59                         m_log->debug("FrostMessageRequester::HandleAllData error with public key "+xml.GetFrostPublicKey());\r
60                         validmessage=false;\r
61                 }\r
62 \r
63                 if(validmessage && xml.GetFrostAuthor()!="Anonymous" && frostid.VerifyAuthor(xml.GetFrostAuthor())==false)\r
64                 {\r
65                         m_log->debug("FrostMessageRequester::HandleAllData error with author "+xml.GetFrostAuthor());\r
66                         validmessage=false;\r
67                 }\r
68 \r
69                 std::string contentv2=xml.GetSignableContentV2();\r
70                 if(validmessage && xml.GetFrostAuthor()!="Anonymous" && frostid.VerifySignature(std::vector<unsigned char>(contentv2.begin(),contentv2.end()),xml.GetFrostSignatureV2())==false)\r
71                 {\r
72                         m_log->debug("FrostMessageRequester::HandleAllData error with signature "+xml.GetFrostSignatureV2());\r
73                         validmessage=false;\r
74                 }\r
75 \r
76                 if(xml.GetFrostAuthor()=="Anonymous" && m_saveanonymous==false)\r
77                 {\r
78                         validmessage=false;\r
79                 }\r
80 \r
81                 if(validmessage==true)\r
82                 {\r
83                         std::string nntpbody="";\r
84                         nntpbody=xml.GetBody();\r
85 \r
86                         //add file keys/sizes to body\r
87                         std::vector<MessageXML::fileattachment> fileattachments=xml.GetFileAttachments();\r
88                         if(fileattachments.size()>0)\r
89                         {\r
90                                 nntpbody+="\r\nAttachments";\r
91                         }\r
92                         for(std::vector<MessageXML::fileattachment>::iterator i=fileattachments.begin(); i!=fileattachments.end(); i++)\r
93                         {\r
94                                 std::string sizestr="0";\r
95                                 StringFunctions::Convert((*i).m_size,sizestr);\r
96 \r
97                                 nntpbody+="\r\n"+(*i).m_key;\r
98                                 nntpbody+="\r\n"+sizestr+" bytes";\r
99                                 nntpbody+="\r\n";\r
100                         }\r
101 \r
102                         m_db->Execute("BEGIN;");\r
103 \r
104                         st=m_db->Prepare("INSERT INTO tblMessage(FromName,MessageDate,MessageTime,Subject,MessageUUID,ReplyBoardID,Body,InsertDate,MessageIndex) VALUES(?,?,?,?,?,?,?,?,?);");\r
105                         st.Bind(0,xml.GetFrostAuthor());\r
106                         st.Bind(1,xml.GetDate());\r
107                         st.Bind(2,xml.GetTime());\r
108                         st.Bind(3,xml.GetSubject());\r
109                         st.Bind(4,xml.GetMessageID());\r
110                         st.Bind(5,idparts[1]);\r
111                         st.Bind(6,nntpbody);\r
112                         st.Bind(7,idparts[3]);\r
113                         st.Bind(8,idparts[2]);\r
114                         inserted=st.Step(true);\r
115                         long messageid=st.GetLastInsertRowID();\r
116 \r
117                         if(inserted==true)\r
118                         {\r
119 \r
120                                 st=m_db->Prepare("INSERT INTO tblMessageBoard(MessageID,BoardID) VALUES(?,?);");\r
121                                 for(std::vector<std::string>::iterator i=boards.begin(); i!=boards.end(); i++)\r
122                                 {\r
123                                         st.Bind(0,messageid);\r
124                                         st.Bind(1,idparts[1]);\r
125                                         st.Step();\r
126                                         st.Reset();\r
127                                 }\r
128                                 st.Finalize();\r
129 \r
130                                 st=m_db->Prepare("INSERT INTO tblMessageReplyTo(MessageID,ReplyToMessageUUID,ReplyOrder) VALUES(?,?,?);");\r
131                                 for(std::map<long,std::string>::iterator j=replyto.begin(); j!=replyto.end(); j++)\r
132                                 {\r
133                                         st.Bind(0,messageid);\r
134                                         st.Bind(1,(*j).second);\r
135                                         st.Bind(2,(*j).first);\r
136                                         st.Step();\r
137                                         st.Reset();\r
138                                 }\r
139                                 st.Finalize();\r
140 \r
141                                 m_log->debug("MessageRequester::HandleAllData parsed Message XML file : "+message["Identifier"]);\r
142 \r
143                         }\r
144                         else    // couldn't insert - was already in database\r
145                         {\r
146                                 std::string errmsg;\r
147                                 m_db->GetLastError(errmsg);\r
148                                 m_log->debug("FrostMessageRequester::HandleAllData could not insert message into database.  SQLite error "+errmsg);\r
149                         }\r
150 \r
151                         st.Finalize();\r
152                         m_db->Execute("COMMIT;");\r
153 \r
154                 }       // if validmessage\r
155                 else\r
156                 {\r
157                         m_log->debug("FrostMessageRequester::HandleAllData invalid message "+message["Identifier"]);\r
158                 }\r
159 \r
160         }\r
161         else\r
162         {\r
163                 m_log->error("FrostMessageRequester::HandleAllData error parsing FrostMessage XML file : "+message["Identifier"]);\r
164         }\r
165 \r
166         RemoveFromRequestList(idparts[0]+"|"+idparts[1]+"|"+idparts[2]);\r
167 \r
168         return true;\r
169 \r
170 }\r
171 \r
172 const bool FrostMessageRequester::HandleGetFailed(FCPv2::Message &message)\r
173 {\r
174         std::vector<std::string> idparts;\r
175         StringFunctions::Split(message["Identifier"],"|",idparts);\r
176 \r
177         if(message["Fatal"]=="true")\r
178         {\r
179                 // insert index so we won't try it again\r
180                 SQLite3DB::Statement st=m_db->Prepare("INSERT INTO tblFrostMessageRequests(BoardID,Day,RequestIndex,Found) VALUES(?,?,?,'true');");\r
181                 st.Bind(0,idparts[0]);\r
182                 st.Bind(1,idparts[2]);\r
183                 st.Bind(2,idparts[1]);\r
184                 st.Step();\r
185         }\r
186 \r
187         m_log->debug("FrostMessageRequester::HandleGetFailed handled failure "+message["Code"]+" of "+message["Identifier"]);\r
188 \r
189         return true;\r
190 \r
191 }\r
192 \r
193 void FrostMessageRequester::Initialize()\r
194 {\r
195         m_fcpuniquename="FrostMessageRequester";\r
196         std::string tempval("");\r
197         m_maxrequests=0;\r
198         Option option(m_db);\r
199 \r
200         option.GetInt("FrostMaxMessageRequests",m_maxrequests);\r
201         if(m_maxrequests<1)\r
202         {\r
203                 m_maxrequests=1;\r
204                 m_log->error("Option FrostMaxMessageRequests is currently set at "+tempval+".  It must be 1 or greater.");\r
205         }\r
206         if(m_maxrequests>100)\r
207         {\r
208                 m_log->warning("Option FrostMaxMessageRequests is currently set at "+tempval+".  This value might be incorrectly configured.");\r
209         }\r
210 \r
211         m_maxdaysbackward=0;\r
212         option.GetInt("FrostMessageMaxDaysBackward",m_maxdaysbackward);\r
213         if(m_maxdaysbackward<0)\r
214         {\r
215                 m_maxdaysbackward=0;\r
216                 m_log->error("Option FrostMessageMaxDaysBackward is currently set at "+tempval+".  It must be 0 or greater.");\r
217         }\r
218         if(m_maxdaysbackward>30)\r
219         {\r
220                 m_log->warning("Option FrostMessageMaxDaysBackward is currently set at "+tempval+".  This value might be incorrectly configured.");\r
221         }\r
222 \r
223         m_boardprefix="";\r
224         option.Get("FrostBoardPrefix",m_boardprefix);\r
225 \r
226         m_frostmessagebase="";\r
227         option.Get("FrostMessageBase",m_frostmessagebase);\r
228 \r
229         m_saveanonymous=false;\r
230         option.Get("FrostSaveAnonymousMessages",tempval);\r
231         if(tempval=="true")\r
232         {\r
233                 m_saveanonymous=true;\r
234         }\r
235 \r
236         m_maxindexesforward=4;\r
237 \r
238 }\r
239 \r
240 void FrostMessageRequester::PopulateIDList()\r
241 {\r
242 \r
243         Poco::DateTime pastdate;\r
244         long expectedindex;\r
245         SQLite3DB::Statement st=m_db->Prepare("SELECT BoardID, BoardName FROM tblBoard WHERE BoardName LIKE ? || '%' AND SaveReceivedMessages='true';");\r
246         SQLite3DB::Statement st2=m_db->Prepare("SELECT Day, RequestIndex FROM tblFrostMessageRequests WHERE BoardID=? AND Day=? ORDER BY RequestIndex ASC;");\r
247 \r
248         st.Bind(0,m_boardprefix);\r
249         st.Step();\r
250 \r
251         while(st.RowReturned())\r
252         {\r
253                 int boardid=-1;\r
254                 std::string boardidstr="";\r
255                 std::string boardname="";\r
256 \r
257                 st.ResultInt(0,boardid);\r
258                 st.ResultText(1,boardname);\r
259 \r
260                 StringFunctions::Convert(boardid,boardidstr);\r
261 \r
262                 for(long backdays=0; backdays<=m_maxdaysbackward; backdays++)\r
263                 {\r
264 \r
265                         pastdate=Poco::DateTime()-Poco::Timespan(backdays,0,0,0,0);\r
266                         std::string day=Poco::DateTimeFormatter::format(pastdate,"%Y-%m-%d");\r
267 \r
268                         st2.Bind(0,boardid);\r
269                         st2.Bind(1,day);\r
270                         st2.Step();\r
271 \r
272                         expectedindex=0;\r
273                         while(st2.RowReturned())\r
274                         {\r
275                                 int thisindex=-1;\r
276                                 st2.ResultText(0,day);\r
277                                 st2.ResultInt(1,thisindex);\r
278 \r
279                                 // fill in indexes we haven't downloaded yet\r
280                                 if(expectedindex<thisindex)\r
281                                 {\r
282                                         for(long i=expectedindex; i<thisindex; i++)\r
283                                         {\r
284                                                 std::string istr="";\r
285                                                 StringFunctions::Convert(i,istr);\r
286                                                 m_ids[boardidstr+"|"+istr+"|"+day]=false;\r
287                                         }\r
288                                 }\r
289 \r
290                                 expectedindex=thisindex+1;\r
291 \r
292                                 st2.Step();\r
293 \r
294                         }\r
295                         st2.Reset();\r
296 \r
297                         // fill in remaining indexes\r
298                         for(long i=expectedindex; i<=expectedindex+m_maxindexesforward; i++)\r
299                         {\r
300                                 std::string istr="";\r
301                                 StringFunctions::Convert(i,istr);       \r
302                                 m_ids[boardidstr+"|"+istr+"|"+day]=false;\r
303                         }\r
304 \r
305                 }\r
306 \r
307                 st.Step();\r
308         }\r
309 \r
310 }\r
311 \r
312 void FrostMessageRequester::StartRequest(const std::string &id)\r
313 {\r
314         Poco::DateTime date;\r
315         int tz=0;\r
316         FCPv2::Message message;\r
317         std::vector<std::string> idparts;\r
318 \r
319         StringFunctions::Split(id,"|",idparts);\r
320 \r
321         Poco::DateTimeParser::tryParse(idparts[2],date,tz);\r
322 \r
323         SQLite3DB::Statement st=m_db->Prepare("SELECT BoardName FROM tblBoard WHERE BoardID=?;");\r
324         st.Bind(0,idparts[0]);\r
325         st.Step();\r
326 \r
327         if(st.RowReturned())\r
328         {\r
329                 std::string boardname="";\r
330 \r
331                 st.ResultText(0,boardname);\r
332                 // erase prefix from the board name\r
333                 if(m_boardprefix.size()>0)\r
334                 {\r
335                         boardname.erase(0,m_boardprefix.size());\r
336                 }\r
337 \r
338                 message.SetName("ClientGet");\r
339                 message["URI"]="KSK@"+m_frostmessagebase+"|message|news|"+Poco::DateTimeFormatter::format(date,"%Y.%n.%e")+"-"+boardname+"-"+idparts[1]+".xml";\r
340                 message["Identifier"]=m_fcpuniquename+"|"+id+"|"+message["URI"];\r
341                 message["ReturnType"]="direct";\r
342                 message["MaxSize"]="1000000";           // 1 MB\r
343 \r
344                 m_fcp->Send(message);\r
345 \r
346                 m_requesting.push_back(id);\r
347         }\r
348 \r
349         m_ids[id]=true;\r
350 \r
351 }\r