<commit_before>/*****************************************************************************
 * Copyright (C) 2011-2016 Michael Ira Krufky
 *
 * Author: Michael Ira Krufky <mkrufky@linuxtv.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#include <inttypes.h>
#include "string.h"

#include "stats.h"
#include "log.h"
#define CLASS_MODULE "stats"

#define DBG 0

#define dPrintf(fmt, arg...) __dPrintf(DBG_STATS, "(%s) " fmt, parent, ##arg)

stats::stats(const char *caller)
  : tei_count(0)
  , __timenow(0)
  , parent(caller)
  , streamtime_cb(NULL)
  , streamtime_priv(NULL)
  , statistics_cb(NULL)
  , statistics_priv(NULL)
  , statistics_iface(NULL)
{
	dPrintf("(%s)", parent);
}

stats::~stats()
{
	dPrintf("(%s)", parent);
	show(false);
}

#if 0
stats::stats(const stats&)
{
	dPrintf("(%s, copy)", parent);
}

stats& stats::operator= (const stats& cSource)
{
	dPrintf("(%s, operator=)", parent);

	if (this == &cSource)
		return *this;

	return *this;
}
#endif

static void parse_pcr(uint8_t *pcr, uint64_t *pcr_base, unsigned int *pcr_ext)
{
	*pcr_base = ((uint64_t)pcr[0] * 0x2000000) +
		    ((uint64_t)pcr[1] * 0x20000) +
		    ((uint64_t)pcr[2] * 0x200) +
		    ((uint64_t)pcr[3] * 0x02) +
		    ((uint64_t)(pcr[4] & 0x80) >> 7);

	*pcr_ext = ((pcr[4] & 0x01) * 0x100) + pcr[5];

	return;
}

static time_t walltime(void *p) { (void)p; return time(NULL); }

char *stats_scale_unit(char *b, size_t n, uint64_t x)
{
	memset(b, 0, n);

	if (x >= 1000000) {
#if 1
		if ((x % 1000000) < 1000)
			snprintf(b, n, "%3" PRIu64 ".%03" PRIu64 " m", x / 1000000, x % 1000000);
		else
#endif
			snprintf(b, n, "%3" PRIu64 ".%03" PRIu64 " m", x / 1000000, x % 1000);
	} else if (x >= 1000) {
#if 0
		if ((x % 1000) < 100)
			snprintf(b, n, "%3" PRIu64 ".%03" PRIu64 " k", x / 1000, x % 1000);
		else
#endif
			snprintf(b, n, "%3" PRIu64 ".%03" PRIu64 " k", x / 1000, x % 1000);
	} else
		snprintf(b, n, "    %3" PRIu64 "  ", x);
	return b;
}

void stats::show(bool per_sec)
{
	if (statistics_cb) {
		statistics_cb(statistics_priv, statistics, discontinuities, tei_count, per_sec);
	}
	if (statistics_iface) {
		statistics_iface->stats(statistics, discontinuities, tei_count, per_sec);
	}
	if (statistics_cb || statistics_iface) {
		return;
	}
	for (stats_map::const_iterator iter = statistics.begin(); iter != statistics.end(); ++iter) {
		char a[16];
		char b[16];
		if (dbg & DBG_STATS) __log_printf(stderr, "pid %04x %5" PRIu64 " p%s  %sb%s  %sbit\n",
			     iter->first, iter->second / 188, (per_sec) ? "/s" : "",
			     stats_scale_unit(a, sizeof(a), iter->second), (per_sec) ? "/s" : "",
			     stats_scale_unit(b, sizeof(b), iter->second * 8));
	}
	for (stats_map::const_iterator iter = discontinuities.begin(); iter != discontinuities.end(); ++iter)
		if (dbg & DBG_STATS) __log_printf(stderr, "pid %04x\t%" PRIu64 " continuity errors (%" PRIu64 "%%)\n", iter->first, iter->second, ((!iter->second) || (!statistics[iter->first])) ? 0 : (!statistics.count(iter->first)) ? 0 : (100 * iter->second / (statistics[iter->first] / 188)));

	if ((tei_count) && (dbg & DBG_STATS)) __log_printf(stderr, "tei count: %" PRIu64 " (%" PRIu64 "%%)\n", tei_count, (!statistics[0x2000]) ? 0 : (18800 * tei_count / statistics[0x2000]));
}

void stats::push_pid(int c, const uint16_t pid)
{
	streamtime_callback cb = (streamtime_cb) ? streamtime_cb : &walltime;
	time_t timenow = cb(streamtime_priv);

	if (timenow > __timenow) {
		show();
		clear_stats();
		__timenow = timenow;
	}

	__push_pid(c, pid);
}

pkt_stats_t *stats::parse(const uint8_t *p, pkt_stats_t *pkt_stats)
{
	adaptation_field_t adapt;
	pkt_hdr_t hdr;

	return parse(p, pkt_stats, hdr, adapt);
}

pkt_stats_t *stats::parse(const uint8_t *p, pkt_stats_t *pkt_stats, pkt_hdr_t &hdr, adaptation_field_t &adapt)
{
	if (pkt_stats) {
		memset(pkt_stats, 0, sizeof(pkt_stats_t));

		const uint8_t *q = p;

		memset(&adapt, 0, sizeof(adapt));
		memset(&hdr, 0, sizeof(hdr));

		hdr.sync_byte          = q[0];
		hdr.tei                = (q[1] & 0x80) >> 7;
		hdr.payload_unit_start = (q[1] & 0x40) >> 6;
		hdr.transport_priority = (q[1] & 0x20) >> 5;
		hdr.pid                = ((q[1] & 0x1f) << 8) | q[2];
		hdr.scrambling         = (q[3] & 0xc0) >> 6;
		hdr.adaptation_flags   = (q[3] & 0x30) >> 4;
		hdr.continuity_ctr     = (q[3] & 0x0f);

		if (hdr.adaptation_flags & 0x02) {
			q += 4;
			adapt.field_length   = q[0];
			adapt.discontinuity  = (q[1] & 0x80) >> 7;
			adapt.random_access  = (q[1] & 0x40) >> 6; /* set to 1 if the PES pkt in this TS pkt starts an a/v sequence */
			adapt.es_priority    = (q[1] & 0x20) >> 5;
			adapt.pcr            = (q[1] & 0x10) >> 4;
			adapt.opcr           = (q[1] & 0x08) >> 3;
			adapt.splicing_point = (q[1] & 0x04) >> 2;
			adapt.tp_priv_data   = (q[1] & 0x02) >> 1;
			adapt.field_ext      = (q[1] & 0x01) >> 0;

			if (adapt.pcr) {
				memcpy(adapt.PCR, &q[2], 6);
				q += 6;
			}
			if (adapt.opcr) {
				memcpy(adapt.OPCR, &q[2], 6);
				q += 6;
			}
			if (adapt.splicing_point) {
				adapt.splicing_countdown = q[2];
				q ++;
			}
		}

		pkt_stats->sync_loss = (hdr.sync_byte != 0x47) ? true : false;
		if (!pkt_stats->sync_loss) {
			pkt_stats->tei = (hdr.tei) ? true : false;
			pkt_stats->pid = hdr.pid;
		} else
			pkt_stats->pid = (uint16_t) - 1;
	}
	return pkt_stats;
}

void stats::clear_stats()
{
	statistics.clear();
	discontinuities.clear();
	last_pcr_base.clear();
	tei_count = 0;
}

void stats::push_stats(pkt_stats_t *pkt_stats)
{
	if (pkt_stats->tei) {
		tei_count++;
		push_pid((uint16_t) - 1);
	} else
		push_pid(pkt_stats->pid);
}

void stats::push(const uint8_t *p, pkt_stats_t *pkt_stats)
{
	adaptation_field_t adapt;
	pkt_hdr_t hdr;

	if (!pkt_stats) {
		__push(p);
		return;
	}

	memset(pkt_stats, 0, sizeof(pkt_stats_t));

	parse(p, pkt_stats, hdr, adapt);

	if (hdr.adaptation_flags & 0x01) {// payload present
		if (continuity.count(hdr.pid)) {
			uint8_t next = (continuity[hdr.pid] + 1) & 0x0f;
			if ((next != (hdr.continuity_ctr & 0x0f)) && (hdr.continuity_ctr + continuity[hdr.pid] > 0)) {
				if (!(hdr.adaptation_flags & 0x02) && (adapt.discontinuity)) {
					push_discontinuity(hdr.pid);
#if DBG
					dPrintf("CONTINUITY ERROR pid: %04x cur: 0x%x prev 0x%x", hdr.pid, hdr.continuity_ctr, continuity[hdr.pid]);
#endif
				}
			}
		}
		continuity[hdr.pid] = hdr.continuity_ctr;
	}

	if (hdr.adaptation_flags & 0x02) {
		if (adapt.pcr) {
			uint64_t pcr_base;
			unsigned int pcr_ext;

			parse_pcr(adapt.PCR, &pcr_base, &pcr_ext);
			if (dbg & DBG_STATS) __log_printf(stderr, "PID: 0x%04x, PCR base: %" PRIu64 ", ext: %d\n", hdr.pid, pcr_base, pcr_ext);

#if DBG
			stats_map::const_iterator iter = last_pcr_base.find(hdr.pid);
			if ((iter != last_pcr_base.end()) && (pcr_base < iter->second))
				__log_printf(stderr, "%s: PID: 0x%04x, %" PRIu64 " < %" PRIu64 " !!!\n",
					__func__, hdr.pid, pcr_base, iter->second);
#endif
			last_pcr_base[hdr.pid] = pcr_base;
		}
		if (adapt.opcr) {
			uint64_t pcr_base;
			unsigned int pcr_ext;

			parse_pcr(adapt.OPCR, &pcr_base, &pcr_ext);
			if (dbg & DBG_STATS) __log_printf(stderr, "PID: 0x%04x, PCR base: %" PRIu64 ", ext: %d\n", hdr.pid, pcr_base, pcr_ext);
		}
		if (adapt.splicing_point) {
			dPrintf("PID: 0x%04x, splicing countdown: %d", hdr.pid, adapt.splicing_countdown);
		}
	}

	push_stats(pkt_stats);
}
<commit_msg>stats: differentiate between PCR & OPCR in (DBG_STATS|DBG_TIME) logs<commit_after>/*****************************************************************************
 * Copyright (C) 2011-2016 Michael Ira Krufky
 *
 * Author: Michael Ira Krufky <mkrufky@linuxtv.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#include <inttypes.h>
#include "string.h"

#include "stats.h"
#include "log.h"
#define CLASS_MODULE "stats"

#define DBG 0

#define dPrintf(fmt, arg...) __dPrintf(DBG_STATS, "(%s) " fmt, parent, ##arg)

#define pcr_printf(fd, fmt, arg...) if (dbg & (DBG_STATS | DBG_TIME)) __log_printf(fd, fmt, ##arg)

stats::stats(const char *caller)
  : tei_count(0)
  , __timenow(0)
  , parent(caller)
  , streamtime_cb(NULL)
  , streamtime_priv(NULL)
  , statistics_cb(NULL)
  , statistics_priv(NULL)
  , statistics_iface(NULL)
{
	dPrintf("(%s)", parent);
}

stats::~stats()
{
	dPrintf("(%s)", parent);
	show(false);
}

#if 0
stats::stats(const stats&)
{
	dPrintf("(%s, copy)", parent);
}

stats& stats::operator= (const stats& cSource)
{
	dPrintf("(%s, operator=)", parent);

	if (this == &cSource)
		return *this;

	return *this;
}
#endif

static void parse_pcr(uint8_t *pcr, uint64_t *pcr_base, unsigned int *pcr_ext)
{
	*pcr_base = ((uint64_t)pcr[0] * 0x2000000) +
		    ((uint64_t)pcr[1] * 0x20000) +
		    ((uint64_t)pcr[2] * 0x200) +
		    ((uint64_t)pcr[3] * 0x02) +
		    ((uint64_t)(pcr[4] & 0x80) >> 7);

	*pcr_ext = ((pcr[4] & 0x01) * 0x100) + pcr[5];

	return;
}

static time_t walltime(void *p) { (void)p; return time(NULL); }

char *stats_scale_unit(char *b, size_t n, uint64_t x)
{
	memset(b, 0, n);

	if (x >= 1000000) {
#if 1
		if ((x % 1000000) < 1000)
			snprintf(b, n, "%3" PRIu64 ".%03" PRIu64 " m", x / 1000000, x % 1000000);
		else
#endif
			snprintf(b, n, "%3" PRIu64 ".%03" PRIu64 " m", x / 1000000, x % 1000);
	} else if (x >= 1000) {
#if 0
		if ((x % 1000) < 100)
			snprintf(b, n, "%3" PRIu64 ".%03" PRIu64 " k", x / 1000, x % 1000);
		else
#endif
			snprintf(b, n, "%3" PRIu64 ".%03" PRIu64 " k", x / 1000, x % 1000);
	} else
		snprintf(b, n, "    %3" PRIu64 "  ", x);
	return b;
}

void stats::show(bool per_sec)
{
	if (statistics_cb) {
		statistics_cb(statistics_priv, statistics, discontinuities, tei_count, per_sec);
	}
	if (statistics_iface) {
		statistics_iface->stats(statistics, discontinuities, tei_count, per_sec);
	}
	if (statistics_cb || statistics_iface) {
		return;
	}
	for (stats_map::const_iterator iter = statistics.begin(); iter != statistics.end(); ++iter) {
		char a[16];
		char b[16];
		if (dbg & DBG_STATS) __log_printf(stderr, "pid %04x %5" PRIu64 " p%s  %sb%s  %sbit\n",
			     iter->first, iter->second / 188, (per_sec) ? "/s" : "",
			     stats_scale_unit(a, sizeof(a), iter->second), (per_sec) ? "/s" : "",
			     stats_scale_unit(b, sizeof(b), iter->second * 8));
	}
	for (stats_map::const_iterator iter = discontinuities.begin(); iter != discontinuities.end(); ++iter)
		if (dbg & DBG_STATS) __log_printf(stderr, "pid %04x\t%" PRIu64 " continuity errors (%" PRIu64 "%%)\n", iter->first, iter->second, ((!iter->second) || (!statistics[iter->first])) ? 0 : (!statistics.count(iter->first)) ? 0 : (100 * iter->second / (statistics[iter->first] / 188)));

	if ((tei_count) && (dbg & DBG_STATS)) __log_printf(stderr, "tei count: %" PRIu64 " (%" PRIu64 "%%)\n", tei_count, (!statistics[0x2000]) ? 0 : (18800 * tei_count / statistics[0x2000]));
}

void stats::push_pid(int c, const uint16_t pid)
{
	streamtime_callback cb = (streamtime_cb) ? streamtime_cb : &walltime;
	time_t timenow = cb(streamtime_priv);

	if (timenow > __timenow) {
		show();
		clear_stats();
		__timenow = timenow;
	}

	__push_pid(c, pid);
}

pkt_stats_t *stats::parse(const uint8_t *p, pkt_stats_t *pkt_stats)
{
	adaptation_field_t adapt;
	pkt_hdr_t hdr;

	return parse(p, pkt_stats, hdr, adapt);
}

pkt_stats_t *stats::parse(const uint8_t *p, pkt_stats_t *pkt_stats, pkt_hdr_t &hdr, adaptation_field_t &adapt)
{
	if (pkt_stats) {
		memset(pkt_stats, 0, sizeof(pkt_stats_t));

		const uint8_t *q = p;

		memset(&adapt, 0, sizeof(adapt));
		memset(&hdr, 0, sizeof(hdr));

		hdr.sync_byte          = q[0];
		hdr.tei                = (q[1] & 0x80) >> 7;
		hdr.payload_unit_start = (q[1] & 0x40) >> 6;
		hdr.transport_priority = (q[1] & 0x20) >> 5;
		hdr.pid                = ((q[1] & 0x1f) << 8) | q[2];
		hdr.scrambling         = (q[3] & 0xc0) >> 6;
		hdr.adaptation_flags   = (q[3] & 0x30) >> 4;
		hdr.continuity_ctr     = (q[3] & 0x0f);

		if (hdr.adaptation_flags & 0x02) {
			q += 4;
			adapt.field_length   = q[0];
			adapt.discontinuity  = (q[1] & 0x80) >> 7;
			adapt.random_access  = (q[1] & 0x40) >> 6; /* set to 1 if the PES pkt in this TS pkt starts an a/v sequence */
			adapt.es_priority    = (q[1] & 0x20) >> 5;
			adapt.pcr            = (q[1] & 0x10) >> 4;
			adapt.opcr           = (q[1] & 0x08) >> 3;
			adapt.splicing_point = (q[1] & 0x04) >> 2;
			adapt.tp_priv_data   = (q[1] & 0x02) >> 1;
			adapt.field_ext      = (q[1] & 0x01) >> 0;

			if (adapt.pcr) {
				memcpy(adapt.PCR, &q[2], 6);
				q += 6;
			}
			if (adapt.opcr) {
				memcpy(adapt.OPCR, &q[2], 6);
				q += 6;
			}
			if (adapt.splicing_point) {
				adapt.splicing_countdown = q[2];
				q ++;
			}
		}

		pkt_stats->sync_loss = (hdr.sync_byte != 0x47) ? true : false;
		if (!pkt_stats->sync_loss) {
			pkt_stats->tei = (hdr.tei) ? true : false;
			pkt_stats->pid = hdr.pid;
		} else
			pkt_stats->pid = (uint16_t) - 1;
	}
	return pkt_stats;
}

void stats::clear_stats()
{
	statistics.clear();
	discontinuities.clear();
	last_pcr_base.clear();
	tei_count = 0;
}

void stats::push_stats(pkt_stats_t *pkt_stats)
{
	if (pkt_stats->tei) {
		tei_count++;
		push_pid((uint16_t) - 1);
	} else
		push_pid(pkt_stats->pid);
}

void stats::push(const uint8_t *p, pkt_stats_t *pkt_stats)
{
	adaptation_field_t adapt;
	pkt_hdr_t hdr;

	if (!pkt_stats) {
		__push(p);
		return;
	}

	memset(pkt_stats, 0, sizeof(pkt_stats_t));

	parse(p, pkt_stats, hdr, adapt);

	if (hdr.adaptation_flags & 0x01) {// payload present
		if (continuity.count(hdr.pid)) {
			uint8_t next = (continuity[hdr.pid] + 1) & 0x0f;
			if ((next != (hdr.continuity_ctr & 0x0f)) && (hdr.continuity_ctr + continuity[hdr.pid] > 0)) {
				if (!(hdr.adaptation_flags & 0x02) && (adapt.discontinuity)) {
					push_discontinuity(hdr.pid);
#if DBG
					dPrintf("CONTINUITY ERROR pid: %04x cur: 0x%x prev 0x%x", hdr.pid, hdr.continuity_ctr, continuity[hdr.pid]);
#endif
				}
			}
		}
		continuity[hdr.pid] = hdr.continuity_ctr;
	}

	if (hdr.adaptation_flags & 0x02) {
		if (adapt.pcr) {
			uint64_t pcr_base;
			unsigned int pcr_ext;

			parse_pcr(adapt.PCR, &pcr_base, &pcr_ext);
			pcr_printf(stderr, " PCR: PID: 0x%04x, PCR base: %" PRIu64 ", ext: %d\n", hdr.pid, pcr_base, pcr_ext);

#if DBG
			stats_map::const_iterator iter = last_pcr_base.find(hdr.pid);
			if ((iter != last_pcr_base.end()) && (pcr_base < iter->second))
				__log_printf(stderr, "%s: PID: 0x%04x, %" PRIu64 " < %" PRIu64 " !!!\n",
					__func__, hdr.pid, pcr_base, iter->second);
#endif
			last_pcr_base[hdr.pid] = pcr_base;
		}
		if (adapt.opcr) {
			uint64_t pcr_base;
			unsigned int pcr_ext;

			parse_pcr(adapt.OPCR, &pcr_base, &pcr_ext);
			pcr_printf(stderr, "OPCR: PID: 0x%04x, PCR base: %" PRIu64 ", ext: %d\n", hdr.pid, pcr_base, pcr_ext);
		}
		if (adapt.splicing_point) {
			dPrintf("PID: 0x%04x, splicing countdown: %d", hdr.pid, adapt.splicing_countdown);
		}
	}

	push_stats(pkt_stats);
}
<|endoftext|>


<commit_before>// This file is a part of the IncludeOS unikernel - www.includeos.org
//
// Copyright 2017 Oslo and Akershus University College of Applied Sciences
// and Alfred Bratterud
//
// 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 <net/conntrack.hpp>
#include <set>

//#define CT_DEBUG 1
#ifdef CT_DEBUG
#define CTDBG(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
#define CTDBG(fmt, ...) /* fmt */
#endif

namespace net {

std::string proto_str(const Protocol proto)
{
  switch(proto) {
    case Protocol::TCP: return "TCP";
    case Protocol::UDP: return "UDP";
    case Protocol::ICMPv4: return "ICMPv4";
    default: return "???";
  }
}

std::string state_str(const Conntrack::State state)
{
  switch(state) {
    case Conntrack::State::NEW: return "NEW";
    case Conntrack::State::ESTABLISHED: return "EST";
    case Conntrack::State::RELATED: return "RELATED";
    case Conntrack::State::UNCONFIRMED: return "UNCONFIRMED";
    default: return "???";
  }
}

std::string flag_str(const uint8_t flags)
{
  std::string str;
  if(flags & static_cast<uint8_t>(Conntrack::Flag::UNREPLIED))
    str.append(" UNREPLIED");
  if(flags & static_cast<uint8_t>(Conntrack::Flag::ASSURED))
    str.append(" ASSURED");
  return str;
}

std::string Conntrack::Entry::to_string() const
{
  return "[ " + first.to_string() + " ] [ " + second.to_string() + " ]"
    + " P: " + proto_str(proto) + " S: " + state_str(state) + " F:" + flag_str(flags);
}

Conntrack::Entry::~Entry()
{
  if(this->on_close)
    on_close(this);
}

Conntrack::Entry* Conntrack::simple_track_in(Quadruple q, const Protocol proto)
{
  // find the entry
  auto* entry = get(q, proto);

  CTDBG("<Conntrack> Track in S: %s - D: %s\n",
    q.src.to_string().c_str(), q.dst.to_string().c_str());

  // if none, add new and return
  if(entry == nullptr)
  {
    entry = add_entry(q, proto);
    return entry;
  }

  // temp
  CTDBG("<Conntrack> Entry found: %s\n", entry->to_string().c_str());

  if(entry->state == State::NEW and q == entry->second)
  {
    entry->state = State::ESTABLISHED;
    CTDBG("<Conntrack> Assuming ESTABLISHED\n");
  }

  update_timeout(*entry, (entry->state == State::ESTABLISHED) ? timeout.established : timeout.confirmed);

  return entry;
}

Conntrack::Entry* dumb_in(Conntrack& ct, Quadruple q, const PacketIP4& pkt)
{ return  ct.simple_track_in(std::move(q), pkt.ip_protocol()); }

Conntrack::Entry* dumb6_in(Conntrack& ct, Quadruple q, const PacketIP6& pkt)
{ return  ct.simple_track_in(std::move(q), pkt.ip_protocol()); }

Conntrack::Conntrack()
 : Conntrack(0)
{}

Conntrack::Conntrack(size_t max_entries)
 : maximum_entries{max_entries},
   tcp_in{&dumb_in},
   tcp6_in{&dumb6_in},
   flush_timer({this, &Conntrack::on_timeout})
{
}

Conntrack::Entry* Conntrack::get(const PacketIP4& pkt) const
{
  const auto proto = pkt.ip_protocol();
  switch(proto)
  {
    case Protocol::TCP:
    case Protocol::UDP:
      return get(get_quadruple(pkt), proto);

    case Protocol::ICMPv4:
      return get(get_quadruple_icmp(pkt), proto);

    default:
      return nullptr;
  }
}

Conntrack::Entry* Conntrack::get(const PacketIP6& pkt) const
{
  const auto proto = pkt.ip_protocol();
  switch(proto)
  {
    case Protocol::TCP:
    case Protocol::UDP:
      return get(get_quadruple(pkt), proto);

    case Protocol::ICMPv6:
      return get(get_quadruple_icmp(pkt), proto);

    default:
      return nullptr;
  }
}

Conntrack::Entry* Conntrack::get(const Quadruple& quad, const Protocol proto) const
{
  auto it = entries.find({quad, proto});

  if(it != entries.end())
    return it->second.get();

  return nullptr;
}

Conntrack::Entry* Conntrack::in(const PacketIP4& pkt)
{
  const auto proto = pkt.ip_protocol();
  switch(proto)
  {
    case Protocol::TCP:
      return tcp_in(*this, get_quadruple(pkt), pkt);

    case Protocol::UDP:
      return simple_track_in(get_quadruple(pkt), proto);

    case Protocol::ICMPv4:
      return simple_track_in(get_quadruple_icmp(pkt), proto);

    default:
      return nullptr;
  }
}

Conntrack::Entry* Conntrack::in(const PacketIP6& pkt)
{
  const auto proto = pkt.ip_protocol();
  switch(proto)
  {
    case Protocol::TCP:
      return tcp6_in(*this, get_quadruple(pkt), pkt);

    case Protocol::UDP:
      return simple_track_in(get_quadruple(pkt), proto);

    case Protocol::ICMPv6:
      return simple_track_in(get_quadruple_icmp(pkt), proto);

    default:
      return nullptr;
  }
}

Conntrack::Entry* Conntrack::confirm(const PacketIP4& pkt)
{
  const auto proto = pkt.ip_protocol();

  auto quad = [&]()->Quadruple {
    switch(proto)
    {
      case Protocol::TCP:
      case Protocol::UDP:
        return get_quadruple(pkt);

      case Protocol::ICMPv4:
        return get_quadruple_icmp(pkt);

      default:
        return Quadruple();
    }
  }();

  return confirm(quad, proto);
}

Conntrack::Entry* Conntrack::confirm(const PacketIP6& pkt)
{
  const auto proto = pkt.ip_protocol();

  auto quad = [&]()->Quadruple {
    switch(proto)
    {
      case Protocol::TCP:
      case Protocol::UDP:
        return get_quadruple(pkt);

      case Protocol::ICMPv6:
        return get_quadruple_icmp(pkt);

      default:
        return Quadruple();
    }
  }();

  return confirm(quad, proto);
}

Conntrack::Entry* Conntrack::confirm(Quadruple quad, const Protocol proto)
{
  auto* entry = get(quad, proto);

  if(UNLIKELY(entry == nullptr)) {
    CTDBG("<Conntrack> Entry not found on confirm, checking swapped: %s\n",
      quad.to_string().c_str());
    // the packet my be NATed. note: not sure if this is good
    if(UNLIKELY((entry = get(quad.swap(), proto)) == nullptr)) {
      return nullptr;
    }
  }

  if(entry->state == State::UNCONFIRMED)
  {
    CTDBG("<Conntrack> Confirming %s\n", entry->to_string().c_str());
    entry->state = State::NEW;
    update_timeout(*entry, timeout.confirmed);
  }

  return entry;
}

Conntrack::Entry* Conntrack::add_entry(
  const Quadruple& quad, const Protocol proto)
{
  // Return nullptr if conntrack is full
  if(UNLIKELY(maximum_entries != 0 and
    entries.size() + 2 > maximum_entries))
  {
    CTDBG("<Conntrack> Limit reached (limit=%lu sz=%lu)\n",
      maximum_entries, entries.size());
    return nullptr;
  }

  if(not flush_timer.is_running())
    flush_timer.start(flush_interval);

  // we dont check if it's already exists
  // because it should be called from in()

  // create the entry
  auto entry = std::make_shared<Entry>(quad, proto);

  entries.emplace(std::piecewise_construct,
    std::forward_as_tuple(entry->first, proto),
    std::forward_as_tuple(entry));

  entries.emplace(std::piecewise_construct,
    std::forward_as_tuple(entry->second, proto),
    std::forward_as_tuple(entry));

  CTDBG("<Conntrack> Entry added: %s\n", entry->to_string().c_str());

  update_timeout(*entry, timeout.unconfirmed);

  return entry.get();
}

Conntrack::Entry* Conntrack::update_entry(
  const Protocol proto, const Quadruple& oldq, const Quadruple& newq)
{
  // find the entry that has quintuple containing the old quant
  const auto quint = Quintuple{oldq, proto};
  auto it = entries.find(quint);

  if(UNLIKELY(it == entries.end())) {
    CTDBG("<Conntrack> Cannot find entry when updating: %s\n",
      oldq.to_string().c_str());
    return nullptr;
  }

  auto entry = it->second;

  // determine if the old quant hits the first or second quantuple
  auto& quad = (entry->first == oldq)
    ? entry->first : entry->second;

  // give it a new value
  quad = newq;

  // replace this ...
  // erase the old entry
  entries.erase(quint);
  // insert the entry with updated quintuple
  entries.emplace(std::piecewise_construct,
    std::forward_as_tuple(newq, proto),
    std::forward_as_tuple(entry));

  // ... with this (when compile on clang)
  /*
  // update the key in the map with the new quadruple
  auto ent = entries.extract(it);
  ent.key().quad = newq;
  entries.insert(std::move(ent));
  */

  CTDBG("<Conntrack> Entry updated: %s\n", entry->to_string().c_str());

  return entry.get();
}

void Conntrack::remove_expired()
{
  CTDBG("<Conntrack> Removing expired entries\n");
  const auto NOW = RTC::now();
  // entries data structure
  for(auto it = entries.begin(); it != entries.end();)
  {
    if(it->second->timeout > NOW) {
      ++it;
    }
    else {
      CTDBG("<Conntrack> Erasing %s\n", it->second->to_string().c_str());
      it = entries.erase(it);
    }
  }
}

void Conntrack::on_timeout()
{
  remove_expired();

  if(not entries.empty())
    flush_timer.restart(flush_interval);
}

int Conntrack::Entry::deserialize_from(void* addr)
{
  auto& entry = *reinterpret_cast<Entry*>(addr);
  this->first   = entry.first;
  this->second  = entry.second;
  this->timeout = entry.timeout;
  this->proto   = entry.proto;
  this->state   = entry.state;
  this->flags   = entry.flags;
  this->other   = entry.other;
  return sizeof(Entry) - sizeof(on_close);
}

void Conntrack::Entry::serialize_to(std::vector<char>& buf) const
{
  const size_t size = sizeof(Entry) - sizeof(on_close);
  const auto* ptr = reinterpret_cast<const char*>(this);
  buf.insert(buf.end(), ptr, ptr + size);
}

int Conntrack::deserialize_from(void* addr)
{
  const auto prev_size = entries.size();
  auto* buffer = reinterpret_cast<uint8_t*>(addr);

  const auto size = *reinterpret_cast<size_t*>(buffer);
  buffer += sizeof(size_t);

  for(auto i = size; i > 0; i--)
  {
    // create the entry
    auto entry = std::make_shared<Entry>();
    buffer += entry->deserialize_from(buffer);

    entries.emplace(std::piecewise_construct,
      std::forward_as_tuple(entry->first, entry->proto),
      std::forward_as_tuple(entry));

    entries.emplace(std::piecewise_construct,
      std::forward_as_tuple(entry->second, entry->proto),
      std::forward_as_tuple(entry));
  }

  Ensures(entries.size() - prev_size == size * 2);

  return buffer - reinterpret_cast<uint8_t*>(addr);
}

void Conntrack::serialize_to(std::vector<char>& buf) const
{
  int unserialized = 0;

  // Since each entry is stored twice in the map,
  // we iterate and put it in a set if not already there
  std::set<Entry*> to_serialize;
  for(auto& i : entries)
  {
    auto* ent = i.second.get();

    // We cannot restore delegates, so just ignore
    // the ones with close handler set
    if(ent->on_close != nullptr) {
      unserialized++;
      continue;
    }
    // If not in set, add
    if(to_serialize.find(ent) == to_serialize.end())
      to_serialize.emplace(ent);
  }

  // Serialize number of entries
  size_t size = to_serialize.size();
  const auto* size_ptr = reinterpret_cast<const char*>(&size);

  const auto expected_buf_size = sizeof(size) + (size * (sizeof(Entry) - sizeof(Entry_handler)));
  buf.reserve(expected_buf_size);

  buf.insert(buf.end(), size_ptr, size_ptr + sizeof(size));
  // Serialize each entry
  for(auto& ent : to_serialize)
    ent->serialize_to(buf);

  if(unserialized > 0)
    INFO("Conntrack", "%i entries not serialized\n", unserialized);
}

}
<commit_msg>net: Update Conntrack entry if already exists when deserializing<commit_after>// This file is a part of the IncludeOS unikernel - www.includeos.org
//
// Copyright 2017 Oslo and Akershus University College of Applied Sciences
// and Alfred Bratterud
//
// 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 <net/conntrack.hpp>
#include <set>

//#define CT_DEBUG 1
#ifdef CT_DEBUG
#define CTDBG(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
#define CTDBG(fmt, ...) /* fmt */
#endif

namespace net {

std::string proto_str(const Protocol proto)
{
  switch(proto) {
    case Protocol::TCP: return "TCP";
    case Protocol::UDP: return "UDP";
    case Protocol::ICMPv4: return "ICMPv4";
    default: return "???";
  }
}

std::string state_str(const Conntrack::State state)
{
  switch(state) {
    case Conntrack::State::NEW: return "NEW";
    case Conntrack::State::ESTABLISHED: return "EST";
    case Conntrack::State::RELATED: return "RELATED";
    case Conntrack::State::UNCONFIRMED: return "UNCONFIRMED";
    default: return "???";
  }
}

std::string flag_str(const uint8_t flags)
{
  std::string str;
  if(flags & static_cast<uint8_t>(Conntrack::Flag::UNREPLIED))
    str.append(" UNREPLIED");
  if(flags & static_cast<uint8_t>(Conntrack::Flag::ASSURED))
    str.append(" ASSURED");
  return str;
}

std::string Conntrack::Entry::to_string() const
{
  return "[ " + first.to_string() + " ] [ " + second.to_string() + " ]"
    + " P: " + proto_str(proto) + " S: " + state_str(state) + " F:" + flag_str(flags);
}

Conntrack::Entry::~Entry()
{
  if(this->on_close)
    on_close(this);
}

Conntrack::Entry* Conntrack::simple_track_in(Quadruple q, const Protocol proto)
{
  // find the entry
  auto* entry = get(q, proto);

  CTDBG("<Conntrack> Track in S: %s - D: %s\n",
    q.src.to_string().c_str(), q.dst.to_string().c_str());

  // if none, add new and return
  if(entry == nullptr)
  {
    entry = add_entry(q, proto);
    return entry;
  }

  // temp
  CTDBG("<Conntrack> Entry found: %s\n", entry->to_string().c_str());

  if(entry->state == State::NEW and q == entry->second)
  {
    entry->state = State::ESTABLISHED;
    CTDBG("<Conntrack> Assuming ESTABLISHED\n");
  }

  update_timeout(*entry, (entry->state == State::ESTABLISHED) ? timeout.established : timeout.confirmed);

  return entry;
}

Conntrack::Entry* dumb_in(Conntrack& ct, Quadruple q, const PacketIP4& pkt)
{ return  ct.simple_track_in(std::move(q), pkt.ip_protocol()); }

Conntrack::Entry* dumb6_in(Conntrack& ct, Quadruple q, const PacketIP6& pkt)
{ return  ct.simple_track_in(std::move(q), pkt.ip_protocol()); }

Conntrack::Conntrack()
 : Conntrack(0)
{}

Conntrack::Conntrack(size_t max_entries)
 : maximum_entries{max_entries},
   tcp_in{&dumb_in},
   tcp6_in{&dumb6_in},
   flush_timer({this, &Conntrack::on_timeout})
{
}

Conntrack::Entry* Conntrack::get(const PacketIP4& pkt) const
{
  const auto proto = pkt.ip_protocol();
  switch(proto)
  {
    case Protocol::TCP:
    case Protocol::UDP:
      return get(get_quadruple(pkt), proto);

    case Protocol::ICMPv4:
      return get(get_quadruple_icmp(pkt), proto);

    default:
      return nullptr;
  }
}

Conntrack::Entry* Conntrack::get(const PacketIP6& pkt) const
{
  const auto proto = pkt.ip_protocol();
  switch(proto)
  {
    case Protocol::TCP:
    case Protocol::UDP:
      return get(get_quadruple(pkt), proto);

    case Protocol::ICMPv6:
      return get(get_quadruple_icmp(pkt), proto);

    default:
      return nullptr;
  }
}

Conntrack::Entry* Conntrack::get(const Quadruple& quad, const Protocol proto) const
{
  auto it = entries.find({quad, proto});

  if(it != entries.end())
    return it->second.get();

  return nullptr;
}

Conntrack::Entry* Conntrack::in(const PacketIP4& pkt)
{
  const auto proto = pkt.ip_protocol();
  switch(proto)
  {
    case Protocol::TCP:
      return tcp_in(*this, get_quadruple(pkt), pkt);

    case Protocol::UDP:
      return simple_track_in(get_quadruple(pkt), proto);

    case Protocol::ICMPv4:
      return simple_track_in(get_quadruple_icmp(pkt), proto);

    default:
      return nullptr;
  }
}

Conntrack::Entry* Conntrack::in(const PacketIP6& pkt)
{
  const auto proto = pkt.ip_protocol();
  switch(proto)
  {
    case Protocol::TCP:
      return tcp6_in(*this, get_quadruple(pkt), pkt);

    case Protocol::UDP:
      return simple_track_in(get_quadruple(pkt), proto);

    case Protocol::ICMPv6:
      return simple_track_in(get_quadruple_icmp(pkt), proto);

    default:
      return nullptr;
  }
}

Conntrack::Entry* Conntrack::confirm(const PacketIP4& pkt)
{
  const auto proto = pkt.ip_protocol();

  auto quad = [&]()->Quadruple {
    switch(proto)
    {
      case Protocol::TCP:
      case Protocol::UDP:
        return get_quadruple(pkt);

      case Protocol::ICMPv4:
        return get_quadruple_icmp(pkt);

      default:
        return Quadruple();
    }
  }();

  return confirm(quad, proto);
}

Conntrack::Entry* Conntrack::confirm(const PacketIP6& pkt)
{
  const auto proto = pkt.ip_protocol();

  auto quad = [&]()->Quadruple {
    switch(proto)
    {
      case Protocol::TCP:
      case Protocol::UDP:
        return get_quadruple(pkt);

      case Protocol::ICMPv6:
        return get_quadruple_icmp(pkt);

      default:
        return Quadruple();
    }
  }();

  return confirm(quad, proto);
}

Conntrack::Entry* Conntrack::confirm(Quadruple quad, const Protocol proto)
{
  auto* entry = get(quad, proto);

  if(UNLIKELY(entry == nullptr)) {
    CTDBG("<Conntrack> Entry not found on confirm, checking swapped: %s\n",
      quad.to_string().c_str());
    // the packet my be NATed. note: not sure if this is good
    if(UNLIKELY((entry = get(quad.swap(), proto)) == nullptr)) {
      return nullptr;
    }
  }

  if(entry->state == State::UNCONFIRMED)
  {
    CTDBG("<Conntrack> Confirming %s\n", entry->to_string().c_str());
    entry->state = State::NEW;
    update_timeout(*entry, timeout.confirmed);
  }

  return entry;
}

Conntrack::Entry* Conntrack::add_entry(
  const Quadruple& quad, const Protocol proto)
{
  // Return nullptr if conntrack is full
  if(UNLIKELY(maximum_entries != 0 and
    entries.size() + 2 > maximum_entries))
  {
    CTDBG("<Conntrack> Limit reached (limit=%lu sz=%lu)\n",
      maximum_entries, entries.size());
    return nullptr;
  }

  if(not flush_timer.is_running())
    flush_timer.start(flush_interval);

  // we dont check if it's already exists
  // because it should be called from in()

  // create the entry
  auto entry = std::make_shared<Entry>(quad, proto);

  entries.emplace(std::piecewise_construct,
    std::forward_as_tuple(entry->first, proto),
    std::forward_as_tuple(entry));

  entries.emplace(std::piecewise_construct,
    std::forward_as_tuple(entry->second, proto),
    std::forward_as_tuple(entry));

  CTDBG("<Conntrack> Entry added: %s\n", entry->to_string().c_str());

  update_timeout(*entry, timeout.unconfirmed);

  return entry.get();
}

Conntrack::Entry* Conntrack::update_entry(
  const Protocol proto, const Quadruple& oldq, const Quadruple& newq)
{
  // find the entry that has quintuple containing the old quant
  const auto quint = Quintuple{oldq, proto};
  auto it = entries.find(quint);

  if(UNLIKELY(it == entries.end())) {
    CTDBG("<Conntrack> Cannot find entry when updating: %s\n",
      oldq.to_string().c_str());
    return nullptr;
  }

  auto entry = it->second;

  // determine if the old quant hits the first or second quantuple
  auto& quad = (entry->first == oldq)
    ? entry->first : entry->second;

  // give it a new value
  quad = newq;

  // replace this ...
  // erase the old entry
  entries.erase(quint);
  // insert the entry with updated quintuple
  entries.emplace(std::piecewise_construct,
    std::forward_as_tuple(newq, proto),
    std::forward_as_tuple(entry));

  // ... with this (when compile on clang)
  /*
  // update the key in the map with the new quadruple
  auto ent = entries.extract(it);
  ent.key().quad = newq;
  entries.insert(std::move(ent));
  */

  CTDBG("<Conntrack> Entry updated: %s\n", entry->to_string().c_str());

  return entry.get();
}

void Conntrack::remove_expired()
{
  CTDBG("<Conntrack> Removing expired entries\n");
  const auto NOW = RTC::now();
  // entries data structure
  for(auto it = entries.begin(); it != entries.end();)
  {
    if(it->second->timeout > NOW) {
      ++it;
    }
    else {
      CTDBG("<Conntrack> Erasing %s\n", it->second->to_string().c_str());
      it = entries.erase(it);
    }
  }
}

void Conntrack::on_timeout()
{
  remove_expired();

  if(not entries.empty())
    flush_timer.restart(flush_interval);
}

int Conntrack::Entry::deserialize_from(void* addr)
{
  auto& entry = *reinterpret_cast<Entry*>(addr);
  this->first   = entry.first;
  this->second  = entry.second;
  this->timeout = entry.timeout;
  this->proto   = entry.proto;
  this->state   = entry.state;
  this->flags   = entry.flags;
  this->other   = entry.other;
  return sizeof(Entry) - sizeof(on_close);
}

void Conntrack::Entry::serialize_to(std::vector<char>& buf) const
{
  const size_t size = sizeof(Entry) - sizeof(on_close);
  const auto* ptr = reinterpret_cast<const char*>(this);
  buf.insert(buf.end(), ptr, ptr + size);
}

int Conntrack::deserialize_from(void* addr)
{
  const auto prev_size = entries.size();
  auto* buffer = reinterpret_cast<uint8_t*>(addr);

  const auto size = *reinterpret_cast<size_t*>(buffer);
  buffer += sizeof(size_t);

  size_t dupes = 0;
  for(auto i = size; i > 0; i--)
  {
    // create the entry
    auto entry = std::make_shared<Entry>();
    buffer += entry->deserialize_from(buffer);

    bool insert = false;
    insert = entries.insert_or_assign({entry->first, entry->proto}, entry).second;
    if(not insert)
      dupes++;
    insert = entries.insert_or_assign({entry->second, entry->proto}, entry).second;
    if(not insert)
      dupes++;
  }

  Ensures(entries.size() - (prev_size-dupes) == size * 2);

  return buffer - reinterpret_cast<uint8_t*>(addr);
}

void Conntrack::serialize_to(std::vector<char>& buf) const
{
  int unserialized = 0;

  // Since each entry is stored twice in the map,
  // we iterate and put it in a set if not already there
  std::set<Entry*> to_serialize;
  for(auto& i : entries)
  {
    auto* ent = i.second.get();

    // We cannot restore delegates, so just ignore
    // the ones with close handler set
    if(ent->on_close != nullptr) {
      unserialized++;
      continue;
    }
    // If not in set, add
    if(to_serialize.find(ent) == to_serialize.end())
      to_serialize.emplace(ent);
  }

  // Serialize number of entries
  size_t size = to_serialize.size();
  const auto* size_ptr = reinterpret_cast<const char*>(&size);

  const auto expected_buf_size = sizeof(size) + (size * (sizeof(Entry) - sizeof(Entry_handler)));
  buf.reserve(expected_buf_size);

  buf.insert(buf.end(), size_ptr, size_ptr + sizeof(size));
  // Serialize each entry
  for(auto& ent : to_serialize)
    ent->serialize_to(buf);

  if(unserialized > 0)
    INFO("Conntrack", "%i entries not serialized\n", unserialized);
}

}
<|endoftext|>


<commit_before>// Copyright (c) 2013, German Neuroinformatics Node (G-Node)
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted under the terms of the BSD License. See
// LICENSE file in the root of the Project.

#include <nix/hdf5/Group.hpp>
#include <nix/util/util.hpp>
#include <boost/multi_array.hpp>
#include <H5Gpublic.h>

namespace nix {
namespace hdf5 {

optGroup::optGroup(const Group &parent, const std::string &g_name)
    : parent(parent), g_name(g_name)
{}

boost::optional<Group> optGroup::operator() (bool create) const {
    if (parent.hasGroup(g_name)) {
        g = boost::optional<Group>(parent.openGroup(g_name));
    } else if (create) {
        g = boost::optional<Group>(parent.openGroup(g_name, true));
    }
    return g;
}

Group::Group()
    : h5group()
{}

Group::Group(H5::Group h5group)
    : h5group(h5group)
{}

Group::Group(const Group &group)
    : h5group(group.h5group)
{}

Group& Group::operator=(const Group &group)
{
    h5group = group.h5group;
    return *this;
}

bool Group::hasAttr(const std::string &name) const {
    return H5Aexists(h5group.getId(), name.c_str());
}

void Group::removeAttr(const std::string &name) const {
    h5group.removeAttr(name);
}

bool Group::hasObject(const std::string &name) const {
    // empty string should return false, not exception (which H5Lexists would)
    if (name.empty()) {
        return false;
    }
    htri_t res = H5Lexists(h5group.getLocId(), name.c_str(), H5P_DEFAULT);
    return res;
}

size_t Group::objectCount() const {
    return h5group.getNumObjs();
}

boost::optional<Group> Group::findGroupByAttribute(const std::string &attribute, const std::string &value) const {
    boost::optional<Group> ret;

    // look up first direct sub-group that has given attribute with given value
    for (size_t index = 0; index < objectCount(); index++) {
        std::string obj_name = objectName(index);
        if(hasGroup(obj_name)) {
            Group group = openGroup(obj_name, false);
            if(group.hasAttr(attribute)) {
                std::string attr_value;
                group.getAttr(attribute, attr_value);
                if (attr_value == value) {
                    ret = group;
                    break;
                }
            }
        }
    }

    return ret;
}

boost::optional<DataSet> Group::findDataByAttribute(const std::string &attribute, const std::string &value) const {
    std::vector<DataSet> dsets;
    boost::optional<DataSet> ret;

    // look up all direct sub-datasets that have the given attribute
    for (size_t index = 0; index < objectCount(); index++) {
        std::string obj_name = objectName(index);
        if(hasData(obj_name)) {
            DataSet dset(h5group.openDataSet(obj_name));
            if(dset.hasAttr(attribute)) dsets.push_back(dset);
        }
    }
    // look for first dataset with given attribute set to given value
    auto found = std::find_if(dsets.begin(), dsets.end(),
                 [value, attribute](DataSet &dset) {
                     std::string attr_value;
                     dset.getAttr(attribute, attr_value);
                     return attr_value == value; });
    if(found != dsets.end()) ret = *found;

    return ret;
}

std::string Group::objectName(size_t index) const {
    // check if index valid
    if(index > objectCount()) {
        throw OutOfBounds("No object at given index", index);
    }

    std::string str_name;
    // check whether name is found by index
    ssize_t name_len = H5Lget_name_by_idx(h5group.getLocId(),
                                                  ".",
                                                  H5_INDEX_NAME,
                                                  H5_ITER_NATIVE,
                                                  (hsize_t) index,
                                                  NULL,
                                                  0,
                                                  H5P_DEFAULT);
    if (name_len > 0) {
        char* name = new char[name_len+1];
        name_len = H5Lget_name_by_idx(h5group.getLocId(),
                                      ".",
                                      H5_INDEX_NAME,
                                      H5_ITER_NATIVE,
                                      (hsize_t) index,
                                      name,
                                      name_len+1,
                                      H5P_DEFAULT);
        str_name = name;
        delete [] name;
    } else {
        throw std::runtime_error("objectName: No object found, H5Lget_name_by_idx returned no name");
    }

    return str_name;
}

bool Group::hasData(const std::string &name) const {
    if (hasObject(name)) {
        H5G_stat_t info;
        h5group.getObjinfo(name, info);
        if (info.type == H5G_DATASET) {
            return true;
        }
    }
    return false;
}

void Group::removeData(const std::string &name) {
    if (hasData(name))
        h5group.unlink(name);
}

DataSet Group::openData(const std::string &name) const {
    H5::DataSet ds5 = h5group.openDataSet(name);
    return DataSet(ds5);
}

bool Group::hasGroup(const std::string &name) const {
    if (hasObject(name)) {
        H5G_stat_t info;
        h5group.getObjinfo(name, info);
        if (info.type == H5G_GROUP) {
            return true;
        }
    }
    return false;
}

Group Group::openGroup(const std::string &name, bool create) const {
    if(!util::nameCheck(name)) throw InvalidName("openGroup");
    Group g;
    if (hasGroup(name)) {
        g = Group(h5group.openGroup(name));
    } else if (create) {
        hid_t gcpl = H5Pcreate(H5P_GROUP_CREATE);

        if (gcpl < 0) {
            throw std::runtime_error("Unable to create group with name '" + name + "'! (H5Pcreate)");
        }

        //we want hdf5 to keep track of the order in which links were created so that
        //the order for indexed based accessors is stable cf. issue #387
        herr_t res = H5Pset_link_creation_order(gcpl, H5P_CRT_ORDER_TRACKED|H5P_CRT_ORDER_INDEXED);
        if (res < 0) {
            throw std::runtime_error("Unable to create group with name '" + name + "'! (H5Pset_link_cr...)");
        }

        hid_t h5_gid = H5Gcreate2(h5group.getLocId(), name.c_str(), H5P_DEFAULT, gcpl, H5P_DEFAULT);
        H5Pclose(gcpl);
        if (h5_gid < 0) {
            throw std::runtime_error("Unable to create group with name '" + name + "'! (H5Gcreate2)");
        }
        g = Group(H5::Group(h5_gid));
    } else {
        throw std::runtime_error("Unable to open group with name '" + name + "'!");
    }
    return g;
}

optGroup Group::openOptGroup(const std::string &name) {
    if(!util::nameCheck(name)) throw InvalidName("openOptGroup");
    return optGroup(*this, name);
}

void Group::removeGroup(const std::string &name) {
    if (hasGroup(name))
        h5group.unlink(name);
}

void Group::renameGroup(const std::string &old_name, const std::string &new_name) {
    if(!util::nameCheck(new_name)) throw InvalidName("renameGroup");
    if (hasGroup(old_name)) {
        h5group.move(old_name, new_name);
    }
}

bool Group::operator==(const Group &group) const {
    return h5group.getLocId() == group.h5group.getLocId();
}

bool Group::operator!=(const Group &group) const {
    return h5group.getLocId() != group.h5group.getLocId();
}

H5::Group Group::h5Group() const {
    return h5group;
}

Group Group::createLink(const Group &target, const std::string &link_name) {
    if(!util::nameCheck(link_name)) throw InvalidName("createLink");
    herr_t error = H5Lcreate_hard(target.h5group.getLocId(), ".", h5group.getLocId(), link_name.c_str(),
                                  H5L_SAME_LOC, H5L_SAME_LOC);
    if (error)
        throw std::runtime_error("Unable to create link " + link_name);

    return openGroup(link_name, false);
}

// TODO implement some kind of roll-back in order to avoid half renamed links.
bool Group::renameAllLinks(const std::string &old_name, const std::string &new_name) {
    if(!util::nameCheck(new_name)) throw InvalidName("renameAllLinks");
    bool renamed = false;

    if (hasGroup(old_name)) {
        std::vector<std::string> links;

        Group  group     = openGroup(old_name, false);
        size_t size      = 128;
        char *name_read  = new char[size];

        size_t size_read = H5Iget_name(group.h5group.getId(), name_read, size);
        while (size_read > 0) {

            if (size_read < size) {
                H5Ldelete(h5group.getId(), name_read, H5L_SAME_LOC);
                links.push_back(name_read);
            } else {
                delete[] name_read;
                size = size * 2;
                name_read = new char[size];
            }

            size_read = H5Iget_name(group.h5group.getId(), name_read, size);
        }

        renamed = links.size() > 0;
        for (std::string curr_name: links) {
            size_t pos = curr_name.find_last_of('/') + 1;

            if (curr_name.substr(pos) == old_name) {
                curr_name.replace(curr_name.begin() + pos, curr_name.end(), new_name.begin(), new_name.end());
            }

            herr_t error = H5Lcreate_hard(group.h5group.getLocId(), ".", h5group.getLocId(), curr_name.c_str(),
                                          H5L_SAME_LOC, H5L_SAME_LOC);

            renamed = renamed && (error >= 0);
        }
    }

    return renamed;
}

// TODO implement some kind of roll-back in order to avoid half removed links.
bool Group::removeAllLinks(const std::string &name) {
    bool removed = false;

    if (hasGroup(name)) {
        Group  group      = openGroup(name, false);
        size_t size       = 128;
        char *name_read   = new char[size];

        size_t size_read  = H5Iget_name(group.h5group.getId(), name_read, size);
        while (size_read > 0) {
            if (size_read < size) {
                H5Ldelete(h5group.getId(), name_read, H5L_SAME_LOC);
            } else {
                delete[] name_read;
                size = size * 2;
                name_read = new char[size];
            }
            size_read = H5Iget_name(group.h5group.getId(), name_read, size);
        }

        delete[] name_read;
        removed = true;
    }

    return removed;
}

void Group::readAttr(const H5::Attribute &attr, H5::DataType mem_type, const NDSize &size, void *data) {
    attr.read(mem_type, data);
}

void Group::readAttr(const H5::Attribute &attr, H5::DataType mem_type, const NDSize &size, std::string *data) {
    StringWriter writer(size, data);
    attr.read(mem_type, *writer);
    writer.finish();
    H5::DataSet::vlenReclaim(*writer, mem_type, attr.getSpace()); //recycle space?
}

void Group::writeAttr(const H5::Attribute &attr, H5::DataType mem_type, const NDSize &size, const void *data) {
    attr.write(mem_type, data);
}

void Group::writeAttr(const H5::Attribute &attr, H5::DataType mem_type, const NDSize &size, const std::string *data) {
    StringReader reader(size, data);
    attr.write(mem_type, *reader);
}

Group::~Group() {
    h5group.close();
}

static bool looksLikeUUID(const std::string id) {
    return true;
}

boost::optional<Group> Group::findGroupByNameOrAttribute(std::string const &attr, std::string const &value) const {

    if (hasObject(value)) {
        return boost::make_optional(openGroup(value, false));
    } else if (looksLikeUUID(value)) {
        return findGroupByAttribute(attr, value);
    } else {
        return boost::optional<Group>();
    }
}

} // namespace hdf5
} // namespace nix
<commit_msg>Group: implement looksLikeUUID<commit_after>// Copyright (c) 2013, German Neuroinformatics Node (G-Node)
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted under the terms of the BSD License. See
// LICENSE file in the root of the Project.

#include <nix/hdf5/Group.hpp>
#include <nix/util/util.hpp>
#include <boost/multi_array.hpp>
#include <H5Gpublic.h>

namespace nix {
namespace hdf5 {

optGroup::optGroup(const Group &parent, const std::string &g_name)
    : parent(parent), g_name(g_name)
{}

boost::optional<Group> optGroup::operator() (bool create) const {
    if (parent.hasGroup(g_name)) {
        g = boost::optional<Group>(parent.openGroup(g_name));
    } else if (create) {
        g = boost::optional<Group>(parent.openGroup(g_name, true));
    }
    return g;
}

Group::Group()
    : h5group()
{}

Group::Group(H5::Group h5group)
    : h5group(h5group)
{}

Group::Group(const Group &group)
    : h5group(group.h5group)
{}

Group& Group::operator=(const Group &group)
{
    h5group = group.h5group;
    return *this;
}

bool Group::hasAttr(const std::string &name) const {
    return H5Aexists(h5group.getId(), name.c_str());
}

void Group::removeAttr(const std::string &name) const {
    h5group.removeAttr(name);
}

bool Group::hasObject(const std::string &name) const {
    // empty string should return false, not exception (which H5Lexists would)
    if (name.empty()) {
        return false;
    }
    htri_t res = H5Lexists(h5group.getLocId(), name.c_str(), H5P_DEFAULT);
    return res;
}

size_t Group::objectCount() const {
    return h5group.getNumObjs();
}

boost::optional<Group> Group::findGroupByAttribute(const std::string &attribute, const std::string &value) const {
    boost::optional<Group> ret;

    // look up first direct sub-group that has given attribute with given value
    for (size_t index = 0; index < objectCount(); index++) {
        std::string obj_name = objectName(index);
        if(hasGroup(obj_name)) {
            Group group = openGroup(obj_name, false);
            if(group.hasAttr(attribute)) {
                std::string attr_value;
                group.getAttr(attribute, attr_value);
                if (attr_value == value) {
                    ret = group;
                    break;
                }
            }
        }
    }

    return ret;
}

boost::optional<DataSet> Group::findDataByAttribute(const std::string &attribute, const std::string &value) const {
    std::vector<DataSet> dsets;
    boost::optional<DataSet> ret;

    // look up all direct sub-datasets that have the given attribute
    for (size_t index = 0; index < objectCount(); index++) {
        std::string obj_name = objectName(index);
        if(hasData(obj_name)) {
            DataSet dset(h5group.openDataSet(obj_name));
            if(dset.hasAttr(attribute)) dsets.push_back(dset);
        }
    }
    // look for first dataset with given attribute set to given value
    auto found = std::find_if(dsets.begin(), dsets.end(),
                 [value, attribute](DataSet &dset) {
                     std::string attr_value;
                     dset.getAttr(attribute, attr_value);
                     return attr_value == value; });
    if(found != dsets.end()) ret = *found;

    return ret;
}

std::string Group::objectName(size_t index) const {
    // check if index valid
    if(index > objectCount()) {
        throw OutOfBounds("No object at given index", index);
    }

    std::string str_name;
    // check whether name is found by index
    ssize_t name_len = H5Lget_name_by_idx(h5group.getLocId(),
                                                  ".",
                                                  H5_INDEX_NAME,
                                                  H5_ITER_NATIVE,
                                                  (hsize_t) index,
                                                  NULL,
                                                  0,
                                                  H5P_DEFAULT);
    if (name_len > 0) {
        char* name = new char[name_len+1];
        name_len = H5Lget_name_by_idx(h5group.getLocId(),
                                      ".",
                                      H5_INDEX_NAME,
                                      H5_ITER_NATIVE,
                                      (hsize_t) index,
                                      name,
                                      name_len+1,
                                      H5P_DEFAULT);
        str_name = name;
        delete [] name;
    } else {
        throw std::runtime_error("objectName: No object found, H5Lget_name_by_idx returned no name");
    }

    return str_name;
}

bool Group::hasData(const std::string &name) const {
    if (hasObject(name)) {
        H5G_stat_t info;
        h5group.getObjinfo(name, info);
        if (info.type == H5G_DATASET) {
            return true;
        }
    }
    return false;
}

void Group::removeData(const std::string &name) {
    if (hasData(name))
        h5group.unlink(name);
}

DataSet Group::openData(const std::string &name) const {
    H5::DataSet ds5 = h5group.openDataSet(name);
    return DataSet(ds5);
}

bool Group::hasGroup(const std::string &name) const {
    if (hasObject(name)) {
        H5G_stat_t info;
        h5group.getObjinfo(name, info);
        if (info.type == H5G_GROUP) {
            return true;
        }
    }
    return false;
}

Group Group::openGroup(const std::string &name, bool create) const {
    if(!util::nameCheck(name)) throw InvalidName("openGroup");
    Group g;
    if (hasGroup(name)) {
        g = Group(h5group.openGroup(name));
    } else if (create) {
        hid_t gcpl = H5Pcreate(H5P_GROUP_CREATE);

        if (gcpl < 0) {
            throw std::runtime_error("Unable to create group with name '" + name + "'! (H5Pcreate)");
        }

        //we want hdf5 to keep track of the order in which links were created so that
        //the order for indexed based accessors is stable cf. issue #387
        herr_t res = H5Pset_link_creation_order(gcpl, H5P_CRT_ORDER_TRACKED|H5P_CRT_ORDER_INDEXED);
        if (res < 0) {
            throw std::runtime_error("Unable to create group with name '" + name + "'! (H5Pset_link_cr...)");
        }

        hid_t h5_gid = H5Gcreate2(h5group.getLocId(), name.c_str(), H5P_DEFAULT, gcpl, H5P_DEFAULT);
        H5Pclose(gcpl);
        if (h5_gid < 0) {
            throw std::runtime_error("Unable to create group with name '" + name + "'! (H5Gcreate2)");
        }
        g = Group(H5::Group(h5_gid));
    } else {
        throw std::runtime_error("Unable to open group with name '" + name + "'!");
    }
    return g;
}

optGroup Group::openOptGroup(const std::string &name) {
    if(!util::nameCheck(name)) throw InvalidName("openOptGroup");
    return optGroup(*this, name);
}

void Group::removeGroup(const std::string &name) {
    if (hasGroup(name))
        h5group.unlink(name);
}

void Group::renameGroup(const std::string &old_name, const std::string &new_name) {
    if(!util::nameCheck(new_name)) throw InvalidName("renameGroup");
    if (hasGroup(old_name)) {
        h5group.move(old_name, new_name);
    }
}

bool Group::operator==(const Group &group) const {
    return h5group.getLocId() == group.h5group.getLocId();
}

bool Group::operator!=(const Group &group) const {
    return h5group.getLocId() != group.h5group.getLocId();
}

H5::Group Group::h5Group() const {
    return h5group;
}

Group Group::createLink(const Group &target, const std::string &link_name) {
    if(!util::nameCheck(link_name)) throw InvalidName("createLink");
    herr_t error = H5Lcreate_hard(target.h5group.getLocId(), ".", h5group.getLocId(), link_name.c_str(),
                                  H5L_SAME_LOC, H5L_SAME_LOC);
    if (error)
        throw std::runtime_error("Unable to create link " + link_name);

    return openGroup(link_name, false);
}

// TODO implement some kind of roll-back in order to avoid half renamed links.
bool Group::renameAllLinks(const std::string &old_name, const std::string &new_name) {
    if(!util::nameCheck(new_name)) throw InvalidName("renameAllLinks");
    bool renamed = false;

    if (hasGroup(old_name)) {
        std::vector<std::string> links;

        Group  group     = openGroup(old_name, false);
        size_t size      = 128;
        char *name_read  = new char[size];

        size_t size_read = H5Iget_name(group.h5group.getId(), name_read, size);
        while (size_read > 0) {

            if (size_read < size) {
                H5Ldelete(h5group.getId(), name_read, H5L_SAME_LOC);
                links.push_back(name_read);
            } else {
                delete[] name_read;
                size = size * 2;
                name_read = new char[size];
            }

            size_read = H5Iget_name(group.h5group.getId(), name_read, size);
        }

        renamed = links.size() > 0;
        for (std::string curr_name: links) {
            size_t pos = curr_name.find_last_of('/') + 1;

            if (curr_name.substr(pos) == old_name) {
                curr_name.replace(curr_name.begin() + pos, curr_name.end(), new_name.begin(), new_name.end());
            }

            herr_t error = H5Lcreate_hard(group.h5group.getLocId(), ".", h5group.getLocId(), curr_name.c_str(),
                                          H5L_SAME_LOC, H5L_SAME_LOC);

            renamed = renamed && (error >= 0);
        }
    }

    return renamed;
}

// TODO implement some kind of roll-back in order to avoid half removed links.
bool Group::removeAllLinks(const std::string &name) {
    bool removed = false;

    if (hasGroup(name)) {
        Group  group      = openGroup(name, false);
        size_t size       = 128;
        char *name_read   = new char[size];

        size_t size_read  = H5Iget_name(group.h5group.getId(), name_read, size);
        while (size_read > 0) {
            if (size_read < size) {
                H5Ldelete(h5group.getId(), name_read, H5L_SAME_LOC);
            } else {
                delete[] name_read;
                size = size * 2;
                name_read = new char[size];
            }
            size_read = H5Iget_name(group.h5group.getId(), name_read, size);
        }

        delete[] name_read;
        removed = true;
    }

    return removed;
}

void Group::readAttr(const H5::Attribute &attr, H5::DataType mem_type, const NDSize &size, void *data) {
    attr.read(mem_type, data);
}

void Group::readAttr(const H5::Attribute &attr, H5::DataType mem_type, const NDSize &size, std::string *data) {
    StringWriter writer(size, data);
    attr.read(mem_type, *writer);
    writer.finish();
    H5::DataSet::vlenReclaim(*writer, mem_type, attr.getSpace()); //recycle space?
}

void Group::writeAttr(const H5::Attribute &attr, H5::DataType mem_type, const NDSize &size, const void *data) {
    attr.write(mem_type, data);
}

void Group::writeAttr(const H5::Attribute &attr, H5::DataType mem_type, const NDSize &size, const std::string *data) {
    StringReader reader(size, data);
    attr.write(mem_type, *reader);
}

Group::~Group() {
    h5group.close();
}

static bool looksLikeUUID(const std::string &id) {
    // we don't want a complete check, just a glance
    // uuid form is: 8-4-4-4-12 = 36 [8, 13, 18, 23, ]
    return id.size() == 36 && id[8] == '-' && id[13] == '-' && id[18] == '-' && id[23] =='-';
}

boost::optional<Group> Group::findGroupByNameOrAttribute(std::string const &attr, std::string const &value) const {

    if (hasObject(value)) {
        return boost::make_optional(openGroup(value, false));
    } else if (looksLikeUUID(value)) {
        return findGroupByAttribute(attr, value);
    } else {
        return boost::optional<Group>();
    }
}

} // namespace hdf5
} // namespace nix
<|endoftext|>


<commit_before>#include "MainWindow.h"
#include "NotifySingletons.h"
#include "MqPublisher.h"
#include <iostream>
using namespace std;

MainWindow::MainWindow(QObject *parent /*= 0*/)
: QObject(parent)
, oContacts (this)
, oInbox (this)
, checkCounter (0)
{
    initLogging ();

    qRegisterMetaType<ContactInfo>("ContactInfo");

    GVAccess &webPage = Singletons::getRef().getGVAccess ();
    // webPage status
    QObject::connect (&webPage, SIGNAL (status(const QString &, int)),
                       this   , SLOT   (setStatus(const QString &, int)));
    // Status from contacts object
    QObject::connect (&oContacts, SIGNAL (status   (const QString &, int)),
                       this     , SLOT   (setStatus(const QString &, int)));
    // oContacts.allContacts -> this.getContactsDone
    QObject::connect (&oContacts, SIGNAL (allContacts (bool, bool)),
                      this      , SLOT   (getContactsDone (bool, bool)));
    // Status from inbox object
    QObject::connect (&oInbox, SIGNAL (status   (const QString &, int)),
                       this  , SLOT   (setStatus(const QString &, int)));
    // Inbox has updated
    QObject::connect (&oInbox, SIGNAL (inboxChanged ()),
                       this  , SLOT   (inboxChanged ()));
    // Timer tick
    QObject::connect (&mainTimer, SIGNAL(timeout()), this, SLOT(doWork()));

    webPage.setEmitLog (false);

    if (!checkParams ()) {
        qApp->quit();
        return;
    }

    QTimer::singleShot (100, this, SLOT(doWork ()));
}//MainWindow::MainWindow

void
MainWindow::initLogging ()
{
    QString strLogfile = baseDir ();
    strLogfile += QDir::separator();
    strLogfile += "notify.log";
    fLogfile.setFileName (strLogfile);
    fLogfile.open (QIODevice::WriteOnly | QIODevice::Append);
}//MainWindow::initLogging

/** Log information to console and to log file
 * This function is invoked from the qDebug handler that is installed in main.
 * @param strText Text to be logged
 * @param level Log level
 */
void
MainWindow::log (const QString &strText, int level /*= 10*/)
{
    QString strDisp;
    QRegExp regex("^\"(.*)\"\\s*");
    if (strText.indexOf (regex) != -1) {
        strDisp = regex.cap (1);
    } else {
        strDisp = strText;
    }

    QDateTime dt = QDateTime::currentDateTime ();
    QString strLog = QString("%1 : %2 : %3")
                     .arg(dt.toString ("yyyy-MM-dd hh:mm:ss.zzz"))
                     .arg(level)
                     .arg(strDisp);

    // Send to standard output
    cout << strLog.toStdString () << endl;

    // Send to log file
    if (fLogfile.isOpen ()) {
        QTextStream streamLog(&fLogfile);
        streamLog << strLog << endl;
    }
}//MainWindow::log

void
MainWindow::setStatus(const QString &strText, int /*timeout = 3000*/)
{
    qDebug () << strText;
}//MainWindow::setStatus

QString
MainWindow::baseDir()
{
    QString strBasedir = QDir::homePath();
    QDir baseDir(strBasedir);
    if (!baseDir.exists (".qgvdial")) {
        baseDir.mkdir (".qgvdial");
    }
    strBasedir += QDir::separator();
    strBasedir += ".qgvdial";
    return strBasedir;
}//MainWindow::baseDir

void
MainWindow::doWork ()
{
    if (strSelfNumber.isEmpty ()) {
        doLogin ();
        return;
    }

    checkCounter++;
    if (0 == checkCounter % 100) {
        qDebug() << "Checked" << checkCounter << "times";
    }

    // Get the contacts
    oContacts.refreshContacts ();
    // Get inbox
    oInbox.refresh ();
}//MainWindow::doWork

bool
MainWindow::checkParams ()
{
    bool rv = false, bUseDefaultIni = false;
    QString strIni;
    QStringList args = qApp->arguments ();
    if (args.length () < 2) {
        qWarning ("No ini file specified, using default");
        bUseDefaultIni = true;
    } else {
        QFileInfo fi(args[1]);
        strIni = fi.absoluteFilePath ();
    }

    if (bUseDefaultIni) {
        strIni = baseDir ();
        strIni += QDir::separator();
        strIni += "notify.ini";
    }

    qDebug () << "Using ini file at" << strIni;

    bool bUseProxy = false, bUseSystemProxy = false, bProxyAuth = false;
    QString strProxy, strProxyUser, strProxyPass;
    int proxyport;

    do { // Begin cleanup block (not a loop)
        QSettings settings (strIni, QSettings::IniFormat, this);

        QByteArray byD;
        QTextStream in(stdin);
        if (!settings.contains ("user")) {
            qWarning ("Ini file does not contain a username");
            cout << "Enter username:";
            in >> strUser;
            settings.setValue ("user", strUser);
        } else {
            strUser = settings.value ("user").toString ();
        }

        if (!settings.contains ("password")) {
            qWarning ("Ini file does not contain a password");
            cout << "Enter password:";
            in >> strPass;
            cipher (strPass.toLocal8Bit (), byD, true);
            settings.setValue ("password", QString(byD.toHex ()));
        } else {
            byD = settings.value("password").toByteArray();
            cipher (QByteArray::fromHex (byD), byD, false);
            strPass = byD;
        }

        checkTimeout = 0;
        if (settings.contains ("check_timeout")) {
            checkTimeout = settings.value ("check_timeout").toUInt ();
        }
        if (checkTimeout < 5) {
            qWarning () << "Check frequency =" << checkTimeout
                        << "is not valid. Resetting to lowest value = 5";
            checkTimeout = 5;
            settings.setValue ("check_timeout", checkTimeout);
        }

        if (!settings.contains ("mqserver")) {
            qWarning ("Ini file does not contain the mq server hostname");
            cout << "Enter server hostname:";
            in >> m_strMqServer;
            settings.setValue ("mqserver", m_strMqServer);
        } else {
            m_strMqServer = settings.value ("mqserver").toString ();
        }

        if (!settings.contains ("mqport")) {
            qWarning ("Ini file does not contain the mq server port");
            cout << "Enter server port:";
            in >> m_mqPort;
            if (0 == m_mqPort) m_mqPort = 1883;
            settings.setValue ("mqport", m_mqPort);
        } else {
            m_mqPort = settings.value ("mqport").toInt (&rv);
            if (!rv) break;
            if (0 == m_mqPort) m_mqPort = 1883;
            settings.setValue ("mqport", m_mqPort);
            rv = false;
        }

        if (!settings.contains ("mqtopic")) {
            qWarning ("Ini file does not contain the mq topic");
            cout << "Enter topic:";
            in >> m_strMqTopic;
            settings.setValue ("mqtopic", m_strMqTopic);
        } else {
            m_strMqTopic = settings.value ("mqtopic").toString ();
        }

        if (!settings.contains ("useproxy")) {
            settings.setValue ("useproxy", false);
            bUseProxy = false;
        } else {
            bUseProxy = settings.value ("useproxy", false).toBool ();
        }

        if (!settings.contains ("usesystemproxy")) {
            settings.setValue ("usesystemproxy", false);
            bUseSystemProxy = false;
        } else {
            bUseSystemProxy = settings.value ("usesystemproxy", false).toBool();
        }

        if (!settings.contains ("proxyserver")) {
            settings.setValue ("proxyserver", "proxy.example.com");
        } else {
            strProxy = settings.value ("proxyserver", "").toString ();
        }

        if (!settings.contains ("proxyport")) {
            settings.setValue ("proxyport", 80);
            proxyport = 80;
        } else {
            proxyport = settings.value ("proxyport", 80).toInt ();
        }

        if (!settings.contains ("proxyauth")) {
            settings.setValue ("proxyauth", false);
            bProxyAuth = false;
        } else {
            bProxyAuth = settings.value ("proxyauth", false).toBool ();
        }

        if (!settings.contains ("proxyuser")) {
            if (bUseProxy & !bUseSystemProxy && bProxyAuth) {
                qWarning ("Ini file needs proxy authentication user");
                cout << "Enter proxy user:";
                in >> strProxyUser;
                settings.setValue ("proxyuser", "proxy_user");
            }
        } else {
            strProxyUser = settings.value ("proxyuser", "").toString ();
        }

        if (!settings.contains ("proxypass")) {
            if (bUseProxy & !bUseSystemProxy && bProxyAuth) {
                qWarning ("Ini file needs proxy authentication password");
                cout << "Enter proxy password:";
                in >> strProxyPass;
                cipher (strPass.toLocal8Bit (), byD, true);
                settings.setValue ("proxypass", QString(byD.toHex ()));
            }
        } else {
            byD = settings.value("proxypass", "").toByteArray ();
            cipher (QByteArray::fromHex (byD), byD, false);
            strProxyPass = byD;
        }

        if (bUseProxy) {
            GVAccess &webPage = Singletons::getRef().getGVAccess ();
            webPage.setProxySettings (bUseProxy, bUseSystemProxy,
                                      strProxy, proxyport,
                                      bProxyAuth,
                                      strProxyUser, strProxyPass);
        }
        rv = true;
    } while (0); // End cleanup block (not a loop)

    return rv;
}//MainWindow::checkParams

bool
MainWindow::cipher(const QByteArray &byIn, QByteArray &byOut, bool bEncrypt)
{
    int iEVP, inl, outl, c;
    EVP_CIPHER_CTX cipherCtx;
    char cipherIv[16], cipherIn[16], cipherOut[16 + EVP_MAX_BLOCK_LENGTH];
    memset (&cipherCtx, 0, sizeof cipherCtx);
    memset (&cipherIv, 0xFA, sizeof cipherIv);

#define QGV_CIPHER_KEY "01234567890123456789012345678901"
    EVP_CIPHER_CTX_init (&cipherCtx);
    iEVP = EVP_CipherInit_ex (&cipherCtx, EVP_aes_256_cbc (), NULL, NULL, NULL,
                              bEncrypt?1:0);
    if (1 != iEVP) return false;
    EVP_CIPHER_CTX_set_key_length(&cipherCtx, sizeof(QGV_CIPHER_KEY)-1);
    iEVP = EVP_CipherInit_ex (&cipherCtx, EVP_aes_256_cbc (), NULL,
                              (quint8 *) QGV_CIPHER_KEY, (quint8 *) cipherIv,
                               bEncrypt?1:0);
    if (1 != iEVP) return false;

    byOut.clear ();
    c = 0;
    while (c < byIn.size ()) {
        inl = byIn.size () - c;
        inl = (uint)inl > sizeof (cipherIn) ? sizeof (cipherIn) : inl;
        memcpy (cipherIn, &(byIn.constData()[c]), inl);
        memset (&cipherOut, 0, sizeof cipherOut);
        outl = sizeof cipherOut;
        iEVP = EVP_CipherUpdate (&cipherCtx,
                                (quint8 *) &cipherOut, &outl,
                                 (quint8 *) &cipherIn , inl);
        if (1 != iEVP) {
            qWarning ("Cipher update failed. Aborting");
            break;
        }
        byOut += QByteArray(cipherOut, outl);
        c += inl;
    }

    if (1 == iEVP) {
        outl = sizeof cipherOut;
        memset (&cipherOut, 0, sizeof cipherOut);
        iEVP = EVP_CipherFinal_ex (&cipherCtx, (quint8 *) &cipherOut, &outl);
        if (1 == iEVP) {
            byOut += QByteArray(cipherOut, outl);
        }
    }

    EVP_CIPHER_CTX_cleanup(&cipherCtx);

    return (1 == iEVP);
}//MainWindow::cipher

/** Invoked to begin the login process.
 * We already have the username and password, so just start the login to the GV
 * website. The async completion routine is loginCompleted.
 */
void
MainWindow::doLogin ()
{
    GVAccess &webPage = Singletons::getRef().getGVAccess ();
    QVariantList l;

    bool bOk = false;
    do { // Begin cleanup block (not a loop)
        webPage.setTimeout(60);

        l += strUser;
        l += strPass;

        qDebug ("Logging in...");

        // webPage.workCompleted -> this.loginCompleted
        if (!webPage.enqueueWork (GVAW_login, l, this,
                SLOT (loginCompleted (bool, const QVariantList &))))
        {
            qCritical ("Login returned immediately with failure!");
            break;
        }

        bOk = true;
    } while (0); // End cleanup block (not a loop)

    if (!bOk)
    {
        webPage.setTimeout(20);
        // Cleanup if any
        strUser.clear ();
        strPass.clear ();

        l.clear ();
        logoutCompleted (true, l);

        qApp->quit ();
    }
}//MainWindow::doLogin

void
MainWindow::loginCompleted (bool bOk, const QVariantList &varList)
{
    strSelfNumber.clear ();
    GVAccess &webPage = Singletons::getRef().getGVAccess ();
    webPage.setTimeout(20);

    if (!bOk)
    {
        QVariantList l;
        logoutCompleted (true, l);

        qCritical ("User login failed");
        qApp->quit ();
    }
    else
    {
        qDebug ("User logged in");

        // Save the users GV number returned by the login completion
        strSelfNumber = varList[varList.size()-1].toString ();

        oContacts.setUserPass (strUser, strPass);
        oContacts.loginSuccess ();
        oInbox.loginSuccess ();

        QTimer::singleShot (100, this, SLOT(doWork ()));
    }
}//MainWindow::loginCompleted

void
MainWindow::doLogout ()
{
    GVAccess &webPage = Singletons::getRef().getGVAccess ();
    QVariantList l;
    webPage.enqueueWork (GVAW_logout, l, this,
                         SLOT (logoutCompleted (bool, const QVariantList &)));
}//MainWindow::doLogout

void
MainWindow::logoutCompleted (bool, const QVariantList &)
{
    // This clears out the table and the view as well
    oContacts.loggedOut ();
    oInbox.loggedOut ();
}//MainWindow::logoutCompleted

void
MainWindow::getContactsDone (bool bChanges, bool bOK)
{
    if (bOK && bChanges) {
        qDebug ("Contacts changed, update mosquitto");

        MqPublisher pub(QString("qgvnotify:%1").arg(QHostInfo::localHostName()),
                        m_strMqServer, m_mqPort, m_strMqTopic,
                        this);
        pub.publish ("contact");
    }

    startTimer ();
}//MainWindow::getContactsDone

void
MainWindow::inboxChanged ()
{
    qDebug ("Inbox changed, update mosquitto");

    MqPublisher pub(QString("qgvnotify:%1").arg(QHostInfo::localHostName()),
                    m_strMqServer, m_mqPort, m_strMqTopic,
                    this);
    pub.publish ("inbox");

    startTimer ();
}//MainWindow::inboxChanged

void
MainWindow::startTimer ()
{
    mainTimer.stop ();
    mainTimer.setSingleShot (true);
    mainTimer.setInterval (checkTimeout * 1000);
    mainTimer.start ();
}//MainWindow::startTimer
<commit_msg>Log file for notify for a non-standard ini file needs to be non-<commit_after>#include "MainWindow.h"
#include "NotifySingletons.h"
#include "MqPublisher.h"
#include <iostream>
using namespace std;

MainWindow::MainWindow(QObject *parent /*= 0*/)
: QObject(parent)
, oContacts (this)
, oInbox (this)
, checkCounter (0)
{
    initLogging ();

    qRegisterMetaType<ContactInfo>("ContactInfo");

    GVAccess &webPage = Singletons::getRef().getGVAccess ();
    // webPage status
    QObject::connect (&webPage, SIGNAL (status(const QString &, int)),
                       this   , SLOT   (setStatus(const QString &, int)));
    // Status from contacts object
    QObject::connect (&oContacts, SIGNAL (status   (const QString &, int)),
                       this     , SLOT   (setStatus(const QString &, int)));
    // oContacts.allContacts -> this.getContactsDone
    QObject::connect (&oContacts, SIGNAL (allContacts (bool, bool)),
                      this      , SLOT   (getContactsDone (bool, bool)));
    // Status from inbox object
    QObject::connect (&oInbox, SIGNAL (status   (const QString &, int)),
                       this  , SLOT   (setStatus(const QString &, int)));
    // Inbox has updated
    QObject::connect (&oInbox, SIGNAL (inboxChanged ()),
                       this  , SLOT   (inboxChanged ()));
    // Timer tick
    QObject::connect (&mainTimer, SIGNAL(timeout()), this, SLOT(doWork()));

    webPage.setEmitLog (false);

    if (!checkParams ()) {
        qApp->quit();
        return;
    }

    QTimer::singleShot (100, this, SLOT(doWork ()));
}//MainWindow::MainWindow

void
MainWindow::initLogging ()
{
    QString strLogfile = baseDir ();
    strLogfile += QDir::separator();
    strLogfile += "notify.log";
    fLogfile.setFileName (strLogfile);
    fLogfile.open (QIODevice::WriteOnly | QIODevice::Append);
}//MainWindow::initLogging

/** Log information to console and to log file
 * This function is invoked from the qDebug handler that is installed in main.
 * @param strText Text to be logged
 * @param level Log level
 */
void
MainWindow::log (const QString &strText, int level /*= 10*/)
{
    QString strDisp;
    QRegExp regex("^\"(.*)\"\\s*");
    if (strText.indexOf (regex) != -1) {
        strDisp = regex.cap (1);
    } else {
        strDisp = strText;
    }

    QDateTime dt = QDateTime::currentDateTime ();
    QString strLog = QString("%1 : %2 : %3")
                     .arg(dt.toString ("yyyy-MM-dd hh:mm:ss.zzz"))
                     .arg(level)
                     .arg(strDisp);

    // Send to standard output
    cout << strLog.toStdString () << endl;

    // Send to log file
    if (fLogfile.isOpen ()) {
        QTextStream streamLog(&fLogfile);
        streamLog << strLog << endl;
    }
}//MainWindow::log

void
MainWindow::setStatus(const QString &strText, int /*timeout = 3000*/)
{
    qDebug () << strText;
}//MainWindow::setStatus

QString
MainWindow::baseDir()
{
    QString strBasedir = QDir::homePath();
    QDir baseDir(strBasedir);
    if (!baseDir.exists (".qgvdial")) {
        baseDir.mkdir (".qgvdial");
    }
    strBasedir += QDir::separator();
    strBasedir += ".qgvdial";
    return strBasedir;
}//MainWindow::baseDir

void
MainWindow::doWork ()
{
    if (strSelfNumber.isEmpty ()) {
        doLogin ();
        return;
    }

    checkCounter++;
    if (0 == checkCounter % 100) {
        qDebug() << "Checked" << checkCounter << "times";
    }

    // Get the contacts
    oContacts.refreshContacts ();
    // Get inbox
    oInbox.refresh ();
}//MainWindow::doWork

bool
MainWindow::checkParams ()
{
    bool rv = false, bUseDefaultIni = false;
    QString strIni;
    QStringList args = qApp->arguments ();
    if (args.length () < 2) {
        qWarning ("No ini file specified, using default");
        bUseDefaultIni = true;
    } else {
        QFileInfo fi(args[1]);
        strIni = fi.absoluteFilePath ();

        QString strLogfile = strIni + ".log";
        fLogfile.close ();
        fLogfile.setFileName (strLogfile);
        fLogfile.open (QIODevice::WriteOnly | QIODevice::Append);
    }

    if (bUseDefaultIni) {
        strIni = baseDir ();
        strIni += QDir::separator();
        strIni += "notify.ini";
    }

    qDebug () << "Using ini file at" << strIni;

    bool bUseProxy = false, bUseSystemProxy = false, bProxyAuth = false;
    QString strProxy, strProxyUser, strProxyPass;
    int proxyport;

    do { // Begin cleanup block (not a loop)
        QSettings settings (strIni, QSettings::IniFormat, this);

        QByteArray byD;
        QTextStream in(stdin);
        if (!settings.contains ("user")) {
            qWarning ("Ini file does not contain a username");
            cout << "Enter username:";
            in >> strUser;
            settings.setValue ("user", strUser);
        } else {
            strUser = settings.value ("user").toString ();
        }

        if (!settings.contains ("password")) {
            qWarning ("Ini file does not contain a password");
            cout << "Enter password:";
            in >> strPass;
            cipher (strPass.toLocal8Bit (), byD, true);
            settings.setValue ("password", QString(byD.toHex ()));
        } else {
            byD = settings.value("password").toByteArray();
            cipher (QByteArray::fromHex (byD), byD, false);
            strPass = byD;
        }

        checkTimeout = 0;
        if (settings.contains ("check_timeout")) {
            checkTimeout = settings.value ("check_timeout").toUInt ();
        }
        if (checkTimeout < 5) {
            qWarning () << "Check frequency =" << checkTimeout
                        << "is not valid. Resetting to lowest value = 5";
            checkTimeout = 5;
            settings.setValue ("check_timeout", checkTimeout);
        }

        if (!settings.contains ("mqserver")) {
            qWarning ("Ini file does not contain the mq server hostname");
            cout << "Enter server hostname:";
            in >> m_strMqServer;
            settings.setValue ("mqserver", m_strMqServer);
        } else {
            m_strMqServer = settings.value ("mqserver").toString ();
        }

        if (!settings.contains ("mqport")) {
            qWarning ("Ini file does not contain the mq server port");
            cout << "Enter server port:";
            in >> m_mqPort;
            if (0 == m_mqPort) m_mqPort = 1883;
            settings.setValue ("mqport", m_mqPort);
        } else {
            m_mqPort = settings.value ("mqport").toInt (&rv);
            if (!rv) break;
            if (0 == m_mqPort) m_mqPort = 1883;
            settings.setValue ("mqport", m_mqPort);
            rv = false;
        }

        if (!settings.contains ("mqtopic")) {
            qWarning ("Ini file does not contain the mq topic");
            cout << "Enter topic:";
            in >> m_strMqTopic;
            settings.setValue ("mqtopic", m_strMqTopic);
        } else {
            m_strMqTopic = settings.value ("mqtopic").toString ();
        }

        if (!settings.contains ("useproxy")) {
            settings.setValue ("useproxy", false);
            bUseProxy = false;
        } else {
            bUseProxy = settings.value ("useproxy", false).toBool ();
        }

        if (!settings.contains ("usesystemproxy")) {
            settings.setValue ("usesystemproxy", false);
            bUseSystemProxy = false;
        } else {
            bUseSystemProxy = settings.value ("usesystemproxy", false).toBool();
        }

        if (!settings.contains ("proxyserver")) {
            settings.setValue ("proxyserver", "proxy.example.com");
        } else {
            strProxy = settings.value ("proxyserver", "").toString ();
        }

        if (!settings.contains ("proxyport")) {
            settings.setValue ("proxyport", 80);
            proxyport = 80;
        } else {
            proxyport = settings.value ("proxyport", 80).toInt ();
        }

        if (!settings.contains ("proxyauth")) {
            settings.setValue ("proxyauth", false);
            bProxyAuth = false;
        } else {
            bProxyAuth = settings.value ("proxyauth", false).toBool ();
        }

        if (!settings.contains ("proxyuser")) {
            if (bUseProxy & !bUseSystemProxy && bProxyAuth) {
                qWarning ("Ini file needs proxy authentication user");
                cout << "Enter proxy user:";
                in >> strProxyUser;
                settings.setValue ("proxyuser", "proxy_user");
            }
        } else {
            strProxyUser = settings.value ("proxyuser", "").toString ();
        }

        if (!settings.contains ("proxypass")) {
            if (bUseProxy & !bUseSystemProxy && bProxyAuth) {
                qWarning ("Ini file needs proxy authentication password");
                cout << "Enter proxy password:";
                in >> strProxyPass;
                cipher (strPass.toLocal8Bit (), byD, true);
                settings.setValue ("proxypass", QString(byD.toHex ()));
            }
        } else {
            byD = settings.value("proxypass", "").toByteArray ();
            cipher (QByteArray::fromHex (byD), byD, false);
            strProxyPass = byD;
        }

        if (bUseProxy) {
            GVAccess &webPage = Singletons::getRef().getGVAccess ();
            webPage.setProxySettings (bUseProxy, bUseSystemProxy,
                                      strProxy, proxyport,
                                      bProxyAuth,
                                      strProxyUser, strProxyPass);
        }
        rv = true;
    } while (0); // End cleanup block (not a loop)

    return rv;
}//MainWindow::checkParams

bool
MainWindow::cipher(const QByteArray &byIn, QByteArray &byOut, bool bEncrypt)
{
    int iEVP, inl, outl, c;
    EVP_CIPHER_CTX cipherCtx;
    char cipherIv[16], cipherIn[16], cipherOut[16 + EVP_MAX_BLOCK_LENGTH];
    memset (&cipherCtx, 0, sizeof cipherCtx);
    memset (&cipherIv, 0xFA, sizeof cipherIv);

#define QGV_CIPHER_KEY "01234567890123456789012345678901"
    EVP_CIPHER_CTX_init (&cipherCtx);
    iEVP = EVP_CipherInit_ex (&cipherCtx, EVP_aes_256_cbc (), NULL, NULL, NULL,
                              bEncrypt?1:0);
    if (1 != iEVP) return false;
    EVP_CIPHER_CTX_set_key_length(&cipherCtx, sizeof(QGV_CIPHER_KEY)-1);
    iEVP = EVP_CipherInit_ex (&cipherCtx, EVP_aes_256_cbc (), NULL,
                              (quint8 *) QGV_CIPHER_KEY, (quint8 *) cipherIv,
                               bEncrypt?1:0);
    if (1 != iEVP) return false;

    byOut.clear ();
    c = 0;
    while (c < byIn.size ()) {
        inl = byIn.size () - c;
        inl = (uint)inl > sizeof (cipherIn) ? sizeof (cipherIn) : inl;
        memcpy (cipherIn, &(byIn.constData()[c]), inl);
        memset (&cipherOut, 0, sizeof cipherOut);
        outl = sizeof cipherOut;
        iEVP = EVP_CipherUpdate (&cipherCtx,
                                (quint8 *) &cipherOut, &outl,
                                 (quint8 *) &cipherIn , inl);
        if (1 != iEVP) {
            qWarning ("Cipher update failed. Aborting");
            break;
        }
        byOut += QByteArray(cipherOut, outl);
        c += inl;
    }

    if (1 == iEVP) {
        outl = sizeof cipherOut;
        memset (&cipherOut, 0, sizeof cipherOut);
        iEVP = EVP_CipherFinal_ex (&cipherCtx, (quint8 *) &cipherOut, &outl);
        if (1 == iEVP) {
            byOut += QByteArray(cipherOut, outl);
        }
    }

    EVP_CIPHER_CTX_cleanup(&cipherCtx);

    return (1 == iEVP);
}//MainWindow::cipher

/** Invoked to begin the login process.
 * We already have the username and password, so just start the login to the GV
 * website. The async completion routine is loginCompleted.
 */
void
MainWindow::doLogin ()
{
    GVAccess &webPage = Singletons::getRef().getGVAccess ();
    QVariantList l;

    bool bOk = false;
    do { // Begin cleanup block (not a loop)
        webPage.setTimeout(60);

        l += strUser;
        l += strPass;

        qDebug ("Logging in...");

        // webPage.workCompleted -> this.loginCompleted
        if (!webPage.enqueueWork (GVAW_login, l, this,
                SLOT (loginCompleted (bool, const QVariantList &))))
        {
            qCritical ("Login returned immediately with failure!");
            break;
        }

        bOk = true;
    } while (0); // End cleanup block (not a loop)

    if (!bOk)
    {
        webPage.setTimeout(20);
        // Cleanup if any
        strUser.clear ();
        strPass.clear ();

        l.clear ();
        logoutCompleted (true, l);

        qApp->quit ();
    }
}//MainWindow::doLogin

void
MainWindow::loginCompleted (bool bOk, const QVariantList &varList)
{
    strSelfNumber.clear ();
    GVAccess &webPage = Singletons::getRef().getGVAccess ();
    webPage.setTimeout(20);

    if (!bOk)
    {
        QVariantList l;
        logoutCompleted (true, l);

        qCritical ("User login failed");
        qApp->quit ();
    }
    else
    {
        qDebug ("User logged in");

        // Save the users GV number returned by the login completion
        strSelfNumber = varList[varList.size()-1].toString ();

        oContacts.setUserPass (strUser, strPass);
        oContacts.loginSuccess ();
        oInbox.loginSuccess ();

        QTimer::singleShot (100, this, SLOT(doWork ()));
    }
}//MainWindow::loginCompleted

void
MainWindow::doLogout ()
{
    GVAccess &webPage = Singletons::getRef().getGVAccess ();
    QVariantList l;
    webPage.enqueueWork (GVAW_logout, l, this,
                         SLOT (logoutCompleted (bool, const QVariantList &)));
}//MainWindow::doLogout

void
MainWindow::logoutCompleted (bool, const QVariantList &)
{
    // This clears out the table and the view as well
    oContacts.loggedOut ();
    oInbox.loggedOut ();
}//MainWindow::logoutCompleted

void
MainWindow::getContactsDone (bool bChanges, bool bOK)
{
    if (bOK && bChanges) {
        qDebug ("Contacts changed, update mosquitto");

        MqPublisher pub(QString("qgvnotify:%1").arg(QHostInfo::localHostName()),
                        m_strMqServer, m_mqPort, m_strMqTopic,
                        this);
        pub.publish ("contact");
    }

    startTimer ();
}//MainWindow::getContactsDone

void
MainWindow::inboxChanged ()
{
    qDebug ("Inbox changed, update mosquitto");

    MqPublisher pub(QString("qgvnotify:%1").arg(QHostInfo::localHostName()),
                    m_strMqServer, m_mqPort, m_strMqTopic,
                    this);
    pub.publish ("inbox");

    startTimer ();
}//MainWindow::inboxChanged

void
MainWindow::startTimer ()
{
    mainTimer.stop ();
    mainTimer.setSingleShot (true);
    mainTimer.setInterval (checkTimeout * 1000);
    mainTimer.start ();
}//MainWindow::startTimer
<|endoftext|>


<commit_before>#include "AutoTest.h"
#include <QString>
#include <QtTest>

class TesttestTest : public QObject
{
    Q_OBJECT

public:
    TesttestTest();

private Q_SLOTS:
    void testCase1();
};

TesttestTest::TesttestTest()
{
}

void TesttestTest::testCase1()
{
    QVERIFY2(true, "Failure");
}

QTEST_APPLESS_MAIN(TesttestTest)

#include "tst_testtesttest.moc"
<commit_msg>Delete tst_downloader.cpp<commit_after><|endoftext|>