version 0.3.20
[fms.git] / src / freenet / identityintroductionrequester.cpp
1 #include "../../include/freenet/identityintroductionrequester.h"\r
2 #include "../../include/freenet/identityintroductionxml.h"\r
3 #include "../../include/freenet/freenetssk.h"\r
4 #include "../../include/option.h"\r
5 #include "../../include/stringfunctions.h"\r
6 #include "../../include/hex.h"\r
7 \r
8 #include <Poco/DateTime.h>\r
9 #include <Poco/Timestamp.h>\r
10 #include <Poco/DateTimeFormatter.h>\r
11 #include <Poco/SHA1Engine.h>\r
12 \r
13 #ifdef XMEM\r
14         #include <xmem.h>\r
15 #endif\r
16 \r
17 IdentityIntroductionRequester::IdentityIntroductionRequester()\r
18 {\r
19         Initialize();\r
20 }\r
21 \r
22 IdentityIntroductionRequester::IdentityIntroductionRequester(FCPv2 *fcp):IFCPConnected(fcp)\r
23 {\r
24         Initialize();\r
25 }\r
26 \r
27 void IdentityIntroductionRequester::FCPConnected()\r
28 {\r
29         m_requesting.clear();\r
30         PopulateIDList();\r
31 }\r
32 \r
33 void IdentityIntroductionRequester::FCPDisconnected()\r
34 {\r
35         \r
36 }\r
37 \r
38 const bool IdentityIntroductionRequester::HandleAllData(FCPMessage &message)\r
39 {\r
40         FreenetSSK ssk;\r
41         Poco::DateTime date;\r
42         std::vector<std::string> idparts;\r
43         std::vector<char> data;\r
44         long datalength;\r
45         IdentityIntroductionXML xml;\r
46 \r
47         StringFunctions::Split(message["Identifier"],"|",idparts);\r
48         StringFunctions::Convert(message["DataLength"],datalength);\r
49 \r
50         // wait for all data to be received from connection\r
51         while(m_fcp->Connected() && m_fcp->ReceiveBufferSize()<datalength)\r
52         {\r
53                 m_fcp->Update(1);\r
54         }\r
55 \r
56         // if we got disconnected- return immediately\r
57         if(m_fcp->Connected()==false)\r
58         {\r
59                 return false;\r
60         }\r
61 \r
62         // receive the file\r
63         data.resize(datalength);\r
64         if(data.size()>0)\r
65         {\r
66                 m_fcp->ReceiveRaw(&data[0],datalength);\r
67         }\r
68 \r
69         // parse file into xml and update the database\r
70         if(data.size()>0 && xml.ParseXML(std::string(data.begin(),data.end()))==true)\r
71         {\r
72 \r
73                 ssk.SetPublicKey(xml.GetIdentity());\r
74 \r
75                 // mark puzzle found\r
76                 SQLite3DB::Statement st=m_db->Prepare("UPDATE tblIntroductionPuzzleInserts SET FoundSolution='true' WHERE UUID=?;");\r
77                 st.Bind(0,idparts[3]);\r
78                 st.Step();\r
79                 st.Finalize();\r
80 \r
81                 if(ssk.ValidPublicKey()==true)\r
82                 {\r
83                         // try to find existing identity with this SSK\r
84                         st=m_db->Prepare("SELECT IdentityID FROM tblIdentity WHERE PublicKey=?;");\r
85                         st.Bind(0,xml.GetIdentity());\r
86                         st.Step();\r
87                         if(st.RowReturned()==false)\r
88                         {\r
89                                 // we don't already know about this id - add it\r
90                                 st.Finalize();\r
91                                 date=Poco::Timestamp();\r
92                                 st=m_db->Prepare("INSERT INTO tblIdentity(PublicKey,DateAdded,AddedMethod) VALUES(?,?,?);");\r
93                                 st.Bind(0,xml.GetIdentity());\r
94                                 st.Bind(1,Poco::DateTimeFormatter::format(date,"%Y-%m-%d %H:%M:%S"));\r
95                                 st.Bind(2,"solved captcha");\r
96                                 st.Step();\r
97                         }\r
98                         st.Finalize();\r
99 \r
100                         m_log->debug("IdentityIntroductionRequester::HandleAddData parsed a valid identity.");\r
101                 }\r
102                 else\r
103                 {\r
104                         m_log->error("IdentityIntroductionRequester::HandleAllData parsed, public SSK key was not valid.");\r
105                 }\r
106 \r
107                 m_log->debug("IdentityIntroductionRequester::HandleAllData parsed IdentityIntroduction XML file : "+message["Identifier"]);\r
108         }\r
109         else\r
110         {\r
111 \r
112                 SQLite3DB::Statement st=m_db->Prepare("UPDATE tblIntroductionPuzzleInserts SET FoundSolution='true' WHERE UUID=?;");\r
113                 st.Bind(0,idparts[3]);\r
114                 st.Step();\r
115                 st.Finalize();          \r
116 \r
117                 m_log->error("IdentityIntroductionRequester::HandleAllData error parsing IdentityIntroduction XML file : "+message["Identifier"]);\r
118         }\r
119 \r
120         // remove UUID from request list\r
121         RemoveFromRequestList(idparts[3]);\r
122 \r
123         return true;\r
124 }\r
125 \r
126 const bool IdentityIntroductionRequester::HandleGetFailed(FCPMessage &message)\r
127 {\r
128         std::vector<std::string> idparts;\r
129 \r
130         StringFunctions::Split(message["Identifier"],"|",idparts);\r
131 \r
132         // fatal error - don't try to download again\r
133         if(message["Fatal"]=="true")\r
134         {\r
135                 SQLite3DB::Statement st=m_db->Prepare("UPDATE tblIntroductionPuzzleInserts SET FoundSolution='true' WHERE UUID=?;");\r
136                 st.Bind(0,idparts[3]);\r
137                 st.Step();\r
138                 st.Finalize();\r
139 \r
140                 m_log->debug("IdentityIntroductionRequester::HandleAllData Fatal GetFailed for "+message["Identifier"]);\r
141         }\r
142 \r
143         // remove UUID from request list\r
144         RemoveFromRequestList(idparts[3]);\r
145 \r
146         return true;\r
147 }\r
148 \r
149 const bool IdentityIntroductionRequester::HandleMessage(FCPMessage &message)\r
150 {\r
151 \r
152         if(message["Identifier"].find("IdentityIntroductionRequester")==0)\r
153         {\r
154                 \r
155                 // ignore DataFound\r
156                 if(message.GetName()=="DataFound")\r
157                 {\r
158                         return true;\r
159                 }\r
160 \r
161                 if(message.GetName()=="AllData")\r
162                 {\r
163                         return HandleAllData(message);\r
164                 }\r
165 \r
166                 if(message.GetName()=="GetFailed")\r
167                 {\r
168                         return HandleGetFailed(message);\r
169                 }\r
170 \r
171                 if(message.GetName()=="IdentifierCollision")\r
172                 {\r
173                         // remove one of the ids from the requesting list\r
174                         std::vector<std::string> idparts;\r
175                         StringFunctions::Split(message["Identifier"],"|",idparts);\r
176                         RemoveFromRequestList(idparts[3]);\r
177                         return true;\r
178                 }\r
179 \r
180         }\r
181 \r
182         return false;\r
183 }\r
184 \r
185 void IdentityIntroductionRequester::Initialize()\r
186 {\r
187         m_maxrequests=0;\r
188         Option::Instance()->GetInt("MaxIdentityIntroductionRequests",m_maxrequests);\r
189         if(m_maxrequests<1)\r
190         {\r
191                 m_maxrequests=1;\r
192                 m_log->error("Option MaxIdentityIntroductionRequests is currently less than 1.  It must be 1 or greater.");\r
193         }\r
194         if(m_maxrequests>100)\r
195         {\r
196                 m_log->warning("Option MaxIdentityIntroductionRequests is currently set at more than 100.  This value might be incorrectly configured.");\r
197         }\r
198         Option::Instance()->Get("MessageBase",m_messagebase);\r
199         m_tempdate=Poco::Timestamp();\r
200 }\r
201 \r
202 void IdentityIntroductionRequester::PopulateIDList()\r
203 {\r
204         Poco::DateTime date;\r
205         int id;\r
206 \r
207         date-=Poco::Timespan(1,0,0,0,0);\r
208 \r
209         // get all identities that have unsolved puzzles from yesterday or today\r
210         SQLite3DB::Statement st=m_db->Prepare("SELECT LocalIdentityID FROM tblIntroductionPuzzleInserts WHERE Day>='"+Poco::DateTimeFormatter::format(date,"%Y-%m-%d")+"' AND FoundSolution='false' GROUP BY LocalIdentityID;");\r
211         st.Step();\r
212 \r
213         m_ids.clear();\r
214 \r
215         while(st.RowReturned())\r
216         {\r
217                 st.ResultInt(0,id);\r
218                 m_ids[id]=false;\r
219                 st.Step();\r
220         }\r
221 \r
222 }\r
223 \r
224 void IdentityIntroductionRequester::Process()\r
225 {\r
226         // max is the smaller of the config value or the total number of identities we will request from\r
227         long max=m_maxrequests>m_ids.size() ? m_ids.size() : m_maxrequests;\r
228 \r
229                 // try to keep up to max requests going\r
230         if(m_requesting.size()<max)\r
231         {\r
232                 std::map<long,bool>::iterator i=m_ids.begin();\r
233                 while(i!=m_ids.end() && (*i).second==true)\r
234                 {\r
235                         i++;\r
236                 }\r
237 \r
238                 if(i!=m_ids.end())\r
239                 {\r
240                         StartRequests((*i).first);\r
241                 }\r
242                 else\r
243                 {\r
244                         // we requested from all ids in the list, repopulate the list\r
245                         PopulateIDList();\r
246                 }\r
247         }\r
248         // special case - if there were 0 identities on the list when we started then we will never get a chance to repopulate the list\r
249         // this will recheck for ids every minute\r
250         Poco::DateTime now;\r
251         if(m_ids.size()==0 && m_tempdate<(now-Poco::Timespan(0,0,1,0,0)))\r
252         {\r
253                 PopulateIDList();\r
254                 m_tempdate=now;\r
255         }\r
256 }\r
257 \r
258 void IdentityIntroductionRequester::RegisterWithThread(FreenetMasterThread *thread)\r
259 {\r
260         thread->RegisterFCPConnected(this);\r
261         thread->RegisterFCPMessageHandler(this);\r
262         thread->RegisterPeriodicProcessor(this);\r
263 }\r
264 \r
265 void IdentityIntroductionRequester::RemoveFromRequestList(const std::string &UUID)\r
266 {\r
267         std::vector<std::string>::iterator i=m_requesting.begin();\r
268         while(i!=m_requesting.end() && (*i)!=UUID)\r
269         {\r
270                 i++;\r
271         }\r
272         if(i!=m_requesting.end())\r
273         {\r
274                 m_requesting.erase(i);\r
275         }\r
276 }\r
277 \r
278 void IdentityIntroductionRequester::StartRequest(const std::string &UUID)\r
279 {\r
280         std::string day;\r
281         std::string solution;\r
282         std::string encodedhash;\r
283         FCPMessage message;\r
284         SQLite3DB::Statement st=m_db->Prepare("SELECT Day, PuzzleSolution FROM tblIntroductionPuzzleInserts WHERE FoundSolution='false' AND UUID=?;");\r
285         st.Bind(0,UUID);\r
286         st.Step();\r
287 \r
288         if(st.RowReturned())\r
289         {\r
290                 st.ResultText(0,day);\r
291                 st.ResultText(1,solution);\r
292 \r
293                 // get the hash of the solution\r
294                 Poco::SHA1Engine sha1;\r
295                 sha1.update(solution);\r
296                 encodedhash=Poco::DigestEngine::digestToHex(sha1.digest());\r
297                 StringFunctions::UpperCase(encodedhash,encodedhash);\r
298 \r
299                 //start request for the solution\r
300                 message.SetName("ClientGet");\r
301                 message["URI"]="KSK@"+m_messagebase+"|"+day+"|"+UUID+"|"+encodedhash+".xml";\r
302                 message["Identifier"]="IdentityIntroductionRequester|"+message["URI"];\r
303                 message["ReturnType"]="direct";\r
304                 message["MaxSize"]="10000";\r
305 \r
306                 m_fcp->SendMessage(message);\r
307 \r
308                 m_requesting.push_back(UUID);\r
309 \r
310         }\r
311 \r
312 }\r
313 \r
314 void IdentityIntroductionRequester::StartRequests(const long localidentityid)\r
315 {\r
316         Poco::DateTime date;\r
317         std::string localidentityidstr;\r
318         std::string uuid;\r
319 \r
320         date-=Poco::Timespan(1,0,0,0,0);\r
321         StringFunctions::Convert(localidentityid,localidentityidstr);\r
322 \r
323         // get all non-solved puzzles from yesterday and today for this identity\r
324         SQLite3DB::Statement st=m_db->Prepare("SELECT UUID FROM tblIntroductionPuzzleInserts WHERE LocalIdentityID="+localidentityidstr+" AND Day>='"+Poco::DateTimeFormatter::format(date,"%Y-%m-%d")+"' AND FoundSolution='false';");\r
325         st.Step();\r
326 \r
327         // start requests for all non-solved puzzles\r
328         while(st.RowReturned())\r
329         {\r
330                 uuid="";\r
331                 st.ResultText(0,uuid);\r
332                 StartRequest(uuid);\r
333                 st.Step();\r
334         }\r
335 \r
336         m_ids[localidentityid]=true;\r
337 \r
338 }\r