20fb31febff618537c5c958665f52c8b1ab39e76
[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::Connection *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(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::Instance()->GetInt("MaxIdentityIntroductionRequests",m_maxrequests);\r
182         if(m_maxrequests<1)\r
183         {\r
184                 m_maxrequests=1;\r
185                 m_log->error("Option MaxIdentityIntroductionRequests is currently less than 1.  It must be 1 or greater.");\r
186         }\r
187         if(m_maxrequests>100)\r
188         {\r
189                 m_log->warning("Option MaxIdentityIntroductionRequests is currently set at more than 100.  This value might be incorrectly configured.");\r
190         }\r
191         Option::Instance()->Get("MessageBase",m_messagebase);\r
192         m_tempdate=Poco::Timestamp();\r
193 }\r
194 \r
195 void IdentityIntroductionRequester::PopulateIDList()\r
196 {\r
197         Poco::DateTime date;\r
198         int id;\r
199 \r
200         date-=Poco::Timespan(1,0,0,0,0);\r
201 \r
202         // get all identities that have unsolved puzzles from yesterday or today\r
203         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
204         st.Step();\r
205 \r
206         m_ids.clear();\r
207 \r
208         while(st.RowReturned())\r
209         {\r
210                 st.ResultInt(0,id);\r
211                 m_ids[id]=false;\r
212                 st.Step();\r
213         }\r
214 \r
215 }\r
216 \r
217 void IdentityIntroductionRequester::Process()\r
218 {\r
219         // max is the smaller of the config value or the total number of identities we will request from\r
220         long max=m_maxrequests>m_ids.size() ? m_ids.size() : m_maxrequests;\r
221 \r
222                 // try to keep up to max requests going\r
223         if(m_requesting.size()<max)\r
224         {\r
225                 std::map<long,bool>::iterator i=m_ids.begin();\r
226                 while(i!=m_ids.end() && (*i).second==true)\r
227                 {\r
228                         i++;\r
229                 }\r
230 \r
231                 if(i!=m_ids.end())\r
232                 {\r
233                         StartRequests((*i).first);\r
234                 }\r
235                 else\r
236                 {\r
237                         // we requested from all ids in the list, repopulate the list\r
238                         PopulateIDList();\r
239                 }\r
240         }\r
241         // 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
242         // this will recheck for ids every minute\r
243         Poco::DateTime now;\r
244         if(m_ids.size()==0 && m_tempdate<(now-Poco::Timespan(0,0,1,0,0)))\r
245         {\r
246                 PopulateIDList();\r
247                 m_tempdate=now;\r
248         }\r
249 }\r
250 \r
251 void IdentityIntroductionRequester::RegisterWithThread(FreenetMasterThread *thread)\r
252 {\r
253         thread->RegisterFCPConnected(this);\r
254         thread->RegisterFCPMessageHandler(this);\r
255         thread->RegisterPeriodicProcessor(this);\r
256 }\r
257 \r
258 void IdentityIntroductionRequester::RemoveFromRequestList(const std::string &UUID)\r
259 {\r
260         std::vector<std::string>::iterator i=m_requesting.begin();\r
261         while(i!=m_requesting.end() && (*i)!=UUID)\r
262         {\r
263                 i++;\r
264         }\r
265         if(i!=m_requesting.end())\r
266         {\r
267                 m_requesting.erase(i);\r
268         }\r
269 }\r
270 \r
271 void IdentityIntroductionRequester::StartRequest(const std::string &UUID)\r
272 {\r
273         std::string day;\r
274         std::string solution;\r
275         std::string encodedhash;\r
276         FCPv2::Message message;\r
277         SQLite3DB::Statement st=m_db->Prepare("SELECT Day, PuzzleSolution FROM tblIntroductionPuzzleInserts WHERE FoundSolution='false' AND UUID=?;");\r
278         st.Bind(0,UUID);\r
279         st.Step();\r
280 \r
281         if(st.RowReturned())\r
282         {\r
283                 st.ResultText(0,day);\r
284                 st.ResultText(1,solution);\r
285 \r
286                 // get the hash of the solution\r
287                 Poco::SHA1Engine sha1;\r
288                 sha1.update(solution);\r
289                 encodedhash=Poco::DigestEngine::digestToHex(sha1.digest());\r
290                 StringFunctions::UpperCase(encodedhash,encodedhash);\r
291 \r
292                 //start request for the solution\r
293                 message.SetName("ClientGet");\r
294                 message["URI"]="KSK@"+m_messagebase+"|"+day+"|"+UUID+"|"+encodedhash+".xml";\r
295                 message["Identifier"]="IdentityIntroductionRequester|"+message["URI"];\r
296                 message["ReturnType"]="direct";\r
297                 message["MaxSize"]="10000";\r
298 \r
299                 m_fcp->Send(message);\r
300 \r
301                 m_requesting.push_back(UUID);\r
302 \r
303         }\r
304 \r
305 }\r
306 \r
307 void IdentityIntroductionRequester::StartRequests(const long localidentityid)\r
308 {\r
309         Poco::DateTime date;\r
310         std::string localidentityidstr;\r
311         std::string uuid;\r
312 \r
313         date-=Poco::Timespan(1,0,0,0,0);\r
314         StringFunctions::Convert(localidentityid,localidentityidstr);\r
315 \r
316         // get all non-solved puzzles from yesterday and today for this identity\r
317         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
318         st.Step();\r
319 \r
320         // start requests for all non-solved puzzles\r
321         while(st.RowReturned())\r
322         {\r
323                 uuid="";\r
324                 st.ResultText(0,uuid);\r
325                 StartRequest(uuid);\r
326                 st.Step();\r
327         }\r
328 \r
329         m_ids[localidentityid]=true;\r
330 \r
331 }\r