version 0.3.9
[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         if(index<50)\r
177         {\r
178                 SQLite3DB::Recordset rs2=m_db->Query("SELECT PrivateKey,PublicKey FROM tblLocalIdentity WHERE LocalIdentityID="+idstring+";");\r
179                 if(rs2.Empty()==false && rs2.GetField(0)!=NULL)\r
180                 {\r
181                         privatekey=rs2.GetField(0);\r
182                         if(rs2.GetField(1))\r
183                         {\r
184                                 publickey=rs2.GetField(1);\r
185                         }\r
186                         if(publickey.size()>=50)\r
187                         {\r
188                                 // remove - and ~\r
189                                 keypart=StringFunctions::Replace(StringFunctions::Replace(publickey.substr(4,43),"-",""),"~","");\r
190                         }\r
191                 }\r
192 \r
193                 Option::Instance()->Get("MessageBase",messagebase);\r
194 \r
195                 GenerateCaptcha(encodedpuzzle,solutionstring);\r
196 \r
197                 try\r
198                 {\r
199                         uuid=uuidgen.createRandom();\r
200                 }\r
201                 catch(...)\r
202                 {\r
203                         m_log->fatal("IntroductionPuzzleInserter::StartInsert could not create UUID");\r
204                 }\r
205 \r
206                 xml.SetType("captcha");\r
207                 std::string uuidstr=uuid.toString();\r
208                 StringFunctions::UpperCase(uuidstr,uuidstr);\r
209                 xml.SetUUID(uuidstr+"@"+keypart);\r
210                 xml.SetPuzzleData(encodedpuzzle);\r
211                 xml.SetMimeType("image/bmp");\r
212 \r
213                 xmldata=xml.GetXML();\r
214                 StringFunctions::Convert(xmldata.size(),xmldatasizestr);\r
215 \r
216                 message.SetName("ClientPut");\r
217                 message["URI"]=privatekey+messagebase+"|"+Poco::DateTimeFormatter::format(now,"%Y-%m-%d")+"|IntroductionPuzzle|"+indexstr+".xml";\r
218                 message["Identifier"]=m_fcpuniquename+"|"+idstring+"|"+indexstr+"|"+xml.GetUUID()+"|"+message["URI"];\r
219                 message["UploadFrom"]="direct";\r
220                 message["DataLength"]=xmldatasizestr;\r
221                 m_fcp->SendMessage(message);\r
222                 m_fcp->SendRaw(xmldata.c_str(),xmldata.size());\r
223 \r
224                 // insert to USK\r
225                 message.Reset();\r
226                 message.SetName("ClientPutComplexDir");\r
227                 message["URI"]="USK"+privatekey.substr(3)+messagebase+"|"+Poco::DateTimeFormatter::format(now,"%Y.%m.%d")+"|IntroductionPuzzle/0/";\r
228                 message["Identifier"]=m_fcpuniquename+"USK|"+message["URI"];\r
229                 message["DefaultName"]="IntroductionPuzzle.xml";\r
230                 message["Files.0.Name"]="IntroductionPuzzle.xml";\r
231                 message["Files.0.UplaodFrom"]="direct";\r
232                 message["Files.0.DataLength"]=xmldatasizestr;\r
233                 m_fcp->SendMessage(message);\r
234                 m_fcp->SendRaw(xmldata.c_str(),xmldata.size());\r
235 \r
236                 m_db->Execute("INSERT INTO tblIntroductionPuzzleInserts(UUID,Type,MimeType,LocalIdentityID,PuzzleData,PuzzleSolution) VALUES('"+xml.GetUUID()+"','captcha','image/bmp',"+idstring+",'"+encodedpuzzle+"','"+solutionstring+"');");\r
237 \r
238                 m_inserting.push_back(localidentityid);\r
239 \r
240                 m_log->debug("IntroductionPuzzleInserter::StartInsert started insert for id "+idstring);\r
241         }\r
242         else\r
243         {\r
244                 m_log->warning("IntroductionPuzzleInserter::StartInsert already inserted 50 puzzles for "+idstring);\r
245         }\r
246 \r
247         return true;\r
248 \r
249 }\r