diff options
author | Tobias Markmann <tm@ayena.de> | 2016-02-10 19:25:21 (GMT) |
---|---|---|
committer | Kevin Smith <kevin.smith@isode.com> | 2016-02-15 12:31:10 (GMT) |
commit | ca226e7bb019308db4bfc818d7e04422d9d28106 (patch) | |
tree | 9f7ef065f20db3e04eb08560674eed16d10e95e5 /Swiften/FileTransfer | |
parent | 431eb62386101dc8fc1e7d346c49bd0d81fda70e (diff) | |
download | swift-ca226e7bb019308db4bfc818d7e04422d9d28106.zip swift-ca226e7bb019308db4bfc818d7e04422d9d28106.tar.bz2 |
Fix crash when saving a received file to non-writable location
WriteBytestream::write(…) now returns a boolean indicating
its success state (false in case of an error). Adjusted
FileWriteBytestream accordingly.
The QtWebKitChatView will test if the file path selected by
the user is writable before accepting it and starting the
transfer. If it is not writable a red warning message will be
added to the file-transfer element in the chat view.
Test-Information:
Added an integration test that tests the new behavior for
the FileWriteBytestream class.
Tested two file transfers on OS X 10.11.3, one to a write
protected location and another to /tmp. The first is not accepted
by the UI, and without the UI sanity check it results in a
file-transfer error. The second succeeds as expected.
Change-Id: I5aa0c617423073feb371365a23a294c149c88036
Diffstat (limited to 'Swiften/FileTransfer')
-rw-r--r-- | Swiften/FileTransfer/ByteArrayWriteBytestream.h | 5 | ||||
-rw-r--r-- | Swiften/FileTransfer/FileTransferError.h | 3 | ||||
-rw-r--r-- | Swiften/FileTransfer/FileWriteBytestream.cpp | 17 | ||||
-rw-r--r-- | Swiften/FileTransfer/FileWriteBytestream.h | 2 | ||||
-rw-r--r-- | Swiften/FileTransfer/SOCKS5BytestreamClientSession.cpp | 3 | ||||
-rw-r--r-- | Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp | 40 | ||||
-rw-r--r-- | Swiften/FileTransfer/SOCKS5BytestreamServerSession.h | 9 | ||||
-rw-r--r-- | Swiften/FileTransfer/WriteBytestream.h | 12 |
8 files changed, 49 insertions, 42 deletions
diff --git a/Swiften/FileTransfer/ByteArrayWriteBytestream.h b/Swiften/FileTransfer/ByteArrayWriteBytestream.h index 938b1d4..08c4d4b 100644 --- a/Swiften/FileTransfer/ByteArrayWriteBytestream.h +++ b/Swiften/FileTransfer/ByteArrayWriteBytestream.h @@ -1,30 +1,31 @@ /* - * Copyright (c) 2010-2015 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #pragma once #include <Swiften/Base/API.h> #include <Swiften/FileTransfer/WriteBytestream.h> namespace Swift { class SWIFTEN_API ByteArrayWriteBytestream : public WriteBytestream { public: ByteArrayWriteBytestream() { } - virtual void write(const std::vector<unsigned char>& bytes) { + virtual bool write(const std::vector<unsigned char>& bytes) { data.insert(data.end(), bytes.begin(), bytes.end()); onWrite(bytes); + return true; } const std::vector<unsigned char>& getData() const { return data; } private: std::vector<unsigned char> data; }; } diff --git a/Swiften/FileTransfer/FileTransferError.h b/Swiften/FileTransfer/FileTransferError.h index 67e32f2..eff8ca9 100644 --- a/Swiften/FileTransfer/FileTransferError.h +++ b/Swiften/FileTransfer/FileTransferError.h @@ -1,30 +1,31 @@ /* - * Copyright (c) 2010-2015 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #pragma once #include <Swiften/Base/API.h> namespace Swift { class SWIFTEN_API FileTransferError { public: enum Type { UnknownError, PeerError, ReadError, + WriteError, ClosedError }; FileTransferError(Type type = UnknownError) : type(type) {} Type getType() const { return type; } private: Type type; }; } diff --git a/Swiften/FileTransfer/FileWriteBytestream.cpp b/Swiften/FileTransfer/FileWriteBytestream.cpp index bbf3d51..c39d63a 100644 --- a/Swiften/FileTransfer/FileWriteBytestream.cpp +++ b/Swiften/FileTransfer/FileWriteBytestream.cpp @@ -1,47 +1,52 @@ /* - * Copyright (c) 2010-2015 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include <Swiften/FileTransfer/FileWriteBytestream.h> #include <cassert> #include <boost/filesystem/fstream.hpp> #include <boost/numeric/conversion/cast.hpp> namespace Swift { FileWriteBytestream::FileWriteBytestream(const boost::filesystem::path& file) : file(file), stream(NULL) { } FileWriteBytestream::~FileWriteBytestream() { if (stream) { stream->close(); delete stream; stream = NULL; } } -void FileWriteBytestream::write(const std::vector<unsigned char>& data) { +bool FileWriteBytestream::write(const std::vector<unsigned char>& data) { if (data.empty()) { - return; + return true; } if (!stream) { stream = new boost::filesystem::ofstream(file, std::ios_base::out|std::ios_base::binary); } - assert(stream->good()); - stream->write(reinterpret_cast<const char*>(&data[0]), boost::numeric_cast<std::streamsize>(data.size())); - onWrite(data); + if (stream->good()) { + stream->write(reinterpret_cast<const char*>(&data[0]), boost::numeric_cast<std::streamsize>(data.size())); + if (stream->good()) { + onWrite(data); + return true; + } + } + return false; } void FileWriteBytestream::close() { if (stream) { stream->close(); delete stream; stream = NULL; } } } diff --git a/Swiften/FileTransfer/FileWriteBytestream.h b/Swiften/FileTransfer/FileWriteBytestream.h index b2d3347..02e1b46 100644 --- a/Swiften/FileTransfer/FileWriteBytestream.h +++ b/Swiften/FileTransfer/FileWriteBytestream.h @@ -1,28 +1,28 @@ /* * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #pragma once #include <boost/filesystem/fstream.hpp> #include <boost/filesystem/path.hpp> #include <Swiften/Base/API.h> #include <Swiften/FileTransfer/WriteBytestream.h> namespace Swift { class SWIFTEN_API FileWriteBytestream : public WriteBytestream { public: FileWriteBytestream(const boost::filesystem::path& file); virtual ~FileWriteBytestream(); - virtual void write(const std::vector<unsigned char>&); + virtual bool write(const std::vector<unsigned char>&); void close(); private: boost::filesystem::path file; boost::filesystem::ofstream* stream; }; } diff --git a/Swiften/FileTransfer/SOCKS5BytestreamClientSession.cpp b/Swiften/FileTransfer/SOCKS5BytestreamClientSession.cpp index a38501b..4fc0246 100644 --- a/Swiften/FileTransfer/SOCKS5BytestreamClientSession.cpp +++ b/Swiften/FileTransfer/SOCKS5BytestreamClientSession.cpp @@ -1,38 +1,38 @@ /* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ /* - * Copyright (c) 2013-2015 Isode Limited. + * Copyright (c) 2013-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include "SOCKS5BytestreamClientSession.h" #include <boost/bind.hpp> #include <boost/numeric/conversion/cast.hpp> #include <Swiften/Base/Algorithm.h> #include <Swiften/Base/ByteArray.h> #include <Swiften/Base/Concat.h> #include <Swiften/Base/Log.h> #include <Swiften/Base/SafeByteArray.h> #include <Swiften/FileTransfer/BytestreamException.h> #include <Swiften/Network/TimerFactory.h> #include <Swiften/StringCodecs/Hexify.h> namespace Swift { SOCKS5BytestreamClientSession::SOCKS5BytestreamClientSession( boost::shared_ptr<Connection> connection, const HostAddressPort& addressPort, const std::string& destination, TimerFactory* timerFactory) : connection(connection), addressPort(addressPort), destination(destination), state(Initial), chunkSize(131072) { @@ -139,61 +139,60 @@ void SOCKS5BytestreamClientSession::process() { SWIFT_LOG(debug) << "Data: " << Hexify::hexify(unprocessedData) << std::endl; unprocessedData.clear(); //assert(false); } } void SOCKS5BytestreamClientSession::hello() { // Version 5, 1 auth method, No authentication const SafeByteArray hello = createSafeByteArray("\x05\x01\x00", 3); connection->write(hello); state = Hello; } void SOCKS5BytestreamClientSession::authenticate() { SWIFT_LOG(debug) << std::endl; SafeByteArray header = createSafeByteArray("\x05\x01\x00\x03", 4); SafeByteArray message = header; append(message, createSafeByteArray(boost::numeric_cast<char>(destination.size()))); authenticateAddress = createByteArray(destination); append(message, authenticateAddress); append(message, createSafeByteArray("\x00\x00", 2)); // 2 byte for port connection->write(message); state = Authenticating; } void SOCKS5BytestreamClientSession::startReceiving(boost::shared_ptr<WriteBytestream> writeStream) { if (state == Ready) { state = Reading; writeBytestream = writeStream; writeBytestream->write(unprocessedData); - //onBytesReceived(unprocessedData.size()); unprocessedData.clear(); } else { SWIFT_LOG(debug) << "Session isn't ready for transfer yet!" << std::endl; } } void SOCKS5BytestreamClientSession::startSending(boost::shared_ptr<ReadBytestream> readStream) { if (state == Ready) { state = Writing; readBytestream = readStream; dataWrittenConnection = connection->onDataWritten.connect( boost::bind(&SOCKS5BytestreamClientSession::sendData, this)); sendData(); } else { SWIFT_LOG(debug) << "Session isn't ready for transfer yet!" << std::endl; } } HostAddressPort SOCKS5BytestreamClientSession::getAddressPort() const { return addressPort; } void SOCKS5BytestreamClientSession::sendData() { if (!readBytestream->isFinished()) { try { boost::shared_ptr<ByteArray> dataToSend = readBytestream->read(boost::numeric_cast<size_t>(chunkSize)); connection->write(createSafeByteArray(*dataToSend)); onBytesSent(dataToSend->size()); } catch (const BytestreamException&) { diff --git a/Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp b/Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp index 7838dd1..0e1eb6b 100644 --- a/Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp +++ b/Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp @@ -1,199 +1,195 @@ /* - * Copyright (c) 2010-2013 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include <Swiften/FileTransfer/SOCKS5BytestreamServerSession.h> #include <boost/bind.hpp> #include <boost/numeric/conversion/cast.hpp> -#include <iostream> -#include <Swiften/Base/ByteArray.h> -#include <Swiften/Base/SafeByteArray.h> #include <Swiften/Base/Algorithm.h> +#include <Swiften/Base/ByteArray.h> #include <Swiften/Base/Concat.h> #include <Swiften/Base/Log.h> -#include <Swiften/Network/HostAddressPort.h> -#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h> +#include <Swiften/Base/SafeByteArray.h> #include <Swiften/FileTransfer/BytestreamException.h> +#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h> +#include <Swiften/Network/HostAddressPort.h> namespace Swift { SOCKS5BytestreamServerSession::SOCKS5BytestreamServerSession( boost::shared_ptr<Connection> connection, SOCKS5BytestreamRegistry* bytestreams) : connection(connection), bytestreams(bytestreams), state(Initial), chunkSize(131072), waitingForData(false) { disconnectedConnection = connection->onDisconnected.connect(boost::bind(&SOCKS5BytestreamServerSession::handleDisconnected, this, _1)); } SOCKS5BytestreamServerSession::~SOCKS5BytestreamServerSession() { if (state != Finished && state != Initial) { - std::cerr << "Warning: SOCKS5BytestreamServerSession unfinished" << std::endl; - finish(false); + SWIFT_LOG(warning) << "SOCKS5BytestreamServerSession unfinished" << std::endl; + finish(); } } void SOCKS5BytestreamServerSession::start() { SWIFT_LOG(debug) << std::endl; dataReadConnection = connection->onDataRead.connect( boost::bind(&SOCKS5BytestreamServerSession::handleDataRead, this, _1)); state = WaitingForAuthentication; } void SOCKS5BytestreamServerSession::stop() { - finish(false); + finish(); } void SOCKS5BytestreamServerSession::startSending(boost::shared_ptr<ReadBytestream> stream) { if (state != ReadyForTransfer) { SWIFT_LOG(debug) << "Not ready for transfer!" << std::endl; return; } readBytestream = stream; state = WritingData; dataAvailableConnection = readBytestream->onDataAvailable.connect( boost::bind(&SOCKS5BytestreamServerSession::handleDataAvailable, this)); dataWrittenConnection = connection->onDataWritten.connect( boost::bind(&SOCKS5BytestreamServerSession::sendData, this)); sendData(); } void SOCKS5BytestreamServerSession::startReceiving(boost::shared_ptr<WriteBytestream> stream) { if (state != ReadyForTransfer) { SWIFT_LOG(debug) << "Not ready for transfer!" << std::endl; return; } writeBytestream = stream; state = ReadingData; writeBytestream->write(unprocessedData); // onBytesReceived(unprocessedData.size()); unprocessedData.clear(); } HostAddressPort SOCKS5BytestreamServerSession::getAddressPort() const { return connection->getLocalAddress(); } void SOCKS5BytestreamServerSession::handleDataRead(boost::shared_ptr<SafeByteArray> data) { if (state != ReadingData) { append(unprocessedData, *data); process(); } else { - writeBytestream->write(createByteArray(vecptr(*data), data->size())); - // onBytesReceived(data->size()); + if (!writeBytestream->write(createByteArray(vecptr(*data), data->size()))) { + finish(boost::optional<FileTransferError>(FileTransferError::WriteError)); + } } } void SOCKS5BytestreamServerSession::handleDataAvailable() { if (waitingForData) { sendData(); } } void SOCKS5BytestreamServerSession::handleDisconnected(const boost::optional<Connection::Error>& error) { SWIFT_LOG(debug) << (error ? (error == Connection::ReadError ? "Read Error" : "Write Error") : "No Error") << std::endl; - finish(error ? true : false); + finish(error ? boost::optional<FileTransferError>(FileTransferError::PeerError) : boost::optional<FileTransferError>()); } void SOCKS5BytestreamServerSession::process() { if (state == WaitingForAuthentication) { if (unprocessedData.size() >= 2) { size_t authCount = unprocessedData[1]; size_t i = 2; while (i < 2 + authCount && i < unprocessedData.size()) { // Skip authentication mechanism ++i; } if (i == 2 + authCount) { // Authentication message is complete if (i != unprocessedData.size()) { SWIFT_LOG(debug) << "Junk after authentication mechanism" << std::endl; } unprocessedData.clear(); connection->write(createSafeByteArray("\x05\x00", 2)); state = WaitingForRequest; } } } else if (state == WaitingForRequest) { if (unprocessedData.size() >= 5) { ByteArray requestID; size_t i = 5; size_t hostnameSize = unprocessedData[4]; while (i < 5 + hostnameSize && i < unprocessedData.size()) { requestID.push_back(unprocessedData[i]); ++i; } // Skip the port: 2 byte large, one already skipped. Add one for comparison with size i += 2; if (i <= unprocessedData.size()) { if (i != unprocessedData.size()) { SWIFT_LOG(debug) << "Junk after authentication mechanism" << std::endl; } unprocessedData.clear(); streamID = byteArrayToString(requestID); bool hasBytestream = bytestreams->hasBytestream(streamID); SafeByteArray result = createSafeByteArray("\x05", 1); result.push_back(hasBytestream ? 0x0 : 0x4); append(result, createByteArray("\x00\x03", 2)); result.push_back(boost::numeric_cast<unsigned char>(requestID.size())); append(result, concat(requestID, createByteArray("\x00\x00", 2))); if (!hasBytestream) { SWIFT_LOG(debug) << "Readstream or Wrtiestream with ID " << streamID << " not found!" << std::endl; connection->write(result); - finish(true); + finish(boost::optional<FileTransferError>(FileTransferError::PeerError)); } else { SWIFT_LOG(debug) << "Found stream. Sent OK." << std::endl; connection->write(result); state = ReadyForTransfer; } } } } } void SOCKS5BytestreamServerSession::sendData() { if (!readBytestream->isFinished()) { try { SafeByteArray dataToSend = createSafeByteArray(*readBytestream->read(boost::numeric_cast<size_t>(chunkSize))); if (!dataToSend.empty()) { connection->write(dataToSend); onBytesSent(dataToSend.size()); waitingForData = false; } else { waitingForData = true; } } catch (const BytestreamException&) { - finish(true); + finish(boost::optional<FileTransferError>(FileTransferError::PeerError)); } } else { - finish(false); + finish(); } } -void SOCKS5BytestreamServerSession::finish(bool error) { - SWIFT_LOG(debug) << error << " " << state << std::endl; +void SOCKS5BytestreamServerSession::finish(const boost::optional<FileTransferError>& error) { + SWIFT_LOG(debug) << "state: " << state << std::endl; if (state == Finished) { return; } disconnectedConnection.disconnect(); dataReadConnection.disconnect(); dataWrittenConnection.disconnect(); dataAvailableConnection.disconnect(); readBytestream.reset(); state = Finished; - if (error) { - onFinished(boost::optional<FileTransferError>(FileTransferError::PeerError)); - } else { - onFinished(boost::optional<FileTransferError>()); - } + onFinished(error); } } diff --git a/Swiften/FileTransfer/SOCKS5BytestreamServerSession.h b/Swiften/FileTransfer/SOCKS5BytestreamServerSession.h index d8eea34..ed5272f 100644 --- a/Swiften/FileTransfer/SOCKS5BytestreamServerSession.h +++ b/Swiften/FileTransfer/SOCKS5BytestreamServerSession.h @@ -1,84 +1,83 @@ /* - * Copyright (c) 2010 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #pragma once #include <boost/shared_ptr.hpp> #include <Swiften/Base/API.h> #include <Swiften/Base/boost_bsignals.h> -#include <Swiften/Network/Connection.h> +#include <Swiften/FileTransfer/FileTransferError.h> #include <Swiften/FileTransfer/ReadBytestream.h> #include <Swiften/FileTransfer/WriteBytestream.h> -#include <Swiften/FileTransfer/FileTransferError.h> +#include <Swiften/Network/Connection.h> namespace Swift { class SOCKS5BytestreamRegistry; class SWIFTEN_API SOCKS5BytestreamServerSession { public: typedef boost::shared_ptr<SOCKS5BytestreamServerSession> ref; public: enum State { Initial, WaitingForAuthentication, WaitingForRequest, ReadyForTransfer, ReadingData, WritingData, Finished }; SOCKS5BytestreamServerSession(boost::shared_ptr<Connection> connection, SOCKS5BytestreamRegistry* registry); ~SOCKS5BytestreamServerSession(); void setChunkSize(int chunkSize) { this->chunkSize = chunkSize; } void start(); void stop(); void startSending(boost::shared_ptr<ReadBytestream>); void startReceiving(boost::shared_ptr<WriteBytestream>); HostAddressPort getAddressPort() const; boost::signal<void (boost::optional<FileTransferError>)> onFinished; boost::signal<void (unsigned long long)> onBytesSent; - // boost::signal<void (unsigned long long)> onBytesReceived; const std::string& getStreamID() const { return streamID; } private: - void finish(bool error); + void finish(const boost::optional<FileTransferError>& error = boost::optional<FileTransferError>()); void process(); void handleDataRead(boost::shared_ptr<SafeByteArray>); void handleDisconnected(const boost::optional<Connection::Error>&); void handleDataAvailable(); void sendData(); private: boost::shared_ptr<Connection> connection; SOCKS5BytestreamRegistry* bytestreams; ByteArray unprocessedData; State state; int chunkSize; std::string streamID; boost::shared_ptr<ReadBytestream> readBytestream; boost::shared_ptr<WriteBytestream> writeBytestream; bool waitingForData; boost::bsignals::connection disconnectedConnection; boost::bsignals::connection dataReadConnection; boost::bsignals::connection dataWrittenConnection; boost::bsignals::connection dataAvailableConnection; }; } diff --git a/Swiften/FileTransfer/WriteBytestream.h b/Swiften/FileTransfer/WriteBytestream.h index 3299620..76237c4 100644 --- a/Swiften/FileTransfer/WriteBytestream.h +++ b/Swiften/FileTransfer/WriteBytestream.h @@ -1,26 +1,32 @@ /* - * Copyright (c) 2010 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #pragma once -#include <boost/shared_ptr.hpp> #include <vector> +#include <boost/shared_ptr.hpp> + #include <Swiften/Base/API.h> #include <Swiften/Base/boost_bsignals.h> namespace Swift { class SWIFTEN_API WriteBytestream { public: typedef boost::shared_ptr<WriteBytestream> ref; virtual ~WriteBytestream(); - virtual void write(const std::vector<unsigned char>&) = 0; + /** + * Write data from vector argument to bytestream. + * + * On success true is returned and \ref onWrite is called. On failure false is returned. + */ + virtual bool write(const std::vector<unsigned char>&) = 0; boost::signal<void (const std::vector<unsigned char>&)> onWrite; }; } |