1 |
// Gnomoradio - rainbow/hub-client.cc |
2 |
// Copyright (C) 2003 Jim Garrison |
3 |
// |
4 |
// This program is free software; you can redistribute it and/or modify |
5 |
// it under the terms of the GNU General Public License as published by |
6 |
// the Free Software Foundation; either version 2 of the License, or |
7 |
// (at your option) any later version. |
8 |
// |
9 |
// This program is distributed in the hope that it will be useful, |
10 |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 |
// GNU General Public License for more details. |
13 |
// |
14 |
// You should have received a copy of the GNU General Public License |
15 |
// along with this program; if not, write to the Free Software |
16 |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
17 |
|
18 |
#include "hub-client.h" |
19 |
#include "rainbow/util.h" |
20 |
|
21 |
extern "C" { |
22 |
#include "rainbow/sha1.h" |
23 |
} |
24 |
|
25 |
#include <sys/stat.h> |
26 |
|
27 |
#include <unistd.h> |
28 |
#include <cstdio> |
29 |
#include <string> |
30 |
|
31 |
#include <cstdlib> |
32 |
#include <sstream> |
33 |
|
34 |
#define CRLF "\r\n" |
35 |
|
36 |
namespace Rainbow |
37 |
{ |
38 |
class Checksum : public sigc::trackable |
39 |
{ |
40 |
public: |
41 |
static void verify (const sigc::slot<void,bool,Resource*> &mem_fun, |
42 |
Resource *resource); |
43 |
|
44 |
private: |
45 |
Checksum (const sigc::slot<void,bool,Resource*> &mem_fun, |
46 |
Resource *r); |
47 |
void verify_thread (); |
48 |
void verify_do (); |
49 |
void dispatch (); |
50 |
|
51 |
sigc::signal<void,bool,Resource*> signal; |
52 |
Glib::Dispatcher dispatcher; |
53 |
bool success; |
54 |
Resource *resource; |
55 |
Glib::ustring filename; |
56 |
std::vector<guint8> checksum; |
57 |
}; |
58 |
|
59 |
class HubConnector : public sigc::trackable |
60 |
{ |
61 |
public: |
62 |
static void connect (const sigc::slot<void,int> &mem_fun, |
63 |
const Glib::ustring &hub_server); |
64 |
|
65 |
private: |
66 |
HubConnector (const sigc::slot<void,int> &mem_fun, |
67 |
const Glib::ustring &hub_server); |
68 |
void thread (); |
69 |
void dispatch (); |
70 |
|
71 |
sigc::signal<void,int> signal; |
72 |
Glib::Dispatcher dispatcher; |
73 |
int sock; |
74 |
sockaddr_in hub; |
75 |
Glib::ustring server; |
76 |
}; |
77 |
} |
78 |
|
79 |
using namespace std; |
80 |
using namespace sigc; |
81 |
using namespace Glib; |
82 |
using namespace Rainbow; |
83 |
|
84 |
Rainbow::Checksum::Checksum (const sigc::slot<void,bool,Resource*> &mem_fun, |
85 |
Resource *r) |
86 |
: resource(r), |
87 |
filename(r->get_filename()), |
88 |
checksum(r->checksum), |
89 |
success(false) |
90 |
{ |
91 |
signal.connect(mem_fun); |
92 |
dispatcher.connect(sigc::mem_fun(*this, &Checksum::dispatch)); |
93 |
|
94 |
if (!Glib::thread_supported()) |
95 |
Glib::thread_init(); |
96 |
Glib::Thread::create(sigc::mem_fun(*this, &Checksum::verify_thread), false); |
97 |
} |
98 |
|
99 |
void Rainbow::Checksum::verify (const sigc::slot<void,bool,Resource*> &mem_fun, |
100 |
Resource *resource) |
101 |
{ |
102 |
new Checksum(mem_fun, resource); |
103 |
} |
104 |
|
105 |
void Rainbow::Checksum::verify_thread () |
106 |
{ |
107 |
verify_do(); |
108 |
dispatcher(); |
109 |
} |
110 |
|
111 |
void Rainbow::Checksum::verify_do () |
112 |
{ |
113 |
std::string fn; |
114 |
try { |
115 |
fn = filename_from_utf8(filename); |
116 |
} catch (...) { |
117 |
return; |
118 |
} |
119 |
|
120 |
FILE *file = fopen(fn.c_str(), "rb"); |
121 |
if (file) { |
122 |
const int buf_size = 4096; |
123 |
unsigned char buf[buf_size]; |
124 |
|
125 |
SHA1Context cxt; |
126 |
if (SHA1Reset(&cxt)) |
127 |
return; |
128 |
|
129 |
size_t r; |
130 |
for (;;) { |
131 |
r = fread(buf, 1, buf_size, file); |
132 |
if (r == 0) |
133 |
break; |
134 |
if (SHA1Input(&cxt, buf, r)) |
135 |
return; |
136 |
} |
137 |
|
138 |
fclose(file); |
139 |
|
140 |
guint8 sum[SHA1HashSize]; |
141 |
if (SHA1Result(&cxt, sum)) |
142 |
return; |
143 |
|
144 |
for (int i = 0; i < SHA1HashSize; ++i) |
145 |
if (sum[i] != checksum[i]) |
146 |
return; |
147 |
success = true; |
148 |
} |
149 |
} |
150 |
|
151 |
void Rainbow::Checksum::dispatch () |
152 |
{ |
153 |
signal(success, resource); |
154 |
delete this; |
155 |
} |
156 |
|
157 |
void Rainbow::HubConnector::connect (const sigc::slot<void,int> &mem_fun, |
158 |
const Glib::ustring &hub_server) |
159 |
{ |
160 |
new HubConnector(mem_fun, hub_server); |
161 |
} |
162 |
|
163 |
Rainbow::HubConnector::HubConnector (const sigc::slot<void,int> &mem_fun, |
164 |
const Glib::ustring &hub_server) |
165 |
: server(hub_server), |
166 |
sock(0) |
167 |
{ |
168 |
signal.connect(mem_fun); |
169 |
dispatcher.connect(sigc::mem_fun(*this, &HubConnector::dispatch)); |
170 |
|
171 |
struct hostent *host = gethostbyname(hub_server.c_str()); |
172 |
if (!host) { |
173 |
logmsg << "HubClient: Could not create host entry for " << hub_server << endl; |
174 |
} else { |
175 |
sock = socket(PF_INET, SOCK_STREAM, 0); |
176 |
if (sock < 1) |
177 |
logmsg << "HubClient: Could not create socket" << endl; |
178 |
} |
179 |
|
180 |
if (sock > 0) { |
181 |
hub.sin_addr = *(struct in_addr*) host->h_addr; |
182 |
hub.sin_family = AF_INET; |
183 |
hub.sin_port = htons(18373); |
184 |
|
185 |
if (!Glib::thread_supported()) |
186 |
Glib::thread_init(); |
187 |
Glib::Thread::create(sigc::mem_fun(*this, &HubConnector::thread), false); |
188 |
} else { |
189 |
signal(sock); |
190 |
delete this; |
191 |
} |
192 |
} |
193 |
|
194 |
void Rainbow::HubConnector::thread () |
195 |
{ |
196 |
if (::connect(sock, (struct sockaddr*) &hub, sizeof(hub))) { |
197 |
close(sock); |
198 |
sock = 0; |
199 |
logmsg << "HubClient: Could not connect to host" << endl; |
200 |
} else { |
201 |
// connected, now send handshake line |
202 |
string send = "RAINBOW/1.0 " "4617" CRLF; // FIXME |
203 |
if (!send_data_on_socket(sock, send)) { |
204 |
close(sock); |
205 |
sock = 0; |
206 |
logmsg << "HubClient: Could not initiate handshake (broken socket)" << endl; |
207 |
} |
208 |
} |
209 |
|
210 |
dispatcher(); |
211 |
} |
212 |
|
213 |
void Rainbow::HubConnector::dispatch () |
214 |
{ |
215 |
signal(sock); |
216 |
delete this; |
217 |
} |
218 |
|
219 |
static inline guint8 hex_to_int (char c) |
220 |
{ |
221 |
if (c >= '0' && c <= '9') |
222 |
return guint8(c - '0'); |
223 |
if (c >= 'A' && c <= 'F') |
224 |
return guint8(c - 'A') + 10; |
225 |
if (c >= 'a' && c <= 'f') |
226 |
return guint8(c - 'a') + 10; |
227 |
return 0; |
228 |
} |
229 |
|
230 |
void Rainbow::HubClient::parse_resource (xmlpp::Element *xml, ref_ptr<RdfResource> rdf, Resource *r) |
231 |
{ |
232 |
r->obtained_info = true; |
233 |
|
234 |
if (xml) { |
235 |
xmlpp::Node::NodeList children = xml->get_children(); |
236 |
for (xmlpp::Node::NodeList::iterator i = children.begin(); i != children.end(); ++i) { |
237 |
xmlpp::Element *el = dynamic_cast<xmlpp::Element*>(*i); |
238 |
if (!el) |
239 |
continue; |
240 |
xmlpp::TextNode *content = el->get_child_text(); |
241 |
xmlpp::Attribute *resource_link = el->get_attribute("resource"); |
242 |
|
243 |
if (el->get_name() == "available") { |
244 |
if (resource_link) |
245 |
r->available_locations.push_back(resource_link->get_value()); |
246 |
} else if (el->get_name() == "license") { |
247 |
if (resource_link) { |
248 |
Glib::ustring absolute = RdfResource::absolute_uri(resource_link->get_value(), |
249 |
rdf->get_base_uri()); |
250 |
License::get_and_do(absolute, |
251 |
bind(mem_fun(*this, &HubClient::license_obtained_callback), r), |
252 |
rdf->get_secondary_info(absolute)); |
253 |
} |
254 |
} else if (el->get_name() == "checksum") { |
255 |
if (content && content->get_content().length()) { |
256 |
r->checksum.resize(SHA1HashSize); |
257 |
try { |
258 |
string chk = content->get_content(); |
259 |
int n = chk.length() / 2; |
260 |
if (n > SHA1HashSize) // we only have 20 bytes to work with |
261 |
n = SHA1HashSize; |
262 |
for (int i = 0; i < n; ++i) { |
263 |
r->checksum[i] = hex_to_int(chk[i*2]) * 16; |
264 |
r->checksum[i] += hex_to_int(chk[i*2+1]); |
265 |
} |
266 |
} catch (...) { |
267 |
} |
268 |
} |
269 |
} else if (el->get_name() == "representationOf") { |
270 |
|
271 |
} |
272 |
} |
273 |
} |
274 |
|
275 |
if (r->obtained_info) { |
276 |
if (r->prepare_once_info_is_obtained) |
277 |
prepare_resource(r); |
278 |
r->signal_found_info(bool(xml)); |
279 |
} |
280 |
} |
281 |
|
282 |
void Rainbow::HubClient::license_obtained_callback (Rainbow::ref_ptr<Rainbow::License> license, Resource *r) |
283 |
{ |
284 |
r->sharable = license->sharable(); |
285 |
} |
286 |
|
287 |
Rainbow::HubClient::HubClient (const Glib::ustring &hub) |
288 |
: server(this), |
289 |
hub_host(hub), |
290 |
hubsock(0), |
291 |
cache_size(50), |
292 |
total_allocated_size(0) |
293 |
{ |
294 |
alarm.signal_alarm().connect(mem_fun(*this, &HubClient::connect)); |
295 |
|
296 |
load_database(); |
297 |
save_alarm.signal_alarm().connect(mem_fun(*this, &HubClient::on_save_alarm)); |
298 |
save_alarm.set_relative(save_alarm_interval); |
299 |
|
300 |
// FIXME: we could wait until connected to the hub to start http services |
301 |
if (!server.start(http_server_port)) { |
302 |
logmsg << "HubClient: Could not start http server" << endl; |
303 |
} |
304 |
connect(); |
305 |
} |
306 |
|
307 |
Rainbow::HubClient::~HubClient () |
308 |
{ |
309 |
disconnect(); |
310 |
save_database(); |
311 |
} |
312 |
|
313 |
void Rainbow::HubClient::on_save_alarm () |
314 |
{ |
315 |
save_database(); |
316 |
save_alarm.set_relative(save_alarm_interval); |
317 |
} |
318 |
|
319 |
void Rainbow::HubClient::load_database () |
320 |
{ |
321 |
Mutex::Lock lock(db_mutex); |
322 |
xmlpp::DomParser tree; |
323 |
try { |
324 |
tree.parse_file(string(getenv("HOME")) + "/.rainbow-db.xml"); |
325 |
} catch (...) { |
326 |
return; |
327 |
} |
328 |
|
329 |
xmlpp::Node *root = tree.get_document()->get_root_node(); |
330 |
xmlpp::Node::NodeList headings = root->get_children(); |
331 |
|
332 |
for (xmlpp::Node::NodeList::iterator heading = headings.begin(); |
333 |
heading != headings.end(); ++heading) { |
334 |
if ((*heading)->get_name() == "resources") { |
335 |
xmlpp::Node::NodeList resources = (*heading)->get_children(); |
336 |
for (xmlpp::Node::NodeList::iterator i = resources.begin(); |
337 |
i != resources.end(); ++i) { |
338 |
xmlpp::Element *resource = dynamic_cast<xmlpp::Element*>(*i); |
339 |
if (!resource) |
340 |
continue; |
341 |
|
342 |
xmlpp::Attribute *url = resource->get_attribute("url"); |
343 |
xmlpp::Attribute *filename = resource->get_attribute("filename"); |
344 |
if (!url || !filename) |
345 |
continue; |
346 |
|
347 |
Resource *r = new Resource(url->get_value(), filename->get_value()); |
348 |
|
349 |
xmlpp::Attribute *sz = resource->get_attribute("size"); |
350 |
if (sz) |
351 |
set_allocated_size(r, atoi(sz->get_value().c_str())); |
352 |
else |
353 |
check_allocated_size(r); |
354 |
|
355 |
xmlpp::Attribute *sharable = resource->get_attribute("sharable"); |
356 |
if (sharable && sharable->get_value() == "true") |
357 |
r->sharable = true; |
358 |
|
359 |
db.insert(make_pair(url->get_value(), |
360 |
ref_ptr<Resource>(r))); |
361 |
} |
362 |
} |
363 |
} |
364 |
} |
365 |
|
366 |
void Rainbow::HubClient::save_database () |
367 |
{ |
368 |
Mutex::Lock lock(db_mutex); |
369 |
xmlpp::Document tree; |
370 |
tree.create_root_node("rainbow"); |
371 |
xmlpp::Node *root = tree.get_root_node(); |
372 |
|
373 |
xmlpp::Node *resources = root->add_child("resources"); |
374 |
map<Glib::ustring,ref_ptr<Resource> >::iterator p; |
375 |
for (p = db.begin(); p != db.end(); ++p) { |
376 |
if (!p->second->prepared) |
377 |
continue; |
378 |
xmlpp::Element *node = resources->add_child("resource"); |
379 |
node->set_attribute("url", p->second->get_url()); |
380 |
node->set_attribute("filename", p->second->get_filename()); |
381 |
node->set_attribute("sharable", p->second->sharable ? "true" : "false"); |
382 |
if (p->second->allocated_size) { |
383 |
ostringstream as; |
384 |
as << p->second->allocated_size; |
385 |
node->set_attribute("size", as.str()); |
386 |
} |
387 |
} |
388 |
|
389 |
tree.write_to_file_formatted(string(getenv("HOME")) + "/.rainbow-db.xml"); |
390 |
} |
391 |
|
392 |
void Rainbow::HubClient::connect () |
393 |
{ |
394 |
if (hub_host.length() == 0) |
395 |
return; |
396 |
|
397 |
if (hubsock > 0) { |
398 |
// connection is already/still established |
399 |
alarm.set_relative(reconnect_interval); |
400 |
} else { |
401 |
HubConnector::connect(mem_fun(*this, &HubClient::on_connect), hub_host); |
402 |
} |
403 |
} |
404 |
|
405 |
void Rainbow::HubClient::disconnect () |
406 |
{ |
407 |
if (hubsock > 0) { |
408 |
close(hubsock); |
409 |
} |
410 |
hubsock = 0; |
411 |
} |
412 |
|
413 |
void Rainbow::HubClient::on_connect (int socket) |
414 |
{ |
415 |
hubsock = socket; |
416 |
|
417 |
// reset alarm |
418 |
alarm.set_relative(reconnect_interval); |
419 |
|
420 |
// send database to server |
421 |
if (socket > 0) { |
422 |
string send; |
423 |
map<Glib::ustring,ref_ptr<Resource> >::iterator i; |
424 |
for (i = db.begin(); i != db.end(); ++i) |
425 |
if (i->second->sharable) |
426 |
send.append('+' + i->first + CRLF); |
427 |
|
428 |
if (!send_data_on_socket(hubsock, send)) |
429 |
disconnect(); |
430 |
} |
431 |
} |
432 |
|
433 |
ref_ptr<Resource> Rainbow::HubClient::find (const Glib::ustring &url) |
434 |
{ |
435 |
Mutex::Lock lock(db_mutex); |
436 |
map<Glib::ustring,ref_ptr<Resource> >::iterator p; |
437 |
p = db.find(url); |
438 |
if (p != db.end()) |
439 |
return p->second; |
440 |
else |
441 |
return ref_ptr<Resource>(); |
442 |
} |
443 |
|
444 |
ref_ptr<Resource> Rainbow::HubClient::create (const Glib::ustring &url) |
445 |
{ |
446 |
ref_ptr<Resource> r = find(url); |
447 |
if (!&*r) { |
448 |
Mutex::Lock lock(db_mutex); |
449 |
r = ref_ptr<Resource>(new Resource(url)); |
450 |
db.insert(make_pair(url, r)); |
451 |
|
452 |
if (r->has_rdf()) |
453 |
RdfResource::get_and_do(url, bind(mem_fun(*this, &HubClient::parse_resource), &*r)); |
454 |
else { |
455 |
r->available_locations.push_back(url); |
456 |
r->obtained_info = true; |
457 |
} |
458 |
} |
459 |
return r; |
460 |
} |
461 |
|
462 |
bool Rainbow::HubClient::get_filename_threadsafe (const Glib::ustring &url, |
463 |
Glib::ustring &filename, |
464 |
bool only_if_sharable) |
465 |
{ |
466 |
ref_ptr<Resource> r = find(url); |
467 |
if (!&*r) |
468 |
return false; |
469 |
if (only_if_sharable && !r->sharable) |
470 |
return false; |
471 |
|
472 |
Mutex::Lock lock(db_mutex); |
473 |
filename = r->get_filename(); |
474 |
return true; |
475 |
} |
476 |
|
477 |
size_t Rainbow::HubClient::get_size_threadsafe (const Glib::ustring &url) |
478 |
{ |
479 |
ref_ptr<Resource> r = find(url); |
480 |
if (!&*r) |
481 |
return 0; |
482 |
|
483 |
Mutex::Lock lock(db_mutex); |
484 |
size_t s = r->allocated_size; |
485 |
return s; |
486 |
} |
487 |
|
488 |
static inline bool check_path (const char *path) |
489 |
{ |
490 |
struct stat s; |
491 |
|
492 |
if (!stat(path, &s)) { |
493 |
if (!S_ISDIR(s.st_mode)) { |
494 |
//g_warning("%s exists but is not a directory", path); |
495 |
return false; |
496 |
} |
497 |
} else { |
498 |
if (mkdir(path, 0755)) { |
499 |
//g_warning("%s cannot be created", path); |
500 |
return false; |
501 |
} |
502 |
} |
503 |
|
504 |
return true; |
505 |
} |
506 |
|
507 |
Glib::ustring Rainbow::HubClient::random_filename () |
508 |
{ |
509 |
string basedir = getenv("HOME") + string("/.rainbow-cache"); |
510 |
check_path(basedir.c_str()); // error out if false? |
511 |
|
512 |
string filename; |
513 |
struct stat s; |
514 |
do { |
515 |
char rnd[] = " "; |
516 |
for (int i = 0; i < 8; ++i) |
517 |
rnd[i] = rand() % 26 + 'a'; |
518 |
filename = basedir + string("/") + rnd; |
519 |
} while (!stat(filename.c_str(), &s)); |
520 |
|
521 |
return filename_to_utf8(filename); |
522 |
} |
523 |
|
524 |
void Rainbow::HubClient::prepare_resource (Resource *r) |
525 |
{ |
526 |
if (r->prepared || r->downloading) |
527 |
return; |
528 |
if (!r->obtained_info) { |
529 |
r->prepare_once_info_is_obtained = true; |
530 |
return; |
531 |
} |
532 |
|
533 |
r->downloading = true; |
534 |
|
535 |
if (r->has_rdf()) |
536 |
query_hub(r); // FIXME: make this asynchronous |
537 |
start_download(r); |
538 |
} |
539 |
|
540 |
void Rainbow::HubClient::query_hub (Resource *r) |
541 |
{ |
542 |
string query('?' + r->get_url() + CRLF); |
543 |
string buffer; |
544 |
int lines_remaining = -1; |
545 |
if (hubsock <= 0 || !send_data_on_socket(hubsock, query)) { |
546 |
disconnect(); |
547 |
} else { |
548 |
while (lines_remaining) { |
549 |
const size_t buf_size = 2048; |
550 |
char buf[buf_size]; |
551 |
ssize_t rb = read(hubsock, buf, buf_size); |
552 |
if (rb <= 0) |
553 |
break; |
554 |
buffer.append(buf, rb); |
555 |
string::size_type p; |
556 |
while (lines_remaining |
557 |
&& (p = buffer.find(CRLF)) != string::npos) { |
558 |
string line = buffer.substr(0, p); |
559 |
buffer = buffer.substr(p + 2); |
560 |
if (lines_remaining == -1) { |
561 |
// first line |
562 |
lines_remaining = atoi(line.c_str()); |
563 |
if (lines_remaining < 0) |
564 |
return; |
565 |
} else { |
566 |
// add to array |
567 |
try { |
568 |
Glib::ustring l(line); |
569 |
if (l.validate()) |
570 |
r->mirrors.push_back(l); |
571 |
} catch (...) { |
572 |
} |
573 |
--lines_remaining; |
574 |
} |
575 |
} |
576 |
} |
577 |
} |
578 |
} |
579 |
|
580 |
void Rainbow::HubClient::start_download (Resource *r) |
581 |
{ |
582 |
if (!r->filename.size() && (r->mirrors.size() || r->available_locations.size())) |
583 |
r->filename = random_filename(); |
584 |
|
585 |
if (r->mirrors.size()) { |
586 |
Glib::ustring mirror = r->mirrors[r->mirrors.size() - 1]; |
587 |
r->mirrors.pop_back(); |
588 |
unsigned short port = 80; |
589 |
Glib::ustring::size_type colon = mirror.find(':'); |
590 |
if (colon != Glib::ustring::npos) { |
591 |
Glib::ustring prt = mirror.substr(colon + 1); |
592 |
mirror = mirror.substr(0, colon); |
593 |
port = atoi(prt.c_str()); |
594 |
} |
595 |
// FIXME: use different DL |
596 |
r->dl.reset(new HttpClient(mirror, port, false)); |
597 |
if (r->get_url().substr(0, 6) != "http:/") { |
598 |
start_download(r); // try, try again |
599 |
return; |
600 |
} |
601 |
r->dl->get(r->get_url().substr(6), r->get_filename()); |
602 |
r->dl->signal_request_done.connect(bind(mem_fun(*this, &HubClient::file_download_done_callback), r)); |
603 |
r->dl->signal_download_percent.connect(bind(mem_fun(*this, &HubClient::file_download_percent_callback), r)); |
604 |
} else if (r->available_locations.size() > r->available_locations_cursor) { |
605 |
Glib::ustring remote_host, remote_file; |
606 |
unsigned short remote_port; |
607 |
if (!HttpClient::parse_url(r->available_locations[r->available_locations_cursor++], |
608 |
remote_host, remote_port, remote_file)) { |
609 |
start_download(r); // try, try again |
610 |
return; |
611 |
} |
612 |
r->dl.reset(new HttpClient(remote_host, remote_port, false)); |
613 |
r->dl->get(remote_file, r->get_filename()); |
614 |
r->dl->signal_request_done.connect(bind(mem_fun(*this, &HubClient::file_download_done_callback), r)); |
615 |
r->dl->signal_download_percent.connect(bind(mem_fun(*this, &HubClient::file_download_percent_callback), r)); |
616 |
} else { |
617 |
// not available anywhere now |
618 |
logmsg << "HubClient: Resource not available currently: " << r->get_url() << endl; |
619 |
r->downloading = false; |
620 |
r->signal_download_done(false); |
621 |
} |
622 |
} |
623 |
|
624 |
void Rainbow::HubClient::set_hub (const Glib::ustring &hub) |
625 |
{ |
626 |
if (hub == hub_host) |
627 |
return; |
628 |
|
629 |
hub_host = hub; |
630 |
alarm.reset(); |
631 |
disconnect(); |
632 |
connect(); |
633 |
} |
634 |
|
635 |
void Rainbow::HubClient::file_download_percent_callback (unsigned int percent, Resource *r) |
636 |
{ |
637 |
r->signal_downloading(percent); |
638 |
} |
639 |
|
640 |
void Rainbow::HubClient::download_success (Resource *r) |
641 |
{ |
642 |
r->prepared = true; |
643 |
r->downloading = false; |
644 |
r->signal_download_done(true); |
645 |
check_allocated_size(r); |
646 |
} |
647 |
|
648 |
void Rainbow::HubClient::file_download_done_callback (bool success, Resource *r) |
649 |
{ |
650 |
r->prepared = success; |
651 |
r->dl.reset(); |
652 |
|
653 |
if (success) { |
654 |
// verify checksum |
655 |
if (r->checksum.size() != 0) |
656 |
Checksum::verify(mem_fun(*this, &HubClient::verify_checksum_callback), r); |
657 |
else |
658 |
download_success(r); |
659 |
} else |
660 |
start_download(r); |
661 |
} |
662 |
|
663 |
void Rainbow::HubClient::verify_checksum_callback (bool success, Resource *r) |
664 |
{ |
665 |
if (success) { |
666 |
download_success(r); |
667 |
// tell hub we have this file |
668 |
if (hubsock > 0) { |
669 |
string send(' ' + r->get_url() + CRLF); |
670 |
if (r->sharable) |
671 |
send[0] = '+'; |
672 |
else |
673 |
send[0] = '-'; // fixme: is the necessary? |
674 |
|
675 |
if (!send_data_on_socket(hubsock, send)) |
676 |
disconnect(); |
677 |
} |
678 |
} else |
679 |
start_download(r); |
680 |
} |
681 |
|
682 |
void Rainbow::HubClient::check_allocated_size (Resource *r) |
683 |
{ |
684 |
struct stat s; |
685 |
try { |
686 |
if (!stat(filename_from_utf8(r->get_filename()).c_str(), &s)) |
687 |
set_allocated_size(r, s.st_size); |
688 |
} catch (...) { |
689 |
} |
690 |
} |
691 |
|
692 |
void Rainbow::HubClient::set_allocated_size (Resource *r, size_t s) |
693 |
{ |
694 |
total_allocated_size -= r->allocated_size / 1024; |
695 |
r->allocated_size = s; |
696 |
total_allocated_size += r->allocated_size / 1024; |
697 |
|
698 |
if (s != 0) |
699 |
review_cache_size(); |
700 |
} |
701 |
|
702 |
void Rainbow::HubClient::review_cache_size () |
703 |
{ |
704 |
const size_t allowed_allocated_size = cache_size * 1024; |
705 |
if (total_allocated_size > allowed_allocated_size) |
706 |
signal_must_uncache(total_allocated_size - allowed_allocated_size); |
707 |
} |
708 |
|
709 |
void Rainbow::HubClient::set_cache_size (unsigned int megabytes) |
710 |
{ |
711 |
if (megabytes < 30) |
712 |
megabytes = 30; // there has to be a bottom limit |
713 |
|
714 |
cache_size = megabytes; |
715 |
review_cache_size(); |
716 |
} |
717 |
|
718 |
void Rainbow::HubClient::uncache_resource (Resource *r) |
719 |
{ |
720 |
if (!r->prepared) |
721 |
return; |
722 |
|
723 |
set_allocated_size(r, 0); |
724 |
r->prepared = false; |
725 |
r->obtained_info = false; |
726 |
r->download_percent = 0; |
727 |
r->available_locations_cursor = 0; |
728 |
r->available_locations.clear(); |
729 |
r->mirrors.clear(); |
730 |
r->signal_file_deleted(); |
731 |
|
732 |
unlink(r->filename.c_str()); |
733 |
|
734 |
// fixme: this should not be necessary now, but it needs to be done at some point in case you decide to download again |
735 |
if (r->has_rdf()) |
736 |
RdfResource::get_and_do(r->get_url(), bind(mem_fun(*this, &HubClient::parse_resource), r)); |
737 |
} |
738 |
|
739 |
// fixme: maybe size should be stored, with a boolean whether it's allocated or used |