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