1 /*****************************************************************************\
3 * Name : file_transfer_tentacle *
4 * Author : Chris Koeritz *
6 *******************************************************************************
7 * Copyright (c) 2005-$now By Author. This program is free software; you can *
8 * redistribute it and/or modify it under the terms of the GNU General Public *
9 * License as published by the Free Software Foundation; either version 2 of *
10 * the License or (at your option) any later version. This is online at: *
11 * http://www.fsf.org/copyleft/gpl.html *
12 * Please send any updates to: fred@gruntose.com *
13 \*****************************************************************************/
15 #include "file_transfer_tentacle.h"
17 #include <basis/mutex.h>
18 #include <filesystem/directory_tree.h>
19 #include <filesystem/filename.h>
20 #include <filesystem/filename_list.h>
21 #include <filesystem/heavy_file_ops.h>
22 #include <loggers/program_wide_logger.h>
23 #include <octopus/entity_defs.h>
24 #include <octopus/unhandled_request.h>
25 #include <processes/ethread.h>
26 #include <textual/parser_bits.h>
28 using namespace basis;
29 using namespace filesystem;
30 using namespace loggers;
31 using namespace octopi;
32 using namespace processes;
33 using namespace structures;
34 using namespace textual;
35 using namespace timely;
40 #define AUTO_LOCK auto_synchronizer loc(*_lock);
41 // protects our lists.
43 const int FTT_CLEANING_INTERVAL = 30 * SECOND_ms;
44 // this is how frequently we clean up the list to remove outdated transfers.
46 const int TRANSFER_TIMEOUT = 10 * MINUTE_ms;
47 // if it hasn't been touched in this long, it's out of there.
49 #define DEBUG_FILE_TRANSFER_TENTACLE
50 // uncomment for noisier version.
53 #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s)
57 class file_transfer_record
60 // valid for both transfers and correspondences.
61 astring _src_root; // where the info is on the data provider.
62 time_stamp _last_active; // when this was last used.
64 // valid for file transfers only.
65 octopus_entity _ent; // the entity requesting this service.
66 astring _dest_root; // where the info is on the data sink.
67 filename_list *_diffs; // the differences to be transferred.
68 file_transfer_header _last_sent; // the last chunk that was sent.
69 bool _done; // true if the transfer is finished.
70 string_array _includes; // the set to include.
72 // valid for correspondence records only.
73 directory_tree *_local_dir; // our local information about the transfer.
74 astring _source_mapping; // valid for a correspondence record.
75 int _refresh_interval; // the rate of refreshing the source tree.
77 file_transfer_record() : _diffs(NIL), _last_sent(file_time()),
78 _done(false), _local_dir(NIL)
81 ~file_transfer_record() {
86 astring text_form() const {
88 to_return += astring("src=") + _src_root + astring(" last act=")
89 + _last_active.text_form();
90 if (_ent.blank()) to_return += astring(" ent=") + _ent.text_form();
92 to_return += astring(" dest=") + _dest_root;
93 to_return += astring(" last_sent=") + _last_sent.text_form();
101 // this implementation assumes that the same entity will never simultaneously
102 // transfer the same source to the same destination. that assumption holds
103 // up fine for different clients, since they should have different entities.
104 // when there is a collision on the entity/src/dest, then the default action
105 // is to assume that the transfer is just being started over.
107 class file_transfer_status : public amorph<file_transfer_record>
110 // find a transfer record by the key fields.
111 file_transfer_record *find(const octopus_entity &ent, const astring &src,
112 const astring &dest) {
113 for (int i = 0; i < elements(); i++) {
114 const file_transfer_record *rec = get(i);
115 if (rec && (rec->_ent == ent) && (rec->_src_root == src)
116 && (rec->_dest_root == dest) ) {
123 virtual ~file_transfer_status() {}
125 DEFINE_CLASS_NAME("file_transfer_status");
127 // find a file correspondence record by the mapping name.
128 file_transfer_record *find_mapping(const astring &source_mapping) {
129 for (int i = 0; i < elements(); i++) {
130 const file_transfer_record *rec = get(i);
131 if (rec && (rec->_source_mapping == source_mapping) )
137 // turns a source mapping into the location that it corresponds to.
138 astring translate(const astring &source_path) const {
139 // FUNCDEF("translate");
141 filename(source_path).separate(pieces);
142 astring source_mapping = pieces[0];
143 pieces.zap(0, 0); // remove source part.
145 for (int i = 0; i < elements(); i++) {
146 const file_transfer_record *rec = get(i);
147 if (rec && (rec->_source_mapping == source_mapping) ) {
148 return rec->_src_root;
151 return astring::empty_string();
154 // removes a file transfer record by the key fields.
155 bool whack(const octopus_entity &ent, const astring &src,
156 const astring &dest) {
157 for (int i = 0; i < elements(); i++) {
158 const file_transfer_record *rec = get(i);
159 if (rec && (rec->_ent == ent) && (rec->_src_root == src)
160 && (rec->_dest_root == dest) ) {
168 // clean all records for the entity "ent".
169 void whack_all(const octopus_entity &ent) {
170 for (int i = elements() - 1; i >= 0; i--) {
171 const file_transfer_record *rec = get(i);
172 if (rec && (rec->_ent == ent) )
177 // removes a file transfer correspondence.
178 bool whack_mapping(const astring &source_mapping) {
179 for (int i = elements() - 1; i >= 0; i--) {
180 const file_transfer_record *rec = get(i);
181 if (rec && (rec->_source_mapping == source_mapping) ) {
189 // returns a string dump of the fields in this list.
190 astring text_form() const {
192 for (int i = 0; i < elements(); i++) {
193 const file_transfer_record *rec = get(i);
195 to_return += rec->text_form() + parser_bits::platform_eol_to_chars();
203 class file_transfer_cleaner : public ethread
206 file_transfer_cleaner(file_transfer_tentacle &parent)
207 : ethread(FTT_CLEANING_INTERVAL, SLACK_INTERVAL), _parent(parent) {}
209 virtual void perform_activity(void *formal(ptr)) { _parent.periodic_actions(); }
212 file_transfer_tentacle &_parent;
217 file_transfer_tentacle::file_transfer_tentacle(int maximum_transfer,
218 file_transfer_tentacle::transfer_modes mode_of_transfer)
219 : tentacle_helper<file_transfer_infoton>
220 (file_transfer_infoton::file_transfer_classifier(), false),
221 _maximum_transfer(maximum_transfer),
222 _transfers(new file_transfer_status),
223 _correspondences(new file_transfer_status),
225 _cleaner(new file_transfer_cleaner(*this)),
226 _mode(mode_of_transfer)
228 _cleaner->start(NIL);
231 file_transfer_tentacle::~file_transfer_tentacle()
235 WHACK(_correspondences);
240 astring file_transfer_tentacle::text_form() const
243 return _transfers->text_form();
246 void file_transfer_tentacle::expunge(const octopus_entity &to_remove)
249 _transfers->whack_all(to_remove);
252 outcome file_transfer_tentacle::add_correspondence
253 (const astring &source_mapping, const astring &source_root,
254 int refresh_interval)
256 FUNCDEF("add_correspondence");
259 remove_correspondence(source_mapping); // clean the old one out first.
261 // create new file transfer record to hold this correspondence.
262 file_transfer_record *new_record = new file_transfer_record;
263 new_record->_source_mapping = source_mapping;
264 new_record->_src_root = source_root;
265 new_record->_refresh_interval = refresh_interval;
266 new_record->_local_dir = new directory_tree(source_root);
267 //hmmm: doesn't say anything about a pattern. do we need to worry about that?
269 // check that the directory looked healthy.
270 if (!new_record->_local_dir->good()) {
272 return common::ACCESS_DENIED;
274 #ifdef DEBUG_FILE_TRANSFER_TENTACLE
275 LOG(astring("adding tree for: ent=") + new_record->_ent.text_form()
276 + " src=" + new_record->_src_root + " dest=" + new_record->_dest_root);
278 // calculate size and checksum info for the directory.
279 new_record->_local_dir->calculate( !(_mode & COMPARE_CONTENT_SAMPLE) );
281 #ifdef DEBUG_FILE_TRANSFER_TENTACLE
282 LOG(astring("done adding tree for: ent=") + new_record->_ent.text_form()
283 + " src=" + new_record->_src_root + " dest=" + new_record->_dest_root);
286 _correspondences->append(new_record);
291 outcome file_transfer_tentacle::remove_correspondence
292 (const astring &source_mapping)
295 if (!_correspondences->whack_mapping(source_mapping))
300 bool file_transfer_tentacle::get_differences(const octopus_entity &ent,
301 const astring &src, const astring &dest, filename_list &diffs)
303 // FUNCDEF("get_differences");
306 file_transfer_record *the_rec = _transfers->find(ent, src, dest);
307 if (!the_rec) return false;
308 if (!the_rec->_diffs) return false; // no diffs listed.
309 diffs = *the_rec->_diffs;
313 bool file_transfer_tentacle::status(const octopus_entity &ent,
314 const astring &src, const astring &dest, double &total_size,
315 int &total_files, double ¤t_size, int ¤t_files, bool &done,
316 time_stamp &last_active)
318 // FUNCDEF("status");
324 file_transfer_record *the_rec = _transfers->find(ent, src, dest);
325 if (!the_rec) return false;
326 done = the_rec->_done;
327 last_active = the_rec->_last_active;
329 if (the_rec->_diffs) {
330 the_rec->_diffs->calculate_progress(the_rec->_last_sent._filename,
331 the_rec->_last_sent._byte_start + the_rec->_last_sent._length,
332 current_files, current_size);
333 total_files = the_rec->_diffs->total_files();
334 total_size = the_rec->_diffs->total_size();
340 outcome file_transfer_tentacle::register_file_transfer
341 (const octopus_entity &ent, const astring &src_root,
342 const astring &dest_root, const string_array &includes)
344 FUNCDEF("register_file_transfer");
346 // make sure that this isn't an existing transfer. if so, we just update
348 file_transfer_record *the_rec = _transfers->find(ent, src_root, dest_root);
350 the_rec = new file_transfer_record;
351 the_rec->_src_root = src_root;
352 the_rec->_dest_root = dest_root;
354 the_rec->_includes = includes;
355 _transfers->append(the_rec); // add the new record.
357 the_rec->_done = false;
358 the_rec->_includes = includes;
359 the_rec->_last_active.reset(); // freshen up the last activity time.
364 outcome file_transfer_tentacle::cancel_file_transfer(const octopus_entity &ent,
365 const astring &src_root, const astring &dest_root)
368 return _transfers->whack(ent, src_root, dest_root)? OKAY : NOT_FOUND;
371 directory_tree *file_transfer_tentacle::lock_directory(const astring &key)
374 file_transfer_record *the_rec = _correspondences->find_mapping(key);
375 if (!the_rec || !the_rec->_local_dir) {
377 return NIL; // unknown transfer.
379 return the_rec->_local_dir;
382 void file_transfer_tentacle::unlock_directory()
387 bool file_transfer_tentacle::add_path(const astring &key,
388 const astring &new_path)
391 file_transfer_record *the_rec = _correspondences->find_mapping(key);
392 if (!the_rec) return false; // unknown transfer.
393 if (!the_rec->_local_dir) return false; // not right type.
394 return the_rec->_local_dir->add_path(new_path) == common::OKAY;
397 bool file_transfer_tentacle::remove_path(const astring &key,
398 const astring &old_path)
401 file_transfer_record *the_rec = _correspondences->find_mapping(key);
402 if (!the_rec) return false; // unknown transfer.
403 if (!the_rec->_local_dir) return false; // not right type.
404 return the_rec->_local_dir->remove_path(old_path) == common::OKAY;
407 void file_transfer_tentacle::periodic_actions()
409 FUNCDEF("periodic_actions");
412 // first, we'll clean out old transfers.
413 time_stamp oldest_allowed(-TRANSFER_TIMEOUT);
414 // nothing older than this should be kept.
415 for (int i = _transfers->elements() - 1; i >= 0; i--) {
416 const file_transfer_record *curr = _transfers->get(i);
417 if (curr->_last_active < oldest_allowed) {
418 #ifdef DEBUG_FILE_TRANSFER_TENTACLE
419 LOG(astring("cleaning record for: ent=") + curr->_ent.text_form()
420 + " src=" + curr->_src_root + " dest=" + curr->_dest_root);
422 _transfers->zap(i, i);
426 // then we'll rescan any trees that are ready for it.
427 for (int i = 0; i < _correspondences->elements(); i++) {
428 file_transfer_record *curr = _correspondences->borrow(i);
429 if (curr->_last_active < time_stamp(-curr->_refresh_interval)) {
430 if (curr->_local_dir) {
431 #ifdef DEBUG_FILE_TRANSFER_TENTACLE
432 LOG(astring("refreshing tree for: ent=") + curr->_ent.text_form()
433 + " src=" + curr->_src_root + " dest=" + curr->_dest_root);
435 WHACK(curr->_local_dir);
436 curr->_local_dir = new directory_tree(curr->_src_root);
437 curr->_local_dir->calculate( !(_mode & COMPARE_CONTENT_SAMPLE) );
438 #ifdef DEBUG_FILE_TRANSFER_TENTACLE
439 LOG(astring("done refreshing tree for: ent=") + curr->_ent.text_form()
440 + " src=" + curr->_src_root + " dest=" + curr->_dest_root);
443 curr->_last_active.reset(); // reset our action time.
448 outcome file_transfer_tentacle::reconstitute(const string_array &classifier,
449 byte_array &packed_form, infoton * &reformed)
451 // this method doesn't use the lists, so it doesn't need locking.
452 if (classifier != file_transfer_infoton::file_transfer_classifier())
454 return reconstituter(classifier, packed_form, reformed,
455 (file_transfer_infoton *)NIL);
458 // the "handle_" and "conclude_" methods are thread-safe because the mutex is locked before
459 // their invocations.
461 outcome file_transfer_tentacle::handle_tree_compare_request
462 (file_transfer_infoton &req, const octopus_request_id &item_id)
464 FUNCDEF("handle_tree_compare_request");
466 // get the mapping from the specified location on this side.
467 filename splitting(req._src_root);
469 splitting.separate(pieces);
470 astring source_mapping = pieces[0];
472 // patch the name up to find the sub_path for the source.
473 filename source_start;
475 source_start.join(pieces);
477 // locate the allowed transfer depot for the mapping they provided.
478 file_transfer_record *mapping_record
479 = _correspondences->find_mapping(source_mapping);
480 if (!mapping_record) {
481 LOG(astring("could not find source mapping of ") + source_mapping);
485 // unpack the tree that they sent us which describes their local area.
486 directory_tree *dest_tree = new directory_tree;
487 if (!dest_tree->unpack(req._packed_data)) {
488 LOG(astring("could not unpack requester's directory tree"));
493 string_array requested_names;
494 if (!requested_names.unpack(req._packed_data)) {
495 LOG(astring("could not unpack requester's filename includes"));
500 // look up to see if this is about something that has already been seen.
501 // we don't want to add a new transfer record if they're already working on
502 // this. that also lets them do a new tree compare to restart the transfer.
503 file_transfer_record *the_rec = _transfers->find(item_id._entity,
504 req._src_root, req._dest_root);
506 // there was no existing record; we'll create a new one.
507 the_rec = new file_transfer_record;
508 the_rec->_ent = item_id._entity;
509 the_rec->_src_root = req._src_root;
510 the_rec->_dest_root = req._dest_root;
511 _transfers->append(the_rec);
513 // record some activity on this record.
514 the_rec->_done = false;
515 the_rec->_last_active.reset();
518 the_rec->_diffs = new filename_list;
520 int how_comp = file_info::EQUAL_NAME; // the prize for doing nothing.
521 if (_mode & COMPARE_SIZE_AND_TIME)
522 how_comp |= file_info::EQUAL_FILESIZE | file_info::EQUAL_TIMESTAMP;
523 if (_mode & COMPARE_CONTENT_SAMPLE)
524 how_comp |= file_info::EQUAL_CHECKSUM;
526 // compare the two trees of files.
527 directory_tree::compare_trees(*mapping_record->_local_dir,
528 source_start.raw(), *dest_tree, astring::empty_string(),
529 *the_rec->_diffs, (file_info::file_similarity)how_comp);
531 //LOG(astring("filenames decided as different:\n") + the_rec->_diffs->text_form());
533 // now prune the diffs to accord with what they claim they want.
534 if (requested_names.length()) {
535 for (int i = the_rec->_diffs->elements() - 1; i >= 0; i--) {
536 filename diff_curr = *the_rec->_diffs->get(i);
538 for (int j = 0; j < requested_names.length(); j++) {
539 filename req_curr(requested_names[j]);
540 if (req_curr.compare_suffix(diff_curr)) {
542 //LOG(astring("will use: ") + req_curr);
546 if (!found) the_rec->_diffs->zap(i, i);
550 req._packed_data.reset(); // clear out existing stuff before cloning.
551 file_transfer_infoton *reply = dynamic_cast<file_transfer_infoton *>(req.clone());
552 the_rec->_diffs->pack(reply->_packed_data);
554 //hmmm: does the other side really need the list of filenames? i guess we
555 // could check validity of what's transferred or check space available
556 // before the client starts the transfer.
558 reply->_request = false; // it's a response now.
559 store_product(reply, item_id);
560 // send back the comparison list.
565 outcome file_transfer_tentacle::handle_tree_compare_response
566 (file_transfer_infoton &resp, const octopus_request_id &item_id)
568 FUNCDEF("handle_tree_compare_response");
569 file_transfer_record *the_rec = _transfers->find(item_id._entity,
570 resp._src_root, resp._dest_root);
572 LOG(astring("could not find the record for this transfer: item=")
573 + item_id.text_form() + " src=" + resp._src_root + " dest="
575 return NOT_FOUND; // not registered, so reject it.
578 the_rec->_last_active.reset(); // record some activity on this record.
580 filename_list *flist = new filename_list;
581 if (!flist->unpack(resp._packed_data)) {
586 //hmmm: verify space on device?
588 the_rec->_diffs = flist; // set the list of differences.
592 outcome file_transfer_tentacle::handle_storage_request
593 (file_transfer_infoton &req, const octopus_request_id &item_id)
595 FUNCDEF("handle_storage_request");
596 if (_mode & ONLY_REPORT_DIFFS) {
597 // store an unhandled infoton.
598 unhandled_request *deny = new unhandled_request(item_id, req.classifier(), NO_HANDLER);
599 store_product(deny, item_id);
603 // look up the transfer record.
604 file_transfer_record *the_rec = _transfers->find(item_id._entity,
605 req._src_root, req._dest_root);
607 LOG(astring("could not find the record for this transfer: item=")
608 + item_id.text_form() + " src=" + req._src_root + " dest="
610 return NOT_FOUND; // not registered, so reject it.
613 the_rec->_last_active.reset(); // mark it as still active.
615 file_transfer_infoton *resp = dynamic_cast<file_transfer_infoton *>(req.clone());
617 if (!the_rec->_diffs) return BAD_INPUT; // wrong type of object.
619 outcome bufret = heavy_file_operations::buffer_files
620 (_correspondences->translate(the_rec->_src_root), *the_rec->_diffs,
621 the_rec->_last_sent, resp->_packed_data, _maximum_transfer);
622 if (bufret == heavy_file_operations::FINISHED) {
623 //here we go. finish by setting command to conclude.
624 LOG("got the final marker saying heavy file ops done!");
625 the_rec->_done = true;
626 resp->_command = file_transfer_infoton::CONCLUDE_TRANSFER_MARKER;
627 bufret = OKAY; // now it's no longer an exceptional outcome.
628 } else if (bufret != OKAY) {
629 // complain, but still send.
630 LOG(astring("buffer files returned an error on item=")
631 + item_id.text_form() + " src=" + req._src_root + " dest="
635 // if ( (bufret == OKAY) && !resp->_packed_data.length() ) {
636 // LOG(astring("failed to pack any data for file: ") + req._src_root);
639 if (!the_rec->_done && (bufret == OKAY) && !resp->_packed_data.length() ) {
640 // seems like the transfer is done.
641 LOG("marking empty transfer as done; why not caught above at FINISHED check?");
642 the_rec->_done = true;
643 resp->_command = file_transfer_infoton::CONCLUDE_TRANSFER_MARKER;
646 resp->_request = false; // it's a response now.
647 store_product(resp, item_id);
651 outcome file_transfer_tentacle::handle_storage_response
652 (file_transfer_infoton &resp, const octopus_request_id &item_id)
654 FUNCDEF("handle_storage_response");
655 if (_mode & ONLY_REPORT_DIFFS) {
660 // look up the transfer record.
661 file_transfer_record *the_rec = _transfers->find(item_id._entity,
662 resp._src_root, resp._dest_root);
663 if (!the_rec) return NOT_FOUND; // not registered, so reject it.
665 the_rec->_last_active.reset(); // mark it as still active.
667 if (!resp._packed_data.length()) {
668 // mark that we're done now.
669 the_rec->_done = true;
672 // chew on all the things they sent us.
673 while (resp._packed_data.length()) {
675 file_transfer_header found(empty);
676 if (!found.unpack(resp._packed_data)) {
678 LOG(astring("corruption seen on item=") + item_id.text_form()
679 + " src=" + resp._src_root + " dest=" + resp._dest_root);
682 the_rec->_last_sent = found;
684 if (found._length > resp._packed_data.length()) {
685 // another case for leaving--not enough data left in the buffer.
686 LOG(astring("data underflow seen on item=") + item_id.text_form()
687 + " src=" + resp._src_root + " dest=" + resp._dest_root);
690 byte_array to_write = resp._packed_data.subarray(0, found._length - 1);
691 resp._packed_data.zap(0, found._length - 1);
693 if (!the_rec->_diffs) return BAD_INPUT;
695 const file_info *recorded_info = the_rec->_diffs->find(found._filename);
696 if (!recorded_info) {
697 LOG(astring("unrequested file seen: ") + found._filename);
698 continue; // maybe there are others that aren't confused.
701 astring full_file = resp._dest_root + filename::default_separator()
702 + recorded_info->secondary();
704 outcome ret = heavy_file_operations::write_file_chunk(full_file,
705 found._byte_start, to_write);
707 LOG(astring("failed to write file chunk: error=")
708 + heavy_file_operations::outcome_name(ret) + " file=" + full_file
709 + a_sprintf(" start=%d len=%d", found._byte_start, found._length));
711 found._time.set_time(full_file);
714 // there is no response product to store.
718 outcome file_transfer_tentacle::conclude_storage_request
719 (file_transfer_infoton &req, const octopus_request_id &item_id)
721 FUNCDEF("handle_storage_request");
722 if (_mode & ONLY_REPORT_DIFFS) {
723 // store an unhandled infoton.
724 unhandled_request *deny = new unhandled_request(item_id, req.classifier(), NO_HANDLER);
725 store_product(deny, item_id);
729 // look up the transfer record.
730 file_transfer_record *the_rec = _transfers->find(item_id._entity,
731 req._src_root, req._dest_root);
733 LOG(astring("could not find the record for this transfer: item=")
734 + item_id.text_form() + " src=" + req._src_root + " dest="
736 return NOT_FOUND; // not registered, so reject it.
739 the_rec->_last_active.reset(); // mark it as still active.
741 file_transfer_infoton *resp = dynamic_cast<file_transfer_infoton *>(req.clone());
743 if (!the_rec->_diffs) return BAD_INPUT; // wrong type of object.
745 the_rec->_done = true; // we're concluding the transfer, so that's that.
746 resp->_request = false; // it's a response now.
747 store_product(resp, item_id);
751 outcome file_transfer_tentacle::conclude_storage_response
752 (file_transfer_infoton &resp, const octopus_request_id &item_id)
754 FUNCDEF("handle_storage_response");
755 if (_mode & ONLY_REPORT_DIFFS) {
760 // look up the transfer record.
761 file_transfer_record *the_rec = _transfers->find(item_id._entity,
762 resp._src_root, resp._dest_root);
763 if (!the_rec) return NOT_FOUND; // not registered, so reject it.
765 the_rec->_last_active.reset(); // mark it as still active.
767 // mark that we're done now.
768 the_rec->_done = true;
770 // there is no response product to store.
774 // consume() is the only method that is allowed to invoke the "handle_X" methods
775 // and it must lock the object beforehand.
777 outcome file_transfer_tentacle::consume(infoton &to_chow,
778 const octopus_request_id &item_id, byte_array &transformed)
782 file_transfer_infoton *inf = dynamic_cast<file_transfer_infoton *>(&to_chow);
783 if (!inf) return DISALLOWED; // not for us.
785 AUTO_LOCK; // protect our lists while we're working on them.
787 switch (inf->_command) {
788 case file_transfer_infoton::TREE_COMPARISON: {
789 if (inf->_request) return handle_tree_compare_request(*inf, item_id);
790 else return handle_tree_compare_response(*inf, item_id);
792 case file_transfer_infoton::PLACE_FILE_CHUNKS: {
793 if (inf->_request) return handle_storage_request(*inf, item_id);
794 else return handle_storage_response(*inf, item_id);
796 case file_transfer_infoton::CONCLUDE_TRANSFER_MARKER: {
797 if (inf->_request) return conclude_storage_request(*inf, item_id);
798 else return conclude_storage_response(*inf, item_id);
801 return BAD_INPUT; // not a recognized command.
804 outcome file_transfer_tentacle::refresh_now(const astring &source_mapping)
806 FUNCDEF("refresh_now");
808 for (int i = 0; i < _correspondences->elements(); i++) {
809 file_transfer_record *curr = _correspondences->borrow(i);
811 if (curr->_source_mapping != source_mapping) continue;
812 if (curr->_local_dir) {
813 #ifdef DEBUG_FILE_TRANSFER_TENTACLE
814 LOG(astring("refreshing tree for: ent=") + curr->_ent.text_form()
815 + " src=" + curr->_src_root + " dest=" + curr->_dest_root);
817 WHACK(curr->_local_dir);
818 curr->_local_dir = new directory_tree(curr->_src_root);
819 curr->_local_dir->calculate( !(_mode & COMPARE_CONTENT_SAMPLE) );
820 #ifdef DEBUG_FILE_TRANSFER_TENTACLE
821 LOG(astring("done refreshing tree for: ent=") + curr->_ent.text_form()
822 + " src=" + curr->_src_root + " dest=" + curr->_dest_root);
825 curr->_last_active.reset(); // reset our action time.