0e5e3ddb637d27b5dcd478d81ed8a7ffd7cf2496
[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/xyssl/sha1.h"\r
7 #include "../../include/hex.h"\r
8 \r
9 #ifdef XMEM\r
10         #include <xmem.h>\r
11 #endif\r
12 \r
13 IdentityIntroductionRequester::IdentityIntroductionRequester()\r
14 {\r
15         Initialize();\r
16 }\r
17 \r
18 IdentityIntroductionRequester::IdentityIntroductionRequester(FCPv2 *fcp):IFCPConnected(fcp)\r
19 {\r
20         Initialize();\r
21 }\r
22 \r
23 void IdentityIntroductionRequester::FCPConnected()\r
24 {\r
25         m_requesting.clear();\r
26         PopulateIDList();\r
27 }\r
28 \r
29 void IdentityIntroductionRequester::FCPDisconnected()\r
30 {\r
31         \r
32 }\r
33 \r
34 const bool IdentityIntroductionRequester::HandleAllData(FCPMessage &message)\r
35 {\r
36         FreenetSSK ssk;\r
37         DateTime date;\r
38         std::vector<std::string> idparts;\r
39         std::vector<char> data;\r
40         long datalength;\r
41         IdentityIntroductionXML xml;\r
42 \r
43         StringFunctions::Split(message["Identifier"],"|",idparts);\r
44         StringFunctions::Convert(message["DataLength"],datalength);\r
45 \r
46         // wait for all data to be received from connection\r
47         while(m_fcp->Connected() && m_fcp->ReceiveBufferSize()<datalength)\r
48         {\r
49                 m_fcp->Update(1);\r
50         }\r
51 \r
52         // if we got disconnected- return immediately\r
53         if(m_fcp->Connected()==false)\r
54         {\r
55                 return false;\r
56         }\r
57 \r
58         // receive the file\r
59         data.resize(datalength);\r
60         m_fcp->ReceiveRaw(&data[0],datalength);\r
61 \r
62         // parse file into xml and update the database\r
63         if(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.SetToGMTime();\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,date.Format("%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->WriteLog(LogFile::LOGLEVEL_DEBUG,"IdentityIntroductionRequester::HandleAddData parsed a valid identity.");\r
94                 }\r
95                 else\r
96                 {\r
97                         m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"IdentityIntroductionRequester::HandleAllData parsed, public SSK key was not valid.");\r
98                 }\r
99 \r
100                 m_log->WriteLog(LogFile::LOGLEVEL_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->WriteLog(LogFile::LOGLEVEL_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(FCPMessage &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->WriteLog(LogFile::LOGLEVEL_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(FCPMessage &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         std::string tempval="";\r
181         Option::Instance()->Get("MaxIdentityIntroductionRequests",tempval);\r
182         StringFunctions::Convert(tempval,m_maxrequests);\r
183         if(m_maxrequests<1)\r
184         {\r
185                 m_maxrequests=1;\r
186                 m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"Option MaxIdentityIntroductionRequests is currently set at "+tempval+".  It must be 1 or greater.");\r
187         }\r
188         if(m_maxrequests>100)\r
189         {\r
190                 m_log->WriteLog(LogFile::LOGLEVEL_WARNING,"Option MaxIdentityIntroductionRequests is currently set at "+tempval+".  This value might be incorrectly configured.");\r
191         }\r
192         Option::Instance()->Get("MessageBase",m_messagebase);\r
193         m_tempdate.SetToGMTime();\r
194 }\r
195 \r
196 void IdentityIntroductionRequester::PopulateIDList()\r
197 {\r
198         DateTime date;\r
199         int id;\r
200 \r
201         date.SetToGMTime();\r
202         date.Add(0,0,0,-1);\r
203 \r
204         // get all identities that have unsolved puzzles from yesterday or today\r
205         SQLite3DB::Statement st=m_db->Prepare("SELECT LocalIdentityID FROM tblIntroductionPuzzleInserts WHERE Day>='"+date.Format("%Y-%m-%d")+"' AND FoundSolution='false' GROUP BY LocalIdentityID;");\r
206         st.Step();\r
207 \r
208         m_ids.clear();\r
209 \r
210         while(st.RowReturned())\r
211         {\r
212                 st.ResultInt(0,id);\r
213                 m_ids[id]=false;\r
214                 st.Step();\r
215         }\r
216 \r
217 }\r
218 \r
219 void IdentityIntroductionRequester::Process()\r
220 {\r
221         // max is the smaller of the config value or the total number of identities we will request from\r
222         long max=m_maxrequests>m_ids.size() ? m_ids.size() : m_maxrequests;\r
223 \r
224                 // try to keep up to max requests going\r
225         if(m_requesting.size()<max)\r
226         {\r
227                 std::map<long,bool>::iterator i=m_ids.begin();\r
228                 while(i!=m_ids.end() && (*i).second==true)\r
229                 {\r
230                         i++;\r
231                 }\r
232 \r
233                 if(i!=m_ids.end())\r
234                 {\r
235                         StartRequests((*i).first);\r
236                 }\r
237                 else\r
238                 {\r
239                         // we requested from all ids in the list, repopulate the list\r
240                         PopulateIDList();\r
241                 }\r
242         }\r
243         // 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
244         // this will recheck for ids every minute\r
245         DateTime now;\r
246         now.SetToGMTime();\r
247         if(m_ids.size()==0 && m_tempdate<(now-(1.0/1440.0)))\r
248         {\r
249                 PopulateIDList();\r
250                 m_tempdate=now;\r
251         }\r
252 }\r
253 \r
254 void IdentityIntroductionRequester::RegisterWithThread(FreenetMasterThread *thread)\r
255 {\r
256         thread->RegisterFCPConnected(this);\r
257         thread->RegisterFCPMessageHandler(this);\r
258         thread->RegisterPeriodicProcessor(this);\r
259 }\r
260 \r
261 void IdentityIntroductionRequester::RemoveFromRequestList(const std::string &UUID)\r
262 {\r
263         std::vector<std::string>::iterator i=m_requesting.begin();\r
264         while(i!=m_requesting.end() && (*i)!=UUID)\r
265         {\r
266                 i++;\r
267         }\r
268         if(i!=m_requesting.end())\r
269         {\r
270                 m_requesting.erase(i);\r
271         }\r
272 }\r
273 \r
274 void IdentityIntroductionRequester::StartRequest(const std::string &UUID)\r
275 {\r
276         std::string day;\r
277         std::string solution;\r
278         std::vector<unsigned char> solutionhash;\r
279         std::string encodedhash;\r
280         FCPMessage message;\r
281         SQLite3DB::Statement st=m_db->Prepare("SELECT Day, PuzzleSolution FROM tblIntroductionPuzzleInserts WHERE FoundSolution='false' AND UUID=?;");\r
282         st.Bind(0,UUID);\r
283         st.Step();\r
284 \r
285         if(st.RowReturned())\r
286         {\r
287                 st.ResultText(0,day);\r
288                 st.ResultText(1,solution);\r
289 \r
290                 // get the hash of the solution\r
291                 solutionhash.resize(20);\r
292                 sha1((unsigned char *)solution.c_str(),solution.size(),&solutionhash[0]);\r
293                 Hex::Encode(solutionhash,encodedhash);\r
294 \r
295                 //start request for the solution\r
296                 message.SetName("ClientGet");\r
297                 message["URI"]="KSK@"+m_messagebase+"|"+day+"|"+UUID+"|"+encodedhash+".xml";\r
298                 message["Identifier"]="IdentityIntroductionRequester|"+message["URI"];\r
299                 message["ReturnType"]="direct";\r
300                 message["MaxSize"]="10000";\r
301 \r
302                 m_fcp->SendMessage(message);\r
303 \r
304                 m_requesting.push_back(UUID);\r
305 \r
306         }\r
307 \r
308 }\r
309 \r
310 void IdentityIntroductionRequester::StartRequests(const long localidentityid)\r
311 {\r
312         DateTime date;\r
313         std::string localidentityidstr;\r
314         std::string uuid;\r
315 \r
316         date.SetToGMTime();\r
317         date.Add(0,0,0,-1);\r
318         StringFunctions::Convert(localidentityid,localidentityidstr);\r
319 \r
320         // get all non-solved puzzles from yesterday and today for this identity\r
321         SQLite3DB::Statement st=m_db->Prepare("SELECT UUID FROM tblIntroductionPuzzleInserts WHERE LocalIdentityID="+localidentityidstr+" AND Day>='"+date.Format("%Y-%m-%d")+"' AND FoundSolution='false';");\r
322         st.Step();\r
323 \r
324         // start requests for all non-solved puzzles\r
325         while(st.RowReturned())\r
326         {\r
327                 uuid="";\r
328                 st.ResultText(0,uuid);\r
329                 StartRequest(uuid);\r
330                 st.Step();\r
331         }\r
332 \r
333         m_ids[localidentityid]=true;\r
334 \r
335 }\r