📄 fileshare.cc
字号:
*path = pathname.pathname();}bool FileShareSession::GetItemBaseUrl(size_t index, bool preview, std::string* url) { // This function composes a URL to the referenced item. It may be a local // file url (file:///...), or a remote peer url relayed through localhost // (http://...) ASSERT(NULL != url); if (index >= manifest_->size()) { ASSERT(false); return false; } const FileShareManifest::Item& item = manifest_->item(index); bool is_remote; if (is_sender_) { if (!preview) { talk_base::Pathname path(local_folder_); path.SetFilename(item.name); *url = path.url(); return true; } is_remote = false; } else { if ((index < stored_location_.size()) && !stored_location_[index].empty()) { if (!preview) { *url = talk_base::Pathname(stored_location_[index]).url(); return true; } // Note: Using the local downloaded files as a source for previews is // desireable, because it means that previews can be regenerated if IE's // cached versions get flushed for some reason, and the remote side is // not available. However, it has the downside that IE _must_ regenerate // the preview locally, which takes time, memory and CPU. Eventually, // we will unify the remote and local cached copy through some sort of // smart http proxying. In the meantime, always use the remote url, to // eliminate the annoying transition from remote to local caching. //is_remote = false; is_remote = true; } else { is_remote = true; } } talk_base::SocketAddress address; if (!GetProxyAddress(address, is_remote)) return false; std::string path; GetItemNetworkPath(index, preview, &path); talk_base::Url<char> make_url(path.c_str(), address.IPAsString().c_str(), address.port()); *url = make_url.url(); return true;}bool FileShareSession::GetProxyAddress(talk_base::SocketAddress& address, bool is_remote) { talk_base::AsyncSocket*& proxy_listener = is_remote ? remote_listener_ : local_listener_; if (!proxy_listener) { talk_base::AsyncSocket* listener = talk_base::Thread::Current()->socketserver() ->CreateAsyncSocket(SOCK_STREAM); if (!listener) return false; talk_base::SocketAddress bind_address("127.0.0.1", 0); if ((listener->Bind(bind_address) != 0) || (listener->Listen(5) != 0)) { delete listener; return false; } LOG(LS_INFO) << "Proxy listener available @ " << listener->GetLocalAddress().ToString(); listener->SignalReadEvent.connect(this, &FileShareSession::OnProxyAccept); proxy_listener = listener; } if (proxy_listener->GetState() == talk_base::Socket::CS_CLOSED) { if (is_remote) { address = remote_listener_address_; return true; } return false; } address = proxy_listener->GetLocalAddress(); return !address.IsAny();}talk_base::StreamInterface* FileShareSession::CreateChannel( const std::string& channel_name) { ASSERT(NULL != session_); // Send a heads-up for our new channel cricket::Session::XmlElements els; buzz::XmlElement* xel_channel = new buzz::XmlElement(QN_SHARE_CHANNEL, true); xel_channel->AddAttr(buzz::QN_NAME, channel_name); els.push_back(xel_channel); session_->SendInfoMessage(els); cricket::PseudoTcpChannel* channel = new cricket::PseudoTcpChannel(talk_base::Thread::Current(), session_); VERIFY(channel->Connect(channel_name)); return channel->GetStream();}void FileShareSession::SetState(FileShareState state, bool prevent_close) { if (state == state_) return; if (IsComplete()) { // Entering a completion state is permanent. ASSERT(false); return; } state_ = state; if (IsComplete()) { // All completion states auto-close except for FS_COMPLETE bool close = (state_ > FS_COMPLETE) || !prevent_close; if (close) { DoClose(true); } } SignalState(state_);}void FileShareSession::OnInitiate() { // Cache the variables we will need, in case session_ goes away is_sender_ = session_->initiator(); jid_ = buzz::Jid(session_->remote_name()); manifest_ = new FileShareManifest(description()->manifest); source_path_ = description()->source_path; preview_path_ = description()->preview_path; if (local_folder_.empty()) { LOG(LS_ERROR) << "FileShareSession - no local folder, using temp"; talk_base::Pathname temp_folder; talk_base::Filesystem::GetTemporaryFolder(temp_folder, true, NULL); local_folder_ = temp_folder.pathname(); } LOG(LS_INFO) << session_->state(); SetState(FS_OFFER, false);}void FileShareSession::NextDownload() { if (FS_TRANSFER != state_) return; if (item_transferring_ >= manifest_->size()) { // Notify the other side that transfer has completed cricket::Session::XmlElements els; els.push_back(new buzz::XmlElement(QN_SHARE_COMPLETE, true)); session_->SendInfoMessage(els); SetState(FS_COMPLETE, !proxies_.empty()); return; } const FileShareManifest::Item& item = manifest_->item(item_transferring_); if ((item.type != FileShareManifest::T_FILE) && (item.type != FileShareManifest::T_IMAGE) && (item.type != FileShareManifest::T_FOLDER)) { item_transferring_ += 1; NextDownload(); return; } const bool is_folder = (item.type == FileShareManifest::T_FOLDER); talk_base::Pathname temp_name; temp_name.SetFilename(item.name); if (!talk_base::CreateUniqueFile(temp_name, !is_folder)) { SetState(FS_FAILURE, false); return; } talk_base::StreamInterface* stream = NULL; if (is_folder) { // Convert unique filename into unique foldername temp_name.AppendFolder(temp_name.filename()); temp_name.SetFilename(""); talk_base::TarStream* tar = new talk_base::TarStream; // Note: the 'target' directory will be a subdirectory of the transfer_path_ talk_base::Pathname target; target.SetFolder(item.name); tar->AddFilter(target.pathname()); if (!tar->Open(temp_name.pathname(), false)) { delete tar; SetState(FS_FAILURE, false); return; } stream = tar; tar->SignalNextEntry.connect(this, &FileShareSession::OnNextEntry); } else { talk_base::FileStream* file = new talk_base::FileStream; if (!file->Open(temp_name.pathname().c_str(), "wb")) { delete file; talk_base::Filesystem::DeleteFile(temp_name); SetState(FS_FAILURE, false); return; } stream = file; } ASSERT(NULL != stream); transfer_path_ = temp_name.pathname(); std::string remote_path; GetItemNetworkPath(item_transferring_, false, &remote_path); StreamCounter* counter = new StreamCounter(stream); counter->SignalUpdateByteCount.connect(this, &FileShareSession::OnUpdateBytes); counter_ = counter; http_client_->reset(); http_client_->set_server(talk_base::SocketAddress(jid_.Str(), 0, false)); http_client_->request().verb = talk_base::HV_GET; http_client_->request().path = remote_path; http_client_->response().document.reset(counter); http_client_->start();}const FileShareSession::FileShareDescription* FileShareSession::description()const { ASSERT(NULL != session_); const cricket::SessionDescription* desc = session_->initiator() ? session_->description() : session_->remote_description(); return static_cast<const FileShareDescription*>(desc);}void FileShareSession::DoClose(bool terminate) { ASSERT(!is_closed_); ASSERT(IsComplete()); ASSERT(NULL != session_); is_closed_ = true; if (http_client_) { http_client_->reset(); } if (http_server_) { http_server_->CloseAll(true); // Currently, CloseAll doesn't result in OnHttpRequestComplete callback. // If we change that, the following resetting won't be necessary. transfer_connection_id_ = talk_base::HTTP_INVALID_CONNECTION_ID; transfer_name_.clear(); counter_ = NULL; } // 'reset' and 'CloseAll' cause counter_ to clear. ASSERT(NULL == counter_); if (remote_listener_) { // Cache the address for the remote_listener_, so that we can continue to // present a consistent URL for remote previews, which is necessary for IE // to continue using its cached copy. remote_listener_address_ = remote_listener_->GetLocalAddress(); remote_listener_->Close(); LOG(LS_INFO) << "Proxy listener closed @ " << remote_listener_address_.ToString(); } if (terminate) { session_->Terminate(); }}///////////////////////////////// FileShareSessionClient //////////////////////////////void FileShareSessionClient::OnSessionCreate(cricket::Session* session, bool received_initiate) { VERIFY(sessions_.insert(session).second); if (received_initiate) { FileShareSession* share = new FileShareSession(session, user_agent_); SignalFileShareSessionCreate(share); UNUSED(share); // FileShareSession registers itself with the UI }}void FileShareSessionClient::OnSessionDestroy(cricket::Session* session) { VERIFY(1 == sessions_.erase(session));}const cricket::SessionDescription* FileShareSessionClient::CreateSessionDescription( const buzz::XmlElement* element) { FileShareSession::FileShareDescription* share_desc = new FileShareSession::FileShareDescription; if (element->Name() != QN_SHARE_DESCRIPTION) return share_desc; const buzz::XmlElement* manifest = element->FirstNamed(QN_SHARE_MANIFEST); const buzz::XmlElement* protocol = element->FirstNamed(QN_SHARE_PROTOCOL); if (!manifest || !protocol) return share_desc; for (const buzz::XmlElement* item = manifest->FirstElement(); item != NULL; item = item->NextElement()) { bool is_folder; if (item->Name() == QN_SHARE_FOLDER) { is_folder = true; } else if (item->Name() == QN_SHARE_FILE) { is_folder = false; } else { continue; } std::string name; if (const buzz::XmlElement* el_name = item->FirstNamed(QN_SHARE_NAME)) { name = el_name->BodyText(); } if (name.empty()) { continue; } size_t size = FileShareManifest::SIZE_UNKNOWN; if (item->HasAttr(QN_SIZE)) { size = strtoul(item->Attr(QN_SIZE).c_str(), NULL, 10); } if (is_folder) { share_desc->manifest.AddFolder(name, size); } else { // Check if there is a valid image description for this file. if (const buzz::XmlElement* image = item->FirstNamed(QN_SHARE_IMAGE)) { if (image->HasAttr(QN_WIDTH) && image->HasAttr(QN_HEIGHT)) { size_t width = strtoul(image->Attr(QN_WIDTH).c_str(), NULL, 10); size_t height = strtoul(image->Attr(QN_HEIGHT).c_str(), NULL, 10); if (AllowedImageDimensions(width, height)) { share_desc->manifest.AddImage(name, size, width, height); continue; } } } share_desc->manifest.AddFile(name, size); } } if (const buzz::XmlElement* http = protocol->FirstNamed(QN_SHARE_HTTP)) { share_desc->supports_http = true; for (const buzz::XmlElement* url = http->FirstNamed(QN_SHARE_URL); url != NULL; url = url->NextNamed(QN_SHARE_URL)) { if (url->Attr(buzz::QN_NAME) == kHttpSourcePath) { share_desc->source_path = url->BodyText(); } else if (url->Attr(buzz::QN_NAME) == kHttpPreviewPath) { share_desc->preview_path = url->BodyText(); } } } return share_desc;}buzz::XmlElement* FileShareSessionClient::TranslateSessionDescription( const cricket::SessionDescription* description) { const FileShareSession::FileShareDescription* share_desc = static_cast<const FileShareSession::FileShareDescription*>(description); scoped_ptr<buzz::XmlElement> el(new buzz::XmlElement(QN_SHARE_DESCRIPTION, true)); const FileShareManifest& manifest = share_desc->manifest; el->AddElement(new buzz::XmlElement(QN_SHARE_MANIFEST)); for (size_t i=0; i<manifest.size(); ++i) { const FileShareManifest::Item& item = manifest.item(i); buzz::QName qname; if (item.type == FileShareManifest::T_FOLDER) { qname = QN_SHARE_FOLDER; } else if ((item.type == FileShareManifest::T_FILE) || (item.type == FileShareManifest::T_IMAGE)) { qname = QN_SHARE_FILE; } else { ASSERT(false); continue; } el->AddElement(new buzz::XmlElement(qname), 1); if (item.size != FileShareManifest::SIZE_UNKNOWN) { char buffer[256]; talk_base::sprintfn(buffer, sizeof(buffer), "%lu", item.size); el->AddAttr(QN_SIZE, buffer, 2); } buzz::XmlElement* el_name = new buzz::XmlElement(QN_SHARE_NAME); el_name->SetBodyText(item.name); el->AddElement(el_name, 2); if ((item.type == FileShareManifest::T_IMAGE) && AllowedImageDimensions(item.width, item.height)) { el->AddElement(new buzz::XmlElement(QN_SHARE_IMAGE), 2); char buffer[256]; talk_base::sprintfn(buffer, sizeof(buffer), "%lu", item.width); el->AddAttr(QN_WIDTH, buffer, 3); talk_base::sprintfn(buffer, sizeof(buffer), "%lu", item.height); el->AddAttr(QN_HEIGHT, buffer, 3); } } el->AddElement(new buzz::XmlElement(QN_SHARE_PROTOCOL)); if (share_desc->supports_http) { el->AddElement(new buzz::XmlElement(QN_SHARE_HTTP), 1); if (!share_desc->source_path.empty()) { buzz::XmlElement* url = new buzz::XmlElement(QN_SHARE_URL); url->SetAttr(buzz::QN_NAME, kHttpSourcePath); url->SetBodyText(share_desc->source_path); el->AddElement(url, 2); } if (!share_desc->preview_path.empty()) { buzz::XmlElement* url = new buzz::XmlElement(QN_SHARE_URL); url->SetAttr(buzz::QN_NAME, kHttpPreviewPath); url->SetBodyText(share_desc->preview_path); el->AddElement(url, 2); } } return el.release();}FileShareSession *FileShareSessionClient::CreateFileShareSession() { cricket::Session* session = sm_->CreateSession(jid_.Str(), NS_GOOGLE_SHARE); FileShareSession* share = new FileShareSession(session, user_agent_); SignalFileShareSessionCreate(share); return share;}} // namespace cricket
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -