version 0.1.2
[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) VALUES(?,?);");\r
86                                 st.Bind(0,xml.GetIdentity());\r
87                                 st.Bind(1,date.Format("%Y-%m-%d %H:%M:%S"));\r
88                                 st.Step();\r
89                         }\r
90                         st.Finalize();\r
91 \r
92                         m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"IdentityIntroductionRequester::HandleAddData parsed a valid identity.");\r
93                 }\r
94                 else\r
95                 {\r
96                         m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"IdentityIntroductionRequester::HandleAllData parsed, public SSK key was not valid.");\r
97                 }\r
98 \r
99                 m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"IdentityIntroductionRequester::HandleAllData parsed IdentityIntroduction XML file : "+message["Identifier"]);\r
100         }\r
101         else\r
102         {\r
103 \r
104                 SQLite3DB::Statement st=m_db->Prepare("UPDATE tblIntroductionPuzzleInserts SET FoundSolution='true' WHERE UUID=?;");\r
105                 st.Bind(0,idparts[3]);\r
106                 st.Step();\r
107                 st.Finalize();          \r
108 \r
109                 m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"IdentityIntroductionRequester::HandleAllData error parsing IdentityIntroduction XML file : "+message["Identifier"]);\r
110         }\r
111 \r
112         // remove UUID from request list\r
113         RemoveFromRequestList(idparts[3]);\r
114 \r
115         return true;\r
116 }\r
117 \r
118 const bool IdentityIntroductionRequester::HandleGetFailed(FCPMessage &message)\r
119 {\r
120         std::vector<std::string> idparts;\r
121 \r
122         StringFunctions::Split(message["Identifier"],"|",idparts);\r
123 \r
124         // fatal error - don't try to download again\r
125         if(message["Fatal"]=="true")\r
126         {\r
127                 SQLite3DB::Statement st=m_db->Prepare("UPDATE tblIntroductionPuzzleInserts SET FoundSolution='true' WHERE UUID=?;");\r
128                 st.Bind(0,idparts[3]);\r
129                 st.Step();\r
130                 st.Finalize();\r
131 \r
132                 m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"IdentityIntroductionRequester::HandleAllData Fatal GetFailed for "+message["Identifier"]);\r
133         }\r
134 \r
135         // remove UUID from request list\r
136         RemoveFromRequestList(idparts[3]);\r
137 \r
138         return true;\r
139 }\r
140 \r
141 const bool IdentityIntroductionRequester::HandleMessage(FCPMessage &message)\r
142 {\r
143 \r
144         if(message["Identifier"].find("IdentityIntroductionRequester")==0)\r
145         {\r
146                 \r
147                 // ignore DataFound\r
148                 if(message.GetName()=="DataFound")\r
149                 {\r
150                         return true;\r
151                 }\r
152 \r
153                 if(message.GetName()=="AllData")\r
154                 {\r
155                         return HandleAllData(message);\r
156                 }\r
157 \r
158                 if(message.GetName()=="GetFailed")\r
159                 {\r
160                         return HandleGetFailed(message);\r
161                 }\r
162 \r
163                 if(message.GetName()=="IdentifierCollision")\r
164                 {\r
165                         // remove one of the ids from the requesting list\r
166                         std::vector<std::string> idparts;\r
167                         StringFunctions::Split(message["Identifier"],"|",idparts);\r
168                         RemoveFromRequestList(idparts[3]);\r
169                         return true;\r
170                 }\r
171 \r
172         }\r
173 \r
174         return false;\r
175 }\r
176 \r
177 void IdentityIntroductionRequester::Initialize()\r
178 {\r
179         std::string tempval="";\r
180         Option::instance()->Get("MaxIdentityIntroductionRequests",tempval);\r
181         StringFunctions::Convert(tempval,m_maxrequests);\r
182         if(m_maxrequests<1)\r
183         {\r
184                 m_maxrequests=1;\r
185                 m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"Option MaxIdentityIntroductionRequests is currently set at "+tempval+".  It must be 1 or greater.");\r
186         }\r
187         if(m_maxrequests>100)\r
188         {\r
189                 m_log->WriteLog(LogFile::LOGLEVEL_WARNING,"Option MaxIdentityIntroductionRequests is currently set at "+tempval+".  This value might be incorrectly configured.");\r
190         }\r
191         Option::instance()->Get("MessageBase",m_messagebase);\r
192         m_tempdate.SetToGMTime();\r
193 }\r
194 \r
195 void IdentityIntroductionRequester::PopulateIDList()\r
196 {\r
197         DateTime date;\r
198         int id;\r
199 \r
200         date.SetToGMTime();\r
201         date.Add(0,0,0,-1);\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>='"+date.Format("%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         DateTime now;\r
245         now.SetToGMTime();\r
246         if(m_tempdate<(now-(1.0/1440.0)))\r
247         {\r
248                 PopulateIDList();\r
249                 m_tempdate=now;\r
250         }\r
251 }\r
252 \r
253 void IdentityIntroductionRequester::RegisterWithThread(FreenetMasterThread *thread)\r
254 {\r
255         thread->RegisterFCPConnected(this);\r
256         thread->RegisterFCPMessageHandler(this);\r
257         thread->RegisterPeriodicProcessor(this);\r
258 }\r
259 \r
260 void IdentityIntroductionRequester::RemoveFromRequestList(const std::string &UUID)\r
261 {\r
262         std::vector<std::string>::iterator i=m_requesting.begin();\r
263         while(i!=m_requesting.end() && (*i)!=UUID)\r
264         {\r
265                 i++;\r
266         }\r
267         if(i!=m_requesting.end())\r
268         {\r
269                 m_requesting.erase(i);\r
270         }\r
271 }\r
272 \r
273 void IdentityIntroductionRequester::StartRequest(const std::string &UUID)\r
274 {\r
275         std::string day;\r
276         std::string solution;\r
277         std::vector<unsigned char> solutionhash;\r
278         std::string encodedhash;\r
279         FCPMessage message;\r
280         SQLite3DB::Statement st=m_db->Prepare("SELECT Day, PuzzleSolution FROM tblIntroductionPuzzleInserts WHERE FoundSolution='false' AND UUID=?;");\r
281         st.Bind(0,UUID);\r
282         st.Step();\r
283 \r
284         if(st.RowReturned())\r
285         {\r
286                 st.ResultText(0,day);\r
287                 st.ResultText(1,solution);\r
288 \r
289                 // get the hash of the solution\r
290                 solutionhash.resize(20);\r
291                 sha1((unsigned char *)solution.c_str(),solution.size(),&solutionhash[0]);\r
292                 Hex::Encode(solutionhash,encodedhash);\r
293 \r
294                 //start request for the solution\r
295                 message.SetName("ClientGet");\r
296                 message["URI"]="KSK@"+m_messagebase+"|"+day+"|"+UUID+"|"+encodedhash+".xml";\r
297                 message["Identifier"]="IdentityIntroductionRequester|"+message["URI"];\r
298                 message["ReturnType"]="direct";\r
299                 message["MaxSize"]="10000";\r
300 \r
301                 m_fcp->SendMessage(message);\r
302 \r
303                 m_requesting.push_back(UUID);\r
304 \r
305         }\r
306 \r
307 }\r
308 \r
309 void IdentityIntroductionRequester::StartRequests(const long localidentityid)\r
310 {\r
311         DateTime date;\r
312         std::string localidentityidstr;\r
313         std::string uuid;\r
314 \r
315         date.SetToGMTime();\r
316         date.Add(0,0,0,-1);\r
317         StringFunctions::Convert(localidentityid,localidentityidstr);\r
318 \r
319         // get all non-solved puzzles from yesterday and today for this identity\r
320         SQLite3DB::Statement st=m_db->Prepare("SELECT UUID FROM tblIntroductionPuzzleInserts WHERE LocalIdentityID="+localidentityidstr+" AND Day>='"+date.Format("%Y-%m-%d")+"' AND FoundSolution='false';");\r
321         st.Step();\r
322 \r
323         // start requests for all non-solved puzzles\r
324         while(st.RowReturned())\r
325         {\r
326                 uuid="";\r
327                 st.ResultText(0,uuid);\r
328                 StartRequest(uuid);\r
329                 st.Step();\r
330         }\r
331 \r
332         m_ids[localidentityid]=true;\r
333 \r
334 }\r