summaryrefslogtreecommitdiffstats
blob: 258412b579b1523e24d999176be90b33f76407bd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/*
 * Copyright (c) 2010-2018 Isode Limited.
 * All rights reserved.
 * See the COPYING file for more information.
 */

#include <Swiften/FileTransfer/IBBSendSession.h>

#include <boost/bind.hpp>
#include <boost/numeric/conversion/cast.hpp>

#include <Swiften/Base/ByteArray.h>
#include <Swiften/FileTransfer/BytestreamException.h>
#include <Swiften/FileTransfer/IBBRequest.h>
#include <Swiften/Queries/IQRouter.h>

namespace Swift {

IBBSendSession::IBBSendSession(
        const std::string& id,
        const JID& from,
        const JID& to,
        std::shared_ptr<ReadBytestream> bytestream,
        IQRouter* router) :
            id(id),
            from(from),
            to(to),
            bytestream(bytestream),
            router(router),
            blockSize(4096),
            sequenceNumber(0),
            active(false),
            waitingForData(false) {
    bytestream->onDataAvailable.connect(boost::bind(&IBBSendSession::handleDataAvailable, this));
}

IBBSendSession::~IBBSendSession() {
    bytestream->onDataAvailable.disconnect(boost::bind(&IBBSendSession::handleDataAvailable, this));
}

void IBBSendSession::start() {
    IBBRequest::ref request = IBBRequest::create(
            from, to, IBB::createIBBOpen(id, blockSize), router);
    request->onResponse.connect(boost::bind(&IBBSendSession::handleIBBResponse, this, _1, _2));
    active = true;
    request->send();
    currentRequest = request;
}

void IBBSendSession::stop() {
    if (active && router->isAvailable()) {
        IBBRequest::create(from, to, IBB::createIBBClose(id), router)->send();
    }
    if (currentRequest) {
        currentRequest->onResponse.disconnect(boost::bind(&IBBSendSession::handleIBBResponse, this, _1, _2));
    }
    finish(boost::optional<FileTransferError>());
}

void IBBSendSession::handleIBBResponse(IBB::ref, ErrorPayload::ref error) {
    currentRequest.reset();

    if (!error && active) {
        if (!bytestream->isFinished()) {
            sendMoreData();
        }
        else {
            finish(boost::optional<FileTransferError>());
        }
    }
    else {
        finish(FileTransferError(FileTransferError::PeerError));
    }
}

void IBBSendSession::sendMoreData() {
    try {
        std::shared_ptr<ByteArray> data = bytestream->read(blockSize);
        if (!data->empty()) {
            waitingForData = false;
            IBBRequest::ref request = IBBRequest::create(from, to, IBB::createIBBData(id, sequenceNumber, *data), router);
            sequenceNumber++;
            request->onResponse.connect(boost::bind(&IBBSendSession::handleIBBResponse, this, _1, _2));
            request->send();
            currentRequest = request;
            onBytesSent(data->size());
        }
        else {
            waitingForData = true;
        }
    }
    catch (const BytestreamException&) {
        finish(FileTransferError(FileTransferError::ReadError));
    }
}

void IBBSendSession::finish(boost::optional<FileTransferError> error) {
    active = false;
    onFinished(error);
}

void IBBSendSession::handleDataAvailable() {
    if (waitingForData) {
        sendMoreData();
    }
}

}