1 #include "core/fileupload.hpp"
2 #include "core/misc.hpp"
3 #include "library/curve25519.hpp"
4 #include "library/httpauth.hpp"
5 #include "library/httpreq.hpp"
6 #include "library/skein.hpp"
7 #include "library/streamcompress.hpp"
8 #include "library/string.hpp"
12 #include <boost/iostreams/categories.hpp>
13 #include <boost/iostreams/copy.hpp>
14 #include <boost/iostreams/stream.hpp>
15 #include <boost/iostreams/stream_buffer.hpp>
16 #include <boost/iostreams/filter/symmetric.hpp>
17 #include <boost/iostreams/filter/zlib.hpp>
18 #include <boost/iostreams/filtering_stream.hpp>
19 #include <boost/iostreams/device/back_inserter.hpp>
23 void file_upload_trampoline(file_upload
* x
)
28 struct upload_output_handler
: http_request::output_handler
30 upload_output_handler(std::function
<void(std::string
&)> _output_cb
)
32 output_cb
= _output_cb
;
34 ~upload_output_handler() {}
35 void header(const std::string
& name
, const std::string
& content
)
37 if(http_strlower(name
) == "location") location
= content
;
39 void write(const char* source
, size_t srcsize
)
41 std::string
x(source
, srcsize
);
42 while(x
.find_first_of("\n") < x
.length()) {
43 size_t split
= x
.find_first_of("\n");
44 std::string line
= x
.substr(0, split
);
45 x
= x
.substr(split
+ 1);
46 incomplete_line
+= line
;
47 while(incomplete_line
.length() > 0 &&
48 incomplete_line
[incomplete_line
.length() - 1] == '\r')
49 incomplete_line
= incomplete_line
.substr(0, incomplete_line
.length() - 1);
50 output_cb(incomplete_line
);
53 if(x
!= "") incomplete_line
+= x
;
57 if(incomplete_line
!= "") output_cb(incomplete_line
);
59 std::string
get_location()
64 std::function
<void(std::string
&)> output_cb
;
66 std::string incomplete_line
;
69 void compress(std::vector
<char>& buf
, std::string
& output
, std::string
& compression
)
71 streamcompress::base
* X
= NULL
;
74 X
= streamcompress::base::create_compressor("xz", "level=7,extreme=true");
81 X
= streamcompress::base::create_compressor("gzip", "level=7");
87 std::vector
<char> out
;
88 boost::iostreams::filtering_istream
* s
= new boost::iostreams::filtering_istream();
89 if(X
) s
->push(streamcompress::iostream(X
));
90 s
->push(boost::iostreams::array_source(&buf
[0], buf
.size()));
91 boost::iostreams::back_insert_device
<std::vector
<char>> rd(out
);
92 boost::iostreams::copy(*s
, rd
);
95 output
= std::string(&out
[0], out
.size());
98 void load_dh25519_key(uint8_t* out
)
100 std::string path
= get_config_path() + "/dh25519.key";
101 std::ifstream
fp(path
, std::ios::binary
);
103 throw std::runtime_error("Can't open dh25519 keyfile");
104 skein::hash
h(skein::hash::PIPE_512
, 256);
107 fp
.read(buf
, sizeof(buf
));
108 if(fp
.gcount() == 0) break;
109 h
.write((const uint8_t*)buf
, fp
.gcount());
110 skein::zeroize(buf
, fp
.gcount());
113 curve25519_clamp(out
);
117 file_upload::file_upload()
125 file_upload::~file_upload()
127 if(dh25519
) delete dh25519
;
131 void file_upload::do_async()
133 (new threads::thread(file_upload_trampoline
, this))->detach();
136 void file_upload::_do_async()
139 load_dh25519_key(key
);
140 dh25519
= new dh25519_http_auth(key
);
141 skein::zeroize(key
, sizeof(key
));
143 http_async_request obtainkey
;
148 http_request::null_input_handler nullinput
;
150 http_request::www_authenticate_extractor
extractor([auth
](const std::string
& content
) {
151 auth
->parse_auth_response(content
);
153 obtainkey
.ihandler
= &nullinput
;
154 obtainkey
.ohandler
= &extractor
;
155 obtainkey
.verb
= "PUT";
156 obtainkey
.url
= base_url
;
157 obtainkey
.authorization
= dh25519
->format_get_session_request();
158 add_msg("Obtaining short-term credentials...");
159 obtainkey
.lauch_async();
160 while(!obtainkey
.finished
) {
161 threads::alock
hx(obtainkey
.m
);
162 obtainkey
.finished_cond
.wait(hx
);
164 if(obtainkey
.errormsg
!= "") {
165 add_msg((stringfmt() << "Failed: " << obtainkey
.errormsg
).str());
166 { threads::alock
h(m
); req
= NULL
; }
170 if(obtainkey
.http_code
!= 401) {
171 add_msg((stringfmt() << "Failed: Expected 401, got " << obtainkey
.http_code
).str());
172 { threads::alock
h(m
); req
= NULL
; }
176 if(!dh25519
->is_ready()) {
177 add_msg((stringfmt() << "Failed: Authenticator is not ready!").str());
178 { threads::alock
h(m
); req
= NULL
; }
182 add_msg("Got short-term credentials.");
183 { threads::alock
h(m
); req
= NULL
; }
186 http_async_request upload
;
191 property_upload_request input
;
192 upload_output_handler
output([this](const std::string
& msg
) { add_msg(msg
); });
194 input
.data
["filename"] = filename
;
195 if(title
!= "") input
.data
["title"] = title
;
196 if(description
!= "") input
.data
["description"] = description
;
197 if(gamename
!= "") input
.data
["game"] = gamename
;
198 input
.data
["hidden"] = hidden
? "1" : "0";
199 compress(content
, input
.data
["content"], input
.data
["compression"]);
201 upload
.ihandler
= &input
;
202 upload
.ohandler
= &output
;
204 upload
.url
= base_url
;
205 add_msg("Hashing file...");
206 auto authobj
= dh25519
->start_request(upload
.url
, upload
.verb
);
209 size_t r
= input
.read(buf
, sizeof(buf
));
211 authobj
.hash((const uint8_t*)buf
, r
);
213 upload
.authorization
= authobj
.get_authorization();
215 add_msg("Uploading file...");
216 upload
.lauch_async();
217 while(!upload
.finished
) {
218 threads::alock
hx(upload
.m
);
219 upload
.finished_cond
.wait(hx
);
222 if(upload
.errormsg
!= "") {
223 add_msg((stringfmt() << "Failed: " << upload
.errormsg
).str());
225 { threads::alock
h(m
); req
= NULL
; }
228 if(upload
.http_code
!= 201) {
229 add_msg((stringfmt() << "Failed: Expected 201, got " << upload
.http_code
).str());
231 { threads::alock
h(m
); req
= NULL
; }
234 add_msg((stringfmt() << "Sucessful! URL: " << output
.get_location()).str());
235 final_url
= output
.get_location();
238 { threads::alock
h(m
); req
= NULL
; }
242 void file_upload::cancel()
245 if(req
) req
->cancel();
248 std::list
<std::string
> file_upload::get_messages()
251 std::list
<std::string
> x
= msgs
;
256 int file_upload::get_progress_ppm()
261 int64_t dnow
, dtotal
, unow
, utotal
;
262 req
->get_xfer_status(dnow
, dtotal
, unow
, utotal
);
264 return 1000000 * unow
/ utotal
;
269 void file_upload::add_msg(const std::string
& msg
)
275 void get_dh25519_pubkey(uint8_t* out
)
278 load_dh25519_key(privkey
);
279 curve25519_clamp(privkey
);
280 curve25519(out
, privkey
, curve25519_base
);
281 skein::zeroize(privkey
, sizeof(privkey
));