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