version 0.3.27
[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(m_fcpuniquename+"::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(m_fcpuniquename+"::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(m_fcpuniquename+"::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(m_fcpuniquename+"::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(m_fcpuniquename+"::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="ActiveMessageListRequester";\r
343         std::string tempval="";\r
344 \r
345         m_maxrequests=0;\r
346         Option::Instance()->GetInt("MaxMessageListRequests",m_maxrequests);\r
347 \r
348         // active identities get 1/2 of the max requests option + any remaining if not evenly divisible - inactive identities get 1/2\r
349         m_maxrequests=(m_maxrequests/2)+(m_maxrequests%2);\r
350 \r
351         if(m_maxrequests<1)\r
352         {\r
353                 m_maxrequests=1;\r
354                 m_log->error("Option MaxMessageListRequests is currently set at "+tempval+".  It must be 1 or greater.");\r
355         }\r
356         if(m_maxrequests>100)\r
357         {\r
358                 m_log->warning("Option MaxMessageListRequests is currently set at "+tempval+".  This value might be incorrectly configured.");\r
359         }\r
360 \r
361         tempval="";\r
362         Option::Instance()->Get("LocalTrustOverridesPeerTrust",tempval);\r
363         if(tempval=="true")\r
364         {\r
365                 m_localtrustoverrides=true;\r
366         }\r
367         else\r
368         {\r
369                 m_localtrustoverrides=false;\r
370         }\r
371 \r
372         tempval="";\r
373         Option::Instance()->Get("SaveMessagesFromNewBoards",tempval);\r
374         if(tempval=="true")\r
375         {\r
376                 m_savetonewboards=true;\r
377         }\r
378         else\r
379         {\r
380                 m_savetonewboards=false;\r
381         }\r
382 \r
383         m_messagedownloadmaxdaysbackward=5;\r
384         tempval="5";\r
385         Option::Instance()->Get("MessageDownloadMaxDaysBackward",tempval);\r
386         StringFunctions::Convert(tempval,m_messagedownloadmaxdaysbackward);\r
387 \r
388 }\r
389 \r
390 void MessageListRequester::PopulateIDList()\r
391 {\r
392         Poco::DateTime date;\r
393         Poco::DateTime yesterday=date-Poco::Timespan(1,0,0,0,0);\r
394         int id;\r
395 \r
396         SQLite3DB::Statement st;\r
397 \r
398         // 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
399         if(m_localtrustoverrides==false)\r
400         {\r
401                 st=m_db->Prepare("SELECT tblIdentity.IdentityID FROM tblIdentity INNER JOIN vwIdentityStats ON tblIdentity.IdentityID=vwIdentityStats.IdentityID WHERE PublicKey IS NOT NULL AND PublicKey <> '' AND LastSeen>='"+Poco::DateTimeFormatter::format(date,"%Y-%m-%d")+"' AND (vwIdentityStats.LastMessageDate>='"+Poco::DateTimeFormatter::format(yesterday,"%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
402         }\r
403         else\r
404         {\r
405                 st=m_db->Prepare("SELECT tblIdentity.IdentityID FROM tblIdentity INNER JOIN vwIdentityStats ON tblIdentity.IdentityID=vwIdentityStats.IdentityID WHERE PublicKey IS NOT NULL AND PublicKey <> '' AND LastSeen>='"+Poco::DateTimeFormatter::format(date,"%Y-%m-%d")+"' AND (vwIdentityStats.LastMessageDate>='"+Poco::DateTimeFormatter::format(yesterday,"%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
406         }\r
407         st.Step();\r
408 \r
409         m_ids.clear();\r
410 \r
411         while(st.RowReturned())\r
412         {\r
413                 st.ResultInt(0,id);\r
414                 m_ids[id]=false;\r
415                 st.Step();\r
416         }\r
417 }\r
418 \r
419 void MessageListRequester::StartRedirectRequest(FCPMessage &message)\r
420 {\r
421         std::vector<std::string> parts;\r
422         std::string indexstr="";\r
423         std::string identityidstr="";\r
424         std::string datestr="";\r
425         FCPMessage newmessage;\r
426 \r
427         // get the new edition #\r
428         StringFunctions::Split(message["RedirectURI"],"/",parts);\r
429         //edition # is 2nd to last part\r
430         if(parts.size()>2)\r
431         {\r
432                 indexstr=parts[parts.size()-2];\r
433         }\r
434 \r
435         // get identityid\r
436         parts.clear();\r
437         StringFunctions::Split(message["Identifier"],"|",parts);\r
438         if(parts.size()>1)\r
439         {\r
440                 identityidstr=parts[1];\r
441         }\r
442         if(parts.size()>4)\r
443         {\r
444                 datestr=parts[4];\r
445         }\r
446 \r
447         newmessage.SetName("ClientGet");\r
448         newmessage["URI"]=StringFunctions::UriDecode(message["RedirectURI"]);\r
449         newmessage["Identifier"]=m_fcpuniquename+"|"+identityidstr+"|"+indexstr+"|_|"+datestr+"|"+newmessage["URI"];\r
450         newmessage["ReturnType"]="direct";\r
451         newmessage["MaxSize"]="1000000";\r
452 \r
453         m_fcp->SendMessage(newmessage);\r
454 \r
455 }\r
456 \r
457 void MessageListRequester::StartRequest(const long &id)\r
458 {\r
459         Poco::DateTime now;\r
460         FCPMessage message;\r
461         std::string publickey;\r
462         int index=0;\r
463         std::string indexstr;\r
464         std::string identityidstr;\r
465 \r
466         SQLite3DB::Statement st=m_db->Prepare("SELECT PublicKey FROM tblIdentity WHERE IdentityID=?;");\r
467         st.Bind(0,id);\r
468         st.Step();\r
469 \r
470         if(st.RowReturned())\r
471         {\r
472                 st.ResultText(0,publickey);\r
473 \r
474                 now=Poco::Timestamp();\r
475 \r
476                 SQLite3DB::Statement st2=m_db->Prepare("SELECT MAX(RequestIndex) FROM tblMessageListRequests WHERE Day=? AND IdentityID=?;");\r
477                 st2.Bind(0,Poco::DateTimeFormatter::format(now,"%Y-%m-%d"));\r
478                 st2.Bind(1,id);\r
479                 st2.Step();\r
480 \r
481                 index=0;\r
482                 if(st2.RowReturned())\r
483                 {\r
484                         if(st2.ResultNull(0)==false)\r
485                         {\r
486                                 st2.ResultInt(0,index);\r
487                                 // don't increment index here - the node will let us know if there is a new edition\r
488                                 // 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
489                                 index++;\r
490                         }\r
491                 }\r
492                 st2.Finalize();\r
493 \r
494                 StringFunctions::Convert(index,indexstr);\r
495                 StringFunctions::Convert(id,identityidstr);\r
496 \r
497                 message.SetName("ClientGet");\r
498                 message["URI"]="USK"+publickey.substr(3)+m_messagebase+"|"+Poco::DateTimeFormatter::format(now,"%Y.%m.%d")+"|MessageList/"+indexstr+"/MessageList.xml";\r
499                 message["Identifier"]=m_fcpuniquename+"|"+identityidstr+"|"+indexstr+"|_|"+Poco::DateTimeFormatter::format(now,"%Y-%m-%d")+"|"+message["URI"];\r
500                 message["ReturnType"]="direct";\r
501                 message["MaxSize"]="1000000";\r
502 \r
503                 m_fcp->SendMessage(message);\r
504 \r
505                 m_requesting.push_back(id);\r
506         }\r
507         st.Finalize();\r
508 \r
509         m_ids[id]=true;\r
510 }\r