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