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