<commit_before>/*

Copyright (c) 2006-2014, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include <algorithm>
#include <ctime>
#include <boost/crc.hpp>

#include "libtorrent/kademlia/node_id.hpp"
#include "libtorrent/kademlia/node_entry.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/assert.hpp"
#include "libtorrent/broadcast_socket.hpp" // for is_local et.al
#include "libtorrent/socket_io.hpp" // for hash_address
#include "libtorrent/random.hpp" // for random
#include "libtorrent/hasher.hpp" // for hasher

namespace libtorrent { namespace dht
{

// returns the distance between the two nodes
// using the kademlia XOR-metric
node_id distance(node_id const& n1, node_id const& n2)
{
	node_id ret;
	node_id::iterator k = ret.begin();
	for (node_id::const_iterator i = n1.begin(), j = n2.begin()
		, end(n1.end()); i != end; ++i, ++j, ++k)
	{
		*k = *i ^ *j;
	}
	return ret;
}

// returns true if: distance(n1, ref) < distance(n2, ref)
bool compare_ref(node_id const& n1, node_id const& n2, node_id const& ref)
{
	for (node_id::const_iterator i = n1.begin(), j = n2.begin()
		, k = ref.begin(), end(n1.end()); i != end; ++i, ++j, ++k)
	{
		boost::uint8_t lhs = (*i ^ *k);
		boost::uint8_t rhs = (*j ^ *k);
		if (lhs < rhs) return true;
		if (lhs > rhs) return false;
	}
	return false;
}

// returns n in: 2^n <= distance(n1, n2) < 2^(n+1)
// useful for finding out which bucket a node belongs to
int distance_exp(node_id const& n1, node_id const& n2)
{
	int byte = node_id::size - 1;
	for (node_id::const_iterator i = n1.begin(), j = n2.begin()
		, end(n1.end()); i != end; ++i, ++j, --byte)
	{
		TORRENT_ASSERT(byte >= 0);
		boost::uint8_t t = *i ^ *j;
		if (t == 0) continue;
		// we have found the first non-zero byte
		// return the bit-number of the first bit
		// that differs
		int bit = byte * 8;
		for (int b = 7; b >= 0; --b)
			if (t >= (1 << b)) return bit + b;
		return bit;
	}

	return 0;
}

node_id generate_id_impl(address const& ip_, boost::uint32_t r)
{
	boost::uint8_t* ip = 0;

	const static boost::uint8_t v4mask[] = { 0x03, 0x0f, 0x3f, 0xff };
	const static boost::uint8_t v6mask[] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };
	boost::uint8_t const* mask = 0;
	int num_octets = 0;

	address_v4::bytes_type b4;
#if TORRENT_USE_IPV6
	address_v6::bytes_type b6;
	if (ip_.is_v6())
	{
		b6 = ip_.to_v6().to_bytes();
		ip = &b6[0];
		num_octets = 8;
		mask = v6mask;
	}
	else
#endif
	{
		b4 = ip_.to_v4().to_bytes();
		ip = &b4[0];
		num_octets = 4;
		mask = v4mask;
	}

	for (int i = 0; i < num_octets; ++i)
		ip[i] &= mask[i];

	ip[0] |= (r & 0x7) << 5;

	// this is the crc32c (Castagnoli) polynomial
	// TODO: 2 this could be optimized if SSE 4.2 is
	// available. It could also be optimized given
	// that we have a fixed length
	boost::crc_optimal<32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true, true> crc;
	crc.process_block(ip, ip + num_octets);
	boost::uint32_t c = crc.checksum();
	node_id id;

	id[0] = (c >> 24) & 0xff;
	id[1] = (c >> 16) & 0xff;
	id[2] = ((c >> 8) & 0xf8) | (random() & 0x7);

	for (int i = 3; i < 19; ++i) id[i] = random() & 0xff;
	id[19] = r & 0xff;

	return id;
}

static boost::uint32_t secret = 0;

void make_id_secret(node_id& in)
{
	if (secret == 0) secret = (random() % 0xfffffffe) + 1;

	boost::uint32_t rand = random();

	// generate the last 4 bytes as a "signature" of the previous 4 bytes. This
	// lets us verify whether a hash came from this function or not in the future.
	hasher h((char*)&secret, 4);
	h.update((char*)&rand, 4);
	sha1_hash secret_hash = h.final();
	memcpy(&in[20-4], &secret_hash[0], 4);
	memcpy(&in[20-8], &rand, 4);
}

node_id generate_random_id()
{
	char r[20];
	for (int i = 0; i < 20; ++i) r[i] = random() & 0xff;
	return hasher(r, 20).final();
}

node_id generate_secret_id()
{
	node_id ret = generate_random_id();
	make_id_secret(ret);
	return ret;
}

bool verify_secret_id(node_id const& nid)
{
	if (secret == 0) return false;

	hasher h((char*)&secret, 4);
	h.update((char const*)&nid[20-8], 4);
	sha1_hash secret_hash = h.final();
	return memcmp(&nid[20-4], &secret_hash[0], 4) == 0;
}

// verifies whether a node-id matches the IP it's used from
// returns true if the node-id is OK coming from this source
// and false otherwise.
bool verify_id(node_id const& nid, address const& source_ip)
{
	// no need to verify local IPs, they would be incorrect anyway
	if (is_local(source_ip)) return true;

	node_id h = generate_id_impl(source_ip, nid[19]);
	return nid[0] == h[0] && nid[1] == h[1] && (nid[2] & 0xf8) == (h[2] & 0xf8);
}

node_id generate_id(address const& ip)
{
	return generate_id_impl(ip, random());
}

bool matching_prefix(node_entry const& n, int mask, int prefix, int bucket_index)
{
	node_id id = n.id;
	id <<= bucket_index + 1;
	return (id[0] & mask) == prefix;
}

node_id generate_prefix_mask(int bits)
{
	TORRENT_ASSERT(bits >= 0);
	TORRENT_ASSERT(bits <= 160);
	node_id mask(0);
	int b = 0;
	for (; b < bits - 7; b += 8) mask[b/8] |= 0xff;
	if (bits < 160) mask[b/8] |= (0xff << (8 - (bits&7))) & 0xff;
	return mask;
}

} }  // namespace libtorrent::dht

<commit_msg>use hardware optimized CRC32C function in DHT<commit_after>/*

Copyright (c) 2006-2014, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include <algorithm>
#include <ctime>

#include "libtorrent/kademlia/node_id.hpp"
#include "libtorrent/kademlia/node_entry.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/assert.hpp"
#include "libtorrent/broadcast_socket.hpp" // for is_local et.al
#include "libtorrent/socket_io.hpp" // for hash_address
#include "libtorrent/random.hpp" // for random
#include "libtorrent/hasher.hpp" // for hasher
#include "libtorrent/crc32c.hpp" // for crc32c

namespace libtorrent { namespace dht
{

// returns the distance between the two nodes
// using the kademlia XOR-metric
node_id distance(node_id const& n1, node_id const& n2)
{
	node_id ret;
	node_id::iterator k = ret.begin();
	for (node_id::const_iterator i = n1.begin(), j = n2.begin()
		, end(n1.end()); i != end; ++i, ++j, ++k)
	{
		*k = *i ^ *j;
	}
	return ret;
}

// returns true if: distance(n1, ref) < distance(n2, ref)
bool compare_ref(node_id const& n1, node_id const& n2, node_id const& ref)
{
	for (node_id::const_iterator i = n1.begin(), j = n2.begin()
		, k = ref.begin(), end(n1.end()); i != end; ++i, ++j, ++k)
	{
		boost::uint8_t lhs = (*i ^ *k);
		boost::uint8_t rhs = (*j ^ *k);
		if (lhs < rhs) return true;
		if (lhs > rhs) return false;
	}
	return false;
}

// returns n in: 2^n <= distance(n1, n2) < 2^(n+1)
// useful for finding out which bucket a node belongs to
int distance_exp(node_id const& n1, node_id const& n2)
{
	int byte = node_id::size - 1;
	for (node_id::const_iterator i = n1.begin(), j = n2.begin()
		, end(n1.end()); i != end; ++i, ++j, --byte)
	{
		TORRENT_ASSERT(byte >= 0);
		boost::uint8_t t = *i ^ *j;
		if (t == 0) continue;
		// we have found the first non-zero byte
		// return the bit-number of the first bit
		// that differs
		int bit = byte * 8;
		for (int b = 7; b >= 0; --b)
			if (t >= (1 << b)) return bit + b;
		return bit;
	}

	return 0;
}

node_id generate_id_impl(address const& ip_, boost::uint32_t r)
{
	boost::uint8_t* ip = 0;

	const static boost::uint8_t v4mask[] = { 0x03, 0x0f, 0x3f, 0xff };
	const static boost::uint8_t v6mask[] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };
	boost::uint8_t const* mask = 0;
	int num_octets = 0;

	address_v4::bytes_type b4;
#if TORRENT_USE_IPV6
	address_v6::bytes_type b6;
	if (ip_.is_v6())
	{
		b6 = ip_.to_v6().to_bytes();
		ip = &b6[0];
		num_octets = 8;
		mask = v6mask;
	}
	else
#endif
	{
		b4 = ip_.to_v4().to_bytes();
		ip = &b4[0];
		num_octets = 4;
		mask = v4mask;
	}

	for (int i = 0; i < num_octets; ++i)
		ip[i] &= mask[i];

	ip[0] |= (r & 0x7) << 5;

	// this is the crc32c (Castagnoli) polynomial
	boost::uint32_t c;
	if (num_octets == 4)
	{
		c = crc32c_32(*reinterpret_cast<boost::uint32_t*>(ip));
	}
	else
	{
		TORRENT_ASSERT(num_octets == 8);
		c = crc32c(reinterpret_cast<boost::uint64_t*>(ip), 1);
	}
	node_id id;

	id[0] = (c >> 24) & 0xff;
	id[1] = (c >> 16) & 0xff;
	id[2] = ((c >> 8) & 0xf8) | (random() & 0x7);

	for (int i = 3; i < 19; ++i) id[i] = random() & 0xff;
	id[19] = r & 0xff;

	return id;
}

static boost::uint32_t secret = 0;

void make_id_secret(node_id& in)
{
	if (secret == 0) secret = (random() % 0xfffffffe) + 1;

	boost::uint32_t rand = random();

	// generate the last 4 bytes as a "signature" of the previous 4 bytes. This
	// lets us verify whether a hash came from this function or not in the future.
	hasher h((char*)&secret, 4);
	h.update((char*)&rand, 4);
	sha1_hash secret_hash = h.final();
	memcpy(&in[20-4], &secret_hash[0], 4);
	memcpy(&in[20-8], &rand, 4);
}

node_id generate_random_id()
{
	char r[20];
	for (int i = 0; i < 20; ++i) r[i] = random() & 0xff;
	return hasher(r, 20).final();
}

node_id generate_secret_id()
{
	node_id ret = generate_random_id();
	make_id_secret(ret);
	return ret;
}

bool verify_secret_id(node_id const& nid)
{
	if (secret == 0) return false;

	hasher h((char*)&secret, 4);
	h.update((char const*)&nid[20-8], 4);
	sha1_hash secret_hash = h.final();
	return memcmp(&nid[20-4], &secret_hash[0], 4) == 0;
}

// verifies whether a node-id matches the IP it's used from
// returns true if the node-id is OK coming from this source
// and false otherwise.
bool verify_id(node_id const& nid, address const& source_ip)
{
	// no need to verify local IPs, they would be incorrect anyway
	if (is_local(source_ip)) return true;

	node_id h = generate_id_impl(source_ip, nid[19]);
	return nid[0] == h[0] && nid[1] == h[1] && (nid[2] & 0xf8) == (h[2] & 0xf8);
}

node_id generate_id(address const& ip)
{
	return generate_id_impl(ip, random());
}

bool matching_prefix(node_entry const& n, int mask, int prefix, int bucket_index)
{
	node_id id = n.id;
	id <<= bucket_index + 1;
	return (id[0] & mask) == prefix;
}

node_id generate_prefix_mask(int bits)
{
	TORRENT_ASSERT(bits >= 0);
	TORRENT_ASSERT(bits <= 160);
	node_id mask(0);
	int b = 0;
	for (; b < bits - 7; b += 8) mask[b/8] |= 0xff;
	if (bits < 160) mask[b/8] |= (0xff << (8 - (bits&7))) & 0xff;
	return mask;
}

} }  // namespace libtorrent::dht

<|endoftext|>


<commit_before>/*

Copyright (c) 2006, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/pch.hpp"

#include <algorithm>
#include <ctime>

#include "libtorrent/kademlia/node_id.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/assert.hpp"
#include "libtorrent/broadcast_socket.hpp" // for is_local et.al
#include "libtorrent/socket_io.hpp" // for hash_address
#include "libtorrent/random.hpp" // for random

namespace libtorrent { namespace dht
{

// returns the distance between the two nodes
// using the kademlia XOR-metric
node_id distance(node_id const& n1, node_id const& n2)
{
	node_id ret;
	node_id::iterator k = ret.begin();
	for (node_id::const_iterator i = n1.begin(), j = n2.begin()
		, end(n1.end()); i != end; ++i, ++j, ++k)
	{
		*k = *i ^ *j;
	}
	return ret;
}

// returns true if: distance(n1, ref) < distance(n2, ref)
bool compare_ref(node_id const& n1, node_id const& n2, node_id const& ref)
{
	for (node_id::const_iterator i = n1.begin(), j = n2.begin()
		, k = ref.begin(), end(n1.end()); i != end; ++i, ++j, ++k)
	{
		boost::uint8_t lhs = (*i ^ *k);
		boost::uint8_t rhs = (*j ^ *k);
		if (lhs < rhs) return true;
		if (lhs > rhs) return false;
	}
	return false;
}

// returns n in: 2^n <= distance(n1, n2) < 2^(n+1)
// useful for finding out which bucket a node belongs to
int distance_exp(node_id const& n1, node_id const& n2)
{
	int byte = node_id::size - 1;
	for (node_id::const_iterator i = n1.begin(), j = n2.begin()
		, end(n1.end()); i != end; ++i, ++j, --byte)
	{
		TORRENT_ASSERT(byte >= 0);
		boost::uint8_t t = *i ^ *j;
		if (t == 0) continue;
		// we have found the first non-zero byte
		// return the bit-number of the first bit
		// that differs
		int bit = byte * 8;
		for (int b = 7; b >= 0; --b)
			if (t >= (1 << b)) return bit + b;
		return bit;
	}

	return 0;
}

struct static_ { static_() { std::srand((unsigned int)std::time(0)); } } static__;

node_id generate_id_impl(address const& ip_, boost::uint32_t r)
{
	boost::uint8_t* ip = 0;

	const static boost::uint8_t v4mask[] = { 0x03, 0x0f, 0x3f, 0xff };
	const static boost::uint8_t v6mask[] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };
	boost::uint8_t const* mask = 0;
	int num_octets = 0;

	address_v4::bytes_type b4;
#if TORRENT_USE_IPV6
	address_v6::bytes_type b6;
	if (ip_.is_v6())
	{
		b6 = ip_.to_v6().to_bytes();
		ip = &b6[0];
		num_octets = 8;
		mask = v6mask;
	}
	else
#endif
	{
		b4 = ip_.to_v4().to_bytes();
		ip = &b4[0];
		num_octets = 4;
		mask = v4mask;
	}

	for (int i = 0; i < num_octets; ++i)
		ip[i] &= mask[i];

	hasher h;
	h.update((char*)ip, num_octets);
	boost::uint8_t rand = r & 0x7;
	h.update((char*)&r, 1);
	node_id id = h.final();
	for (int i = 4; i < 19; ++i) id[i] = random();
	id[19] = r;

	return id;
}

node_id generate_random_id()
{
	char r[20];
	for (int i = 0; i < 20; ++i) r[i] = random();
	return hasher(r, 20).final();
}

// verifies whether a node-id matches the IP it's used from
// returns true if the node-id is OK coming from this source
// and false otherwise.
bool verify_id(node_id const& nid, address const& source_ip)
{
	// no need to verify local IPs, they would be incorrect anyway
	if (is_local(source_ip)) return true;

	node_id h = generate_id_impl(source_ip, nid[19]);
	return memcmp(&nid[0], &h[0], 4) == 0;
}

node_id generate_id(address const& ip)
{
	return generate_id_impl(ip, random());
}

} }  // namespace libtorrent::dht

<commit_msg>and the typo in trunk as well<commit_after>/*

Copyright (c) 2006, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/pch.hpp"

#include <algorithm>
#include <ctime>

#include "libtorrent/kademlia/node_id.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/assert.hpp"
#include "libtorrent/broadcast_socket.hpp" // for is_local et.al
#include "libtorrent/socket_io.hpp" // for hash_address
#include "libtorrent/random.hpp" // for random

namespace libtorrent { namespace dht
{

// returns the distance between the two nodes
// using the kademlia XOR-metric
node_id distance(node_id const& n1, node_id const& n2)
{
	node_id ret;
	node_id::iterator k = ret.begin();
	for (node_id::const_iterator i = n1.begin(), j = n2.begin()
		, end(n1.end()); i != end; ++i, ++j, ++k)
	{
		*k = *i ^ *j;
	}
	return ret;
}

// returns true if: distance(n1, ref) < distance(n2, ref)
bool compare_ref(node_id const& n1, node_id const& n2, node_id const& ref)
{
	for (node_id::const_iterator i = n1.begin(), j = n2.begin()
		, k = ref.begin(), end(n1.end()); i != end; ++i, ++j, ++k)
	{
		boost::uint8_t lhs = (*i ^ *k);
		boost::uint8_t rhs = (*j ^ *k);
		if (lhs < rhs) return true;
		if (lhs > rhs) return false;
	}
	return false;
}

// returns n in: 2^n <= distance(n1, n2) < 2^(n+1)
// useful for finding out which bucket a node belongs to
int distance_exp(node_id const& n1, node_id const& n2)
{
	int byte = node_id::size - 1;
	for (node_id::const_iterator i = n1.begin(), j = n2.begin()
		, end(n1.end()); i != end; ++i, ++j, --byte)
	{
		TORRENT_ASSERT(byte >= 0);
		boost::uint8_t t = *i ^ *j;
		if (t == 0) continue;
		// we have found the first non-zero byte
		// return the bit-number of the first bit
		// that differs
		int bit = byte * 8;
		for (int b = 7; b >= 0; --b)
			if (t >= (1 << b)) return bit + b;
		return bit;
	}

	return 0;
}

struct static_ { static_() { std::srand((unsigned int)std::time(0)); } } static__;

node_id generate_id_impl(address const& ip_, boost::uint32_t r)
{
	boost::uint8_t* ip = 0;

	const static boost::uint8_t v4mask[] = { 0x03, 0x0f, 0x3f, 0xff };
	const static boost::uint8_t v6mask[] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };
	boost::uint8_t const* mask = 0;
	int num_octets = 0;

	address_v4::bytes_type b4;
#if TORRENT_USE_IPV6
	address_v6::bytes_type b6;
	if (ip_.is_v6())
	{
		b6 = ip_.to_v6().to_bytes();
		ip = &b6[0];
		num_octets = 8;
		mask = v6mask;
	}
	else
#endif
	{
		b4 = ip_.to_v4().to_bytes();
		ip = &b4[0];
		num_octets = 4;
		mask = v4mask;
	}

	for (int i = 0; i < num_octets; ++i)
		ip[i] &= mask[i];

	hasher h;
	h.update((char*)ip, num_octets);
	boost::uint8_t rand = r & 0x7;
	h.update((char*)&rand, 1);
	node_id id = h.final();
	for (int i = 4; i < 19; ++i) id[i] = random();
	id[19] = r;

	return id;
}

node_id generate_random_id()
{
	char r[20];
	for (int i = 0; i < 20; ++i) r[i] = random();
	return hasher(r, 20).final();
}

// verifies whether a node-id matches the IP it's used from
// returns true if the node-id is OK coming from this source
// and false otherwise.
bool verify_id(node_id const& nid, address const& source_ip)
{
	// no need to verify local IPs, they would be incorrect anyway
	if (is_local(source_ip)) return true;

	node_id h = generate_id_impl(source_ip, nid[19]);
	return memcmp(&nid[0], &h[0], 4) == 0;
}

node_id generate_id(address const& ip)
{
	return generate_id_impl(ip, random());
}

} }  // namespace libtorrent::dht

<|endoftext|>


<commit_before>/*
 * Copyright 2018-present Facebook, 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 <folly/hash/detail/ChecksumDetail.h>

#include <array>

#include <folly/Bits.h>
#include <folly/ConstexprMath.h>

namespace folly {

// Standard galois-field multiply.  The only modification is that a,
// b, m, and p are all bit-reflected.
//
// https://en.wikipedia.org/wiki/Finite_field_arithmetic
static constexpr uint32_t
gf_multiply_sw_1(size_t i, uint32_t p, uint32_t a, uint32_t b, uint32_t m) {
  // clang-format off
  return i == 32 ? p : gf_multiply_sw_1(
      /* i = */ i + 1,
      /* p = */ p ^ (-((b >> 31) & 1) & a),
      /* a = */ (a >> 1) ^ (-(a & 1) & m),
      /* b = */ b << 1,
      /* m = */ m);
  // clang-format on
}
static constexpr uint32_t gf_multiply_sw(uint32_t a, uint32_t b, uint32_t m) {
  return gf_multiply_sw_1(/* i = */ 0, /* p = */ 0, a, b, m);
}

static constexpr uint32_t gf_square_sw(uint32_t a, uint32_t m) {
  return gf_multiply_sw(a, a, m);
}

namespace {

template <size_t i, uint32_t m>
struct gf_powers_memo {
  static constexpr uint32_t value =
      gf_square_sw(gf_powers_memo<i - 1, m>::value, m);
};
template <uint32_t m>
struct gf_powers_memo<0, m> {
  static constexpr uint32_t value = m;
};

template <uint32_t m>
struct gf_powers_make {
  template <size_t... i>
  constexpr auto operator()(index_sequence<i...>) const {
    return std::array<uint32_t, sizeof...(i)>{{gf_powers_memo<i, m>::value...}};
  }
};

} // namespace

#if FOLLY_SSE_PREREQ(4, 2)

// Reduction taken from
// https://www.nicst.de/crc.pdf
//
// This is an intrinsics-based implementation of listing 3.
static uint32_t gf_multiply_crc32c_hw(uint64_t crc1, uint64_t crc2, uint32_t) {
  const auto crc1_xmm = _mm_set_epi64x(0, crc1);
  const auto crc2_xmm = _mm_set_epi64x(0, crc2);
  const auto count = _mm_set_epi64x(0, 1);
  const auto res0 = _mm_clmulepi64_si128(crc2_xmm, crc1_xmm, 0x00);
  const auto res1 = _mm_sll_epi64(res0, count);

  // Use hardware crc32c to do reduction from 64 -> 32 bytes
  const auto res2 = _mm_cvtsi128_si64(res1);
  const auto res3 = _mm_crc32_u32(0, res2);
  const auto res4 = _mm_extract_epi32(res1, 1);
  return res3 ^ res4;
}

static uint32_t gf_multiply_crc32_hw(uint64_t crc1, uint64_t crc2, uint32_t) {
  const auto crc1_xmm = _mm_set_epi64x(0, crc1);
  const auto crc2_xmm = _mm_set_epi64x(0, crc2);
  const auto count = _mm_set_epi64x(0, 1);
  const auto res0 = _mm_clmulepi64_si128(crc2_xmm, crc1_xmm, 0x00);
  const auto res1 = _mm_sll_epi64(res0, count);

  // Do barrett reduction of 64 -> 32 bytes
  const auto mask32 = _mm_set_epi32(0, 0, 0, 0xFFFFFFFF);
  const auto barrett_reduction_constants =
      _mm_set_epi32(0x1, 0xDB710641, 0x1, 0xF7011641);
  const auto res2 = _mm_clmulepi64_si128(
      _mm_and_si128(res1, mask32), barrett_reduction_constants, 0x00);
  const auto res3 = _mm_clmulepi64_si128(
      _mm_and_si128(res2, mask32), barrett_reduction_constants, 0x10);
  return _mm_cvtsi128_si32(_mm_srli_si128(_mm_xor_si128(res3, res1), 4));
}

#else

static uint32_t gf_multiply_crc32c_hw(uint64_t, uint64_t, uint32_t) {
  return 0;
}
static uint32_t gf_multiply_crc32_hw(uint64_t, uint64_t, uint32_t) {
  return 0;
}

#endif

/*
 * Pre-calculated powers tables for crc32c and crc32.
 */
static constexpr std::array<uint32_t, 62> const crc32c_powers =
    gf_powers_make<0x82f63b78>{}(make_index_sequence<62>{});
static constexpr std::array<uint32_t, 62> const crc32_powers =
    gf_powers_make<0xedb88320>{}(make_index_sequence<62>{});

template <typename F>
static uint32_t crc32_append_zeroes(
    F mult,
    uint32_t crc,
    size_t len,
    uint32_t polynomial,
    std::array<uint32_t, 62> const& powers_array) {
  auto powers = powers_array.data();

  // Append by multiplying by consecutive powers of two of the zeroes
  // array
  len >>= 2;

  while (len) {
    // Advance directly to next bit set.
    auto r = findFirstSet(len) - 1;
    len >>= r;
    powers += r;

    crc = mult(crc, *powers, polynomial);

    len >>= 1;
    powers++;
  }

  return crc;
}

namespace detail {

uint32_t crc32_combine_sw(uint32_t crc1, uint32_t crc2, size_t crc2len) {
  return crc2 ^
      crc32_append_zeroes(
             gf_multiply_sw, crc1, crc2len, 0xEDB88320, crc32_powers);
}

uint32_t crc32_combine_hw(uint32_t crc1, uint32_t crc2, size_t crc2len) {
  return crc2 ^
      crc32_append_zeroes(
             gf_multiply_crc32_hw, crc1, crc2len, 0xEDB88320, crc32_powers);
}

uint32_t crc32c_combine_sw(uint32_t crc1, uint32_t crc2, size_t crc2len) {
  return crc2 ^
      crc32_append_zeroes(
             gf_multiply_sw, crc1, crc2len, 0x82F63B78, crc32c_powers);
}

uint32_t crc32c_combine_hw(uint32_t crc1, uint32_t crc2, size_t crc2len) {
  return crc2 ^
      crc32_append_zeroes(
             gf_multiply_crc32c_hw, crc1, crc2len, 0x82F63B78, crc32c_powers);
}

} // namespace detail

} // namespace folly
<commit_msg>Lift some crc magic constants<commit_after>/*
 * Copyright 2018-present Facebook, 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 <folly/hash/detail/ChecksumDetail.h>

#include <array>

#include <folly/Bits.h>
#include <folly/ConstexprMath.h>

namespace folly {

// Standard galois-field multiply.  The only modification is that a,
// b, m, and p are all bit-reflected.
//
// https://en.wikipedia.org/wiki/Finite_field_arithmetic
static constexpr uint32_t
gf_multiply_sw_1(size_t i, uint32_t p, uint32_t a, uint32_t b, uint32_t m) {
  // clang-format off
  return i == 32 ? p : gf_multiply_sw_1(
      /* i = */ i + 1,
      /* p = */ p ^ (-((b >> 31) & 1) & a),
      /* a = */ (a >> 1) ^ (-(a & 1) & m),
      /* b = */ b << 1,
      /* m = */ m);
  // clang-format on
}
static constexpr uint32_t gf_multiply_sw(uint32_t a, uint32_t b, uint32_t m) {
  return gf_multiply_sw_1(/* i = */ 0, /* p = */ 0, a, b, m);
}

static constexpr uint32_t gf_square_sw(uint32_t a, uint32_t m) {
  return gf_multiply_sw(a, a, m);
}

namespace {

template <size_t i, uint32_t m>
struct gf_powers_memo {
  static constexpr uint32_t value =
      gf_square_sw(gf_powers_memo<i - 1, m>::value, m);
};
template <uint32_t m>
struct gf_powers_memo<0, m> {
  static constexpr uint32_t value = m;
};

template <uint32_t m>
struct gf_powers_make {
  template <size_t... i>
  constexpr auto operator()(index_sequence<i...>) const {
    return std::array<uint32_t, sizeof...(i)>{{gf_powers_memo<i, m>::value...}};
  }
};

} // namespace

#if FOLLY_SSE_PREREQ(4, 2)

// Reduction taken from
// https://www.nicst.de/crc.pdf
//
// This is an intrinsics-based implementation of listing 3.
static uint32_t gf_multiply_crc32c_hw(uint64_t crc1, uint64_t crc2, uint32_t) {
  const auto crc1_xmm = _mm_set_epi64x(0, crc1);
  const auto crc2_xmm = _mm_set_epi64x(0, crc2);
  const auto count = _mm_set_epi64x(0, 1);
  const auto res0 = _mm_clmulepi64_si128(crc2_xmm, crc1_xmm, 0x00);
  const auto res1 = _mm_sll_epi64(res0, count);

  // Use hardware crc32c to do reduction from 64 -> 32 bytes
  const auto res2 = _mm_cvtsi128_si64(res1);
  const auto res3 = _mm_crc32_u32(0, res2);
  const auto res4 = _mm_extract_epi32(res1, 1);
  return res3 ^ res4;
}

static uint32_t gf_multiply_crc32_hw(uint64_t crc1, uint64_t crc2, uint32_t) {
  const auto crc1_xmm = _mm_set_epi64x(0, crc1);
  const auto crc2_xmm = _mm_set_epi64x(0, crc2);
  const auto count = _mm_set_epi64x(0, 1);
  const auto res0 = _mm_clmulepi64_si128(crc2_xmm, crc1_xmm, 0x00);
  const auto res1 = _mm_sll_epi64(res0, count);

  // Do barrett reduction of 64 -> 32 bytes
  const auto mask32 = _mm_set_epi32(0, 0, 0, 0xFFFFFFFF);
  const auto barrett_reduction_constants =
      _mm_set_epi32(0x1, 0xDB710641, 0x1, 0xF7011641);
  const auto res2 = _mm_clmulepi64_si128(
      _mm_and_si128(res1, mask32), barrett_reduction_constants, 0x00);
  const auto res3 = _mm_clmulepi64_si128(
      _mm_and_si128(res2, mask32), barrett_reduction_constants, 0x10);
  return _mm_cvtsi128_si32(_mm_srli_si128(_mm_xor_si128(res3, res1), 4));
}

#else

static uint32_t gf_multiply_crc32c_hw(uint64_t, uint64_t, uint32_t) {
  return 0;
}
static uint32_t gf_multiply_crc32_hw(uint64_t, uint64_t, uint32_t) {
  return 0;
}

#endif

static constexpr uint32_t crc32c_m = 0x82f63b78;
static constexpr uint32_t crc32_m = 0xedb88320;

/*
 * Pre-calculated powers tables for crc32c and crc32.
 */
static constexpr std::array<uint32_t, 62> const crc32c_powers =
    gf_powers_make<crc32c_m>{}(make_index_sequence<62>{});
static constexpr std::array<uint32_t, 62> const crc32_powers =
    gf_powers_make<crc32_m>{}(make_index_sequence<62>{});

template <typename F>
static uint32_t crc32_append_zeroes(
    F mult,
    uint32_t crc,
    size_t len,
    uint32_t polynomial,
    std::array<uint32_t, 62> const& powers_array) {
  auto powers = powers_array.data();

  // Append by multiplying by consecutive powers of two of the zeroes
  // array
  len >>= 2;

  while (len) {
    // Advance directly to next bit set.
    auto r = findFirstSet(len) - 1;
    len >>= r;
    powers += r;

    crc = mult(crc, *powers, polynomial);

    len >>= 1;
    powers++;
  }

  return crc;
}

namespace detail {

uint32_t crc32_combine_sw(uint32_t crc1, uint32_t crc2, size_t crc2len) {
  return crc2 ^
      crc32_append_zeroes(gf_multiply_sw, crc1, crc2len, crc32_m, crc32_powers);
}

uint32_t crc32_combine_hw(uint32_t crc1, uint32_t crc2, size_t crc2len) {
  return crc2 ^
      crc32_append_zeroes(
             gf_multiply_crc32_hw, crc1, crc2len, crc32_m, crc32_powers);
}

uint32_t crc32c_combine_sw(uint32_t crc1, uint32_t crc2, size_t crc2len) {
  return crc2 ^
      crc32_append_zeroes(
             gf_multiply_sw, crc1, crc2len, crc32c_m, crc32c_powers);
}

uint32_t crc32c_combine_hw(uint32_t crc1, uint32_t crc2, size_t crc2len) {
  return crc2 ^
      crc32_append_zeroes(
             gf_multiply_crc32c_hw, crc1, crc2len, crc32c_m, crc32c_powers);
}

} // namespace detail

} // namespace folly
<|endoftext|>


<commit_before>/*
 * LiveViewUnitTest.cpp
 *
 *  Created on: 28 Sep 2018
 *      Author: Adam Neaves - wbd45595
 */

#include <boost/test/unit_test.hpp>

#include "LiveViewPlugin.h"

/**
 * Test fixture for the Live View Unit Tests. sets up the plugin and other things required by the tests.
 */
class LiveViewPluginTestFixture
{
public:
  LiveViewPluginTestFixture():
    recv_socket(ZMQ_SUB),
    recv_socket_other(ZMQ_SUB)
  {

    //create dummy data for the test frames
    for(int i = 0; i < 12; i++)
    {
      img_8[i] = i+1;
      img_16[i] = i+1;
    }

    DATA_TYPES[0]  = "uint8";
    DATA_TYPES[1]  = "uint16";
    DATA_TYPES[2]  = "uint32";

    COMPRESS_TYPES[0] = "none";
    COMPRESS_TYPES[1] = "LZ4";
    COMPRESS_TYPES[2] = "BSLZ4";

    dimensions_t img_dims(2); img_dims[0] = 3; img_dims[1] = 4;

    //create test frame
    frame = boost::shared_ptr<FrameProcessor::Frame>(new FrameProcessor::Frame("data"));
    frame->set_frame_number(2);
    frame->set_dimensions(img_dims);
    frame->set_data_type(FrameProcessor::raw_8bit);
    frame->set_compression(0);
    frame->copy_data(static_cast<void*>(img_8), 12);

    //create test frame with uint16 data
    frame_16 = boost::shared_ptr<FrameProcessor::Frame>(new FrameProcessor::Frame("data"));
    frame_16->set_frame_number(2);
    frame_16->set_dimensions(img_dims);
    frame_16->set_data_type(FrameProcessor::raw_16bit);
    frame_16->set_compression(0);
    frame_16->copy_data(static_cast<void*>(img_16), 24);

    //create multiple test frames
    for(int i = 0; i < 10; i++)
    {
      boost::shared_ptr<FrameProcessor::Frame> tmp_frame(new FrameProcessor::Frame("data"));
      tmp_frame->set_frame_number(i);
      tmp_frame->set_dimensions(img_dims);
      tmp_frame->set_data_type(FrameProcessor::raw_8bit);
      tmp_frame->set_dataset_name(i % 4 ? "data" : "not_data");

      tmp_frame->copy_data(static_cast<void*>(img_8), 12);

      frames.push_back(tmp_frame);
    }

    new_socket_recieve = false;

    //set up the recieve sockets so we can read data from the plugin's live output.
    recv_socket.subscribe("");
    recv_socket_other.subscribe("");
    recv_socket.connect("tcp://127.0.0.1:5020");
    recv_socket_other.connect("tcp://127.0.0.1:5021");

  }
  ~LiveViewPluginTestFixture() {
    BOOST_TEST_MESSAGE("Live View Fixture Teardown");
    recv_socket.close();
    recv_socket_other.close();
  }

  boost::shared_ptr<FrameProcessor::Frame> frame;
  boost::shared_ptr<FrameProcessor::Frame> frame_16;
  std::vector< boost::shared_ptr<FrameProcessor::Frame> >frames;

  uint8_t img_8[12];
  uint16_t img_16[12];

  uint8_t  pbuf[12]; //create basic array to store data
  uint16_t pbuf_16[12];

  std::map<int, std::string> DATA_TYPES;
  std::map<int, std::string> COMPRESS_TYPES;

  bool new_socket_recieve;
  std::string message;

  OdinData::IpcChannel recv_socket;
  OdinData::IpcChannel recv_socket_other;
  FrameProcessor::LiveViewPlugin plugin;
  OdinData::IpcMessage reply;
  OdinData::IpcMessage cfg;
  rapidjson::Document doc;

};

BOOST_FIXTURE_TEST_SUITE(LiveViewPluginUnitTest, LiveViewPluginTestFixture);

/**
 * tests the basic functionality, passing the plugin a single frame and seeing if it appears the same on the other end of the socket
 */
BOOST_AUTO_TEST_CASE(LiveViewBasicSendTest)
{
  //setting the frame number here guarantees that it'll be passed to the live view socket
  frame->set_frame_number(FrameProcessor::LiveViewPlugin::DEFAULT_FRAME_FREQ);
  //test we can output frame
  while(!recv_socket.poll(100)) //to avoid issue with slow subscribers, keep sending the frame until the subscriber has received it
  {
    //send the frame to the plugin
    plugin.process_frame(frame);
  }
  message = recv_socket.recv();
  BOOST_TEST_MESSAGE(message);
  doc.Parse(message.c_str());

  //TEST HEADER CONTENTS
  BOOST_CHECK_EQUAL(doc["frame_num"].GetInt(), frame->get_frame_number());
  BOOST_CHECK_EQUAL(doc["acquisition_id"].GetString(), frame->get_acquisition_id());
  BOOST_CHECK_EQUAL(doc["dtype"].GetString(), DATA_TYPES[frame->get_data_type()]);
  BOOST_CHECK_EQUAL(doc["dsize"].GetInt() , frame->get_data_size());
  BOOST_CHECK_EQUAL(doc["compression"].GetString(), COMPRESS_TYPES[frame->get_compression()]);
  char *end;
  BOOST_CHECK_EQUAL(strtol(doc["shape"][0].GetString(), &end, 10), frame->get_dimensions()[0]);
  BOOST_CHECK_EQUAL(strtol(doc["shape"][1].GetString(), &end, 10), frame->get_dimensions()[1]);

  //TEST DATA CONTENTS
  recv_socket.recv_raw(&pbuf); //put data into arary
  std::vector<uint8_t> buf(frame->get_data_size()); //create vector of size of data
  std::copy(pbuf, pbuf + frame->get_data_size(), buf.begin()); //copy data from array to vector, so it can be compared
  std::vector<uint8_t> original(img_8, img_8 + sizeof img_8 / sizeof img_8[0]);
  BOOST_CHECK_EQUAL_COLLECTIONS(buf.begin(), buf.end() , original.begin(), original.end());

}

/**
 * tests to make sure the downscale factor works. Checks that only those frames with the needed frame number are passed to the live view socket
 */
BOOST_AUTO_TEST_CASE(LiveViewDownscaleTest)
{

  //TEST DOWNSCALE OPTION
  while(recv_socket.poll(10))
  {
    recv_socket.recv(); //clear any extra data from the socket. Also gives the recieve socket time to init
  }
  //process all frames. with a downscale factor of 2, this should return all the even numbered frames.
  for(int i = 0; i < frames.size(); i++)
  {
    plugin.process_frame(frames[i]);
  }

  std::vector<std::string> processed_frames;
  while(recv_socket.poll(10))
  {
    std::string tmp_string = recv_socket.recv();
    processed_frames.push_back(tmp_string);
    recv_socket.recv_raw(pbuf); //we dont need the data for this test but still need to read from the socket to clear it

  }
  BOOST_CHECK_EQUAL(processed_frames.size(), 5);

}

/**
 * Tests to ensure the configuration works, including testing to make sure we can change the address of the live view socket
 */
BOOST_AUTO_TEST_CASE(LiveViewConfigTest)
{
  //TEST CONFIGURATION
  cfg.set_param(FrameProcessor::LiveViewPlugin::CONFIG_FRAME_FREQ, 1);
  cfg.set_param(FrameProcessor::LiveViewPlugin::CONFIG_DATASET_NAME, std::string("data"));
  cfg.set_param(FrameProcessor::LiveViewPlugin::CONFIG_SOCKET_ADDR, std::string("tcp://127.0.0.1:5021"));

  BOOST_CHECK_NO_THROW(plugin.configure(cfg, reply));

  //send frame again to check its going to a different socket

  for(int i = 0; i < 10; i ++)
  {
    plugin.process_frame(frame);
    BOOST_CHECK(!recv_socket.poll(100));
    new_socket_recieve = recv_socket_other.poll(100);
    if(new_socket_recieve)
    {
      break;
    }
  }
  BOOST_CHECK(new_socket_recieve);

}

/**
 * test the filtering by dataset option works
 */
BOOST_AUTO_TEST_CASE(LiveViewDatasetFilterTest)
{
  //TEST DATASET FILTERING
  cfg.set_param(FrameProcessor::LiveViewPlugin::CONFIG_FRAME_FREQ, 1);
  cfg.set_param(FrameProcessor::LiveViewPlugin::CONFIG_DATASET_NAME, std::string("data"));

  plugin.configure(cfg, reply); //configure the plugin to push any frame with the "data" dataset to the live view socket

  while(recv_socket.poll(10))
  {
    recv_socket.recv(); //clear any extra data from the above while loop
  }

  for(int i = 0; i < frames.size(); i++)
  {
    plugin.process_frame(frames[i]);
  }

  std::vector<std::string> dataset_processed_frames;
  while(recv_socket.poll(10))
  {
    std::string tmp_string = recv_socket.recv();
    BOOST_TEST_MESSAGE( "Received Header: " << tmp_string);
    dataset_processed_frames.push_back(tmp_string);
    recv_socket.recv_raw(pbuf); //we dont need the data for this test but still need to read from the socket to clear it from the queue
  }
  for(int i = 0; i < dataset_processed_frames.size(); i++)
  {
    doc.Parse(dataset_processed_frames[i].c_str());
    BOOST_CHECK_EQUAL(doc["dataset"].GetString(), "data"); //check to make sure only "data" frames were passed through
  }
  BOOST_CHECK_EQUAL(dataset_processed_frames.size(), 7); // check to make sure all the frames expected were passed through
}

/**
 * test to make sure the live view plugin can work with data types of more than a byte, and that the data is still preserved when it gets passed through
 */
BOOST_AUTO_TEST_CASE(LiveViewOtherDatatypeTest)
{

  //send the frame of uint16 data until the receiver socket can receive something
  while(!recv_socket.poll(10))
  {
    plugin.process_frame(frame_16);
  }
  message = recv_socket.recv();
  BOOST_TEST_MESSAGE(message);
  recv_socket.recv_raw(pbuf_16);
  std::vector<uint16_t> buf_16(12); //create vector to store the data
  std::copy(pbuf_16, pbuf_16 + frame_16->get_data_size()/2, buf_16.begin()); //Divide the data size in half, as each byte is only half a point of data
  std::vector<uint16_t> original_16(img_16, img_16 + sizeof img_16 / sizeof img_16[0]);
  BOOST_CHECK_EQUAL_COLLECTIONS(buf_16.begin(), buf_16.end(), original_16.begin(), original_16.end()); //test that the data is the same

  doc.Parse(message.c_str());
  BOOST_CHECK_EQUAL(doc["dsize"].GetInt() , frame_16->get_data_size()); //test that the plugin got the correct size for the frame
  BOOST_CHECK_EQUAL(doc["dtype"].GetString(), DATA_TYPES[frame_16->get_data_type()]); //test that the plugin got the correct data type for the frame

}

BOOST_AUTO_TEST_SUITE_END(); //LiveViewPluginUnitTest
<commit_msg>Set LINGER of sockets to 0 before closing, meaning they should not wait for new messages at all when shutting down<commit_after>/*
 * LiveViewUnitTest.cpp
 *
 *  Created on: 28 Sep 2018
 *      Author: Adam Neaves - wbd45595
 */

#include <boost/test/unit_test.hpp>

#include "LiveViewPlugin.h"

/**
 * Test fixture for the Live View Unit Tests. sets up the plugin and other things required by the tests.
 */
class LiveViewPluginTestFixture
{
public:
  LiveViewPluginTestFixture():
    recv_socket(ZMQ_SUB),
    recv_socket_other(ZMQ_SUB)
  {

    //create dummy data for the test frames
    for(int i = 0; i < 12; i++)
    {
      img_8[i] = i+1;
      img_16[i] = i+1;
    }

    DATA_TYPES[0]  = "uint8";
    DATA_TYPES[1]  = "uint16";
    DATA_TYPES[2]  = "uint32";

    COMPRESS_TYPES[0] = "none";
    COMPRESS_TYPES[1] = "LZ4";
    COMPRESS_TYPES[2] = "BSLZ4";

    dimensions_t img_dims(2); img_dims[0] = 3; img_dims[1] = 4;

    //create test frame
    frame = boost::shared_ptr<FrameProcessor::Frame>(new FrameProcessor::Frame("data"));
    frame->set_frame_number(2);
    frame->set_dimensions(img_dims);
    frame->set_data_type(FrameProcessor::raw_8bit);
    frame->set_compression(0);
    frame->copy_data(static_cast<void*>(img_8), 12);

    //create test frame with uint16 data
    frame_16 = boost::shared_ptr<FrameProcessor::Frame>(new FrameProcessor::Frame("data"));
    frame_16->set_frame_number(2);
    frame_16->set_dimensions(img_dims);
    frame_16->set_data_type(FrameProcessor::raw_16bit);
    frame_16->set_compression(0);
    frame_16->copy_data(static_cast<void*>(img_16), 24);

    //create multiple test frames
    for(int i = 0; i < 10; i++)
    {
      boost::shared_ptr<FrameProcessor::Frame> tmp_frame(new FrameProcessor::Frame("data"));
      tmp_frame->set_frame_number(i);
      tmp_frame->set_dimensions(img_dims);
      tmp_frame->set_data_type(FrameProcessor::raw_8bit);
      tmp_frame->set_dataset_name(i % 4 ? "data" : "not_data");

      tmp_frame->copy_data(static_cast<void*>(img_8), 12);

      frames.push_back(tmp_frame);
    }

    new_socket_recieve = false;

    //set up the recieve sockets so we can read data from the plugin's live output.
    recv_socket.subscribe("");
    recv_socket_other.subscribe("");
    recv_socket.connect("tcp://127.0.0.1:5020");
    recv_socket_other.connect("tcp://127.0.0.1:5021");

  }
  ~LiveViewPluginTestFixture() {
    BOOST_TEST_MESSAGE("Live View Fixture Teardown");
    uint32_t linger_option = 0;
    recv_socket.setsockopt(ZMQ_LINGER, &linger_option, sizeof(linger_option));
    recv_socket_other.setsockopt(ZMQ_LINGER, &linger_option, sizeof(linger_option));
    recv_socket.close();
    recv_socket_other.close();
  }

  boost::shared_ptr<FrameProcessor::Frame> frame;
  boost::shared_ptr<FrameProcessor::Frame> frame_16;
  std::vector< boost::shared_ptr<FrameProcessor::Frame> >frames;

  uint8_t img_8[12];
  uint16_t img_16[12];

  uint8_t  pbuf[12]; //create basic array to store data
  uint16_t pbuf_16[12];

  std::map<int, std::string> DATA_TYPES;
  std::map<int, std::string> COMPRESS_TYPES;

  bool new_socket_recieve;
  std::string message;

  OdinData::IpcChannel recv_socket;
  OdinData::IpcChannel recv_socket_other;
  FrameProcessor::LiveViewPlugin plugin;
  OdinData::IpcMessage reply;
  OdinData::IpcMessage cfg;
  rapidjson::Document doc;

};

BOOST_FIXTURE_TEST_SUITE(LiveViewPluginUnitTest, LiveViewPluginTestFixture);

/**
 * tests the basic functionality, passing the plugin a single frame and seeing if it appears the same on the other end of the socket
 */
BOOST_AUTO_TEST_CASE(LiveViewBasicSendTest)
{
  //setting the frame number here guarantees that it'll be passed to the live view socket
  frame->set_frame_number(FrameProcessor::LiveViewPlugin::DEFAULT_FRAME_FREQ);
  //test we can output frame
  while(!recv_socket.poll(100)) //to avoid issue with slow subscribers, keep sending the frame until the subscriber has received it
  {
    //send the frame to the plugin
    plugin.process_frame(frame);
  }
  message = recv_socket.recv();
  BOOST_TEST_MESSAGE(message);
  doc.Parse(message.c_str());

  //TEST HEADER CONTENTS
  BOOST_CHECK_EQUAL(doc["frame_num"].GetInt(), frame->get_frame_number());
  BOOST_CHECK_EQUAL(doc["acquisition_id"].GetString(), frame->get_acquisition_id());
  BOOST_CHECK_EQUAL(doc["dtype"].GetString(), DATA_TYPES[frame->get_data_type()]);
  BOOST_CHECK_EQUAL(doc["dsize"].GetInt() , frame->get_data_size());
  BOOST_CHECK_EQUAL(doc["compression"].GetString(), COMPRESS_TYPES[frame->get_compression()]);
  char *end;
  BOOST_CHECK_EQUAL(strtol(doc["shape"][0].GetString(), &end, 10), frame->get_dimensions()[0]);
  BOOST_CHECK_EQUAL(strtol(doc["shape"][1].GetString(), &end, 10), frame->get_dimensions()[1]);

  //TEST DATA CONTENTS
  recv_socket.recv_raw(&pbuf); //put data into arary
  std::vector<uint8_t> buf(frame->get_data_size()); //create vector of size of data
  std::copy(pbuf, pbuf + frame->get_data_size(), buf.begin()); //copy data from array to vector, so it can be compared
  std::vector<uint8_t> original(img_8, img_8 + sizeof img_8 / sizeof img_8[0]);
  BOOST_CHECK_EQUAL_COLLECTIONS(buf.begin(), buf.end() , original.begin(), original.end());

}

/**
 * tests to make sure the downscale factor works. Checks that only those frames with the needed frame number are passed to the live view socket
 */
BOOST_AUTO_TEST_CASE(LiveViewDownscaleTest)
{

  //TEST DOWNSCALE OPTION
  while(recv_socket.poll(10))
  {
    recv_socket.recv(); //clear any extra data from the socket. Also gives the recieve socket time to init
  }
  //process all frames. with a downscale factor of 2, this should return all the even numbered frames.
  for(int i = 0; i < frames.size(); i++)
  {
    plugin.process_frame(frames[i]);
  }

  std::vector<std::string> processed_frames;
  while(recv_socket.poll(10))
  {
    std::string tmp_string = recv_socket.recv();
    processed_frames.push_back(tmp_string);
    recv_socket.recv_raw(pbuf); //we dont need the data for this test but still need to read from the socket to clear it

  }
  BOOST_CHECK_EQUAL(processed_frames.size(), 5);

}

/**
 * Tests to ensure the configuration works, including testing to make sure we can change the address of the live view socket
 */
BOOST_AUTO_TEST_CASE(LiveViewConfigTest)
{
  //TEST CONFIGURATION
  cfg.set_param(FrameProcessor::LiveViewPlugin::CONFIG_FRAME_FREQ, 1);
  cfg.set_param(FrameProcessor::LiveViewPlugin::CONFIG_DATASET_NAME, std::string("data"));
  cfg.set_param(FrameProcessor::LiveViewPlugin::CONFIG_SOCKET_ADDR, std::string("tcp://127.0.0.1:5021"));

  BOOST_CHECK_NO_THROW(plugin.configure(cfg, reply));

  //send frame again to check its going to a different socket

  for(int i = 0; i < 10; i ++)
  {
    plugin.process_frame(frame);
    BOOST_CHECK(!recv_socket.poll(100));
    new_socket_recieve = recv_socket_other.poll(100);
    if(new_socket_recieve)
    {
      break;
    }
  }
  BOOST_CHECK(new_socket_recieve);

}

/**
 * test the filtering by dataset option works
 */
BOOST_AUTO_TEST_CASE(LiveViewDatasetFilterTest)
{
  //TEST DATASET FILTERING
  cfg.set_param(FrameProcessor::LiveViewPlugin::CONFIG_FRAME_FREQ, 1);
  cfg.set_param(FrameProcessor::LiveViewPlugin::CONFIG_DATASET_NAME, std::string("data"));

  plugin.configure(cfg, reply); //configure the plugin to push any frame with the "data" dataset to the live view socket

  while(recv_socket.poll(10))
  {
    recv_socket.recv(); //clear any extra data from the above while loop
  }

  for(int i = 0; i < frames.size(); i++)
  {
    plugin.process_frame(frames[i]);
  }

  std::vector<std::string> dataset_processed_frames;
  while(recv_socket.poll(10))
  {
    std::string tmp_string = recv_socket.recv();
    BOOST_TEST_MESSAGE( "Received Header: " << tmp_string);
    dataset_processed_frames.push_back(tmp_string);
    recv_socket.recv_raw(pbuf); //we dont need the data for this test but still need to read from the socket to clear it from the queue
  }
  for(int i = 0; i < dataset_processed_frames.size(); i++)
  {
    doc.Parse(dataset_processed_frames[i].c_str());
    BOOST_CHECK_EQUAL(doc["dataset"].GetString(), "data"); //check to make sure only "data" frames were passed through
  }
  BOOST_CHECK_EQUAL(dataset_processed_frames.size(), 7); // check to make sure all the frames expected were passed through
}

/**
 * test to make sure the live view plugin can work with data types of more than a byte, and that the data is still preserved when it gets passed through
 */
BOOST_AUTO_TEST_CASE(LiveViewOtherDatatypeTest)
{

  //send the frame of uint16 data until the receiver socket can receive something
  while(!recv_socket.poll(10))
  {
    plugin.process_frame(frame_16);
  }
  message = recv_socket.recv();
  BOOST_TEST_MESSAGE(message);
  recv_socket.recv_raw(pbuf_16);
  std::vector<uint16_t> buf_16(12); //create vector to store the data
  std::copy(pbuf_16, pbuf_16 + frame_16->get_data_size()/2, buf_16.begin()); //Divide the data size in half, as each byte is only half a point of data
  std::vector<uint16_t> original_16(img_16, img_16 + sizeof img_16 / sizeof img_16[0]);
  BOOST_CHECK_EQUAL_COLLECTIONS(buf_16.begin(), buf_16.end(), original_16.begin(), original_16.end()); //test that the data is the same

  doc.Parse(message.c_str());
  BOOST_CHECK_EQUAL(doc["dsize"].GetInt() , frame_16->get_data_size()); //test that the plugin got the correct size for the frame
  BOOST_CHECK_EQUAL(doc["dtype"].GetString(), DATA_TYPES[frame_16->get_data_type()]); //test that the plugin got the correct data type for the frame

}

BOOST_AUTO_TEST_SUITE_END(); //LiveViewPluginUnitTest
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * 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/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you 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 .
 */

#include <classes/taskcreator.hxx>
#include "services/taskcreatorsrv.hxx"
#include <threadhelp/readguard.hxx>
#include <loadenv/targethelper.hxx>
#include <services.h>

#include <com/sun/star/frame/Desktop.hpp>
#include <com/sun/star/frame/TaskCreator.hpp>
#include <com/sun/star/lang/XSingleServiceFactory.hpp>
#include <com/sun/star/beans/NamedValue.hpp>

#include <comphelper/configurationhelper.hxx>
#include <vcl/svapp.hxx>

namespace framework{

/*-****************************************************************************************************//**
    @short      initialize instance with neccessary information
    @descr      We need a valid uno service manager to create or instanciate new services.
                All other information to create frames or tasks come in on right interface methods.

    @param      xContext
                    points to the valid uno service manager
*//*-*****************************************************************************************************/
TaskCreator::TaskCreator( const css::uno::Reference< css::uno::XComponentContext >& xContext )
    : ThreadHelpBase(       )
    , m_xContext    ( xContext )
{
}

/*-****************************************************************************************************//**
    @short      deinitialize instance
    @descr      We should release all used resource which are not needed any longer.
*//*-*****************************************************************************************************/
TaskCreator::~TaskCreator()
{
    m_xContext.clear();
}

/*-****************************************************************************************************//**
    TODO document me
*//*-*****************************************************************************************************/
css::uno::Reference< css::frame::XFrame > TaskCreator::createTask( const OUString& sName    ,
                                                                         sal_Bool         bVisible )
{
    /* SAFE { */
    ReadGuard aReadLock( m_aLock );
    css::uno::Reference< css::uno::XComponentContext > xContext = m_xContext;
    aReadLock.unlock();
    /* } SAFE */

    css::uno::Reference< css::lang::XSingleServiceFactory > xCreator;
    OUString sCreator = IMPLEMENTATIONNAME_FWK_TASKCREATOR;

    try
    {
        if (
            ( TargetHelper::matchSpecialTarget(sName, TargetHelper::E_BLANK  ) ) ||
            ( TargetHelper::matchSpecialTarget(sName, TargetHelper::E_DEFAULT) )
           )
        {
            ::comphelper::ConfigurationHelper::readDirectKey(
                xContext,
                "org.openoffice.Office.TabBrowse",
                "TaskCreatorService",
                "ImplementationName",
                ::comphelper::ConfigurationHelper::E_READONLY) >>= sCreator;
        }

        xCreator = css::uno::Reference< css::lang::XSingleServiceFactory >(
                    xContext->getServiceManager()->createInstanceWithContext(sCreator, xContext), css::uno::UNO_QUERY_THROW);
    }
    catch(const css::uno::Exception&)
    {}

    // no catch here ... without an task creator service we cant open ANY document window within the office.
    // Thats IMHO not a good idea. Then we should accept the stacktrace showing us the real problem.
    // BTW: The used fallback creator service (IMPLEMENTATIONNAME_FWK_TASKCREATOR) is implemented in the same
    // library then these class here ... Why we should not be able to create it ?
    if ( ! xCreator.is())
        xCreator = css::frame::TaskCreator::create(xContext);

    css::uno::Sequence< css::uno::Any > lArgs(5);
    css::beans::NamedValue              aArg    ;

    aArg.Name    = OUString(ARGUMENT_PARENTFRAME);
    aArg.Value <<= css::uno::Reference< css::frame::XFrame >( css::frame::Desktop::create( xContext ), css::uno::UNO_QUERY_THROW);
    lArgs[0]   <<= aArg;

    aArg.Name    = OUString(ARGUMENT_CREATETOPWINDOW);
    aArg.Value <<= sal_True;
    lArgs[1]   <<= aArg;

    aArg.Name    = OUString(ARGUMENT_MAKEVISIBLE);
    aArg.Value <<= bVisible;
    lArgs[2]   <<= aArg;

    aArg.Name    = OUString(ARGUMENT_SUPPORTPERSISTENTWINDOWSTATE);
    aArg.Value <<= sal_True;
    lArgs[3]   <<= aArg;

    aArg.Name    = OUString(ARGUMENT_FRAMENAME);
    aArg.Value <<= sName;
    lArgs[4]   <<= aArg;

    css::uno::Reference< css::frame::XFrame > xTask(xCreator->createInstanceWithArguments(lArgs), css::uno::UNO_QUERY_THROW);
    return xTask;
}

} // namespace framework

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>fdo#46037: no more comphelper/configurationhelper.hxx in framework/classes<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * 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/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you 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 .
 */

#include <classes/taskcreator.hxx>
#include "services/taskcreatorsrv.hxx"
#include <threadhelp/readguard.hxx>
#include <loadenv/targethelper.hxx>
#include <services.h>

#include <com/sun/star/frame/Desktop.hpp>
#include <com/sun/star/frame/TaskCreator.hpp>
#include <com/sun/star/lang/XSingleServiceFactory.hpp>
#include <com/sun/star/beans/NamedValue.hpp>

#include <vcl/svapp.hxx>

#include <officecfg/Office/TabBrowse.hxx>

namespace framework{

/*-****************************************************************************************************//**
    @short      initialize instance with neccessary information
    @descr      We need a valid uno service manager to create or instanciate new services.
                All other information to create frames or tasks come in on right interface methods.

    @param      xContext
                    points to the valid uno service manager
*//*-*****************************************************************************************************/
TaskCreator::TaskCreator( const css::uno::Reference< css::uno::XComponentContext >& xContext )
    : ThreadHelpBase(       )
    , m_xContext    ( xContext )
{
}

/*-****************************************************************************************************//**
    @short      deinitialize instance
    @descr      We should release all used resource which are not needed any longer.
*//*-*****************************************************************************************************/
TaskCreator::~TaskCreator()
{
    m_xContext.clear();
}

/*-****************************************************************************************************//**
    TODO document me
*//*-*****************************************************************************************************/
css::uno::Reference< css::frame::XFrame > TaskCreator::createTask( const OUString& sName    ,
                                                                         sal_Bool         bVisible )
{
    /* SAFE { */
    ReadGuard aReadLock( m_aLock );
    css::uno::Reference< css::uno::XComponentContext > xContext = m_xContext;
    aReadLock.unlock();
    /* } SAFE */

    css::uno::Reference< css::lang::XSingleServiceFactory > xCreator;
    OUString sCreator = IMPLEMENTATIONNAME_FWK_TASKCREATOR;

    try
    {
        if (
            ( TargetHelper::matchSpecialTarget(sName, TargetHelper::E_BLANK  ) ) ||
            ( TargetHelper::matchSpecialTarget(sName, TargetHelper::E_DEFAULT) )
           )
        {

            boost::optional<OUString> x(officecfg::Office::TabBrowse::TaskCreatorService::ImplementationName::get(xContext));
            if (x) sCreator = x.get();
        }

        xCreator = css::uno::Reference< css::lang::XSingleServiceFactory >(
                    xContext->getServiceManager()->createInstanceWithContext(sCreator, xContext), css::uno::UNO_QUERY_THROW);
    }
    catch(const css::uno::Exception&)
    {}

    // no catch here ... without an task creator service we cant open ANY document window within the office.
    // Thats IMHO not a good idea. Then we should accept the stacktrace showing us the real problem.
    // BTW: The used fallback creator service (IMPLEMENTATIONNAME_FWK_TASKCREATOR) is implemented in the same
    // library then these class here ... Why we should not be able to create it ?
    if ( ! xCreator.is())
        xCreator = css::frame::TaskCreator::create(xContext);

    css::uno::Sequence< css::uno::Any > lArgs(5);
    css::beans::NamedValue              aArg    ;

    aArg.Name    = OUString(ARGUMENT_PARENTFRAME);
    aArg.Value <<= css::uno::Reference< css::frame::XFrame >( css::frame::Desktop::create( xContext ), css::uno::UNO_QUERY_THROW);
    lArgs[0]   <<= aArg;

    aArg.Name    = OUString(ARGUMENT_CREATETOPWINDOW);
    aArg.Value <<= sal_True;
    lArgs[1]   <<= aArg;

    aArg.Name    = OUString(ARGUMENT_MAKEVISIBLE);
    aArg.Value <<= bVisible;
    lArgs[2]   <<= aArg;

    aArg.Name    = OUString(ARGUMENT_SUPPORTPERSISTENTWINDOWSTATE);
    aArg.Value <<= sal_True;
    lArgs[3]   <<= aArg;

    aArg.Name    = OUString(ARGUMENT_FRAMENAME);
    aArg.Value <<= sName;
    lArgs[4]   <<= aArg;

    css::uno::Reference< css::frame::XFrame > xTask(xCreator->createInstanceWithArguments(lArgs), css::uno::UNO_QUERY_THROW);
    return xTask;
}

} // namespace framework

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<|endoftext|>


<commit_before><commit_msg>Fix messed up include<commit_after><|endoftext|>


<commit_before> /*

    Copyright (c) 2006      by Olivier Goffart  <ogoffart at kde.org>

    Kopete    (c) 2006 by the Kopete developers <kopete-devel@kde.org>

    *************************************************************************
    *                                                                       *
    * This program is free software; you can redistribute it and/or modify  *
    * it under the terms of the GNU General Public License as published by  *
    * the Free Software Foundation; either version 2 of the License, or     *
    * (at your option) any later version.                                   *
    *                                                                       *
    *************************************************************************
 */

#include "jabbertransport.h"
#include "jabbercontact.h"
#include "jabberaccount.h"
#include "jabberprotocol.h"
#include "jabbercontactpool.h"
#include "jabberchatsession.h"

#include <kopeteaccountmanager.h>
#include <kopetecontact.h>
#include <kopetecontactlist.h>
#include <kopeteidentity.h>
#include <kopetestatusmessage.h>
#include <kopeteversion.h>

#include <qpixmap.h>
#include <qtimer.h>
#include <kmenu.h>
#include <kaction.h>
#include <kactionmenu.h>
#include <kdebug.h>
#include <klocale.h>
#include <kconfig.h>

#include "xmpp_tasks.h"

JabberTransport::JabberTransport (JabberAccount * parentAccount, const XMPP::RosterItem & item, const QString& gateway_type)
	: Kopete::Account ( parentAccount->protocol(), parentAccount->accountId()+'/'+ item.jid().bare() )
{
	m_status=Creating;
	m_account = parentAccount;
	m_account->addTransport( this,item.jid().bare() );

	JabberContact *myContact = m_account->contactPool()->addContact ( item , Kopete::ContactList::self()->myself(), false );
	setMyself( myContact );

	kDebug(JABBER_DEBUG_GLOBAL) << accountId() <<" transport created:  myself: " << myContact;

	setColor( account()->color() );

	QString cIcon;
	if(gateway_type=="msn")
		cIcon="jabber_gateway_msn";
	else if(gateway_type=="icq")
		cIcon="jabber_gateway_icq";
	else if(gateway_type=="aim")
		cIcon="jabber_gateway_aim";
	else if(gateway_type=="yahoo")
		cIcon="jabber_gateway_yahoo";
	else if(gateway_type=="sms")
		cIcon="jabber_gateway_sms";
	else if(gateway_type=="gadu-gadu")
		cIcon="jabber_gateway_gadu";
	else if(gateway_type=="smtp")
		cIcon="jabber_gateway_smtp";
	else if(gateway_type=="http-ws")
		cIcon="jabber_gateway_http-ws";
	else if(gateway_type=="qq")
		cIcon="jabber_gateway_qq";
	else if(gateway_type=="tlen")
		cIcon="jabber_gateway_tlen";
	else if(gateway_type=="irc")  //NOTE: this is not official
		cIcon="irc_protocol";

	if( !cIcon.isEmpty() )
		setCustomIcon( cIcon );

	configGroup()->writeEntry("GatewayJID" , item.jid().full() );

	QTimer::singleShot(0, this, SLOT(eatContacts()));

	m_status=Normal;
}

JabberTransport::JabberTransport( JabberAccount * parentAccount, const QString & _accountId )
	: Kopete::Account ( parentAccount->protocol(), _accountId )
{
	m_status=Creating;
	m_account = parentAccount;

	const QString contactJID_s = configGroup()->readEntry("GatewayJID");

	if(contactJID_s.isEmpty())
	{
		kError(JABBER_DEBUG_GLOBAL) << _accountId <<": GatewayJID is empty: MISCONFIGURATION  (have you used Kopete 0.12 beta ?)" << endl;
	}

	XMPP::Jid contactJID= XMPP::Jid( contactJID_s );

	m_account->addTransport( this, contactJID.bare() );

	JabberContact *myContact = m_account->contactPool()->addContact ( contactJID , Kopete::ContactList::self()->myself(), false );
	setMyself( myContact );

	kDebug(JABBER_DEBUG_GLOBAL) << accountId() <<" transport created:  myself: " << myContact;

	m_status=Normal;
}

JabberTransport::~JabberTransport ()
{
	m_account->removeTransport( myself()->contactId() );
}

void JabberTransport::fillActionMenu( KActionMenu *actionMenu )
{
	actionMenu->setIcon( myself()->onlineStatus().iconFor( this ) );

	QString nick;
	if ( identity()->hasProperty( Kopete::Global::Properties::self()->nickName().key() ))
		nick = identity()->property( Kopete::Global::Properties::self()->nickName()).value().toString();
	else
		nick = myself()->nickName();

	actionMenu->menu()->addTitle( myself()->onlineStatus().iconFor( myself() ),
	nick.isNull() ? accountLabel() : i18n( "%2 <%1>", accountLabel(), nick )
								  );

	QList<KAction*> *customActions = myself()->customContextMenuActions(  );
	if( customActions && !customActions->isEmpty() )
	{
		actionMenu->addSeparator();

		foreach( KAction *a, *customActions )
			actionMenu->menu()->addAction(a);
	}
	delete customActions;

/*	KActionMenu *m_actionMenu = Kopete::Account::actionMenu();

	m_actionMenu->popupMenu()->insertSeparator();

	m_actionMenu->insert(new KAction (i18n ("Join Groupchat..."), "jabber_group", 0,
		this, SLOT (slotJoinNewChat ()), this, "actionJoinChat"));

	m_actionMenu->popupMenu()->insertSeparator();

	m_actionMenu->insert ( new KAction ( i18n ("Services..."), "jabber_serv_on", 0,
										 this, SLOT ( slotGetServices () ), this, "actionJabberServices") );

	m_actionMenu->insert ( new KAction ( i18n ("Send Raw Packet to Server..."), "mail_new", 0,
										 this, SLOT ( slotSendRaw () ), this, "actionJabberSendRaw") );

	m_actionMenu->insert ( new KAction ( i18n ("Edit User Info..."), "identity", 0,
										 this, SLOT ( slotEditVCard () ), this, "actionEditVCard") );

	return m_actionMenu;*/
}

bool JabberTransport::hasCustomStatusMenu() const
{
	return true;
}

bool JabberTransport::createContact (const QString & contactId,  Kopete::MetaContact * metaContact)
{
	Q_UNUSED(contactId);
	Q_UNUSED(metaContact);
#if 0 //TODO
	// collect all group names
	QStringList groupNames;
	Kopete::GroupList groupList = metaContact->groups();
	for(Kopete::Group *group = groupList.first(); group; group = groupList.next())
		groupNames += group->displayName();

	XMPP::Jid jid ( contactId );
	XMPP::RosterItem item ( jid );
	item.setName ( metaContact->displayName () );
	item.setGroups ( groupNames );

	// this contact will be created with the "dirty" flag set
	// (it will get reset if the contact appears in the roster during connect)
	JabberContact *contact = contactPool()->addContact ( item, metaContact, true );

	return ( contact != 0 );
#endif
	return false;
}

void JabberTransport::setOnlineStatus( const Kopete::OnlineStatus& status, const Kopete::StatusMessage &reason, const OnlineStatusOptions& options )
{
	Q_UNUSED(status);
	Q_UNUSED(reason);
	Q_UNUSED(options);
#if 0
	if( status.status() == Kopete::OnlineStatus::Offline )
	{
		disconnect( Kopete::Account::Manual );
		return;
	}

	if( isConnecting () )
	{
		errorConnectionInProgress ();
		return;
	}

	XMPP::Status xmppStatus ( "", reason );

	switch ( status.internalStatus () )
	{
		case JabberProtocol::JabberFreeForChat:
			xmppStatus.setShow ( "chat" );
			break;

		case JabberProtocol::JabberOnline:
			xmppStatus.setShow ( "" );
			break;

		case JabberProtocol::JabberAway:
			xmppStatus.setShow ( "away" );
			break;

		case JabberProtocol::JabberXA:
			xmppStatus.setShow ( "xa" );
			break;

		case JabberProtocol::JabberDND:
			xmppStatus.setShow ( "dnd" );
			break;

		case JabberProtocol::JabberInvisible:
			xmppStatus.setIsInvisible ( true );
			break;
	}

	if ( !isConnected () )
	{
		// we are not connected yet, so connect now
		m_initialPresence = xmppStatus;
		connect ();
	}
	else
	{
		setPresence ( xmppStatus );
	}
#endif
}

void JabberTransport::setStatusMessage( const Kopete::StatusMessage &statusMessage )
{
	Q_UNUSED(statusMessage);
}

JabberProtocol * JabberTransport::protocol( ) const
{
	return m_account->protocol();
}

bool JabberTransport::removeAccount( )
{
	if(m_status == Removing  ||  m_status == AccountRemoved)
		return true; //so it can be deleted

	if (!account()->isConnected())
	{
		account()->errorConnectFirst ();
		return false;
	}

	m_status = Removing;
	XMPP::JT_Register *task = new XMPP::JT_Register ( m_account->client()->rootTask () );
	QObject::connect ( task, SIGNAL ( finished () ), this, SLOT ( removeAllContacts() ) );

	//JabberContact *my=static_cast<JabberContact*>(myself());
	task->unreg ( myself()->contactId() );
	task->go ( true );
	return false; //delay the removal
}

void JabberTransport::removeAllContacts( )
{
//	XMPP::JT_Register * task = (XMPP::JT_Register *) sender ();

/*	if ( ! task->success ())
	KMessageBox::queuedMessageBox ( 0L, KMessageBox::Error,
									i18n ("An error occurred when trying to remove the transport:\n%1").arg(task->statusString()),
									i18n ("Jabber Service Unregistration"));
	*/ //we don't really care, we remove everithing anyway.

	kDebug(JABBER_DEBUG_GLOBAL) << "delete all contacts of the transport";
	QHash<QString, Kopete::Contact*>::ConstIterator it, itEnd = contacts().constEnd();
	for( it = contacts().constBegin(); it != itEnd; ++it )
	{
		XMPP::JT_Roster * rosterTask = new XMPP::JT_Roster ( account()->client()->rootTask () );
		rosterTask->remove ( static_cast<JabberBaseContact*>(it.value())->rosterItem().jid() );
		rosterTask->go ( true );
	}

	// Remove myself
	XMPP::JT_Roster * rosterTask = new XMPP::JT_Roster ( account()->client()->rootTask () );
	rosterTask->remove ( static_cast<JabberBaseContact*>(myself())->rosterItem().jid() );
	rosterTask->go ( true );

	m_status = Removing; //in theory that's already our status
	Kopete::AccountManager::self()->removeAccount( this ); //this will delete this
}

QString JabberTransport::legacyId( const XMPP::Jid & jid )
{
	if(jid.node().isEmpty())
		return QString();
	QString node = jid.node();
	return node.replace('%','@');
}

void JabberTransport::jabberAccountRemoved( )
{
	m_status = AccountRemoved;
	Kopete::AccountManager::self()->removeAccount( this ); //this will delete this
}

void JabberTransport::eatContacts( )
{
	/*
	* "Gateway Contact Eating" (c)(r)(tm)(g)(o)(f)
	* this comes directly from my mind into the kopete code.
	* principle: - the transport is hungry
	*            - it will eat contacts which belong to him
	*            - the contact will die
	*            - a new contact will born, with the same characteristics, but owned by the transport
	* - Olivier 2006-01-17 -
	*/
    kDebug(JABBER_DEBUG_GLOBAL) ;
	QHash<QString, Kopete::Contact*> cts=account()->contacts();
	QHash<QString, Kopete::Contact*>::ConstIterator it, itEnd = cts.constEnd();
	for( it = cts.constBegin(); it != itEnd; ++it )
	{
		JabberContact *contact=dynamic_cast<JabberContact*>(it.value());
		if( contact && !contact->transport() && contact->rosterItem().jid().domain() == myself()->contactId())
		{
			XMPP::RosterItem item=contact->rosterItem();
			Kopete::MetaContact *mc=contact->metaContact();
			Kopete::OnlineStatus status = contact->onlineStatus();
			kDebug(JABBER_DEBUG_GLOBAL) << item.jid().full() << " will be soon eat  - " << contact;
			delete contact;
			Kopete::Contact *c2=account()->contactPool()->addContact( item , mc , false ); //not sure this is false;
			if(c2)
				c2->setOnlineStatus( status ); //put back the old status
		}
	}
}

#include "jabbertransport.moc"

// vim: set noet ts=4 sts=4 sw=4:
<commit_msg>Always use jid.domain() as a key in transport map.<commit_after> /*

    Copyright (c) 2006      by Olivier Goffart  <ogoffart at kde.org>

    Kopete    (c) 2006 by the Kopete developers <kopete-devel@kde.org>

    *************************************************************************
    *                                                                       *
    * This program is free software; you can redistribute it and/or modify  *
    * it under the terms of the GNU General Public License as published by  *
    * the Free Software Foundation; either version 2 of the License, or     *
    * (at your option) any later version.                                   *
    *                                                                       *
    *************************************************************************
 */

#include "jabbertransport.h"
#include "jabbercontact.h"
#include "jabberaccount.h"
#include "jabberprotocol.h"
#include "jabbercontactpool.h"
#include "jabberchatsession.h"

#include <kopeteaccountmanager.h>
#include <kopetecontact.h>
#include <kopetecontactlist.h>
#include <kopeteidentity.h>
#include <kopetestatusmessage.h>
#include <kopeteversion.h>

#include <qpixmap.h>
#include <qtimer.h>
#include <kmenu.h>
#include <kaction.h>
#include <kactionmenu.h>
#include <kdebug.h>
#include <klocale.h>
#include <kconfig.h>

#include "xmpp_tasks.h"

JabberTransport::JabberTransport (JabberAccount * parentAccount, const XMPP::RosterItem & item, const QString& gateway_type)
	: Kopete::Account ( parentAccount->protocol(), parentAccount->accountId()+'/'+ item.jid().bare() )
{
	m_status=Creating;
	m_account = parentAccount;
	m_account->addTransport( this,item.jid().bare() );

	JabberContact *myContact = m_account->contactPool()->addContact ( item , Kopete::ContactList::self()->myself(), false );
	setMyself( myContact );

	kDebug(JABBER_DEBUG_GLOBAL) << accountId() <<" transport created:  myself: " << myContact;

	setColor( account()->color() );

	QString cIcon;
	if(gateway_type=="msn")
		cIcon="jabber_gateway_msn";
	else if(gateway_type=="icq")
		cIcon="jabber_gateway_icq";
	else if(gateway_type=="aim")
		cIcon="jabber_gateway_aim";
	else if(gateway_type=="yahoo")
		cIcon="jabber_gateway_yahoo";
	else if(gateway_type=="sms")
		cIcon="jabber_gateway_sms";
	else if(gateway_type=="gadu-gadu")
		cIcon="jabber_gateway_gadu";
	else if(gateway_type=="smtp")
		cIcon="jabber_gateway_smtp";
	else if(gateway_type=="http-ws")
		cIcon="jabber_gateway_http-ws";
	else if(gateway_type=="qq")
		cIcon="jabber_gateway_qq";
	else if(gateway_type=="tlen")
		cIcon="jabber_gateway_tlen";
	else if(gateway_type=="irc")  //NOTE: this is not official
		cIcon="irc_protocol";

	if( !cIcon.isEmpty() )
		setCustomIcon( cIcon );

	configGroup()->writeEntry("GatewayJID" , item.jid().full() );

	QTimer::singleShot(0, this, SLOT(eatContacts()));

	m_status=Normal;
}

JabberTransport::JabberTransport( JabberAccount * parentAccount, const QString & _accountId )
	: Kopete::Account ( parentAccount->protocol(), _accountId )
{
	m_status=Creating;
	m_account = parentAccount;

	const QString contactJID_s = configGroup()->readEntry("GatewayJID");

	if(contactJID_s.isEmpty())
	{
		kError(JABBER_DEBUG_GLOBAL) << _accountId <<": GatewayJID is empty: MISCONFIGURATION  (have you used Kopete 0.12 beta ?)" << endl;
	}

	XMPP::Jid contactJID= XMPP::Jid( contactJID_s );

	m_account->addTransport( this, contactJID.domain() );

	JabberContact *myContact = m_account->contactPool()->addContact ( contactJID , Kopete::ContactList::self()->myself(), false );
	setMyself( myContact );

	kDebug(JABBER_DEBUG_GLOBAL) << accountId() <<" transport created:  myself: " << myContact;

	m_status=Normal;
}

JabberTransport::~JabberTransport ()
{
	m_account->removeTransport( XMPP::Jid( myself()->contactId() ).domain() );
}

void JabberTransport::fillActionMenu( KActionMenu *actionMenu )
{
	actionMenu->setIcon( myself()->onlineStatus().iconFor( this ) );

	QString nick;
	if ( identity()->hasProperty( Kopete::Global::Properties::self()->nickName().key() ))
		nick = identity()->property( Kopete::Global::Properties::self()->nickName()).value().toString();
	else
		nick = myself()->nickName();

	actionMenu->menu()->addTitle( myself()->onlineStatus().iconFor( myself() ),
	nick.isNull() ? accountLabel() : i18n( "%2 <%1>", accountLabel(), nick )
								  );

	QList<KAction*> *customActions = myself()->customContextMenuActions(  );
	if( customActions && !customActions->isEmpty() )
	{
		actionMenu->addSeparator();

		foreach( KAction *a, *customActions )
			actionMenu->menu()->addAction(a);
	}
	delete customActions;

/*	KActionMenu *m_actionMenu = Kopete::Account::actionMenu();

	m_actionMenu->popupMenu()->insertSeparator();

	m_actionMenu->insert(new KAction (i18n ("Join Groupchat..."), "jabber_group", 0,
		this, SLOT (slotJoinNewChat ()), this, "actionJoinChat"));

	m_actionMenu->popupMenu()->insertSeparator();

	m_actionMenu->insert ( new KAction ( i18n ("Services..."), "jabber_serv_on", 0,
										 this, SLOT ( slotGetServices () ), this, "actionJabberServices") );

	m_actionMenu->insert ( new KAction ( i18n ("Send Raw Packet to Server..."), "mail_new", 0,
										 this, SLOT ( slotSendRaw () ), this, "actionJabberSendRaw") );

	m_actionMenu->insert ( new KAction ( i18n ("Edit User Info..."), "identity", 0,
										 this, SLOT ( slotEditVCard () ), this, "actionEditVCard") );

	return m_actionMenu;*/
}

bool JabberTransport::hasCustomStatusMenu() const
{
	return true;
}

bool JabberTransport::createContact (const QString & contactId,  Kopete::MetaContact * metaContact)
{
	Q_UNUSED(contactId);
	Q_UNUSED(metaContact);
#if 0 //TODO
	// collect all group names
	QStringList groupNames;
	Kopete::GroupList groupList = metaContact->groups();
	for(Kopete::Group *group = groupList.first(); group; group = groupList.next())
		groupNames += group->displayName();

	XMPP::Jid jid ( contactId );
	XMPP::RosterItem item ( jid );
	item.setName ( metaContact->displayName () );
	item.setGroups ( groupNames );

	// this contact will be created with the "dirty" flag set
	// (it will get reset if the contact appears in the roster during connect)
	JabberContact *contact = contactPool()->addContact ( item, metaContact, true );

	return ( contact != 0 );
#endif
	return false;
}

void JabberTransport::setOnlineStatus( const Kopete::OnlineStatus& status, const Kopete::StatusMessage &reason, const OnlineStatusOptions& options )
{
	Q_UNUSED(status);
	Q_UNUSED(reason);
	Q_UNUSED(options);
#if 0
	if( status.status() == Kopete::OnlineStatus::Offline )
	{
		disconnect( Kopete::Account::Manual );
		return;
	}

	if( isConnecting () )
	{
		errorConnectionInProgress ();
		return;
	}

	XMPP::Status xmppStatus ( "", reason );

	switch ( status.internalStatus () )
	{
		case JabberProtocol::JabberFreeForChat:
			xmppStatus.setShow ( "chat" );
			break;

		case JabberProtocol::JabberOnline:
			xmppStatus.setShow ( "" );
			break;

		case JabberProtocol::JabberAway:
			xmppStatus.setShow ( "away" );
			break;

		case JabberProtocol::JabberXA:
			xmppStatus.setShow ( "xa" );
			break;

		case JabberProtocol::JabberDND:
			xmppStatus.setShow ( "dnd" );
			break;

		case JabberProtocol::JabberInvisible:
			xmppStatus.setIsInvisible ( true );
			break;
	}

	if ( !isConnected () )
	{
		// we are not connected yet, so connect now
		m_initialPresence = xmppStatus;
		connect ();
	}
	else
	{
		setPresence ( xmppStatus );
	}
#endif
}

void JabberTransport::setStatusMessage( const Kopete::StatusMessage &statusMessage )
{
	Q_UNUSED(statusMessage);
}

JabberProtocol * JabberTransport::protocol( ) const
{
	return m_account->protocol();
}

bool JabberTransport::removeAccount( )
{
	if(m_status == Removing  ||  m_status == AccountRemoved)
		return true; //so it can be deleted

	if (!account()->isConnected())
	{
		account()->errorConnectFirst ();
		return false;
	}

	m_status = Removing;
	XMPP::JT_Register *task = new XMPP::JT_Register ( m_account->client()->rootTask () );
	QObject::connect ( task, SIGNAL ( finished () ), this, SLOT ( removeAllContacts() ) );

	//JabberContact *my=static_cast<JabberContact*>(myself());
	task->unreg ( myself()->contactId() );
	task->go ( true );
	return false; //delay the removal
}

void JabberTransport::removeAllContacts( )
{
//	XMPP::JT_Register * task = (XMPP::JT_Register *) sender ();

/*	if ( ! task->success ())
	KMessageBox::queuedMessageBox ( 0L, KMessageBox::Error,
									i18n ("An error occurred when trying to remove the transport:\n%1").arg(task->statusString()),
									i18n ("Jabber Service Unregistration"));
	*/ //we don't really care, we remove everithing anyway.

	kDebug(JABBER_DEBUG_GLOBAL) << "delete all contacts of the transport";
	QHash<QString, Kopete::Contact*>::ConstIterator it, itEnd = contacts().constEnd();
	for( it = contacts().constBegin(); it != itEnd; ++it )
	{
		XMPP::JT_Roster * rosterTask = new XMPP::JT_Roster ( account()->client()->rootTask () );
		rosterTask->remove ( static_cast<JabberBaseContact*>(it.value())->rosterItem().jid() );
		rosterTask->go ( true );
	}

	// Remove myself
	XMPP::JT_Roster * rosterTask = new XMPP::JT_Roster ( account()->client()->rootTask () );
	rosterTask->remove ( static_cast<JabberBaseContact*>(myself())->rosterItem().jid() );
	rosterTask->go ( true );

	m_status = Removing; //in theory that's already our status
	Kopete::AccountManager::self()->removeAccount( this ); //this will delete this
}

QString JabberTransport::legacyId( const XMPP::Jid & jid )
{
	if(jid.node().isEmpty())
		return QString();
	QString node = jid.node();
	return node.replace('%','@');
}

void JabberTransport::jabberAccountRemoved( )
{
	m_status = AccountRemoved;
	Kopete::AccountManager::self()->removeAccount( this ); //this will delete this
}

void JabberTransport::eatContacts( )
{
	/*
	* "Gateway Contact Eating" (c)(r)(tm)(g)(o)(f)
	* this comes directly from my mind into the kopete code.
	* principle: - the transport is hungry
	*            - it will eat contacts which belong to him
	*            - the contact will die
	*            - a new contact will born, with the same characteristics, but owned by the transport
	* - Olivier 2006-01-17 -
	*/
    kDebug(JABBER_DEBUG_GLOBAL) ;
	QHash<QString, Kopete::Contact*> cts=account()->contacts();
	QHash<QString, Kopete::Contact*>::ConstIterator it, itEnd = cts.constEnd();
	for( it = cts.constBegin(); it != itEnd; ++it )
	{
		JabberContact *contact=dynamic_cast<JabberContact*>(it.value());
		if( contact && !contact->transport() && contact->rosterItem().jid().domain() == myself()->contactId())
		{
			XMPP::RosterItem item=contact->rosterItem();
			Kopete::MetaContact *mc=contact->metaContact();
			Kopete::OnlineStatus status = contact->onlineStatus();
			kDebug(JABBER_DEBUG_GLOBAL) << item.jid().full() << " will be soon eat  - " << contact;
			delete contact;
			Kopete::Contact *c2=account()->contactPool()->addContact( item , mc , false ); //not sure this is false;
			if(c2)
				c2->setOnlineStatus( status ); //put back the old status
		}
	}
}

#include "jabbertransport.moc"

// vim: set noet ts=4 sts=4 sw=4:
<|endoftext|>


<commit_before>#include "config.hpp"

#include "extract.hpp"
#include "download.hpp"

extern "C" {
#include "cia.h"
}

Entry::Entry(json jsonEntry)
{
	state = STATE_NONE;
	if (jsonEntry["inarchive"].is_string())
		m_inArchive = jsonEntry["inarchive"];
	if (jsonEntry["inrelease"].is_string())
		m_inRelease = jsonEntry["inrelease"];
	if (jsonEntry["path"].is_string())
		m_path = jsonEntry["path"];
	if (jsonEntry["url"].is_string())
		m_url = jsonEntry["url"];
	if (jsonEntry["name"].is_string())
		name = jsonEntry["name"];
}

void Entry::update(bool deleteArchive, bool deleteCia)
{
	printf("\x1b[40;34mUpdating %s...\x1b[0m\n", name.c_str());
	std::string downloadPath;

	//if the file to download isnt an archive, direcly download where wanted
	if (m_inArchive.empty()) {
		downloadPath = m_path;
	}
	//otherwise, download to an archive in the working dir, then extract where wanted
	else {
		std::stringstream downloadPathStream;
		downloadPathStream << WORKING_DIR << name.c_str() << ".archive";
		downloadPath = downloadPathStream.str();
	}

	Result ret = 0;

	//if the entry doesnt want anything from a release, expect it to be a normal file
	if (m_inRelease.empty())
		ret = downloadToFile(m_url, downloadPath, false);
	else
		ret = downloadFromRelease(m_url, m_inRelease, downloadPath);

	if (ret != 0) {
		printf("\x1b[40;31mDownload failed!");
		goto failure;
	}
	else
		printf("\x1b[40;32mDownload successful!");

	if (!(m_inArchive.empty())) {
		printf("\n\x1b[40;34mExtracting file from the archive...\x1b[0m\n");
		ret = extractArchive(downloadPath, m_inArchive, m_path);

		if (deleteArchive)
			deleteFile(downloadPath.c_str());

		if (ret != 0) {
			printf("\x1b[40;31mExtraction failed!");
			goto failure;
		}
		else
			printf("\x1b[40;32mExtraction successful!");
	}

	if (matchPattern(".*(\\.cia)$", m_path)) {
		printf("\n\x1b[40;34mInstalling CIA...\x1b[0m\n");
		ret = installCia(m_path.c_str());
		if (deleteCia)
			deleteFile(m_path.c_str());

		if (ret != 0) {
			printf("\x1b[40;31mInstall failed!");
			goto failure;
		}
		else
			printf("\x1b[40;32mInstall successful!");
	}

	printf("\n\x1b[40;32mUpdate complete!\x1b[0m\n");
	state = STATE_SUCCESS;
	return;

	failure:
	printf("\n\x1b[40;31mUpdate failed!\n");
	printf("\x1b[40;33mError code: %.8lx\x1b[0m\n", ret);
	state = STATE_FAILED;
	return;
}

Config::Config()
{
	m_selfUpdater = true;
	m_deleteCIA = false;
	m_deleteArchive = true;

	Handle configHandle;
	char* configString = nullptr;
	Result ret = openFile(&configHandle, CONFIG_FILE_PATH, false);

	if (R_FAILED(ret)) {
		json configDownloadJson;
		configDownloadJson["name"] = "Download the latest example config!";
		configDownloadJson["url"] = CONFIG_FILE_URL;
		configDownloadJson["path"] = CONFIG_FILE_PATH;

		Entry configDownloadEntry(configDownloadJson);
		entries.push_back(configDownloadEntry);
	}
	else {
		u64 configSize = 0;
		FSFILE_GetSize(configHandle, &configSize);
		configString = (char*)calloc(configSize+1, sizeof(char));
		u32 bytesRead = 0;
		FSFILE_Read(configHandle, &bytesRead, 0, configString, (u32)configSize);

		json parsedConfig = json::parse(configString);

		if (parsedConfig["config"].is_object()) {
			if (parsedConfig["config"]["self_updater"].is_boolean())
				m_selfUpdater = parsedConfig["config"]["self_updater"];
			if (parsedConfig["config"]["delete_cias"].is_boolean())
				m_deleteCIA = parsedConfig["config"]["delete_cias"];
			if (parsedConfig["config"]["delete_archives"].is_boolean())
				m_deleteArchive = parsedConfig["config"]["delete_archives"];
		}

		if (m_selfUpdater) {
			json selfUpdaterJson;
			selfUpdaterJson["name"] = "MultiUpdater";
			selfUpdaterJson["url"] = "https://github.com/LiquidFenrir/MultiUpdater";
			selfUpdaterJson["inrelease"] = "MultiUpdater.*\\.zip";
			selfUpdaterJson["inarchive"] = "MultiUpdater.cia";
			selfUpdaterJson["path"] = "/cias/MultiUpdater.cia";

			Entry selfUpdateEntry(selfUpdaterJson);
			entries.push_back(selfUpdateEntry);
		}

		if (parsedConfig["entries"].is_array()) {
			for (auto jsonEntry : parsedConfig["entries"]) {
				if (jsonEntry.is_object()) {
					Entry configEntry(jsonEntry);
					entries.push_back(configEntry);
				}
			}
		}
	}

	free(configString);
	FSFILE_Close(configHandle);
}
<commit_msg>fix archive path<commit_after>#include "config.hpp"

#include "extract.hpp"
#include "download.hpp"

extern "C" {
#include "cia.h"
}

Entry::Entry(json jsonEntry)
{
	state = STATE_NONE;
	if (jsonEntry["inarchive"].is_string())
		m_inArchive = jsonEntry["inarchive"];
	if (jsonEntry["inrelease"].is_string())
		m_inRelease = jsonEntry["inrelease"];
	if (jsonEntry["path"].is_string())
		m_path = jsonEntry["path"];
	if (jsonEntry["url"].is_string())
		m_url = jsonEntry["url"];
	if (jsonEntry["name"].is_string())
		name = jsonEntry["name"];
}

void Entry::update(bool deleteArchive, bool deleteCia)
{
	printf("\x1b[40;34mUpdating %s...\x1b[0m\n", name.c_str());
	std::string downloadPath;

	//if the file to download isnt an archive, direcly download where wanted
	if (m_inArchive.empty()) {
		downloadPath = m_path;
	}
	//otherwise, download to an archive in the working dir, then extract where wanted
	else {
		std::stringstream downloadPathStream;
		downloadPathStream << WORKING_DIR << "/" << name.c_str() << ".archive";
		downloadPath = downloadPathStream.str();
	}

	Result ret = 0;

	//if the entry doesnt want anything from a release, expect it to be a normal file
	if (m_inRelease.empty())
		ret = downloadToFile(m_url, downloadPath, false);
	else
		ret = downloadFromRelease(m_url, m_inRelease, downloadPath);

	if (ret != 0) {
		printf("\x1b[40;31mDownload failed!");
		goto failure;
	}
	else
		printf("\x1b[40;32mDownload successful!");

	if (!(m_inArchive.empty())) {
		printf("\n\x1b[40;34mExtracting file from the archive...\x1b[0m\n");
		ret = extractArchive(downloadPath, m_inArchive, m_path);

		if (deleteArchive)
			deleteFile(downloadPath.c_str());

		if (ret != 0) {
			printf("\x1b[40;31mExtraction failed!");
			goto failure;
		}
		else
			printf("\x1b[40;32mExtraction successful!");
	}

	if (matchPattern(".*(\\.cia)$", m_path)) {
		printf("\n\x1b[40;34mInstalling CIA...\x1b[0m\n");
		ret = installCia(m_path.c_str());
		if (deleteCia)
			deleteFile(m_path.c_str());

		if (ret != 0) {
			printf("\x1b[40;31mInstall failed!");
			goto failure;
		}
		else
			printf("\x1b[40;32mInstall successful!");
	}

	printf("\n\x1b[40;32mUpdate complete!\x1b[0m\n");
	state = STATE_SUCCESS;
	return;

	failure:
	printf("\n\x1b[40;31mUpdate failed!\n");
	printf("\x1b[40;33mError code: %.8lx\x1b[0m\n", ret);
	state = STATE_FAILED;
	return;
}

Config::Config()
{
	m_selfUpdater = true;
	m_deleteCIA = false;
	m_deleteArchive = true;

	Handle configHandle;
	char* configString = nullptr;
	Result ret = openFile(&configHandle, CONFIG_FILE_PATH, false);

	if (R_FAILED(ret)) {
		json configDownloadJson;
		configDownloadJson["name"] = "Download the latest example config!";
		configDownloadJson["url"] = CONFIG_FILE_URL;
		configDownloadJson["path"] = CONFIG_FILE_PATH;

		Entry configDownloadEntry(configDownloadJson);
		entries.push_back(configDownloadEntry);
	}
	else {
		u64 configSize = 0;
		FSFILE_GetSize(configHandle, &configSize);
		configString = (char*)calloc(configSize+1, sizeof(char));
		u32 bytesRead = 0;
		FSFILE_Read(configHandle, &bytesRead, 0, configString, (u32)configSize);

		json parsedConfig = json::parse(configString);

		if (parsedConfig["config"].is_object()) {
			if (parsedConfig["config"]["self_updater"].is_boolean())
				m_selfUpdater = parsedConfig["config"]["self_updater"];
			if (parsedConfig["config"]["delete_cias"].is_boolean())
				m_deleteCIA = parsedConfig["config"]["delete_cias"];
			if (parsedConfig["config"]["delete_archives"].is_boolean())
				m_deleteArchive = parsedConfig["config"]["delete_archives"];
		}

		if (m_selfUpdater) {
			json selfUpdaterJson;
			selfUpdaterJson["name"] = "MultiUpdater";
			selfUpdaterJson["url"] = "https://github.com/LiquidFenrir/MultiUpdater";
			selfUpdaterJson["inrelease"] = "MultiUpdater.*\\.zip";
			selfUpdaterJson["inarchive"] = "MultiUpdater.cia";
			selfUpdaterJson["path"] = "/cias/MultiUpdater.cia";

			Entry selfUpdateEntry(selfUpdaterJson);
			entries.push_back(selfUpdateEntry);
		}

		if (parsedConfig["entries"].is_array()) {
			for (auto jsonEntry : parsedConfig["entries"]) {
				if (jsonEntry.is_object()) {
					Entry configEntry(jsonEntry);
					entries.push_back(configEntry);
				}
			}
		}
	}

	free(configString);
	FSFILE_Close(configHandle);
}
<|endoftext|>