07d052f92d2b63c82805519b94f0b06933f5d258
[fms.git] / src / freenet / messagelistrequester.cpp
1 #include "../../include/freenet/messagelistrequester.h"\r
2 #include "../../include/freenet/messagelistxml.h"\r
3 \r
4 #include <Poco/DateTime.h>\r
5 #include <Poco/DateTimeFormatter.h>\r
6 #include <Poco/DateTimeParser.h>\r
7 #include <Poco/Timestamp.h>\r
8 \r
9 #ifdef XMEM\r
10         #include <xmem.h>\r
11 #endif\r
12 \r
13 MessageListRequester::MessageListRequester()\r
14 {\r
15         Initialize();\r
16 }\r
17 \r
18 MessageListRequester::MessageListRequester(FCPv2 *fcp):IIndexRequester<long>(fcp)\r
19 {\r
20         Initialize();\r
21 }\r
22 \r
23 const bool MessageListRequester::CheckDateNotFuture(const std::string &datestr) const\r
24 {\r
25         std::vector<std::string> dateparts;\r
26         int year=0;\r
27         int month=0;\r
28         int day=0;\r
29         Poco::DateTime today;\r
30 \r
31         StringFunctions::Split(datestr,"-",dateparts);\r
32         if(dateparts.size()==3)\r
33         {\r
34                 StringFunctions::Convert(dateparts[0],year);\r
35                 StringFunctions::Convert(dateparts[1],month);\r
36                 StringFunctions::Convert(dateparts[2],day);\r
37                 if(today.year()>year || (today.year()==year && today.month()>month) || (today.year()==year && today.month()==month && today.day()>=day))\r
38                 {\r
39                         return true;\r
40                 }\r
41                 else\r
42                 {\r
43                         return false;\r
44                 }\r
45         }\r
46         else\r
47         {\r
48                 return false;\r
49         }\r
50 \r
51 }\r
52 \r
53 const bool MessageListRequester::CheckDateWithinMaxDays(const std::string &datestr) const\r
54 {\r
55         Poco::DateTime checkdate;\r
56         Poco::DateTime date;\r
57         int tzdiff=0;\r
58         if(Poco::DateTimeParser::tryParse(datestr,date,tzdiff))\r
59         {\r
60                 checkdate-=Poco::Timespan(m_messagedownloadmaxdaysbackward,0,0,0,0);\r
61                 if(checkdate<=date)\r
62                 {\r
63                         return true;\r
64                 }\r
65                 else\r
66                 {\r
67                         return false;\r
68                 }\r
69         }\r
70         else\r
71         {\r
72                 return false;\r
73         }\r
74 }\r
75 \r
76 void MessageListRequester::GetBoardList(std::map<std::string,bool> &boards)\r
77 {\r
78         SQLite3DB::Statement st=m_db->Prepare("SELECT BoardName, SaveReceivedMessages FROM tblBoard;");\r
79         st.Step();\r
80         while(st.RowReturned())\r
81         {\r
82                 std::string boardname="";\r
83                 std::string tempval="";\r
84                 st.ResultText(0,boardname);\r
85                 st.ResultText(1,tempval);\r
86 \r
87                 if(tempval=="true")\r
88                 {\r
89                         boards[boardname]=true;\r
90                 }\r
91                 else\r
92                 {\r
93                         boards[boardname]=false;\r
94                 }\r
95 \r
96                 st.Step();\r
97         }\r
98 }\r
99 \r
100 const bool MessageListRequester::HandleAllData(FCPMessage &message)\r
101 {       \r
102         SQLite3DB::Statement st;\r
103         SQLite3DB::Statement trustst;\r
104         std::vector<std::string> idparts;\r
105         long datalength;\r
106         std::vector<char> data;\r
107         MessageListXML xml;\r
108         long identityid;\r
109         long index;\r
110         std::map<std::string,bool> boards;      // list of boards and if we will save messages for that board or not\r
111         bool addmessage=false;\r
112         std::string boardsstr="";\r
113         std::string datestr="";\r
114         std::vector<std::string> dateparts;\r
115 \r
116         GetBoardList(boards);\r
117 \r
118         StringFunctions::Split(message["Identifier"],"|",idparts);\r
119         StringFunctions::Convert(message["DataLength"],datalength);\r
120         StringFunctions::Convert(idparts[1],identityid);\r
121         StringFunctions::Convert(idparts[2],index);\r
122 \r
123         // wait for all data to be received from connection\r
124         while(m_fcp->Connected() && m_fcp->ReceiveBufferSize()<datalength)\r
125         {\r
126                 m_fcp->Update(1);\r
127         }\r
128 \r
129         // if we got disconnected- return immediately\r
130         if(m_fcp->Connected()==false)\r
131         {\r
132                 return false;\r
133         }\r
134 \r
135         // receive the file\r
136         data.resize(datalength);\r
137         if(data.size()>0)\r
138         {\r
139                 m_fcp->ReceiveRaw(&data[0],datalength);\r
140         }\r
141 \r
142         // parse file into xml and update the database\r
143         if(data.size()>0 && xml.ParseXML(std::string(data.begin(),data.end()))==true)\r
144         {\r
145 \r
146                 SQLite3DB::Statement st=m_db->Prepare("SELECT IdentityID FROM tblMessageRequests WHERE IdentityID=? AND Day=? AND RequestIndex=?;");\r
147                 SQLite3DB::Statement spk=m_db->Prepare("SELECT IdentityID FROM tblIdentity WHERE PublicKey=?;");\r
148                 SQLite3DB::Statement mst=m_db->Prepare("INSERT INTO tblMessageRequests(IdentityID,Day,RequestIndex,FromMessageList) VALUES(?,?,?,'true');");\r
149                 for(long i=0; i<xml.MessageCount(); i++)\r
150                 {\r
151 \r
152                         // go through each board the message was posted to and see if we are saving messages to that board\r
153                         // if the board isn't found, see if we are saving messages to new boards\r
154                         boardsstr="";\r
155                         addmessage=false;\r
156                         std::vector<std::string> messageboards=xml.GetBoards(i);\r
157                         for(std::vector<std::string>::iterator j=messageboards.begin(); j!=messageboards.end(); j++)\r
158                         {\r
159                                 if(boards.find((*j))!=boards.end())\r
160                                 {\r
161                                         if(boards[(*j)]==true)\r
162                                         {\r
163                                                 addmessage=true;\r
164                                         }\r
165                                 }\r
166                                 else if(m_savetonewboards==true)\r
167                                 {\r
168                                         addmessage=true;\r
169                                 }\r
170                                 if(j!=messageboards.begin())\r
171                                 {\r
172                                         boardsstr+=", ";\r
173                                 }\r
174                                 boardsstr+=(*j);\r
175                         }\r
176 \r
177                         if(CheckDateNotFuture(xml.GetDate(i))==false)\r
178                         {\r
179                                 addmessage=false;\r
180                                 m_log->error("MessageListRequester::HandleAllData date for message is in future! "+xml.GetDate(i));\r
181                         }\r
182 \r
183                         if(addmessage==true && CheckDateWithinMaxDays(xml.GetDate(i))==false)\r
184                         {\r
185                                 addmessage=false;\r
186                         }\r
187 \r
188                         if(addmessage==true)\r
189                         {\r
190                                 st.Bind(0,identityid);\r
191                                 st.Bind(1,xml.GetDate(i));\r
192                                 st.Bind(2,xml.GetIndex(i));\r
193                                 st.Step();\r
194                                 if(st.RowReturned()==false)\r
195                                 {\r
196                                         mst.Bind(0,identityid);\r
197                                         mst.Bind(1,xml.GetDate(i));\r
198                                         mst.Bind(2,xml.GetIndex(i));\r
199                                         mst.Step();\r
200                                         mst.Reset();\r
201                                 }\r
202                                 st.Reset();\r
203                         }\r
204                         else\r
205                         {\r
206                                 //m_log->trace("MessageListRequester::HandleAllData will not download message posted to "+boardsstr+" on "+xml.GetDate(i));\r
207                         }\r
208                 }\r
209 \r
210                 // insert external message indexes\r
211                 for(long i=0; i<xml.ExternalMessageCount(); i++)\r
212                 {\r
213                         if(xml.GetExternalType(i)=="Keyed")\r
214                         {\r
215                                 // go through each board the message was posted to and see if we are saving messages to that board\r
216                                 // if the board isn't found, see if we are saving messages to new boards\r
217                                 boardsstr="";\r
218                                 addmessage=false;\r
219                                 std::vector<std::string> messageboards=xml.GetExternalBoards(i);\r
220                                 for(std::vector<std::string>::iterator j=messageboards.begin(); j!=messageboards.end(); j++)\r
221                                 {\r
222                                         if(boards.find((*j))!=boards.end())\r
223                                         {\r
224                                                 if(boards[(*j)]==true)\r
225                                                 {\r
226                                                         addmessage=true;\r
227                                                 }\r
228                                         }\r
229                                         else if(m_savetonewboards==true)\r
230                                         {\r
231                                                 addmessage=true;\r
232                                         }\r
233                                         if(j!=messageboards.begin())\r
234                                         {\r
235                                                 boardsstr+=", ";\r
236                                         }\r
237                                         boardsstr+=(*j);\r
238                                 }\r
239 \r
240                                 if(CheckDateNotFuture(xml.GetExternalDate(i))==false)\r
241                                 {\r
242                                         addmessage=false;\r
243                                         m_log->error("MessageListRequester::HandleAllData date for external message is in future! "+xml.GetExternalDate(i));\r
244                                 }\r
245 \r
246                                 if(addmessage==true && CheckDateWithinMaxDays(xml.GetExternalDate(i))==false)\r
247                                 {\r
248                                         addmessage=false;\r
249                                 }\r
250 \r
251                                 if(addmessage==true)\r
252                                 {\r
253                                         spk.Bind(0,xml.GetExternalIdentity(i));\r
254                                         spk.Step();\r
255                                         if(spk.RowReturned())\r
256                                         {\r
257                                                 int thisidentityid=0;\r
258                                                 spk.ResultInt(0,thisidentityid);\r
259                                                 mst.Bind(0,thisidentityid);\r
260                                                 mst.Bind(1,xml.GetExternalDate(i));\r
261                                                 mst.Bind(2,xml.GetExternalIndex(i));\r
262                                                 mst.Step();\r
263                                                 mst.Reset();\r
264                                         }\r
265                                         spk.Reset();\r
266                                 }\r
267                                 else\r
268                                 {\r
269                                         //m_log->trace("MessageListRequester::HandleAllData will not download external message posted to "+boardsstr+" from " + xml.GetExternalIdentity(i) + " on " + xml.GetExternalDate(i));\r
270                                 }\r
271                         }\r
272                 }\r
273 \r
274                 st=m_db->Prepare("INSERT INTO tblMessageListRequests(IdentityID,Day,RequestIndex,Found) VALUES(?,?,?,'true');");\r
275                 st.Bind(0,identityid);\r
276                 st.Bind(1,idparts[4]);\r
277                 st.Bind(2,index);\r
278                 st.Step();\r
279                 st.Finalize();\r
280 \r
281                 m_log->debug("MessageListRequester::HandleAllData parsed MessageList XML file : "+message["Identifier"]);\r
282         }\r
283         else\r
284         {\r
285                 // bad data - mark index\r
286                 st=m_db->Prepare("INSERT INTO tblMessageListRequests(IdentityID,Day,RequestIndex,Found) VALUES(?,?,?,'false');");\r
287                 st.Bind(0,identityid);\r
288                 st.Bind(1,idparts[4]);\r
289                 st.Bind(2,index);\r
290                 st.Step();\r
291                 st.Finalize();\r
292 \r
293                 m_log->error("MessageListRequester::HandleAllData error parsing MessageList XML file : "+message["Identifier"]);\r
294         }\r
295 \r
296         // remove this identityid from request list\r
297         RemoveFromRequestList(identityid);\r
298 \r
299         return true;\r
300 \r
301 }\r
302 \r
303 const bool MessageListRequester::HandleGetFailed(FCPMessage &message)\r
304 {\r
305         SQLite3DB::Statement st;\r
306         std::vector<std::string> idparts;\r
307         long identityid;\r
308         long index;\r
309 \r
310         StringFunctions::Split(message["Identifier"],"|",idparts);\r
311         StringFunctions::Convert(idparts[1],identityid);\r
312         StringFunctions::Convert(idparts[2],index);     \r
313 \r
314         // code 27 - permanent redirect\r
315         if(message["Code"]=="27")\r
316         {\r
317                 StartRedirectRequest(message);\r
318                 return true;\r
319         }\r
320 \r
321         // if this is a fatal error - insert index into database so we won't try to download this index again\r
322         if(message["Fatal"]=="true")\r
323         {\r
324                 st=m_db->Prepare("INSERT INTO tblMessageListRequests(IdentityID,Day,RequestIndex,Found) VALUES(?,?,?,'false');");\r
325                 st.Bind(0,identityid);\r
326                 st.Bind(1,idparts[4]);\r
327                 st.Bind(2,index);\r
328                 st.Step();\r
329                 st.Finalize();\r
330 \r
331                 m_log->error("MessageListRequester::HandleGetFailed fatal error code="+message["Code"]+" requesting "+message["Identifier"]);\r
332         }\r
333 \r
334         // remove this identityid from request list\r
335         RemoveFromRequestList(identityid);\r
336 \r
337         return true;\r
338 }\r
339 \r
340 void MessageListRequester::Initialize()\r
341 {\r
342         m_fcpuniquename="MessageListRequester";\r
343         std::string tempval="";\r
344 \r
345         m_maxrequests=0;\r
346         Option::Instance()->GetInt("MaxMessageListRequests",m_maxrequests);\r
347         if(m_maxrequests<1)\r
348         {\r
349                 m_maxrequests=1;\r
350                 m_log->error("Option MaxMessageListRequests is currently set at "+tempval+".  It must be 1 or greater.");\r
351         }\r
352         if(m_maxrequests>100)\r
353         {\r
354                 m_log->warning("Option MaxMessageListRequests is currently set at "+tempval+".  This value might be incorrectly configured.");\r
355         }\r
356 \r
357         tempval="";\r
358         Option::Instance()->Get("LocalTrustOverridesPeerTrust",tempval);\r
359         if(tempval=="true")\r
360         {\r
361                 m_localtrustoverrides=true;\r
362         }\r
363         else\r
364         {\r
365                 m_localtrustoverrides=false;\r
366         }\r
367 \r
368         tempval="";\r
369         Option::Instance()->Get("SaveMessagesFromNewBoards",tempval);\r
370         if(tempval=="true")\r
371         {\r
372                 m_savetonewboards=true;\r
373         }\r
374         else\r
375         {\r
376                 m_savetonewboards=false;\r
377         }\r
378 \r
379         m_messagedownloadmaxdaysbackward=5;\r
380         tempval="5";\r
381         Option::Instance()->Get("MessageDownloadMaxDaysBackward",tempval);\r
382         StringFunctions::Convert(tempval,m_messagedownloadmaxdaysbackward);\r
383 \r
384 }\r
385 \r
386 void MessageListRequester::PopulateIDList()\r
387 {\r
388         Poco::DateTime date;\r
389         int id;\r
390 \r
391         SQLite3DB::Statement st;\r
392 \r
393         // select identities we want to query (we've seen them today) - sort by their trust level (descending) with secondary sort on how long ago we saw them (ascending)\r
394         if(m_localtrustoverrides==false)\r
395         {\r
396                 st=m_db->Prepare("SELECT IdentityID FROM tblIdentity WHERE PublicKey IS NOT NULL AND PublicKey <> '' AND LastSeen>='"+Poco::DateTimeFormatter::format(date,"%Y-%m-%d")+"' AND (LocalMessageTrust IS NULL OR LocalMessageTrust>=(SELECT OptionValue FROM tblOption WHERE Option='MinLocalMessageTrust')) AND (PeerMessageTrust IS NULL OR PeerMessageTrust>=(SELECT OptionValue FROM tblOption WHERE Option='MinPeerMessageTrust')) ORDER BY LocalMessageTrust+LocalTrustListTrust DESC, LastSeen;");\r
397         }\r
398         else\r
399         {\r
400                 st=m_db->Prepare("SELECT IdentityID FROM tblIdentity WHERE PublicKey IS NOT NULL AND PublicKey <> '' AND LastSeen>='"+Poco::DateTimeFormatter::format(date,"%Y-%m-%d")+"' AND (LocalMessageTrust>=(SELECT OptionValue FROM tblOption WHERE Option='MinLocalMessageTrust') OR (LocalMessageTrust IS NULL AND (PeerMessageTrust IS NULL OR PeerMessageTrust>=(SELECT OptionValue FROM tblOption WHERE Option='MinPeerMessageTrust')))) ORDER BY LocalMessageTrust+LocalTrustListTrust DESC, LastSeen;");\r
401         }\r
402         st.Step();\r
403 \r
404         m_ids.clear();\r
405 \r
406         while(st.RowReturned())\r
407         {\r
408                 st.ResultInt(0,id);\r
409                 m_ids[id]=false;\r
410                 st.Step();\r
411         }\r
412 }\r
413 \r
414 void MessageListRequester::StartRedirectRequest(FCPMessage &message)\r
415 {\r
416         std::vector<std::string> parts;\r
417         std::string indexstr="";\r
418         std::string identityidstr="";\r
419         std::string datestr="";\r
420         FCPMessage newmessage;\r
421 \r
422         // get the new edition #\r
423         StringFunctions::Split(message["RedirectURI"],"/",parts);\r
424         //edition # is 2nd to last part\r
425         if(parts.size()>2)\r
426         {\r
427                 indexstr=parts[parts.size()-2];\r
428         }\r
429 \r
430         // get identityid\r
431         parts.clear();\r
432         StringFunctions::Split(message["Identifier"],"|",parts);\r
433         if(parts.size()>1)\r
434         {\r
435                 identityidstr=parts[1];\r
436         }\r
437         if(parts.size()>4)\r
438         {\r
439                 datestr=parts[4];\r
440         }\r
441 \r
442         newmessage.SetName("ClientGet");\r
443         newmessage["URI"]=StringFunctions::UriDecode(message["RedirectURI"]);\r
444         newmessage["Identifier"]=m_fcpuniquename+"|"+identityidstr+"|"+indexstr+"|_|"+datestr+"|"+newmessage["URI"];\r
445         newmessage["ReturnType"]="direct";\r
446         newmessage["MaxSize"]="1000000";\r
447 \r
448         m_fcp->SendMessage(newmessage);\r
449 \r
450 }\r
451 \r
452 void MessageListRequester::StartRequest(const long &id)\r
453 {\r
454         Poco::DateTime now;\r
455         FCPMessage message;\r
456         std::string publickey;\r
457         int index=0;\r
458         std::string indexstr;\r
459         std::string identityidstr;\r
460 \r
461         SQLite3DB::Statement st=m_db->Prepare("SELECT PublicKey FROM tblIdentity WHERE IdentityID=?;");\r
462         st.Bind(0,id);\r
463         st.Step();\r
464 \r
465         if(st.RowReturned())\r
466         {\r
467                 st.ResultText(0,publickey);\r
468 \r
469                 now=Poco::Timestamp();\r
470 \r
471                 SQLite3DB::Statement st2=m_db->Prepare("SELECT MAX(RequestIndex) FROM tblMessageListRequests WHERE Day=? AND IdentityID=?;");\r
472                 st2.Bind(0,Poco::DateTimeFormatter::format(now,"%Y-%m-%d"));\r
473                 st2.Bind(1,id);\r
474                 st2.Step();\r
475 \r
476                 index=0;\r
477                 if(st2.RowReturned())\r
478                 {\r
479                         if(st2.ResultNull(0)==false)\r
480                         {\r
481                                 st2.ResultInt(0,index);\r
482                                 // don't increment index here - the node will let us know if there is a new edition\r
483                                 // 2008-05-31 - well actually the node isn't reliably retreiving the latest edition for USKs, so we DO need to increment the index\r
484                                 index++;\r
485                         }\r
486                 }\r
487                 st2.Finalize();\r
488 \r
489                 StringFunctions::Convert(index,indexstr);\r
490                 StringFunctions::Convert(id,identityidstr);\r
491 \r
492                 message.SetName("ClientGet");\r
493                 message["URI"]="USK"+publickey.substr(3)+m_messagebase+"|"+Poco::DateTimeFormatter::format(now,"%Y.%m.%d")+"|MessageList/"+indexstr+"/MessageList.xml";\r
494                 message["Identifier"]=m_fcpuniquename+"|"+identityidstr+"|"+indexstr+"|_|"+Poco::DateTimeFormatter::format(now,"%Y-%m-%d")+"|"+message["URI"];\r
495                 message["ReturnType"]="direct";\r
496                 message["MaxSize"]="1000000";\r
497 \r
498                 m_fcp->SendMessage(message);\r
499 \r
500                 m_requesting.push_back(id);\r
501         }\r
502         st.Finalize();\r
503 \r
504         m_ids[id]=true;\r
505 }\r