<commit_before>/** @file bts/blockchain/fork_blocks.hpp
 *  @brief Defines global block number constants for when hardforks take effect
 */

#pragma once

#include <stdint.h>
#include <vector>

#define BTS_EXPECTED_CHAIN_ID       digest_type( "6984e235f2573526db030e92f4844312ee93d63068c5224506a34a9c0b7b5db7" )
#define BTS_DESIRED_CHAIN_ID        digest_type( "2fbac26f05df0f2e8a66794d7d9eeae8a14d1478c040fb9ec02b4e6a9b09971e" )

#define BTS_V0_4_0_FORK_BLOCK_NUM        0
#define BTS_V0_4_9_FORK_BLOCK_NUM        0
#define BTS_V0_4_9_FORK_2_BLOCK_NUM      0
#define BTS_V0_4_10_FORK_BLOCK_NUM       0
#define BTS_V0_4_12_FORK_BLOCK_NUM       0
#define BTS_V0_4_13_FORK_BLOCK_NUM       0
#define BTS_V0_4_15_FORK_BLOCK_NUM       0
#define BTS_V0_4_16_FORK_BLOCK_NUM       0
#define BTS_V0_4_17_FORK_BLOCK_NUM       0
#define BTS_V0_4_18_FORK_BLOCK_NUM       0
#define BTS_V0_4_19_FORK_BLOCK_NUM       0
#define BTS_V0_4_21_FORK_BLOCK_NUM       0
#define BTS_V0_4_23_FORK_BLOCK_NUM       0
#define BTS_V0_4_24_FORK_BLOCK_NUM       0
#define BTS_V0_4_26_FORK_BLOCK_NUM       0
#define BTS_V0_5_0_FORK_BLOCK_NUM    25000
#define BTS_V0_6_0_FORK_BLOCK_NUM   120000
#define BTS_V0_6_1_FORK_BLOCK_NUM   999999

#define BTS_V0_4_28_FORK_BLOCK_NUM  9999999
#define BTS_V0_4_29_FORK_BLOCK_NUM  9999999

#define BTS_FORK_TO_UNIX_TIME_LIST  ((BTS_V0_4_0_FORK_BLOCK_NUM,   "0.4.0",     1408064036)) \
                                    ((BTS_V0_4_9_FORK_2_BLOCK_NUM, "0.4.9",     1409193626)) \
                                    ((BTS_V0_4_10_FORK_BLOCK_NUM,  "0.4.10",    1409437355)) \
                                    ((BTS_V0_4_12_FORK_BLOCK_NUM,  "0.4.12",    1409846462)) \
                                    ((BTS_V0_4_13_FORK_BLOCK_NUM,  "0.4.13",    1410294635)) \
                                    ((BTS_V0_4_15_FORK_BLOCK_NUM,  "0.4.15",    1410657316)) \
                                    ((BTS_V0_4_16_FORK_BLOCK_NUM,  "0.4.16",    1411258737)) \
                                    ((BTS_V0_4_17_FORK_BLOCK_NUM,  "0.4.17",    1411599233)) \
                                    ((BTS_V0_4_18_FORK_BLOCK_NUM,  "0.4.18",    1411765631)) \
                                    ((BTS_V0_4_19_FORK_BLOCK_NUM,  "0.4.19",    1412203442)) \
                                    ((BTS_V0_4_21_FORK_BLOCK_NUM,  "0.4.21",    1414019090))
                                    //((BTS_V0_4_23_FORK_BLOCK_NUM,  "0.4.23",    1414527136)) \
                                    //((BTS_V0_4_24_FORK_BLOCK_NUM,  "0.4.24",    1415731817)) \
                                    //((BTS_V0_4_26_FORK_BLOCK_NUM,  "0.4.26",    1418402446))

namespace bts { namespace blockchain {
  uint32_t estimate_last_known_fork_from_git_revision_timestamp(uint32_t revision_time);
  std::vector<uint32_t> get_list_of_fork_block_numbers();
} }
<commit_msg>Set fork block num<commit_after>/** @file bts/blockchain/fork_blocks.hpp
 *  @brief Defines global block number constants for when hardforks take effect
 */

#pragma once

#include <stdint.h>
#include <vector>

#define BTS_EXPECTED_CHAIN_ID       digest_type( "6984e235f2573526db030e92f4844312ee93d63068c5224506a34a9c0b7b5db7" )
#define BTS_DESIRED_CHAIN_ID        digest_type( "2fbac26f05df0f2e8a66794d7d9eeae8a14d1478c040fb9ec02b4e6a9b09971e" )

#define BTS_V0_4_0_FORK_BLOCK_NUM        0
#define BTS_V0_4_9_FORK_BLOCK_NUM        0
#define BTS_V0_4_9_FORK_2_BLOCK_NUM      0
#define BTS_V0_4_10_FORK_BLOCK_NUM       0
#define BTS_V0_4_12_FORK_BLOCK_NUM       0
#define BTS_V0_4_13_FORK_BLOCK_NUM       0
#define BTS_V0_4_15_FORK_BLOCK_NUM       0
#define BTS_V0_4_16_FORK_BLOCK_NUM       0
#define BTS_V0_4_17_FORK_BLOCK_NUM       0
#define BTS_V0_4_18_FORK_BLOCK_NUM       0
#define BTS_V0_4_19_FORK_BLOCK_NUM       0
#define BTS_V0_4_21_FORK_BLOCK_NUM       0
#define BTS_V0_4_23_FORK_BLOCK_NUM       0
#define BTS_V0_4_24_FORK_BLOCK_NUM       0
#define BTS_V0_4_26_FORK_BLOCK_NUM       0
#define BTS_V0_5_0_FORK_BLOCK_NUM    25000
#define BTS_V0_6_0_FORK_BLOCK_NUM   120000
#define BTS_V0_6_1_FORK_BLOCK_NUM   129600

#define BTS_V0_4_28_FORK_BLOCK_NUM  9999999
#define BTS_V0_4_29_FORK_BLOCK_NUM  9999999

#define BTS_FORK_TO_UNIX_TIME_LIST  ((BTS_V0_4_0_FORK_BLOCK_NUM,   "0.4.0",     1408064036)) \
                                    ((BTS_V0_4_9_FORK_2_BLOCK_NUM, "0.4.9",     1409193626)) \
                                    ((BTS_V0_4_10_FORK_BLOCK_NUM,  "0.4.10",    1409437355)) \
                                    ((BTS_V0_4_12_FORK_BLOCK_NUM,  "0.4.12",    1409846462)) \
                                    ((BTS_V0_4_13_FORK_BLOCK_NUM,  "0.4.13",    1410294635)) \
                                    ((BTS_V0_4_15_FORK_BLOCK_NUM,  "0.4.15",    1410657316)) \
                                    ((BTS_V0_4_16_FORK_BLOCK_NUM,  "0.4.16",    1411258737)) \
                                    ((BTS_V0_4_17_FORK_BLOCK_NUM,  "0.4.17",    1411599233)) \
                                    ((BTS_V0_4_18_FORK_BLOCK_NUM,  "0.4.18",    1411765631)) \
                                    ((BTS_V0_4_19_FORK_BLOCK_NUM,  "0.4.19",    1412203442)) \
                                    ((BTS_V0_4_21_FORK_BLOCK_NUM,  "0.4.21",    1414019090))
                                    //((BTS_V0_4_23_FORK_BLOCK_NUM,  "0.4.23",    1414527136)) \
                                    //((BTS_V0_4_24_FORK_BLOCK_NUM,  "0.4.24",    1415731817)) \
                                    //((BTS_V0_4_26_FORK_BLOCK_NUM,  "0.4.26",    1418402446))

namespace bts { namespace blockchain {
  uint32_t estimate_last_known_fork_from_git_revision_timestamp(uint32_t revision_time);
  std::vector<uint32_t> get_list_of_fork_block_numbers();
} }
<|endoftext|>


<commit_before>/*
 * qhtmlnodemodel - A QAbstractXmlNodeModel for HTML documents
 * Copyright (C) 2011 Jonas Gehring
 * 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 copyright holders 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 HOLDER 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 <QList>
#include <QHash>

#include <htmlcxx/html/ParserDom.h>

#include "qhtmlnodemodel.h"

using namespace htmlcxx;

// Internal private data
class QHtmlNodeModelPrivate
{
public:
	QHtmlNodeModel *model;
	QUrl uri;

	HTML::ParserDom parser;
	tree<HTML::Node> dom;

	// Mapping of attribute names to IDs
	mutable QHash<QByteArray, int> attributeIds;
	mutable QList<QByteArray> attributeNames;
	mutable int attributeCount;
	const int firstAttribute;

	QHtmlNodeModelPrivate(QHtmlNodeModel *model)
		: model(model), firstAttribute(1)
	{
		attributeCount = firstAttribute;
		while (attributeNames.count() < firstAttribute) {
			attributeNames += QByteArray();
		}
	}

	// Parses the given source tree
	void parse(const QByteArray &source)
	{
		dom = parser.parseTree(source.data());
	}

	// Converts a model index to a HTML node
	tree_node_<HTML::Node> *toNode(const QXmlNodeModelIndex &index) const
	{
		return (tree_node_<HTML::Node> *)index.internalPointer();
	}

	// Converts a model index to a tree iterator
	tree<HTML::Node>::iterator toIterator(const QXmlNodeModelIndex &index) const
	{
		return tree<HTML::Node>::iterator((tree_node_<HTML::Node> *)index.internalPointer());
	}

	// Converts a HTML node to a model index
	QXmlNodeModelIndex toNodeIndex(tree_node_<HTML::Node> *node) const
	{
		return model->createIndex((void *)node);
	}

	// Converts an attribute of a HTML node to a model index
	QXmlNodeModelIndex toNodeIndex(tree_node_<HTML::Node> *node, const QByteArray &attribute) const
	{
		// Register attribute if necessary
		if (!attributeIds.contains(attribute)) {
			attributeIds[attribute] = attributeCount;
			attributeNames += attribute;
			++attributeCount;
		}
		return model->createIndex((void *)node, attributeIds[attribute]);
	}

	// Converts a whole DOM tree to a model index
	QXmlNodeModelIndex toNodeIndex(tree<HTML::Node> *tree) const
	{
		return toNodeIndex(tree->begin().node);
	}

	// Checks if the given node represents an attribute
	bool isAttribute(const QXmlNodeModelIndex &index)
	{
		return (index.additionalData() >= firstAttribute);
	}

	// Returns the attribute name of the given index
	QByteArray attributeName(const QXmlNodeModelIndex &index)
	{
		int id = index.additionalData();
		if (id < attributeNames.count()) {
			return attributeNames[id];
		}
		return QByteArray();
	}
};

/*!
 * Constructor
 */
QHtmlNodeModel::QHtmlNodeModel(const QXmlNamePool &namepool, const QByteArray &source, const QUrl &uri)
	: QSimpleXmlNodeModel(namepool), d(new QHtmlNodeModelPrivate(this))
{
	d->uri = uri;
	d->parse(source);
}

/*!
 * Destructor
 */
QHtmlNodeModel::~QHtmlNodeModel()
{
	delete d;
}

/*!
 * This function returns the relative document order for the
 * nodes indexed by \a ni1 and \a ni2. It is used for the \c Is
 * operator and for sorting nodes in document order.
 */
QXmlNodeModelIndex::DocumentOrder QHtmlNodeModel::compareOrder(const QXmlNodeModelIndex &, const QXmlNodeModelIndex &) const
{
	// No order defined here
	return QXmlNodeModelIndex::Precedes;
}

/*!
 * Returns the name of \a ni. The caller guarantees that \a ni is not
 * \c null and that it belongs to this QAbstractXmlNodeModel.
 */
QXmlName QHtmlNodeModel::name(const QXmlNodeModelIndex &node) const
{
	if (d->isAttribute(node)) {
		return QXmlName(namePool(), d->attributeName(node));
	}

	tree<HTML::Node>::iterator it = d->toIterator(node);
	if (it->isComment()) {
		return QXmlName(namePool(), "_comment");
	}
	return QXmlName(namePool(), QLatin1String(it->tagName().c_str()));
}

/*!
 * Returns the document URI of \a n. The document URI identifies the
 * resource which is the document. For example, the document could be a
 * regular file, e.g., \c{file:/}, or it could be the \c{http://} URL of
 * the location of a file. The document URI is used for resolving URIs
 * and to simply know where the document is.
 */
QUrl QHtmlNodeModel::documentUri(const QXmlNodeModelIndex &node) const
{
	Q_UNUSED(node);
	return d->uri;
}

/*!
 * Returns a value indicating the kind of node identified by \a ni.
 * The caller guarantees that \a ni is not null and that it identifies
 * a node in this node model. This function maps to the \c
 * dm:node-kind() accessor.
 */
QXmlNodeModelIndex::NodeKind QHtmlNodeModel::kind(const QXmlNodeModelIndex &node) const
{
	if (d->isAttribute(node)) {
		return QXmlNodeModelIndex::Attribute;
	}
	return QXmlNodeModelIndex::Element;
}

/*!
 * Returns the root node of the tree that contains the node whose index
 * is \a n. The caller guarantees that \a n is not \c null and that it
 * identifies a node in this node model.
 */
QXmlNodeModelIndex QHtmlNodeModel::root(const QXmlNodeModelIndex &node) const
{
	Q_UNUSED(node)
	return d->toNodeIndex(&d->dom);
}

/*!
 * Returns the typed value for node \a node.
 */
QVariant QHtmlNodeModel::typedValue(const QXmlNodeModelIndex &node) const
{
	tree<HTML::Node>::iterator it = d->toIterator(node);
	if (d->isAttribute(node)) {
		QByteArray attrName = d->attributeName(node);
		return QString(it->attribute(attrName.data()).second.c_str());
	}
	return QString(it->tagName().c_str());
}

/*!
 * Returns the attributes of \a element. The caller guarantees
 * that \a element is an element in this node model.
 */
QVector<QXmlNodeModelIndex> QHtmlNodeModel::attributes(const QXmlNodeModelIndex &element) const
{
	QVector<QXmlNodeModelIndex> result;

	tree_node_<HTML::Node> *t = d->toNode(element);
	t->data.parseAttributes();

	std::map<std::string, std::string>::const_iterator it;
	std::map<std::string, std::string>::const_iterator end = t->data.attributes().end();
	for (it = t->data.attributes().begin(); it != end; ++it) {
		result += d->toNodeIndex(t, it->first.c_str());
	}

	return result;
}

/*!
 * When QtXmlPatterns evaluate path expressions, it emulate them through a
 * combination of calls with QSimpleXmlNodeModel::SimpleAxis values. Therefore,
 * the implementation of this function must return the node, if any, that
 * appears on the \a axis emanating from the \a origin.
 */
QXmlNodeModelIndex QHtmlNodeModel::nextFromSimpleAxis(SimpleAxis axis, const QXmlNodeModelIndex &origin) const
{
	tree<HTML::Node>::iterator it = d->toIterator(origin), jt;
	switch (axis) {
		case Parent:
			return d->toNodeIndex(d->dom.parent(it).node);

		case FirstChild:
			jt = d->dom.begin(it);
			if (jt == d->dom.end(it)) {
				return QXmlNodeModelIndex();
			}
			return d->toNodeIndex(jt.node);

		case PreviousSibling:
			jt = d->dom.previous_sibling(it);
			if (!d->dom.is_valid(jt)) {
				return QXmlNodeModelIndex();
			}
			return d->toNodeIndex(jt.node);

		case NextSibling:
			jt = d->dom.next_sibling(it);
			if (!d->dom.is_valid(jt)) {
				return QXmlNodeModelIndex();
			}
			return d->toNodeIndex(jt.node);
	}

	Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid axis!");
	return QXmlNodeModelIndex();
}
<commit_msg>Fixed representation of comment and text nodes<commit_after>/*
 * qhtmlnodemodel - A QAbstractXmlNodeModel for HTML documents
 * Copyright (C) 2011 Jonas Gehring
 * 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 copyright holders 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 HOLDER 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 <QList>
#include <QHash>

#include <htmlcxx/html/ParserDom.h>

#include "qhtmlnodemodel.h"

using namespace htmlcxx;

// Internal private data
class QHtmlNodeModelPrivate
{
public:
	QHtmlNodeModel *model;
	QUrl uri;

	HTML::ParserDom parser;
	tree<HTML::Node> dom;

	// Mapping of attribute names to IDs
	mutable QHash<QByteArray, int> attributeIds;
	mutable QList<QByteArray> attributeNames;
	mutable int attributeCount;
	const int firstAttribute;

	QHtmlNodeModelPrivate(QHtmlNodeModel *model)
		: model(model), firstAttribute(1)
	{
		attributeCount = firstAttribute;
		while (attributeNames.count() < firstAttribute) {
			attributeNames += QByteArray();
		}
	}

	// Parses the given source tree
	void parse(const QByteArray &source)
	{
		dom = parser.parseTree(source.data());
	}

	// Converts a model index to a HTML node
	tree_node_<HTML::Node> *toNode(const QXmlNodeModelIndex &index) const
	{
		return (tree_node_<HTML::Node> *)index.internalPointer();
	}

	// Converts a model index to a tree iterator
	tree<HTML::Node>::iterator toIterator(const QXmlNodeModelIndex &index) const
	{
		return tree<HTML::Node>::iterator((tree_node_<HTML::Node> *)index.internalPointer());
	}

	// Converts a HTML node to a model index
	QXmlNodeModelIndex toNodeIndex(tree_node_<HTML::Node> *node) const
	{
		return model->createIndex((void *)node);
	}

	// Converts an attribute of a HTML node to a model index
	QXmlNodeModelIndex toNodeIndex(tree_node_<HTML::Node> *node, const QByteArray &attribute) const
	{
		// Register attribute if necessary
		if (!attributeIds.contains(attribute)) {
			attributeIds[attribute] = attributeCount;
			attributeNames += attribute;
			++attributeCount;
		}
		return model->createIndex((void *)node, attributeIds[attribute]);
	}

	// Converts a whole DOM tree to a model index
	QXmlNodeModelIndex toNodeIndex(tree<HTML::Node> *tree) const
	{
		return toNodeIndex(tree->begin().node);
	}

	// Checks if the given node represents an attribute
	bool isAttribute(const QXmlNodeModelIndex &index)
	{
		return (index.additionalData() >= firstAttribute);
	}

	// Returns the attribute name of the given index
	QByteArray attributeName(const QXmlNodeModelIndex &index)
	{
		int id = index.additionalData();
		if (id < attributeNames.count()) {
			return attributeNames[id];
		}
		return QByteArray();
	}
};

/*!
 * Constructor
 */
QHtmlNodeModel::QHtmlNodeModel(const QXmlNamePool &namepool, const QByteArray &source, const QUrl &uri)
	: QSimpleXmlNodeModel(namepool), d(new QHtmlNodeModelPrivate(this))
{
	d->uri = uri;
	d->parse(source);
}

/*!
 * Destructor
 */
QHtmlNodeModel::~QHtmlNodeModel()
{
	delete d;
}

/*!
 * This function returns the relative document order for the
 * nodes indexed by \a ni1 and \a ni2. It is used for the \c Is
 * operator and for sorting nodes in document order.
 */
QXmlNodeModelIndex::DocumentOrder QHtmlNodeModel::compareOrder(const QXmlNodeModelIndex &, const QXmlNodeModelIndex &) const
{
	// No order defined here
	return QXmlNodeModelIndex::Precedes;
}

/*!
 * Returns the name of \a ni. The caller guarantees that \a ni is not
 * \c null and that it belongs to this QAbstractXmlNodeModel.
 */
QXmlName QHtmlNodeModel::name(const QXmlNodeModelIndex &node) const
{
	if (d->isAttribute(node)) {
		return QXmlName(namePool(), d->attributeName(node));
	}

	tree<HTML::Node>::iterator it = d->toIterator(node);
	if (!it->isTag()) {
		return QXmlName();
	}
	return QXmlName(namePool(), QLatin1String(it->tagName().c_str()));
}

/*!
 * Returns the document URI of \a n. The document URI identifies the
 * resource which is the document. For example, the document could be a
 * regular file, e.g., \c{file:/}, or it could be the \c{http://} URL of
 * the location of a file. The document URI is used for resolving URIs
 * and to simply know where the document is.
 */
QUrl QHtmlNodeModel::documentUri(const QXmlNodeModelIndex &node) const
{
	Q_UNUSED(node);
	return d->uri;
}

/*!
 * Returns a value indicating the kind of node identified by \a ni.
 * The caller guarantees that \a ni is not null and that it identifies
 * a node in this node model. This function maps to the \c
 * dm:node-kind() accessor.
 */
QXmlNodeModelIndex::NodeKind QHtmlNodeModel::kind(const QXmlNodeModelIndex &node) const
{
	if (d->isAttribute(node)) {
		return QXmlNodeModelIndex::Attribute;
	}

	tree<HTML::Node>::iterator it = d->toIterator(node);
	if (it->isComment()) {
		return QXmlNodeModelIndex::Comment;
	}
	if (it->isTag()) {
		return QXmlNodeModelIndex::Element;
	}
	return QXmlNodeModelIndex::Text;
}

/*!
 * Returns the root node of the tree that contains the node whose index
 * is \a n. The caller guarantees that \a n is not \c null and that it
 * identifies a node in this node model.
 */
QXmlNodeModelIndex QHtmlNodeModel::root(const QXmlNodeModelIndex &node) const
{
	Q_UNUSED(node);
	return d->toNodeIndex(&d->dom);
}

/*!
 * Returns the typed value for node \a node.
 */
QVariant QHtmlNodeModel::typedValue(const QXmlNodeModelIndex &node) const
{
	tree<HTML::Node>::iterator it = d->toIterator(node);
	if (d->isAttribute(node)) {
		QByteArray attrName = d->attributeName(node);
		return QString(it->attribute(attrName.data()).second.c_str());
	}

	if (!it->isTag()) {
		return QLatin1String(it->text().c_str());
	}
	return QString(it->tagName().c_str());
}

/*!
 * Returns the attributes of \a element. The caller guarantees
 * that \a element is an element in this node model.
 */
QVector<QXmlNodeModelIndex> QHtmlNodeModel::attributes(const QXmlNodeModelIndex &element) const
{
	QVector<QXmlNodeModelIndex> result;

	tree_node_<HTML::Node> *t = d->toNode(element);
	t->data.parseAttributes();

	std::map<std::string, std::string>::const_iterator it;
	std::map<std::string, std::string>::const_iterator end = t->data.attributes().end();
	for (it = t->data.attributes().begin(); it != end; ++it) {
		result += d->toNodeIndex(t, it->first.c_str());
	}

	return result;
}

/*!
 * When QtXmlPatterns evaluate path expressions, it emulate them through a
 * combination of calls with QSimpleXmlNodeModel::SimpleAxis values. Therefore,
 * the implementation of this function must return the node, if any, that
 * appears on the \a axis emanating from the \a origin.
 */
QXmlNodeModelIndex QHtmlNodeModel::nextFromSimpleAxis(SimpleAxis axis, const QXmlNodeModelIndex &origin) const
{
	tree<HTML::Node>::iterator it = d->toIterator(origin), jt;
	switch (axis) {
		case Parent:
			return d->toNodeIndex(d->dom.parent(it).node);

		case FirstChild:
			jt = d->dom.begin(it);
			if (jt == d->dom.end(it)) {
				return QXmlNodeModelIndex();
			}
			return d->toNodeIndex(jt.node);

		case PreviousSibling:
			jt = d->dom.previous_sibling(it);
			if (!d->dom.is_valid(jt)) {
				return QXmlNodeModelIndex();
			}
			return d->toNodeIndex(jt.node);

		case NextSibling:
			jt = d->dom.next_sibling(it);
			if (!d->dom.is_valid(jt)) {
				return QXmlNodeModelIndex();
			}
			return d->toNodeIndex(jt.node);
	}

	Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid axis!");
	return QXmlNodeModelIndex();
}
<|endoftext|>


<commit_before>// Copyright (c) 2011-2014 The Bitcoin developers
// Copyright (c) 2014-2015 The Dash developers
// Copyright (c) 2015-2017 The PIVX developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "clientmodel.h"

#include "guiconstants.h"
#include "peertablemodel.h"

#include "alert.h"
#include "chainparams.h"
#include "checkpoints.h"
#include "clientversion.h"
#include "main.h"
#include "masternode-sync.h"
#include "masternodeman.h"
#include "net.h"
#include "ui_interface.h"
#include "util.h"

#include <stdint.h>

#include <QDateTime>
#include <QDebug>
#include <QTimer>

static const int64_t nClientStartupTime = GetTime();

ClientModel::ClientModel(OptionsModel* optionsModel, QObject* parent) : QObject(parent),
                                                                        optionsModel(optionsModel),
                                                                        peerTableModel(0),
                                                                        cachedNumBlocks(0),
                                                                        cachedMasternodeCountString(""),
                                                                        cachedReindexing(0), cachedImporting(0),
                                                                        numBlocksAtStartup(-1), pollTimer(0)
{
    peerTableModel = new PeerTableModel(this);
    pollTimer = new QTimer(this);
    connect(pollTimer, SIGNAL(timeout()), this, SLOT(updateTimer()));
    pollTimer->start(MODEL_UPDATE_DELAY);

    pollMnTimer = new QTimer(this);
    connect(pollMnTimer, SIGNAL(timeout()), this, SLOT(updateMnTimer()));
    // no need to update as frequent as data for balances/txes/blocks
    pollMnTimer->start(MODEL_UPDATE_DELAY * 4);

    subscribeToCoreSignals();
}

ClientModel::~ClientModel()
{
    unsubscribeFromCoreSignals();
}

int ClientModel::getNumConnections(unsigned int flags) const
{
    LOCK(cs_vNodes);
    if (flags == CONNECTIONS_ALL) // Shortcut if we want total
        return vNodes.size();

    int nNum = 0;
    BOOST_FOREACH (CNode* pnode, vNodes)
        if (flags & (pnode->fInbound ? CONNECTIONS_IN : CONNECTIONS_OUT))
            nNum++;

    return nNum;
}

QString ClientModel::getMasternodeCountString() const
{
    return tr("Total: %1 (OBF compatible: %2 / Enabled: %3)").arg(QString::number((int)mnodeman.size())).arg(QString::number((int)mnodeman.CountEnabled(ActiveProtocol()))).arg(QString::number((int)mnodeman.CountEnabled()));
}

int ClientModel::getNumBlocks() const
{
    LOCK(cs_main);
    return chainActive.Height();
}

int ClientModel::getNumBlocksAtStartup()
{
    if (numBlocksAtStartup == -1) numBlocksAtStartup = getNumBlocks();
    return numBlocksAtStartup;
}

quint64 ClientModel::getTotalBytesRecv() const
{
    return CNode::GetTotalBytesRecv();
}

quint64 ClientModel::getTotalBytesSent() const
{
    return CNode::GetTotalBytesSent();
}

QDateTime ClientModel::getLastBlockDate() const
{
    LOCK(cs_main);
    if (chainActive.Tip())
        return QDateTime::fromTime_t(chainActive.Tip()->GetBlockTime());
    else
        return QDateTime::fromTime_t(Params().GenesisBlock().GetBlockTime()); // Genesis block's time of current network
}

double ClientModel::getVerificationProgress() const
{
    LOCK(cs_main);
    return Checkpoints::GuessVerificationProgress(chainActive.Tip());
}

void ClientModel::updateTimer()
{
    // Get required lock upfront. This avoids the GUI from getting stuck on
    // periodical polls if the core is holding the locks for a longer time -
    // for example, during a wallet rescan.
    TRY_LOCK(cs_main, lockMain);
    if (!lockMain)
        return;
    // Some quantities (such as number of blocks) change so fast that we don't want to be notified for each change.
    // Periodically check and update with a timer.
    int newNumBlocks = getNumBlocks();

    static int prevAttempt = -1;
    static int prevAssets = -1;

    // check for changed number of blocks we have, number of blocks peers claim to have, reindexing state and importing state
    if (cachedNumBlocks != newNumBlocks ||
        cachedReindexing != fReindex || cachedImporting != fImporting ||
        masternodeSync.RequestedMasternodeAttempt != prevAttempt || masternodeSync.RequestedMasternodeAssets != prevAssets) {
        cachedNumBlocks = newNumBlocks;
        cachedReindexing = fReindex;
        cachedImporting = fImporting;
        prevAttempt = masternodeSync.RequestedMasternodeAttempt;
        prevAssets = masternodeSync.RequestedMasternodeAssets;

        emit numBlocksChanged(newNumBlocks);
    }

    emit bytesChanged(getTotalBytesRecv(), getTotalBytesSent());
}

void ClientModel::updateMnTimer()
{
    // Get required lock upfront. This avoids the GUI from getting stuck on
    // periodical polls if the core is holding the locks for a longer time -
    // for example, during a wallet rescan.
    TRY_LOCK(cs_main, lockMain);
    if (!lockMain)
        return;
    QString newMasternodeCountString = getMasternodeCountString();

    if (cachedMasternodeCountString != newMasternodeCountString) {
        cachedMasternodeCountString = newMasternodeCountString;

        emit strMasternodesChanged(cachedMasternodeCountString);
    }
}

void ClientModel::updateNumConnections(int numConnections)
{
    emit numConnectionsChanged(numConnections);
}

void ClientModel::updateAlert(const QString& hash, int status)
{
    // Show error message notification for new alert
    if (status == CT_NEW) {
        uint256 hash_256;
        hash_256.SetHex(hash.toStdString());
        CAlert alert = CAlert::getAlertByHash(hash_256);
        if (!alert.IsNull()) {
            emit message(tr("Network Alert"), QString::fromStdString(alert.strStatusBar), CClientUIInterface::ICON_ERROR);
        }
    }

    emit alertsChanged(getStatusBarWarnings());
}

bool ClientModel::inInitialBlockDownload() const
{
    return IsInitialBlockDownload();
}

enum BlockSource ClientModel::getBlockSource() const
{
    if (fReindex)
        return BLOCK_SOURCE_REINDEX;
    else if (fImporting)
        return BLOCK_SOURCE_DISK;
    else if (getNumConnections() > 0)
        return BLOCK_SOURCE_NETWORK;

    return BLOCK_SOURCE_NONE;
}

QString ClientModel::getStatusBarWarnings() const
{
    return QString::fromStdString(GetWarnings("statusbar"));
}

OptionsModel* ClientModel::getOptionsModel()
{
    return optionsModel;
}

PeerTableModel* ClientModel::getPeerTableModel()
{
    return peerTableModel;
}

QString ClientModel::formatFullVersion() const
{
    return QString::fromStdString(FormatFullVersion());
}

QString ClientModel::formatBuildDate() const
{
    return QString::fromStdString(CLIENT_DATE);
}

bool ClientModel::isReleaseVersion() const
{
    return CLIENT_VERSION_IS_RELEASE;
}

QString ClientModel::clientName() const
{
    return QString::fromStdString(CLIENT_NAME);
}

QString ClientModel::formatClientStartupTime() const
{
    return QDateTime::fromTime_t(nClientStartupTime).toString();
}

// Handlers for core signals
static void ShowProgress(ClientModel* clientmodel, const std::string& title, int nProgress)
{
    // emits signal "showProgress"
    QMetaObject::invokeMethod(clientmodel, "showProgress", Qt::QueuedConnection,
        Q_ARG(QString, QString::fromStdString(title)),
        Q_ARG(int, nProgress));
}

static void NotifyNumConnectionsChanged(ClientModel* clientmodel, int newNumConnections)
{
    // Too noisy: qDebug() << "NotifyNumConnectionsChanged : " + QString::number(newNumConnections);
    QMetaObject::invokeMethod(clientmodel, "updateNumConnections", Qt::QueuedConnection,
        Q_ARG(int, newNumConnections));
}

static void NotifyAlertChanged(ClientModel* clientmodel, const uint256& hash, ChangeType status)
{
    qDebug() << "NotifyAlertChanged : " + QString::fromStdString(hash.GetHex()) + " status=" + QString::number(status);
    QMetaObject::invokeMethod(clientmodel, "updateAlert", Qt::QueuedConnection,
        Q_ARG(QString, QString::fromStdString(hash.GetHex())),
        Q_ARG(int, status));
}

void ClientModel::subscribeToCoreSignals()
{
    // Connect signals to client
    uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2));
    uiInterface.NotifyNumConnectionsChanged.connect(boost::bind(NotifyNumConnectionsChanged, this, _1));
    uiInterface.NotifyAlertChanged.connect(boost::bind(NotifyAlertChanged, this, _1, _2));
}

void ClientModel::unsubscribeFromCoreSignals()
{
    // Disconnect signals from client
    uiInterface.ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2));
    uiInterface.NotifyNumConnectionsChanged.disconnect(boost::bind(NotifyNumConnectionsChanged, this, _1));
    uiInterface.NotifyAlertChanged.disconnect(boost::bind(NotifyAlertChanged, this, _1, _2));
}
<commit_msg>Update clientmodel.cpp<commit_after>// Copyright (c) 2011-2014 The Bitcoin developers
// Copyright (c) 2014-2015 The Dash developers
// Copyright (c) 2015-2017 The PIVX developers
// Copyright (c) 2017 The Phore developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "clientmodel.h"

#include "guiconstants.h"
#include "peertablemodel.h"

#include "alert.h"
#include "chainparams.h"
#include "checkpoints.h"
#include "clientversion.h"
#include "main.h"
#include "masternode-sync.h"
#include "masternodeman.h"
#include "net.h"
#include "ui_interface.h"
#include "util.h"

#include <stdint.h>

#include <QDateTime>
#include <QDebug>
#include <QTimer>

static const int64_t nClientStartupTime = GetTime();

ClientModel::ClientModel(OptionsModel* optionsModel, QObject* parent) : QObject(parent),
                                                                        optionsModel(optionsModel),
                                                                        peerTableModel(0),
                                                                        cachedNumBlocks(0),
                                                                        cachedMasternodeCountString(""),
                                                                        cachedReindexing(0), cachedImporting(0),
                                                                        numBlocksAtStartup(-1), pollTimer(0)
{
    peerTableModel = new PeerTableModel(this);
    pollTimer = new QTimer(this);
    connect(pollTimer, SIGNAL(timeout()), this, SLOT(updateTimer()));
    pollTimer->start(MODEL_UPDATE_DELAY);

    pollMnTimer = new QTimer(this);
    connect(pollMnTimer, SIGNAL(timeout()), this, SLOT(updateMnTimer()));
    // no need to update as frequent as data for balances/txes/blocks
    pollMnTimer->start(MODEL_UPDATE_DELAY * 4);

    subscribeToCoreSignals();
}

ClientModel::~ClientModel()
{
    unsubscribeFromCoreSignals();
}

int ClientModel::getNumConnections(unsigned int flags) const
{
    LOCK(cs_vNodes);
    if (flags == CONNECTIONS_ALL) // Shortcut if we want total
        return vNodes.size();

    int nNum = 0;
    BOOST_FOREACH (CNode* pnode, vNodes)
        if (flags & (pnode->fInbound ? CONNECTIONS_IN : CONNECTIONS_OUT))
            nNum++;

    return nNum;
}

QString ClientModel::getMasternodeCountString() const
{
    return tr("Total: %1 (OBF compatible: %2 / Enabled: %3)").arg(QString::number((int)mnodeman.size())).arg(QString::number((int)mnodeman.CountEnabled(ActiveProtocol()))).arg(QString::number((int)mnodeman.CountEnabled()));
}

int ClientModel::getNumBlocks() const
{
    LOCK(cs_main);
    return chainActive.Height();
}

int ClientModel::getNumBlocksAtStartup()
{
    if (numBlocksAtStartup == -1) numBlocksAtStartup = getNumBlocks();
    return numBlocksAtStartup;
}

quint64 ClientModel::getTotalBytesRecv() const
{
    return CNode::GetTotalBytesRecv();
}

quint64 ClientModel::getTotalBytesSent() const
{
    return CNode::GetTotalBytesSent();
}

QDateTime ClientModel::getLastBlockDate() const
{
    LOCK(cs_main);
    if (chainActive.Tip())
        return QDateTime::fromTime_t(chainActive.Tip()->GetBlockTime());
    else
        return QDateTime::fromTime_t(Params().GenesisBlock().GetBlockTime()); // Genesis block's time of current network
}

double ClientModel::getVerificationProgress() const
{
    LOCK(cs_main);
    return Checkpoints::GuessVerificationProgress(chainActive.Tip());
}

void ClientModel::updateTimer()
{
    // Get required lock upfront. This avoids the GUI from getting stuck on
    // periodical polls if the core is holding the locks for a longer time -
    // for example, during a wallet rescan.
    TRY_LOCK(cs_main, lockMain);
    if (!lockMain)
        return;
    // Some quantities (such as number of blocks) change so fast that we don't want to be notified for each change.
    // Periodically check and update with a timer.
    int newNumBlocks = getNumBlocks();

    static int prevAttempt = -1;
    static int prevAssets = -1;

    // check for changed number of blocks we have, number of blocks peers claim to have, reindexing state and importing state
    if (cachedNumBlocks != newNumBlocks ||
        cachedReindexing != fReindex || cachedImporting != fImporting ||
        masternodeSync.RequestedMasternodeAttempt != prevAttempt || masternodeSync.RequestedMasternodeAssets != prevAssets) {
        cachedNumBlocks = newNumBlocks;
        cachedReindexing = fReindex;
        cachedImporting = fImporting;
        prevAttempt = masternodeSync.RequestedMasternodeAttempt;
        prevAssets = masternodeSync.RequestedMasternodeAssets;

        emit numBlocksChanged(newNumBlocks);
    }

    emit bytesChanged(getTotalBytesRecv(), getTotalBytesSent());
}

void ClientModel::updateMnTimer()
{
    // Get required lock upfront. This avoids the GUI from getting stuck on
    // periodical polls if the core is holding the locks for a longer time -
    // for example, during a wallet rescan.
    TRY_LOCK(cs_main, lockMain);
    if (!lockMain)
        return;
    QString newMasternodeCountString = getMasternodeCountString();

    if (cachedMasternodeCountString != newMasternodeCountString) {
        cachedMasternodeCountString = newMasternodeCountString;

        emit strMasternodesChanged(cachedMasternodeCountString);
    }
}

void ClientModel::updateNumConnections(int numConnections)
{
    emit numConnectionsChanged(numConnections);
}

void ClientModel::updateAlert(const QString& hash, int status)
{
    // Show error message notification for new alert
    if (status == CT_NEW) {
        uint256 hash_256;
        hash_256.SetHex(hash.toStdString());
        CAlert alert = CAlert::getAlertByHash(hash_256);
        if (!alert.IsNull()) {
            emit message(tr("Network Alert"), QString::fromStdString(alert.strStatusBar), CClientUIInterface::ICON_ERROR);
        }
    }

    emit alertsChanged(getStatusBarWarnings());
}

bool ClientModel::inInitialBlockDownload() const
{
    return IsInitialBlockDownload();
}

enum BlockSource ClientModel::getBlockSource() const
{
    if (fReindex)
        return BLOCK_SOURCE_REINDEX;
    else if (fImporting)
        return BLOCK_SOURCE_DISK;
    else if (getNumConnections() > 0)
        return BLOCK_SOURCE_NETWORK;

    return BLOCK_SOURCE_NONE;
}

QString ClientModel::getStatusBarWarnings() const
{
    return QString::fromStdString(GetWarnings("statusbar"));
}

OptionsModel* ClientModel::getOptionsModel()
{
    return optionsModel;
}

PeerTableModel* ClientModel::getPeerTableModel()
{
    return peerTableModel;
}

QString ClientModel::formatFullVersion() const
{
    return QString::fromStdString(FormatFullVersion());
}

QString ClientModel::formatBuildDate() const
{
    return QString::fromStdString(CLIENT_DATE);
}

bool ClientModel::isReleaseVersion() const
{
    return CLIENT_VERSION_IS_RELEASE;
}

QString ClientModel::clientName() const
{
    return QString::fromStdString(CLIENT_NAME);
}

QString ClientModel::formatClientStartupTime() const
{
    return QDateTime::fromTime_t(nClientStartupTime).toString();
}

// Handlers for core signals
static void ShowProgress(ClientModel* clientmodel, const std::string& title, int nProgress)
{
    // emits signal "showProgress"
    QMetaObject::invokeMethod(clientmodel, "showProgress", Qt::QueuedConnection,
        Q_ARG(QString, QString::fromStdString(title)),
        Q_ARG(int, nProgress));
}

static void NotifyNumConnectionsChanged(ClientModel* clientmodel, int newNumConnections)
{
    // Too noisy: qDebug() << "NotifyNumConnectionsChanged : " + QString::number(newNumConnections);
    QMetaObject::invokeMethod(clientmodel, "updateNumConnections", Qt::QueuedConnection,
        Q_ARG(int, newNumConnections));
}

static void NotifyAlertChanged(ClientModel* clientmodel, const uint256& hash, ChangeType status)
{
    qDebug() << "NotifyAlertChanged : " + QString::fromStdString(hash.GetHex()) + " status=" + QString::number(status);
    QMetaObject::invokeMethod(clientmodel, "updateAlert", Qt::QueuedConnection,
        Q_ARG(QString, QString::fromStdString(hash.GetHex())),
        Q_ARG(int, status));
}

void ClientModel::subscribeToCoreSignals()
{
    // Connect signals to client
    uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2));
    uiInterface.NotifyNumConnectionsChanged.connect(boost::bind(NotifyNumConnectionsChanged, this, _1));
    uiInterface.NotifyAlertChanged.connect(boost::bind(NotifyAlertChanged, this, _1, _2));
}

void ClientModel::unsubscribeFromCoreSignals()
{
    // Disconnect signals from client
    uiInterface.ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2));
    uiInterface.NotifyNumConnectionsChanged.disconnect(boost::bind(NotifyNumConnectionsChanged, this, _1));
    uiInterface.NotifyAlertChanged.disconnect(boost::bind(NotifyAlertChanged, this, _1, _2));
}
<|endoftext|>


<commit_before>#include "notificator.h"

#include <QMetaType>
#include <QVariant>
#include <QIcon>
#include <QApplication>
#include <QStyle>
#include <QByteArray>
#include <QSystemTrayIcon>
#include <QMessageBox>
#include <QTemporaryFile>
#include <QImageWriter>

#ifdef USE_DBUS
#include <QtDBus>
#include <stdint.h>
#endif

#ifdef Q_OS_MAC
#include <ApplicationServices/ApplicationServices.h>
extern bool qt_mac_execute_apple_script(const QString &script, AEDesc *ret);
#endif

#ifdef USE_DBUS
// https://wiki.ubuntu.com/NotificationDevelopmentGuidelines recommends at least 128
const int FREEDESKTOP_NOTIFICATION_ICON_SIZE = 128;
#endif

Notificator::Notificator(const QString &programName, QSystemTrayIcon *trayicon, QWidget *parent):
    QObject(parent),
    parent(parent),
    programName(programName),
    mode(None),
    trayIcon(trayicon)
#ifdef USE_DBUS
    ,interface(0)
#endif
{
    if(trayicon && trayicon->supportsMessages())
    {
        mode = QSystemTray;
    }
#ifdef USE_DBUS
    interface = new QDBusInterface("org.freedesktop.Notifications",
          "/org/freedesktop/Notifications", "org.freedesktop.Notifications");
    if(interface->isValid())
    {
        mode = Freedesktop;
    }
#endif
#ifdef Q_OS_MAC
    // Check if Growl is installed (based on Qt's tray icon implementation)
    CFURLRef cfurl;
    OSStatus status = LSGetApplicationForInfo(kLSUnknownType, kLSUnknownCreator, CFSTR("growlTicket"), kLSRolesAll, 0, &cfurl);
    if (status != kLSApplicationNotFoundErr) {
        CFBundleRef bundle = CFBundleCreate(0, cfurl);
        if (CFStringCompare(CFBundleGetIdentifier(bundle), CFSTR("com.Growl.GrowlHelperApp"), kCFCompareCaseInsensitive | kCFCompareBackwards) == kCFCompareEqualTo) {
            if (CFStringHasSuffix(CFURLGetString(cfurl), CFSTR("/Growl.app/")))
                mode = Growl13;
            else
                mode = Growl12;
        }
        CFRelease(cfurl);
        CFRelease(bundle);
    }
#endif
}

Notificator::~Notificator()
{
#ifdef USE_DBUS
    delete interface;
#endif
}

#ifdef USE_DBUS

// Loosely based on http://www.qtcentre.org/archive/index.php/t-25879.html
class FreedesktopImage
{
public:
    FreedesktopImage() {}
    FreedesktopImage(const QImage &img);

    static int metaType();

    // Image to variant that can be marshalled over DBus
    static QVariant toVariant(const QImage &img);

private:
    int width, height, stride;
    bool hasAlpha;
    int channels;
    int bitsPerSample;
    QByteArray image;

    friend QDBusArgument &operator<<(QDBusArgument &a, const FreedesktopImage &i);
    friend const QDBusArgument &operator>>(const QDBusArgument &a, FreedesktopImage &i);
};

Q_DECLARE_METATYPE(FreedesktopImage);

// Image configuration settings
const int CHANNELS = 4;
const int BYTES_PER_PIXEL = 4;
const int BITS_PER_SAMPLE = 8;

FreedesktopImage::FreedesktopImage(const QImage &img):
    width(img.width()),
    height(img.height()),
    stride(img.width() * BYTES_PER_PIXEL),
    hasAlpha(true),
    channels(CHANNELS),
    bitsPerSample(BITS_PER_SAMPLE)
{
    // Convert 00xAARRGGBB to RGBA bytewise (endian-independent) format
    QImage tmp = img.convertToFormat(QImage::Format_ARGB32);
    const uint32_t *data = reinterpret_cast<const uint32_t*>(tmp.bits());

    unsigned int num_pixels = width * height;
    image.resize(num_pixels * BYTES_PER_PIXEL);

    for(unsigned int ptr = 0; ptr < num_pixels; ++ptr)
    {
        image[ptr*BYTES_PER_PIXEL+0] = data[ptr] >> 16; // R
        image[ptr*BYTES_PER_PIXEL+1] = data[ptr] >> 8;  // G
        image[ptr*BYTES_PER_PIXEL+2] = data[ptr];       // B
        image[ptr*BYTES_PER_PIXEL+3] = data[ptr] >> 24; // A
    }
}

QDBusArgument &operator<<(QDBusArgument &a, const FreedesktopImage &i)
{
    a.beginStructure();
    a << i.width << i.height << i.stride << i.hasAlpha << i.bitsPerSample << i.channels << i.image;
    a.endStructure();
    return a;
}

const QDBusArgument &operator>>(const QDBusArgument &a, FreedesktopImage &i)
{
    a.beginStructure();
    a >> i.width >> i.height >> i.stride >> i.hasAlpha >> i.bitsPerSample >> i.channels >> i.image;
    a.endStructure();
    return a;
}

int FreedesktopImage::metaType()
{
    return qDBusRegisterMetaType<FreedesktopImage>();
}

QVariant FreedesktopImage::toVariant(const QImage &img)
{
    FreedesktopImage fimg(img);
    return QVariant(FreedesktopImage::metaType(), &fimg);
}

void Notificator::notifyDBus(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout)
{
    Q_UNUSED(cls);
    // Arguments for DBus call:
    QList<QVariant> args;

    // Program Name:
    args.append(programName);

    // Unique ID of this notification type:
    args.append(0U);

    // Application Icon, empty string
    args.append(QString());

    // Summary
    args.append(title);

    // Body
    args.append(text);

    // Actions (none, actions are deprecated)
    QStringList actions;
    args.append(actions);

    // Hints
    QVariantMap hints;

    // If no icon specified, set icon based on class
    QIcon tmpicon;
    if(icon.isNull())
    {
        QStyle::StandardPixmap sicon = QStyle::SP_MessageBoxQuestion;
        switch(cls)
        {
        case Information: sicon = QStyle::SP_MessageBoxInformation; break;
        case Warning: sicon = QStyle::SP_MessageBoxWarning; break;
        case Critical: sicon = QStyle::SP_MessageBoxCritical; break;
        default: break;
        }
        tmpicon = QApplication::style()->standardIcon(sicon);
    }
    else
    {
        tmpicon = icon;
    }
    hints["icon_data"] = FreedesktopImage::toVariant(tmpicon.pixmap(FREEDESKTOP_NOTIFICATION_ICON_SIZE).toImage());
    args.append(hints);

    // Timeout (in msec)
    args.append(millisTimeout);

    // "Fire and forget"
    interface->callWithArgumentList(QDBus::NoBlock, "Notify", args);
}
#endif

void Notificator::notifySystray(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout)
{
    Q_UNUSED(icon);
    QSystemTrayIcon::MessageIcon sicon = QSystemTrayIcon::NoIcon;
    switch(cls) // Set icon based on class
    {
    case Information: sicon = QSystemTrayIcon::Information; break;
    case Warning: sicon = QSystemTrayIcon::Warning; break;
    case Critical: sicon = QSystemTrayIcon::Critical; break;
    }
    trayIcon->showMessage(title, text, sicon, millisTimeout);
}

// Based on Qt's tray icon implementation
#ifdef Q_OS_MAC
void Notificator::notifyGrowl(Class cls, const QString &title, const QString &text, const QIcon &icon)
{
    const QString script(
        "tell application \"%5\"\n"
        "  set the allNotificationsList to {\"Notification\"}\n" // -- Make a list of all the notification types (all)
        "  set the enabledNotificationsList to {\"Notification\"}\n" // -- Make a list of the notifications (enabled)
        "  register as application \"%1\" all notifications allNotificationsList default notifications enabledNotificationsList\n" // -- Register our script with Growl
        "  notify with name \"Notification\" title \"%2\" description \"%3\" application name \"%1\"%4\n" // -- Send a Notification
        "end tell"
    );

    QString notificationApp(QApplication::applicationName());
    if (notificationApp.isEmpty())
        notificationApp = "Application";

    QPixmap notificationIconPixmap;
    if (icon.isNull()) { // If no icon specified, set icon based on class
        QStyle::StandardPixmap sicon = QStyle::SP_MessageBoxQuestion;
        switch (cls)
        {
        case Information: sicon = QStyle::SP_MessageBoxInformation; break;
        case Warning: sicon = QStyle::SP_MessageBoxWarning; break;
        case Critical: sicon = QStyle::SP_MessageBoxCritical; break;
        }
        notificationIconPixmap = QApplication::style()->standardPixmap(sicon);
    }
    else {
        QSize size = icon.actualSize(QSize(48, 48));
        notificationIconPixmap = icon.pixmap(size);
    }

    QString notificationIcon;
    QTemporaryFile notificationIconFile;
    if (!notificationIconPixmap.isNull() && notificationIconFile.open()) {
        QImageWriter writer(&notificationIconFile, "PNG");
        if (writer.write(notificationIconPixmap.toImage()))
            notificationIcon = QString(" image from location \"file://%1\"").arg(notificationIconFile.fileName());
    }

    QString quotedTitle(title), quotedText(text);
    quotedTitle.replace("\\", "\\\\").replace("\"", "\\");
    quotedText.replace("\\", "\\\\").replace("\"", "\\");
    QString growlApp(this->mode == Notificator::Growl13 ? "Growl" : "GrowlHelperApp");
    qt_mac_execute_apple_script(script.arg(notificationApp, quotedTitle, quotedText, notificationIcon, growlApp), 0);
}
#endif

void Notificator::notify(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout)
{
    switch(mode)
    {
#ifdef USE_DBUS
    case Freedesktop:
        notifyDBus(cls, title, text, icon, millisTimeout);
        break;
#endif
    case QSystemTray:
        notifySystray(cls, title, text, icon, millisTimeout);
        break;
#ifdef Q_OS_MAC
    case Growl12:
    case Growl13:
        notifyGrowl(cls, title, text, icon);
        break;
#endif
    default:
        if(cls == Critical)
        {
            // Fall back to old fashioned pop-up dialog if critical and no other notification available
            QMessageBox::critical(parent, title, text, QMessageBox::Ok, QMessageBox::Ok);
        }
        break;
    }
}
<commit_msg>New Qt x64 does not have old api call<commit_after>#include "notificator.h"

#include <QMetaType>
#include <QVariant>
#include <QIcon>
#include <QApplication>
#include <QStyle>
#include <QByteArray>
#include <QSystemTrayIcon>
#include <QMessageBox>
#include <QTemporaryFile>
#include <QImageWriter>

#ifdef USE_DBUS
#include <QtDBus>
#include <stdint.h>
#endif

#ifdef Q_OS_MAC
#include <ApplicationServices/ApplicationServices.h>
//extern bool qt_mac_execute_apple_script(const QString &script, AEDesc *ret);
#endif

#ifdef USE_DBUS
// https://wiki.ubuntu.com/NotificationDevelopmentGuidelines recommends at least 128
const int FREEDESKTOP_NOTIFICATION_ICON_SIZE = 128;
#endif

Notificator::Notificator(const QString &programName, QSystemTrayIcon *trayicon, QWidget *parent):
    QObject(parent),
    parent(parent),
    programName(programName),
    mode(None),
    trayIcon(trayicon)
#ifdef USE_DBUS
    ,interface(0)
#endif
{
    if(trayicon && trayicon->supportsMessages())
    {
        mode = QSystemTray;
    }
#ifdef USE_DBUS
    interface = new QDBusInterface("org.freedesktop.Notifications",
          "/org/freedesktop/Notifications", "org.freedesktop.Notifications");
    if(interface->isValid())
    {
        mode = Freedesktop;
    }
#endif
#ifdef Q_OS_MAC
    // Check if Growl is installed (based on Qt's tray icon implementation)
    CFURLRef cfurl;
    OSStatus status = LSGetApplicationForInfo(kLSUnknownType, kLSUnknownCreator, CFSTR("growlTicket"), kLSRolesAll, 0, &cfurl);
    if (status != kLSApplicationNotFoundErr) {
        CFBundleRef bundle = CFBundleCreate(0, cfurl);
        if (CFStringCompare(CFBundleGetIdentifier(bundle), CFSTR("com.Growl.GrowlHelperApp"), kCFCompareCaseInsensitive | kCFCompareBackwards) == kCFCompareEqualTo) {
            if (CFStringHasSuffix(CFURLGetString(cfurl), CFSTR("/Growl.app/")))
                mode = Growl13;
            else
                mode = Growl12;
        }
        CFRelease(cfurl);
        CFRelease(bundle);
    }
#endif
}

Notificator::~Notificator()
{
#ifdef USE_DBUS
    delete interface;
#endif
}

#ifdef USE_DBUS

// Loosely based on http://www.qtcentre.org/archive/index.php/t-25879.html
class FreedesktopImage
{
public:
    FreedesktopImage() {}
    FreedesktopImage(const QImage &img);

    static int metaType();

    // Image to variant that can be marshalled over DBus
    static QVariant toVariant(const QImage &img);

private:
    int width, height, stride;
    bool hasAlpha;
    int channels;
    int bitsPerSample;
    QByteArray image;

    friend QDBusArgument &operator<<(QDBusArgument &a, const FreedesktopImage &i);
    friend const QDBusArgument &operator>>(const QDBusArgument &a, FreedesktopImage &i);
};

Q_DECLARE_METATYPE(FreedesktopImage);

// Image configuration settings
const int CHANNELS = 4;
const int BYTES_PER_PIXEL = 4;
const int BITS_PER_SAMPLE = 8;

FreedesktopImage::FreedesktopImage(const QImage &img):
    width(img.width()),
    height(img.height()),
    stride(img.width() * BYTES_PER_PIXEL),
    hasAlpha(true),
    channels(CHANNELS),
    bitsPerSample(BITS_PER_SAMPLE)
{
    // Convert 00xAARRGGBB to RGBA bytewise (endian-independent) format
    QImage tmp = img.convertToFormat(QImage::Format_ARGB32);
    const uint32_t *data = reinterpret_cast<const uint32_t*>(tmp.bits());

    unsigned int num_pixels = width * height;
    image.resize(num_pixels * BYTES_PER_PIXEL);

    for(unsigned int ptr = 0; ptr < num_pixels; ++ptr)
    {
        image[ptr*BYTES_PER_PIXEL+0] = data[ptr] >> 16; // R
        image[ptr*BYTES_PER_PIXEL+1] = data[ptr] >> 8;  // G
        image[ptr*BYTES_PER_PIXEL+2] = data[ptr];       // B
        image[ptr*BYTES_PER_PIXEL+3] = data[ptr] >> 24; // A
    }
}

QDBusArgument &operator<<(QDBusArgument &a, const FreedesktopImage &i)
{
    a.beginStructure();
    a << i.width << i.height << i.stride << i.hasAlpha << i.bitsPerSample << i.channels << i.image;
    a.endStructure();
    return a;
}

const QDBusArgument &operator>>(const QDBusArgument &a, FreedesktopImage &i)
{
    a.beginStructure();
    a >> i.width >> i.height >> i.stride >> i.hasAlpha >> i.bitsPerSample >> i.channels >> i.image;
    a.endStructure();
    return a;
}

int FreedesktopImage::metaType()
{
    return qDBusRegisterMetaType<FreedesktopImage>();
}

QVariant FreedesktopImage::toVariant(const QImage &img)
{
    FreedesktopImage fimg(img);
    return QVariant(FreedesktopImage::metaType(), &fimg);
}

void Notificator::notifyDBus(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout)
{
    Q_UNUSED(cls);
    // Arguments for DBus call:
    QList<QVariant> args;

    // Program Name:
    args.append(programName);

    // Unique ID of this notification type:
    args.append(0U);

    // Application Icon, empty string
    args.append(QString());

    // Summary
    args.append(title);

    // Body
    args.append(text);

    // Actions (none, actions are deprecated)
    QStringList actions;
    args.append(actions);

    // Hints
    QVariantMap hints;

    // If no icon specified, set icon based on class
    QIcon tmpicon;
    if(icon.isNull())
    {
        QStyle::StandardPixmap sicon = QStyle::SP_MessageBoxQuestion;
        switch(cls)
        {
        case Information: sicon = QStyle::SP_MessageBoxInformation; break;
        case Warning: sicon = QStyle::SP_MessageBoxWarning; break;
        case Critical: sicon = QStyle::SP_MessageBoxCritical; break;
        default: break;
        }
        tmpicon = QApplication::style()->standardIcon(sicon);
    }
    else
    {
        tmpicon = icon;
    }
    hints["icon_data"] = FreedesktopImage::toVariant(tmpicon.pixmap(FREEDESKTOP_NOTIFICATION_ICON_SIZE).toImage());
    args.append(hints);

    // Timeout (in msec)
    args.append(millisTimeout);

    // "Fire and forget"
    interface->callWithArgumentList(QDBus::NoBlock, "Notify", args);
}
#endif

void Notificator::notifySystray(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout)
{
    Q_UNUSED(icon);
    QSystemTrayIcon::MessageIcon sicon = QSystemTrayIcon::NoIcon;
    switch(cls) // Set icon based on class
    {
    case Information: sicon = QSystemTrayIcon::Information; break;
    case Warning: sicon = QSystemTrayIcon::Warning; break;
    case Critical: sicon = QSystemTrayIcon::Critical; break;
    }
    trayIcon->showMessage(title, text, sicon, millisTimeout);
}

// Based on Qt's tray icon implementation
#ifdef Q_OS_MAC
void Notificator::notifyGrowl(Class cls, const QString &title, const QString &text, const QIcon &icon)
{
    const QString script(
        "tell application \"%5\"\n"
        "  set the allNotificationsList to {\"Notification\"}\n" // -- Make a list of all the notification types (all)
        "  set the enabledNotificationsList to {\"Notification\"}\n" // -- Make a list of the notifications (enabled)
        "  register as application \"%1\" all notifications allNotificationsList default notifications enabledNotificationsList\n" // -- Register our script with Growl
        "  notify with name \"Notification\" title \"%2\" description \"%3\" application name \"%1\"%4\n" // -- Send a Notification
        "end tell"
    );

    QString notificationApp(QApplication::applicationName());
    if (notificationApp.isEmpty())
        notificationApp = "Application";

    QPixmap notificationIconPixmap;
    if (icon.isNull()) { // If no icon specified, set icon based on class
        QStyle::StandardPixmap sicon = QStyle::SP_MessageBoxQuestion;
        switch (cls)
        {
        case Information: sicon = QStyle::SP_MessageBoxInformation; break;
        case Warning: sicon = QStyle::SP_MessageBoxWarning; break;
        case Critical: sicon = QStyle::SP_MessageBoxCritical; break;
        }
        notificationIconPixmap = QApplication::style()->standardPixmap(sicon);
    }
    else {
        QSize size = icon.actualSize(QSize(48, 48));
        notificationIconPixmap = icon.pixmap(size);
    }

    QString notificationIcon;
    QTemporaryFile notificationIconFile;
    if (!notificationIconPixmap.isNull() && notificationIconFile.open()) {
        QImageWriter writer(&notificationIconFile, "PNG");
        if (writer.write(notificationIconPixmap.toImage()))
            notificationIcon = QString(" image from location \"file://%1\"").arg(notificationIconFile.fileName());
    }

    QString quotedTitle(title), quotedText(text);
    quotedTitle.replace("\\", "\\\\").replace("\"", "\\");
    quotedText.replace("\\", "\\\\").replace("\"", "\\");
    QString growlApp(this->mode == Notificator::Growl13 ? "Growl" : "GrowlHelperApp");
    //OI: disabled due to qt changes and x64
	//qt_mac_execute_apple_script(script.arg(notificationApp, quotedTitle, quotedText, notificationIcon, growlApp), 0);
}
#endif

void Notificator::notify(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout)
{
    switch(mode)
    {
#ifdef USE_DBUS
    case Freedesktop:
        notifyDBus(cls, title, text, icon, millisTimeout);
        break;
#endif
    case QSystemTray:
        notifySystray(cls, title, text, icon, millisTimeout);
        break;
#ifdef Q_OS_MAC
    case Growl12:
    case Growl13:
        notifyGrowl(cls, title, text, icon);
        break;
#endif
    default:
        if(cls == Critical)
        {
            // Fall back to old fashioned pop-up dialog if critical and no other notification available
            QMessageBox::critical(parent, title, text, QMessageBox::Ok, QMessageBox::Ok);
        }
        break;
    }
}
<|endoftext|>


<commit_before>/******************************************************************************
 *                    _   _____   __________                                  *
 *                   | | / / _ | / __/_  __/     Visibility                   *
 *                   | |/ / __ |_\ \  / /          Across                     *
 *                   |___/_/ |_/___/ /_/       Space and Time                 *
 *                                                                            *
 * This file is part of VAST. It is subject to the license terms in the       *
 * LICENSE file found in the top-level directory of this distribution and at  *
 * http://vast.io/license. No part of VAST, including this file, may be       *
 * copied, modified, propagated, or distributed except according to the terms *
 * contained in the LICENSE file.                                             *
 ******************************************************************************/

#define SUITE expression

#include "vast/expression.hpp"
#include "vast/expression_visitors.hpp"

#include "vast/test/test.hpp"

#include <string>

#include "vast/concept/parseable/to.hpp"
#include "vast/concept/parseable/vast/expression.hpp"
#include "vast/concept/parseable/vast/schema.hpp"
#include "vast/concept/parseable/vast/time.hpp"
#include "vast/concept/printable/to_string.hpp"
#include "vast/concept/printable/vast/expression.hpp"
#include "vast/detail/steady_map.hpp"
#include "vast/event.hpp"
#include "vast/load.hpp"
#include "vast/save.hpp"
#include "vast/schema.hpp"

using caf::get;
using caf::get_if;
using caf::holds_alternative;

using namespace std::string_literals;
using namespace vast;

namespace {

template <class T>
expression to_expr(T&& x) {
  return unbox(to<expression>(std::forward<T>(x)));
}

struct fixture {
  fixture() {
    // expr0 := !(x.y.z <= 42 && #foo == T)
    auto p0 = predicate{key_extractor{"x.y.z"}, less_equal, data{42}};
    auto p1 = predicate{attribute_extractor{caf::atom("foo")},
                        equal, data{true}};
    auto conj = conjunction{p0, p1};
    expr0 = negation{conj};
    // expr0 || :real > 4.2
    auto p2 = predicate{type_extractor{real_type{}}, greater_equal, data{4.2}};
    expr1 = disjunction{expr0, p2};
  }

  expression expr0;
  expression expr1;
};

} // namespace <anonymous>

FIXTURE_SCOPE(expr_tests, fixture)

TEST(construction) {
  auto n = caf::get_if<negation>(&expr0);
  REQUIRE(n);
  auto c = caf::get_if<conjunction>(&n->expr());
  REQUIRE(c);
  REQUIRE(c->size() == 2);
  auto p0 = caf::get_if<predicate>(&c->at(0));
  REQUIRE(p0);
  CHECK_EQUAL(get<key_extractor>(p0->lhs).key, "x.y.z");
  CHECK_EQUAL(p0->op, less_equal);
  CHECK(get<data>(p0->rhs) == data{42});
  auto p1 = caf::get_if<predicate>(&c->at(1));
  REQUIRE(p1);
  CHECK(get<attribute_extractor>(p1->lhs).attr == attribute{"foo"});
  CHECK_EQUAL(p1->op, equal);
  CHECK(get<data>(p1->rhs) == data{true});
}

TEST(serialization) {
  expression ex0, ex1;
  std::vector<char> buf;
  CHECK_EQUAL(save(nullptr, buf, expr0, expr1), caf::none);
  CHECK_EQUAL(load(nullptr, buf, ex0, ex1), caf::none);
  auto d = caf::get_if<disjunction>(&ex1);
  REQUIRE(d);
  REQUIRE(!d->empty());
  auto n = caf::get_if<negation>(&d->at(0));
  REQUIRE(n);
  auto c = caf::get_if<conjunction>(&n->expr());
  REQUIRE(c);
  REQUIRE_EQUAL(c->size(), 2u);
  auto p = caf::get_if<predicate>(&c->at(1));
  REQUIRE(p);
  CHECK_EQUAL(p->op, equal);
}

TEST(normalization) {
  MESSAGE("extractor on LHS");
  auto expr = to<expression>("\"foo\" in bar");
  auto normalized = to<expression>("bar ni \"foo\"");
  REQUIRE(expr);
  REQUIRE(normalized);
  CHECK_EQUAL(normalize(*expr), *normalized);
  // The normalizer must not touch predicates with two extractors, regardless
  // of whether that's actually a valid construct.
  expr = to<expression>("#foo == #bar");
  REQUIRE(expr);
  CHECK_EQUAL(normalize(*expr), *expr);
  MESSAGE("pushing down negations to predicate level");
  expr = to<expression>("! (x > 42 && x < 84)");
  normalized = to<expression>("x <= 42 || x >= 84");
  REQUIRE(expr);
  REQUIRE(normalized);
  CHECK_EQUAL(normalize(*expr), *normalized);
  MESSAGE("removal of negations");
  expr = to<expression>("! x < 42");
  normalized = to<expression>("x >= 42");
  REQUIRE(expr);
  REQUIRE(normalized);
  CHECK_EQUAL(normalize(*expr), *normalized);
  expr = to<expression>("x == 42");
  REQUIRE(expr);
  *expr = negation{expression{negation{std::move(*expr)}}};
  normalized = to<expression>("x == 42");
  REQUIRE(normalized);
  CHECK_EQUAL(normalize(*expr), *normalized);
  *expr = negation{std::move(*expr)};
  normalized = to<expression>("x != 42");
  REQUIRE(expr);
  REQUIRE(normalized);
  CHECK_EQUAL(normalize(*expr), *normalized);
  expr = to<expression>("! (x > -1 && x < +1)");
  normalized = to<expression>("x <= -1 || x >= +1");
  REQUIRE(expr);
  REQUIRE(normalized);
  CHECK_EQUAL(normalize(*expr), *normalized);
  MESSAGE("deduplication");
  expr = to<expression>("x == 42 || x == 42");
  normalized = to<expression>("x == 42");
  REQUIRE(expr);
  REQUIRE(normalized);
  CHECK_EQUAL(normalize(*expr), *normalized);
  expr = to<expression>("x == 42 || 42 == x");
  normalized = to<expression>("x == 42");
  REQUIRE(expr);
  REQUIRE(normalized);
  CHECK_EQUAL(normalize(*expr), *normalized);
  MESSAGE("performing all normalizations in one shot");
  expr = to<expression>("a > 42 && 42 < a && ! (\"foo\" in bar || ! x == 1337)");
  normalized = to<expression>("a > 42 && bar !ni \"foo\" && x == 1337");
  REQUIRE(expr);
  REQUIRE(normalized);
  CHECK_EQUAL(normalize(*expr), *normalized);
}

TEST(validation - attribute extractor) {
  // The "type" attribute extractor requires a string operand.
  auto expr = to<expression>("#type == \"foo\"");
  REQUIRE(expr);
  CHECK(caf::visit(validator{}, *expr));
  expr = to<expression>("#type == 42");
  REQUIRE(expr);
  CHECK(!caf::visit(validator{}, *expr));
  expr = to<expression>("#type == zeek.conn");
  REQUIRE(expr);
  CHECK(!caf::visit(validator{}, *expr));
  // The "time" attribute extractor requires a timestamp operand.
  expr = to<expression>("#time < now");
  REQUIRE(expr);
  CHECK(caf::visit(validator{}, *expr));
  expr = to<expression>("#time < 2017-06-16");
  REQUIRE(expr);
  CHECK(caf::visit(validator{}, *expr));
  expr = to<expression>("#time > -42");
  REQUIRE(expr);
  CHECK(!caf::visit(validator{}, *expr));
  expr = to<expression>("#time > -42 secs");
  REQUIRE(expr);
  CHECK(!caf::visit(validator{}, *expr));
}

TEST(validation - type extractor) {
  auto expr = to<expression>(":port == 443/tcp");
  REQUIRE(expr);
  CHECK(caf::visit(validator{}, *expr));
  expr = to<expression>(":addr in 10.0.0.0/8");
  REQUIRE(expr);
  CHECK(caf::visit(validator{}, *expr));
  expr = to<expression>(":port > -42");
  REQUIRE(expr);
  CHECK(!caf::visit(validator{}, *expr));
}

TEST(matcher) {
  auto match = [](const std::string& str, auto&& t) {
    auto expr = to<expression>(str);
    REQUIRE(expr);
    auto resolved = caf::visit(type_resolver(t), *expr);
    REQUIRE(resolved);
    return caf::visit(matcher{t}, *resolved);
  };
  MESSAGE("type extractors");
  CHECK(match(":real < 4.2", real_type{}));
  CHECK(!match(":int == -42", real_type{}));
  CHECK(!match(":count == 42 && :real < 4.2", real_type{}));
  CHECK(match(":count == 42 || :real < 4.2", real_type{}));
  auto r = record_type{
    {"x", real_type{}},
    {"y", port_type{}},
    {"z", address_type{}}
  };
  CHECK(match(":count == 42 || :real < 4.2", r));
  CHECK(match(":port == 80/tcp && :real < 4.2", r));
  MESSAGE("key extractors");
  CHECK(match("x < 4.2 || (y == 80/tcp && z in 10.0.0.0/8)", r));
  CHECK(match("x < 4.2 && (y == 80/tcp || :bool == F)", r));
  CHECK(!match("x < 4.2 && a == T", r));
  MESSAGE("attribute extractors");
  CHECK(!match("#type == \"foo\"", r));
  r = r.name("foo");
  CHECK(match("#type == \"foo\"", r));
  CHECK(match("#type != \"bar\"", r));
}

TEST(labeler) {
  auto str
    = "(x == 5 && :bool == T) || (foo ~ /foo/ && !(x == 5 || #type ~ /bar/))"s;
  auto expr = to_expr(str);
  // Create a visitor that records all offsets in order.
  detail::steady_map<expression, offset> offset_map;
  auto visitor = labeler{
    [&](const auto& x, const offset& o) {
      offset_map.emplace(x, o);
    }
  };
  caf::visit(visitor, expr);
  decltype(offset_map) expected_offset_map{
    {to_expr(str), {0}},
    {to_expr("x == 5 && :bool == T"), {0, 0}},
    {to_expr("x == 5"), {0, 0, 0}},
    {to_expr(":bool == T"), {0, 0, 1}},
    {to_expr("foo ~ /foo/ && !(x == 5 || #type ~ /bar/)"), {0, 1}},
    {to_expr("foo ~ /foo/"), {0, 1, 0}},
    {to_expr("!(x == 5 || #type ~ /bar/)"), {0, 1, 1}},
    {to_expr("x == 5 || #type ~ /bar/"), {0, 1, 1, 0}},
    {to_expr("x == 5"), {0, 1, 1, 0, 0}},
    {to_expr("#type ~ /bar/"), {0, 1, 1, 0, 1}},
  };
  CHECK_EQUAL(offset_map, expected_offset_map);
}

TEST(at) {
  auto str
    = "(x == 5 && :bool == T) || (foo ~ /foo/ && !(x == 5 || #type ~ /bar/))"s;
  auto expr = to_expr(str);
  CHECK_EQUAL(at(expr, {}), nullptr); // invalid offset
  CHECK_EQUAL(at(expr, {0}), &expr); // root node
  CHECK_EQUAL(at(expr, {1}), nullptr); // invalid root offset
  CHECK_EQUAL(*at(expr, {0, 0}), to_expr("x == 5 && :bool == T"));
  CHECK_EQUAL(*at(expr, {0, 1, 0}), to_expr("foo ~ /foo/"));
  CHECK_EQUAL(*at(expr, {0, 1, 1, 0, 1}), to_expr("#type ~ /bar/"));
  CHECK_EQUAL(at(expr, {0, 1, 1, 0, 1, 0}), nullptr); // offset too long
}

TEST(resolve) {
  using result_type = std::vector<std::pair<offset, predicate>>;
  auto resolve_pred = [](auto&& x, offset o, type t) -> result_type {
    result_type result;
    auto pred = to<predicate>(x);
    auto resolved = type_resolver{t}(unbox(pred));
    for (auto& pred : caf::visit(predicatizer{}, *resolved))
      result.emplace_back(o, std::move(pred));
    return result;
  };
  auto expr = to_expr("(x == 5 && y == T) || (x == 5 && y == F)"); // tautology
  auto t = record_type{
    {"x", count_type{}},
    {"y", boolean_type{}}
  }.name("foo");
  auto xs = resolve(expr, t);
  decltype(xs) expected;
  auto concat = [](auto&& xs, auto&& ys) {
    auto begin = std::make_move_iterator(ys.begin());
    auto end = std::make_move_iterator(ys.end());
    xs.insert(xs.end(), begin, end);
  };
  // TODO: How should we handle duplicates? Weed them out? --MV
  concat(expected, resolve_pred("x == 5", {0,0,0}, t));
  concat(expected, resolve_pred("y == T", {0,0,1}, t));
  concat(expected, resolve_pred("x == 5", {0,1,0}, t));
  concat(expected, resolve_pred("y == F", {0,1,1}, t));
  CHECK_EQUAL(xs, expected);
}

FIXTURE_SCOPE_END()
<commit_msg>Add test for type resolver with not_in operator<commit_after>/******************************************************************************
 *                    _   _____   __________                                  *
 *                   | | / / _ | / __/_  __/     Visibility                   *
 *                   | |/ / __ |_\ \  / /          Across                     *
 *                   |___/_/ |_/___/ /_/       Space and Time                 *
 *                                                                            *
 * This file is part of VAST. It is subject to the license terms in the       *
 * LICENSE file found in the top-level directory of this distribution and at  *
 * http://vast.io/license. No part of VAST, including this file, may be       *
 * copied, modified, propagated, or distributed except according to the terms *
 * contained in the LICENSE file.                                             *
 ******************************************************************************/

#define SUITE expression

#include "vast/expression.hpp"
#include "vast/expression_visitors.hpp"

#include "vast/test/test.hpp"

#include <string>

#include "vast/concept/parseable/to.hpp"
#include "vast/concept/parseable/vast/expression.hpp"
#include "vast/concept/parseable/vast/schema.hpp"
#include "vast/concept/parseable/vast/time.hpp"
#include "vast/concept/printable/to_string.hpp"
#include "vast/concept/printable/vast/expression.hpp"
#include "vast/detail/steady_map.hpp"
#include "vast/event.hpp"
#include "vast/load.hpp"
#include "vast/save.hpp"
#include "vast/schema.hpp"

using caf::get;
using caf::get_if;
using caf::holds_alternative;

using namespace std::string_literals;
using namespace vast;

namespace {

template <class T>
expression to_expr(T&& x) {
  return unbox(to<expression>(std::forward<T>(x)));
}

struct fixture {
  fixture() {
    // expr0 := !(x.y.z <= 42 && #foo == T)
    auto p0 = predicate{key_extractor{"x.y.z"}, less_equal, data{42}};
    auto p1 = predicate{attribute_extractor{caf::atom("foo")},
                        equal, data{true}};
    auto conj = conjunction{p0, p1};
    expr0 = negation{conj};
    // expr0 || :real > 4.2
    auto p2 = predicate{type_extractor{real_type{}}, greater_equal, data{4.2}};
    expr1 = disjunction{expr0, p2};
  }

  expression expr0;
  expression expr1;
};

} // namespace <anonymous>

FIXTURE_SCOPE(expr_tests, fixture)

TEST(construction) {
  auto n = caf::get_if<negation>(&expr0);
  REQUIRE(n);
  auto c = caf::get_if<conjunction>(&n->expr());
  REQUIRE(c);
  REQUIRE(c->size() == 2);
  auto p0 = caf::get_if<predicate>(&c->at(0));
  REQUIRE(p0);
  CHECK_EQUAL(get<key_extractor>(p0->lhs).key, "x.y.z");
  CHECK_EQUAL(p0->op, less_equal);
  CHECK(get<data>(p0->rhs) == data{42});
  auto p1 = caf::get_if<predicate>(&c->at(1));
  REQUIRE(p1);
  CHECK(get<attribute_extractor>(p1->lhs).attr == attribute{"foo"});
  CHECK_EQUAL(p1->op, equal);
  CHECK(get<data>(p1->rhs) == data{true});
}

TEST(serialization) {
  expression ex0, ex1;
  std::vector<char> buf;
  CHECK_EQUAL(save(nullptr, buf, expr0, expr1), caf::none);
  CHECK_EQUAL(load(nullptr, buf, ex0, ex1), caf::none);
  auto d = caf::get_if<disjunction>(&ex1);
  REQUIRE(d);
  REQUIRE(!d->empty());
  auto n = caf::get_if<negation>(&d->at(0));
  REQUIRE(n);
  auto c = caf::get_if<conjunction>(&n->expr());
  REQUIRE(c);
  REQUIRE_EQUAL(c->size(), 2u);
  auto p = caf::get_if<predicate>(&c->at(1));
  REQUIRE(p);
  CHECK_EQUAL(p->op, equal);
}

TEST(normalization) {
  MESSAGE("extractor on LHS");
  auto expr = to<expression>("\"foo\" in bar");
  auto normalized = to<expression>("bar ni \"foo\"");
  REQUIRE(expr);
  REQUIRE(normalized);
  CHECK_EQUAL(normalize(*expr), *normalized);
  // The normalizer must not touch predicates with two extractors, regardless
  // of whether that's actually a valid construct.
  expr = to<expression>("#foo == #bar");
  REQUIRE(expr);
  CHECK_EQUAL(normalize(*expr), *expr);
  MESSAGE("pushing down negations to predicate level");
  expr = to<expression>("! (x > 42 && x < 84)");
  normalized = to<expression>("x <= 42 || x >= 84");
  REQUIRE(expr);
  REQUIRE(normalized);
  CHECK_EQUAL(normalize(*expr), *normalized);
  MESSAGE("removal of negations");
  expr = to<expression>("! x < 42");
  normalized = to<expression>("x >= 42");
  REQUIRE(expr);
  REQUIRE(normalized);
  CHECK_EQUAL(normalize(*expr), *normalized);
  expr = to<expression>("x == 42");
  REQUIRE(expr);
  *expr = negation{expression{negation{std::move(*expr)}}};
  normalized = to<expression>("x == 42");
  REQUIRE(normalized);
  CHECK_EQUAL(normalize(*expr), *normalized);
  *expr = negation{std::move(*expr)};
  normalized = to<expression>("x != 42");
  REQUIRE(expr);
  REQUIRE(normalized);
  CHECK_EQUAL(normalize(*expr), *normalized);
  expr = to<expression>("! (x > -1 && x < +1)");
  normalized = to<expression>("x <= -1 || x >= +1");
  REQUIRE(expr);
  REQUIRE(normalized);
  CHECK_EQUAL(normalize(*expr), *normalized);
  MESSAGE("deduplication");
  expr = to<expression>("x == 42 || x == 42");
  normalized = to<expression>("x == 42");
  REQUIRE(expr);
  REQUIRE(normalized);
  CHECK_EQUAL(normalize(*expr), *normalized);
  expr = to<expression>("x == 42 || 42 == x");
  normalized = to<expression>("x == 42");
  REQUIRE(expr);
  REQUIRE(normalized);
  CHECK_EQUAL(normalize(*expr), *normalized);
  MESSAGE("performing all normalizations in one shot");
  expr = to<expression>("a > 42 && 42 < a && ! (\"foo\" in bar || ! x == 1337)");
  normalized = to<expression>("a > 42 && bar !ni \"foo\" && x == 1337");
  REQUIRE(expr);
  REQUIRE(normalized);
  CHECK_EQUAL(normalize(*expr), *normalized);
}

TEST(type extractor) {
  MESSAGE("extractor !in data");
  auto r = record_type{{"orig_h", address_type{}}, {"resp_h", address_type{}}};
  auto expr = unbox(to<expression>(":addr !in 192.168.0.0/24"));
  auto resolved = caf::visit(type_resolver(r), expr);
  auto sn = unbox(to<subnet>("192.168.0.0/24"));
  auto pred0 = predicate{data_extractor{r, offset{0}}, not_in, data{sn}};
  auto pred1 = predicate{data_extractor{r, offset{1}}, not_in, data{sn}};
  auto normalized = conjunction{pred0, pred1};
  CHECK_EQUAL(resolved, &normalized);
}

TEST(validation - attribute extractor) {
  // The "type" attribute extractor requires a string operand.
  auto expr = to<expression>("#type == \"foo\"");
  REQUIRE(expr);
  CHECK(caf::visit(validator{}, *expr));
  expr = to<expression>("#type == 42");
  REQUIRE(expr);
  CHECK(!caf::visit(validator{}, *expr));
  expr = to<expression>("#type == zeek.conn");
  REQUIRE(expr);
  CHECK(!caf::visit(validator{}, *expr));
  // The "time" attribute extractor requires a timestamp operand.
  expr = to<expression>("#time < now");
  REQUIRE(expr);
  CHECK(caf::visit(validator{}, *expr));
  expr = to<expression>("#time < 2017-06-16");
  REQUIRE(expr);
  CHECK(caf::visit(validator{}, *expr));
  expr = to<expression>("#time > -42");
  REQUIRE(expr);
  CHECK(!caf::visit(validator{}, *expr));
  expr = to<expression>("#time > -42 secs");
  REQUIRE(expr);
  CHECK(!caf::visit(validator{}, *expr));
}

TEST(validation - type extractor) {
  auto expr = to<expression>(":port == 443/tcp");
  REQUIRE(expr);
  CHECK(caf::visit(validator{}, *expr));
  expr = to<expression>(":addr in 10.0.0.0/8");
  REQUIRE(expr);
  CHECK(caf::visit(validator{}, *expr));
  expr = to<expression>(":port > -42");
  REQUIRE(expr);
  CHECK(!caf::visit(validator{}, *expr));
}

TEST(matcher) {
  auto match = [](const std::string& str, auto&& t) {
    auto expr = to<expression>(str);
    REQUIRE(expr);
    auto resolved = caf::visit(type_resolver(t), *expr);
    REQUIRE(resolved);
    return caf::visit(matcher{t}, *resolved);
  };
  MESSAGE("type extractors");
  CHECK(match(":real < 4.2", real_type{}));
  CHECK(!match(":int == -42", real_type{}));
  CHECK(!match(":count == 42 && :real < 4.2", real_type{}));
  CHECK(match(":count == 42 || :real < 4.2", real_type{}));
  auto r = record_type{
    {"x", real_type{}},
    {"y", port_type{}},
    {"z", address_type{}}
  };
  CHECK(match(":count == 42 || :real < 4.2", r));
  CHECK(match(":port == 80/tcp && :real < 4.2", r));
  MESSAGE("key extractors");
  CHECK(match("x < 4.2 || (y == 80/tcp && z in 10.0.0.0/8)", r));
  CHECK(match("x < 4.2 && (y == 80/tcp || :bool == F)", r));
  CHECK(!match("x < 4.2 && a == T", r));
  MESSAGE("attribute extractors");
  CHECK(!match("#type == \"foo\"", r));
  r = r.name("foo");
  CHECK(match("#type == \"foo\"", r));
  CHECK(match("#type != \"bar\"", r));
}

TEST(labeler) {
  auto str
    = "(x == 5 && :bool == T) || (foo ~ /foo/ && !(x == 5 || #type ~ /bar/))"s;
  auto expr = to_expr(str);
  // Create a visitor that records all offsets in order.
  detail::steady_map<expression, offset> offset_map;
  auto visitor = labeler{
    [&](const auto& x, const offset& o) {
      offset_map.emplace(x, o);
    }
  };
  caf::visit(visitor, expr);
  decltype(offset_map) expected_offset_map{
    {to_expr(str), {0}},
    {to_expr("x == 5 && :bool == T"), {0, 0}},
    {to_expr("x == 5"), {0, 0, 0}},
    {to_expr(":bool == T"), {0, 0, 1}},
    {to_expr("foo ~ /foo/ && !(x == 5 || #type ~ /bar/)"), {0, 1}},
    {to_expr("foo ~ /foo/"), {0, 1, 0}},
    {to_expr("!(x == 5 || #type ~ /bar/)"), {0, 1, 1}},
    {to_expr("x == 5 || #type ~ /bar/"), {0, 1, 1, 0}},
    {to_expr("x == 5"), {0, 1, 1, 0, 0}},
    {to_expr("#type ~ /bar/"), {0, 1, 1, 0, 1}},
  };
  CHECK_EQUAL(offset_map, expected_offset_map);
}

TEST(at) {
  auto str
    = "(x == 5 && :bool == T) || (foo ~ /foo/ && !(x == 5 || #type ~ /bar/))"s;
  auto expr = to_expr(str);
  CHECK_EQUAL(at(expr, {}), nullptr); // invalid offset
  CHECK_EQUAL(at(expr, {0}), &expr); // root node
  CHECK_EQUAL(at(expr, {1}), nullptr); // invalid root offset
  CHECK_EQUAL(*at(expr, {0, 0}), to_expr("x == 5 && :bool == T"));
  CHECK_EQUAL(*at(expr, {0, 1, 0}), to_expr("foo ~ /foo/"));
  CHECK_EQUAL(*at(expr, {0, 1, 1, 0, 1}), to_expr("#type ~ /bar/"));
  CHECK_EQUAL(at(expr, {0, 1, 1, 0, 1, 0}), nullptr); // offset too long
}

TEST(resolve) {
  using result_type = std::vector<std::pair<offset, predicate>>;
  auto resolve_pred = [](auto&& x, offset o, type t) -> result_type {
    result_type result;
    auto pred = to<predicate>(x);
    auto resolved = type_resolver{t}(unbox(pred));
    for (auto& pred : caf::visit(predicatizer{}, *resolved))
      result.emplace_back(o, std::move(pred));
    return result;
  };
  auto expr = to_expr("(x == 5 && y == T) || (x == 5 && y == F)"); // tautology
  auto t = record_type{
    {"x", count_type{}},
    {"y", boolean_type{}}
  }.name("foo");
  auto xs = resolve(expr, t);
  decltype(xs) expected;
  auto concat = [](auto&& xs, auto&& ys) {
    auto begin = std::make_move_iterator(ys.begin());
    auto end = std::make_move_iterator(ys.end());
    xs.insert(xs.end(), begin, end);
  };
  // TODO: How should we handle duplicates? Weed them out? --MV
  concat(expected, resolve_pred("x == 5", {0,0,0}, t));
  concat(expected, resolve_pred("y == T", {0,0,1}, t));
  concat(expected, resolve_pred("x == 5", {0,1,0}, t));
  concat(expected, resolve_pred("y == F", {0,1,1}, t));
  CHECK_EQUAL(xs, expected);
}

FIXTURE_SCOPE_END()
<|endoftext|>