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