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