<commit_before>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2017 Couchbase, 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 "config.h"
#include "memcached.h"
#include "mcaudit.h"
#include "memcached_audit_events.h"
#include "buckets.h"
#include "debug_helpers.h"
#include "runtime.h"

#include <memcached/audit_interface.h>
#include <cJSON.h>
#include <memcached/isotime.h>

static std::atomic_bool audit_enabled{false};

static const int First = MEMCACHED_AUDIT_OPENED_DCP_CONNECTION;
static const int Last = MEMCACHED_AUDIT_DOCUMENT_DELETE;

static std::array<std::atomic_bool, Last - First + 1> events;

static bool isEnabled(uint32_t id) {
    if (!audit_enabled.load(std::memory_order_consume)) {
        // Global switch is off.. All events are disabled
        return false;
    }

    if (id >= First && id <= Last) {
        // This is one of ours
        return events[id - First].load(std::memory_order_consume);
    }

    // we don't have information about this id... let the underlying event
    // framework deal with it
    return true;
}

void setEnabled(uint32_t id, bool enable) {
    bool expected = !enable;

    if (id == 0) {
        if (audit_enabled.compare_exchange_strong(expected, enable)) {
            LOG_INFO("Audit changed from: {} to: {}",
                     !enable ? "enabled" : "disabled",
                     enable ? "enabled" : "disabled");
        }
    }

    if (id >= First && id <= Last) {
        if (events[id - First].compare_exchange_strong(expected, enable)) {
            LOG_INFO("Audit descriptor {} changed from: {} to: {}",
                     id,
                     expected ? "enabled" : "disabled",
                     enable ? "enabled" : "disabled");
        }
    }
}

/**
 * Create the typical memcached audit object. It constists of a
 * timestamp, the socket endpoints and the creds. Then each audit event
 * may add event-specific content.
 *
 * @param c the connection object
 * @return the cJSON object containing the basic information
 */
static unique_cJSON_ptr create_memcached_audit_object(const Connection* c) {
    cJSON *root = cJSON_CreateObject();

    std::string timestamp = ISOTime::generatetimestamp();
    cJSON_AddStringToObject(root, "timestamp", timestamp.c_str());

    cJSON_AddStringToObject(root, "peername", c->getPeername().c_str());
    cJSON_AddStringToObject(root, "sockname", c->getSockname().c_str());
    cJSON *source = cJSON_CreateObject();
    cJSON_AddStringToObject(source, "source", "memcached");
    cJSON_AddStringToObject(source, "user", c->getUsername());
    cJSON_AddItemToObject(root, "real_userid", source);

    return unique_cJSON_ptr(root);
}

/**
 * Convert the JSON object to text and send it to the audit framework
 *
 * @param c the connection object requesting the call
 * @param id the audit identifier
 * @param event the payload of the audit description
 * @param warn what to log if we're failing to put the audit event
 */
static void do_audit(const Connection* c,
                     uint32_t id,
                     unique_cJSON_ptr& event,
                     const char* warn) {
    auto text = to_string(event, false);
    auto status = put_audit_event(get_audit_handle(), id, text.data(),
                                  text.length());

    if (status != AUDIT_SUCCESS) {
        LOG_WARNING("{}: {}", warn, text);
    }
}

void audit_auth_failure(const Connection* c, const char* reason) {
    if (!isEnabled(MEMCACHED_AUDIT_AUTHENTICATION_FAILED)) {
        return;
    }
    auto root = create_memcached_audit_object(c);
    cJSON_AddStringToObject(root.get(), "reason", reason);

    do_audit(c, MEMCACHED_AUDIT_AUTHENTICATION_FAILED, root,
             "Failed to send AUTH FAILED audit event");
}

void audit_auth_success(const Connection* c) {
    if (!isEnabled(MEMCACHED_AUDIT_AUTHENTICATION_SUCCEEDED)) {
        return;
    }
    auto root = create_memcached_audit_object(c);
    do_audit(c, MEMCACHED_AUDIT_AUTHENTICATION_SUCCEEDED, root,
             "Failed to send AUTH SUCCESS audit event");
}

void audit_bucket_flush(const Connection* c, const char* bucket) {
    if (!isEnabled(MEMCACHED_AUDIT_BUCKET_FLUSH)) {
        return;
    }
    auto root = create_memcached_audit_object(c);
    cJSON_AddStringToObject(root.get(), "bucket", bucket);

    do_audit(c, MEMCACHED_AUDIT_BUCKET_FLUSH, root,
             "Failed to send BUCKET_FLUSH audit event");
}

void audit_dcp_open(const Connection* c) {
    if (!isEnabled(MEMCACHED_AUDIT_OPENED_DCP_CONNECTION)) {
        return;
    }
    if (c->isInternal()) {
        LOG_INFO("Open DCP stream with admin credentials");
    } else {
        auto root = create_memcached_audit_object(c);
        cJSON_AddStringToObject(root.get(), "bucket", getBucketName(c));

        do_audit(c, MEMCACHED_AUDIT_OPENED_DCP_CONNECTION, root,
                 "Failed to send DCP open connection "
                 "audit event to audit daemon");
    }
}

void audit_set_privilege_debug_mode(const Connection* c, bool enable) {
    if (!isEnabled(MEMCACHED_AUDIT_PRIVILEGE_DEBUG_CONFIGURED)) {
        return;
    }
    auto root = create_memcached_audit_object(c);
    cJSON_AddBoolToObject(root.get(), "enable", enable);
    do_audit(c, MEMCACHED_AUDIT_PRIVILEGE_DEBUG_CONFIGURED, root,
             "Failed to send modifications in privilege debug state "
             "audit event to audit daemon");
}

void audit_privilege_debug(const Connection* c,
                           const std::string& command,
                           const std::string& bucket,
                           const std::string& privilege,
                           const std::string& context) {
    if (!isEnabled(MEMCACHED_AUDIT_PRIVILEGE_DEBUG)) {
        return;
    }
    auto root = create_memcached_audit_object(c);
    cJSON_AddStringToObject(root.get(), "command", command.c_str());
    cJSON_AddStringToObject(root.get(), "bucket", bucket.c_str());
    cJSON_AddStringToObject(root.get(), "privilege", privilege.c_str());
    cJSON_AddStringToObject(root.get(), "context", context.c_str());

    do_audit(c, MEMCACHED_AUDIT_PRIVILEGE_DEBUG, root,
             "Failed to send privilege debug audit event to audit daemon");
}

void audit_command_access_failed(const Cookie& cookie) {
    if (!isEnabled(MEMCACHED_AUDIT_COMMAND_ACCESS_FAILURE)) {
        return;
    }
    const auto& connection = cookie.getConnection();
    auto root = create_memcached_audit_object(&connection);
    char buffer[256];
    memset(buffer, 0, sizeof(buffer));
    const auto packet = cookie.getPacket();
    // Deliberately ignore failure of bytes_to_output_string
    // We'll either have a partial string or no string.
    bytes_to_output_string(buffer,
                           sizeof(buffer),
                           connection.getId(),
                           true,
                           "Access to command is not allowed:",
                           reinterpret_cast<const char*>(packet.data()),
                           packet.size());
    cJSON_AddStringToObject(root.get(), "packet", buffer);
    do_audit(&connection, MEMCACHED_AUDIT_COMMAND_ACCESS_FAILURE, root, buffer);
}

void audit_invalid_packet(const Cookie& cookie) {
    if (!isEnabled(MEMCACHED_AUDIT_INVALID_PACKET)) {
        return;
    }
    const auto& connection = cookie.getConnection();
    auto root = create_memcached_audit_object(&connection);
    char buffer[256];
    memset(buffer, 0, sizeof(buffer));
    const auto packet = cookie.getPacket();
    // Deliberately ignore failure of bytes_to_output_string
    // We'll either have a partial string or no string.
    bytes_to_output_string(buffer,
                           sizeof(buffer),
                           connection.getId(),
                           true,
                           "Invalid Packet:",
                           reinterpret_cast<const char*>(packet.data()),
                           packet.size());
    cJSON_AddStringToObject(root.get(), "packet", buffer);
    do_audit(&connection, MEMCACHED_AUDIT_INVALID_PACKET, root, buffer);
}

bool mc_audit_event(uint32_t audit_eventid, cb::const_byte_buffer payload) {
    if (!audit_enabled) {
        return true;
    }

    return put_audit_event(get_audit_handle(),
                           audit_eventid,
                           payload.data(),
                           payload.size()) == AUDIT_SUCCESS;
}

namespace cb {
namespace audit {
namespace document {

void add(const Cookie& cookie, Operation operation) {
    uint32_t id = 0;
    switch (operation) {
    case Operation::Read:
        id = MEMCACHED_AUDIT_DOCUMENT_READ;
        break;
    case Operation::Lock:
        id = MEMCACHED_AUDIT_DOCUMENT_LOCKED;
        break;
    case Operation::Modify:
        id = MEMCACHED_AUDIT_DOCUMENT_MODIFY;
        break;
    case Operation::Delete:
        id = MEMCACHED_AUDIT_DOCUMENT_DELETE;
        break;
    }

    if (id == 0) {
        throw std::invalid_argument(
            "cb::audit::document::add: Invalid operation");
    }

    if (!isEnabled(id)) {
        return;
    }

    const auto& connection = cookie.getConnection();
    auto root = create_memcached_audit_object(&connection);
    cJSON_AddStringToObject(root.get(), "bucket", connection.getBucket().name);
    cJSON_AddStringToObject(
            root.get(), "key", cookie.getPrintableRequestKey().c_str());

    switch (operation) {
    case Operation::Read:
        do_audit(&connection,
                 MEMCACHED_AUDIT_DOCUMENT_READ,
                 root,
                 "Failed to send document read audit event to audit daemon");
        break;
    case Operation::Lock:
        do_audit(&connection,
                 MEMCACHED_AUDIT_DOCUMENT_LOCKED,
                 root,
                 "Failed to send document locked audit event to audit daemon");
        break;
    case Operation::Modify:
        do_audit(&connection,
                 MEMCACHED_AUDIT_DOCUMENT_MODIFY,
                 root,
                 "Failed to send document modify audit event to audit daemon");
        break;
    case Operation::Delete:
        do_audit(&connection,
                 MEMCACHED_AUDIT_DOCUMENT_DELETE,
                 root,
                 "Failed to send document delete audit event to audit daemon");
        break;
    }
}

} // namespace documnent
} // namespace audit
} // namespace cb

static void event_state_listener(uint32_t id, bool enabled) {
    setEnabled(id, enabled);
}

void initialize_audit() {
/* Start the audit daemon */
    AUDIT_EXTENSION_DATA audit_extension_data;
    memset(&audit_extension_data, 0, sizeof(audit_extension_data));
    audit_extension_data.notify_io_complete = notify_io_complete;
    audit_extension_data.configfile = settings.getAuditFile().c_str();
    Audit* handle = nullptr;
    if (start_auditdaemon(&audit_extension_data, &handle) != AUDIT_SUCCESS) {
        FATAL_ERROR(EXIT_FAILURE, "FATAL: Failed to start audit daemon");
    }
    set_audit_handle(handle);
    cb::audit::add_event_state_listener(handle, event_state_listener);
    cb::audit::notify_all_event_states(handle);
}
<commit_msg>MB-29238: Use domain instead of source for memcached audit events<commit_after>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2017 Couchbase, 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 "config.h"
#include "memcached.h"
#include "mcaudit.h"
#include "memcached_audit_events.h"
#include "buckets.h"
#include "debug_helpers.h"
#include "runtime.h"

#include <memcached/audit_interface.h>
#include <cJSON.h>
#include <memcached/isotime.h>

static std::atomic_bool audit_enabled{false};

static const int First = MEMCACHED_AUDIT_OPENED_DCP_CONNECTION;
static const int Last = MEMCACHED_AUDIT_DOCUMENT_DELETE;

static std::array<std::atomic_bool, Last - First + 1> events;

static bool isEnabled(uint32_t id) {
    if (!audit_enabled.load(std::memory_order_consume)) {
        // Global switch is off.. All events are disabled
        return false;
    }

    if (id >= First && id <= Last) {
        // This is one of ours
        return events[id - First].load(std::memory_order_consume);
    }

    // we don't have information about this id... let the underlying event
    // framework deal with it
    return true;
}

void setEnabled(uint32_t id, bool enable) {
    bool expected = !enable;

    if (id == 0) {
        if (audit_enabled.compare_exchange_strong(expected, enable)) {
            LOG_INFO("Audit changed from: {} to: {}",
                     !enable ? "enabled" : "disabled",
                     enable ? "enabled" : "disabled");
        }
    }

    if (id >= First && id <= Last) {
        if (events[id - First].compare_exchange_strong(expected, enable)) {
            LOG_INFO("Audit descriptor {} changed from: {} to: {}",
                     id,
                     expected ? "enabled" : "disabled",
                     enable ? "enabled" : "disabled");
        }
    }
}

/**
 * Create the typical memcached audit object. It constists of a
 * timestamp, the socket endpoints and the creds. Then each audit event
 * may add event-specific content.
 *
 * @param c the connection object
 * @return the cJSON object containing the basic information
 */
static unique_cJSON_ptr create_memcached_audit_object(const Connection* c) {
    cJSON *root = cJSON_CreateObject();

    std::string timestamp = ISOTime::generatetimestamp();
    cJSON_AddStringToObject(root, "timestamp", timestamp.c_str());

    cJSON_AddStringToObject(root, "peername", c->getPeername().c_str());
    cJSON_AddStringToObject(root, "sockname", c->getSockname().c_str());
    cJSON *domain = cJSON_CreateObject();
    cJSON_AddStringToObject(domain, "domain", "memcached");
    cJSON_AddStringToObject(domain, "user", c->getUsername());
    cJSON_AddItemToObject(root, "real_userid", domain);

    return unique_cJSON_ptr(root);
}

/**
 * Convert the JSON object to text and send it to the audit framework
 *
 * @param c the connection object requesting the call
 * @param id the audit identifier
 * @param event the payload of the audit description
 * @param warn what to log if we're failing to put the audit event
 */
static void do_audit(const Connection* c,
                     uint32_t id,
                     unique_cJSON_ptr& event,
                     const char* warn) {
    auto text = to_string(event, false);
    auto status = put_audit_event(get_audit_handle(), id, text.data(),
                                  text.length());

    if (status != AUDIT_SUCCESS) {
        LOG_WARNING("{}: {}", warn, text);
    }
}

void audit_auth_failure(const Connection* c, const char* reason) {
    if (!isEnabled(MEMCACHED_AUDIT_AUTHENTICATION_FAILED)) {
        return;
    }
    auto root = create_memcached_audit_object(c);
    cJSON_AddStringToObject(root.get(), "reason", reason);

    do_audit(c, MEMCACHED_AUDIT_AUTHENTICATION_FAILED, root,
             "Failed to send AUTH FAILED audit event");
}

void audit_auth_success(const Connection* c) {
    if (!isEnabled(MEMCACHED_AUDIT_AUTHENTICATION_SUCCEEDED)) {
        return;
    }
    auto root = create_memcached_audit_object(c);
    do_audit(c, MEMCACHED_AUDIT_AUTHENTICATION_SUCCEEDED, root,
             "Failed to send AUTH SUCCESS audit event");
}

void audit_bucket_flush(const Connection* c, const char* bucket) {
    if (!isEnabled(MEMCACHED_AUDIT_BUCKET_FLUSH)) {
        return;
    }
    auto root = create_memcached_audit_object(c);
    cJSON_AddStringToObject(root.get(), "bucket", bucket);

    do_audit(c, MEMCACHED_AUDIT_BUCKET_FLUSH, root,
             "Failed to send BUCKET_FLUSH audit event");
}

void audit_dcp_open(const Connection* c) {
    if (!isEnabled(MEMCACHED_AUDIT_OPENED_DCP_CONNECTION)) {
        return;
    }
    if (c->isInternal()) {
        LOG_INFO("Open DCP stream with admin credentials");
    } else {
        auto root = create_memcached_audit_object(c);
        cJSON_AddStringToObject(root.get(), "bucket", getBucketName(c));

        do_audit(c, MEMCACHED_AUDIT_OPENED_DCP_CONNECTION, root,
                 "Failed to send DCP open connection "
                 "audit event to audit daemon");
    }
}

void audit_set_privilege_debug_mode(const Connection* c, bool enable) {
    if (!isEnabled(MEMCACHED_AUDIT_PRIVILEGE_DEBUG_CONFIGURED)) {
        return;
    }
    auto root = create_memcached_audit_object(c);
    cJSON_AddBoolToObject(root.get(), "enable", enable);
    do_audit(c, MEMCACHED_AUDIT_PRIVILEGE_DEBUG_CONFIGURED, root,
             "Failed to send modifications in privilege debug state "
             "audit event to audit daemon");
}

void audit_privilege_debug(const Connection* c,
                           const std::string& command,
                           const std::string& bucket,
                           const std::string& privilege,
                           const std::string& context) {
    if (!isEnabled(MEMCACHED_AUDIT_PRIVILEGE_DEBUG)) {
        return;
    }
    auto root = create_memcached_audit_object(c);
    cJSON_AddStringToObject(root.get(), "command", command.c_str());
    cJSON_AddStringToObject(root.get(), "bucket", bucket.c_str());
    cJSON_AddStringToObject(root.get(), "privilege", privilege.c_str());
    cJSON_AddStringToObject(root.get(), "context", context.c_str());

    do_audit(c, MEMCACHED_AUDIT_PRIVILEGE_DEBUG, root,
             "Failed to send privilege debug audit event to audit daemon");
}

void audit_command_access_failed(const Cookie& cookie) {
    if (!isEnabled(MEMCACHED_AUDIT_COMMAND_ACCESS_FAILURE)) {
        return;
    }
    const auto& connection = cookie.getConnection();
    auto root = create_memcached_audit_object(&connection);
    char buffer[256];
    memset(buffer, 0, sizeof(buffer));
    const auto packet = cookie.getPacket();
    // Deliberately ignore failure of bytes_to_output_string
    // We'll either have a partial string or no string.
    bytes_to_output_string(buffer,
                           sizeof(buffer),
                           connection.getId(),
                           true,
                           "Access to command is not allowed:",
                           reinterpret_cast<const char*>(packet.data()),
                           packet.size());
    cJSON_AddStringToObject(root.get(), "packet", buffer);
    do_audit(&connection, MEMCACHED_AUDIT_COMMAND_ACCESS_FAILURE, root, buffer);
}

void audit_invalid_packet(const Cookie& cookie) {
    if (!isEnabled(MEMCACHED_AUDIT_INVALID_PACKET)) {
        return;
    }
    const auto& connection = cookie.getConnection();
    auto root = create_memcached_audit_object(&connection);
    char buffer[256];
    memset(buffer, 0, sizeof(buffer));
    const auto packet = cookie.getPacket();
    // Deliberately ignore failure of bytes_to_output_string
    // We'll either have a partial string or no string.
    bytes_to_output_string(buffer,
                           sizeof(buffer),
                           connection.getId(),
                           true,
                           "Invalid Packet:",
                           reinterpret_cast<const char*>(packet.data()),
                           packet.size());
    cJSON_AddStringToObject(root.get(), "packet", buffer);
    do_audit(&connection, MEMCACHED_AUDIT_INVALID_PACKET, root, buffer);
}

bool mc_audit_event(uint32_t audit_eventid, cb::const_byte_buffer payload) {
    if (!audit_enabled) {
        return true;
    }

    return put_audit_event(get_audit_handle(),
                           audit_eventid,
                           payload.data(),
                           payload.size()) == AUDIT_SUCCESS;
}

namespace cb {
namespace audit {
namespace document {

void add(const Cookie& cookie, Operation operation) {
    uint32_t id = 0;
    switch (operation) {
    case Operation::Read:
        id = MEMCACHED_AUDIT_DOCUMENT_READ;
        break;
    case Operation::Lock:
        id = MEMCACHED_AUDIT_DOCUMENT_LOCKED;
        break;
    case Operation::Modify:
        id = MEMCACHED_AUDIT_DOCUMENT_MODIFY;
        break;
    case Operation::Delete:
        id = MEMCACHED_AUDIT_DOCUMENT_DELETE;
        break;
    }

    if (id == 0) {
        throw std::invalid_argument(
            "cb::audit::document::add: Invalid operation");
    }

    if (!isEnabled(id)) {
        return;
    }

    const auto& connection = cookie.getConnection();
    auto root = create_memcached_audit_object(&connection);
    cJSON_AddStringToObject(root.get(), "bucket", connection.getBucket().name);
    cJSON_AddStringToObject(
            root.get(), "key", cookie.getPrintableRequestKey().c_str());

    switch (operation) {
    case Operation::Read:
        do_audit(&connection,
                 MEMCACHED_AUDIT_DOCUMENT_READ,
                 root,
                 "Failed to send document read audit event to audit daemon");
        break;
    case Operation::Lock:
        do_audit(&connection,
                 MEMCACHED_AUDIT_DOCUMENT_LOCKED,
                 root,
                 "Failed to send document locked audit event to audit daemon");
        break;
    case Operation::Modify:
        do_audit(&connection,
                 MEMCACHED_AUDIT_DOCUMENT_MODIFY,
                 root,
                 "Failed to send document modify audit event to audit daemon");
        break;
    case Operation::Delete:
        do_audit(&connection,
                 MEMCACHED_AUDIT_DOCUMENT_DELETE,
                 root,
                 "Failed to send document delete audit event to audit daemon");
        break;
    }
}

} // namespace documnent
} // namespace audit
} // namespace cb

static void event_state_listener(uint32_t id, bool enabled) {
    setEnabled(id, enabled);
}

void initialize_audit() {
/* Start the audit daemon */
    AUDIT_EXTENSION_DATA audit_extension_data;
    memset(&audit_extension_data, 0, sizeof(audit_extension_data));
    audit_extension_data.notify_io_complete = notify_io_complete;
    audit_extension_data.configfile = settings.getAuditFile().c_str();
    Audit* handle = nullptr;
    if (start_auditdaemon(&audit_extension_data, &handle) != AUDIT_SUCCESS) {
        FATAL_ERROR(EXIT_FAILURE, "FATAL: Failed to start audit daemon");
    }
    set_audit_handle(handle);
    cb::audit::add_event_state_listener(handle, event_state_listener);
    cb::audit::notify_all_event_states(handle);
}
<|endoftext|>


<commit_before>/*
    incomingtransfer.cpp - msn p2p protocol

    Copyright (c) 2003-2005 by Olivier Goffart        <ogoffart@ kde.org>
    Copyright (c) 2005      by Gregg Edghill          <gregg.edghill@gmail.com>

    *************************************************************************
    *                                                                       *
    * 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 "incomingtransfer.h"
using P2P::TransferContext;
using P2P::IncomingTransfer;
using P2P::Message;

// Kde includes
#include <kbufferedsocket.h>
#include <kdebug.h>
#include <klocale.h>
#include <kserversocket.h>
#include <kstandarddirs.h>
#include <ktempfile.h>
using namespace KNetwork;

// Qt includes
#include <qfile.h>
#include <qregexp.h>

// Kopete includes
#include <kopetetransfermanager.h>

IncomingTransfer::IncomingTransfer(const QString& from, P2P::Dispatcher *dispatcher, Q_UINT32 sessionId)
: TransferContext(from,dispatcher,sessionId)
{
	m_direction = P2P::Incoming;
	m_listener  = 0l;
}

IncomingTransfer::~IncomingTransfer()
{
	kdDebug(14140) << k_funcinfo << endl;
	if(m_listener)
	{
		delete m_listener;
		m_listener = 0l;
	}

	if(m_socket)
	{
		delete m_socket;
		m_socket = 0l;
	}
}

void IncomingTransfer::slotTransferAccepted(Kopete::Transfer* transfer, const QString& /*fileName*/)
{
	Q_UINT32 sessionId = transfer->info().internalId().toUInt();
	if(sessionId!=m_sessionId)
		return;

	QObject::connect(transfer , SIGNAL(transferCanceled()), this, SLOT(abort()));
	m_transfer = transfer;

	QString content = QString("SessionID: %1\r\n\r\n").arg(sessionId);
	sendMessage(OK, content);

	QObject::disconnect(Kopete::TransferManager::transferManager(), 0l, this, 0l);
}

void IncomingTransfer::slotTransferRefused(const Kopete::FileTransferInfo& info)
{
	Q_UINT32 sessionId = info.internalId().toUInt();
	if(sessionId!=m_sessionId)
		return;

	QString content = QString("SessionID: %1\r\n\r\n").arg(sessionId);
	// Send the sending client a cancelation message.
	sendMessage(DECLINE, content);
	m_state=Finished;

	QObject::disconnect(Kopete::TransferManager::transferManager(), 0l, this, 0l);
}

void IncomingTransfer::acknowledged()
{
	kdDebug(14140) << k_funcinfo << endl;

	switch(m_state)
	{
		case Invitation:
				// NOTE UDI: base identifier acknowledge message, ignore.
				//      UDI: 200 OK message should follow.
				if(m_type == File){
					// FT: 200 OK acknowledged message.
					// Direct connection invitation should follow.
					m_state = Negotiation;
				}
			break;

		case Negotiation:
				// 200 OK acknowledge message.
			break;

		case DataTransfer:
			break;

		case Finished:
			// UDI: Bye acknowledge message.
			m_dispatcher->detach(this);
			break;
	}
}

void IncomingTransfer::processMessage(const Message& message)
{
	if(m_file && (message.header.flag == 0x20 || message.header.flag == 0x01000030))
	{
		// UserDisplayIcon data or File data is in this message.
		// Write the recieved data to the file.
		kdDebug(14140) << k_funcinfo << QString("Received, %1 bytes").arg(message.header.dataSize) << endl;

		m_file->writeBlock(message.body.data(), message.header.dataSize);
		if(m_transfer){
			m_transfer->slotProcessed(message.header.dataOffset + message.header.dataSize);
		}

		if((message.header.dataOffset + message.header.dataSize) == message.header.totalDataSize)
		{
			// Transfer is complete.
			if(m_type == UserDisplayIcon){
				m_tempFile->close();
				m_dispatcher->displayIconReceived(m_tempFile, m_object);
				m_tempFile = 0l;
				m_file = 0l;
			}
			else
			{
				m_file->close();
			}

			m_isComplete = true;
			// Send data acknowledge message.
			acknowledge(message);

			if(m_type == UserDisplayIcon)
			{
				m_state = Finished;
				// Send BYE message.
				sendMessage(BYE, "\r\n");
			}
		}
	}
	else if(message.header.dataSize == 4 && message.applicationIdentifier == 1)
	{
		// Data preparation message.
		m_tempFile = new KTempFile(locateLocal("tmp", "msnpicture--"), ".png");
		m_tempFile->setAutoDelete(true);
		m_file = m_tempFile->file();
		m_state = DataTransfer;
		// Send data preparation acknowledge message.
		acknowledge(message);
	}
	else
	{
		QString body =
			QCString(message.body.data(), message.header.dataSize);
//		kdDebug(14140) << k_funcinfo << "received, " << body << endl;

		if(body.startsWith("INVITE"))
		{
			// Retrieve some MSNSLP headers used when
			// replying to this INVITE message.
			QRegExp regex(";branch=\\{([0-9A-F\\-]*)\\}\r\n");
			regex.search(body);
			m_branch = regex.cap(1);
			// NOTE Call-ID never changes.
			regex = QRegExp("Call-ID: \\{([0-9A-F\\-]*)\\}\r\n");
			regex.search(body);
			m_callId = regex.cap(1);
			regex = QRegExp("Bridges: ([^\r\n]*)\r\n");
			regex.search(body);
			QString bridges = regex.cap(1);
			// The NetID field is 0 if the Conn-Type is
			// Direct-Connect or Firewall, otherwise, it is
			// a randomly generated number.
			regex = QRegExp("NetID: (\\-?\\d+)\r\n");
			regex.search(body);
			QString netId = regex.cap(1);
			kdDebug(14140) << "net id, " << netId << endl;
			// Connection Types
			// - Direct-Connect
			// - Port-Restrict-NAT
			// - IP-Restrict-NAT
			// - Symmetric-NAT
			// - Firewall
			regex = QRegExp("Conn-Type: ([^\r\n]+)\r\n");
			regex.search(body);
			QString connType = regex.cap(1);

			bool wouldListen = false;
			if(netId.toUInt() == 0 && connType == "Direct-Connect"){
				wouldListen = true;

			}
			else if(connType == "IP-Restrict-NAT"){
				wouldListen = true;
			}
#if 1
			wouldListen = false; // TODO Direct connection support
#endif
			QString content;

			if(wouldListen)
			{
				// Create a listening socket for direct file transfer.
				m_listener = new KServerSocket("", "");
				m_listener->setResolutionEnabled(true);
				// Create the callback that will try to accept incoming connections.
				QObject::connect(m_listener, SIGNAL(readyAccept()), SLOT(slotAccept()));
				QObject::connect(m_listener, SIGNAL(gotError(int)), this, SLOT(slotListenError(int)));
				// Listen for incoming connections.
				bool isListening = m_listener->listen(1);
				kdDebug(14140) << k_funcinfo << (isListening ? "listening" : "not listening") << endl;
				kdDebug(14140) << k_funcinfo
					<< "local endpoint, " << m_listener->localAddress().nodeName()
					<< endl;

				content = "Bridge: TCPv1\r\n"
					"Listening: true\r\n" +
					QString("Hashed-Nonce: {%1}\r\n").arg(P2P::Uid::createUid()) +
					QString("IPv4Internal-Addrs: %1\r\n").arg(m_listener->localAddress().nodeName())   +
					QString("IPv4Internal-Port: %1\r\n").arg(m_listener->localAddress().serviceName()) +
					"\r\n";
			}
			else
			{
				content =
					"Bridge: TCPv1\r\n"
					"Listening: false\r\n"
					"Hashed-Nonce: {00000000-0000-0000-0000-000000000000}\r\n"
					"\r\n";
			}

			acknowledge(message);

			if(m_transfer)
			{
				// NOTE The sending client can ask for a direct connections
				// if one was established before.
				QFile *destionation = new QFile(m_transfer->destinationURL().path());
				if(!destionation->open(IO_WriteOnly))
				{
					if(m_transfer){
						m_transfer->slotError(KIO::ERR_CANNOT_OPEN_FOR_WRITING, i18n("Cannot open file for writing"));
						m_transfer = 0l;
					}

					error();
					return;
				}

				m_file = destionation;
			}

			m_state = DataTransfer;
			// Send 200 OK message to the sending client.
			sendMessage(OK, content);
		}
		else if(body.startsWith("BYE"))
		{
			m_state = Finished;
			// Send the sending client an acknowledge message.
			acknowledge(message);

			if(m_file && m_transfer)
			{
				if(m_isComplete){
					// The transfer is complete.
					m_transfer->slotComplete();
				}
				else
				{
					// The transfer has been canceled remotely.
					if(m_transfer){
						// Inform the user of the file transfer cancelation.
						m_transfer->slotError(KIO::ERR_ABORTED, i18n("File transfer canceled."));
					}
					// Remove the partially received file.
					m_file->remove();
				}
			}

			// Dispose of this transfer context.
			m_dispatcher->detach(this);
		}
		else if(body.startsWith("MSNSLP/1.0 200 OK"))
		{
			if(m_type == UserDisplayIcon){
				m_state = Negotiation;
				// Acknowledge the 200 OK message.
				acknowledge(message);
			}
		}
	}
}

void IncomingTransfer::slotListenError(int /*errorCode*/)
{
	kdDebug(14140) << k_funcinfo << m_listener->errorString() << endl;
}

void IncomingTransfer::slotAccept()
{
	// Try to accept an incoming connection from the sending client.
	m_socket = static_cast<KBufferedSocket*>(m_listener->accept());
	if(!m_socket)
	{
		// NOTE If direct connection fails, the sending
		// client wil transfer the file data through the
		// existing session.
		kdDebug(14140) << k_funcinfo << "Direct connection failed." << endl;
		// Close the listening endpoint.
		m_listener->close();
		return;
	}

	kdDebug(14140) << k_funcinfo << "Direct connection established." << endl;

	// Set the socket to non blocking,
	// enable the ready read signal and disable
	// ready write signal.
	// NOTE readyWrite consumes too much cpu usage.
	m_socket->setBlocking(false);
	m_socket->enableRead(true);
	m_socket->enableWrite(false);

	// Create the callback that will try to read bytes from the accepted socket.
	QObject::connect(m_socket, SIGNAL(readyRead()),   this, SLOT(slotSocketRead()));
	// Create the callback that will try to handle the socket close event.
	QObject::connect(m_socket, SIGNAL(closed()),      this, SLOT(slotSocketClosed()));
	// Create the callback that will try to handle the socket error event.
	QObject::connect(m_socket, SIGNAL(gotError(int)), this, SLOT(slotSocketError(int)));
}

void IncomingTransfer::slotSocketRead()
{
	int available = m_socket->bytesAvailable();
	kdDebug(14140) << k_funcinfo << available << ", bytes available." << endl;
	if(available > 0)
	{
		QByteArray buffer(available);
		m_socket->readBlock(buffer.data(), buffer.size());

		if(QString(buffer) == "foo"){
			kdDebug(14140) << "Connection Check." << endl;
		}
	}
}

void IncomingTransfer::slotSocketClosed()
{
	kdDebug(14140) << k_funcinfo << endl;
}

void IncomingTransfer::slotSocketError(int errorCode)
{
	kdDebug(14140) << k_funcinfo << errorCode << endl;
}

#include "incomingtransfer.moc"
<commit_msg>BUG: 113525<commit_after>/*
    incomingtransfer.cpp - msn p2p protocol

    Copyright (c) 2003-2005 by Olivier Goffart        <ogoffart@ kde.org>
    Copyright (c) 2005      by Gregg Edghill          <gregg.edghill@gmail.com>

    *************************************************************************
    *                                                                       *
    * 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 "incomingtransfer.h"
using P2P::TransferContext;
using P2P::IncomingTransfer;
using P2P::Message;

// Kde includes
#include <kbufferedsocket.h>
#include <kdebug.h>
#include <klocale.h>
#include <kserversocket.h>
#include <kstandarddirs.h>
#include <ktempfile.h>
using namespace KNetwork;

// Qt includes
#include <qfile.h>
#include <qregexp.h>

// Kopete includes
#include <kopetetransfermanager.h>

IncomingTransfer::IncomingTransfer(const QString& from, P2P::Dispatcher *dispatcher, Q_UINT32 sessionId)
: TransferContext(from,dispatcher,sessionId)
{
	m_direction = P2P::Incoming;
	m_listener  = 0l;
}

IncomingTransfer::~IncomingTransfer()
{
	kdDebug(14140) << k_funcinfo << endl;
	if(m_listener)
	{
		delete m_listener;
		m_listener = 0l;
	}

	if(m_socket)
	{
		delete m_socket;
		m_socket = 0l;
	}
}

void IncomingTransfer::slotTransferAccepted(Kopete::Transfer* transfer, const QString& /*fileName*/)
{
	Q_UINT32 sessionId = transfer->info().internalId().toUInt();
	if(sessionId!=m_sessionId)
		return;

	QObject::connect(transfer , SIGNAL(transferCanceled()), this, SLOT(abort()));
	m_transfer = transfer;

	QString content = QString("SessionID: %1\r\n\r\n").arg(sessionId);
	sendMessage(OK, content);

	QObject::disconnect(Kopete::TransferManager::transferManager(), 0l, this, 0l);
}

void IncomingTransfer::slotTransferRefused(const Kopete::FileTransferInfo& info)
{
	Q_UINT32 sessionId = info.internalId().toUInt();
	if(sessionId!=m_sessionId)
		return;

	QString content = QString("SessionID: %1\r\n\r\n").arg(sessionId);
	// Send the sending client a cancelation message.
	sendMessage(DECLINE, content);
	m_state=Finished;

	QObject::disconnect(Kopete::TransferManager::transferManager(), 0l, this, 0l);
}

void IncomingTransfer::acknowledged()
{
	kdDebug(14140) << k_funcinfo << endl;

	switch(m_state)
	{
		case Invitation:
				// NOTE UDI: base identifier acknowledge message, ignore.
				//      UDI: 200 OK message should follow.
				if(m_type == File){
					// FT: 200 OK acknowledged message.
					// Direct connection invitation should follow.
					m_state = Negotiation;
				}
			break;

		case Negotiation:
				// 200 OK acknowledge message.
			break;

		case DataTransfer:
			break;

		case Finished:
			// UDI: Bye acknowledge message.
			m_dispatcher->detach(this);
			break;
	}
}

void IncomingTransfer::processMessage(const Message& message)
{
	if(m_file && (message.header.flag == 0x20 || message.header.flag == 0x01000030))
	{
		// UserDisplayIcon data or File data is in this message.
		// Write the recieved data to the file.
		kdDebug(14140) << k_funcinfo << QString("Received, %1 bytes").arg(message.header.dataSize) << endl;

		m_file->writeBlock(message.body.data(), message.header.dataSize);
		if(m_transfer){
			m_transfer->slotProcessed(message.header.dataOffset + message.header.dataSize);
		}

		if((message.header.dataOffset + message.header.dataSize) == message.header.totalDataSize)
		{
			// Transfer is complete.
			if(m_type == UserDisplayIcon){
				m_tempFile->close();
				m_dispatcher->displayIconReceived(m_tempFile, m_object);
				m_tempFile = 0l;
				m_file = 0l;
			}
			else
			{
				m_file->close();
			}

			m_isComplete = true;
			// Send data acknowledge message.
			acknowledge(message);

			if(m_type == UserDisplayIcon)
			{
				m_state = Finished;
				// Send BYE message.
				sendMessage(BYE, "\r\n");
			}
		}
	}
	else if(message.header.dataSize == 4 && message.applicationIdentifier == 1)
	{
		// Data preparation message.
		m_tempFile = new KTempFile(locateLocal("tmp", "msnpicture--"), ".png");
		m_tempFile->setAutoDelete(true);
		m_file = m_tempFile->file();
		m_state = DataTransfer;
		// Send data preparation acknowledge message.
		acknowledge(message);
	}
	else
	{
		QString body =
			QCString(message.body.data(), message.header.dataSize);
//		kdDebug(14140) << k_funcinfo << "received, " << body << endl;

		if(body.startsWith("INVITE"))
		{
			// Retrieve some MSNSLP headers used when
			// replying to this INVITE message.
			QRegExp regex(";branch=\\{([0-9A-F\\-]*)\\}\r\n");
			regex.search(body);
			m_branch = regex.cap(1);
			// NOTE Call-ID never changes.
			regex = QRegExp("Call-ID: \\{([0-9A-F\\-]*)\\}\r\n");
			regex.search(body);
			m_callId = regex.cap(1);
			regex = QRegExp("Bridges: ([^\r\n]*)\r\n");
			regex.search(body);
			QString bridges = regex.cap(1);
			// The NetID field is 0 if the Conn-Type is
			// Direct-Connect or Firewall, otherwise, it is
			// a randomly generated number.
			regex = QRegExp("NetID: (\\-?\\d+)\r\n");
			regex.search(body);
			QString netId = regex.cap(1);
			kdDebug(14140) << "net id, " << netId << endl;
			// Connection Types
			// - Direct-Connect
			// - Port-Restrict-NAT
			// - IP-Restrict-NAT
			// - Symmetric-NAT
			// - Firewall
			regex = QRegExp("Conn-Type: ([^\r\n]+)\r\n");
			regex.search(body);
			QString connType = regex.cap(1);

			bool wouldListen = false;
			if(netId.toUInt() == 0 && connType == "Direct-Connect"){
				wouldListen = true;

			}
			else if(connType == "IP-Restrict-NAT"){
				wouldListen = true;
			}
#if 1
			wouldListen = false; // TODO Direct connection support
#endif
			QString content;

			if(wouldListen)
			{
				// Create a listening socket for direct file transfer.
				m_listener = new KServerSocket("", "");
				m_listener->setResolutionEnabled(true);
				// Create the callback that will try to accept incoming connections.
				QObject::connect(m_listener, SIGNAL(readyAccept()), SLOT(slotAccept()));
				QObject::connect(m_listener, SIGNAL(gotError(int)), this, SLOT(slotListenError(int)));
				// Listen for incoming connections.
				bool isListening = m_listener->listen(1);
				kdDebug(14140) << k_funcinfo << (isListening ? "listening" : "not listening") << endl;
				kdDebug(14140) << k_funcinfo
					<< "local endpoint, " << m_listener->localAddress().nodeName()
					<< endl;

				content = "Bridge: TCPv1\r\n"
					"Listening: true\r\n" +
					QString("Hashed-Nonce: {%1}\r\n").arg(P2P::Uid::createUid()) +
					QString("IPv4Internal-Addrs: %1\r\n").arg(m_listener->localAddress().nodeName())   +
					QString("IPv4Internal-Port: %1\r\n").arg(m_listener->localAddress().serviceName()) +
					"\r\n";
			}
			else
			{
				content =
					"Bridge: TCPv1\r\n"
					"Listening: false\r\n"
					"Hashed-Nonce: {00000000-0000-0000-0000-000000000000}\r\n"
					"\r\n";
			}

			acknowledge(message);

			if(m_transfer)
			{
				// NOTE The sending client can ask for a direct connections
				// if one was established before.
				if(!m_file)
				{
					QFile *destionation = new QFile(m_transfer->destinationURL().path());
					if(!destionation->open(IO_WriteOnly))
					{
						if(m_transfer){
							m_transfer->slotError(KIO::ERR_CANNOT_OPEN_FOR_WRITING, i18n("Cannot open file for writing"));
							m_transfer = 0l;
						}

						error();
						return;
					}

					m_file = destionation;
				}
				else
				{
					// TODO
				}
			}

			m_state = DataTransfer;
			// Send 200 OK message to the sending client.
			sendMessage(OK, content);
		}
		else if(body.startsWith("BYE"))
		{
			m_state = Finished;
			// Send the sending client an acknowledge message.
			acknowledge(message);

			if(m_file && m_transfer)
			{
				if(m_isComplete){
					// The transfer is complete.
					m_transfer->slotComplete();
				}
				else
				{
					// The transfer has been canceled remotely.
					if(m_transfer){
						// Inform the user of the file transfer cancelation.
						m_transfer->slotError(KIO::ERR_ABORTED, i18n("File transfer canceled."));
					}
					// Remove the partially received file.
					m_file->remove();
				}
			}

			// Dispose of this transfer context.
			m_dispatcher->detach(this);
		}
		else if(body.startsWith("MSNSLP/1.0 200 OK"))
		{
			if(m_type == UserDisplayIcon){
				m_state = Negotiation;
				// Acknowledge the 200 OK message.
				acknowledge(message);
			}
		}
	}
}

void IncomingTransfer::slotListenError(int /*errorCode*/)
{
	kdDebug(14140) << k_funcinfo << m_listener->errorString() << endl;
}

void IncomingTransfer::slotAccept()
{
	// Try to accept an incoming connection from the sending client.
	m_socket = static_cast<KBufferedSocket*>(m_listener->accept());
	if(!m_socket)
	{
		// NOTE If direct connection fails, the sending
		// client wil transfer the file data through the
		// existing session.
		kdDebug(14140) << k_funcinfo << "Direct connection failed." << endl;
		// Close the listening endpoint.
		m_listener->close();
		return;
	}

	kdDebug(14140) << k_funcinfo << "Direct connection established." << endl;

	// Set the socket to non blocking,
	// enable the ready read signal and disable
	// ready write signal.
	// NOTE readyWrite consumes too much cpu usage.
	m_socket->setBlocking(false);
	m_socket->enableRead(true);
	m_socket->enableWrite(false);

	// Create the callback that will try to read bytes from the accepted socket.
	QObject::connect(m_socket, SIGNAL(readyRead()),   this, SLOT(slotSocketRead()));
	// Create the callback that will try to handle the socket close event.
	QObject::connect(m_socket, SIGNAL(closed()),      this, SLOT(slotSocketClosed()));
	// Create the callback that will try to handle the socket error event.
	QObject::connect(m_socket, SIGNAL(gotError(int)), this, SLOT(slotSocketError(int)));
}

void IncomingTransfer::slotSocketRead()
{
	int available = m_socket->bytesAvailable();
	kdDebug(14140) << k_funcinfo << available << ", bytes available." << endl;
	if(available > 0)
	{
		QByteArray buffer(available);
		m_socket->readBlock(buffer.data(), buffer.size());

		if(QString(buffer) == "foo"){
			kdDebug(14140) << "Connection Check." << endl;
		}
	}
}

void IncomingTransfer::slotSocketClosed()
{
	kdDebug(14140) << k_funcinfo << endl;
}

void IncomingTransfer::slotSocketError(int errorCode)
{
	kdDebug(14140) << k_funcinfo << errorCode << endl;
}

#include "incomingtransfer.moc"
<|endoftext|>


<commit_before>{
   //
   gROOT->Reset();
   c1 = new TCanvas("c1","Help to run demos",200,10,700,500);

   welcome = new TPaveText(.1,.8,.9,.97);
   welcome->AddText("Welcome to the ROOT demos");
   welcome->SetTextColor(4);
   welcome->SetFillColor(24);
   welcome->Draw();

   hdemo = new TPaveText(.05,.05,.95,.7);
   hdemo->SetTextAlign(12);
   hdemo->SetTextFont(61);
   hdemo->AddText("- Run demo Hsimple first. Then in any order");
   hdemo->AddText("- Click left mouse button to execute one demo");
   hdemo->AddText("- Click right mouse button to see the title of the demo");
   hdemo->AddText("- Click on 'Close Bar' to exit from the demo menu");
   hdemo->AddText("- Select 'File/Print' to print a Postscript view of the canvas");
   hdemo->AddText("- You can execute a demo with the mouse or type commands");
   hdemo->AddText("- During the demo (try on this canvas) you can :");
   hdemo->AddText(".... Use left button to move/grow/etc objects");
   hdemo->AddText(".... Use middle button to pop overlapping objects");
   hdemo->AddText("     (with 2-buttons mouse just use left and right buttons simultaneously)");
   hdemo->AddText(".... Use right button to get an object sensitive pop-up");
   hdemo->SetAllWith("....","color",2);
   hdemo->SetAllWith("....","font",72);
   hdemo->SetAllWith("(with","color",4);
   hdemo->SetAllWith("(with","font",8);

   hdemo->Draw();
}
<commit_msg>Modify the help demo: -change the name of the canvas from c1 to chelp -change the font and colors<commit_after>{
   //
   gROOT->Reset();
   new TCanvas("chelp","Help to run demos",200,10,700,500);

   welcome = new TPaveText(.1,.8,.9,.97);
   welcome->AddText("Welcome to the ROOT demos");
   welcome->SetTextFont(32);
   welcome->SetTextColor(4);
   welcome->SetFillColor(24);
   welcome->Draw();

   hdemo = new TPaveText(.05,.05,.95,.7);
   hdemo->SetTextAlign(12);
   hdemo->SetTextFont(52);
   hdemo->AddText("- Run demo hsimple.C first. Then in any order");
   hdemo->AddText("- Click left mouse button to execute one demo");
   hdemo->AddText("- Click right mouse button to see the title of the demo");
   hdemo->AddText("- Click on 'Close Bar' to exit from the demo menu");
   hdemo->AddText("- Select 'File/Print' to print a Postscript view of the canvas");
   hdemo->AddText("- You can execute a demo with the mouse or type commands");
   hdemo->AddText("- During the demo (try on this canvas) you can :");
   hdemo->AddText("  .... Use left button to move/grow/etc objects");
   hdemo->AddText("  .... Use middle button to pop overlapping objects");
   hdemo->AddText("  .... Use right button to get an object sensitive pop-up");
   hdemo->AddText(" ");
   hdemo->SetAllWith("....","color",2);
   hdemo->SetAllWith("....","font",72);
   hdemo->SetAllWith("....","size",0.04);

   hdemo->Draw();
}
<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2016.  All rights reserved
 */
#include    "../StroikaPreComp.h"

#if     qPlatform_Linux
#include    <execinfo.h>
#include    <unistd.h>
#elif   qPlatform_Windows
#include    <Windows.h>
#endif

#include    "../Characters/LineEndings.h"
#include    "../Characters/StringBuilder.h"
#include    "../Execution/Finally.h"

#include    "BackTrace.h"

using   namespace   Stroika::Foundation;
using   namespace   Stroika::Foundation::Characters;

/*
 ********************************************************************************
 ********************************* Debug::BackTrace *****************************
 ********************************************************************************
 */
String    Debug::BackTrace ()
{
#if     qPlatform_Linux
    // @see http://man7.org/linux/man-pages/man3/backtrace.3.html
    constexpr   size_t  kMaxStackSize_  =   100;        // could look at return size and re-run if equals exactly...
    void* stackTraceBuf[kMaxStackSize_] {};
    int nptrs = ::backtrace (buffer, NEltsOf (stackTraceBuf));
    DbgTrace ("backtrace() returned %d addresses\n", nptrs);
    char**   syms = ::backtrace_symbols (stackTraceBuf, nptrs);
    if (syms == NULL) {
        DbgTrace ("%d errno", errno); // perror("backtrace_symbols");
        return String {};
    }
    Execution::Finally cleanup ([syms] () { if (syms != nullptr) ::free (syms); });
    Characters::StringBuilder    out;
    for (int j = 0; j < nptrs; j++) {
        out.Append (String::FromNarrowSDKString (syms[j]) + Characters::GetEOL<wchar_t> ());
    }
    return out.str ();
}
#else
    return Characters::String {};
#endif
}
<commit_msg>minor cleanups for Debug::BackTrace ()<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2016.  All rights reserved
 */
#include    "../StroikaPreComp.h"

#if     qPlatform_Linux
#include    <execinfo.h>
#include    <unistd.h>
#elif   qPlatform_Windows
#include    <Windows.h>
#endif

#include    "../Characters/LineEndings.h"
#include    "../Characters/StringBuilder.h"
#include    "../Execution/Finally.h"

#include    "BackTrace.h"

using   namespace   Stroika::Foundation;
using   namespace   Stroika::Foundation::Characters;

/*
 ********************************************************************************
 ********************************* Debug::BackTrace *****************************
 ********************************************************************************
 */
String    Debug::BackTrace ()
{
#if     qPlatform_Linux
    // @see http://man7.org/linux/man-pages/man3/backtrace.3.html
    constexpr   size_t  kMaxStackSize_  =   100;        // could look at return size and re-run if equals exactly...
    void* stackTraceBuf[kMaxStackSize_] {};
    int nptrs = ::backtrace (stackTraceBuf, NEltsOf (stackTraceBuf));
    DbgTrace ("backtrace() returned %d addresses\n", nptrs);
    char**   syms = ::backtrace_symbols (stackTraceBuf, nptrs);
    if (syms == NULL) {
        DbgTrace ("%d errno", errno); // perror("backtrace_symbols");
        return String {};
    }
    Execution::Finally cleanup ([syms] () { if (syms != nullptr) ::free (syms); });
    StringBuilder    out;
    for (int j = 0; j < nptrs; j++) {
        out.Append (String::FromNarrowSDKString (syms[j]) + Characters::GetEOL<wchar_t> ());
    }
    return out.str ();
}
#else
    return Characters::String {};
#endif
}
<|endoftext|>


<commit_before>
/*
  icqaccount.cpp  -  ICQ Account Class

  Copyright (c) 2003 by Stefan Gehn
  Kopete    (c) 2003 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 "icqaccount.h"
#include "icqcontact.h"
#include "icqprotocol.h"
#include "icqchangestatus.h"

#include <kdebug.h>
#include <klocale.h>
#include <kpopupmenu.h>

#include "kopetestdaction.h"

#include <oscarcontact.h>

ICQAccount::ICQAccount(KopeteProtocol *parent, QString accountID, const char *name)
	: OscarAccount(parent, accountID, name, true)
{
	mMyself = 0;
	mAwayDialog = new ICQChangeStatus(getEngine());
}

void ICQAccount::loaded()
{
	// needs to be here because pluginData() does not work in constructor
	mMyself = new ICQContact(accountId(), pluginData(protocol(), "NickName"), this, 0L);
}

ICQAccount::~ICQAccount()
{
//	kdDebug(14200) << k_funcinfo << "[" << accountId() << "] deleted" << endl;
	delete mAwayDialog;
}

KActionMenu* ICQAccount::actionMenu()
{
	// mActionMenu is managed by KopeteAccount.  It is deleted when
	// it is no longer shown, so we can (safely) just make a new one here.

	KActionMenu* mActionMenu = new KActionMenu(accountId(),
		"icq_protocol", this, "ICQAccount::mActionMenu");

	ICQProtocol *p = ICQProtocol::protocol();

	KAction* mActionOnline = new KAction(p->statusOnline.caption(),
		p->statusOnline.iconFor(this), 0,
		this, SLOT(slotGoOnline()), this, "ICQAccount::mActionOnline");

	KAction* mActionOffline = new KAction(p->statusOffline.caption(),
		p->statusOffline.iconFor(this), 0,
		this, SLOT(slotGoOffline()), this, "ICQAccount::mActionOffline");

	KAction* mActionAway		= new KAction(p->statusAway.caption(),
		p->statusAway.iconFor(this), 0,
		this, SLOT(slotGoAway()), this, "ICQAccount::mActionAway");

	KAction* mActionNA = new KAction(p->statusNA.caption(),
		p->statusNA.iconFor(this), 0,
		this, SLOT(slotGoNA()), this, "ICQAccount::mActionNA");

	KAction* mActionDND = new KAction(p->statusDND.caption(),
		p->statusDND.iconFor(this), 0,
		this, SLOT(slotGoDND()), this, "ICQAccount::mActionDND");

	KAction* mActionOccupied = new KAction(p->statusOCC.caption(),
		p->statusOCC.iconFor(this), 0,
		this, SLOT(slotGoOCC()), this, "ICQAccount::mActionOccupied");

	KAction* mActionFFC = new KAction(p->statusFFC.caption(),
		p->statusFFC.iconFor(this), 0,
		this, SLOT(slotGoFFC()), this, "ICQAccount::mActionFFC");

	mActionMenu->popupMenu()->insertTitle(
		mMyself->onlineStatus().iconFor(mMyself),
		i18n("%2 <%1>").arg(accountId()).arg(mMyself->displayName()));

	mActionMenu->insert(mActionOnline); // always first
	mActionMenu->insert(mActionFFC);
	mActionMenu->insert(mActionAway);
	mActionMenu->insert(mActionNA);
	mActionMenu->insert(mActionDND);
	mActionMenu->insert(mActionOccupied);
	mActionMenu->insert(mActionOffline);
	mActionMenu->popupMenu()->insertSeparator();
	mActionMenu->insert(
		KopeteStdAction::contactInfo(myself(), SLOT(slotUserInfo()),
			mActionMenu, "ICQAccount::mActionEditInfo"));

	return mActionMenu;
}

void ICQAccount::slotGoNA()
{
	kdDebug(14200) << k_funcinfo << "Called" << endl;
	// Away could also be a different AWAY mode (like NA or OCC)
	if(
		(myself()->onlineStatus().status() == KopeteOnlineStatus::Online) ||
		(myself()->onlineStatus().status() == KopeteOnlineStatus::Away)
		)
	{
		mAwayDialog->show(OSCAR_NA);
	}
}

void ICQAccount::slotGoOCC()
{
	kdDebug(14200) << k_funcinfo << "Called" << endl;
	// Away could also be a different AWAY mode (like NA or OCC)
	if(
		(myself()->onlineStatus().status() == KopeteOnlineStatus::Online) ||
		(myself()->onlineStatus().status() == KopeteOnlineStatus::Away)
		)
	{
		mAwayDialog->show(OSCAR_OCC);
	}
}

void ICQAccount::slotGoFFC()
{
	kdDebug(14200) << k_funcinfo << "Called" << endl;
	// Away could also be a different AWAY mode (like NA or OCC)
	if(
		(myself()->onlineStatus().status() == KopeteOnlineStatus::Online) ||
		(myself()->onlineStatus().status() == KopeteOnlineStatus::Away)
		)
		mEngine->sendStatus(ICQ_STATUS_FFC);
}

void ICQAccount::slotGoDND()
{
	kdDebug(14200) << k_funcinfo << "Called" << endl;
	// Away could also be a different AWAY mode (like NA or OCC)
	if(
		(myself()->onlineStatus().status() == KopeteOnlineStatus::Online) ||
		(myself()->onlineStatus().status() == KopeteOnlineStatus::Away)
		)
		mEngine->sendStatus(ICQ_STATUS_DND);
}

void ICQAccount::setAway(bool away, const QString &awayReason)
{
	kdDebug(14200) << k_funcinfo << " " << accountId() << endl;
// TODO: Make use of away message as well
	if(away)
	{
		if((myself()->onlineStatus().status() == KopeteOnlineStatus::Online) ||
			(myself()->onlineStatus().status() == KopeteOnlineStatus::Away))
		{
			mEngine->sendStatus(ICQ_STATUS_AWAY);
		}
	}
	else
	{
		if(myself()->onlineStatus().status() == KopeteOnlineStatus::Away)
			mEngine->sendStatus(ICQ_STATUS_ONLINE);
	}
}

OscarContact *ICQAccount::createNewContact(
	const QString &contactId,
	const QString &displayName,
	KopeteMetaContact *parentContact)
{
	kdDebug(14200) << k_funcinfo << "contactId='" << contactId << "', displayName='" <<
		displayName << "', ptr parentContact=" << parentContact << endl;
	return new ICQContact(contactId,displayName,this,parentContact);
}

#include "icqaccount.moc"
// vim: set noet ts=4 sts=4 sw=4:
<commit_msg>The myself contact has to be created in the constructor, like the doc say<commit_after>
/*
  icqaccount.cpp  -  ICQ Account Class

  Copyright (c) 2003 by Stefan Gehn
  Kopete    (c) 2003 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 "icqaccount.h"
#include "icqcontact.h"
#include "icqprotocol.h"
#include "icqchangestatus.h"

#include <kdebug.h>
#include <klocale.h>
#include <kpopupmenu.h>

#include "kopetestdaction.h"

#include <oscarcontact.h>

ICQAccount::ICQAccount(KopeteProtocol *parent, QString accountID, const char *name)
	: OscarAccount(parent, accountID, name, true)
{
	//myself has to be created in constructor
	mMyself = new ICQContact(accountId(), accountId(), this, 0L);
	mAwayDialog = new ICQChangeStatus(getEngine());
}

void ICQAccount::loaded()
{
	// needs to be here because pluginData() does not work in constructor
	mMyself->rename(pluginData(protocol(), "NickName"));
}

ICQAccount::~ICQAccount()
{
//	kdDebug(14200) << k_funcinfo << "[" << accountId() << "] deleted" << endl;
	delete mAwayDialog;
}

KActionMenu* ICQAccount::actionMenu()
{
	// mActionMenu is managed by KopeteAccount.  It is deleted when
	// it is no longer shown, so we can (safely) just make a new one here.

	KActionMenu* mActionMenu = new KActionMenu(accountId(),
		"icq_protocol", this, "ICQAccount::mActionMenu");

	ICQProtocol *p = ICQProtocol::protocol();

	KAction* mActionOnline = new KAction(p->statusOnline.caption(),
		p->statusOnline.iconFor(this), 0,
		this, SLOT(slotGoOnline()), this, "ICQAccount::mActionOnline");

	KAction* mActionOffline = new KAction(p->statusOffline.caption(),
		p->statusOffline.iconFor(this), 0,
		this, SLOT(slotGoOffline()), this, "ICQAccount::mActionOffline");

	KAction* mActionAway		= new KAction(p->statusAway.caption(),
		p->statusAway.iconFor(this), 0,
		this, SLOT(slotGoAway()), this, "ICQAccount::mActionAway");

	KAction* mActionNA = new KAction(p->statusNA.caption(),
		p->statusNA.iconFor(this), 0,
		this, SLOT(slotGoNA()), this, "ICQAccount::mActionNA");

	KAction* mActionDND = new KAction(p->statusDND.caption(),
		p->statusDND.iconFor(this), 0,
		this, SLOT(slotGoDND()), this, "ICQAccount::mActionDND");

	KAction* mActionOccupied = new KAction(p->statusOCC.caption(),
		p->statusOCC.iconFor(this), 0,
		this, SLOT(slotGoOCC()), this, "ICQAccount::mActionOccupied");

	KAction* mActionFFC = new KAction(p->statusFFC.caption(),
		p->statusFFC.iconFor(this), 0,
		this, SLOT(slotGoFFC()), this, "ICQAccount::mActionFFC");

	mActionMenu->popupMenu()->insertTitle(
		mMyself->onlineStatus().iconFor(mMyself),
		i18n("%2 <%1>").arg(accountId()).arg(mMyself->displayName()));

	mActionMenu->insert(mActionOnline); // always first
	mActionMenu->insert(mActionFFC);
	mActionMenu->insert(mActionAway);
	mActionMenu->insert(mActionNA);
	mActionMenu->insert(mActionDND);
	mActionMenu->insert(mActionOccupied);
	mActionMenu->insert(mActionOffline);
	mActionMenu->popupMenu()->insertSeparator();
	mActionMenu->insert(
		KopeteStdAction::contactInfo(myself(), SLOT(slotUserInfo()),
			mActionMenu, "ICQAccount::mActionEditInfo"));

	return mActionMenu;
}

void ICQAccount::slotGoNA()
{
	kdDebug(14200) << k_funcinfo << "Called" << endl;
	// Away could also be a different AWAY mode (like NA or OCC)
	if(
		(myself()->onlineStatus().status() == KopeteOnlineStatus::Online) ||
		(myself()->onlineStatus().status() == KopeteOnlineStatus::Away)
		)
	{
		mAwayDialog->show(OSCAR_NA);
	}
}

void ICQAccount::slotGoOCC()
{
	kdDebug(14200) << k_funcinfo << "Called" << endl;
	// Away could also be a different AWAY mode (like NA or OCC)
	if(
		(myself()->onlineStatus().status() == KopeteOnlineStatus::Online) ||
		(myself()->onlineStatus().status() == KopeteOnlineStatus::Away)
		)
	{
		mAwayDialog->show(OSCAR_OCC);
	}
}

void ICQAccount::slotGoFFC()
{
	kdDebug(14200) << k_funcinfo << "Called" << endl;
	// Away could also be a different AWAY mode (like NA or OCC)
	if(
		(myself()->onlineStatus().status() == KopeteOnlineStatus::Online) ||
		(myself()->onlineStatus().status() == KopeteOnlineStatus::Away)
		)
		mEngine->sendStatus(ICQ_STATUS_FFC);
}

void ICQAccount::slotGoDND()
{
	kdDebug(14200) << k_funcinfo << "Called" << endl;
	// Away could also be a different AWAY mode (like NA or OCC)
	if(
		(myself()->onlineStatus().status() == KopeteOnlineStatus::Online) ||
		(myself()->onlineStatus().status() == KopeteOnlineStatus::Away)
		)
		mEngine->sendStatus(ICQ_STATUS_DND);
}

void ICQAccount::setAway(bool away, const QString &awayReason)
{
	kdDebug(14200) << k_funcinfo << " " << accountId() << endl;
// TODO: Make use of away message as well
	if(away)
	{
		if((myself()->onlineStatus().status() == KopeteOnlineStatus::Online) ||
			(myself()->onlineStatus().status() == KopeteOnlineStatus::Away))
		{
			mEngine->sendStatus(ICQ_STATUS_AWAY);
		}
	}
	else
	{
		if(myself()->onlineStatus().status() == KopeteOnlineStatus::Away)
			mEngine->sendStatus(ICQ_STATUS_ONLINE);
	}
}

OscarContact *ICQAccount::createNewContact(
	const QString &contactId,
	const QString &displayName,
	KopeteMetaContact *parentContact)
{
	kdDebug(14200) << k_funcinfo << "contactId='" << contactId << "', displayName='" <<
		displayName << "', ptr parentContact=" << parentContact << endl;
	return new ICQContact(contactId,displayName,this,parentContact);
}

#include "icqaccount.moc"
// vim: set noet ts=4 sts=4 sw=4:
<|endoftext|>


<commit_before>//
// Copyright (C) 2013-2018 University of Amsterdam
//
// 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.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

#include "labels.h"
#include "iostream"

using namespace std;

map<int, map<int, string> > Labels::_orgStringValues;
int Labels::_counter = 0;

Labels::Labels(boost::interprocess::managed_shared_memory *mem)
	: _labels(mem->get_segment_manager())
{
	 _id = ++Labels::_counter;
	_mem = mem;
}

Labels::~Labels()
{
}

void Labels::clear()
{
	_labels.clear();
}

int Labels::add(int display)
{
	Label label(display);
	_labels.push_back(label);

	return display;
}

int Labels::add(const std::string &display)
{
	return add(_labels.size(), display, true);
}

int Labels::add(int key, const std::string &display, bool filterAllows)
{
	Label label(display, key, filterAllows);
	_labels.push_back(label);

	return key;
}

void Labels::removeValues(std::set<int> valuesToRemove)
{
	_labels.erase(
		std::remove_if(
			_labels.begin(),
			_labels.end(),
			[&valuesToRemove](const Label& label) {
				return std::find(valuesToRemove.begin(), valuesToRemove.end(), label.value()) != valuesToRemove.end();
			}),
		_labels.end());
}

bool Labels::syncInts(map<int, string> &values)
{
	std::set<int> keys;
	for (const auto &value : values)
		keys.insert(value.first);

	bool changed = syncInts(keys);

	for (Label& label : _labels)
	{
		int value = label.value();
		string &new_string_label = values[value];
		string old_string_label = label.text();
		if (new_string_label != old_string_label)
		{
			_setNewStringForLabel(label, new_string_label);
			changed = true;
		}
	}

	return changed;
}

bool Labels::syncInts(const std::set<int> &values)
{
	std::set<int> valuesToAdd = values;
	std::set<int> valuesToRemove;

	for (const Label& label : _labels)
	{
		int value = label.value();
		if (std::find(values.begin(), values.end(), value) != values.end())
		{
			std::set<int>::iterator value_it = std::find(valuesToAdd.begin(), valuesToAdd.end(), value);
			if (value_it != valuesToAdd.end())
				valuesToAdd.erase(value_it);
		}
		else
		{
			std::cout << "Remove label " << label.text() << std::endl;
			std::cout.flush();
			valuesToRemove.insert(value);
		}
	}

	removeValues(valuesToRemove);

	for (int value : valuesToAdd)
		add(value);

	return valuesToAdd.size() + valuesToRemove.size() > 0;
}

std::map<std::string, int> Labels::syncStrings(const std::vector<std::string> &new_values, const std::map<std::string, std::string> &new_labels, bool *changedSomething)
{
	std::vector<std::pair<std::string,std::string> > valuesToAdd;
	std::map<std::string, std::vector<uint> > mapValuesToAdd;
	uint valuesToAddIndex = 0;

	for (const std::string& newValue : new_values)
	{
		std::string shortValue	= newValue.length() > Label::MAX_LABEL_LENGTH ? newValue.substr(0, Label::MAX_LABEL_LENGTH) : newValue;
		valuesToAdd.push_back(make_pair(newValue, shortValue));
		auto elt = mapValuesToAdd.find(shortValue);
		if (elt != mapValuesToAdd.end())
			elt->second.push_back(valuesToAddIndex);
		else
			mapValuesToAdd[shortValue] = { valuesToAddIndex };
		valuesToAddIndex++;
	}

	std::set<int>				valuesToRemove;
	std::map<std::string, int>	result;
	int							maxLabelKey = 0;

	for (const Label &label : _labels)
	{
		std::string labelText	= _getOrgValueFromLabel(label);
		int labelValue			= label.value();

		if (labelValue > maxLabelKey)
			maxLabelKey = labelValue;

		auto elt = mapValuesToAdd.find(labelText);
		if (elt != mapValuesToAdd.end())
		{
			for (uint i : elt->second)
				result[valuesToAdd[i].first] = labelValue;
			mapValuesToAdd.erase(elt);
		}
		else
			valuesToRemove.insert(labelValue);
	}

	if(changedSomething != nullptr && (valuesToRemove.size() > 0 || mapValuesToAdd.size() > 0))
		*changedSomething = true;

	removeValues(valuesToRemove);

	for (auto elt : valuesToAdd)
	{
		maxLabelKey++;
		add(maxLabelKey, elt.second, true);
		result[elt.first] = maxLabelKey;
	}

	for (Label& label : _labels)
	{
		std::string labelText = _getOrgValueFromLabel(label);
		auto newLabelIt = new_labels.find(labelText);
		if (newLabelIt != new_labels.end())
		{
			std::string newStringLabel = newLabelIt->second;
			if (labelText != newStringLabel)
			{
				_setNewStringForLabel(label, newStringLabel);

				if(changedSomething != nullptr)
					*changedSomething = true;
			}
		}
	}
	return result;
}

std::set<int> Labels::getIntValues()
{
	std::set<int> result;
	for (const Label& label : _labels)
		result.insert(label.value());

	return result;
}

map<int, string> &Labels::getOrgStringValues() const
{
	return Labels::_orgStringValues[_id];
}

void Labels::setOrgStringValues(int key, std::string value)
{
	map<int, string> &orgStringValues = getOrgStringValues();
	orgStringValues[key] = value;
}

const Label &Labels::getLabelObjectFromKey(int index) const

{
	for (const Label &label: _labels)
	{
		if (label.value() == index)
			return label;
	}

	std::cout << "Cannot find entry " << index << std::endl;
	for(const Label &label: _labels)
	{
		std::cout << "Label Value: " << label.value() << ", Text: " << label.text() << std::endl;
	}
	std::cout.flush();
	throw runtime_error("Cannot find this entry");
}

bool Labels::setLabelFromRow(int row, const string &display)
{
	if (row >= (int)_labels.size() || row < 0)
	{
		std::cout << "Set label with wrong row: " << row << ", size: " << _labels.size() << std::endl;
		std::cout.flush();
		return false;
	}

	try
	{
		Label &label = _labels.at(row);
		if (label.text() == display)
			return false;

		_setNewStringForLabel(label, display);
	}
	catch(...)
	{
		return false;
	}
	return true;
}

void Labels::_setNewStringForLabel(Label &label, const string &display)
{
	int label_value = label.value();
	string label_string = label.text();
	map<int, string> &orgStringValues = getOrgStringValues();
	if (orgStringValues.find(label_value) == orgStringValues.end())
		orgStringValues[label_value] = label_string;
	label.setLabel(display);
}

string Labels::_getValueFromLabel(const Label &label) const
{
	if (label.hasIntValue())
	{
		std::ostringstream ss;
		ss << label.value();
		return ss.str();
	}
	else
	{
		return _getOrgValueFromLabel(label);
	}
}

string Labels::_getOrgValueFromLabel(const Label &label) const
{
	map<int, string> &orgStringValues = getOrgStringValues();
	map<int, string>::const_iterator it = orgStringValues.find(label.value());
	if (it == orgStringValues.end())
		return label.text();
	else
		return it->second;
}

string Labels::getValueFromKey(int key) const
{
	const Label &label = getLabelObjectFromKey(key);
	return _getValueFromLabel(label);
}

string Labels::getValueFromRow(int row)
{
	if (row >= (int)_labels.size())
	{
		std::cout << "Get value with wrong row: " << row << ", size: " << _labels.size() << std::endl;
		std::cout.flush();
		return "";
	}
	const Label &label = _labels.at(row);
	return _getValueFromLabel(label);
}

Label& Labels::operator[](size_t index)
{
	return _labels.at(index);
}

std::string Labels::getLabelFromRow(int row)
{
	if (row >= (int)_labels.size())
	{
		std::cout << "Get label with wrong row: " << row << ", size: " << _labels.size() << std::endl;
		std::cout.flush();
		return "";
	}
	Label &label = _labels.at(row);
	return label.text();
}

void Labels::set(vector<Label> &labels)
{
	clear();
	for (const Label &label : labels)
	{
		_labels.push_back(label);
	}
}

size_t Labels::size() const
{
	return _labels.size();
}

Labels &Labels::operator=(const Labels &labels)
{
	if (&labels != this)
	{
		this->_mem = labels._mem;
		this->_labels = labels._labels;
	}

	return *this;
}

void Labels::setSharedMemory(boost::interprocess::managed_shared_memory *mem)
{
	_mem = mem;
}

Labels::const_iterator Labels::begin() const
{
	return _labels.begin();
}

Labels::const_iterator Labels::end() const
{
	return _labels.end();
}
<commit_msg>Windows doesn't know about uint...<commit_after>//
// Copyright (C) 2013-2018 University of Amsterdam
//
// 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.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

#include "labels.h"
#include "iostream"

using namespace std;

map<int, map<int, string> > Labels::_orgStringValues;
int Labels::_counter = 0;

Labels::Labels(boost::interprocess::managed_shared_memory *mem)
	: _labels(mem->get_segment_manager())
{
	 _id = ++Labels::_counter;
	_mem = mem;
}

Labels::~Labels()
{
}

void Labels::clear()
{
	_labels.clear();
}

int Labels::add(int display)
{
	Label label(display);
	_labels.push_back(label);

	return display;
}

int Labels::add(const std::string &display)
{
	return add(_labels.size(), display, true);
}

int Labels::add(int key, const std::string &display, bool filterAllows)
{
	Label label(display, key, filterAllows);
	_labels.push_back(label);

	return key;
}

void Labels::removeValues(std::set<int> valuesToRemove)
{
	_labels.erase(
		std::remove_if(
			_labels.begin(),
			_labels.end(),
			[&valuesToRemove](const Label& label) {
				return std::find(valuesToRemove.begin(), valuesToRemove.end(), label.value()) != valuesToRemove.end();
			}),
		_labels.end());
}

bool Labels::syncInts(map<int, string> &values)
{
	std::set<int> keys;
	for (const auto &value : values)
		keys.insert(value.first);

	bool changed = syncInts(keys);

	for (Label& label : _labels)
	{
		int value = label.value();
		string &new_string_label = values[value];
		string old_string_label = label.text();
		if (new_string_label != old_string_label)
		{
			_setNewStringForLabel(label, new_string_label);
			changed = true;
		}
	}

	return changed;
}

bool Labels::syncInts(const std::set<int> &values)
{
	std::set<int> valuesToAdd = values;
	std::set<int> valuesToRemove;

	for (const Label& label : _labels)
	{
		int value = label.value();
		if (std::find(values.begin(), values.end(), value) != values.end())
		{
			std::set<int>::iterator value_it = std::find(valuesToAdd.begin(), valuesToAdd.end(), value);
			if (value_it != valuesToAdd.end())
				valuesToAdd.erase(value_it);
		}
		else
		{
			std::cout << "Remove label " << label.text() << std::endl;
			std::cout.flush();
			valuesToRemove.insert(value);
		}
	}

	removeValues(valuesToRemove);

	for (int value : valuesToAdd)
		add(value);

	return valuesToAdd.size() + valuesToRemove.size() > 0;
}

std::map<std::string, int> Labels::syncStrings(const std::vector<std::string> &new_values, const std::map<std::string, std::string> &new_labels, bool *changedSomething)
{
	std::vector<std::pair<std::string,std::string> > valuesToAdd;
	std::map<std::string, std::vector<unsigned int> > mapValuesToAdd;
	uint valuesToAddIndex = 0;

	for (const std::string& newValue : new_values)
	{
		std::string shortValue	= newValue.length() > Label::MAX_LABEL_LENGTH ? newValue.substr(0, Label::MAX_LABEL_LENGTH) : newValue;
		valuesToAdd.push_back(make_pair(newValue, shortValue));
		auto elt = mapValuesToAdd.find(shortValue);
		if (elt != mapValuesToAdd.end())
			elt->second.push_back(valuesToAddIndex);
		else
			mapValuesToAdd[shortValue] = { valuesToAddIndex };
		valuesToAddIndex++;
	}

	std::set<int>				valuesToRemove;
	std::map<std::string, int>	result;
	int							maxLabelKey = 0;

	for (const Label &label : _labels)
	{
		std::string labelText	= _getOrgValueFromLabel(label);
		int labelValue			= label.value();

		if (labelValue > maxLabelKey)
			maxLabelKey = labelValue;

		auto elt = mapValuesToAdd.find(labelText);
		if (elt != mapValuesToAdd.end())
		{
			for (uint i : elt->second)
				result[valuesToAdd[i].first] = labelValue;
			mapValuesToAdd.erase(elt);
		}
		else
			valuesToRemove.insert(labelValue);
	}

	if(changedSomething != nullptr && (valuesToRemove.size() > 0 || mapValuesToAdd.size() > 0))
		*changedSomething = true;

	removeValues(valuesToRemove);

	for (auto elt : valuesToAdd)
	{
		maxLabelKey++;
		add(maxLabelKey, elt.second, true);
		result[elt.first] = maxLabelKey;
	}

	for (Label& label : _labels)
	{
		std::string labelText = _getOrgValueFromLabel(label);
		auto newLabelIt = new_labels.find(labelText);
		if (newLabelIt != new_labels.end())
		{
			std::string newStringLabel = newLabelIt->second;
			if (labelText != newStringLabel)
			{
				_setNewStringForLabel(label, newStringLabel);

				if(changedSomething != nullptr)
					*changedSomething = true;
			}
		}
	}
	return result;
}

std::set<int> Labels::getIntValues()
{
	std::set<int> result;
	for (const Label& label : _labels)
		result.insert(label.value());

	return result;
}

map<int, string> &Labels::getOrgStringValues() const
{
	return Labels::_orgStringValues[_id];
}

void Labels::setOrgStringValues(int key, std::string value)
{
	map<int, string> &orgStringValues = getOrgStringValues();
	orgStringValues[key] = value;
}

const Label &Labels::getLabelObjectFromKey(int index) const

{
	for (const Label &label: _labels)
	{
		if (label.value() == index)
			return label;
	}

	std::cout << "Cannot find entry " << index << std::endl;
	for(const Label &label: _labels)
	{
		std::cout << "Label Value: " << label.value() << ", Text: " << label.text() << std::endl;
	}
	std::cout.flush();
	throw runtime_error("Cannot find this entry");
}

bool Labels::setLabelFromRow(int row, const string &display)
{
	if (row >= (int)_labels.size() || row < 0)
	{
		std::cout << "Set label with wrong row: " << row << ", size: " << _labels.size() << std::endl;
		std::cout.flush();
		return false;
	}

	try
	{
		Label &label = _labels.at(row);
		if (label.text() == display)
			return false;

		_setNewStringForLabel(label, display);
	}
	catch(...)
	{
		return false;
	}
	return true;
}

void Labels::_setNewStringForLabel(Label &label, const string &display)
{
	int label_value = label.value();
	string label_string = label.text();
	map<int, string> &orgStringValues = getOrgStringValues();
	if (orgStringValues.find(label_value) == orgStringValues.end())
		orgStringValues[label_value] = label_string;
	label.setLabel(display);
}

string Labels::_getValueFromLabel(const Label &label) const
{
	if (label.hasIntValue())
	{
		std::ostringstream ss;
		ss << label.value();
		return ss.str();
	}
	else
	{
		return _getOrgValueFromLabel(label);
	}
}

string Labels::_getOrgValueFromLabel(const Label &label) const
{
	map<int, string> &orgStringValues = getOrgStringValues();
	map<int, string>::const_iterator it = orgStringValues.find(label.value());
	if (it == orgStringValues.end())
		return label.text();
	else
		return it->second;
}

string Labels::getValueFromKey(int key) const
{
	const Label &label = getLabelObjectFromKey(key);
	return _getValueFromLabel(label);
}

string Labels::getValueFromRow(int row)
{
	if (row >= (int)_labels.size())
	{
		std::cout << "Get value with wrong row: " << row << ", size: " << _labels.size() << std::endl;
		std::cout.flush();
		return "";
	}
	const Label &label = _labels.at(row);
	return _getValueFromLabel(label);
}

Label& Labels::operator[](size_t index)
{
	return _labels.at(index);
}

std::string Labels::getLabelFromRow(int row)
{
	if (row >= (int)_labels.size())
	{
		std::cout << "Get label with wrong row: " << row << ", size: " << _labels.size() << std::endl;
		std::cout.flush();
		return "";
	}
	Label &label = _labels.at(row);
	return label.text();
}

void Labels::set(vector<Label> &labels)
{
	clear();
	for (const Label &label : labels)
	{
		_labels.push_back(label);
	}
}

size_t Labels::size() const
{
	return _labels.size();
}

Labels &Labels::operator=(const Labels &labels)
{
	if (&labels != this)
	{
		this->_mem = labels._mem;
		this->_labels = labels._labels;
	}

	return *this;
}

void Labels::setSharedMemory(boost::interprocess::managed_shared_memory *mem)
{
	_mem = mem;
}

Labels::const_iterator Labels::begin() const
{
	return _labels.begin();
}

Labels::const_iterator Labels::end() const
{
	return _labels.end();
}
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2012 - 2013 Jolla Ltd.
** Contact: http://jolla.com/
**
** This file is part of Qt Creator.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Digia.
**
****************************************************************************/

#include "mertargetsxmlparser.h"

#include <utils/fileutils.h>

#include <QAbstractMessageHandler>
#include <QAbstractXmlReceiver>
#include <QXmlSchema>
#include <QXmlSchemaValidator>
#include <QXmlQuery>
#include <QXmlStreamWriter>
#include <QStack>

const char TARGET[] = "target";
const char TARGETS[] = "targets";
const char OUTPUT[] = "output";
const char NAME[] = "name";
const char VERSION[] = "version";
const char GCCDUMPMACHINE[] = "GccDumpMachine";
const char QMAKEQUERY[] = "QmakeQuery";

namespace Mer {

namespace {

class MessageHandler : public QAbstractMessageHandler
{
    Q_OBJECT

public:
    QString errorString() const
    {
        return tr("Error %1:%2:%3 %4").arg(m_identifier.toString())
                .arg(m_sourceLocation.line())
                .arg(m_sourceLocation.column())
                .arg(m_description);
    }

protected:
    virtual void handleMessage(QtMsgType /*type*/, const QString &description,
                               const QUrl &identifier, const QSourceLocation &sourceLocation)
    {
        m_description = description;
        m_identifier = identifier;
        m_sourceLocation = sourceLocation;
    }

private:
    QString m_description;
    QSourceLocation m_sourceLocation;
    QUrl m_identifier;
};

class XmlReceiver : public QAbstractXmlReceiver
{
public:
    XmlReceiver(const QXmlNamePool &namePool) : m_namePool(namePool), m_version(-1) {}

    int version() const
    {
        return m_version;
    }

    QList<MerTargetData> targetData() const
    {
        return m_targets;
    }

protected:
    virtual void startElement(const QXmlName &name)
    {
        const QString element = name.localName(m_namePool);
        if (element == QLatin1String(TARGET))
            m_currentTarget.clear();
        m_currentElementStack.push(element);
    }

    virtual void endElement()
    {
        if (m_currentElementStack.pop() == QLatin1String(TARGET))
            m_targets.append(m_currentTarget);
    }

    virtual void attribute(const QXmlName &name,
                           const QStringRef &value)
    {
        const QString attributeName = name.localName(m_namePool);
        const QString attributeValue = value.toString();
        const QString element = m_currentElementStack.top();
        if (element == QLatin1String(TARGETS) && attributeName == QLatin1String(VERSION))
            m_version = attributeValue.toInt();
        else if (element == QLatin1String(TARGET) && attributeName == QLatin1String(NAME))
            m_currentTarget.name = attributeValue;
        else if (element == QLatin1String(OUTPUT) && attributeName == QLatin1String(NAME))
            m_attributeValue = attributeValue;
        else
            m_attributeValue.clear();
    }

    virtual void characters(const QStringRef &value)
    {
        if (m_currentElementStack.top() == QLatin1String(OUTPUT)) {
            if (m_attributeValue == QLatin1String(GCCDUMPMACHINE))
                m_currentTarget.gccDumpMachine = value.toString();
            else if (m_attributeValue == QLatin1String(QMAKEQUERY))
                m_currentTarget.qmakeQuery = value.toString();
        }
    }

    virtual void comment(const QString &/*value*/)
    {
    }

    virtual void startDocument()
    {
    }

    virtual void endDocument()
    {
    }

    virtual void processingInstruction(const QXmlName &/*target*/, const QString &/*value*/)
    {
    }

    virtual void atomicValue(const QVariant &/*value*/)
    {
    }
    virtual void namespaceBinding(const QXmlName &/*name*/)
    {
    }
    virtual void startOfSequence()
    {
    }
    virtual void endOfSequence()
    {
    }

private:
    QXmlNamePool m_namePool;
    QStack<QString> m_currentElementStack;
    QList<MerTargetData> m_targets;
    MerTargetData m_currentTarget;
    QString m_attributeValue;
    int m_version;
};

#ifdef Q_OS_MAC
#  define SHARE_PATH "/../Resources"
#else
#  define SHARE_PATH "/../share/qtcreator"
#endif

static QString applicationDirPath()
{
    return QCoreApplication::applicationDirPath();
}

static inline QString sharedDirPath()
{
    QString appPath = applicationDirPath();

    return QFileInfo(appPath + QLatin1String(SHARE_PATH)).absoluteFilePath();
}

} // Anonymous

class MerTargetsXmlReaderPrivate
{
public:
    MerTargetsXmlReaderPrivate()
        : receiver(new XmlReceiver(query.namePool())),
          error(false)
    {}

    ~MerTargetsXmlReaderPrivate()
    {
        delete receiver;
    }

    QXmlQuery query;
    XmlReceiver *receiver;
    MessageHandler messageHandler;
    bool error;
    QString errorString;
};

MerTargetsXmlReader::MerTargetsXmlReader(const QString &fileName, QObject *parent)
    : QObject(parent),
      d(new MerTargetsXmlReaderPrivate)
{
    Utils::FileReader reader;
    d->error = !reader.fetch(fileName, QIODevice::ReadOnly);
    if (d->error) {
        d->errorString = reader.errorString();
        return;
    }

    QXmlSchema schema;
    schema.setMessageHandler(&d->messageHandler);

    Utils::FileReader schemeReader;
    d->error = !schemeReader.fetch(QString::fromLatin1("%1/mer/targets.xsd").arg(sharedDirPath()),
            QIODevice::ReadOnly);
    if (d->error) {
        d->errorString = schemeReader.errorString();
        return;
    }
    schema.load(schemeReader.data());
    d->error = !schema.isValid();
    if (d->error) {
        d->errorString = d->messageHandler.errorString();
        return;
    }

    QXmlSchemaValidator validator(schema);
    validator.setMessageHandler(&d->messageHandler);
    d->error = !validator.validate(reader.data());
    if (d->error) {
        d->errorString = d->messageHandler.errorString();
        return;
    }

    d->query.setQuery(QString::fromLatin1("doc('%1')").arg(fileName));
    d->query.setMessageHandler(&d->messageHandler);
    d->error = !d->query.isValid();
    if (d->error) {
        d->errorString = d->messageHandler.errorString();
        return;
    }

    d->error = !d->query.evaluateTo(d->receiver);
    if (d->error)
        d->errorString = d->messageHandler.errorString();
}

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

bool MerTargetsXmlReader::hasError() const
{
    return d->error;
}

QString MerTargetsXmlReader::errorString() const
{
    return d->errorString;
}

QList<MerTargetData> MerTargetsXmlReader::targetData() const
{
    return d->receiver->targetData();
}

int MerTargetsXmlReader::version() const
{
    return d->receiver->version();
}

void MerTargetData::clear()
{
    name.clear();
    gccDumpMachine.clear();
    qmakeQuery.clear();
}

class MerTargetsXmlWriterPrivate
{
public:
    MerTargetsXmlWriterPrivate(const QString &fileName) : fileSaver(fileName, QIODevice::WriteOnly) {}

    Utils::FileSaver fileSaver;
};

MerTargetsXmlWriter::MerTargetsXmlWriter(const QString &fileName, int version,
                                         const QList<MerTargetData> &targetData, QObject *parent)
    : QObject(parent),
      d(new MerTargetsXmlWriterPrivate(fileName))
{
    QByteArray data;
    QXmlStreamWriter writer(&data);
    writer.setAutoFormatting(true);
    writer.writeStartDocument();
    writer.writeStartElement(QLatin1String(TARGETS));
    writer.writeAttribute(QLatin1String(VERSION), QString::number(version));
    foreach (const MerTargetData &data, targetData) {
        writer.writeStartElement(QLatin1String(TARGET));
        writer.writeAttribute(QLatin1String(NAME), data.name);
        writer.writeStartElement(QLatin1String(OUTPUT));
        writer.writeAttribute(QLatin1String(NAME), QLatin1String(GCCDUMPMACHINE));
        writer.writeCharacters(data.gccDumpMachine);
        writer.writeEndElement(); // output
        writer.writeStartElement(QLatin1String(OUTPUT));
        writer.writeAttribute(QLatin1String(NAME), QLatin1String(QMAKEQUERY));
        writer.writeCharacters(data.qmakeQuery);
        writer.writeEndElement(); // output
        writer.writeEndElement(); // target
    }
    writer.writeEndElement(); // targets
    writer.writeEndDocument();
    d->fileSaver.write(data);
    d->fileSaver.finalize();
}

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

bool MerTargetsXmlWriter::hasError() const
{
    return d->fileSaver.hasError();
}

QString MerTargetsXmlWriter::errorString() const
{
    return d->fileSaver.errorString();
}

} // Mer
<commit_msg>Mer: fix obscure linker error<commit_after>/****************************************************************************
**
** Copyright (C) 2012 - 2013 Jolla Ltd.
** Contact: http://jolla.com/
**
** This file is part of Qt Creator.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Digia.
**
****************************************************************************/

#include "mertargetsxmlparser.h"

#include <utils/fileutils.h>

#include <QAbstractMessageHandler>
#include <QAbstractXmlReceiver>
#include <QXmlSchema>
#include <QXmlSchemaValidator>
#include <QXmlQuery>
#include <QXmlStreamWriter>
#include <QStack>

const char TARGET[] = "target";
const char TARGETS[] = "targets";
const char OUTPUT[] = "output";
const char NAME[] = "name";
const char VERSION[] = "version";
const char GCCDUMPMACHINE[] = "GccDumpMachine";
const char QMAKEQUERY[] = "QmakeQuery";

namespace Mer {

namespace {

class MessageHandler : public QAbstractMessageHandler
{
public:
    QString errorString() const
    {
        return QObject::tr("Error %1:%2:%3 %4").arg(m_identifier.toString())
                .arg(m_sourceLocation.line())
                .arg(m_sourceLocation.column())
                .arg(m_description);
    }

protected:
    virtual void handleMessage(QtMsgType /*type*/, const QString &description,
                               const QUrl &identifier, const QSourceLocation &sourceLocation)
    {
        m_description = description;
        m_identifier = identifier;
        m_sourceLocation = sourceLocation;
    }

private:
    QString m_description;
    QSourceLocation m_sourceLocation;
    QUrl m_identifier;
};

class XmlReceiver : public QAbstractXmlReceiver
{
public:
    XmlReceiver(const QXmlNamePool &namePool) : m_namePool(namePool), m_version(-1) {}

    int version() const
    {
        return m_version;
    }

    QList<MerTargetData> targetData() const
    {
        return m_targets;
    }

protected:
    virtual void startElement(const QXmlName &name)
    {
        const QString element = name.localName(m_namePool);
        if (element == QLatin1String(TARGET))
            m_currentTarget.clear();
        m_currentElementStack.push(element);
    }

    virtual void endElement()
    {
        if (m_currentElementStack.pop() == QLatin1String(TARGET))
            m_targets.append(m_currentTarget);
    }

    virtual void attribute(const QXmlName &name,
                           const QStringRef &value)
    {
        const QString attributeName = name.localName(m_namePool);
        const QString attributeValue = value.toString();
        const QString element = m_currentElementStack.top();
        if (element == QLatin1String(TARGETS) && attributeName == QLatin1String(VERSION))
            m_version = attributeValue.toInt();
        else if (element == QLatin1String(TARGET) && attributeName == QLatin1String(NAME))
            m_currentTarget.name = attributeValue;
        else if (element == QLatin1String(OUTPUT) && attributeName == QLatin1String(NAME))
            m_attributeValue = attributeValue;
        else
            m_attributeValue.clear();
    }

    virtual void characters(const QStringRef &value)
    {
        if (m_currentElementStack.top() == QLatin1String(OUTPUT)) {
            if (m_attributeValue == QLatin1String(GCCDUMPMACHINE))
                m_currentTarget.gccDumpMachine = value.toString();
            else if (m_attributeValue == QLatin1String(QMAKEQUERY))
                m_currentTarget.qmakeQuery = value.toString();
        }
    }

    virtual void comment(const QString &/*value*/)
    {
    }

    virtual void startDocument()
    {
    }

    virtual void endDocument()
    {
    }

    virtual void processingInstruction(const QXmlName &/*target*/, const QString &/*value*/)
    {
    }

    virtual void atomicValue(const QVariant &/*value*/)
    {
    }
    virtual void namespaceBinding(const QXmlName &/*name*/)
    {
    }
    virtual void startOfSequence()
    {
    }
    virtual void endOfSequence()
    {
    }

private:
    QXmlNamePool m_namePool;
    QStack<QString> m_currentElementStack;
    QList<MerTargetData> m_targets;
    MerTargetData m_currentTarget;
    QString m_attributeValue;
    int m_version;
};

#ifdef Q_OS_MAC
#  define SHARE_PATH "/../Resources"
#else
#  define SHARE_PATH "/../share/qtcreator"
#endif

static QString applicationDirPath()
{
    return QCoreApplication::applicationDirPath();
}

static inline QString sharedDirPath()
{
    QString appPath = applicationDirPath();

    return QFileInfo(appPath + QLatin1String(SHARE_PATH)).absoluteFilePath();
}

} // Anonymous

class MerTargetsXmlReaderPrivate
{
public:
    MerTargetsXmlReaderPrivate()
        : receiver(new XmlReceiver(query.namePool())),
          error(false)
    {}

    ~MerTargetsXmlReaderPrivate()
    {
        delete receiver;
    }

    QXmlQuery query;
    XmlReceiver *receiver;
    MessageHandler messageHandler;
    bool error;
    QString errorString;
};

MerTargetsXmlReader::MerTargetsXmlReader(const QString &fileName, QObject *parent)
    : QObject(parent),
      d(new MerTargetsXmlReaderPrivate)
{
    Utils::FileReader reader;
    d->error = !reader.fetch(fileName, QIODevice::ReadOnly);
    if (d->error) {
        d->errorString = reader.errorString();
        return;
    }

    QXmlSchema schema;
    schema.setMessageHandler(&d->messageHandler);

    Utils::FileReader schemeReader;
    d->error = !schemeReader.fetch(QString::fromLatin1("%1/mer/targets.xsd").arg(sharedDirPath()),
            QIODevice::ReadOnly);
    if (d->error) {
        d->errorString = schemeReader.errorString();
        return;
    }
    schema.load(schemeReader.data());
    d->error = !schema.isValid();
    if (d->error) {
        d->errorString = d->messageHandler.errorString();
        return;
    }

    QXmlSchemaValidator validator(schema);
    validator.setMessageHandler(&d->messageHandler);
    d->error = !validator.validate(reader.data());
    if (d->error) {
        d->errorString = d->messageHandler.errorString();
        return;
    }

    d->query.setQuery(QString::fromLatin1("doc('%1')").arg(fileName));
    d->query.setMessageHandler(&d->messageHandler);
    d->error = !d->query.isValid();
    if (d->error) {
        d->errorString = d->messageHandler.errorString();
        return;
    }

    d->error = !d->query.evaluateTo(d->receiver);
    if (d->error)
        d->errorString = d->messageHandler.errorString();
}

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

bool MerTargetsXmlReader::hasError() const
{
    return d->error;
}

QString MerTargetsXmlReader::errorString() const
{
    return d->errorString;
}

QList<MerTargetData> MerTargetsXmlReader::targetData() const
{
    return d->receiver->targetData();
}

int MerTargetsXmlReader::version() const
{
    return d->receiver->version();
}

void MerTargetData::clear()
{
    name.clear();
    gccDumpMachine.clear();
    qmakeQuery.clear();
}

class MerTargetsXmlWriterPrivate
{
public:
    MerTargetsXmlWriterPrivate(const QString &fileName) : fileSaver(fileName, QIODevice::WriteOnly) {}

    Utils::FileSaver fileSaver;
};

MerTargetsXmlWriter::MerTargetsXmlWriter(const QString &fileName, int version,
                                         const QList<MerTargetData> &targetData, QObject *parent)
    : QObject(parent),
      d(new MerTargetsXmlWriterPrivate(fileName))
{
    QByteArray data;
    QXmlStreamWriter writer(&data);
    writer.setAutoFormatting(true);
    writer.writeStartDocument();
    writer.writeStartElement(QLatin1String(TARGETS));
    writer.writeAttribute(QLatin1String(VERSION), QString::number(version));
    foreach (const MerTargetData &data, targetData) {
        writer.writeStartElement(QLatin1String(TARGET));
        writer.writeAttribute(QLatin1String(NAME), data.name);
        writer.writeStartElement(QLatin1String(OUTPUT));
        writer.writeAttribute(QLatin1String(NAME), QLatin1String(GCCDUMPMACHINE));
        writer.writeCharacters(data.gccDumpMachine);
        writer.writeEndElement(); // output
        writer.writeStartElement(QLatin1String(OUTPUT));
        writer.writeAttribute(QLatin1String(NAME), QLatin1String(QMAKEQUERY));
        writer.writeCharacters(data.qmakeQuery);
        writer.writeEndElement(); // output
        writer.writeEndElement(); // target
    }
    writer.writeEndElement(); // targets
    writer.writeEndDocument();
    d->fileSaver.write(data);
    d->fileSaver.finalize();
}

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

bool MerTargetsXmlWriter::hasError() const
{
    return d->fileSaver.hasError();
}

QString MerTargetsXmlWriter::errorString() const
{
    return d->fileSaver.errorString();
}

} // Mer
<|endoftext|>