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"
11 #include <boost/iostreams/categories.hpp>
12 #include <boost/iostreams/copy.hpp>
13 #include <boost/iostreams/stream.hpp>
14 #include <boost/iostreams/stream_buffer.hpp>
15 #include <boost/iostreams/filter/symmetric.hpp>
16 #include <boost/iostreams/filter/zlib.hpp>
17 #include <boost/iostreams/filtering_stream.hpp>
18 #include <boost/iostreams/device/back_inserter.hpp>
22 void file_upload_trampoline(file_upload
* x
)
27 struct upload_output_handler
: http_request::output_handler
29 upload_output_handler(std::function
<void(std::string
&)> _output_cb
)
31 output_cb
= _output_cb
;
33 ~upload_output_handler() {}
34 void header(const std::string
& name
, const std::string
& content
)
36 if(http_strlower(name
) == "location") location
= content
;
38 void write(const char* source
, size_t srcsize
)
40 std::string
x(source
, srcsize
);
41 while(x
.find_first_of("\n") < x
.length()) {
42 size_t split
= x
.find_first_of("\n");
43 std::string line
= x
.substr(0, split
);
44 x
= x
.substr(split
+ 1);
45 incomplete_line
+= line
;
46 while(incomplete_line
.length() > 0 &&
47 incomplete_line
[incomplete_line
.length() - 1] == '\r')
48 incomplete_line
= incomplete_line
.substr(0, incomplete_line
.length() - 1);
49 output_cb(incomplete_line
);
52 if(x
!= "") incomplete_line
+= x
;
56 if(incomplete_line
!= "") output_cb(incomplete_line
);
58 std::string
get_location()
63 std::function
<void(std::string
&)> output_cb
;
65 std::string incomplete_line
;
68 void compress(std::vector
<char>& buf
, std::string
& output
, std::string
& compression
)
70 streamcompress::base
* X
= NULL
;
73 X
= streamcompress::base::create_compressor("xz", "level=7,extreme=true");
80 X
= streamcompress::base::create_compressor("gzip", "level=7");
86 std::vector
<char> out
;
87 boost::iostreams::filtering_istream
* s
= new boost::iostreams::filtering_istream();
88 if(X
) s
->push(streamcompress::iostream(X
));
89 s
->push(boost::iostreams::array_source(&buf
[0], buf
.size()));
90 boost::iostreams::back_insert_device
<std::vector
<char>> rd(out
);
91 boost::iostreams::copy(*s
, rd
);
94 output
= std::string(&out
[0], out
.size());
97 void load_dh25519_key(uint8_t* out
)
99 std::string path
= get_config_path() + "/dh25519.key";
100 std::ifstream
fp(path
, std::ios::binary
);
102 throw std::runtime_error("Can't open dh25519 keyfile");
103 skein::hash
h(skein::hash::PIPE_512
, 256);
106 fp
.read(buf
, sizeof(buf
));
107 if(fp
.gcount() == 0) break;
108 h
.write((const uint8_t*)buf
, fp
.gcount());
109 skein::zeroize(buf
, fp
.gcount());
112 curve25519_clamp(out
);
116 file_upload::file_upload()
124 file_upload::~file_upload()
126 if(dh25519
) delete dh25519
;
130 void file_upload::do_async()
132 (new threads::thread(file_upload_trampoline
, this))->detach();
135 void file_upload::_do_async()
138 load_dh25519_key(key
);
139 dh25519
= new dh25519_http_auth(key
);
140 skein::zeroize(key
, sizeof(key
));
142 http_async_request obtainkey
;
147 http_request::null_input_handler nullinput
;
149 http_request::www_authenticate_extractor
extractor([auth
](const std::string
& content
) {
150 auth
->parse_auth_response(content
);
152 obtainkey
.ihandler
= &nullinput
;
153 obtainkey
.ohandler
= &extractor
;
154 obtainkey
.verb
= "PUT";
155 obtainkey
.url
= base_url
;
156 obtainkey
.authorization
= dh25519
->format_get_session_request();
157 add_msg("Obtaining short-term credentials...");
158 obtainkey
.lauch_async();
159 while(!obtainkey
.finished
) {
160 threads::alock
hx(obtainkey
.m
);
161 obtainkey
.finished_cond
.wait(hx
);
163 if(obtainkey
.errormsg
!= "") {
164 add_msg((stringfmt() << "Failed: " << obtainkey
.errormsg
).str());
165 { threads::alock
h(m
); req
= NULL
; }
169 if(obtainkey
.http_code
!= 401) {
170 add_msg((stringfmt() << "Failed: Expected 401, got " << obtainkey
.http_code
).str());
171 { threads::alock
h(m
); req
= NULL
; }
175 if(!dh25519
->is_ready()) {
176 add_msg((stringfmt() << "Failed: Authenticator is not ready!").str());
177 { threads::alock
h(m
); req
= NULL
; }
181 add_msg("Got short-term credentials.");
182 { threads::alock
h(m
); req
= NULL
; }
185 http_async_request upload
;
190 property_upload_request input
;
191 upload_output_handler
output([this](const std::string
& msg
) { add_msg(msg
); });
193 input
.data
["filename"] = filename
;
194 if(title
!= "") input
.data
["title"] = title
;
195 if(description
!= "") input
.data
["description"] = description
;
196 if(gamename
!= "") input
.data
["game"] = gamename
;
197 input
.data
["hidden"] = hidden
? "1" : "0";
198 compress(content
, input
.data
["content"], input
.data
["compression"]);
200 upload
.ihandler
= &input
;
201 upload
.ohandler
= &output
;
203 upload
.url
= base_url
;
204 add_msg("Hashing file...");
205 auto authobj
= dh25519
->start_request(upload
.url
, upload
.verb
);
208 size_t r
= input
.read(buf
, sizeof(buf
));
210 authobj
.hash((const uint8_t*)buf
, r
);
212 upload
.authorization
= authobj
.get_authorization();
214 add_msg("Uploading file...");
215 upload
.lauch_async();
216 while(!upload
.finished
) {
217 threads::alock
hx(upload
.m
);
218 upload
.finished_cond
.wait(hx
);
221 if(upload
.errormsg
!= "") {
222 add_msg((stringfmt() << "Failed: " << upload
.errormsg
).str());
224 { threads::alock
h(m
); req
= NULL
; }
227 if(upload
.http_code
!= 201) {
228 add_msg((stringfmt() << "Failed: Expected 201, got " << upload
.http_code
).str());
230 { threads::alock
h(m
); req
= NULL
; }
233 add_msg((stringfmt() << "Sucessful! URL: " << output
.get_location()).str());
234 final_url
= output
.get_location();
237 { threads::alock
h(m
); req
= NULL
; }
241 void file_upload::cancel()
244 if(req
) req
->cancel();
247 std::list
<std::string
> file_upload::get_messages()
250 std::list
<std::string
> x
= msgs
;
255 int file_upload::get_progress_ppm()
260 int64_t dnow
, dtotal
, unow
, utotal
;
261 req
->get_xfer_status(dnow
, dtotal
, unow
, utotal
);
263 return 1000000 * unow
/ utotal
;
268 void file_upload::add_msg(const std::string
& msg
)
274 void get_dh25519_pubkey(uint8_t* out
)
277 load_dh25519_key(privkey
);
278 curve25519_clamp(privkey
);
279 curve25519(out
, privkey
, curve25519_base
);
280 skein::zeroize(privkey
, sizeof(privkey
));