version 0.1.10
[fms.git] / src / freenet / introductionpuzzleinserter.cpp
1 #include "../../include/freenet/introductionpuzzleinserter.h"\r
2 #include "../../include/freenet/introductionpuzzlexml.h"\r
3 #include "../../include/stringfunctions.h"\r
4 #include "../../include/option.h"\r
5 #include "../../include/freenet/captcha/simplecaptcha.h"\r
6 #include "../../include/uuidgenerator.h"\r
7 #include "../../include/base64.h"\r
8 \r
9 #ifdef XMEM\r
10         #include <xmem.h>\r
11 #endif\r
12 \r
13 IntroductionPuzzleInserter::IntroductionPuzzleInserter()\r
14 {\r
15         Initialize();\r
16 }\r
17 \r
18 IntroductionPuzzleInserter::IntroductionPuzzleInserter(FCPv2 *fcp):IFCPConnected(fcp)\r
19 {\r
20         Initialize();\r
21 }\r
22 \r
23 void IntroductionPuzzleInserter::CheckForNeededInsert()\r
24 {\r
25         // select all local ids that aren't single use and that aren't currently inserting a puzzle\r
26         SQLite3DB::Recordset rs=m_db->Query("SELECT LocalIdentityID FROM tblLocalIdentity WHERE SingleUse='false' AND InsertingPuzzle='false' AND PrivateKey IS NOT NULL AND PrivateKey <> '' ORDER BY LastInsertedPuzzle;");\r
27         \r
28         while(!rs.AtEnd())\r
29         {\r
30                 std::string localidentityidstr;\r
31                 DateTime now;\r
32                 now.SetToGMTime();\r
33 \r
34                 if(rs.GetField(0))\r
35                 {\r
36                         localidentityidstr=rs.GetField(0);\r
37                 }\r
38 \r
39                 // if this identity has any non-solved puzzles for today, we don't need to insert a new puzzle\r
40                 SQLite3DB::Recordset rs2=m_db->Query("SELECT UUID FROM tblIntroductionPuzzleInserts WHERE Day='"+now.Format("%Y-%m-%d")+"' AND FoundSolution='false' AND LocalIdentityID="+localidentityidstr+";");\r
41 \r
42                 // identity doesn't have any non-solved puzzles for today - start a new insert\r
43                 if(rs2.Empty()==true)\r
44                 {\r
45                         StartInsert(rs.GetInt(0));\r
46                 }\r
47 \r
48                 rs.Next();\r
49         }\r
50 }\r
51 \r
52 void IntroductionPuzzleInserter::FCPConnected()\r
53 {\r
54         m_db->Execute("UPDATE tblLocalIdentity SET InsertingPuzzle='false';");\r
55 }\r
56 \r
57 void IntroductionPuzzleInserter::FCPDisconnected()\r
58 {\r
59         \r
60 }\r
61 \r
62 void IntroductionPuzzleInserter::GenerateCaptcha(std::string &encodeddata, std::string &solution)\r
63 {\r
64         SimpleCaptcha captcha;\r
65         std::vector<unsigned char> puzzle;\r
66         std::vector<unsigned char> puzzlesolution;\r
67 \r
68         captcha.Generate();\r
69         captcha.GetPuzzle(puzzle);\r
70         captcha.GetSolution(puzzlesolution);\r
71 \r
72         encodeddata.clear();\r
73         solution.clear();\r
74 \r
75         Base64::Encode(puzzle,encodeddata);\r
76         solution.insert(solution.begin(),puzzlesolution.begin(),puzzlesolution.end());\r
77 \r
78 }\r
79 \r
80 const bool IntroductionPuzzleInserter::HandleMessage(FCPMessage &message)\r
81 {\r
82 \r
83         if(message["Identifier"].find("IntroductionPuzzleInserter")==0)\r
84         {\r
85 \r
86                 // ignore URIGenerated message\r
87                 if(message.GetName()=="URIGenerated")\r
88                 {\r
89                         return true;\r
90                 }\r
91 \r
92                 if(message.GetName()=="PutSuccessful")\r
93                 {\r
94                         return HandlePutSuccessful(message);\r
95                 }\r
96 \r
97                 if(message.GetName()=="PutFailed")\r
98                 {\r
99                         return HandlePutFailed(message);\r
100                 }\r
101 \r
102         }\r
103 \r
104         return false;\r
105 }\r
106 \r
107 const bool IntroductionPuzzleInserter::HandlePutFailed(FCPMessage &message)\r
108 {\r
109         SQLite3DB::Statement st;\r
110         std::vector<std::string> idparts;\r
111         long localidentityid;\r
112 \r
113         StringFunctions::Split(message["Identifier"],"|",idparts);\r
114         StringFunctions::Convert(idparts[1],localidentityid);\r
115 \r
116         st=m_db->Prepare("UPDATE tblLocalIdentity SET InsertingPuzzle='false' WHERE LocalIdentityID=?;");\r
117         st.Bind(0,localidentityid);\r
118         st.Step();\r
119         st.Finalize();\r
120 \r
121         // if fatal error or collision - mark index\r
122         if(message["Fatal"]=="true" || message["Code"]=="9")\r
123         {\r
124                 m_db->Execute("UPDATE tblIntroductionPuzzleInserts SET Day='"+idparts[5]+"', InsertIndex="+idparts[2]+", FoundSolution='true' WHERE UUID='"+idparts[3]+"';");\r
125         }\r
126 \r
127         m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"IntroductionPuzzleInserter::HandlePutFailed failed to insert puzzle "+idparts[3]);\r
128 \r
129         return true;\r
130 }\r
131 \r
132 const bool IntroductionPuzzleInserter::HandlePutSuccessful(FCPMessage &message)\r
133 {\r
134         DateTime now;\r
135         SQLite3DB::Statement st;\r
136         std::vector<std::string> idparts;\r
137         long localidentityid;\r
138         long insertindex;\r
139 \r
140         now.SetToGMTime();\r
141         StringFunctions::Split(message["Identifier"],"|",idparts);\r
142         StringFunctions::Convert(idparts[1],localidentityid);\r
143         StringFunctions::Convert(idparts[2],insertindex);\r
144 \r
145         st=m_db->Prepare("UPDATE tblIntroductionPuzzleInserts SET Day=?, InsertIndex=? WHERE UUID=?;");\r
146         st.Bind(0,idparts[5]);\r
147         st.Bind(1,insertindex);\r
148         st.Bind(2,idparts[3]);\r
149         st.Step();\r
150         st.Finalize();\r
151 \r
152         st=m_db->Prepare("UPDATE tblLocalIdentity SET InsertingPuzzle='false', LastInsertedPuzzle=? WHERE LocalIdentityID=?;");\r
153         st.Bind(0,now.Format("%Y-%m-%d %H:%M:%S"));\r
154         st.Bind(1,localidentityid);\r
155         st.Step();\r
156         st.Finalize();\r
157 \r
158         m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"IntroductionPuzzleInserter::HandlePutSuccessful inserted puzzle "+idparts[3]);\r
159 \r
160         return true;\r
161 }\r
162 \r
163 void IntroductionPuzzleInserter::Initialize()\r
164 {\r
165         m_lastchecked.SetToGMTime();\r
166 }\r
167 \r
168 void IntroductionPuzzleInserter::Process()\r
169 {\r
170 \r
171         DateTime now;\r
172 \r
173         now.SetToGMTime();\r
174 \r
175         if(m_lastchecked<(now-(1.0/1440.0)))\r
176         {\r
177                 CheckForNeededInsert();\r
178                 m_lastchecked=now;\r
179         }\r
180 \r
181 }\r
182 \r
183 void IntroductionPuzzleInserter::RegisterWithThread(FreenetMasterThread *thread)\r
184 {\r
185         thread->RegisterFCPConnected(this);\r
186         thread->RegisterFCPMessageHandler(this);\r
187         thread->RegisterPeriodicProcessor(this);\r
188 }\r
189 \r
190 void IntroductionPuzzleInserter::StartInsert(const long localidentityid)\r
191 {\r
192         DateTime now;\r
193         std::string idstring;\r
194         long index=0;\r
195         std::string indexstr;\r
196         UUIDGenerator uuid;\r
197         std::string messagebase;\r
198         IntroductionPuzzleXML xml;\r
199         std::string encodedpuzzle;\r
200         std::string solutionstring;\r
201         FCPMessage message;\r
202         std::string xmldata;\r
203         std::string xmldatasizestr;\r
204         std::string privatekey;\r
205 \r
206         StringFunctions::Convert(localidentityid,idstring);\r
207         now.SetToGMTime();\r
208         SQLite3DB::Recordset rs=m_db->Query("SELECT MAX(InsertIndex) FROM tblIntroductionPuzzleInserts WHERE Day='"+now.Format("%Y-%m-%d")+"' AND LocalIdentityID="+idstring+";");\r
209 \r
210         if(rs.Empty() || rs.GetField(0)==NULL)\r
211         {\r
212                 index=0;\r
213         }\r
214         else\r
215         {\r
216                 index=rs.GetInt(0)+1;\r
217         }\r
218         StringFunctions::Convert(index,indexstr);\r
219 \r
220         SQLite3DB::Recordset rs2=m_db->Query("SELECT PrivateKey FROM tblLocalIdentity WHERE LocalIdentityID="+idstring+";");\r
221         if(rs2.Empty()==false && rs2.GetField(0)!=NULL)\r
222         {\r
223                 privatekey=rs2.GetField(0);\r
224         }\r
225 \r
226         Option::Instance()->Get("MessageBase",messagebase);\r
227 \r
228         GenerateCaptcha(encodedpuzzle,solutionstring);\r
229 \r
230         xml.SetType("captcha");\r
231         xml.SetUUID(uuid.Generate());\r
232         xml.SetPuzzleData(encodedpuzzle);\r
233         xml.SetMimeType("image/bmp");\r
234 \r
235         xmldata=xml.GetXML();\r
236         StringFunctions::Convert(xmldata.size(),xmldatasizestr);\r
237 \r
238         message.SetName("ClientPut");\r
239         message["URI"]=privatekey+messagebase+"|"+now.Format("%Y-%m-%d")+"|IntroductionPuzzle|"+indexstr+".xml";\r
240         message["Identifier"]="IntroductionPuzzleInserter|"+idstring+"|"+indexstr+"|"+xml.GetUUID()+"|"+message["URI"];\r
241         message["UploadFrom"]="direct";\r
242         message["DataLength"]=xmldatasizestr;\r
243         m_fcp->SendMessage(message);\r
244         m_fcp->SendRaw(xmldata.c_str(),xmldata.size());\r
245 \r
246         m_db->Execute("UPDATE tblLocalIdentity SET InsertingPuzzle='true' WHERE LocalIdentityID="+idstring+";");\r
247         m_db->Execute("INSERT INTO tblIntroductionPuzzleInserts(UUID,Type,MimeType,LocalIdentityID,PuzzleData,PuzzleSolution) VALUES('"+xml.GetUUID()+"','captcha','image/bmp',"+idstring+",'"+encodedpuzzle+"','"+solutionstring+"');");\r
248 \r
249         m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"IntroductionPuzzleInserter::StartInsert started insert for id "+idstring);\r
250 \r
251 }\r