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