<commit_before>// Copyright 2017 MongoDB Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <algorithm>
#include <cstdint>
#include <cstdlib>
#include <vector>

#include <bsoncxx/builder/basic/document.hpp>
#include <bsoncxx/builder/basic/kvp.hpp>
#include <bsoncxx/document/value.hpp>
#include <bsoncxx/oid.hpp>
#include <bsoncxx/test_util/catch.hh>
#include <bsoncxx/types.hpp>
#include <bsoncxx/types/value.hpp>
#include <mongocxx/client.hpp>
#include <mongocxx/database.hpp>
#include <mongocxx/exception/logic_error.hpp>
#include <mongocxx/gridfs/bucket.hpp>
#include <mongocxx/instance.hpp>
#include <mongocxx/options/gridfs/upload.hpp>
#include <mongocxx/stdx.hpp>
#include <mongocxx/uri.hpp>

using namespace mongocxx;

using bsoncxx::builder::basic::kvp;
using bsoncxx::builder::basic::make_document;

TEST_CASE("mongocxx::gridfs::bucket default constructor makes invalid bucket", "[gridfs::bucket]") {
    instance::current();

    gridfs::bucket bucket;
    REQUIRE(!bucket);
    REQUIRE_THROWS_AS(bucket.bucket_name(), logic_error);
}

TEST_CASE("mongocxx::gridfs::bucket copy constructor", "[gridfs::bucket]") {
    instance::current();

    client client{uri{}};
    database db = client["gridfs_bucket_copy_constructor"];

    SECTION("constructing from valid") {
        gridfs::bucket bucket_a = db.gridfs_bucket(options::gridfs::bucket{}.bucket_name("a"));
        gridfs::bucket bucket_b{bucket_a};
        REQUIRE(bucket_b);
        REQUIRE(bucket_b.bucket_name() == stdx::string_view{"a"});
    }

    SECTION("constructing from invalid") {
        gridfs::bucket bucket_a;
        gridfs::bucket bucket_b{bucket_a};
        REQUIRE(!bucket_b);
    }
}

TEST_CASE("mongocxx::gridfs::bucket copy assignment operator", "[gridfs::bucket]") {
    instance::current();

    client client{uri{}};
    database db = client["gridfs_bucket_copy_assignment"];

    SECTION("assigning valid to valid") {
        gridfs::bucket bucket_a = db.gridfs_bucket(options::gridfs::bucket{}.bucket_name("a"));
        gridfs::bucket bucket_b = db.gridfs_bucket(options::gridfs::bucket{}.bucket_name("b"));
        bucket_b = bucket_a;
        REQUIRE(bucket_b);
        REQUIRE(bucket_b.bucket_name() == stdx::string_view{"a"});
    }

    SECTION("assigning invalid to valid") {
        gridfs::bucket bucket_a;
        gridfs::bucket bucket_b = db.gridfs_bucket(options::gridfs::bucket{}.bucket_name("b"));
        bucket_b = bucket_a;
        REQUIRE(!bucket_b);
    }

    SECTION("assigning valid to invalid") {
        gridfs::bucket bucket_a = db.gridfs_bucket(options::gridfs::bucket{}.bucket_name("a"));
        gridfs::bucket bucket_b;
        bucket_b = bucket_a;
        REQUIRE(bucket_b);
        REQUIRE(bucket_b.bucket_name() == stdx::string_view{"a"});
    }

    SECTION("assigning invalid to invalid") {
        gridfs::bucket bucket_a;
        gridfs::bucket bucket_b;
        bucket_b = bucket_a;
        REQUIRE(!bucket_b);
    }
}

// Add an arbitrary GridFS file with a specified length, chunk size, and id to a database's default
// GridFS bucket (i.e. the "fs.files" and "fs.chunks" collections).
//
// Returns a vector of the bytes stored in the GridFS chunks.
std::vector<std::uint8_t> manual_gridfs_initialize(database db,
                                                   std::int64_t length,
                                                   std::int32_t chunk_size,
                                                   bsoncxx::types::value id) {
    std::vector<std::uint8_t> bytes;

    // Populate the vector with arbitrary values.
    for (std::int64_t i = 0; i < length; ++i) {
        bytes.push_back((i + 200) % 256);
    }

    std::vector<bsoncxx::document::value> chunks;
    std::int64_t bytes_written = 0;

    for (std::int32_t i = 0; bytes_written < length; ++i) {
        // The last chunk should be truncated to fit specified file length.
        std::int32_t current_chunk_size = static_cast<std::int32_t>(
            std::min(static_cast<std::int64_t>(chunk_size), length - bytes_written));

        bsoncxx::types::b_binary data = {bsoncxx::binary_sub_type::k_binary,
                                         static_cast<std::uint32_t>(current_chunk_size),
                                         bytes.data() + bytes_written};

        chunks.push_back(make_document(kvp("files_id", id), kvp("n", i), kvp("data", data)));
        bytes_written += current_chunk_size;
    }

    db["fs.chunks"].insert_many(chunks);
    db["fs.files"].insert_one(make_document(kvp("_id", id),
                                            kvp("length", length),
                                            kvp("chunkSize", bsoncxx::types::b_int32{chunk_size})));

    return bytes;
}

TEST_CASE("mongocxx::gridfs::downloader::read with arbitrary sizes", "[gridfs::downloader]") {
    instance::current();

    client client{uri{}};
    database db = client["gridfs_download_read_test"];
    gridfs::bucket bucket = db.gridfs_bucket();

    db["fs.files"].delete_many({});
    db["fs.chunks"].delete_many({});

    std::int64_t file_length = 100;
    std::int32_t chunk_size = 9;
    std::int32_t read_size;

    SECTION("read_size = 1") {
        read_size = 1;
    }

    SECTION("read_size = chunk_size - 1") {
        read_size = chunk_size - 1;
    }

    SECTION("read_size = chunk_size") {
        read_size = chunk_size;
    }

    SECTION("read_size = chunk_size + 1") {
        read_size = chunk_size + 1;
    }

    SECTION("read_size = 2 * chunk_size") {
        read_size = 2 * chunk_size;
    }

    SECTION("read_size = file_length") {
        read_size = file_length;
    }

    SECTION("read_size > file_length") {
        read_size = file_length + 1;
    }

    bsoncxx::types::b_oid id = {bsoncxx::oid{}};
    std::vector<std::uint8_t> expected =
        manual_gridfs_initialize(db, file_length, chunk_size, bsoncxx::types::value{id});

    // Allocate a buffer large enough to fit the data read from the downloader.
    std::vector<std::uint8_t> buffer;
    buffer.reserve(read_size);

    std::size_t total_bytes_read = 0;
    auto downloader = bucket.open_download_stream(bsoncxx::types::value{id});

    while (std::size_t bytes_read = downloader.read(read_size, buffer.data())) {
        std::vector<std::uint8_t> expected_bytes{expected.data() + total_bytes_read,
                                                 expected.data() + total_bytes_read + bytes_read};
        std::vector<std::uint8_t> actual_bytes{buffer.data(), buffer.data() + bytes_read};

        REQUIRE(expected_bytes == actual_bytes);

        total_bytes_read += bytes_read;
    }

    REQUIRE(total_bytes_read == static_cast<std::size_t>(file_length));
}

TEST_CASE("mongocxx::gridfs::uploader::abort works", "[gridfs::uploader]") {
    instance::current();

    client client{uri{}};
    database db = client["gridfs_upload_abort_test"];
    gridfs::bucket bucket = db.gridfs_bucket();

    bsoncxx::types::b_oid id = {bsoncxx::oid{}};
    gridfs::uploader uploader =
        bucket.open_upload_stream_with_id(bsoncxx::types::value{id}, "file");

    // The intended purpose of gridfs::uploader::abort is to clean up any chunks that are already
    // uploaded and to prevent any more data from being written to the uploader. The most
    // straightforward way to test this would be to write enough data to the uploader to ensure that
    // one or more chunks is written and then to call abort. However, in the interest of efficiency,
    // the uploader will only send the chunks to the server either when the amount of data contained
    // in the unset chunks reaches the limit that can fit in a single document or when the user
    // indicates that the file is fully written by calling gridfs::uploader::close. Since it takes a
    // long time to write enough data for the uploader to flush the chunks to the server and calling
    // `close` prevents calling abort afterwards, the easiest way to test abort is to manually
    // insert a chunk with the matching `files_id`, calling abort, and then ensuring that the chunk
    // has been deleted.
    bsoncxx::builder::basic::document document{};
    document.append(bsoncxx::builder::basic::kvp("files_id", id));
    db["fs.chunks"].insert_one(document.extract());

    uploader.abort();
    REQUIRE_THROWS(uploader.close());

    REQUIRE(!db["fs.files"].find_one({}));
    REQUIRE(!db["fs.chunks"].find_one({}));
}

TEST_CASE("mongocxx::gridfs::uploader::write with arbitrary sizes", "[gridfs::uploader]") {
    instance::current();

    client client{uri{}};
    database db = client["gridfs_upload_write_test"];
    gridfs::bucket bucket = db.gridfs_bucket();

    auto files_coll = db["fs.files"];
    auto chunks_coll = db["fs.chunks"];

    files_coll.delete_many({});
    chunks_coll.delete_many({});

    std::int64_t file_length = 100;
    std::int32_t chunk_size = 9;
    std::int32_t write_size;

    SECTION("write_size = 1") {
        write_size = 1;
    }

    SECTION("write_size = chunk_size - 1") {
        write_size = chunk_size - 1;
    }

    SECTION("write_size = chunk_size") {
        write_size = chunk_size;
    }

    SECTION("write_size = chunk_size + 1") {
        write_size = chunk_size + 1;
    }

    SECTION("write_size = 2 * chunk_size") {
        write_size = 2 * chunk_size;
    }

    SECTION("write_size = file_length") {
        write_size = file_length;
    }

    std::vector<std::uint8_t> bytes;

    // Populate the vector with arbitrary values.
    for (std::size_t i = 0; i < static_cast<std::size_t>(file_length); ++i) {
        bytes.push_back((i + 200) % 256);
    }

    std::size_t bytes_written = 0;
    auto upload_options = options::gridfs::upload{}.chunk_size_bytes(chunk_size);
    auto uploader = bucket.open_upload_stream("test_file", upload_options);

    while (bytes_written < static_cast<std::size_t>(file_length)) {
        std::size_t actual_write_size = static_cast<std::size_t>(
            std::min(static_cast<std::int64_t>(write_size),
                     file_length - static_cast<std::int64_t>(bytes_written)));

        uploader.write(actual_write_size, bytes.data() + bytes_written);
        bytes_written += actual_write_size;
    }

    auto result = uploader.close();
    auto file_doc = files_coll.find_one(make_document(kvp("_id", result.id())));
    REQUIRE(file_doc);

    REQUIRE(file_doc->view()["length"].get_int64().value == bytes_written);
    REQUIRE(file_doc->view()["chunkSize"].get_int32().value == chunk_size);

    auto chunks = chunks_coll.find(make_document(kvp("files_id", result.id())));

    std::int32_t chunk_count = 0;
    std::size_t bytes_read = 0;

    for (auto chunk_doc : chunks) {
        REQUIRE(chunk_count == chunk_doc["n"].get_int32().value);
        auto data = chunk_doc["data"].get_binary();

        std::vector<std::uint8_t> expected_bytes{bytes.data() + bytes_read,
                                                 bytes.data() + bytes_read + data.size};
        std::vector<std::uint8_t> actual_bytes{data.bytes, data.bytes + data.size};

        REQUIRE(expected_bytes == actual_bytes);

        bytes_read += data.size;
        ++chunk_count;
    }

    REQUIRE(bytes_read == file_length);
    REQUIRE(chunk_count == 12);
}

TEST_CASE("gridfs upload/download round trip", "[gridfs::uploader] [gridfs::downloader]") {
    instance::current();

    client client{uri{}};
    database db = client["gridfs_upload_download_round_trip_test"];
    gridfs::bucket bucket = db.gridfs_bucket();

    db["fs.files"].delete_many({});
    db["fs.chunks"].delete_many({});

    std::array<std::uint8_t, 100> uploaded_bytes;

    // Initialize array with arbitrary values.
    for (std::uint8_t i = 0; i < 100; ++i) {
        uploaded_bytes[i] = 200 - i;
    }

    std::array<std::uint8_t, 100> downloaded_bytes;

    auto uploader = bucket.open_upload_stream("file");
    uploader.write(100, uploaded_bytes.data());
    auto result = uploader.close();

    auto downloader = bucket.open_download_stream(result.id());
    auto bytes_read = downloader.read(100, downloaded_bytes.data());
    REQUIRE(bytes_read == 100);

    std::uint8_t c;
    REQUIRE(downloader.read(1, &c) == 0);

    REQUIRE(uploaded_bytes == downloaded_bytes);
}
<commit_msg>CXX-1236 Fix mixed integer comparisons in gridfs integration tests<commit_after>// Copyright 2017 MongoDB Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <algorithm>
#include <cstdint>
#include <cstdlib>
#include <vector>

#include <bsoncxx/builder/basic/document.hpp>
#include <bsoncxx/builder/basic/kvp.hpp>
#include <bsoncxx/document/value.hpp>
#include <bsoncxx/oid.hpp>
#include <bsoncxx/test_util/catch.hh>
#include <bsoncxx/types.hpp>
#include <bsoncxx/types/value.hpp>
#include <mongocxx/client.hpp>
#include <mongocxx/database.hpp>
#include <mongocxx/exception/logic_error.hpp>
#include <mongocxx/gridfs/bucket.hpp>
#include <mongocxx/instance.hpp>
#include <mongocxx/options/gridfs/upload.hpp>
#include <mongocxx/stdx.hpp>
#include <mongocxx/uri.hpp>

using namespace mongocxx;

using bsoncxx::builder::basic::kvp;
using bsoncxx::builder::basic::make_document;

TEST_CASE("mongocxx::gridfs::bucket default constructor makes invalid bucket", "[gridfs::bucket]") {
    instance::current();

    gridfs::bucket bucket;
    REQUIRE(!bucket);
    REQUIRE_THROWS_AS(bucket.bucket_name(), logic_error);
}

TEST_CASE("mongocxx::gridfs::bucket copy constructor", "[gridfs::bucket]") {
    instance::current();

    client client{uri{}};
    database db = client["gridfs_bucket_copy_constructor"];

    SECTION("constructing from valid") {
        gridfs::bucket bucket_a = db.gridfs_bucket(options::gridfs::bucket{}.bucket_name("a"));
        gridfs::bucket bucket_b{bucket_a};
        REQUIRE(bucket_b);
        REQUIRE(bucket_b.bucket_name() == stdx::string_view{"a"});
    }

    SECTION("constructing from invalid") {
        gridfs::bucket bucket_a;
        gridfs::bucket bucket_b{bucket_a};
        REQUIRE(!bucket_b);
    }
}

TEST_CASE("mongocxx::gridfs::bucket copy assignment operator", "[gridfs::bucket]") {
    instance::current();

    client client{uri{}};
    database db = client["gridfs_bucket_copy_assignment"];

    SECTION("assigning valid to valid") {
        gridfs::bucket bucket_a = db.gridfs_bucket(options::gridfs::bucket{}.bucket_name("a"));
        gridfs::bucket bucket_b = db.gridfs_bucket(options::gridfs::bucket{}.bucket_name("b"));
        bucket_b = bucket_a;
        REQUIRE(bucket_b);
        REQUIRE(bucket_b.bucket_name() == stdx::string_view{"a"});
    }

    SECTION("assigning invalid to valid") {
        gridfs::bucket bucket_a;
        gridfs::bucket bucket_b = db.gridfs_bucket(options::gridfs::bucket{}.bucket_name("b"));
        bucket_b = bucket_a;
        REQUIRE(!bucket_b);
    }

    SECTION("assigning valid to invalid") {
        gridfs::bucket bucket_a = db.gridfs_bucket(options::gridfs::bucket{}.bucket_name("a"));
        gridfs::bucket bucket_b;
        bucket_b = bucket_a;
        REQUIRE(bucket_b);
        REQUIRE(bucket_b.bucket_name() == stdx::string_view{"a"});
    }

    SECTION("assigning invalid to invalid") {
        gridfs::bucket bucket_a;
        gridfs::bucket bucket_b;
        bucket_b = bucket_a;
        REQUIRE(!bucket_b);
    }
}

// Add an arbitrary GridFS file with a specified length, chunk size, and id to a database's default
// GridFS bucket (i.e. the "fs.files" and "fs.chunks" collections).
//
// Returns a vector of the bytes stored in the GridFS chunks.
std::vector<std::uint8_t> manual_gridfs_initialize(database db,
                                                   std::int64_t length,
                                                   std::int32_t chunk_size,
                                                   bsoncxx::types::value id) {
    std::vector<std::uint8_t> bytes;

    // Populate the vector with arbitrary values.
    for (std::int64_t i = 0; i < length; ++i) {
        bytes.push_back((i + 200) % 256);
    }

    std::vector<bsoncxx::document::value> chunks;
    std::int64_t bytes_written = 0;

    for (std::int32_t i = 0; bytes_written < length; ++i) {
        // The last chunk should be truncated to fit specified file length.
        std::int32_t current_chunk_size = static_cast<std::int32_t>(
            std::min(static_cast<std::int64_t>(chunk_size), length - bytes_written));

        bsoncxx::types::b_binary data = {bsoncxx::binary_sub_type::k_binary,
                                         static_cast<std::uint32_t>(current_chunk_size),
                                         bytes.data() + bytes_written};

        chunks.push_back(make_document(kvp("files_id", id), kvp("n", i), kvp("data", data)));
        bytes_written += current_chunk_size;
    }

    db["fs.chunks"].insert_many(chunks);
    db["fs.files"].insert_one(make_document(kvp("_id", id),
                                            kvp("length", length),
                                            kvp("chunkSize", bsoncxx::types::b_int32{chunk_size})));

    return bytes;
}

TEST_CASE("mongocxx::gridfs::downloader::read with arbitrary sizes", "[gridfs::downloader]") {
    instance::current();

    client client{uri{}};
    database db = client["gridfs_download_read_test"];
    gridfs::bucket bucket = db.gridfs_bucket();

    db["fs.files"].delete_many({});
    db["fs.chunks"].delete_many({});

    std::int64_t file_length = 100;
    std::int32_t chunk_size = 9;
    std::int32_t read_size;

    SECTION("read_size = 1") {
        read_size = 1;
    }

    SECTION("read_size = chunk_size - 1") {
        read_size = chunk_size - 1;
    }

    SECTION("read_size = chunk_size") {
        read_size = chunk_size;
    }

    SECTION("read_size = chunk_size + 1") {
        read_size = chunk_size + 1;
    }

    SECTION("read_size = 2 * chunk_size") {
        read_size = 2 * chunk_size;
    }

    SECTION("read_size = file_length") {
        read_size = file_length;
    }

    SECTION("read_size > file_length") {
        read_size = file_length + 1;
    }

    bsoncxx::types::b_oid id = {bsoncxx::oid{}};
    std::vector<std::uint8_t> expected =
        manual_gridfs_initialize(db, file_length, chunk_size, bsoncxx::types::value{id});

    // Allocate a buffer large enough to fit the data read from the downloader.
    std::vector<std::uint8_t> buffer;
    buffer.reserve(read_size);

    std::size_t total_bytes_read = 0;
    auto downloader = bucket.open_download_stream(bsoncxx::types::value{id});

    while (std::size_t bytes_read = downloader.read(read_size, buffer.data())) {
        std::vector<std::uint8_t> expected_bytes{expected.data() + total_bytes_read,
                                                 expected.data() + total_bytes_read + bytes_read};
        std::vector<std::uint8_t> actual_bytes{buffer.data(), buffer.data() + bytes_read};

        REQUIRE(expected_bytes == actual_bytes);

        total_bytes_read += bytes_read;
    }

    REQUIRE(total_bytes_read == static_cast<std::size_t>(file_length));
}

TEST_CASE("mongocxx::gridfs::uploader::abort works", "[gridfs::uploader]") {
    instance::current();

    client client{uri{}};
    database db = client["gridfs_upload_abort_test"];
    gridfs::bucket bucket = db.gridfs_bucket();

    bsoncxx::types::b_oid id = {bsoncxx::oid{}};
    gridfs::uploader uploader =
        bucket.open_upload_stream_with_id(bsoncxx::types::value{id}, "file");

    // The intended purpose of gridfs::uploader::abort is to clean up any chunks that are already
    // uploaded and to prevent any more data from being written to the uploader. The most
    // straightforward way to test this would be to write enough data to the uploader to ensure that
    // one or more chunks is written and then to call abort. However, in the interest of efficiency,
    // the uploader will only send the chunks to the server either when the amount of data contained
    // in the unset chunks reaches the limit that can fit in a single document or when the user
    // indicates that the file is fully written by calling gridfs::uploader::close. Since it takes a
    // long time to write enough data for the uploader to flush the chunks to the server and calling
    // `close` prevents calling abort afterwards, the easiest way to test abort is to manually
    // insert a chunk with the matching `files_id`, calling abort, and then ensuring that the chunk
    // has been deleted.
    bsoncxx::builder::basic::document document{};
    document.append(bsoncxx::builder::basic::kvp("files_id", id));
    db["fs.chunks"].insert_one(document.extract());

    uploader.abort();
    REQUIRE_THROWS(uploader.close());

    REQUIRE(!db["fs.files"].find_one({}));
    REQUIRE(!db["fs.chunks"].find_one({}));
}

TEST_CASE("mongocxx::gridfs::uploader::write with arbitrary sizes", "[gridfs::uploader]") {
    instance::current();

    client client{uri{}};
    database db = client["gridfs_upload_write_test"];
    gridfs::bucket bucket = db.gridfs_bucket();

    auto files_coll = db["fs.files"];
    auto chunks_coll = db["fs.chunks"];

    files_coll.delete_many({});
    chunks_coll.delete_many({});

    std::int64_t file_length = 100;
    std::int32_t chunk_size = 9;
    std::int32_t write_size;

    SECTION("write_size = 1") {
        write_size = 1;
    }

    SECTION("write_size = chunk_size - 1") {
        write_size = chunk_size - 1;
    }

    SECTION("write_size = chunk_size") {
        write_size = chunk_size;
    }

    SECTION("write_size = chunk_size + 1") {
        write_size = chunk_size + 1;
    }

    SECTION("write_size = 2 * chunk_size") {
        write_size = 2 * chunk_size;
    }

    SECTION("write_size = file_length") {
        write_size = file_length;
    }

    std::vector<std::uint8_t> bytes;

    // Populate the vector with arbitrary values.
    for (std::size_t i = 0; i < static_cast<std::size_t>(file_length); ++i) {
        bytes.push_back((i + 200) % 256);
    }

    std::size_t bytes_written = 0;
    auto upload_options = options::gridfs::upload{}.chunk_size_bytes(chunk_size);
    auto uploader = bucket.open_upload_stream("test_file", upload_options);

    while (static_cast<std::int64_t>(bytes_written) < file_length) {
        std::size_t actual_write_size = static_cast<std::size_t>(
            std::min(static_cast<std::int64_t>(write_size),
                     file_length - static_cast<std::int64_t>(bytes_written)));

        uploader.write(actual_write_size, bytes.data() + bytes_written);
        bytes_written += actual_write_size;
    }

    auto result = uploader.close();
    auto file_doc = files_coll.find_one(make_document(kvp("_id", result.id())));
    REQUIRE(file_doc);

    REQUIRE(file_doc->view()["length"].get_int64().value ==
            static_cast<std::int64_t>(bytes_written));
    REQUIRE(file_doc->view()["chunkSize"].get_int32().value == chunk_size);

    auto chunks = chunks_coll.find(make_document(kvp("files_id", result.id())));

    std::int32_t chunk_count = 0;
    std::size_t bytes_read = 0;

    for (auto chunk_doc : chunks) {
        REQUIRE(chunk_count == chunk_doc["n"].get_int32().value);
        auto data = chunk_doc["data"].get_binary();

        std::vector<std::uint8_t> expected_bytes{bytes.data() + bytes_read,
                                                 bytes.data() + bytes_read + data.size};
        std::vector<std::uint8_t> actual_bytes{data.bytes, data.bytes + data.size};

        REQUIRE(expected_bytes == actual_bytes);

        bytes_read += data.size;
        ++chunk_count;
    }

    REQUIRE(static_cast<std::int64_t>(bytes_read) == file_length);
    REQUIRE(chunk_count == 12);
}

TEST_CASE("gridfs upload/download round trip", "[gridfs::uploader] [gridfs::downloader]") {
    instance::current();

    client client{uri{}};
    database db = client["gridfs_upload_download_round_trip_test"];
    gridfs::bucket bucket = db.gridfs_bucket();

    db["fs.files"].delete_many({});
    db["fs.chunks"].delete_many({});

    std::array<std::uint8_t, 100> uploaded_bytes;

    // Initialize array with arbitrary values.
    for (std::uint8_t i = 0; i < 100; ++i) {
        uploaded_bytes[i] = 200 - i;
    }

    std::array<std::uint8_t, 100> downloaded_bytes;

    auto uploader = bucket.open_upload_stream("file");
    uploader.write(100, uploaded_bytes.data());
    auto result = uploader.close();

    auto downloader = bucket.open_download_stream(result.id());
    auto bytes_read = downloader.read(100, downloaded_bytes.data());
    REQUIRE(bytes_read == 100);

    std::uint8_t c;
    REQUIRE(downloader.read(1, &c) == 0);

    REQUIRE(uploaded_bytes == downloaded_bytes);
}
<|endoftext|>


<commit_before>/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// AliAnalysisMultPt:
// Description: Analysis task to get multiplicity
// and pT distributions
// Author: Negin Alizadehvandchali
// (negin.alizadehvandchali@cern.ch)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include "AliAnalysisMultPt.h"
void runLocal()
{
    // header location
    gInterpreter->ProcessLine(".include $ROOTSYS/include");
    gInterpreter->ProcessLine(".include $ALICE_ROOT/include");

    // create the analysis manager
    AliAnalysisManager *mgr = new AliAnalysisManager("AnalysisMultPt");
    AliAODInputHandler *aodH = new AliAODInputHandler();
    mgr->SetInputEventHandler(aodH);

    // compile the class (locally) with debug symbols
    gInterpreter->LoadMacro("AliAnalysisMultPt.cxx++g");

    // load the addtask macro and create the task
    AliAnalysisMultPt *task = reinterpret_cast<AliAnalysisMultPt*>(gInterpreter->ExecuteMacro("macros/AddMultPt.C"));
    if(!task) return 0x0;

    task->SelectCollisionCandidates(AliVEvent::kINT7);  //????
    task->SetMCRead(kTRUE);
    //task->SetPileUpRead(kTRUE);
    task->SetGeneratorName("Hijing");
    task->SetEtaMinLimit(-0.8);
    task->SetEtaMaxLimit(0.8);
    task->SetFilterBit(128);
    task->SetChi2DoF(4);

    // Add task
    if(!mgr->InitAnalysis()) return;
    mgr->SetDebugLevel(2);
    mgr->PrintStatus();
    mgr->SetUseProgressBar(1, 25);

    // if you want to run locally, we need to define some input
    TChain* chain = new TChain("aodTree");
    //chain->Add("/Users/neginav/AODs for Xe-MC/AliAOD1.root");
    chain->Add("/Users/neginav/AODs for Pb-MC/AliAOD1.root");
    // start the analysis locally, reading the events from the tchain
    mgr->StartAnalysis("local", chain);
    }
<commit_msg>Update runLocalMultPt.C<commit_after>/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// AliAnalysisMultPt:
// Description: Analysis task to get multiplicity
// and pT distributions
// Author: Negin Alizadehvandchali
// (negin.alizadehvandchali@cern.ch)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include "AliAnalysisMultPt.h"
void runLocalMultPt()
{
    // header location
    gInterpreter->ProcessLine(".include $ROOTSYS/include");
    gInterpreter->ProcessLine(".include $ALICE_ROOT/include");

    // create the analysis manager
    AliAnalysisManager *mgr = new AliAnalysisManager("AnalysisMultPt");
    AliAODInputHandler *aodH = new AliAODInputHandler();
    mgr->SetInputEventHandler(aodH);

    // compile the class (locally) with debug symbols
    gInterpreter->LoadMacro("AliAnalysisMultPt.cxx++g");

    // load the addtask macro and create the task
    AliAnalysisMultPt *task = reinterpret_cast<AliAnalysisMultPt*>(gInterpreter->ExecuteMacro("macros/AddMultPt.C"));
    if(!task) return 0x0;

    // Add task
    if(!mgr->InitAnalysis()) return;
    mgr->SetDebugLevel(2);
    mgr->PrintStatus();
    mgr->SetUseProgressBar(1, 25);

    // if you want to run locally, we need to define some input
    TChain* chain = new TChain("aodTree");
    chain->Add("/Users/neginav/AODs for Pb-MC/AliAOD1.root");

    // start the analysis locally, reading the events from the tchain
    mgr->StartAnalysis("local", chain);
}
<|endoftext|>


<commit_before>/*
 * Copyright 2016 - 2021 gary@drinkingtea.net
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#pragma once

#include <functional>

#include <QDockWidget>
#include <QMainWindow>
#include <QModelIndex>
#include <QPoint>
#include <QPointer>
#include <QSharedPointer>
#include <QString>
#include <QTabWidget>
#include <QTreeView>
#include <QUndoGroup>
#include <QVector>

#include <ox/std/types.hpp>

#include "lib/context.hpp"
#include "lib/module.hpp"
#include "lib/project.hpp"

#include "builtinmodules.hpp"
#include "oxfstreeview.hpp"

namespace nostalgia::studio {

struct NostalgiaStudioState {
	QString projectPath;
};

template<typename T>
ox::Error model(T *io, NostalgiaStudioState *obj) {
	oxReturnError(io->setTypeInfo("NostalgiaStudioState", 1));
	oxReturnError(io->field("project_path", &obj->projectPath));
	return OxError(0);
}

struct NostalgiaStudioProfile {
	QString appName;
	QString orgName;
	QVector<QString> modulesPath;
};

template<typename T>
ox::Error model(T *io, NostalgiaStudioProfile *obj) {
	oxReturnError(io->setTypeInfo("NostalgiaStudioProfile", 3));
	oxReturnError(io->field("app_name", &obj->appName));
	oxReturnError(io->field("org_name", &obj->orgName));
	oxReturnError(io->field("modules_path", &obj->modulesPath));
	return OxError(0);
}

class MainWindow: public QMainWindow {
	Q_OBJECT

	private:
		QString m_profilePath;
		NostalgiaStudioProfile m_profile;
		NostalgiaStudioState m_state;
		QAction *m_importAction = nullptr;
		QAction *m_saveAction = nullptr;
		QAction *m_exportAction = nullptr;
		QAction *m_cutAction = nullptr;
		QAction *m_copyAction = nullptr;
		QAction *m_pasteAction = nullptr;
		Context m_ctx;
		QPointer<QMenu> m_viewMenu;
		QVector<QPointer<QDockWidget>> m_dockWidgets;
		QTreeView *m_projectExplorer = nullptr;
		QVector<Module*> m_modules;
		QHash<QString, EditorMaker> m_editorMakers;
		QPointer<OxFSModel> m_oxfsView = nullptr;
		QTabWidget *m_tabs = nullptr;
		QUndoGroup m_undoGroup;
		Editor *m_currentEditor = nullptr;

	public:
		MainWindow(QString profilePath);

		virtual ~MainWindow();

	private:
		void loadModules();

		void loadModuleDir(QString path);

		void loadModule(QString path);

		void loadModule(Module *module);

		void setupMenu();

		void setupProjectExplorer();

		void addDockWidget(Qt::DockWidgetArea area, QDockWidget *dockwidget);

		QAction *addAction(QMenu *menu, QString text, QString toolTip, const QObject *tgt, const char *cb);

		QAction *addAction(QMenu *menu, QString text, QString toolTip,
		                   QKeySequence::StandardKey key, const QObject *tgt, const char *cb);

		QAction *addAction(QMenu *menu, QString text, QString toolTip,
		                   QKeySequence::StandardKey key, void (*cb)());

		int readState();

		void writeState();

		/**
		 * Read open editor tabs for current project.
		 */
		QStringList readTabs();

		/**
		 * Write open editor tabs for current project.
		 */
		void writeTabs(QStringList tabs);

		void openProject(QString);

		void closeProject();

		/**
		 * @param force forces the reopening of the file even if it is already open
		 */
		void openFile(QString path, bool force = false);

	public slots:
		void onExit();

	private slots:
		void openProject();

		void openFileSlot(QModelIndex);

		void saveFile();

		void exportFile();

		void cutAction();

		void copyAction();

		void pasteAction();

		void closeTab(int idx);

		void moveTab(int from, int to);

		void changeTab(int idx);

		void markUnsavedChanges(bool);

		void showNewWizard();

		void showImportWizard();

		void refreshProjectExplorer(QString path);

};

}
<commit_msg>[nostalgia/studio] Add namespacing to models<commit_after>/*
 * Copyright 2016 - 2021 gary@drinkingtea.net
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#pragma once

#include <functional>

#include <QDockWidget>
#include <QMainWindow>
#include <QModelIndex>
#include <QPoint>
#include <QPointer>
#include <QSharedPointer>
#include <QString>
#include <QTabWidget>
#include <QTreeView>
#include <QUndoGroup>
#include <QVector>

#include <ox/std/types.hpp>

#include "lib/context.hpp"
#include "lib/module.hpp"
#include "lib/project.hpp"

#include "builtinmodules.hpp"
#include "oxfstreeview.hpp"

namespace nostalgia::studio {

struct NostalgiaStudioState {
	QString projectPath;
};

template<typename T>
ox::Error model(T *io, NostalgiaStudioState *obj) {
	oxReturnError(io->setTypeInfo("net.drinkingtea.nostalgia.studio.NostalgiaStudioState", 1));
	oxReturnError(io->field("project_path", &obj->projectPath));
	return OxError(0);
}

struct NostalgiaStudioProfile {
	QString appName;
	QString orgName;
	QVector<QString> modulesPath;
};

template<typename T>
ox::Error model(T *io, NostalgiaStudioProfile *obj) {
	oxReturnError(io->setTypeInfo("net.drinkingtea.nostalgia.studio.NostalgiaStudioProfile", 3));
	oxReturnError(io->field("app_name", &obj->appName));
	oxReturnError(io->field("org_name", &obj->orgName));
	oxReturnError(io->field("modules_path", &obj->modulesPath));
	return OxError(0);
}

class MainWindow: public QMainWindow {
	Q_OBJECT

	private:
		QString m_profilePath;
		NostalgiaStudioProfile m_profile;
		NostalgiaStudioState m_state;
		QAction *m_importAction = nullptr;
		QAction *m_saveAction = nullptr;
		QAction *m_exportAction = nullptr;
		QAction *m_cutAction = nullptr;
		QAction *m_copyAction = nullptr;
		QAction *m_pasteAction = nullptr;
		Context m_ctx;
		QPointer<QMenu> m_viewMenu;
		QVector<QPointer<QDockWidget>> m_dockWidgets;
		QTreeView *m_projectExplorer = nullptr;
		QVector<Module*> m_modules;
		QHash<QString, EditorMaker> m_editorMakers;
		QPointer<OxFSModel> m_oxfsView = nullptr;
		QTabWidget *m_tabs = nullptr;
		QUndoGroup m_undoGroup;
		Editor *m_currentEditor = nullptr;

	public:
		MainWindow(QString profilePath);

		virtual ~MainWindow();

	private:
		void loadModules();

		void loadModuleDir(QString path);

		void loadModule(QString path);

		void loadModule(Module *module);

		void setupMenu();

		void setupProjectExplorer();

		void addDockWidget(Qt::DockWidgetArea area, QDockWidget *dockwidget);

		QAction *addAction(QMenu *menu, QString text, QString toolTip, const QObject *tgt, const char *cb);

		QAction *addAction(QMenu *menu, QString text, QString toolTip,
		                   QKeySequence::StandardKey key, const QObject *tgt, const char *cb);

		QAction *addAction(QMenu *menu, QString text, QString toolTip,
		                   QKeySequence::StandardKey key, void (*cb)());

		int readState();

		void writeState();

		/**
		 * Read open editor tabs for current project.
		 */
		QStringList readTabs();

		/**
		 * Write open editor tabs for current project.
		 */
		void writeTabs(QStringList tabs);

		void openProject(QString);

		void closeProject();

		/**
		 * @param force forces the reopening of the file even if it is already open
		 */
		void openFile(QString path, bool force = false);

	public slots:
		void onExit();

	private slots:
		void openProject();

		void openFileSlot(QModelIndex);

		void saveFile();

		void exportFile();

		void cutAction();

		void copyAction();

		void pasteAction();

		void closeTab(int idx);

		void moveTab(int from, int to);

		void changeTab(int idx);

		void markUnsavedChanges(bool);

		void showNewWizard();

		void showImportWizard();

		void refreshProjectExplorer(QString path);

};

}
<|endoftext|>


<commit_before>#ifndef FILTER__H__
#define FILTER__H__

#include <iterbase.hpp>

#include <utility>
#include <iterator>

namespace iter {

    //Forward declarations of Filter and filter
    template <typename FilterFunc, typename Container>
    class Filter;

    template <typename FilterFunc, typename Container>
    Filter<FilterFunc, Container> filter(FilterFunc, Container &);

    template <typename FilterFunc, typename Container>
    class Filter : IterBase<Container>{
        private:
            Container & container;
            FilterFunc filter_func;

            // The filter function is the only thing allowed to create a Filter
            friend Filter filter<FilterFunc, Container>(FilterFunc,
                    Container &);

            using typename IterBase<Container>::contained_iter_type;

            using typename IterBase<Container>::contained_iter_ret;

            // Value constructor for use only in the filter function
            Filter(FilterFunc filter_func, Container & container) :
                container(container),
                filter_func(filter_func)
            { }
            Filter () = delete;
            Filter & operator=(const Filter &) = delete;

        public:
            Filter(const Filter &) = default;

            class Iterator {
                protected:
                    contained_iter_type sub_iter;
                    const contained_iter_type sub_end;
                    FilterFunc filter_func;

                    // increment until the iterator points to is true on the
                    // predicate.  Called by constructor and operator++
                    void skip_failures() {
                        while (this->sub_iter != this->sub_end
                                && !this->filter_func(*this->sub_iter)) {
                            ++this->sub_iter;
                        }
                    }

                public:
                    Iterator (contained_iter_type iter,
                            contained_iter_type end,
                            FilterFunc filter_func) :
                        sub_iter(iter),
                        sub_end(end),
                        filter_func(filter_func)
                    {
                        this->skip_failures();
                    }

                    contained_iter_ret operator*() const {
                        return *this->sub_iter;
                    }

                    Iterator & operator++() {
                        ++this->sub_iter;
                        this->skip_failures();
                        return *this;
                    }

                    bool operator!=(const Iterator & other) const {
                        return this->sub_iter != other.sub_iter;
                    }
            };

            Iterator begin() const {
                return Iterator(
                        std::begin(this->container),
                        std::end(this->container),
                        this->filter_func);
            }

            Iterator end() const {
                return Iterator(
                        std::end(this->container),
                        std::end(this->container),
                        this->filter_func);
            }

    };

    // Helper function to instantiate a Filter
    template <typename FilterFunc, typename Container>
    Filter<FilterFunc, Container> filter(
            FilterFunc filter_func, Container & container) {
        return Filter<FilterFunc, Container>(filter_func, container);
    }

    namespace detail {

        template <typename T>
        bool boolean_cast(const T & t) {
            return bool(t);
        }

        template <typename Container>
        class BoolTester {
            protected:
                using contained_iter_ret =
                    typename IterBase<Container>::contained_iter_ret;

            public:
                bool operator() (const contained_iter_ret item) const {
                    return bool(item);
                }
        };
    }

    template <typename Container>
    auto filter(Container & container) ->
            decltype(filter(detail::BoolTester<Container>(), container)) {
        return filter(detail::BoolTester<Container>(), container);
    }

}

#endif //ifndef FILTER__H__
<commit_msg>Adds support for initializer_lists and temporaries<commit_after>#ifndef FILTER__H__
#define FILTER__H__

#include <iterbase.hpp>

#include <utility>
#include <iterator>
#include <initializer_list>

namespace iter {

    //Forward declarations of Filter and filter
    template <typename FilterFunc, typename Container>
    class Filter;

    template <typename FilterFunc, typename Container>
    Filter<FilterFunc, Container> filter(FilterFunc, Container &&);

    template <typename FilterFunc, typename T>
    Filter<FilterFunc, std::initializer_list<T>> filter(
            FilterFunc, std::initializer_list<T> &&);

    template <typename FilterFunc, typename Container>
    class Filter : IterBase<Container>{
        public:
            Container & container;
            FilterFunc filter_func;

            // The filter function is the only thing allowed to create a Filter
            friend Filter filter<FilterFunc, Container>(
                    FilterFunc, Container &&);

            template <typename T>
            friend Filter<FilterFunc, std::initializer_list<T>> filter(
                    FilterFunc, std::initializer_list<T> &&);
            using typename IterBase<Container>::contained_iter_type;

            using typename IterBase<Container>::contained_iter_ret;

            // Value constructor for use only in the filter function
            Filter(FilterFunc filter_func, Container && container) :
                container(container),
                filter_func(filter_func)
            { }
            Filter () = delete;
            Filter & operator=(const Filter &) = delete;

        public:
            Filter(const Filter &) = default;

            class Iterator {
                protected:
                    contained_iter_type sub_iter;
                    const contained_iter_type sub_end;
                    FilterFunc filter_func;

                    // increment until the iterator points to is true on the
                    // predicate.  Called by constructor and operator++
                    void skip_failures() {
                        while (this->sub_iter != this->sub_end
                                && !this->filter_func(*this->sub_iter)) {
                            ++this->sub_iter;
                        }
                    }

                public:
                    Iterator (contained_iter_type iter,
                            contained_iter_type end,
                            FilterFunc filter_func) :
                        sub_iter(iter),
                        sub_end(end),
                        filter_func(filter_func)
                    {
                        this->skip_failures();
                    }

                    contained_iter_ret operator*() const {
                        return *this->sub_iter;
                    }

                    Iterator & operator++() {
                        ++this->sub_iter;
                        this->skip_failures();
                        return *this;
                    }

                    bool operator!=(const Iterator & other) const {
                        return this->sub_iter != other.sub_iter;
                    }
            };

            Iterator begin() const {
                return Iterator(
                        std::begin(this->container),
                        std::end(this->container),
                        this->filter_func);
            }

            Iterator end() const {
                return Iterator(
                        std::end(this->container),
                        std::end(this->container),
                        this->filter_func);
            }

    };

    // Helper function to instantiate a Filter
    template <typename FilterFunc, typename Container>
    Filter<FilterFunc, Container> filter(
            FilterFunc filter_func, Container && container) {
        return Filter<FilterFunc, Container>(
                filter_func, std::forward<Container>(container));
    }

    namespace detail {

        template <typename T>
        bool boolean_cast(const T & t) {
            return bool(t);
        }

        template <typename Container>
        class BoolTester {
            protected:
                using contained_iter_ret =
                    typename IterBase<Container>::contained_iter_ret;

            public:
                bool operator() (const contained_iter_ret item) const {
                    return bool(item);
                }
        };
    }

    template <typename Container>
    auto filter(Container && container) ->
            decltype(filter(
                        detail::BoolTester<Container>(),
                        std::forward<Container>(container))) {
        return filter(detail::BoolTester<Container>(),
                std::forward<Container>(container));
    }

    template <typename FilterFunc, typename T>
    Filter<FilterFunc, std::initializer_list<T>> filter(
            FilterFunc filter_func,
            std::initializer_list<T> && il)
    {
        return Filter<FilterFunc, std::initializer_list<T>>(
                filter_func,
                std::move(il));
                //std::forward<std::initializer_list<T>>(il));
    }

}

#endif //ifndef FILTER__H__
<|endoftext|>


<commit_before>#include "QOsgViewer.h"

#include <osgGA/TrackballManipulator>
#include <osgViewer/ViewerEventHandlers>
#include "simvis/osg_camera_man.h"
#include "xo/system/log.h"
#include "qevent.h"
#include "simvis/group.h"
#include "simvis/scene.h"
#include "osg/MatrixTransform"
#include "xo/geometry/quat.h"
#include "simvis/osg_tools.h"

// class for routing GUI events to QOsgViewer
// This is needed because QOsgViewer can't derive from GUIEventHandler directly
// because both are derived from osg::Object (basically, because of bad design)
class QOsgEventHandler : public osgGA::GUIEventHandler
{
public:
	QOsgEventHandler( QOsgViewer& viewer ) : viewer_( viewer ) {}
	virtual bool handle( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa ) { return viewer_.handle( ea, aa ); }
private:
	QOsgViewer& viewer_;
};

QOsgViewer::QOsgViewer( QWidget* parent /*= 0*/, Qt::WindowFlags f /*= 0*/, osgViewer::ViewerBase::ThreadingModel threadingModel/*=osgViewer::CompositeViewer::SingleThreaded*/ ) :
QWidget( parent, f ),
capture_handler_( nullptr ),
frame_count_( 0 ),
scene_light_offset_( -3, 8, 2 )
{
	QCoreApplication::instance()->installEventFilter( this );
	setThreadingModel( threadingModel );

	// disable the default setting of viewer.done() by pressing Escape.
	setKeyEventSetsDone( 0 );

	// setup viewer grid
	QWidget* widget1 = addViewWidget( createGraphicsWindow( 0, 0, 100, 100, "", true ) );
	auto* grid = new QGridLayout;
	grid->addWidget( widget1 );
	setLayout( grid );
	grid->setMargin( 1 );

	// start timer
	// TODO: remove this -- only update after something has changed
	connect( &timer_, SIGNAL( timeout() ), this, SLOT( update() ) );
	timer_.start( 1000 / 120 );
}

QWidget* QOsgViewer::addViewWidget( osgQt::GraphicsWindowQt* gw )
{
	view_ = new osgViewer::View;
	addView( view_ );

	osg::Camera* cam = view_->getCamera();
	cam->setGraphicsContext( gw );

	const osg::GraphicsContext::Traits* traits = gw->getTraits();

	cam->setClearColor( osg::Vec4( 0.55, 0.55, 0.55, 1.0 ) );
	cam->setViewport( new osg::Viewport( 0, 0, traits->width, traits->height ) );
	cam->setProjectionMatrixAsPerspective( 30.0f, static_cast<double>( traits->width ) / static_cast<double>( traits->height ), 1.0f, 10000.0f );

	// add event handlers
	view_->addEventHandler( new osgViewer::StatsHandler );
	view_->addEventHandler( new QOsgEventHandler( *this ) );

	// disable lighting by default
	view_->setLightingMode( osg::View::NO_LIGHT );

	// setup camera manipulator
	gw->setTouchEventsEnabled( true );

	camera_man_ = new vis::osg_camera_man();
	camera_man_->setVerticalAxisFixed( false );
	view_->setCameraManipulator( camera_man_ );

	return gw->getGLWidget();
}

osgQt::GraphicsWindowQt* QOsgViewer::createGraphicsWindow( int x, int y, int w, int h, const std::string& name/*=""*/, bool windowDecoration/*=false */ )
{
	osg::DisplaySettings* ds = osg::DisplaySettings::instance().get();
	ds->setNumMultiSamples( 2 );

	osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
	traits->windowName = name;
	traits->windowDecoration = windowDecoration;
	traits->x = x;
	traits->y = y;
	traits->width = w;
	traits->height = h;
	traits->doubleBuffer = true;
	traits->alpha = ds->getMinimumNumAlphaBits();
	traits->stencil = ds->getMinimumNumStencilBits();
	traits->sampleBuffers = ds->getMultiSamples();
	traits->samples = ds->getNumMultiSamples();

	width_ = w;
	height_ = h;

	return new osgQt::GraphicsWindowQt( traits.get() );
}

void QOsgViewer::paintEvent( QPaintEvent* event )
{
	// prevent capturing duplicate frames
	if ( isCapturing() && current_frame_time_ == last_drawn_frame_time_ )
		return; // this frame was already captured, skip

	updateLightPos();

	++frame_count_;
	frame();
	last_drawn_frame_time_ = current_frame_time_;
}

void QOsgViewer::setScene( vis::scene* s )
{
	scene_ = s;
	for ( size_t i = 0; i < getNumViews(); ++i )
		getView( i )->setSceneData( s->osg_node() );

	// init light
	scene_light_ = scene_->add_light( scene_light_offset_, vis::make_white( 1 ) );
	scene_light_.attenuation( 1.0f, 0.0f, 0.0f );
}

void QOsgViewer::setHud( const xo::path& file )
{
	hud_ = vis::plane( xo::vec3f( hud_size, 0, 0 ), xo::vec3f( 0, hud_size, 0 ), file, 1.0f, 1.0f );
	hud_.osg_trans_node().setReferenceFrame( osg::Transform::ABSOLUTE_RF );
	auto geostate = hud_.osg_group().getChild( 0 )->asGeode()->getDrawable( 0 )->getOrCreateStateSet();
	geostate->setMode( GL_DEPTH_TEST, osg::StateAttribute::OFF );
	geostate->setMode( GL_BLEND, osg::StateAttribute::ON );
	view_->getCamera()->addChild( hud_.osg_node() );
	//updateHudPos();
}

void QOsgViewer::updateHudPos()
{
	double fovy, aspect, nearplane, farplane;
	view_->getCamera()->getProjectionMatrixAsPerspective( fovy, aspect, nearplane, farplane );
	//xo::log::info( "aspect ratio = ", aspect );
	auto hh = tan( xo::deg_to_rad( fovy ) / 2 );
	auto hw = tan( atan( hh * aspect ) );
	hud_.pos( xo::vec3f( hw - 0.55f * hud_size, -hh + 0.55f * hud_size, -1 ) );
}

void QOsgViewer::updateLightPos()
{
	auto center = camera_man_->getCenter();
	auto ori = xo::quat_from_axis_angle( xo::vec3f::unit_y(), camera_man_->getYaw() );
	auto v = ori * scene_light_offset_;
	scene_light_.pos( vis::vec3f( vis::from_osg( center ) ) + v );
}

void QOsgViewer::setClearColor( const osg::Vec4& col )
{
	view_->getCamera()->setClearColor( col );
}

void QOsgViewer::moveCamera( const osg::Vec3d& delta_pos )
{
	camera_man_->setCenter( camera_man_->getCenter() + delta_pos );
}

bool QOsgViewer::handle( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa )
{
	if ( ea.getEventType() == osgGA::GUIEventAdapter::RESIZE )
	{
		width_ = ea.getWindowWidth();
		height_ = ea.getWindowHeight();
		xo::log::info( "Viewer resized to ", width_, "x", height_ );
		updateHudPos();
		return true;
	}
	return false;
}

void QOsgViewer::startCapture( const std::string& filename )
{
	// create capture handler
	xo::log::info( "Started capturing video to ", filename );
	capture_handler_ = new osgViewer::ScreenCaptureHandler(
		new osgViewer::ScreenCaptureHandler::WriteToFile( filename, "png", osgViewer::ScreenCaptureHandler::WriteToFile::SEQUENTIAL_NUMBER ), -1 );
	view_->addEventHandler( capture_handler_ );
	capture_handler_->startCapture();
}

void QOsgViewer::stopCapture()
{
	last_drawn_frame_time_ = ~size_t ( 0 );
	if ( capture_handler_ )
	{
		xo::log::info( "Video capture stopped" );
		capture_handler_->stopCapture();
		capture_handler_ = nullptr;
	}
}

void QOsgViewer::captureCurrentFrame( const std::string& filename )
{
	stopCapture();

	capture_handler_ = new osgViewer::ScreenCaptureHandler(
		new osgViewer::ScreenCaptureHandler::WriteToFile( filename, "png", osgViewer::ScreenCaptureHandler::WriteToFile::OVERWRITE ), -1 );
	view_->addEventHandler( capture_handler_ );
	capture_handler_->startCapture();
	frame();
	capture_handler_->stopCapture();
	capture_handler_ = nullptr;
	xo::log::info( "Written image to ", filename );
}

void QOsgViewer::setFrameTime( double t )
{
	if ( current_frame_time_ != t )
	{
		current_frame_time_ = t;
		repaint();
	}
}

bool QOsgViewer::eventFilter( QObject* obj, QEvent* event )
{
	return QObject::eventFilter( obj, event );
}
<commit_msg>U: visualization tweaks and config settings<commit_after>#include "QOsgViewer.h"

#include <osgGA/TrackballManipulator>
#include <osgViewer/ViewerEventHandlers>
#include "simvis/osg_camera_man.h"
#include "xo/system/log.h"
#include "qevent.h"
#include "simvis/group.h"
#include "simvis/scene.h"
#include "osg/MatrixTransform"
#include "xo/geometry/quat.h"
#include "simvis/osg_tools.h"

// class for routing GUI events to QOsgViewer
// This is needed because QOsgViewer can't derive from GUIEventHandler directly
// because both are derived from osg::Object (basically, because of bad design)
class QOsgEventHandler : public osgGA::GUIEventHandler
{
public:
	QOsgEventHandler( QOsgViewer& viewer ) : viewer_( viewer ) {}
	virtual bool handle( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa ) { return viewer_.handle( ea, aa ); }
private:
	QOsgViewer& viewer_;
};

QOsgViewer::QOsgViewer( QWidget* parent /*= 0*/, Qt::WindowFlags f /*= 0*/, osgViewer::ViewerBase::ThreadingModel threadingModel/*=osgViewer::CompositeViewer::SingleThreaded*/ ) :
QWidget( parent, f ),
capture_handler_( nullptr ),
frame_count_( 0 ),
scene_light_offset_( -2, 8, 3 )
{
	QCoreApplication::instance()->installEventFilter( this );
	setThreadingModel( threadingModel );

	// disable the default setting of viewer.done() by pressing Escape.
	setKeyEventSetsDone( 0 );

	// setup viewer grid
	QWidget* widget1 = addViewWidget( createGraphicsWindow( 0, 0, 100, 100, "", true ) );
	auto* grid = new QGridLayout;
	grid->addWidget( widget1 );
	setLayout( grid );
	grid->setMargin( 1 );

	// start timer
	// TODO: remove this -- only update after something has changed
	connect( &timer_, SIGNAL( timeout() ), this, SLOT( update() ) );
	timer_.start( 1000 / 120 );
}

QWidget* QOsgViewer::addViewWidget( osgQt::GraphicsWindowQt* gw )
{
	view_ = new osgViewer::View;
	addView( view_ );

	osg::Camera* cam = view_->getCamera();
	cam->setGraphicsContext( gw );

	const osg::GraphicsContext::Traits* traits = gw->getTraits();

	cam->setClearColor( osg::Vec4( 0.55, 0.55, 0.55, 1.0 ) );
	cam->setViewport( new osg::Viewport( 0, 0, traits->width, traits->height ) );
	cam->setProjectionMatrixAsPerspective( 30.0f, static_cast<double>( traits->width ) / static_cast<double>( traits->height ), 1.0f, 10000.0f );

	// add event handlers
	view_->addEventHandler( new osgViewer::StatsHandler );
	view_->addEventHandler( new QOsgEventHandler( *this ) );

	// disable lighting by default
	view_->setLightingMode( osg::View::NO_LIGHT );

	// setup camera manipulator
	gw->setTouchEventsEnabled( true );

	camera_man_ = new vis::osg_camera_man();
	camera_man_->setVerticalAxisFixed( false );
	view_->setCameraManipulator( camera_man_ );

	return gw->getGLWidget();
}

osgQt::GraphicsWindowQt* QOsgViewer::createGraphicsWindow( int x, int y, int w, int h, const std::string& name/*=""*/, bool windowDecoration/*=false */ )
{
	osg::DisplaySettings* ds = osg::DisplaySettings::instance().get();
	ds->setNumMultiSamples( 2 );

	osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
	traits->windowName = name;
	traits->windowDecoration = windowDecoration;
	traits->x = x;
	traits->y = y;
	traits->width = w;
	traits->height = h;
	traits->doubleBuffer = true;
	traits->alpha = ds->getMinimumNumAlphaBits();
	traits->stencil = ds->getMinimumNumStencilBits();
	traits->sampleBuffers = ds->getMultiSamples();
	traits->samples = ds->getNumMultiSamples();

	width_ = w;
	height_ = h;

	return new osgQt::GraphicsWindowQt( traits.get() );
}

void QOsgViewer::paintEvent( QPaintEvent* event )
{
	// prevent capturing duplicate frames
	if ( isCapturing() && current_frame_time_ == last_drawn_frame_time_ )
		return; // this frame was already captured, skip

	updateLightPos();

	++frame_count_;
	frame();
	last_drawn_frame_time_ = current_frame_time_;
}

void QOsgViewer::setScene( vis::scene* s )
{
	scene_ = s;
	for ( size_t i = 0; i < getNumViews(); ++i )
		getView( i )->setSceneData( s->osg_node() );

	// init light
	scene_light_ = scene_->add_light( scene_light_offset_, vis::make_white( 1 ) );
	scene_light_.attenuation( 1.0f, 0.0f, 0.0f );
}

void QOsgViewer::setHud( const xo::path& file )
{
	hud_ = vis::plane( xo::vec3f( hud_size, 0, 0 ), xo::vec3f( 0, hud_size, 0 ), file, 1.0f, 1.0f );
	hud_.osg_trans_node().setReferenceFrame( osg::Transform::ABSOLUTE_RF );
	auto geostate = hud_.osg_group().getChild( 0 )->asGeode()->getDrawable( 0 )->getOrCreateStateSet();
	geostate->setMode( GL_DEPTH_TEST, osg::StateAttribute::OFF );
	geostate->setMode( GL_BLEND, osg::StateAttribute::ON );
	view_->getCamera()->addChild( hud_.osg_node() );
	//updateHudPos();
}

void QOsgViewer::updateHudPos()
{
	double fovy, aspect, nearplane, farplane;
	view_->getCamera()->getProjectionMatrixAsPerspective( fovy, aspect, nearplane, farplane );
	//xo::log::info( "aspect ratio = ", aspect );
	auto hh = tan( xo::deg_to_rad( fovy ) / 2 );
	auto hw = tan( atan( hh * aspect ) );
	hud_.pos( xo::vec3f( hw - 0.55f * hud_size, -hh + 0.55f * hud_size, -1 ) );
}

void QOsgViewer::updateLightPos()
{
	auto center = camera_man_->getCenter();
	auto ori = xo::quat_from_axis_angle( xo::vec3f::unit_y(), camera_man_->getYaw() );
	auto v = ori * scene_light_offset_;
	scene_light_.pos( vis::vec3f( vis::from_osg( center ) ) + v );
}

void QOsgViewer::setClearColor( const osg::Vec4& col )
{
	view_->getCamera()->setClearColor( col );
}

void QOsgViewer::moveCamera( const osg::Vec3d& delta_pos )
{
	camera_man_->setCenter( camera_man_->getCenter() + delta_pos );
}

bool QOsgViewer::handle( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa )
{
	if ( ea.getEventType() == osgGA::GUIEventAdapter::RESIZE )
	{
		width_ = ea.getWindowWidth();
		height_ = ea.getWindowHeight();
		xo::log::info( "Viewer resized to ", width_, "x", height_ );
		updateHudPos();
		return true;
	}
	return false;
}

void QOsgViewer::startCapture( const std::string& filename )
{
	// create capture handler
	xo::log::info( "Started capturing video to ", filename );
	capture_handler_ = new osgViewer::ScreenCaptureHandler(
		new osgViewer::ScreenCaptureHandler::WriteToFile( filename, "png", osgViewer::ScreenCaptureHandler::WriteToFile::SEQUENTIAL_NUMBER ), -1 );
	view_->addEventHandler( capture_handler_ );
	capture_handler_->startCapture();
}

void QOsgViewer::stopCapture()
{
	last_drawn_frame_time_ = ~size_t ( 0 );
	if ( capture_handler_ )
	{
		xo::log::info( "Video capture stopped" );
		capture_handler_->stopCapture();
		capture_handler_ = nullptr;
	}
}

void QOsgViewer::captureCurrentFrame( const std::string& filename )
{
	stopCapture();

	capture_handler_ = new osgViewer::ScreenCaptureHandler(
		new osgViewer::ScreenCaptureHandler::WriteToFile( filename, "png", osgViewer::ScreenCaptureHandler::WriteToFile::OVERWRITE ), -1 );
	view_->addEventHandler( capture_handler_ );
	capture_handler_->startCapture();
	frame();
	capture_handler_->stopCapture();
	capture_handler_ = nullptr;
	xo::log::info( "Written image to ", filename );
}

void QOsgViewer::setFrameTime( double t )
{
	if ( current_frame_time_ != t )
	{
		current_frame_time_ = t;
		repaint();
	}
}

bool QOsgViewer::eventFilter( QObject* obj, QEvent* event )
{
	return QObject::eventFilter( obj, event );
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2008-2010 The QXmpp developers
 *
 * Author:
 *  Jeremy Lainé
 *
 * Source:
 *  http://code.google.com/p/qxmpp
 *
 * This file is a part of QXmpp library.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 */

#include <QDomElement>
#include <QSslKey>
#include <QSslSocket>

#include "QXmppConstants.h"
#include "QXmppDialback.h"
#include "QXmppIncomingServer.h"
#include "QXmppOutgoingServer.h"
#include "QXmppStreamFeatures.h"
#include "QXmppUtils.h"

class QXmppIncomingServerPrivate
{
public:
    QString authenticated;
    QString domain;
    QString localStreamId;
};

/// Constructs a new incoming server stream.
///
/// \param socket The socket for the XMPP stream.
/// \param domain The local domain.
/// \param parent The parent QObject for the stream (optional).
///

QXmppIncomingServer::QXmppIncomingServer(QSslSocket *socket, const QString &domain, QObject *parent)
    : QXmppStream(parent),
    d(new QXmppIncomingServerPrivate)
{
    setObjectName("S2S-in");
    setSocket(socket);
    d->domain = domain;
}

/// Destroys the current stream.

QXmppIncomingServer::~QXmppIncomingServer()
{
    delete d;
}

/// Returns the stream's identifier.
///

QString QXmppIncomingServer::localStreamId() const
{
    return d->localStreamId;
}

void QXmppIncomingServer::handleStream(const QDomElement &streamElement)
{
    if (!streamElement.attribute("from").isEmpty())
        setObjectName("S2S-in-" + streamElement.attribute("from"));

    // start stream
    d->localStreamId = generateStanzaHash().toAscii();
    QString data = QString("<?xml version='1.0'?><stream:stream"
        " xmlns='%1' xmlns:db='%2' xmlns:stream='%3'"
        " id='%4' version=\"1.0\">").arg(
            ns_server,
            ns_server_dialback,
            ns_stream,
            d->localStreamId);
    sendData(data.toUtf8());

    // send stream features
    QXmppStreamFeatures features;
    if (!socket()->isEncrypted() && !socket()->localCertificate().isNull() && !socket()->privateKey().isNull())
        features.setTlsMode(QXmppStreamFeatures::Enabled);
    sendPacket(features);
}

void QXmppIncomingServer::handleStanza(const QDomElement &stanza)
{
    const QString ns = stanza.namespaceURI();

    if (ns == ns_tls && stanza.tagName() == "starttls")
    {
        sendData("<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
        socket()->flush();
        socket()->startServerEncryption();
        return;
    }
    else if (QXmppDialback::isDialback(stanza))
    {
        QXmppDialback request;
        request.parse(stanza);
        // check the request is valid
        if (!request.type().isEmpty() ||
            request.from().isEmpty() ||
            request.to() != d->domain ||
            request.key().isEmpty())
        {
            warning("Invalid dialback received");
            return;
        }

        const QString domain = request.from();
        setObjectName("S2S-in-" + domain);

        if (request.command() == QXmppDialback::Result)
        {
            // establish dialback connection
            QXmppOutgoingServer *stream = new QXmppOutgoingServer(d->domain, this);
            stream->setObjectName("S2S-dialback-" + domain);
            bool check = connect(stream, SIGNAL(dialbackResponseReceived(QXmppDialback)),
                                 this, SLOT(slotDialbackResponseReceived(QXmppDialback)));
            Q_ASSERT(check);
            Q_UNUSED(check);
            stream->setVerify(d->localStreamId, request.key());
            stream->connectToHost(domain);
        }
        else if (request.command() == QXmppDialback::Verify)
        {
            emit dialbackRequestReceived(request);
        }

    }
    else if (!d->authenticated.isEmpty() &&
             jidToDomain(stanza.attribute("from")) == d->authenticated)
    {
        // relay stanza if the remote party is authenticated
        emit elementReceived(stanza);
    } else {
        warning("Received an element, but remote party is not authenticated");
    }
}

/// Returns true if the socket is connected and the remote server is
/// authenticated.
///

bool QXmppIncomingServer::isConnected() const
{
    return QXmppStream::isConnected() && !d->authenticated.isEmpty();
}

/// Handles a dialback response received from the authority server.
///
/// \param response
///

void QXmppIncomingServer::slotDialbackResponseReceived(const QXmppDialback &dialback)
{
    QXmppOutgoingServer *stream = qobject_cast<QXmppOutgoingServer*>(sender());
    if (!stream ||
        dialback.command() != QXmppDialback::Verify ||
        dialback.id() != d->localStreamId ||
        dialback.from() != stream->remoteDomain())
        return;

    // relay verify response
    QXmppDialback response;
    response.setCommand(QXmppDialback::Result);
    response.setTo(dialback.from());
    response.setFrom(d->domain);
    response.setType(dialback.type());
    sendPacket(response);

    // check for success
    if (response.type() == "valid")
    {
        info("Incoming stream is ready");
        d->authenticated = dialback.from();
        emit connected();
    } else {
        disconnectFromHost();
    }

    // disconnect dialback
    stream->disconnectFromHost();
    stream->deleteLater();
}

<commit_msg>improve debugging of incoming server-to-server streams<commit_after>/*
 * Copyright (C) 2008-2010 The QXmpp developers
 *
 * Author:
 *  Jeremy Lainé
 *
 * Source:
 *  http://code.google.com/p/qxmpp
 *
 * This file is a part of QXmpp library.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 */

#include <QDomElement>
#include <QSslKey>
#include <QSslSocket>

#include "QXmppConstants.h"
#include "QXmppDialback.h"
#include "QXmppIncomingServer.h"
#include "QXmppOutgoingServer.h"
#include "QXmppStreamFeatures.h"
#include "QXmppUtils.h"

class QXmppIncomingServerPrivate
{
public:
    QString authenticated;
    QString domain;
    QString localStreamId;
};

/// Constructs a new incoming server stream.
///
/// \param socket The socket for the XMPP stream.
/// \param domain The local domain.
/// \param parent The parent QObject for the stream (optional).
///

QXmppIncomingServer::QXmppIncomingServer(QSslSocket *socket, const QString &domain, QObject *parent)
    : QXmppStream(parent),
    d(new QXmppIncomingServerPrivate)
{
    d->domain = domain;

    if (socket) {
        info(QString("Incoming server connection from %1 %2").arg(
            socket->peerAddress().toString(),
            QString::number(socket->peerPort())));
        setSocket(socket);
    }
}

/// Destroys the current stream.

QXmppIncomingServer::~QXmppIncomingServer()
{
    delete d;
}

/// Returns the stream's identifier.
///

QString QXmppIncomingServer::localStreamId() const
{
    return d->localStreamId;
}

void QXmppIncomingServer::handleStream(const QDomElement &streamElement)
{
    const QString from = streamElement.attribute("from");
    if (!from.isEmpty())
        info(QString("Incoming server stream from %1").arg(from));

    // start stream
    d->localStreamId = generateStanzaHash().toAscii();
    QString data = QString("<?xml version='1.0'?><stream:stream"
        " xmlns='%1' xmlns:db='%2' xmlns:stream='%3'"
        " id='%4' version=\"1.0\">").arg(
            ns_server,
            ns_server_dialback,
            ns_stream,
            d->localStreamId);
    sendData(data.toUtf8());

    // send stream features
    QXmppStreamFeatures features;
    if (!socket()->isEncrypted() && !socket()->localCertificate().isNull() && !socket()->privateKey().isNull())
        features.setTlsMode(QXmppStreamFeatures::Enabled);
    sendPacket(features);
}

void QXmppIncomingServer::handleStanza(const QDomElement &stanza)
{
    const QString ns = stanza.namespaceURI();

    if (ns == ns_tls && stanza.tagName() == "starttls")
    {
        sendData("<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
        socket()->flush();
        socket()->startServerEncryption();
        return;
    }
    else if (QXmppDialback::isDialback(stanza))
    {
        QXmppDialback request;
        request.parse(stanza);
        // check the request is valid
        if (!request.type().isEmpty() ||
            request.from().isEmpty() ||
            request.to() != d->domain ||
            request.key().isEmpty())
        {
            warning("Invalid dialback received");
            return;
        }

        const QString domain = request.from();
        if (request.command() == QXmppDialback::Result)
        {
            debug(QString("Received a dialback result from %1").arg(domain));

            // establish dialback connection
            QXmppOutgoingServer *stream = new QXmppOutgoingServer(d->domain, this);
            bool check = connect(stream, SIGNAL(dialbackResponseReceived(QXmppDialback)),
                                 this, SLOT(slotDialbackResponseReceived(QXmppDialback)));
            Q_ASSERT(check);
            Q_UNUSED(check);
            stream->setVerify(d->localStreamId, request.key());
            stream->connectToHost(domain);
        }
        else if (request.command() == QXmppDialback::Verify)
        {
            debug(QString("Received a dialback verify from %1").arg(domain));
            emit dialbackRequestReceived(request);
        }

    }
    else if (!d->authenticated.isEmpty() &&
             jidToDomain(stanza.attribute("from")) == d->authenticated)
    {
        // relay stanza if the remote party is authenticated
        emit elementReceived(stanza);
    } else {
        warning("Received an element, but remote party is not authenticated");
    }
}

/// Returns true if the socket is connected and the remote server is
/// authenticated.
///

bool QXmppIncomingServer::isConnected() const
{
    return QXmppStream::isConnected() && !d->authenticated.isEmpty();
}

/// Handles a dialback response received from the authority server.
///
/// \param response
///

void QXmppIncomingServer::slotDialbackResponseReceived(const QXmppDialback &dialback)
{
    QXmppOutgoingServer *stream = qobject_cast<QXmppOutgoingServer*>(sender());
    if (!stream ||
        dialback.command() != QXmppDialback::Verify ||
        dialback.id() != d->localStreamId ||
        dialback.from() != stream->remoteDomain())
        return;

    // relay verify response
    QXmppDialback response;
    response.setCommand(QXmppDialback::Result);
    response.setTo(dialback.from());
    response.setFrom(d->domain);
    response.setType(dialback.type());
    sendPacket(response);

    // check for success
    if (response.type() == "valid")
    {
        info("Incoming stream is ready");
        d->authenticated = dialback.from();
        emit connected();
    } else {
        disconnectFromHost();
    }

    // disconnect dialback
    stream->disconnectFromHost();
    stream->deleteLater();
}

<|endoftext|>


<commit_before>/*
  To compile, do

  rootcint -f OptionDict.C -c Option.C
  g++ `root-config --cflags --libs` \
    -lVMC -lGeom -lMinuit -lXMLIO -lTree -lTreePlayer \
    -I$ALICE_ROOT/include -L$ALICE_ROOT/lib/tgt_${ALICE_TARGET} \
    -lSTEERBase -lESD -lAOD -lANALYSIS -lOADB -lANALYSISalice \
    trainMain.cxx -o runTrain
*/

#include "AvailableSoftware.C"
#include "ChainBuilder.C"
#include "ParUtilities.C"
#include "OutputUtilities.C"
#include "Option.C"
#include "Helper.C"
#include "LocalHelper.C"
#include "ProofHelper.C"
#include "LiteHelper.C"
#include "AAFHelper.C"
#include "PluginHelper.C"
#include "AAFPluginHelper.C"
#include "GridHelper.C"
#include "TrainSetup.C"

#include <TGApplication.h>
#include <TROOT.h>
#include <TList.h>
#include <TObjString.h>
#include <TString.h>

#include <iostream>
#include <iomanip>

/**
 * Custom timer to do a deferred start after the application
 * has been started
 */
struct Deferred : public TTimer
{
  Deferred(const TString& name, const TString& cls,
	   const TCollection* opts)
    : TTimer(1000, false),
      fName(name),
      fClass(cls),
      fOptions(opts)
  {
    Start(1000, true);
  }
  Deferred(const Deferred& o)
    : TTimer(),
      fName(o.fName),
      fClass(o.fClass),
      fOptions(o.fOptions)
  {}
  Deferred& operator=(const Deferred& o)
  {
    if (&o == this) return *this;
    fName    = o.fName;
    fClass   = o.fClass;
    fOptions = o.fOptions;
    return *this;
  }
  Bool_t Notify()
  {
    // gSystem->RemoveTimer(this);
    Info("Notify", "Will run train setup: %s (%s)",
	 fName.Data(), fClass.Data());
    return TrainSetup::Main(fName, fClass, fOptions);
  }
  TString fName;
  TString fClass;
  const TCollection* fOptions;
};

/**
 * Append directory to header and script search path
 *
 * @param dir Directory
 *
 * @ingroup pwglf_forward_trains_run
 */
void AppendPath(const char* dir)
{
  gROOT->SetMacroPath(Form("%s:%s",gROOT->GetMacroPath(), dir));
  gSystem->AddIncludePath(Form("-I%s", dir));
}
/**
 * Print a fake option description.  Used for options specific to this
 * program.
 *
 * @param o    Output stream
 * @param opt  Option (including meta argument)
 * @param desc Option description.
 *
 * @ingroup pwglf_forward_trains_run
 */
void PrintFakeOption(std::ostream& o, const char* opt, const char* desc)
{
  o << "  --" << std::left << std::setw(30) << opt << " " << desc << std::endl;
}

/**
 * Print usage information
 *
 * @param progname Program name
 * @param o        Output stream
 * @param r        Optional runner.
 *
 * @ingroup pwglf_forward_trains_run
 */
void Usage(const char* progname, std::ostream& o)
{
  o << "Usage: " << progname << " --class=CLASS --name=NAME [OPTIONS]\n\n"
    << "PROGRAM OPTIONS:\n";
  PrintFakeOption(o, "class=CLASS",       "Train class");
  PrintFakeOption(o, "name=NAME",         "Name of train");
  PrintFakeOption(o, "include=DIRECTORY", "Append dir to macro/header path");
  PrintFakeOption(o, "batch",             "Batch mode");
}

int
main(int argc, char** argv)
{
  TList optList;
  TString name;
  TString cls;
  Bool_t  batch = false;
  Bool_t  help  = false;

  // --- Parse options -----------------------------------------------
  for (int i = 1; i < argc; i++) {
    if (argv[i][0] == '-' && argv[i][1] == '-') {
      TString arg(argv[i]);
      TString val("");
      arg.ReplaceAll("\"'", "");
      Int_t   eq = arg.Index("=");
      if (eq != kNPOS) val = arg(eq+1, arg.Length()-eq-1);
      if      (arg.BeginsWith("--class"))   cls  = val;
      else if (arg.BeginsWith("--name"))    name = val;
      else if (arg.BeginsWith("--include")) AppendPath(val);
      else if (arg.BeginsWith("--batch"))   batch = true;
      else if (arg.BeginsWith("--help"))    help  = true;
      else optList.Add(new TObjString(&(argv[i][2])));
    }
  }
  // --- check for help ----------------------------------------------
  if (help && cls.IsNull()) {
    if (cls.IsNull()) {
      Usage(argv[0], std::cout);
      return 0;
    }
    optList.Add(new TObjString("help"));
  }

  // --- Check name and class ----------------------------------------
  if (name.IsNull()) {
    Error("main", "No name specified");
    return 1;
  }
  if (cls.IsNull()) {
    Error("main", "No class specified");
    return 1;
  }

  // --- Setup script path -------------------------------------------
  const char* aliPath  = gSystem->ExpandPathName("$ALICE_ROOT");
  const char* fwdPath  = gSystem->ExpandPathName("$ALICE_ROOT/PWGLF/FORWARD/");
  AppendPath(aliPath);
  AppendPath(Form("%s/include",   aliPath));
  AppendPath(Form("%s/trains",    fwdPath));
  AppendPath(Form("%s/analysis2", fwdPath));

  // --- Set-up Application ------------------------------------------
  TApplication* app = 0;
  gROOT->SetBatch(true);
  if (!batch) {
    gROOT->SetBatch(false);
    app = new TGApplication("runTrain", 0, 0);
    app->InitializeGraphics();
  }

  // --- run, possibly in a timer ------------------------------------
  Bool_t ret = true;
  if (batch)
    ret = TrainSetup::Main(name, cls, &optList);
  else {
    new Deferred(name, cls, &optList);
    Info("main", "Running application (%s)", gROOT->IsBatch()
	 ? "batch" : "normal");
    gApplication->Run();
  }

  // --- Return ------------------------------------------------------
  return ret ? 0 : 1;
}
//
// EOF
//

<commit_msg>Can now specify URI in an optional way using --where, ...<commit_after>/*
  To compile, do

  rootcint -f OptionDict.C -c Option.C
  g++ `root-config --cflags --libs` \
    -lVMC -lGeom -lMinuit -lXMLIO -lTree -lTreePlayer \
    -I$ALICE_ROOT/include -L$ALICE_ROOT/lib/tgt_${ALICE_TARGET} \
    -lSTEERBase -lESD -lAOD -lANALYSIS -lOADB -lANALYSISalice \
    trainMain.cxx -o runTrain
*/

#include "AvailableSoftware.C"
#include "ChainBuilder.C"
#include "ParUtilities.C"
#include "OutputUtilities.C"
#include "Option.C"
#include "Helper.C"
#include "LocalHelper.C"
#include "ProofHelper.C"
#include "LiteHelper.C"
#include "AAFHelper.C"
#include "PluginHelper.C"
#include "AAFPluginHelper.C"
#include "GridHelper.C"
#include "TrainSetup.C"

#include <TGApplication.h>
#include <TROOT.h>
#include <TList.h>
#include <TObjString.h>
#include <TString.h>

#include <iostream>
#include <iomanip>

/**
 * Custom timer to do a deferred start after the application
 * has been started
 */
struct Deferred : public TTimer
{
  Deferred(const TString& name, const TString& cls,
	   const TCollection* opts)
    : TTimer(1000, false),
      fName(name),
      fClass(cls),
      fOptions(opts)
  {
    Start(1000, true);
  }
  Deferred(const Deferred& o)
    : TTimer(),
      fName(o.fName),
      fClass(o.fClass),
      fOptions(o.fOptions)
  {}
  Deferred& operator=(const Deferred& o)
  {
    if (&o == this) return *this;
    fName    = o.fName;
    fClass   = o.fClass;
    fOptions = o.fOptions;
    return *this;
  }
  Bool_t Notify()
  {
    // gSystem->RemoveTimer(this);
    Info("Notify", "Will run train setup: %s (%s)",
	 fName.Data(), fClass.Data());
    return TrainSetup::Main(fName, fClass, fOptions);
  }
  TString fName;
  TString fClass;
  const TCollection* fOptions;
};

/**
 * Append directory to header and script search path
 *
 * @param dir Directory
 *
 * @ingroup pwglf_forward_trains_run
 */
void AppendPath(const char* dir)
{
  gROOT->SetMacroPath(Form("%s:%s",gROOT->GetMacroPath(), dir));
  gSystem->AddIncludePath(Form("-I%s", dir));
}
/**
 * Print a fake option description.  Used for options specific to this
 * program.
 *
 * @param o    Output stream
 * @param opt  Option (including meta argument)
 * @param desc Option description.
 *
 * @ingroup pwglf_forward_trains_run
 */
void PrintFakeOption(std::ostream& o, const char* opt, const char* desc)
{
  o << "  --" << std::left << std::setw(30) << opt << " " << desc << std::endl;
}

/**
 * Print usage information
 *
 * @param progname Program name
 * @param o        Output stream
 * @param r        Optional runner.
 *
 * @ingroup pwglf_forward_trains_run
 */
void Usage(const char* progname, std::ostream& o)
{
  o << "Usage: " << progname
    << " --class=CLASS --name=NAME --url=URI [OPTIONS]\n\n"
    << "PROGRAM OPTIONS:\n";
  PrintFakeOption(o, "class=CLASS",       "Train class");
  PrintFakeOption(o, "name=NAME",         "Name of train");
  PrintFakeOption(o, "include=DIRECTORY", "Append dir to macro/header path");
  PrintFakeOption(o, "batch",             "Batch mode");
  PrintFakeOption(o, "url=URI",           "Execution URI");
  o << "\nAlternatively to using --url=URI, one can use\n";
  PrintFakeOption(o, "where=BASE_URI",    "Set protocol, user, host, "
		  "and port URI");
  PrintFakeOption(o, "file=FILE_OR_PATH", "File/path part of URI");
  PrintFakeOption(o, "options=OPTIONS",   "Query options for URI");
  PrintFakeOption(o, "anchor=ANCHOR",     "Query anchor for URI");

}

int
main(int argc, char** argv)
{
  TList optList;
  TString name;
  TString cls;
  TString where;
  TString file;
  TString opts;
  TString anchor;
  Bool_t  batch   = false;
  Bool_t  help    = false;
  Bool_t  urlSeen = false;

  // --- Parse options -----------------------------------------------
  for (int i = 1; i < argc; i++) {
    if (argv[i][0] == '-' && argv[i][1] == '-') {
      TString arg(argv[i]);
      TString val("");
      arg.ReplaceAll("\"'", "");
      Int_t   eq = arg.Index("=");
      if (eq != kNPOS) val = arg(eq+1, arg.Length()-eq-1);
      if      (arg.BeginsWith("--class"))   cls  = val;
      else if (arg.BeginsWith("--name"))    name = val;
      else if (arg.BeginsWith("--include")) AppendPath(val);
      else if (arg.BeginsWith("--batch"))   batch  = true;
      else if (arg.BeginsWith("--help"))    help   = true;
      else if (arg.BeginsWith("--where"))   where  = val;
      else if (arg.BeginsWith("--file"))    file   = val;
      else if (arg.BeginsWith("--opts"))    opts   = val;
      else if (arg.BeginsWith("--anchor"))  anchor = val;
      else {
	if (arg.BeginsWith("--url")) urlSeen = true;
	optList.Add(new TObjString(&(argv[i][2])));
      }
    }
  }
  // --- Initial check or URI/WHERE ----------------------------------
  if (!where.IsNull()) {
    if (urlSeen) {
      Error("main", "option --url and --where mutually exclusive");
      return 1;
    }
    TUrl u(where);
    if (!file.IsNull())   u.SetFile(file);
    if (!opts.IsNull())   u.SetOptions(opts);
    if (!anchor.IsNull()) u.SetAnchor(anchor);
    optList.Add(new TObjString(Form("url=%s", u.GetUrl())));
  }

  // --- check for help ----------------------------------------------
  if (help && cls.IsNull()) {
    if (cls.IsNull()) {
      Usage(argv[0], std::cout);
      return 0;
    }
    optList.Add(new TObjString("help"));
  }

  // --- Check name and class ----------------------------------------
  if (name.IsNull()) {
    Error("main", "No name specified");
    return 1;
  }
  if (cls.IsNull()) {
    Error("main", "No class specified");
    return 1;
  }

  // --- Setup script path -------------------------------------------
  const char* aliPath  = gSystem->ExpandPathName("$ALICE_ROOT");
  const char* fwdPath  = gSystem->ExpandPathName("$ALICE_ROOT/PWGLF/FORWARD/");
  AppendPath(aliPath);
  AppendPath(Form("%s/include",   aliPath));
  AppendPath(Form("%s/trains",    fwdPath));
  AppendPath(Form("%s/analysis2", fwdPath));

  // --- Set-up Application ------------------------------------------
  TApplication* app = 0;
  gROOT->SetBatch(true);
  if (!batch) {
    gROOT->SetBatch(false);
    app = new TGApplication("runTrain", 0, 0);
    app->InitializeGraphics();
  }

  // --- run, possibly in a timer ------------------------------------
  Bool_t ret = true;
  if (batch)
    ret = TrainSetup::Main(name, cls, &optList);
  else {
    new Deferred(name, cls, &optList);
    Info("main", "Running application (%s)", gROOT->IsBatch()
	 ? "batch" : "normal");
    gApplication->Run();
  }

  // --- Return ------------------------------------------------------
  return ret ? 0 : 1;
}
//
// EOF
//

<|endoftext|>


<commit_before>/*
 * Copyright (C) 2008-2010 The QXmpp developers
 *
 * Author:
 *  Jeremy Lainé
 *
 * Source:
 *  http://code.google.com/p/qxmpp
 *
 * This file is a part of QXmpp library.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 */

#include <QDomElement>
#include <QSslKey>
#include <QSslSocket>

#include "QXmppConstants.h"
#include "QXmppDialback.h"
#include "QXmppIncomingServer.h"
#include "QXmppOutgoingServer.h"
#include "QXmppStreamFeatures.h"
#include "QXmppUtils.h"

class QXmppIncomingServerPrivate
{
public:
    QString authenticated;
    QString domain;
    QString localStreamId;
};

/// Constructs a new incoming server stream.
///
/// \param socket The socket for the XMPP stream.
/// \param domain The local domain.
/// \param parent The parent QObject for the stream (optional).
///

QXmppIncomingServer::QXmppIncomingServer(QSslSocket *socket, const QString &domain, QObject *parent)
    : QXmppStream(parent),
    d(new QXmppIncomingServerPrivate)
{
    d->domain = domain;

    if (socket) {
        info(QString("Incoming server connection from %1 %2").arg(
            socket->peerAddress().toString(),
            QString::number(socket->peerPort())));
        setSocket(socket);
    }
}

/// Destroys the current stream.

QXmppIncomingServer::~QXmppIncomingServer()
{
    delete d;
}

/// Returns the stream's identifier.
///

QString QXmppIncomingServer::localStreamId() const
{
    return d->localStreamId;
}

void QXmppIncomingServer::handleStream(const QDomElement &streamElement)
{
    const QString from = streamElement.attribute("from");
    if (!from.isEmpty())
        info(QString("Incoming server stream from %1").arg(from));

    // start stream
    d->localStreamId = generateStanzaHash().toAscii();
    QString data = QString("<?xml version='1.0'?><stream:stream"
        " xmlns='%1' xmlns:db='%2' xmlns:stream='%3'"
        " id='%4' version=\"1.0\">").arg(
            ns_server,
            ns_server_dialback,
            ns_stream,
            d->localStreamId);
    sendData(data.toUtf8());

    // send stream features
    QXmppStreamFeatures features;
    if (!socket()->isEncrypted() && !socket()->localCertificate().isNull() && !socket()->privateKey().isNull())
        features.setTlsMode(QXmppStreamFeatures::Enabled);
    sendPacket(features);
}

void QXmppIncomingServer::handleStanza(const QDomElement &stanza)
{
    const QString ns = stanza.namespaceURI();

    if (ns == ns_tls && stanza.tagName() == "starttls")
    {
        sendData("<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
        socket()->flush();
        socket()->startServerEncryption();
        return;
    }
    else if (QXmppDialback::isDialback(stanza))
    {
        QXmppDialback request;
        request.parse(stanza);
        // check the request is valid
        if (!request.type().isEmpty() ||
            request.from().isEmpty() ||
            request.to() != d->domain ||
            request.key().isEmpty())
        {
            warning("Invalid dialback received");
            return;
        }

        const QString domain = request.from();
        if (request.command() == QXmppDialback::Result)
        {
            debug(QString("Received a dialback result from %1").arg(domain));

            // establish dialback connection
            QXmppOutgoingServer *stream = new QXmppOutgoingServer(d->domain, this);
            bool check = connect(stream, SIGNAL(dialbackResponseReceived(QXmppDialback)),
                                 this, SLOT(slotDialbackResponseReceived(QXmppDialback)));
            Q_ASSERT(check);
            Q_UNUSED(check);
            stream->setVerify(d->localStreamId, request.key());
            stream->connectToHost(domain);
        }
        else if (request.command() == QXmppDialback::Verify)
        {
            debug(QString("Received a dialback verify from %1").arg(domain));
            emit dialbackRequestReceived(request);
        }

    }
    else if (!d->authenticated.isEmpty() &&
             jidToDomain(stanza.attribute("from")) == d->authenticated)
    {
        // relay stanza if the remote party is authenticated
        emit elementReceived(stanza);
    } else {
        warning("Received an element, but remote party is not authenticated");
    }
}

/// Returns true if the socket is connected and the remote server is
/// authenticated.
///

bool QXmppIncomingServer::isConnected() const
{
    return QXmppStream::isConnected() && !d->authenticated.isEmpty();
}

/// Handles a dialback response received from the authority server.
///
/// \param response
///

void QXmppIncomingServer::slotDialbackResponseReceived(const QXmppDialback &dialback)
{
    QXmppOutgoingServer *stream = qobject_cast<QXmppOutgoingServer*>(sender());
    if (!stream ||
        dialback.command() != QXmppDialback::Verify ||
        dialback.id() != d->localStreamId ||
        dialback.from() != stream->remoteDomain())
        return;

    // relay verify response
    QXmppDialback response;
    response.setCommand(QXmppDialback::Result);
    response.setTo(dialback.from());
    response.setFrom(d->domain);
    response.setType(dialback.type());
    sendPacket(response);

    // check for success
    if (response.type() == "valid")
    {
        info("Incoming stream is ready");
        d->authenticated = dialback.from();
        emit connected();
    } else {
        disconnectFromHost();
    }

    // disconnect dialback
    stream->disconnectFromHost();
    stream->deleteLater();
}

<commit_msg>allow incoming s2s streams to authenticate for multiple domains (e.g. google.com hosted domains)<commit_after>/*
 * Copyright (C) 2008-2010 The QXmpp developers
 *
 * Author:
 *  Jeremy Lainé
 *
 * Source:
 *  http://code.google.com/p/qxmpp
 *
 * This file is a part of QXmpp library.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 */

#include <QDomElement>
#include <QSslKey>
#include <QSslSocket>

#include "QXmppConstants.h"
#include "QXmppDialback.h"
#include "QXmppIncomingServer.h"
#include "QXmppOutgoingServer.h"
#include "QXmppStreamFeatures.h"
#include "QXmppUtils.h"

class QXmppIncomingServerPrivate
{
public:
    QSet<QString> authenticated;
    QString domain;
    QString localStreamId;
};

/// Constructs a new incoming server stream.
///
/// \param socket The socket for the XMPP stream.
/// \param domain The local domain.
/// \param parent The parent QObject for the stream (optional).
///

QXmppIncomingServer::QXmppIncomingServer(QSslSocket *socket, const QString &domain, QObject *parent)
    : QXmppStream(parent),
    d(new QXmppIncomingServerPrivate)
{
    d->domain = domain;

    if (socket) {
        info(QString("Incoming server connection from %1 %2").arg(
            socket->peerAddress().toString(),
            QString::number(socket->peerPort())));
        setSocket(socket);
    }
}

/// Destroys the current stream.

QXmppIncomingServer::~QXmppIncomingServer()
{
    delete d;
}

/// Returns the stream's identifier.
///

QString QXmppIncomingServer::localStreamId() const
{
    return d->localStreamId;
}

void QXmppIncomingServer::handleStream(const QDomElement &streamElement)
{
    const QString from = streamElement.attribute("from");
    if (!from.isEmpty())
        info(QString("Incoming server stream from %1").arg(from));

    // start stream
    d->localStreamId = generateStanzaHash().toAscii();
    QString data = QString("<?xml version='1.0'?><stream:stream"
        " xmlns='%1' xmlns:db='%2' xmlns:stream='%3'"
        " id='%4' version=\"1.0\">").arg(
            ns_server,
            ns_server_dialback,
            ns_stream,
            d->localStreamId);
    sendData(data.toUtf8());

    // send stream features
    QXmppStreamFeatures features;
    if (!socket()->isEncrypted() && !socket()->localCertificate().isNull() && !socket()->privateKey().isNull())
        features.setTlsMode(QXmppStreamFeatures::Enabled);
    sendPacket(features);
}

void QXmppIncomingServer::handleStanza(const QDomElement &stanza)
{
    const QString ns = stanza.namespaceURI();

    if (ns == ns_tls && stanza.tagName() == "starttls")
    {
        sendData("<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
        socket()->flush();
        socket()->startServerEncryption();
        return;
    }
    else if (QXmppDialback::isDialback(stanza))
    {
        QXmppDialback request;
        request.parse(stanza);
        // check the request is valid
        if (!request.type().isEmpty() ||
            request.from().isEmpty() ||
            request.to() != d->domain ||
            request.key().isEmpty())
        {
            warning("Invalid dialback received");
            return;
        }

        const QString domain = request.from();
        if (request.command() == QXmppDialback::Result)
        {
            debug(QString("Received a dialback result from %1").arg(domain));

            // establish dialback connection
            QXmppOutgoingServer *stream = new QXmppOutgoingServer(d->domain, this);
            bool check = connect(stream, SIGNAL(dialbackResponseReceived(QXmppDialback)),
                                 this, SLOT(slotDialbackResponseReceived(QXmppDialback)));
            Q_ASSERT(check);
            Q_UNUSED(check);
            stream->setVerify(d->localStreamId, request.key());
            stream->connectToHost(domain);
        }
        else if (request.command() == QXmppDialback::Verify)
        {
            debug(QString("Received a dialback verify from %1").arg(domain));
            emit dialbackRequestReceived(request);
        }

    }
    else if (d->authenticated.contains(jidToDomain(stanza.attribute("from"))))
    {
        // relay stanza if the remote party is authenticated
        emit elementReceived(stanza);
    } else {
        warning(QString("Received an element from unverified domain %1").arg(jidToDomain(stanza.attribute("from"))));
        disconnectFromHost();
    }
}

/// Returns true if the socket is connected and the remote server is
/// authenticated.
///

bool QXmppIncomingServer::isConnected() const
{
    return QXmppStream::isConnected() && !d->authenticated.isEmpty();
}

/// Handles a dialback response received from the authority server.
///
/// \param response
///

void QXmppIncomingServer::slotDialbackResponseReceived(const QXmppDialback &dialback)
{
    QXmppOutgoingServer *stream = qobject_cast<QXmppOutgoingServer*>(sender());
    if (!stream ||
        dialback.command() != QXmppDialback::Verify ||
        dialback.id() != d->localStreamId ||
        dialback.from() != stream->remoteDomain())
        return;

    // relay verify response
    QXmppDialback response;
    response.setCommand(QXmppDialback::Result);
    response.setTo(dialback.from());
    response.setFrom(d->domain);
    response.setType(dialback.type());
    sendPacket(response);

    // check for success
    if (response.type() == "valid")
    {
        info(QString("Verified incoming domain %1").arg(dialback.from()));
        const bool wasConnected = !d->authenticated.isEmpty();
        d->authenticated.insert(dialback.from());
        if (!wasConnected)
            emit connected();
    } else {
        warning(QString("Failed to verify incoming domain %1").arg(dialback.from()));
        disconnectFromHost();
    }

    // disconnect dialback
    stream->disconnectFromHost();
    stream->deleteLater();
}

<|endoftext|>