#define MODULE_LOG_PREFIX "config"

#include "globals.h"
#include "oscam-conf-mk.h"
#include "oscam-net.h"
#include "oscam-string.h"

const char *shortDay[8] = {"SUN","MON","TUE","WED","THU","FRI","SAT","ALL"};
const char *weekdstr = "SUNMONTUEWEDTHUFRISATALL";

/*
 * Creates a string ready to write as a token into config or WebIf for CAIDs. You must free the returned value through free_mk_t().
 */
char *mk_t_caidtab(CAIDTAB *caidtab)
{
	if (!caidtab || !caidtab->ctnum) return "";

	// Max entry length is cs_strlen("1234&ffff:1234,") == 15
	int32_t i, maxlen = 16 * caidtab->ctnum, pos = 0;

	char *ret;
	if (!cs_malloc(&ret, maxlen))
		return "";

	const char *comma = "";

	for(i = 0; i < caidtab->ctnum; i++)
	{
		CAIDTAB_DATA *d = &caidtab->ctdata[i];
		if (d->caid < 0x0100)
			pos += snprintf(ret + pos, maxlen - pos, "%s%02X", comma, d->caid);
		else
			pos += snprintf(ret + pos, maxlen - pos, "%s%04X", comma, d->caid);
		if (d->mask && d->mask != 0xffff)
			pos += snprintf(ret + pos, maxlen - pos, "&%04X", d->mask);
		if (d->cmap)
			pos += snprintf(ret + pos, maxlen - pos, ":%04X", d->cmap);
		comma = ",";
	}
	return ret;
}

/*
 * Creates a string ready to write as a token into config or WebIf for TunTabs. You must free the returned value through free_mk_t().
 */
char *mk_t_tuntab(TUNTAB *ttab)
{
	if (!ttab || !ttab->ttnum) return "";

	// Each entry max length is cs_strlen("aaaa.bbbb:cccc,") == 15
	int32_t i, maxlen = 16 * ttab->ttnum, pos = 0;

	char *ret;
	if (!cs_malloc(&ret, maxlen))
		return "";

	const char *comma = "";

	for(i = 0; i < ttab->ttnum; i++)
	{
		TUNTAB_DATA *d = &ttab->ttdata[i];
		pos += snprintf(ret + pos, maxlen - pos, "%s%04X", comma, d->bt_caidfrom);
		pos += snprintf(ret + pos, maxlen - pos, ".%04X", d->bt_srvid);
		if (d->bt_caidto)
			pos += snprintf(ret + pos, maxlen - pos, ":%04X", d->bt_caidto);
		comma = ",";
	}
	return ret;
}

/*
 * Creates a string ready to write as a token into config or WebIf for groups. You must free the returned value through free_mk_t().
 */
char *mk_t_group(uint64_t grp)
{
	int32_t i = 0, needed = 1, pos = 0, dot = 0;

	for(i = 0; i < 64; i++)
	{
		if(grp & ((uint64_t)1 << i))
		{
			needed += 2;
			if(i > 9) { needed += 1; }
		}
	}

	char *value;
	if(needed == 1 || !cs_malloc(&value, needed)) { return ""; }
	char *saveptr = value;

	for(i = 0; i < 64; i++)
	{
		if(grp & ((uint64_t)1 << i))
		{
			if(dot == 0)
			{
				snprintf(value + pos, needed - (value - saveptr), "%d", i + 1);
				if(i > 8) { pos += 2; }
				else { pos += 1; }
				dot = 1;
			}
			else
			{
				snprintf(value + pos, needed - (value - saveptr), ",%d", i + 1);
				if(i > 8) { pos += 3; }
				else { pos += 2; }
			}
		}
	}
	value[pos] = '\0';
	return value;
}

/*
 * Creates a string ready to write as a token into config or WebIf for FTabs (CHID, Ident). You must free the returned value through free_mk_t().
 */
char *mk_t_ftab(FTAB *ftab)
{
	if (!ftab || !ftab->nfilts) return "";

	// Worst case scenario where each entry have different
	// caid, ident and only one length in it is cs_strlen("1234:123456,") == 12
	int32_t i, j, maxlen = 13 * ftab->nfilts, pos = 0;

	for(i = 0; i < ftab->nfilts; i++)
	{
		maxlen += ftab->filts[i].nprids * 7; /* cs_strlen("123456,") == 7 */
	}

	char *ret;
	if (!cs_malloc(&ret, maxlen))
		return "";

	const char *semicolon = "", *comma = "";

	for(i = 0; i < ftab->nfilts; i++)
	{
		FILTER *cur = &ftab->filts[i];
		pos += snprintf(ret + pos, maxlen - pos, "%s%04X:", semicolon, cur->caid);
		semicolon = ";";
		comma = "";

		for (j = 0; j < cur->nprids; j++)
		{
			pos += snprintf(ret + pos, maxlen - pos, "%s%06X", comma, cur->prids[j]);
			comma = ",";
		}
	}
	return ret;
}

/*
 * Creates a string ready to write as a token into config or WebIf for the camd35 tcp ports. You must free the returned value through free_mk_t().
 */
char *mk_t_camd35tcp_port(void)
{
#if defined(MODULE_CAMD35) || defined(MODULE_CAMD35_TCP)
	int32_t i, j, pos = 0, needed = 1;

	/* Precheck to determine how long the resulting string will maximally be (might be a little bit smaller but that shouldn't hurt) */
	for(i = 0; i < cfg.c35_tcp_ptab.nports; ++i)
	{
		/* Port is maximally 5 chars long, plus comma, plus the @caid, plus the :provid plus the ";" between ports */
		needed += 18;
		if(cfg.c35_tcp_ptab.ports[i].ncd && cfg.c35_tcp_ptab.ports[i].ncd->ncd_ftab.filts[0].nprids > 0)
		{
			needed += cfg.c35_tcp_ptab.ports[i].ncd->ncd_ftab.filts[0].nprids * 7;
		}
	}

	char *value;
	if(needed == 1 || !cs_malloc(&value, needed)) { return ""; }
	char *saveptr = value;
	char *dot1 = "", *dot2;

	for(i = 0; i < cfg.c35_tcp_ptab.nports; ++i)
	{
		if(cfg.c35_tcp_ptab.ports[i].ncd && cfg.c35_tcp_ptab.ports[i].ncd->ncd_ftab.filts[0].caid)
		{
			pos += snprintf(value + pos, needed - (value - saveptr), "%s%d@%04X", dot1,
							cfg.c35_tcp_ptab.ports[i].s_port,
							cfg.c35_tcp_ptab.ports[i].ncd->ncd_ftab.filts[0].caid);

			if(cfg.c35_tcp_ptab.ports[i].ncd->ncd_ftab.filts[0].nprids > 0)
			{
				dot2 = ":";
				for(j = 0; j < cfg.c35_tcp_ptab.ports[i].ncd->ncd_ftab.filts[0].nprids; ++j)
				{
					pos += snprintf(value + pos, needed - (value - saveptr), "%s%06X",
									dot2, cfg.c35_tcp_ptab.ports[i].ncd->ncd_ftab.filts[0].prids[j]);
					dot2 = ",";
				}
			}
			dot1 = ";";
		}
		else
		{
			pos += snprintf(value + pos, needed - (value - saveptr), "%s%d",
							dot1, cfg.c35_tcp_ptab.ports[i].s_port);
			dot1 = ";";
		}
	}
	return value;
#else
	return NULL;
#endif
}

#ifdef MODULE_CCCAM
/*
 * Creates a string ready to write as a token into config or WebIf for the cccam tcp ports. You must free the returned value through free_mk_t().
 */
char *mk_t_cccam_port(void)
{
	int32_t i, pos = 0, needed = CS_MAXPORTS * 6 + 8;

	char *value;
	if(!cs_malloc(&value, needed)) { return ""; }
	char *dot = "";

	for(i = 0; i < CS_MAXPORTS; i++)
	{
		if(!cfg.cc_port[i]) { break; }

		pos += snprintf(value + pos, needed - pos, "%s%d", dot, cfg.cc_port[i]);
		dot = ",";
	}
	return value;
}
#endif

#ifdef MODULE_GBOX
/*
 * Creates a string ready to write as a token into config or WebIf for the gbox udp ports. You must free the returned value through free_mk_t().
 */
char *mk_t_gbox_port(void)
{
	int32_t i, pos = 0, needed = CS_MAXPORTS * 6 + 8;

	char *value;
	if(!cs_malloc(&value, needed)) { return ""; }
	char *dot = "";

	for(i = 0; i < CS_MAXPORTS; i++)
	{
		if(!cfg.gbox_port[i]) { break; }

		pos += snprintf(value + pos, needed - pos, "%s%d", dot, cfg.gbox_port[i]);
		dot = ",";
	}
	return value;
}

/*
 * Creates a string ready to write as a token into config or WebIf for the gbox proxy card. You must free the returned value through free_mk_t().
 */
char *mk_t_gbox_proxy_card(void)
{
	int32_t i, pos = 0, needed = GBOX_MAX_PROXY_CARDS * 9 + 8;

	char *value;
	if(!cs_malloc(&value, needed)) { return ""; }
	char *dot = "";

	for(i = 0; i < GBOX_MAX_PROXY_CARDS; i++)
	{
		if(!cfg.gbox_proxy_card[i]) { break; }

		pos += snprintf(value + pos, needed - pos, "%s%08lX", dot, cfg.gbox_proxy_card[i]);
		dot = ",";
	}
	return value;
}

/*
 * Creates a string ready to write as a token into config or WebIf for the gbox ignore peer. You must free the returned value through free_mk_t().
 */
char *mk_t_gbox_ignored_peer(void)
{
	int32_t i, pos = 0, needed = GBOX_MAX_IGNORED_PEERS * 5 + 8;

	char *value;
	if(!cs_malloc(&value, needed)) { return ""; }
	char *dot = "";

	for(i = 0; i < GBOX_MAX_IGNORED_PEERS; i++)
	{
		if(!cfg.gbox_ignored_peer[i]) { break; }

		pos += snprintf(value + pos, needed - pos, "%s%04hX", dot, cfg.gbox_ignored_peer[i]);
		dot = ",";
	}
	return value;
}

/*
 * Creates a string ready to write as a token into config or WebIf for the gbox accept_remm_peer. You must free the returned value through free_mk_t().
 */
char *mk_t_accept_remm_peer(void)
{
	int32_t i, pos = 0, needed = GBOX_MAX_REMM_PEERS * 5 + 8;

	char *value;
	if(!cs_malloc(&value, needed)) { return ""; }
	char *dot = "";

	for(i = 0; i < GBOX_MAX_REMM_PEERS; i++)
	{
		if(!cfg.accept_remm_peer[i]) { break; }

		pos += snprintf(value + pos, needed - pos, "%s%04hX", dot, cfg.accept_remm_peer[i]);
		dot = ",";
	}
	return value;
}

/*
 * Creates a string ready to write as a token into config or WebIf for the gbox block ecm. You must free the returned value through free_mk_t().
 */
char *mk_t_gbox_block_ecm(void)
{
	int32_t i, pos = 0, needed = GBOX_MAX_BLOCKED_ECM * 5 + 8;

	char *value;
	if(!cs_malloc(&value, needed)) { return ""; }
	char *dot = "";

	for(i = 0; i < GBOX_MAX_BLOCKED_ECM; i++)
	{
		if(!cfg.gbox_block_ecm[i]) { break; }

		pos += snprintf(value + pos, needed - pos, "%s%04hX", dot, cfg.gbox_block_ecm[i]);
		dot = ",";
	}
	return value;
}
/*
 * Creates a string ready to write as a token into config or WebIf for the gbox SMS dest peers. You must free the returned value through free_mk_t().
 */
char *mk_t_gbox_dest_peers(void)
{
	int32_t i, pos = 0, needed = GBOX_MAX_DEST_PEERS * 5 + 8;

	char *value;
	if(!cs_malloc(&value, needed)) { return ""; }
	char *dot = "";

	for(i = 0; i < GBOX_MAX_DEST_PEERS; i++)
	{
		if(!cfg.gbox_dest_peers[i]) { break; }
		pos += snprintf(value + pos, needed - pos, "%s%04hX", dot, cfg.gbox_dest_peers[i]);
		dot = ",";
	}
	return value;
}
#endif

#ifdef READER_VIACCESS
/*
 * Creates a string ready to write as a token into config or WebIf for AESKeys. You must free the returned value through free_mk_t().
 */
char *mk_t_aeskeys(struct s_reader *rdr)
{
	AES_ENTRY *current = rdr->aes_list;
	int32_t i, pos = 0, needed = 1, prevKeyid = 0, prevCaid = 0;
	uint32_t prevIdent = 0;

	/* Precheck for the approximate size that we will need; it's a bit overestimated but we correct that at the end of the function */
	while(current)
	{
		/* The caid, ident, "@" and the trailing ";" need to be output when they are changing */
		if(prevCaid != current->caid || prevIdent != current->ident) { needed += 12 + (current->keyid * 2); }
		/* "0" keys are not saved so we need to check for gaps */
		else if(prevKeyid != current->keyid + 1) { needed += (current->keyid - prevKeyid - 1) * 2; }
		/* The 32 byte key plus either the (heading) ":" or "," */
		needed += 33;
		prevCaid = current->caid;
		prevIdent = current->ident;
		prevKeyid = current->keyid;
		current = current->next;
	}

	/* Set everything back and now create the string */
	current = rdr->aes_list;
	prevCaid = 0;
	prevIdent = 0;
	prevKeyid = 0;
	char tmp[needed];
	char dot;
	if(needed == 1) { tmp[0] = '\0'; }
	char tmpkey[33];

	while(current)
	{
		/* A change in the ident or caid means that we need to output caid and ident */
		if(prevCaid != current->caid || prevIdent != current->ident)
		{
			if(pos > 0)
			{
				tmp[pos] = ';';
				++pos;
			}
			pos += snprintf(tmp + pos, sizeof(tmp) - pos, "%04X@%06X", current->caid, current->ident);
			prevKeyid = -1;
			dot = ':';
		}
		else { dot = ','; }

		/* "0" keys are not saved so we need to check for gaps and output them! */
		for(i = prevKeyid + 1; i < current->keyid; ++i)
		{
			pos += snprintf(tmp + pos, sizeof(tmp) - pos, "%c0", dot);
			dot = ',';
		}

		tmp[pos] = dot;
		++pos;

		for(i = 0; i < 16; ++i)
		{
			snprintf(tmpkey + (i * 2), sizeof(tmpkey) - (i * 2), "%02X", current->plainkey[i]);
		}

		/* A key consisting of only FFs has a special meaning (just return what the card outputted) and can be specified more compact */
		if(strcmp(tmpkey, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF") == 0)
		{
			pos += snprintf(tmp + pos, sizeof(tmp) - pos, "FF");
		}
		else
		{
			pos += snprintf(tmp + pos, sizeof(tmp) - pos, "%s", tmpkey);
		}

		prevCaid = current->caid;
		prevIdent = current->ident;
		prevKeyid = current->keyid;
		current = current->next;
	}

	/* copy to result array of correct size */
	char *value;
	if(pos == 0 || !cs_malloc(&value, pos + 1)) { return ""; }
	memcpy(value, tmp, pos + 1);
	return (value);
}
#endif

/*
 * Creates a string ready to write as a token into config or WebIf for the Newcamd Port. You must free the returned value through free_mk_t().
 */
char *mk_t_newcamd_port(void)
{
#ifdef MODULE_NEWCAMD
	int32_t i, j, k, pos = 0, needed = 1;

	/* Precheck to determine how long the resulting string will maximally be (might be a little bit smaller but that shouldn't hurt) */
	for(i = 0; i < cfg.ncd_ptab.nports; ++i)
	{
		/* Port is maximally 5 chars long, plus the @caid, plus the ";" between ports */
		needed += 11;
		if(cfg.ncd_ptab.ports[i].ncd)
		{
			if(cfg.ncd_ptab.ports[i].ncd->ncd_key_is_set) { needed += 30; }

			if(cfg.ncd_ptab.ports[i].ncd->ncd_ftab.filts[0].nprids > 0)
			{
				needed += cfg.ncd_ptab.ports[i].ncd->ncd_ftab.filts[0].nprids * 7;
			}
		}
	}
	char *value;
	if(needed == 1 || !cs_malloc(&value, needed)) { return ""; }
	char *dot1 = "", *dot2;

	for(i = 0; i < cfg.ncd_ptab.nports; ++i)
	{
		pos += snprintf(value + pos, needed - pos,  "%s%d", dot1, cfg.ncd_ptab.ports[i].s_port);

		// separate DES Key for this port
		if(cfg.ncd_ptab.ports[i].ncd)
		{
			if(cfg.ncd_ptab.ports[i].ncd->ncd_key_is_set)
			{
				pos += snprintf(value + pos, needed - pos, "{");
				for(k = 0; k < (int32_t)sizeof(cfg.ncd_ptab.ports[i].ncd->ncd_key); k++)
				{
					pos += snprintf(value + pos, needed - pos, "%02X", cfg.ncd_ptab.ports[i].ncd->ncd_key[k]);
				}
				pos += snprintf(value + pos, needed - pos, "}");
			}

			pos += snprintf(value + pos, needed - pos, "@%04X", cfg.ncd_ptab.ports[i].ncd->ncd_ftab.filts[0].caid);

			if(cfg.ncd_ptab.ports[i].ncd->ncd_ftab.filts[0].nprids > 0)
			{
				dot2 = ":";
				for(j = 0; j < cfg.ncd_ptab.ports[i].ncd->ncd_ftab.filts[0].nprids; ++j)
				{
					pos += snprintf(value + pos, needed - pos, "%s%06X", dot2, (int)cfg.ncd_ptab.ports[i].ncd->ncd_ftab.filts[0].prids[j]);
					dot2 = ",";
				}
			}
		}
		dot1 = ";";
	}
	return value;
#else
	return NULL;
#endif
}

/*
 * Creates a string ready to write as a token into config or WebIf for au readers. You must free the returned value through free_mk_t().
 */
char *mk_t_aureader(struct s_auth *account)
{
	int32_t pos = 0;
	char *dot = "";

	char *value;
	if(ll_count(account->aureader_list) == 0 || !cs_malloc(&value, 256)) { return ""; }
	value[0] = '\0';

	struct s_reader *rdr;
	LL_ITER itr = ll_iter_create(account->aureader_list);
	while((rdr = ll_iter_next(&itr)))
	{
		pos += snprintf(value + pos, 256 - pos, "%s%s", dot, rdr->label);
		dot = ",";
	}

	return value;
}

/*
 * Creates a string ready to write as a token into config or WebIf for blocknano and savenano. You must free the returned value through free_mk_t().
 */
char *mk_t_nano(uint16_t nano)
{
	int32_t i, pos = 0, needed = 0;

	for(i = 0; i < 16; i++)
	{
		if((1 << i) & nano)
			{ needed++; }
	}

	char *value;
	if(nano == 0xFFFF)
	{
		if(!cs_malloc(&value, 4)) { return ""; }
		snprintf(value, 4, "all");
	}
	else
	{
		if(needed == 0 || !cs_malloc(&value, needed * 3 + 1)) { return ""; }

		value[0] = '\0';
		for(i = 0; i < 16; i++)
		{
			if((1 << i) & nano)
				{ pos += snprintf(value + pos, (needed * 3) + 1 - pos, "%s%02x", pos ? "," : "", (i + 0x80)); }
		}
	}
	return value;
}

/*
 * Creates a string ready to write as a token into config or WebIf for the sidtab. You must free the returned value through free_mk_t().
 */
char *mk_t_service(SIDTABS *sidtabs)
{
	int32_t i, pos;
	char *dot;
	char *value;
	struct s_sidtab *sidtab = cfg.sidtab;
	if(!sidtab || (!sidtabs->ok && !sidtabs->no) || !cs_malloc(&value, 1024)) { return ""; }

	value[0] = '\0';

	for(i = pos = 0, dot = ""; sidtab; sidtab = sidtab->next, i++)
	{
		if(sidtabs->ok & ((SIDTABBITS)1 << i))
		{
			pos += snprintf(value + pos, 1024 - pos, "%s%s", dot, sidtab->label);
			dot = ",";
		}
		if(sidtabs->no & ((SIDTABBITS)1 << i))
		{
			pos += snprintf(value + pos, 1024 - pos, "%s!%s", dot, sidtab->label);
			dot = ",";
		}
	}
	return value;
}

/*
 * Creates a string ready to write as a token into config or WebIf for the logfile parameter. You must free the returned value through free_mk_t().
 */
char *mk_t_logfile(void)
{
	int32_t pos = 0, needed = 1;
	char *value, *dot = "";

	if(cfg.logtostdout == 1) { needed += 7; }
	if(cfg.logtosyslog == 1) { needed += 7; }
	if(cfg.logfile) { needed += cs_strlen(cfg.logfile); }
	if(needed == 1 || !cs_malloc(&value, needed)) { return ""; }

	if(cfg.logtostdout == 1)
	{
		pos += snprintf(value + pos, needed - pos, "stdout");
		dot = ";";
	}

	if(cfg.logtosyslog == 1)
	{
		pos += snprintf(value + pos, needed - pos, "%ssyslog", dot);
		dot = ";";
	}

	if(cfg.logfile)
	{
		snprintf(value + pos, needed - pos, "%s%s", dot, cfg.logfile);
	}
	return value;
}

/*
 * Creates a string ready to write as a token into config or WebIf for the ecm whitelist. You must free the returned value through free_mk_t().
 */
char *mk_t_ecm_whitelist(struct s_ecm_whitelist *ecm_whitelist)
{
	if (!ecm_whitelist || !ecm_whitelist->ewnum) return "";

	// Worst case scenario where each entry have different
	// caid, ident and only one length in it is cs_strlen("1234@123456:01;") == 15
	int32_t i, maxlen = 16 * ecm_whitelist->ewnum, pos = 0;
	char *ret;
	if (!cs_malloc(&ret, maxlen))
		return "";

	const char *semicolon = "", *comma = "";
	ECM_WHITELIST_DATA *last = NULL;

	for(i = 0; i < ecm_whitelist->ewnum; i++)
	{
		ECM_WHITELIST_DATA *cur = &ecm_whitelist->ewdata[i];
		bool change = !last || last->caid != cur->caid || last->ident != cur->ident;

		if(change)
		{
			if(cur->caid && cur->ident)
				pos += snprintf(ret + pos, maxlen - pos, "%s%04X@%06X:", semicolon, cur->caid, cur->ident);
			else if(cur->caid)
				pos += snprintf(ret + pos, maxlen - pos, "%s%04X:", semicolon, cur->caid);
			else if(cur->ident)
				pos += snprintf(ret + pos, maxlen - pos, "%s@%06X:", semicolon, cur->ident);
			else
				pos += snprintf(ret + pos, maxlen - pos, "%s", semicolon);

			semicolon = ";";
			comma = "";
		}
		pos += snprintf(ret + pos, maxlen - pos, "%s%02X", comma, cur->len);
		comma = ",";
		last = &ecm_whitelist->ewdata[i];
	}
	return ret;
}

/*
 * Creates a string ready to write as a token into config or WebIf for the ECM Headerwhitelist. You must free the returned value through free_mk_t().
 */
char *mk_t_ecm_hdr_whitelist(struct s_ecm_hdr_whitelist *ecm_hdr_whitelist)
{
	if(!ecm_hdr_whitelist || !ecm_hdr_whitelist->ehnum) return "";

	// Worst case scenario where each entry have different
	// caid, provid and only one header in it is cs_strlen("1234@123456:0102030405060708091011121314151617181920;") == 52 ((sizeof(header) / 2) + 12)
	int32_t i, r, maxlen = 53 * ecm_hdr_whitelist->ehnum, pos = 0;
	char *ret;
	if (!cs_malloc(&ret, maxlen))
		return "";

	const char *semicolon = "", *comma = "";
	ECM_HDR_WHITELIST_DATA *last = NULL;

	for(i = 0; i < ecm_hdr_whitelist->ehnum; i++)
	{
		ECM_HDR_WHITELIST_DATA *cur = &ecm_hdr_whitelist->ehdata[i];
		bool change = !last || last->caid != cur->caid || last->provid != cur->provid;
		if (change)
		{
			if (cur->caid && cur->provid)
				pos += snprintf(ret + pos, maxlen - pos, "%s%04X@%06X:", semicolon, cur->caid, cur->provid);
			else if (cur->caid)
				pos += snprintf(ret + pos, maxlen - pos, "%s%04X:", semicolon, cur->caid);
			else if (cur->provid)
				pos += snprintf(ret + pos, maxlen - pos, "%s@%06X:", semicolon, cur->provid);
			else
				pos += snprintf(ret + pos, maxlen - pos, "%s", semicolon);

			semicolon = ";";
			comma = "";
		}
		pos += snprintf(ret + pos, maxlen - pos, "%s", comma);
		for(r = 0; r < cur->len / 2; r++)
			pos += snprintf(ret + pos, maxlen - pos, "%02X", cur->header[r]);
		comma = ",";
		last = &ecm_hdr_whitelist->ehdata[i];
	}
	return ret;
}

/*
 * Creates a string ready to write as a token into config or WebIf for an iprange. You must free the returned value through free_mk_t().
 */
char *mk_t_iprange(struct s_ip *range)
{
	struct s_ip *cip;
	char *value, *dot = "";
	int32_t needed = 1, pos = 0;
	for(cip = range; cip; cip = cip->next) { needed += 64; }

	char tmp[needed];

	for(cip = range; cip; cip = cip->next)
	{
		pos += snprintf(tmp + pos, needed - pos, "%s%s", dot, cs_inet_ntoa(cip->ip[0]));
		if(!IP_EQUAL(cip->ip[0], cip->ip[1]))  { pos += snprintf(tmp + pos, needed - pos, "-%s", cs_inet_ntoa(cip->ip[1])); }
		dot = ",";
	}

	if(pos == 0 || !cs_malloc(&value, pos + 1)) { return ""; }

	memcpy(value, tmp, pos + 1);
	return value;
}

/*
 * Creates a string ready to write as a token into config or WebIf for the class attribute. You must free the returned value through free_mk_t().
 */
char *mk_t_cltab(CLASSTAB *clstab)
{
	char *value, *dot = "";
	int32_t i, needed = 1, pos = 0;
	for(i = 0; i < clstab->an; ++i) { needed += 3; }
	for(i = 0; i < clstab->bn; ++i) { needed += 4; }

	char tmp[needed];

	for(i = 0; i < clstab->an; ++i)
	{
		pos += snprintf(tmp + pos, needed - pos, "%s%02x", dot, (int32_t)clstab->aclass[i]);
		dot = ",";
	}

	for(i = 0; i < clstab->bn; ++i)
	{
		pos += snprintf(tmp + pos, needed - pos, "%s!%02x", dot, (int32_t)clstab->bclass[i]);
		dot = ",";
	}

	if(pos == 0 || !cs_malloc(&value, pos + 1)) { return ""; }

	memcpy(value, tmp, pos + 1);
	return value;
}

/*
 * Creates a string ready to write as a token into config or WebIf. You must free the returned value through free_mk_t().
 */
char *mk_t_caidvaluetab(CAIDVALUETAB *caidvaluetab)
{
	if (!caidvaluetab || !caidvaluetab->cvnum) return "";

	// Max entry length is cs_strlen("1234@65535,") == 11
	int32_t i, maxlen = 12 * caidvaluetab->cvnum, pos = 0;
	char *ret;
	if (!cs_malloc(&ret, maxlen))
		return "";
	const char *comma = "";

	for(i = 0; i < caidvaluetab->cvnum; i++)
	{
		CAIDVALUETAB_DATA *d = &caidvaluetab->cvdata[i];
		if (d->caid < 0x0100)
			pos += snprintf(ret + pos, maxlen - pos, "%s%02X:%d", comma, d->caid, d->value);
		else
			pos += snprintf(ret + pos, maxlen - pos, "%s%04X:%d", comma, d->caid, d->value);

		comma = ",";
	}
	return ret;
}

char *mk_t_cacheex_valuetab(CECSPVALUETAB *tab)
{
	if (!tab || !tab->cevnum) return "";

	int32_t i, size = 2 + tab->cevnum * (4 + 1 + 4 + 1 + 6 + 1 + 4 + 1 + 5 + 1 + 5 + 1); // caid&mask@provid$servid:awtime:dwtime","

	char *buf;
	if(!cs_malloc(&buf, size))
		{ return ""; }

	char *ptr = buf;

	for(i = 0; i < tab->cevnum; i++)
	{
		CECSPVALUETAB_DATA *d = &tab->cevdata[i];

		if(i) { ptr += snprintf(ptr, size - (ptr - buf), ","); }

		if(d->caid >= 0)
		{
			if(d->caid == 0)
			{
				if(d->awtime > 0)
					{ ptr += snprintf(ptr, size - (ptr - buf), "%d", d->caid); }
			}
			else if(d->caid < 256) // Do not format 0D as 000D, its a shortcut for 0Dxx:
			{
				ptr += snprintf(ptr, size - (ptr - buf), "%02X", d->caid);
			}
			else
			{
				ptr += snprintf(ptr, size - (ptr - buf), "%04X", d->caid);
			}
		}

		if(d->cmask >= 0)
			{ ptr += snprintf(ptr, size - (ptr - buf), "&%04X", d->cmask); }

		if(d->prid >= 0)
			{ ptr += snprintf(ptr, size - (ptr - buf), "@%06X", d->prid); }

		if(d->srvid >= 0)
			{ ptr += snprintf(ptr, size - (ptr - buf), "$%04X", d->srvid); }

		if(d->awtime > 0)
			{ ptr += snprintf(ptr, size - (ptr - buf), ":%d", d->awtime); }

		if(!(d->dwtime > 0))
			{ ptr += snprintf(ptr, size - (ptr - buf), ":0"); }

		if(d->dwtime > 0)
		{
			if((d->caid <= 0) && (d->prid == -1) && (d->srvid == -1) && (d->srvid == -1) && (d->awtime <= 0))
			{
				ptr += snprintf(ptr, size - (ptr - buf), "%d", d->dwtime);
			}
			else
			{
				ptr += snprintf(ptr, size - (ptr - buf), ":%d", d->dwtime);
			}
		}
	}
	*ptr = 0;
	return buf;
}


char *mk_t_cacheex_cwcheck_valuetab(CWCHECKTAB *tab)
{
	if(!tab || !tab->cwchecknum) { return ""; }

	int32_t i, size = 2 + tab->cwchecknum * (4 + 1 + 4 + 1 + 6 + 1 + 4 + 1 + 5 + 1 + 5 + 1); // caid[&mask][@provid][$servid]:mode:counter","
	char *buf;
	if(!cs_malloc(&buf, size))
		{ return ""; }
	char *ptr = buf;

	for(i = 0; i < tab->cwchecknum; i++)
	{
		CWCHECKTAB_DATA *d = &tab->cwcheckdata[i];

		if(i) { ptr += snprintf(ptr, size - (ptr - buf), ","); }

		if(d->caid >= 0)
		{
			if(d->caid == 0)
			{
				ptr += snprintf(ptr, size - (ptr - buf), "%d", d->caid);
			}
			else if(d->caid < 256) // Do not format 0D as 000D, its a shortcut for 0Dxx:
			{
				ptr += snprintf(ptr, size - (ptr - buf), "%02X", d->caid);
			}
			else
			{
				ptr += snprintf(ptr, size - (ptr - buf), "%04X", d->caid);
			}
		}

		if(d->cmask >= 0)
			{ ptr += snprintf(ptr, size - (ptr - buf), "&%04X", d->cmask); }

		if(d->prid >= 0)
			{ ptr += snprintf(ptr, size - (ptr - buf), "@%06X", d->prid); }

		if(d->srvid >= 0)
			{ ptr += snprintf(ptr, size - (ptr - buf), "$%04X", d->srvid); }

		if(d->mode >= 0)
			{ ptr += snprintf(ptr, size - (ptr - buf), ":%d", d->mode); }

		if(d->counter > 0)
			{ ptr += snprintf(ptr, size - (ptr - buf), ":%d", d->counter); }
	}
	*ptr = 0;
	return buf;
}

char *mk_t_cacheex_hitvaluetab(CECSPVALUETAB *tab)
{
	if (!tab || !tab->cevnum) return "";

	int32_t i, size = 2 + tab->cevnum * (4 + 1 + 4 + 1 + 6 + 1 + 4 + 1); // caid&mask@provid$servid","
	char *buf;
	if(!cs_malloc(&buf, size))
		{ return ""; }
	char *ptr = buf;

	for(i = 0; i < tab->cevnum; i++)
	{
		CECSPVALUETAB_DATA *d = &tab->cevdata[i];

		if(i) { ptr += snprintf(ptr, size - (ptr - buf), ","); }

		if(d->caid > 0)
		{
			if(d->caid < 256) // Do not format 0D as 000D, its a shortcut for 0Dxx:
			{
				ptr += snprintf(ptr, size - (ptr - buf), "%02X", d->caid);
			}
			else
			{
				ptr += snprintf(ptr, size - (ptr - buf), "%04X", d->caid);
			}

			if(d->cmask >= 0)
				{ ptr += snprintf(ptr, size - (ptr - buf), "&%04X", d->cmask); }

			if(d->prid >= 0)
				{ ptr += snprintf(ptr, size - (ptr - buf), "@%06X", d->prid); }

			if(d->srvid >= 0)
				{ ptr += snprintf(ptr, size - (ptr - buf), "$%04X", d->srvid); }
		}
	}
	*ptr = 0;
	return buf;
}

/*
 * returns string of comma separated values
 */
char *mk_t_emmbylen(struct s_reader *rdr)
{
	char *value, *pos, *dot = "";
	int32_t num, needed = 0;
	struct s_emmlen_range *blocklen;

	if(!rdr->blockemmbylen)
		{ return ""; }

	LL_ITER it = ll_iter_create(rdr->blockemmbylen);
	while((blocklen = ll_iter_next(&it)))
	{
		needed += 5 + 1; // max digits of int16 + ","
		if(blocklen->max == 0)
			{ needed += 1 + 1; } // "-" + ","
		else if(blocklen->min != blocklen->max)
			{ needed += 1 + 5 + 1; } // "-" + max digits of int16 + ","
	}

	// the trailing zero is already included: it's the first ","
	if(!cs_malloc(&value, needed))
		{ return ""; }

	pos = value;
	ll_iter_reset(&it);
	while((blocklen = ll_iter_next(&it)))
	{
		if(blocklen->min == blocklen->max)
			{ num = snprintf(pos, needed, "%s%d", dot, blocklen->min); }
		else if(blocklen->max == 0)
			{ num = snprintf(pos, needed, "%s%d-", dot, blocklen->min); }
		else
			{ num = snprintf(pos, needed, "%s%d-%d", dot, blocklen->min, blocklen->max); }

		pos += num;
		needed -= num;
		dot = ",";
	}
	return value;
}

/*
 * makes string from binary structure
 */
char *mk_t_allowedprotocols(struct s_auth *account)
{
	if(!account->allowedprotocols)
		{ return ""; }

	int16_t i, tmp = 1, pos = 0, needed = 255, tagcnt;
	char *tag[] = {"camd33", "cs357x", "cs378x", "newcamd", "cccam", "gbox", "radegast", "dvbapi", "constcw", "serial"};
	char *value, *dot = "";

	if(!cs_malloc(&value, needed))
		{ return ""; }

	tagcnt = sizeof(tag) / sizeof(char *);
	for(i = 0; i < tagcnt; i++)
	{
		if((account->allowedprotocols & tmp) == tmp)
		{
			pos += snprintf(value + pos, needed, "%s%s", dot, tag[i]);
			dot = ",";
		}
		tmp = tmp << 1;
	}
	return value;
}

/*
 * return allowed time frame string from internal array content
 *
 */

char *mk_t_allowedtimeframe(struct s_auth *account)
{
	char *result;
	if(!cs_malloc(&result, MAXALLOWEDTF))
		{ return ""; }

	if(account->allowedtimeframe_set)
	{
		char mytime[8];
		uint8_t day;
		uint8_t value_in_day = 0;
		uint8_t intime = 0;
		uint8_t minutes =0;
		uint8_t hours;
		char septime[2] = {'\0'};
		char sepday[2] = {'\0'};

		for(day = 0; day < SIZE_SHORTDAY; day++)
		{
			for(hours = 0; hours < 24; hours++)
			{
				for(minutes = 0; minutes < 60; minutes++)
				{
					if(CHECK_BIT(account->allowedtimeframe[day][hours][minutes / 30], (minutes % 30)))
					{
						if(value_in_day == 0)
						{
							cs_strncat(result, &sepday[0], MAXALLOWEDTF);
							cs_strncat(result, (char *)shortDay[day], MAXALLOWEDTF);
							cs_strncat(result, "@", MAXALLOWEDTF);
							value_in_day = 1;
							intime = 0;
							sepday[0] = ';';
							septime[0] = '\0';
						}

						if(!intime)
						{
							cs_strncat(result, &septime[0], MAXALLOWEDTF);
							snprintf(mytime, sizeof(mytime), "%02d:%02d", hours, minutes);
							cs_strncat(result, mytime, MAXALLOWEDTF);
							cs_strncat(result, "-", MAXALLOWEDTF);
							septime[0] = ',';
							intime = 1;
						}

						// Special case 23H59 is enabled we close the day at 24H00
						if(((hours * 60) + minutes) == 1439)
						{
							cs_strncat(result, "24:00", MAXALLOWEDTF);
							intime = 0;
							septime[0] = '\0';
							value_in_day = 0;
						}
					}
					else if(intime)
					{
						snprintf(mytime, sizeof(mytime), "%02d:%02d", hours, minutes);
						cs_strncat(result, mytime, MAXALLOWEDTF);
						septime[0] = ',';
						intime = 0;
					}
				}
			}
			value_in_day = 0;
		}
	}
	else
	{
		result = "";
	}
	return result;
}

/*
 * mk_t-functions give back a constant empty string when allocation fails or when the result is an empty string.
 * This function thus checks the stringlength and only frees if necessary.
 */
char *mk_t_cwvote_caidtab(CW_VOTE_CAID_TAB *cwvote_caid_table)
{
	if (!cwvote_caid_table || !cwvote_caid_table->cvcnum) { return ""; }

	// Max entry length is cs_strlen("0000,") == 5
	int32_t i, maxlen = 6 * cwvote_caid_table->cvcnum, pos = 0;
	char *ret;
	if (!cs_malloc(&ret, maxlen))
		{ return ""; }

	const char *comma = "";

	for (i = 0; i < cwvote_caid_table->cvcnum; i++)
	{
		pos += snprintf(ret + pos, maxlen - pos, "%s%04X", comma, cwvote_caid_table->cvcdata[i].caid);
		comma = ",";
	}
	return ret;
}

void free_mk_t(char *value)
{
	if(cs_strlen(value) > 0) { NULLFREE(value); }
}
