📄 fileshare.cc
字号:
/* * libjingle * Copyright 2004--2006, Google Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */#include "talk/session/fileshare/fileshare.h"#include "talk/base/httpcommon-inl.h"#include "talk/base/fileutils.h"#include "talk/base/streamutils.h"#include "talk/base/event.h"#include "talk/base/helpers.h"#include "talk/base/httpclient.h"#include "talk/base/httpserver.h"#include "talk/base/pathutils.h"#include "talk/base/socketstream.h"#include "talk/base/stringdigest.h"#include "talk/base/stringencode.h"#include "talk/base/stringutils.h"#include "talk/base/tarstream.h"#include "talk/base/thread.h"#include "talk/session/tunnel/pseudotcpchannel.h"#include "talk/session/tunnel/tunnelsessionclient.h"///////////////////////////////////////////////////////////////////////////////// <description xmlns="http://www.google.com/session/share">// <manifest>// <file size='341'>// <name>foo.txt</name>// </file>// <file size='51321'>// <name>foo.jpg</name>// <image width='480' height='320'/>// </file>// <folder>// <name>stuff</name>// </folder>// </manifest>// <protocol>// <http>// <url name='source-path'>/temporary/23A53F01/</url>// <url name='preview-path'>/temporary/90266EA1/</url>// </http>// <raw/>// </protocol>// </description>// <p:transport xmns:p="p2p"/>//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Constants and private functions///////////////////////////////////////////////////////////////////////////////const std::string NS_GOOGLE_SHARE("http://www.google.com/session/share");namespace {const buzz::QName QN_SHARE_DESCRIPTION(true, NS_GOOGLE_SHARE, "description");const buzz::QName QN_SHARE_MANIFEST(true, NS_GOOGLE_SHARE, "manifest");const buzz::QName QN_SHARE_FOLDER(true, NS_GOOGLE_SHARE, "folder");const buzz::QName QN_SHARE_FILE(true, NS_GOOGLE_SHARE, "file");const buzz::QName QN_SHARE_NAME(true, NS_GOOGLE_SHARE, "name");const buzz::QName QN_SHARE_IMAGE(true, NS_GOOGLE_SHARE, "image");const buzz::QName QN_SHARE_PROTOCOL(true, NS_GOOGLE_SHARE, "protocol");const buzz::QName QN_SHARE_HTTP(true, NS_GOOGLE_SHARE, "http");const buzz::QName QN_SHARE_URL(true, NS_GOOGLE_SHARE, "url");const buzz::QName QN_SHARE_CHANNEL(true, NS_GOOGLE_SHARE, "channel");const buzz::QName QN_SHARE_COMPLETE(true, NS_GOOGLE_SHARE, "complete");const buzz::QName QN_SIZE(true, buzz::STR_EMPTY, "size");const buzz::QName QN_WIDTH(true, buzz::STR_EMPTY, "width");const buzz::QName QN_HEIGHT(true, buzz::STR_EMPTY, "height");const std::string kHttpSourcePath("source-path");const std::string kHttpPreviewPath("preview-path");const size_t kMinImageSize = 16U;const size_t kMaxImageSize = 0x8000U; // (32k)const size_t kMaxPreviewSize = 1024;// Wait 10 seconds to see if any new proxies get establishedconst uint32 kProxyWait = 10000; const int MSG_RETRY = 1;const uint32 kFileTransferEnableRetryMs = 1000 * 60 * 4; // 4 minutesconst std::string MIME_OCTET_STREAM("application/octet-stream");enum { MSG_PROXY_WAIT,};bool AllowedImageDimensions(size_t width, size_t height) { return (width >= kMinImageSize) && (width <= kMaxImageSize) && (height >= kMinImageSize) && (height <= kMaxImageSize);}} // anon namespacenamespace cricket {///////////////////////////////////////////////////////////////////////////////// FileShareManifest///////////////////////////////////////////////////////////////////////////////voidFileShareManifest::AddFile(const std::string& name, size_t size) { Item i = { T_FILE, name, size }; items_.push_back(i);}voidFileShareManifest::AddImage(const std::string& name, size_t size, size_t width, size_t height) { Item i = { T_IMAGE, name, size, width, height }; items_.push_back(i);}voidFileShareManifest::AddFolder(const std::string& name, size_t size) { Item i = { T_FOLDER, name, size }; items_.push_back(i);}size_tFileShareManifest::GetItemCount(Type t) const { size_t count = 0; for (size_t i=0; i<items_.size(); ++i) { if (items_[i].type == t) ++count; } return count;}///////////////////////////////////////////////////////////////////////////////// FileShareSession///////////////////////////////////////////////////////////////////////////////FileShareSession::FileShareSession(cricket::Session* session, const std::string &user_agent) : session_(session), state_(FS_NONE), is_closed_(false), is_sender_(false), manifest_(NULL), pool_(this), http_client_(NULL), http_server_(NULL), transfer_connection_id_(talk_base::HTTP_INVALID_CONNECTION_ID), counter_(NULL), item_transferring_(0), bytes_transferred_(0), local_cancel_(false), local_listener_(NULL), remote_listener_(NULL), next_channel_id_(1), user_agent_(user_agent) { session_->SignalState.connect(this, &FileShareSession::OnSessionState); session_->SignalInfoMessage.connect(this, &FileShareSession::OnSessionInfoMessage); session_->SignalChannelGone.connect(this, &FileShareSession::OnSessionChannelGone);}FileShareSession::~FileShareSession() { ASSERT(FS_NONE != state_); // If we haven't closed, do cleanup now. if (!IsClosed()) { if (!IsComplete()) { state_ = FS_FAILURE; } DoClose(true); } if (session_) { // Make sure we don't get future state changes on this session. session_->SignalState.disconnect(this); session_->SignalInfoMessage.disconnect(this); session_ = NULL; } for (TransactionList::const_iterator trans_it = transactions_.begin(); trans_it != transactions_.end(); ++trans_it) { (*trans_it)->response()->set_error(talk_base::HC_NOT_FOUND); http_server_->Respond(*trans_it); } delete http_client_; delete http_server_; delete manifest_; delete local_listener_; delete remote_listener_;}boolFileShareSession::IsComplete() const { return (state_ >= FS_COMPLETE);}boolFileShareSession::IsClosed() const { return is_closed_;}FileShareStateFileShareSession::state() const { return state_;}boolFileShareSession::is_sender() const { ASSERT(FS_NONE != state_); return is_sender_;}const buzz::Jid&FileShareSession::jid() const { ASSERT(FS_NONE != state_); return jid_;}const FileShareManifest*FileShareSession::manifest() const { ASSERT(FS_NONE != state_); return manifest_;}const std::string&FileShareSession::local_folder() const { ASSERT(!local_folder_.empty()); return local_folder_;}voidFileShareSession::Share(const buzz::Jid& jid, FileShareManifest* manifest) { ASSERT(FS_NONE == state_); ASSERT(NULL != session_); http_server_ = new talk_base::HttpServer; http_server_->SignalHttpRequest.connect(this, &FileShareSession::OnHttpRequest); http_server_->SignalHttpRequestComplete.connect(this, &FileShareSession::OnHttpRequestComplete); http_server_->SignalConnectionClosed.connect(this, &FileShareSession::OnHttpConnectionClosed); FileShareDescription* desc = new FileShareDescription; desc->supports_http = true; desc->manifest = *manifest; GenerateTemporaryPrefix(&desc->source_path); GenerateTemporaryPrefix(&desc->preview_path); session_->Initiate(jid.Str(), NULL, desc); delete manifest;}voidFileShareSession::Accept() { ASSERT(FS_OFFER == state_); ASSERT(NULL != session_); ASSERT(NULL != manifest_); ASSERT(!http_client_); ASSERT(item_transferring_ == 0); http_client_ = new talk_base::HttpClient(user_agent_, &pool_); http_client_->SignalHttpClientComplete.connect(this, &FileShareSession::OnHttpClientComplete); http_client_->SignalHttpClientClosed.connect(this, &FileShareSession::OnHttpClientClosed); // The receiver now has a need for the http_server_, when previewing already // downloaded content. http_server_ = new talk_base::HttpServer; http_server_->SignalHttpRequest.connect(this, &FileShareSession::OnHttpRequest); http_server_->SignalHttpRequestComplete.connect(this, &FileShareSession::OnHttpRequestComplete); http_server_->SignalConnectionClosed.connect(this, &FileShareSession::OnHttpConnectionClosed); FileShareDescription* desc = new FileShareDescription; desc->supports_http = description()->supports_http; session_->Accept(desc); SetState(FS_TRANSFER, false); NextDownload();}voidFileShareSession::Decline() { ASSERT(FS_OFFER == state_); ASSERT(NULL != session_); local_cancel_ = true; session_->Reject();}voidFileShareSession::Cancel() { ASSERT(!IsComplete()); ASSERT(NULL != session_); local_cancel_ = true; session_->Terminate();}boolFileShareSession::GetItemUrl(size_t index, std::string* url) { return GetItemBaseUrl(index, false, url);}bool FileShareSession::GetImagePreviewUrl(size_t index, size_t width, size_t height, std::string* url) { if (!GetItemBaseUrl(index, true, url)) return false; if (FileShareManifest::T_IMAGE != manifest_->item(index).type) { ASSERT(false); return false; } char query[256]; talk_base::sprintfn(query, ARRAY_SIZE(query), "?width=%u&height=%u", width, height); url->append(query); return true;}void FileShareSession::ResampleComplete(talk_base::StreamInterface *i, talk_base::HttpTransaction *trans, bool success) { bool found = false; for (TransactionList::const_iterator trans_it = transactions_.begin(); trans_it != transactions_.end(); ++trans_it) { if (*trans_it == trans) { found = true; break; } } if (!found) return; transactions_.remove(trans); if (success) { trans->response()->set_success(MIME_OCTET_STREAM, i); http_server_->Respond(trans); } trans->response()->set_error(talk_base::HC_NOT_FOUND); http_server_->Respond(trans);}bool FileShareSession::GetProgress(size_t& bytes) const { bool known = true; bytes = bytes_transferred_; if (counter_) { size_t current_size = manifest_->item(item_transferring_).size; size_t current_pos = counter_->GetByteCount(); if (current_size == FileShareManifest::SIZE_UNKNOWN) { known = false; } else if (current_pos > current_size) { // Don't allow the size of a 'known' item to be reported as larger than // it claimed to be. ASSERT(false); current_pos = current_size; } bytes += current_pos; } return known;}bool FileShareSession::GetTotalSize(size_t& bytes) const { bool known = true; bytes = 0; for (size_t i=0; i<manifest_->size(); ++i) { if (manifest_->item(i).size == FileShareManifest::SIZE_UNKNOWN) { // We make files of unknown length worth a single byte. known = false; bytes += 1; } else { bytes += manifest_->item(i).size; } } return known;}bool FileShareSession::GetCurrentItemName(std::string* name) { if (FS_TRANSFER != state_) { name->clear(); return false; } ASSERT(item_transferring_ < manifest_->size()); if (transfer_name_.empty()) { const FileShareManifest::Item& item = manifest_->item(item_transferring_); *name = item.name; } else { *name = transfer_name_; } return !name->empty();}// StreamPool Implementationtalk_base::StreamInterface* FileShareSession::RequestConnectedStream( const talk_base::SocketAddress& remote, int* err) { ASSERT(remote.IPAsString() == jid_.Str()); ASSERT(!IsClosed()); ASSERT(NULL != session_); if (!session_) { if (err) *err = -1; return NULL; } char channel_name[64]; talk_base::sprintfn(channel_name, ARRAY_SIZE(channel_name), "private-%u", next_channel_id_++); if (err) *err = 0; return CreateChannel(channel_name);}void FileShareSession::ReturnConnectedStream( talk_base::StreamInterface* stream) { talk_base::Thread::Current()->Dispose(stream);}// MessageHandler Implementation
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -