version 0.1.12
[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 \r
85         StringFunctions::Split(message["Identifier"],"|",idparts);\r
86         StringFunctions::Convert(message["DataLength"],datalength);\r
87         StringFunctions::Convert(idparts[2],identityid);\r
88         StringFunctions::Convert(idparts[4],index);\r
89 \r
90         // wait for all data to be received from connection\r
91         while(m_fcp->Connected() && m_fcp->ReceiveBufferSize()<datalength)\r
92         {\r
93                 m_fcp->Update(1);\r
94         }\r
95 \r
96         // if we got disconnected- return immediately\r
97         if(m_fcp->Connected()==false)\r
98         {\r
99                 return false;\r
100         }\r
101 \r
102         // receive the file\r
103         data.resize(datalength);\r
104         m_fcp->ReceiveRaw(&data[0],datalength);\r
105 \r
106         // mark this index as received\r
107         st=m_db->Prepare("UPDATE tblMessageRequests SET Found='true' WHERE IdentityID=? AND Day=? AND RequestIndex=?;");\r
108         st.Bind(0,identityid);\r
109         st.Bind(1,idparts[3]);\r
110         st.Bind(2,index);\r
111         st.Step();\r
112         st.Finalize();\r
113 \r
114         // parse file into xml and update the database\r
115         if(xml.ParseXML(std::string(data.begin(),data.end()))==true)\r
116         {\r
117                 std::vector<std::string> boards=xml.GetBoards();\r
118                 if(boards.size()>m_maxboardspermessage)\r
119                 {\r
120                         boards.resize(m_maxboardspermessage);\r
121                 }\r
122 \r
123                 if(boards.size()<=0)\r
124                 {\r
125                         m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"MessageRequester::HandleAllData Message XML did not contain any boards! "+message["Identifier"]);\r
126                         return true;\r
127                 }\r
128                 if(xml.GetReplyBoard()=="")\r
129                 {\r
130                         m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"MessageRequester::HandleAllData Message XML did not contain a reply board! "+message["Identifier"]);\r
131                         return true;\r
132                 }\r
133                 \r
134                 // make sure the reply board is on the board list - if not, replace the last element of boardswith the reply board\r
135                 if(xml.GetReplyBoard()!="" && std::find(boards.begin(),boards.end(),xml.GetReplyBoard())==boards.end() && boards.size()>0)\r
136                 {\r
137                         boards[boards.size()-1]=xml.GetReplyBoard();\r
138                 }\r
139 \r
140                 // TODO make sure domain of message id match 43 characters of public key of identity (remove - and ~) - if not, discard message\r
141                 // implement after 0.1.12 is released\r
142 \r
143                 st=m_db->Prepare("INSERT INTO tblMessage(IdentityID,FromName,MessageDate,MessageTime,Subject,MessageUUID,ReplyBoardID,Body) VALUES(?,?,?,?,?,?,?,?);");\r
144                 st.Bind(0,identityid);\r
145                 st.Bind(1,GetIdentityName(identityid));\r
146                 st.Bind(2,xml.GetDate());\r
147                 st.Bind(3,xml.GetTime());\r
148                 st.Bind(4,xml.GetSubject());\r
149                 st.Bind(5,xml.GetMessageID());\r
150                 st.Bind(6,GetBoardID(xml.GetReplyBoard()));\r
151                 st.Bind(7,xml.GetBody());\r
152                 inserted=st.Step(true);\r
153                 int messageid=st.GetLastInsertRowID();\r
154 \r
155                 if(inserted==true)\r
156                 {\r
157 \r
158                         st=m_db->Prepare("INSERT INTO tblMessageBoard(MessageID,BoardID) VALUES(?,?);");\r
159                         for(std::vector<std::string>::iterator i=boards.begin(); i!=boards.end(); i++)\r
160                         {\r
161                                 st.Bind(0,messageid);\r
162                                 st.Bind(1,GetBoardID((*i)));\r
163                                 st.Step();\r
164                                 st.Reset();\r
165                         }\r
166                         st.Finalize();\r
167 \r
168                         st=m_db->Prepare("INSERT INTO tblMessageReplyTo(MessageID,ReplyToMessageUUID,ReplyOrder) VALUES(?,?,?);");\r
169                         std::map<long,std::string> replyto=xml.GetInReplyTo();\r
170                         for(std::map<long,std::string>::iterator j=replyto.begin(); j!=replyto.end(); j++)\r
171                         {\r
172                                 st.Bind(0,messageid);\r
173                                 st.Bind(1,(*j).second);\r
174                                 st.Bind(2,(*j).first);\r
175                                 st.Step();\r
176                                 st.Reset();\r
177                         }\r
178                         st.Finalize();\r
179 \r
180                         m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"MessageRequester::HandleAllData parsed Message XML file : "+message["Identifier"]);\r
181 \r
182                 }\r
183                 else    // couldn't insert - was already in database\r
184                 {\r
185                         //m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"MessageRequester::HandleAddData could not insert message into database.  "+message["Identifier"]);\r
186                 }\r
187 \r
188         }\r
189         else\r
190         {\r
191                 m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"MessageRequester::HandleAllData error parsing Message XML file : "+message["Identifier"]);\r
192         }\r
193 \r
194         RemoveFromRequestList(idparts[1]);\r
195 \r
196         return true;\r
197 }\r
198 \r
199 const bool MessageRequester::HandleGetFailed(FCPMessage &message)\r
200 {\r
201         DateTime now;\r
202         SQLite3DB::Statement st;\r
203         std::vector<std::string> idparts;\r
204         std::string requestid;\r
205         long index;\r
206         long identityid;\r
207 \r
208         now.SetToGMTime();\r
209         StringFunctions::Split(message["Identifier"],"|",idparts);\r
210         requestid=idparts[1];\r
211         StringFunctions::Convert(idparts[2],identityid);\r
212         StringFunctions::Convert(idparts[4],index);\r
213 \r
214         // if this is a fatal error - insert index into database so we won't try to download this index again\r
215         if(message["Fatal"]=="true")\r
216         {\r
217                 st=m_db->Prepare("UPDATE tblMessageRequests SET Found='true' WHERE IdentityID=? AND Day=? AND RequestIndex=?;");\r
218                 st.Bind(0,identityid);\r
219                 st.Bind(1,idparts[3]);\r
220                 st.Bind(2,index);\r
221                 st.Step();\r
222                 st.Finalize();\r
223 \r
224                 m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"MessageRequester::HandleGetFailed fatal error requesting "+message["Identifier"]);\r
225         }\r
226 \r
227         // remove this identityid from request list\r
228         RemoveFromRequestList(requestid);\r
229 \r
230         return true;\r
231 }\r
232 \r
233 void MessageRequester::Initialize()\r
234 {\r
235         m_fcpuniquename="MessageRequester";\r
236         std::string tempval;\r
237         Option::Instance()->Get("MaxMessageRequests",tempval);\r
238         StringFunctions::Convert(tempval,m_maxrequests);\r
239         if(m_maxrequests<1)\r
240         {\r
241                 m_maxrequests=1;\r
242                 m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"Option MaxMessageRequests is currently set at "+tempval+".  It must be 1 or greater.");\r
243         }\r
244         if(m_maxrequests>100)\r
245         {\r
246                 m_log->WriteLog(LogFile::LOGLEVEL_WARNING,"Option MaxMessageRequests is currently set at "+tempval+".  This value might be incorrectly configured.");\r
247         }\r
248         Option::Instance()->Get("MessageDownloadMaxDaysBackward",tempval);\r
249         StringFunctions::Convert(tempval,m_maxdaysbackward);\r
250         if(m_maxdaysbackward<0)\r
251         {\r
252                 m_maxdaysbackward=0;\r
253                 m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"Option MessageDownloadMaxDaysBackward is currently set at "+tempval+".  It must be 0 or greater.");\r
254         }\r
255         if(m_maxdaysbackward>30)\r
256         {\r
257                 m_log->WriteLog(LogFile::LOGLEVEL_WARNING,"Option MessageDownloadMaxDaysBackward is currently set at "+tempval+".  This value might be incorrectly configured.");\r
258         }\r
259         Option::Instance()->Get("MaxPeerMessagesPerDay",tempval);\r
260         StringFunctions::Convert(tempval,m_maxpeermessages);\r
261         if(m_maxpeermessages<1)\r
262         {\r
263                 m_maxpeermessages=1;\r
264                 m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"Option MaxPeerMessagesPerDay is currently set at "+tempval+".  It must be 1 or greater.");\r
265         }\r
266         if(m_maxpeermessages<20 || m_maxpeermessages>1000)\r
267         {\r
268                 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
269         }\r
270         Option::Instance()->Get("MaxBoardsPerMessage",tempval);\r
271         StringFunctions::Convert(tempval,m_maxboardspermessage);\r
272         if(m_maxboardspermessage<1)\r
273         {\r
274                 m_maxboardspermessage=1;\r
275                 m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"Option MaxBoardsPerMessage is currently set at "+tempval+".  It must be 1 or greater.");\r
276         }\r
277         if(m_maxboardspermessage>20)\r
278         {\r
279                 m_log->WriteLog(LogFile::LOGLEVEL_WARNING,"Option MaxBoardsPerMessage is currently set at "+tempval+".  This value might be incorrectly configured.");\r
280         }\r
281 }\r
282 \r
283 void MessageRequester::PopulateIDList()\r
284 {\r
285         DateTime date;\r
286         std::string val1;\r
287         std::string val2;\r
288         std::string val3;\r
289         std::string sql;\r
290         long requestindex;\r
291 \r
292         date.SetToGMTime();\r
293         date.Add(0,0,0,-m_maxdaysbackward);\r
294 \r
295         sql="SELECT tblIdentity.IdentityID,Day,RequestIndex ";\r
296         sql+="FROM tblMessageRequests INNER JOIN tblIdentity ON tblMessageRequests.IdentityID=tblIdentity.IdentityID ";\r
297         sql+="WHERE tblIdentity.LocalMessageTrust>=(SELECT OptionValue FROM tblOption WHERE Option='MinLocalMessageTrust') AND FromMessageList='true' AND Found='false' AND Day>='"+date.Format("%Y-%m-%d")+"' ";\r
298         sql+="AND (tblIdentity.PeerMessageTrust IS NULL OR tblIdentity.PeerMessageTrust>=(SELECT OptionValue FROM tblOption WHERE Option='MinPeerMessageTrust')) ";\r
299         sql+=";";\r
300 \r
301         SQLite3DB::Statement st=m_db->Prepare(sql);\r
302         st.Step();\r
303 \r
304         m_ids.clear();\r
305 \r
306         while(st.RowReturned())\r
307         {\r
308                 st.ResultText(0,val1);\r
309                 st.ResultText(1,val2);\r
310                 st.ResultText(2,val3);\r
311 \r
312                 requestindex=0;\r
313                 StringFunctions::Convert(val3,requestindex);\r
314 \r
315                 // only continue if index is < max messages we will accept from a peer\r
316                 if(requestindex<m_maxpeermessages)\r
317                 {\r
318                         if(m_ids.find(val1+"*"+val2+"*"+val3)==m_ids.end())\r
319                         {\r
320                                 m_ids[val1+"*"+val2+"*"+val3]=false;\r
321                         }\r
322                 }\r
323                 st.Step();\r
324         }\r
325 \r
326 }\r
327 \r
328 void MessageRequester::StartRequest(const std::string &requestid)\r
329 {\r
330         FCPMessage message;\r
331         std::vector<std::string> parts;\r
332         std::string tempval;\r
333         long identityid;\r
334         std::string date;\r
335         std::string indexstr;\r
336         std::string publickey;\r
337 \r
338         StringFunctions::Split(requestid,"*",parts);\r
339         StringFunctions::Convert(parts[0],identityid);\r
340         StringFunctions::Convert(parts[1],date);\r
341         indexstr=parts[2];\r
342 \r
343         SQLite3DB::Statement st=m_db->Prepare("SELECT PublicKey FROM tblIdentity WHERE IdentityID=?;");\r
344         st.Bind(0,identityid);\r
345         st.Step();\r
346 \r
347         if(st.RowReturned())\r
348         {\r
349                 st.ResultText(0,publickey);\r
350 \r
351                 message.SetName("ClientGet");\r
352                 message["URI"]=publickey+m_messagebase+"|"+date+"|Message|"+indexstr+".xml";\r
353                 message["Identifier"]=m_fcpuniquename+"|"+requestid+"|"+parts[0]+"|"+parts[1]+"|"+parts[2]+"|"+message["URI"];\r
354                 message["ReturnType"]="direct";\r
355                 message["MaxSize"]="1000000";           // 1 MB\r
356                 message["MaxRetries"]="-1";                     // use new ULPR since we are fairly sure message exists since the author says it does\r
357 \r
358                 m_fcp->SendMessage(message);\r
359 \r
360                 m_requesting.push_back(requestid);\r
361 \r
362                 m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"MessageRequester::StartRequest requesting "+message["Identifier"]);\r
363         }\r
364         \r
365         m_ids[requestid]=true;\r
366 \r
367 }\r