17aba0a2e9f5235899eb6ff1bdbde80d58a0d58b
[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::Connection *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(FCPv2::Message &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         m_fcp->WaitForBytes(1000,datalength);\r
125 \r
126         // if we got disconnected- return immediately\r
127         if(m_fcp->IsConnected()==false)\r
128         {\r
129                 return false;\r
130         }\r
131 \r
132         // receive the file\r
133         m_fcp->Receive(data,datalength);\r
134 \r
135         // parse file into xml and update the database\r
136         if(data.size()>0 && xml.ParseXML(std::string(data.begin(),data.end()))==true)\r
137         {\r
138 \r
139                 SQLite3DB::Statement st=m_db->Prepare("SELECT IdentityID FROM tblMessageRequests WHERE IdentityID=? AND Day=? AND RequestIndex=?;");\r
140                 SQLite3DB::Statement spk=m_db->Prepare("SELECT IdentityID FROM tblIdentity WHERE PublicKey=?;");\r
141                 SQLite3DB::Statement mst=m_db->Prepare("INSERT INTO tblMessageRequests(IdentityID,Day,RequestIndex,FromMessageList) VALUES(?,?,?,'true');");\r
142                 for(long i=0; i<xml.MessageCount(); i++)\r
143                 {\r
144 \r
145                         // go through each board the message was posted to and see if we are saving messages to that board\r
146                         // if the board isn't found, see if we are saving messages to new boards\r
147                         boardsstr="";\r
148                         addmessage=false;\r
149                         std::vector<std::string> messageboards=xml.GetBoards(i);\r
150                         for(std::vector<std::string>::iterator j=messageboards.begin(); j!=messageboards.end(); j++)\r
151                         {\r
152                                 if(boards.find((*j))!=boards.end())\r
153                                 {\r
154                                         if(boards[(*j)]==true)\r
155                                         {\r
156                                                 addmessage=true;\r
157                                         }\r
158                                 }\r
159                                 else if(m_savetonewboards==true)\r
160                                 {\r
161                                         addmessage=true;\r
162                                 }\r
163                                 if(j!=messageboards.begin())\r
164                                 {\r
165                                         boardsstr+=", ";\r
166                                 }\r
167                                 boardsstr+=(*j);\r
168                         }\r
169 \r
170                         if(CheckDateNotFuture(xml.GetDate(i))==false)\r
171                         {\r
172                                 addmessage=false;\r
173                                 m_log->error(m_fcpuniquename+"::HandleAllData date for message is in future! "+xml.GetDate(i));\r
174                         }\r
175 \r
176                         if(addmessage==true && CheckDateWithinMaxDays(xml.GetDate(i))==false)\r
177                         {\r
178                                 addmessage=false;\r
179                         }\r
180 \r
181                         if(addmessage==true)\r
182                         {\r
183                                 st.Bind(0,identityid);\r
184                                 st.Bind(1,xml.GetDate(i));\r
185                                 st.Bind(2,xml.GetIndex(i));\r
186                                 st.Step();\r
187                                 if(st.RowReturned()==false)\r
188                                 {\r
189                                         mst.Bind(0,identityid);\r
190                                         mst.Bind(1,xml.GetDate(i));\r
191                                         mst.Bind(2,xml.GetIndex(i));\r
192                                         mst.Step();\r
193                                         mst.Reset();\r
194                                 }\r
195                                 st.Reset();\r
196                         }\r
197                         else\r
198                         {\r
199                                 //m_log->trace("MessageListRequester::HandleAllData will not download message posted to "+boardsstr+" on "+xml.GetDate(i));\r
200                         }\r
201                 }\r
202 \r
203                 // insert external message indexes\r
204                 for(long i=0; i<xml.ExternalMessageCount(); i++)\r
205                 {\r
206                         if(xml.GetExternalType(i)=="Keyed")\r
207                         {\r
208                                 // go through each board the message was posted to and see if we are saving messages to that board\r
209                                 // if the board isn't found, see if we are saving messages to new boards\r
210                                 boardsstr="";\r
211                                 addmessage=false;\r
212                                 std::vector<std::string> messageboards=xml.GetExternalBoards(i);\r
213                                 for(std::vector<std::string>::iterator j=messageboards.begin(); j!=messageboards.end(); j++)\r
214                                 {\r
215                                         if(boards.find((*j))!=boards.end())\r
216                                         {\r
217                                                 if(boards[(*j)]==true)\r
218                                                 {\r
219                                                         addmessage=true;\r
220                                                 }\r
221                                         }\r
222                                         else if(m_savetonewboards==true)\r
223                                         {\r
224                                                 addmessage=true;\r
225                                         }\r
226                                         if(j!=messageboards.begin())\r
227                                         {\r
228                                                 boardsstr+=", ";\r
229                                         }\r
230                                         boardsstr+=(*j);\r
231                                 }\r
232 \r
233                                 if(CheckDateNotFuture(xml.GetExternalDate(i))==false)\r
234                                 {\r
235                                         addmessage=false;\r
236                                         m_log->error(m_fcpuniquename+"::HandleAllData date for external message is in future! "+xml.GetExternalDate(i));\r
237                                 }\r
238 \r
239                                 if(addmessage==true && CheckDateWithinMaxDays(xml.GetExternalDate(i))==false)\r
240                                 {\r
241                                         addmessage=false;\r
242                                 }\r
243 \r
244                                 if(addmessage==true)\r
245                                 {\r
246                                         spk.Bind(0,xml.GetExternalIdentity(i));\r
247                                         spk.Step();\r
248                                         if(spk.RowReturned())\r
249                                         {\r
250                                                 int thisidentityid=0;\r
251                                                 spk.ResultInt(0,thisidentityid);\r
252                                                 mst.Bind(0,thisidentityid);\r
253                                                 mst.Bind(1,xml.GetExternalDate(i));\r
254                                                 mst.Bind(2,xml.GetExternalIndex(i));\r
255                                                 mst.Step();\r
256                                                 mst.Reset();\r
257                                         }\r
258                                         spk.Reset();\r
259                                 }\r
260                                 else\r
261                                 {\r
262                                         //m_log->trace("MessageListRequester::HandleAllData will not download external message posted to "+boardsstr+" from " + xml.GetExternalIdentity(i) + " on " + xml.GetExternalDate(i));\r
263                                 }\r
264                         }\r
265                 }\r
266 \r
267                 st=m_db->Prepare("INSERT INTO tblMessageListRequests(IdentityID,Day,RequestIndex,Found) VALUES(?,?,?,'true');");\r
268                 st.Bind(0,identityid);\r
269                 st.Bind(1,idparts[4]);\r
270                 st.Bind(2,index);\r
271                 st.Step();\r
272                 st.Finalize();\r
273 \r
274                 m_log->debug(m_fcpuniquename+"::HandleAllData parsed MessageList XML file : "+message["Identifier"]);\r
275         }\r
276         else\r
277         {\r
278                 // bad data - mark index\r
279                 st=m_db->Prepare("INSERT INTO tblMessageListRequests(IdentityID,Day,RequestIndex,Found) VALUES(?,?,?,'false');");\r
280                 st.Bind(0,identityid);\r
281                 st.Bind(1,idparts[4]);\r
282                 st.Bind(2,index);\r
283                 st.Step();\r
284                 st.Finalize();\r
285 \r
286                 m_log->error(m_fcpuniquename+"::HandleAllData error parsing MessageList XML file : "+message["Identifier"]);\r
287         }\r
288 \r
289         // remove this identityid from request list\r
290         RemoveFromRequestList(identityid);\r
291 \r
292         return true;\r
293 \r
294 }\r
295 \r
296 const bool MessageListRequester::HandleGetFailed(FCPv2::Message &message)\r
297 {\r
298         SQLite3DB::Statement st;\r
299         std::vector<std::string> idparts;\r
300         long identityid;\r
301         long index;\r
302 \r
303         StringFunctions::Split(message["Identifier"],"|",idparts);\r
304         StringFunctions::Convert(idparts[1],identityid);\r
305         StringFunctions::Convert(idparts[2],index);     \r
306 \r
307         // code 27 - permanent redirect\r
308         if(message["Code"]=="27")\r
309         {\r
310                 StartRedirectRequest(message);\r
311                 return true;\r
312         }\r
313 \r
314         // if this is a fatal error - insert index into database so we won't try to download this index again\r
315         if(message["Fatal"]=="true")\r
316         {\r
317                 st=m_db->Prepare("INSERT INTO tblMessageListRequests(IdentityID,Day,RequestIndex,Found) VALUES(?,?,?,'false');");\r
318                 st.Bind(0,identityid);\r
319                 st.Bind(1,idparts[4]);\r
320                 st.Bind(2,index);\r
321                 st.Step();\r
322                 st.Finalize();\r
323 \r
324                 m_log->error(m_fcpuniquename+"::HandleGetFailed fatal error code="+message["Code"]+" requesting "+message["Identifier"]);\r
325         }\r
326 \r
327         // remove this identityid from request list\r
328         RemoveFromRequestList(identityid);\r
329 \r
330         return true;\r
331 }\r
332 \r
333 void MessageListRequester::Initialize()\r
334 {\r
335         m_fcpuniquename="ActiveMessageListRequester";\r
336         std::string tempval="";\r
337 \r
338         m_maxrequests=0;\r
339         Option::Instance()->GetInt("MaxMessageListRequests",m_maxrequests);\r
340 \r
341         // active identities get 1/2 of the max requests option + any remaining if not evenly divisible - inactive identities get 1/2\r
342         m_maxrequests=(m_maxrequests/2)+(m_maxrequests%2);\r
343 \r
344         if(m_maxrequests<1)\r
345         {\r
346                 m_maxrequests=1;\r
347                 m_log->error("Option MaxMessageListRequests is currently set at "+tempval+".  It must be 1 or greater.");\r
348         }\r
349         if(m_maxrequests>100)\r
350         {\r
351                 m_log->warning("Option MaxMessageListRequests is currently set at "+tempval+".  This value might be incorrectly configured.");\r
352         }\r
353 \r
354         tempval="";\r
355         Option::Instance()->Get("LocalTrustOverridesPeerTrust",tempval);\r
356         if(tempval=="true")\r
357         {\r
358                 m_localtrustoverrides=true;\r
359         }\r
360         else\r
361         {\r
362                 m_localtrustoverrides=false;\r
363         }\r
364 \r
365         tempval="";\r
366         Option::Instance()->Get("SaveMessagesFromNewBoards",tempval);\r
367         if(tempval=="true")\r
368         {\r
369                 m_savetonewboards=true;\r
370         }\r
371         else\r
372         {\r
373                 m_savetonewboards=false;\r
374         }\r
375 \r
376         m_messagedownloadmaxdaysbackward=5;\r
377         tempval="5";\r
378         Option::Instance()->Get("MessageDownloadMaxDaysBackward",tempval);\r
379         StringFunctions::Convert(tempval,m_messagedownloadmaxdaysbackward);\r
380 \r
381 }\r
382 \r
383 void MessageListRequester::PopulateIDList()\r
384 {\r
385         Poco::DateTime date;\r
386         Poco::DateTime yesterday=date-Poco::Timespan(1,0,0,0,0);\r
387         int id;\r
388 \r
389         SQLite3DB::Statement st;\r
390 \r
391         // 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
392         if(m_localtrustoverrides==false)\r
393         {\r
394                 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
395         }\r
396         else\r
397         {\r
398                 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
399         }\r
400         st.Step();\r
401 \r
402         m_ids.clear();\r
403 \r
404         while(st.RowReturned())\r
405         {\r
406                 st.ResultInt(0,id);\r
407                 m_ids[id]=false;\r
408                 st.Step();\r
409         }\r
410 }\r
411 \r
412 void MessageListRequester::StartRedirectRequest(FCPv2::Message &message)\r
413 {\r
414         std::vector<std::string> parts;\r
415         std::string indexstr="";\r
416         std::string identityidstr="";\r
417         std::string datestr="";\r
418         FCPv2::Message newmessage;\r
419 \r
420         // get the new edition #\r
421         StringFunctions::Split(message["RedirectURI"],"/",parts);\r
422         //edition # is 2nd to last part\r
423         if(parts.size()>2)\r
424         {\r
425                 indexstr=parts[parts.size()-2];\r
426         }\r
427 \r
428         // get identityid\r
429         parts.clear();\r
430         StringFunctions::Split(message["Identifier"],"|",parts);\r
431         if(parts.size()>1)\r
432         {\r
433                 identityidstr=parts[1];\r
434         }\r
435         if(parts.size()>4)\r
436         {\r
437                 datestr=parts[4];\r
438         }\r
439 \r
440         newmessage.SetName("ClientGet");\r
441         newmessage["URI"]=StringFunctions::UriDecode(message["RedirectURI"]);\r
442         newmessage["Identifier"]=m_fcpuniquename+"|"+identityidstr+"|"+indexstr+"|_|"+datestr+"|"+newmessage["URI"];\r
443         newmessage["ReturnType"]="direct";\r
444         newmessage["MaxSize"]="1000000";\r
445 \r
446         m_fcp->Send(newmessage);\r
447 \r
448 }\r
449 \r
450 void MessageListRequester::StartRequest(const long &id)\r
451 {\r
452         Poco::DateTime now;\r
453         FCPv2::Message message;\r
454         std::string publickey;\r
455         int index=0;\r
456         std::string indexstr;\r
457         std::string identityidstr;\r
458 \r
459         SQLite3DB::Statement st=m_db->Prepare("SELECT PublicKey FROM tblIdentity WHERE IdentityID=?;");\r
460         st.Bind(0,id);\r
461         st.Step();\r
462 \r
463         if(st.RowReturned())\r
464         {\r
465                 st.ResultText(0,publickey);\r
466 \r
467                 now=Poco::Timestamp();\r
468 \r
469                 SQLite3DB::Statement st2=m_db->Prepare("SELECT MAX(RequestIndex) FROM tblMessageListRequests WHERE Day=? AND IdentityID=?;");\r
470                 st2.Bind(0,Poco::DateTimeFormatter::format(now,"%Y-%m-%d"));\r
471                 st2.Bind(1,id);\r
472                 st2.Step();\r
473 \r
474                 index=0;\r
475                 if(st2.RowReturned())\r
476                 {\r
477                         if(st2.ResultNull(0)==false)\r
478                         {\r
479                                 st2.ResultInt(0,index);\r
480                                 // don't increment index here - the node will let us know if there is a new edition\r
481                                 // 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
482                                 index++;\r
483                         }\r
484                 }\r
485                 st2.Finalize();\r
486 \r
487                 StringFunctions::Convert(index,indexstr);\r
488                 StringFunctions::Convert(id,identityidstr);\r
489 \r
490                 message.SetName("ClientGet");\r
491                 message["URI"]="USK"+publickey.substr(3)+m_messagebase+"|"+Poco::DateTimeFormatter::format(now,"%Y.%m.%d")+"|MessageList/"+indexstr+"/MessageList.xml";\r
492                 message["Identifier"]=m_fcpuniquename+"|"+identityidstr+"|"+indexstr+"|_|"+Poco::DateTimeFormatter::format(now,"%Y-%m-%d")+"|"+message["URI"];\r
493                 message["ReturnType"]="direct";\r
494                 message["MaxSize"]="1000000";\r
495 \r
496                 m_fcp->Send(message);\r
497 \r
498                 m_requesting.push_back(id);\r
499         }\r
500         st.Finalize();\r
501 \r
502         m_ids[id]=true;\r
503 }\r