80aacb1d857360199d8ed06323a4e93054c316f2
[fms.git] / src / freenet / introductionpuzzlerequester.cpp
1 #include "../../include/freenet/introductionpuzzlerequester.h"\r
2 #include "../../include/freenet/introductionpuzzlexml.h"\r
3 #include "../../include/option.h"\r
4 #include "../../include/stringfunctions.h"\r
5 #include "../../include/bitmapvalidator.h"\r
6 #include "../../include/base64.h"\r
7 \r
8 #include <Poco/DateTime.h>\r
9 #include <Poco/Timespan.h>\r
10 #include <Poco/Timestamp.h>\r
11 #include <Poco/DateTimeFormatter.h>\r
12 \r
13 #ifdef XMEM\r
14         #include <xmem.h>\r
15 #endif\r
16 \r
17 IntroductionPuzzleRequester::IntroductionPuzzleRequester()\r
18 {\r
19         Initialize();\r
20 }\r
21 \r
22 IntroductionPuzzleRequester::IntroductionPuzzleRequester(FCPv2 *fcp):IIndexRequester<long>(fcp)\r
23 {\r
24         Initialize();\r
25 }\r
26 \r
27 const bool IntroductionPuzzleRequester::HandleAllData(FCPMessage &message)\r
28 {\r
29         Poco::DateTime now;\r
30         SQLite3DB::Statement st;\r
31         std::vector<std::string> idparts;\r
32         long datalength;\r
33         std::vector<char> data;\r
34         IntroductionPuzzleXML xml;\r
35         long identityid;\r
36         long index;\r
37         bool validmessage=true;\r
38 \r
39         StringFunctions::Split(message["Identifier"],"|",idparts);\r
40         StringFunctions::Convert(message["DataLength"],datalength);\r
41         StringFunctions::Convert(idparts[1],identityid);\r
42         StringFunctions::Convert(idparts[2],index);\r
43 \r
44         // wait for all data to be received from connection\r
45         while(m_fcp->Connected() && m_fcp->ReceiveBufferSize()<datalength)\r
46         {\r
47                 m_fcp->Update(1);\r
48         }\r
49 \r
50         // if we got disconnected- return immediately\r
51         if(m_fcp->Connected()==false)\r
52         {\r
53                 return false;\r
54         }\r
55 \r
56         // receive the file\r
57         data.resize(datalength);\r
58         if(data.size()>0)\r
59         {\r
60                 m_fcp->ReceiveRaw(&data[0],datalength);\r
61         }\r
62 \r
63         // parse file into xml and update the database\r
64         if(data.size()>0 && xml.ParseXML(std::string(data.begin(),data.end()))==true)\r
65         {\r
66 \r
67                 // check if last part of UUID matches first part of public key of identity who inserted it\r
68                 st=m_db->Prepare("SELECT PublicKey FROM tblIdentity WHERE IdentityID=?;");\r
69                 st.Bind(0,identityid);\r
70                 st.Step();\r
71                 if(st.RowReturned())\r
72                 {\r
73                         std::vector<std::string> uuidparts;\r
74                         std::vector<std::string> keyparts;\r
75                         std::string keypart="";\r
76                         std::string publickey="";\r
77 \r
78                         st.ResultText(0,publickey);\r
79 \r
80                         StringFunctions::SplitMultiple(publickey,"@,",keyparts);\r
81                         StringFunctions::SplitMultiple(xml.GetUUID(),"@",uuidparts);\r
82 \r
83                         if(uuidparts.size()>1 && keyparts.size()>1)\r
84                         {\r
85                                 keypart=StringFunctions::Replace(StringFunctions::Replace(keyparts[1],"-",""),"~","");\r
86                                 if(keypart!=uuidparts[1])\r
87                                 {\r
88                                         m_log->error("IntroductionPuzzleRequester::HandleAllData UUID in IntroductionPuzzle doesn't match public key of identity : "+message["Identifier"]);\r
89                                         validmessage=false;\r
90                                 }\r
91                         }\r
92                         else\r
93                         {\r
94                                 m_log->error("IntroductionPuzzleRequester::HandleAllData Error with identity's public key or UUID : "+message["Identifier"]);\r
95                                 validmessage=false;\r
96                         }\r
97 \r
98                 }\r
99                 else\r
100                 {\r
101                         m_log->error("IntroductionPuzzleRequester::HandleAllData Error couldn't find identity : "+message["Identifier"]);\r
102                         validmessage=false;\r
103                 }\r
104 \r
105                 // we can only validate bitmaps for now\r
106                 BitmapValidator val;\r
107                 val.SetMax(200,200);\r
108                 std::vector<unsigned char> puzzledata;\r
109                 Base64::Decode(xml.GetPuzzleData(),puzzledata);\r
110                 if(xml.GetMimeType()!="image/bmp" || val.Validate(puzzledata)==false)\r
111                 {\r
112                         m_log->error("IntroductionPuzzleRequester::HandleAllData received bad mime type and/or data for "+message["Identifier"]);\r
113                         validmessage=false;\r
114                 }\r
115 \r
116                 st=m_db->Prepare("INSERT INTO tblIntroductionPuzzleRequests(IdentityID,Day,RequestIndex,Found,UUID,Type,MimeType,PuzzleData) VALUES(?,?,?,?,?,?,?,?);");\r
117                 st.Bind(0,identityid);\r
118                 st.Bind(1,idparts[4]);\r
119                 st.Bind(2,index);\r
120                 if(validmessage)\r
121                 {\r
122                         st.Bind(3,"true");\r
123                         st.Bind(4,xml.GetUUID());\r
124                         st.Bind(5,xml.GetType());\r
125                         st.Bind(6,xml.GetMimeType());\r
126                         st.Bind(7,xml.GetPuzzleData());\r
127                 }\r
128                 else\r
129                 {\r
130                         st.Bind(3,"false");\r
131                         st.Bind(4);\r
132                         st.Bind(5);\r
133                         st.Bind(6);\r
134                         st.Bind(7);\r
135                 }\r
136                 st.Step();\r
137                 st.Finalize();\r
138 \r
139                 m_log->debug("IntroductionPuzzleRequester::HandleAllData parsed IntroductionPuzzle XML file : "+message["Identifier"]);\r
140         }\r
141         else\r
142         {\r
143                 // bad data - mark index\r
144                 st=m_db->Prepare("INSERT INTO tblIntroductionPuzzleRequests(IdentityID,Day,RequestIndex,Found) VALUES(?,?,?,'false');");\r
145                 st.Bind(0,identityid);\r
146                 st.Bind(1,idparts[4]);\r
147                 st.Bind(2,index);\r
148                 st.Step();\r
149                 st.Finalize();\r
150 \r
151                 m_log->error("IntroductionPuzzleRequester::HandleAllData error parsing IntroductionPuzzle XML file : "+message["Identifier"]);\r
152         }\r
153 \r
154         // remove this identityid from request list\r
155         RemoveFromRequestList(identityid);\r
156 \r
157         return true;\r
158 \r
159 }\r
160 \r
161 const bool IntroductionPuzzleRequester::HandleGetFailed(FCPMessage &message)\r
162 {\r
163         SQLite3DB::Statement st;\r
164         std::vector<std::string> idparts;\r
165         long identityid;\r
166         long index;\r
167 \r
168         StringFunctions::Split(message["Identifier"],"|",idparts);\r
169         StringFunctions::Convert(idparts[1],identityid);\r
170         StringFunctions::Convert(idparts[2],index);     \r
171 \r
172         // if this is a fatal error - insert index into database so we won't try to download this index again\r
173         if(message["Fatal"]=="true")\r
174         {\r
175                 st=m_db->Prepare("INSERT INTO tblIntroductionPuzzleRequests(IdentityID,Day,RequestIndex,Found) VALUES(?,?,?,'false');");\r
176                 st.Bind(0,identityid);\r
177                 st.Bind(1,idparts[4]);\r
178                 st.Bind(2,index);\r
179                 st.Step();\r
180                 st.Finalize();\r
181 \r
182                 m_log->error("IntroductionPuzzleRequester::HandleGetFailed fatal error requesting "+message["Identifier"]);\r
183         }\r
184 \r
185         // remove this identityid from request list\r
186         RemoveFromRequestList(identityid);\r
187 \r
188         return true;\r
189 \r
190 }\r
191 \r
192 void IntroductionPuzzleRequester::Initialize()\r
193 {\r
194         m_fcpuniquename="IntroductionPuzzleRequester";\r
195         m_maxrequests=0;\r
196         Option::Instance()->GetInt("MaxIntroductionPuzzleRequests",m_maxrequests);\r
197         if(m_maxrequests<1)\r
198         {\r
199                 m_maxrequests=1;\r
200                 m_log->error("Option MaxIntroductionPuzzleRequests is currently set at less than 1.  It must be 1 or greater.");\r
201         }\r
202         if(m_maxrequests>100)\r
203         {\r
204                 m_log->warning("Option MaxIntroductionPuzzleRequests is currently set at more than 100.  This value might be incorrectly configured.");\r
205         }\r
206 }\r
207 \r
208 void IntroductionPuzzleRequester::PopulateIDList()\r
209 {\r
210         Poco::DateTime now;\r
211         int id;\r
212         std::string limitnum="30";\r
213 \r
214         // if we don't have an identity that we haven't seen yet, then set limit to 5\r
215         SQLite3DB::Statement st=m_db->Prepare("SELECT tblLocalIdentity.LocalIdentityID FROM tblLocalIdentity LEFT JOIN tblIdentity ON tblLocalIdentity.PublicKey=tblIdentity.PublicKey WHERE tblIdentity.IdentityID IS NULL;");\r
216         st.Step();\r
217         if(!st.RowReturned())\r
218         {\r
219                 limitnum="5";\r
220         }\r
221         st.Finalize();\r
222 \r
223         // select identities that aren't single use, are publishing a trust list, and have been seen today ( order by trust DESC and limit to limitnum )\r
224         st=m_db->Prepare("SELECT IdentityID FROM tblIdentity WHERE PublishTrustList='true' AND PublicKey IS NOT NULL AND PublicKey <> '' AND SingleUse='false' AND (LocalTrustListTrust IS NULL OR LocalTrustListTrust>=(SELECT OptionValue FROM tblOption WHERE Option='MinLocalTrustListTrust')) AND LastSeen>='"+Poco::DateTimeFormatter::format(now,"%Y-%m-%d")+"' ORDER BY LocalMessageTrust DESC LIMIT 0,"+limitnum+";");\r
225         st.Step();\r
226 \r
227         m_ids.clear();\r
228 \r
229         while(st.RowReturned())\r
230         {\r
231                 st.ResultInt(0,id);\r
232                 m_ids[id]=false;\r
233                 st.Step();\r
234         }\r
235 }\r
236 \r
237 void IntroductionPuzzleRequester::StartRequest(const long &identityid)\r
238 {\r
239         Poco::DateTime now;\r
240         FCPMessage message;\r
241         std::string publickey;\r
242         int index;\r
243         std::string indexstr;\r
244         std::string identityidstr;\r
245 \r
246         SQLite3DB::Statement st=m_db->Prepare("SELECT PublicKey FROM tblIdentity WHERE IdentityID=?;");\r
247         st.Bind(0,identityid);\r
248         st.Step();\r
249 \r
250         if(st.RowReturned())\r
251         {\r
252                 st.ResultText(0,publickey);\r
253 \r
254                 now=Poco::Timestamp();\r
255 \r
256                 SQLite3DB::Statement st2=m_db->Prepare("SELECT MAX(RequestIndex) FROM tblIntroductionPuzzleRequests WHERE Day=? AND IdentityID=?;");\r
257                 st2.Bind(0,Poco::DateTimeFormatter::format(now,"%Y-%m-%d"));\r
258                 st2.Bind(1,identityid);\r
259                 st2.Step();\r
260 \r
261                 index=0;\r
262                 if(st2.RowReturned())\r
263                 {\r
264                         if(st2.ResultNull(0)==false)\r
265                         {\r
266                                 st2.ResultInt(0,index);\r
267                                 index++;\r
268                         }\r
269                 }\r
270                 st2.Finalize();\r
271 \r
272                 StringFunctions::Convert(index,indexstr);\r
273                 StringFunctions::Convert(identityid,identityidstr);\r
274 \r
275                 message.SetName("ClientGet");\r
276                 message["URI"]=publickey+m_messagebase+"|"+Poco::DateTimeFormatter::format(now,"%Y-%m-%d")+"|IntroductionPuzzle|"+indexstr+".xml";\r
277                 message["Identifier"]=m_fcpuniquename+"|"+identityidstr+"|"+indexstr+"|"+message["URI"];\r
278                 message["ReturnType"]="direct";\r
279                 message["MaxSize"]="1000000";           // 1 MB\r
280 \r
281                 m_fcp->SendMessage(message);\r
282                 \r
283                 m_requesting.push_back(identityid);\r
284         }\r
285 \r
286         m_ids[identityid]=true;\r
287 }\r