6680b8b93d675afdc7c18ac6306951d494b7a05d
[fms.git] / src / freenet / messagerequester.cpp
1 #include "../../include/freenet/messagerequester.h"\r
2 #include "../../include/freenet/messagexml.h"\r
3 \r
4 #include <algorithm>\r
5 \r
6 #ifdef XMEM\r
7         #include <xmem.h>\r
8 #endif\r
9 \r
10 MessageRequester::MessageRequester()\r
11 {\r
12         Initialize();\r
13 }\r
14 \r
15 MessageRequester::MessageRequester(FCPv2 *fcp):IIndexRequester<std::string>(fcp)\r
16 {\r
17         Initialize();\r
18 }\r
19 \r
20 const long MessageRequester::GetBoardID(const std::string &boardname)\r
21 {\r
22         SQLite3DB::Statement st=m_db->Prepare("SELECT BoardID FROM tblBoard WHERE BoardName=?;");\r
23         st.Bind(0,boardname);\r
24         st.Step();\r
25 \r
26         if(st.RowReturned())\r
27         {\r
28                 int boardid;\r
29                 st.ResultInt(0,boardid);\r
30                 return boardid;\r
31         }\r
32         else\r
33         {\r
34                 DateTime now;\r
35                 now.SetToGMTime();\r
36                 st=m_db->Prepare("INSERT INTO tblBoard(BoardName,DateAdded) VALUES(?,?);");\r
37                 st.Bind(0,boardname);\r
38                 st.Bind(1,now.Format("%Y-%m-%d %H:%M:%S"));\r
39                 st.Step(true);\r
40                 return st.GetLastInsertRowID();\r
41         }       \r
42 }\r
43 \r
44 const std::string MessageRequester::GetIdentityName(const long identityid)\r
45 {\r
46         SQLite3DB::Statement st=m_db->Prepare("SELECT Name,PublicKey FROM tblIdentity WHERE IdentityID=?;");\r
47         st.Bind(0,identityid);\r
48         st.Step();\r
49         if(st.RowReturned())\r
50         {\r
51                 std::vector<std::string> keyparts;\r
52                 std::string key;\r
53                 std::string name;\r
54                 st.ResultText(0,name);\r
55                 st.ResultText(1,key);\r
56                 \r
57                 StringFunctions::SplitMultiple(key,"@,",keyparts);\r
58                 \r
59                 if(keyparts.size()>1)\r
60                 {\r
61                         return name+"@"+keyparts[1];\r
62                 }\r
63                 else\r
64                 {\r
65                         return name+"@invalidpublickey";\r
66                 }\r
67         }\r
68         else\r
69         {\r
70                 return "";\r
71         }\r
72 }\r
73 \r
74 const bool MessageRequester::HandleAllData(FCPMessage &message)\r
75 {\r
76         SQLite3DB::Statement st;\r
77         std::vector<std::string> idparts;\r
78         long datalength;\r
79         std::vector<char> data;\r
80         MessageXML xml;\r
81         long identityid;\r
82         long index;\r
83         bool inserted=false;\r
84         bool validmessage=true;\r
85         long savetoboardcount=0;\r
86 \r
87         StringFunctions::Split(message["Identifier"],"|",idparts);\r
88         StringFunctions::Convert(message["DataLength"],datalength);\r
89         StringFunctions::Convert(idparts[2],identityid);\r
90         StringFunctions::Convert(idparts[4],index);\r
91 \r
92         // wait for all data to be received from connection\r
93         while(m_fcp->Connected() && m_fcp->ReceiveBufferSize()<datalength)\r
94         {\r
95                 m_fcp->Update(1);\r
96         }\r
97 \r
98         // if we got disconnected- return immediately\r
99         if(m_fcp->Connected()==false)\r
100         {\r
101                 return false;\r
102         }\r
103 \r
104         // receive the file\r
105         data.resize(datalength);\r
106         m_fcp->ReceiveRaw(&data[0],datalength);\r
107 \r
108         // mark this index as received\r
109         st=m_db->Prepare("UPDATE tblMessageRequests SET Found='true' WHERE IdentityID=? AND Day=? AND RequestIndex=?;");\r
110         st.Bind(0,identityid);\r
111         st.Bind(1,idparts[3]);\r
112         st.Bind(2,index);\r
113         st.Step();\r
114         st.Finalize();\r
115 \r
116         // parse file into xml and update the database\r
117         if(xml.ParseXML(std::string(data.begin(),data.end()))==true)\r
118         {\r
119                 std::vector<std::string> boards=xml.GetBoards();\r
120                 std::map<long,std::string> replyto=xml.GetInReplyTo();\r
121 \r
122                 if(boards.size()>m_maxboardspermessage)\r
123                 {\r
124                         boards.resize(m_maxboardspermessage);\r
125                 }\r
126 \r
127                 if(boards.size()<=0)\r
128                 {\r
129                         m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"MessageRequester::HandleAllData Message XML did not contain any boards! "+message["Identifier"]);\r
130                         // remove this identityid from request list\r
131                         RemoveFromRequestList(idparts[1]);                      \r
132                         return true;\r
133                 }\r
134                 if(xml.GetReplyBoard()=="")\r
135                 {\r
136                         m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"MessageRequester::HandleAllData Message XML did not contain a reply board! "+message["Identifier"]);\r
137                         // remove this identityid from request list\r
138                         RemoveFromRequestList(idparts[1]);                      \r
139                         return true;\r
140                 }\r
141 \r
142                 // make sure the reply board is on the board list we are saving - if not, replace the last element of boards with the reply board\r
143                 if(xml.GetReplyBoard()!="" && std::find(boards.begin(),boards.end(),xml.GetReplyBoard())==boards.end() && boards.size()>0)\r
144                 {\r
145                         boards[boards.size()-1]=xml.GetReplyBoard();\r
146                 }\r
147 \r
148                 // make sure domain of message id match 43 characters of public key of identity (remove - and ~) - if not, discard message\r
149                 // implement after 0.1.12 is released\r
150                 st=m_db->Prepare("SELECT PublicKey FROM tblIdentity WHERE IdentityID=?;");\r
151                 st.Bind(0,identityid);\r
152                 st.Step();\r
153                 if(st.RowReturned())\r
154                 {\r
155                         std::vector<std::string> uuidparts;\r
156                         std::vector<std::string> keyparts;\r
157                         std::string keypart="";\r
158                         std::string publickey="";\r
159 \r
160                         st.ResultText(0,publickey);\r
161 \r
162                         StringFunctions::SplitMultiple(publickey,"@,",keyparts);\r
163                         StringFunctions::SplitMultiple(xml.GetMessageID(),"@",uuidparts);\r
164 \r
165                         if(uuidparts.size()>1 && keyparts.size()>1)\r
166                         {\r
167                                 keypart=StringFunctions::Replace(StringFunctions::Replace(keyparts[1],"-",""),"~","");\r
168                                 if(keypart!=uuidparts[1])\r
169                                 {\r
170                                         m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"MessageRequester::HandleAllData MessageID in Message doesn't match public key of identity : "+message["Identifier"]);\r
171                                         validmessage=false;\r
172                                 }\r
173                         }\r
174                         else\r
175                         {\r
176                                 m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"MessageRequester::HandleAllData Error with identity's public key or Message ID : "+message["Identifier"]);\r
177                                 validmessage=false;\r
178                         }\r
179                 }\r
180                 else\r
181                 {\r
182                         m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"MessageRequester::HandleAllData Error couldn't find identity : "+message["Identifier"]);\r
183                         validmessage=false;\r
184                 }\r
185 \r
186                 // make sure we will at least save to 1 board before inserting message\r
187                 savetoboardcount=0;\r
188                 for(std::vector<std::string>::iterator bi=boards.begin(); bi!=boards.end(); bi++)\r
189                 {\r
190                         if(SaveToBoard((*bi)))\r
191                         {\r
192                                 savetoboardcount++;\r
193                         }\r
194                 }\r
195 \r
196                 if(validmessage && savetoboardcount>0)\r
197                 {\r
198                         st=m_db->Prepare("INSERT INTO tblMessage(IdentityID,FromName,MessageDate,MessageTime,Subject,MessageUUID,ReplyBoardID,Body,MessageIndex) VALUES(?,?,?,?,?,?,?,?,?);");\r
199                         st.Bind(0,identityid);\r
200                         st.Bind(1,GetIdentityName(identityid));\r
201                         st.Bind(2,xml.GetDate());\r
202                         st.Bind(3,xml.GetTime());\r
203                         st.Bind(4,xml.GetSubject());\r
204                         st.Bind(5,xml.GetMessageID());\r
205                         st.Bind(6,GetBoardID(xml.GetReplyBoard()));\r
206                         st.Bind(7,xml.GetBody());\r
207                         st.Bind(8,index);\r
208                         inserted=st.Step(true);\r
209                         int messageid=st.GetLastInsertRowID();\r
210 \r
211                         if(inserted==true)\r
212                         {\r
213 \r
214                                 st=m_db->Prepare("INSERT INTO tblMessageBoard(MessageID,BoardID) VALUES(?,?);");\r
215                                 for(std::vector<std::string>::iterator i=boards.begin(); i!=boards.end(); i++)\r
216                                 {\r
217                                         if(SaveToBoard((*i)))\r
218                                         {\r
219                                                 st.Bind(0,messageid);\r
220                                                 st.Bind(1,GetBoardID((*i)));\r
221                                                 st.Step();\r
222                                                 st.Reset();\r
223                                         }\r
224                                 }\r
225                                 st.Finalize();\r
226 \r
227                                 st=m_db->Prepare("INSERT INTO tblMessageReplyTo(MessageID,ReplyToMessageUUID,ReplyOrder) VALUES(?,?,?);");\r
228                                 for(std::map<long,std::string>::iterator j=replyto.begin(); j!=replyto.end(); j++)\r
229                                 {\r
230                                         st.Bind(0,messageid);\r
231                                         st.Bind(1,(*j).second);\r
232                                         st.Bind(2,(*j).first);\r
233                                         st.Step();\r
234                                         st.Reset();\r
235                                 }\r
236                                 st.Finalize();\r
237 \r
238                                 m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"MessageRequester::HandleAllData parsed Message XML file : "+message["Identifier"]);\r
239 \r
240                         }\r
241                         else    // couldn't insert - was already in database\r
242                         {\r
243                                 //m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"MessageRequester::HandleAddData could not insert message into database.  "+message["Identifier"]);\r
244                         }\r
245 \r
246                 }       // if validmessage\r
247         }\r
248         else\r
249         {\r
250                 m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"MessageRequester::HandleAllData error parsing Message XML file : "+message["Identifier"]);\r
251         }\r
252 \r
253         RemoveFromRequestList(idparts[1]);\r
254 \r
255         return true;\r
256 }\r
257 \r
258 const bool MessageRequester::HandleGetFailed(FCPMessage &message)\r
259 {\r
260         DateTime now;\r
261         SQLite3DB::Statement st;\r
262         std::vector<std::string> idparts;\r
263         std::string requestid;\r
264         long index;\r
265         long identityid;\r
266 \r
267         now.SetToGMTime();\r
268         StringFunctions::Split(message["Identifier"],"|",idparts);\r
269         requestid=idparts[1];\r
270         StringFunctions::Convert(idparts[2],identityid);\r
271         StringFunctions::Convert(idparts[4],index);\r
272 \r
273         // if this is a fatal error - insert index into database so we won't try to download this index again\r
274         if(message["Fatal"]=="true")\r
275         {\r
276                 st=m_db->Prepare("UPDATE tblMessageRequests SET Found='true' WHERE IdentityID=? AND Day=? AND RequestIndex=?;");\r
277                 st.Bind(0,identityid);\r
278                 st.Bind(1,idparts[3]);\r
279                 st.Bind(2,index);\r
280                 st.Step();\r
281                 st.Finalize();\r
282 \r
283                 m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"MessageRequester::HandleGetFailed fatal error requesting "+message["Identifier"]);\r
284         }\r
285 \r
286         // remove this identityid from request list\r
287         RemoveFromRequestList(requestid);\r
288 \r
289         return true;\r
290 }\r
291 \r
292 void MessageRequester::Initialize()\r
293 {\r
294         m_fcpuniquename="MessageRequester";\r
295         std::string tempval;\r
296         Option::Instance()->Get("MaxMessageRequests",tempval);\r
297         StringFunctions::Convert(tempval,m_maxrequests);\r
298         if(m_maxrequests<1)\r
299         {\r
300                 m_maxrequests=1;\r
301                 m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"Option MaxMessageRequests is currently set at "+tempval+".  It must be 1 or greater.");\r
302         }\r
303         if(m_maxrequests>100)\r
304         {\r
305                 m_log->WriteLog(LogFile::LOGLEVEL_WARNING,"Option MaxMessageRequests is currently set at "+tempval+".  This value might be incorrectly configured.");\r
306         }\r
307         Option::Instance()->Get("MessageDownloadMaxDaysBackward",tempval);\r
308         StringFunctions::Convert(tempval,m_maxdaysbackward);\r
309         if(m_maxdaysbackward<0)\r
310         {\r
311                 m_maxdaysbackward=0;\r
312                 m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"Option MessageDownloadMaxDaysBackward is currently set at "+tempval+".  It must be 0 or greater.");\r
313         }\r
314         if(m_maxdaysbackward>30)\r
315         {\r
316                 m_log->WriteLog(LogFile::LOGLEVEL_WARNING,"Option MessageDownloadMaxDaysBackward is currently set at "+tempval+".  This value might be incorrectly configured.");\r
317         }\r
318         Option::Instance()->Get("MaxPeerMessagesPerDay",tempval);\r
319         StringFunctions::Convert(tempval,m_maxpeermessages);\r
320         if(m_maxpeermessages<1)\r
321         {\r
322                 m_maxpeermessages=1;\r
323                 m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"Option MaxPeerMessagesPerDay is currently set at "+tempval+".  It must be 1 or greater.");\r
324         }\r
325         if(m_maxpeermessages<20 || m_maxpeermessages>1000)\r
326         {\r
327                 m_log->WriteLog(LogFile::LOGLEVEL_WARNING,"Option MaxPeerMessagesPerDay is currently set at "+tempval+".  This value might be incorrectly configured.  The suggested value is 200.");\r
328         }\r
329         Option::Instance()->Get("MaxBoardsPerMessage",tempval);\r
330         StringFunctions::Convert(tempval,m_maxboardspermessage);\r
331         if(m_maxboardspermessage<1)\r
332         {\r
333                 m_maxboardspermessage=1;\r
334                 m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"Option MaxBoardsPerMessage is currently set at "+tempval+".  It must be 1 or greater.");\r
335         }\r
336         if(m_maxboardspermessage>20)\r
337         {\r
338                 m_log->WriteLog(LogFile::LOGLEVEL_WARNING,"Option MaxBoardsPerMessage is currently set at "+tempval+".  This value might be incorrectly configured.");\r
339         }\r
340 }\r
341 \r
342 void MessageRequester::PopulateIDList()\r
343 {\r
344         DateTime date;\r
345         std::string val1;\r
346         std::string val2;\r
347         std::string val3;\r
348         std::string sql;\r
349         long requestindex;\r
350 \r
351         date.SetToGMTime();\r
352         date.Add(0,0,0,-m_maxdaysbackward);\r
353 \r
354         sql="SELECT tblIdentity.IdentityID,Day,RequestIndex ";\r
355         sql+="FROM tblMessageRequests INNER JOIN tblIdentity ON tblMessageRequests.IdentityID=tblIdentity.IdentityID ";\r
356         sql+="WHERE (tblIdentity.LocalMessageTrust IS NULL OR tblIdentity.LocalMessageTrust>=(SELECT OptionValue FROM tblOption WHERE Option='MinLocalMessageTrust')) ";\r
357         sql+="AND FromMessageList='true' AND Found='false' AND Day>='"+date.Format("%Y-%m-%d")+"' ";\r
358         sql+="AND (tblIdentity.PeerMessageTrust IS NULL OR tblIdentity.PeerMessageTrust>=(SELECT OptionValue FROM tblOption WHERE Option='MinPeerMessageTrust')) ";\r
359         sql+=";";\r
360 \r
361         SQLite3DB::Statement st=m_db->Prepare(sql);\r
362         st.Step();\r
363 \r
364         m_ids.clear();\r
365 \r
366         while(st.RowReturned())\r
367         {\r
368                 st.ResultText(0,val1);\r
369                 st.ResultText(1,val2);\r
370                 st.ResultText(2,val3);\r
371 \r
372                 requestindex=0;\r
373                 StringFunctions::Convert(val3,requestindex);\r
374 \r
375                 // only continue if index is < max messages we will accept from a peer\r
376                 if(requestindex<m_maxpeermessages)\r
377                 {\r
378                         if(m_ids.find(val1+"*"+val2+"*"+val3)==m_ids.end())\r
379                         {\r
380                                 m_ids[val1+"*"+val2+"*"+val3]=false;\r
381                         }\r
382                 }\r
383                 st.Step();\r
384         }\r
385 \r
386 }\r
387 \r
388 const bool MessageRequester::SaveToBoard(const std::string &boardname)\r
389 {\r
390         bool save=true;\r
391         SQLite3DB::Statement st=m_db->Prepare("SELECT SaveReceivedMessages FROM tblBoard WHERE BoardName=?;");\r
392         st.Bind(0,boardname);\r
393         st.Step();\r
394         if(st.RowReturned())\r
395         {\r
396                 std::string val="";\r
397                 st.ResultText(0,val);\r
398                 if(val=="true")\r
399                 {\r
400                         save=true;\r
401                 }\r
402                 else\r
403                 {\r
404                         save=false;\r
405                 }\r
406         }\r
407         return save;\r
408 }\r
409 \r
410 void MessageRequester::StartRequest(const std::string &requestid)\r
411 {\r
412         FCPMessage message;\r
413         std::vector<std::string> parts;\r
414         std::string tempval;\r
415         long identityid;\r
416         std::string date;\r
417         std::string indexstr;\r
418         std::string publickey;\r
419 \r
420         StringFunctions::Split(requestid,"*",parts);\r
421         StringFunctions::Convert(parts[0],identityid);\r
422         StringFunctions::Convert(parts[1],date);\r
423         indexstr=parts[2];\r
424 \r
425         SQLite3DB::Statement st=m_db->Prepare("SELECT PublicKey FROM tblIdentity WHERE IdentityID=?;");\r
426         st.Bind(0,identityid);\r
427         st.Step();\r
428 \r
429         if(st.RowReturned())\r
430         {\r
431                 st.ResultText(0,publickey);\r
432 \r
433                 message.SetName("ClientGet");\r
434                 message["URI"]=publickey+m_messagebase+"|"+date+"|Message|"+indexstr+".xml";\r
435                 message["Identifier"]=m_fcpuniquename+"|"+requestid+"|"+parts[0]+"|"+parts[1]+"|"+parts[2]+"|"+message["URI"];\r
436                 message["ReturnType"]="direct";\r
437                 message["MaxSize"]="1000000";           // 1 MB\r
438                 message["MaxRetries"]="-1";                     // use new ULPR since we are fairly sure message exists since the author says it does\r
439 \r
440                 m_fcp->SendMessage(message);\r
441 \r
442                 m_requesting.push_back(requestid);\r
443 \r
444                 m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"MessageRequester::StartRequest requesting "+message["Identifier"]);\r
445         }\r
446         \r
447         m_ids[requestid]=true;\r
448 \r
449 }\r