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