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