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