Blame src/parser.c

Packit 1422b7
/*
Packit 1422b7
 * liblognorm - a fast samples-based log normalization library
Packit 1422b7
 * Copyright 2010-2018 by Rainer Gerhards and Adiscon GmbH.
Packit 1422b7
 *
Packit 1422b7
 * Modified by Pavel Levshin (pavel@levshin.spb.ru) in 2013
Packit 1422b7
 *
Packit 1422b7
 * This file is part of liblognorm.
Packit 1422b7
 *
Packit 1422b7
 * This library is free software; you can redistribute it and/or
Packit 1422b7
 * modify it under the terms of the GNU Lesser General Public
Packit 1422b7
 * License as published by the Free Software Foundation; either
Packit 1422b7
 * version 2.1 of the License, or (at your option) any later version.
Packit 1422b7
 *
Packit 1422b7
 * This library is distributed in the hope that it will be useful,
Packit 1422b7
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 1422b7
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 1422b7
 * Lesser General Public License for more details.
Packit 1422b7
 *
Packit 1422b7
 * You should have received a copy of the GNU Lesser General Public
Packit 1422b7
 * License along with this library; if not, write to the Free Software
Packit 1422b7
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit 1422b7
 *
Packit 1422b7
 * A copy of the LGPL v2.1 can be found in the file "COPYING" in this distribution.
Packit 1422b7
 */
Packit 1422b7
#include "config.h"
Packit 1422b7
#include <stdlib.h>
Packit 1422b7
#include <stdio.h>
Packit 1422b7
#include <stdarg.h>
Packit 1422b7
#include <assert.h>
Packit 1422b7
#include <ctype.h>
Packit 1422b7
#include <sys/types.h>
Packit 1422b7
#include <string.h>
Packit 1422b7
#include <strings.h>
Packit 1422b7
#include <errno.h>
Packit 1422b7
#include <inttypes.h>
Packit 1422b7
#include <time.h>
Packit 1422b7
Packit 1422b7
#include "liblognorm.h"
Packit 1422b7
#include "lognorm.h"
Packit 1422b7
#include "internal.h"
Packit 1422b7
#include "parser.h"
Packit 1422b7
#include "samp.h"
Packit 1422b7
#include "helpers.h"
Packit 1422b7
Packit 1422b7
#ifdef FEATURE_REGEXP
Packit 1422b7
#include <pcre.h>
Packit 1422b7
#include <errno.h>
Packit 1422b7
#endif
Packit 1422b7
Packit 1422b7
Packit 1422b7
/* how should output values be formatted? */
Packit 1422b7
enum FMT_MODE {
Packit 1422b7
	FMT_AS_STRING = 0,
Packit 1422b7
	FMT_AS_NUMBER = 1,
Packit 1422b7
	FMT_AS_TIMESTAMP_UX = 2,
Packit 1422b7
	FMT_AS_TIMESTAMP_UX_MS = 3
Packit 1422b7
	};
Packit 1422b7
Packit 1422b7
/* some helpers */
Packit 1422b7
static inline int
Packit 1422b7
hParseInt(const unsigned char **buf, size_t *lenBuf)
Packit 1422b7
{
Packit 1422b7
	const unsigned char *p = *buf;
Packit 1422b7
	size_t len = *lenBuf;
Packit 1422b7
	int i = 0;
Packit 1422b7
Packit 1422b7
	while(len > 0 && myisdigit(*p)) {
Packit 1422b7
		i = i * 10 + *p - '0';
Packit 1422b7
		++p;
Packit 1422b7
		--len;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	*buf = p;
Packit 1422b7
	*lenBuf = len;
Packit 1422b7
	return i;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/* parser _parse interface
Packit 1422b7
 *
Packit 1422b7
 * All parsers receive
Packit 1422b7
 *
Packit 1422b7
 * @param[in] npb->str the to-be-parsed string
Packit 1422b7
 * @param[in] npb->strLen length of the to-be-parsed string
Packit 1422b7
 * @param[in] offs an offset into the string
Packit 1422b7
 * @param[in] pointer to parser data block
Packit 1422b7
 * @param[out] parsed bytes
Packit 1422b7
 * @param[out] value ptr to json object containing parsed data
Packit 1422b7
 *             (can be unused, but if used *value MUST be NULL on entry)
Packit 1422b7
 *
Packit 1422b7
 * They will try to parse out "their" object from the string. If they
Packit 1422b7
 * succeed, they:
Packit 1422b7
 *
Packit 1422b7
 * return 0 on success and LN_WRONGPARSER if this parser could
Packit 1422b7
 *           not successfully parse (but all went well otherwise) and something
Packit 1422b7
 *           else in case of an error.
Packit 1422b7
 */
Packit 1422b7
#define PARSER_Parse(ParserName) \
Packit 1422b7
int ln_v2_parse##ParserName( \
Packit 1422b7
	npb_t *const npb, \
Packit 1422b7
	size_t *const offs,       \
Packit 1422b7
	__attribute__((unused)) void *const pdata, \
Packit 1422b7
	size_t *parsed,                                      \
Packit 1422b7
	struct json_object **value) \
Packit 1422b7
{ \
Packit 1422b7
	int r = LN_WRONGPARSER; \
Packit 1422b7
	*parsed = 0;
Packit 1422b7
Packit 1422b7
#define FAILParser \
Packit 1422b7
	goto parserdone; /* suppress warnings */ \
Packit 1422b7
parserdone: \
Packit 1422b7
	r = 0; \
Packit 1422b7
	goto done; /* suppress warnings */ \
Packit 1422b7
done:
Packit 1422b7
Packit 1422b7
#define ENDFailParser \
Packit 1422b7
	return r; \
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/* Return printable representation of parser content for
Packit 1422b7
 * display purposes. This must not be 100% exact, but provide
Packit 1422b7
 * a good indication of what it contains for a human.
Packit 1422b7
 * @param[data] data parser data block
Packit 1422b7
 * @return pointer to c string, NOT to be freed
Packit 1422b7
 */
Packit 1422b7
#define PARSER_DataForDisplay(ParserName) \
Packit 1422b7
const char * ln_DataForDisplay##ParserName(__attribute__((unused)) ln_ctx ctx, void *const pdata)
Packit 1422b7
Packit 1422b7
Packit 1422b7
/* Return JSON parser config. This is primarily for comparison
Packit 1422b7
 * of parser equalness.
Packit 1422b7
 * @param[data] data parser data block
Packit 1422b7
 * @return pointer to c string, NOT to be freed
Packit 1422b7
 */
Packit 1422b7
#define PARSER_JsonConf(ParserName) \
Packit 1422b7
const char * ln_JsonConf##ParserName(__attribute__((unused)) ln_ctx ctx, void *const pdata)
Packit 1422b7
Packit 1422b7
Packit 1422b7
/* parser constructor
Packit 1422b7
 * @param[in] json config json items
Packit 1422b7
 * @param[out] data parser data block (to be allocated)
Packit 1422b7
 * At minimum, *data must be set to NULL
Packit 1422b7
 * @return error status (0 == OK)
Packit 1422b7
 */
Packit 1422b7
#define PARSER_Construct(ParserName) \
Packit 1422b7
int ln_construct##ParserName( \
Packit 1422b7
	__attribute__((unused)) ln_ctx ctx, \
Packit 1422b7
	__attribute__((unused)) json_object *const json, \
Packit 1422b7
	void **pdata)
Packit 1422b7
Packit 1422b7
/* parser destructor
Packit 1422b7
 * @param[data] data parser data block (to be de-allocated)
Packit 1422b7
 */
Packit 1422b7
#define PARSER_Destruct(ParserName) \
Packit 1422b7
void ln_destruct##ParserName(__attribute__((unused)) ln_ctx ctx, void *const pdata)
Packit 1422b7
Packit 1422b7
Packit 1422b7
/* the following table saves us from computing an additional date to get
Packit 1422b7
 * the ordinal day of the year - at least from 1967-2099
Packit 1422b7
 * Note: non-2038+ compliant systems (Solaris) will generate compiler
Packit 1422b7
 * warnings on the post 2038-rollover years.
Packit 1422b7
 */
Packit 1422b7
static const int yearInSec_startYear = 1967;
Packit 1422b7
/* for x in $(seq 1967 2099) ; do
Packit 1422b7
 *   printf %s', ' $(date --date="Dec 31 ${x} UTC 23:59:59" +%s)
Packit 1422b7
 * done |fold -w 70 -s */
Packit 1422b7
static const time_t yearInSecs[] = {
Packit 1422b7
	-63158401, -31536001, -1, 31535999, 63071999, 94694399, 126230399,
Packit 1422b7
	157766399, 189302399, 220924799, 252460799, 283996799, 315532799,
Packit 1422b7
	347155199, 378691199, 410227199, 441763199, 473385599, 504921599,
Packit 1422b7
	536457599, 567993599, 599615999, 631151999, 662687999, 694223999,
Packit 1422b7
	725846399, 757382399, 788918399, 820454399, 852076799, 883612799,
Packit 1422b7
	915148799, 946684799, 978307199, 1009843199, 1041379199, 1072915199,
Packit 1422b7
	1104537599, 1136073599, 1167609599, 1199145599, 1230767999,
Packit 1422b7
	1262303999, 1293839999, 1325375999, 1356998399, 1388534399,
Packit 1422b7
	1420070399, 1451606399, 1483228799, 1514764799, 1546300799,
Packit 1422b7
	1577836799, 1609459199, 1640995199, 1672531199, 1704067199,
Packit 1422b7
	1735689599, 1767225599, 1798761599, 1830297599, 1861919999,
Packit 1422b7
	1893455999, 1924991999, 1956527999, 1988150399, 2019686399,
Packit 1422b7
	2051222399, 2082758399, 2114380799, 2145916799, 2177452799,
Packit 1422b7
	2208988799, 2240611199, 2272147199, 2303683199, 2335219199,
Packit 1422b7
	2366841599, 2398377599, 2429913599, 2461449599, 2493071999,
Packit 1422b7
	2524607999, 2556143999, 2587679999, 2619302399, 2650838399,
Packit 1422b7
	2682374399, 2713910399, 2745532799, 2777068799, 2808604799,
Packit 1422b7
	2840140799, 2871763199, 2903299199, 2934835199, 2966371199,
Packit 1422b7
	2997993599, 3029529599, 3061065599, 3092601599, 3124223999,
Packit 1422b7
	3155759999, 3187295999, 3218831999, 3250454399, 3281990399,
Packit 1422b7
	3313526399, 3345062399, 3376684799, 3408220799, 3439756799,
Packit 1422b7
	3471292799, 3502915199, 3534451199, 3565987199, 3597523199,
Packit 1422b7
	3629145599, 3660681599, 3692217599, 3723753599, 3755375999,
Packit 1422b7
	3786911999, 3818447999, 3849983999, 3881606399, 3913142399,
Packit 1422b7
	3944678399, 3976214399, 4007836799, 4039372799, 4070908799,
Packit 1422b7
	4102444799};
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * convert syslog timestamp to time_t
Packit 1422b7
 * Note: it would be better to use something similar to mktime() here.
Packit 1422b7
 * Unfortunately, mktime() semantics are problematic: first of all, it
Packit 1422b7
 * works on local time, on the machine's time zone. In syslog, we have
Packit 1422b7
 * to deal with multiple time zones at once, so we cannot plainly rely
Packit 1422b7
 * on the local zone, and so we cannot rely on mktime(). One solution would
Packit 1422b7
 * be to refactor all time-related functions so that they are all guarded
Packit 1422b7
 * by a mutex to ensure TZ consistency (which would also enable us to
Packit 1422b7
 * change the TZ at will for specific function calls). But that would
Packit 1422b7
 * potentially mean a lot of overhead.
Packit 1422b7
 * Also, mktime() has some side effects, at least setting of tzname. With
Packit 1422b7
 * a refactoring as described above that should probably not be a problem,
Packit 1422b7
 * but would also need more work. For some more thoughts on this topic,
Packit 1422b7
 * have a look here:
Packit 1422b7
 * http://stackoverflow.com/questions/18355101/is-standard-c-mktime-thread-safe-on-linux
Packit 1422b7
 * In conclusion, we keep our own code for generating the unix timestamp.
Packit 1422b7
 * rgerhards, 2016-03-02 (taken from rsyslog sources)
Packit 1422b7
 */
Packit 1422b7
static time_t
Packit 1422b7
syslogTime2time_t(const int year, const int month, const int day,
Packit 1422b7
	const int hour, const int minute, const int second,
Packit 1422b7
	const int OffsetHour, const int OffsetMinute, const char OffsetMode)
Packit 1422b7
{
Packit 1422b7
	long MonthInDays, NumberOfYears, NumberOfDays;
Packit 1422b7
	int utcOffset;
Packit 1422b7
	time_t TimeInUnixFormat;
Packit 1422b7
Packit 1422b7
	if(year < 1970 || year > 2100) {
Packit 1422b7
		TimeInUnixFormat = 0;
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* Counting how many Days have passed since the 01.01 of the
Packit 1422b7
	 * selected Year (Month level), according to the selected Month*/
Packit 1422b7
Packit 1422b7
	switch(month)
Packit 1422b7
	{
Packit 1422b7
		case 1:
Packit 1422b7
			MonthInDays = 0;         //until 01 of January
Packit 1422b7
			break;
Packit 1422b7
		case 2:
Packit 1422b7
			MonthInDays = 31;        //until 01 of February - leap year handling down below!
Packit 1422b7
			break;
Packit 1422b7
		case 3:
Packit 1422b7
			MonthInDays = 59;        //until 01 of March
Packit 1422b7
			break;
Packit 1422b7
		case 4:
Packit 1422b7
			MonthInDays = 90;        //until 01 of April
Packit 1422b7
			break;
Packit 1422b7
		case 5:
Packit 1422b7
			MonthInDays = 120;       //until 01 of Mai
Packit 1422b7
			break;
Packit 1422b7
		case 6:
Packit 1422b7
			MonthInDays = 151;       //until 01 of June
Packit 1422b7
			break;
Packit 1422b7
		case 7:
Packit 1422b7
			MonthInDays = 181;       //until 01 of July
Packit 1422b7
			break;
Packit 1422b7
		case 8:
Packit 1422b7
			MonthInDays = 212;       //until 01 of August
Packit 1422b7
			break;
Packit 1422b7
		case 9:
Packit 1422b7
			MonthInDays = 243;       //until 01 of September
Packit 1422b7
			break;
Packit 1422b7
		case 10:
Packit 1422b7
			MonthInDays = 273;       //until 01 of Oktober
Packit 1422b7
			break;
Packit 1422b7
		case 11:
Packit 1422b7
			MonthInDays = 304;       //until 01 of November
Packit 1422b7
			break;
Packit 1422b7
		case 12:
Packit 1422b7
			MonthInDays = 334;       //until 01 of December
Packit 1422b7
			break;
Packit 1422b7
		default: /* this cannot happen (and would be a program error)
Packit 1422b7
		          * but we need the code to keep the compiler silent.
Packit 1422b7
			  */
Packit 1422b7
			MonthInDays = 0;	/* any value fits ;) */
Packit 1422b7
			break;
Packit 1422b7
	}
Packit 1422b7
	/* adjust for leap years */
Packit 1422b7
	if((year % 100 != 0 && year % 4 == 0) || (year == 2000)) {
Packit 1422b7
		if(month > 2)
Packit 1422b7
			MonthInDays++;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
Packit 1422b7
	/*	1) Counting how many Years have passed since 1970
Packit 1422b7
		2) Counting how many Days have passed since the 01.01 of the selected Year
Packit 1422b7
			(Day level) according to the Selected Month and Day. Last day doesn't count,
Packit 1422b7
			it should be until last day
Packit 1422b7
		3) Calculating this period (NumberOfDays) in seconds*/
Packit 1422b7
Packit 1422b7
	NumberOfYears = year - yearInSec_startYear - 1;
Packit 1422b7
	NumberOfDays = MonthInDays + day - 1;
Packit 1422b7
	TimeInUnixFormat = (yearInSecs[NumberOfYears] + 1) + NumberOfDays * 86400;
Packit 1422b7
Packit 1422b7
	/*Add Hours, minutes and seconds */
Packit 1422b7
	TimeInUnixFormat += hour*60*60;
Packit 1422b7
	TimeInUnixFormat += minute*60;
Packit 1422b7
	TimeInUnixFormat += second;
Packit 1422b7
	/* do UTC offset */
Packit 1422b7
	utcOffset = OffsetHour*3600 + OffsetMinute*60;
Packit 1422b7
	if(OffsetMode == '+')
Packit 1422b7
		utcOffset *= -1; /* if timestamp is ahead, we need to "go back" to UTC */
Packit 1422b7
	TimeInUnixFormat += utcOffset;
Packit 1422b7
done:
Packit 1422b7
	return TimeInUnixFormat;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
struct data_RFC5424Date {
Packit 1422b7
	enum FMT_MODE fmt_mode;
Packit 1422b7
};
Packit 1422b7
/**
Packit 1422b7
 * Parse a TIMESTAMP as specified in RFC5424 (subset of RFC3339).
Packit 1422b7
 */
Packit 1422b7
PARSER_Parse(RFC5424Date)
Packit 1422b7
	const unsigned char *pszTS;
Packit 1422b7
	struct data_RFC5424Date *const data = (struct data_RFC5424Date*) pdata;
Packit 1422b7
	/* variables to temporarily hold time information while we parse */
Packit 1422b7
	int year;
Packit 1422b7
	int month;
Packit 1422b7
	int day;
Packit 1422b7
	int hour; /* 24 hour clock */
Packit 1422b7
	int minute;
Packit 1422b7
	int second;
Packit 1422b7
	int secfrac;	/* fractional seconds (must be 32 bit!) */
Packit 1422b7
	int secfracPrecision;
Packit 1422b7
	int OffsetHour;		/* UTC offset in hours */
Packit 1422b7
	int OffsetMinute;	/* UTC offset in minutes */
Packit 1422b7
	char OffsetMode;
Packit 1422b7
	size_t len;
Packit 1422b7
	size_t orglen;
Packit 1422b7
	/* end variables to temporarily hold time information while we parse */
Packit 1422b7
Packit 1422b7
	pszTS = (unsigned char*) npb->str + *offs;
Packit 1422b7
	len = orglen = npb->strLen - *offs;
Packit 1422b7
Packit 1422b7
	year = hParseInt(&pszTS, &len;;
Packit 1422b7
Packit 1422b7
	/* We take the liberty to accept slightly malformed timestamps e.g. in
Packit 1422b7
	 * the format of 2003-9-1T1:0:0.  */
Packit 1422b7
	if(len == 0 || *pszTS++ != '-') goto done;
Packit 1422b7
	--len;
Packit 1422b7
	month = hParseInt(&pszTS, &len;;
Packit 1422b7
	if(month < 1 || month > 12) goto done;
Packit 1422b7
Packit 1422b7
	if(len == 0 || *pszTS++ != '-')
Packit 1422b7
		goto done;
Packit 1422b7
	--len;
Packit 1422b7
	day = hParseInt(&pszTS, &len;;
Packit 1422b7
	if(day < 1 || day > 31) goto done;
Packit 1422b7
Packit 1422b7
	if(len == 0 || *pszTS++ != 'T') goto done;
Packit 1422b7
	--len;
Packit 1422b7
Packit 1422b7
	hour = hParseInt(&pszTS, &len;;
Packit 1422b7
	if(hour < 0 || hour > 23) goto done;
Packit 1422b7
Packit 1422b7
	if(len == 0 || *pszTS++ != ':')
Packit 1422b7
		goto done;
Packit 1422b7
	--len;
Packit 1422b7
	minute = hParseInt(&pszTS, &len;;
Packit 1422b7
	if(minute < 0 || minute > 59) goto done;
Packit 1422b7
Packit 1422b7
	if(len == 0 || *pszTS++ != ':') goto done;
Packit 1422b7
	--len;
Packit 1422b7
	second = hParseInt(&pszTS, &len;;
Packit 1422b7
	if(second < 0 || second > 60) goto done;
Packit 1422b7
Packit 1422b7
	/* Now let's see if we have secfrac */
Packit 1422b7
	if(len > 0 && *pszTS == '.') {
Packit 1422b7
		--len;
Packit 1422b7
		const unsigned char *pszStart = ++pszTS;
Packit 1422b7
		secfrac = hParseInt(&pszTS, &len;;
Packit 1422b7
		secfracPrecision = (int) (pszTS - pszStart);
Packit 1422b7
	} else {
Packit 1422b7
		secfracPrecision = 0;
Packit 1422b7
		secfrac = 0;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* check the timezone */
Packit 1422b7
	if(len == 0) goto done;
Packit 1422b7
Packit 1422b7
	if(*pszTS == 'Z') {
Packit 1422b7
		OffsetHour = 0;
Packit 1422b7
		OffsetMinute = 0;
Packit 1422b7
		OffsetMode = '+';
Packit 1422b7
		--len;
Packit 1422b7
		pszTS++; /* eat Z */
Packit 1422b7
	} else if((*pszTS == '+') || (*pszTS == '-')) {
Packit 1422b7
		OffsetMode = *pszTS;
Packit 1422b7
		--len;
Packit 1422b7
		pszTS++;
Packit 1422b7
Packit 1422b7
		OffsetHour = hParseInt(&pszTS, &len;;
Packit 1422b7
		if(OffsetHour < 0 || OffsetHour > 23)
Packit 1422b7
			goto done;
Packit 1422b7
Packit 1422b7
		if(len == 0 || *pszTS++ != ':')
Packit 1422b7
			goto done;
Packit 1422b7
		--len;
Packit 1422b7
		OffsetMinute = hParseInt(&pszTS, &len;;
Packit 1422b7
		if(OffsetMinute < 0 || OffsetMinute > 59)
Packit 1422b7
			goto done;
Packit 1422b7
	} else {
Packit 1422b7
		/* there MUST be TZ information */
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if(len > 0) {
Packit 1422b7
		if(*pszTS != ' ') /* if it is not a space, it can not be a "good" time */
Packit 1422b7
			goto done;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* we had success, so update parse pointer */
Packit 1422b7
	*parsed = orglen - len;
Packit 1422b7
Packit 1422b7
	if(value != NULL) {
Packit 1422b7
		if(data->fmt_mode == FMT_AS_STRING) {
Packit 1422b7
			*value = json_object_new_string_len(npb->str+(*offs), *parsed);
Packit 1422b7
		} else {
Packit 1422b7
			int64_t timestamp = syslogTime2time_t(year, month, day,
Packit 1422b7
				hour, minute, second, OffsetHour, OffsetMinute, OffsetMode);
Packit 1422b7
			if(data->fmt_mode == FMT_AS_TIMESTAMP_UX_MS) {
Packit 1422b7
				timestamp *= 1000;
Packit 1422b7
				/* simulate pow(), do not use math lib! */
Packit 1422b7
				int div = 1;
Packit 1422b7
				if(secfracPrecision == 1) {
Packit 1422b7
					secfrac *= 100;
Packit 1422b7
				} else if(secfracPrecision == 2) {
Packit 1422b7
					secfrac *= 10;
Packit 1422b7
				} else if(secfracPrecision > 3) {
Packit 1422b7
					for(int i = 0 ; i < (secfracPrecision - 3) ; ++i)
Packit 1422b7
						div *= 10;
Packit 1422b7
				}
Packit 1422b7
				timestamp += secfrac / div;
Packit 1422b7
			}
Packit 1422b7
			*value = json_object_new_int64(timestamp);
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
PARSER_Construct(RFC5424Date)
Packit 1422b7
{
Packit 1422b7
	int r = 0;
Packit 1422b7
	struct data_RFC5424Date *data =
Packit 1422b7
		(struct data_RFC5424Date*) calloc(1, sizeof(struct data_RFC5424Date));
Packit 1422b7
	data->fmt_mode = FMT_AS_STRING;
Packit 1422b7
Packit 1422b7
	if(json == NULL)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	struct json_object_iterator it = json_object_iter_begin(json);
Packit 1422b7
	struct json_object_iterator itEnd = json_object_iter_end(json);
Packit 1422b7
	while (!json_object_iter_equal(&it, &itEnd)) {
Packit 1422b7
		const char *key = json_object_iter_peek_name(&it);
Packit 1422b7
		struct json_object *const val = json_object_iter_peek_value(&it);
Packit 1422b7
		if(!strcmp(key, "format")) {
Packit 1422b7
			const char *fmtmode = json_object_get_string(val);
Packit 1422b7
			if(!strcmp(fmtmode, "timestamp-unix")) {
Packit 1422b7
				data->fmt_mode = FMT_AS_TIMESTAMP_UX;
Packit 1422b7
			} else if(!strcmp(fmtmode, "timestamp-unix-ms")) {
Packit 1422b7
				data->fmt_mode = FMT_AS_TIMESTAMP_UX_MS;
Packit 1422b7
			} else if(!strcmp(fmtmode, "string")) {
Packit 1422b7
				data->fmt_mode = FMT_AS_STRING;
Packit 1422b7
			} else {
Packit 1422b7
				ln_errprintf(ctx, 0, "invalid value for date-rfc5424:format %s",
Packit 1422b7
					fmtmode);
Packit 1422b7
			}
Packit 1422b7
		} else {
Packit 1422b7
			ln_errprintf(ctx, 0, "invalid param for date-rfc5424 %s", key);
Packit 1422b7
		}
Packit 1422b7
		json_object_iter_next(&it);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
done:
Packit 1422b7
	*pdata = data;
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
PARSER_Destruct(RFC5424Date)
Packit 1422b7
{
Packit 1422b7
	free(pdata);
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
struct data_RFC3164Date {
Packit 1422b7
	enum FMT_MODE fmt_mode;
Packit 1422b7
};
Packit 1422b7
/**
Packit 1422b7
 * Parse a RFC3164 Date.
Packit 1422b7
 */
Packit 1422b7
PARSER_Parse(RFC3164Date)
Packit 1422b7
	const unsigned char *p;
Packit 1422b7
	size_t len, orglen;
Packit 1422b7
	struct data_RFC3164Date *const data = (struct data_RFC3164Date*) pdata;
Packit 1422b7
	/* variables to temporarily hold time information while we parse */
Packit 1422b7
	int year;
Packit 1422b7
	int month;
Packit 1422b7
	int day;
Packit 1422b7
	int hour; /* 24 hour clock */
Packit 1422b7
	int minute;
Packit 1422b7
	int second;
Packit 1422b7
Packit 1422b7
	p = (unsigned char*) npb->str + *offs;
Packit 1422b7
	orglen = len = npb->strLen - *offs;
Packit 1422b7
	/* If we look at the month (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec),
Packit 1422b7
	 * we may see the following character sequences occur:
Packit 1422b7
	 *
Packit 1422b7
	 * J(an/u(n/l)), Feb, Ma(r/y), A(pr/ug), Sep, Oct, Nov, Dec
Packit 1422b7
	 *
Packit 1422b7
	 * We will use this for parsing, as it probably is the
Packit 1422b7
	 * fastest way to parse it.
Packit 1422b7
	 */
Packit 1422b7
	if(len < 3)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	switch(*p++)
Packit 1422b7
	{
Packit 1422b7
	case 'j':
Packit 1422b7
	case 'J':
Packit 1422b7
		if(*p == 'a' || *p == 'A') {
Packit 1422b7
			++p;
Packit 1422b7
			if(*p == 'n' || *p == 'N') {
Packit 1422b7
				++p;
Packit 1422b7
				month = 1;
Packit 1422b7
			} else
Packit 1422b7
				goto done;
Packit 1422b7
		} else if(*p == 'u' || *p == 'U') {
Packit 1422b7
			++p;
Packit 1422b7
			if(*p == 'n' || *p == 'N') {
Packit 1422b7
				++p;
Packit 1422b7
				month = 6;
Packit 1422b7
			} else if(*p == 'l' || *p == 'L') {
Packit 1422b7
				++p;
Packit 1422b7
				month = 7;
Packit 1422b7
			} else
Packit 1422b7
				goto done;
Packit 1422b7
		} else
Packit 1422b7
			goto done;
Packit 1422b7
		break;
Packit 1422b7
	case 'f':
Packit 1422b7
	case 'F':
Packit 1422b7
		if(*p == 'e' || *p == 'E') {
Packit 1422b7
			++p;
Packit 1422b7
			if(*p == 'b' || *p == 'B') {
Packit 1422b7
				++p;
Packit 1422b7
				month = 2;
Packit 1422b7
			} else
Packit 1422b7
				goto done;
Packit 1422b7
		} else
Packit 1422b7
			goto done;
Packit 1422b7
		break;
Packit 1422b7
	case 'm':
Packit 1422b7
	case 'M':
Packit 1422b7
		if(*p == 'a' || *p == 'A') {
Packit 1422b7
			++p;
Packit 1422b7
			if(*p == 'r' || *p == 'R') {
Packit 1422b7
				++p;
Packit 1422b7
				month = 3;
Packit 1422b7
			} else if(*p == 'y' || *p == 'Y') {
Packit 1422b7
				++p;
Packit 1422b7
				month = 5;
Packit 1422b7
			} else
Packit 1422b7
				goto done;
Packit 1422b7
		} else
Packit 1422b7
			goto done;
Packit 1422b7
		break;
Packit 1422b7
	case 'a':
Packit 1422b7
	case 'A':
Packit 1422b7
		if(*p == 'p' || *p == 'P') {
Packit 1422b7
			++p;
Packit 1422b7
			if(*p == 'r' || *p == 'R') {
Packit 1422b7
				++p;
Packit 1422b7
				month = 4;
Packit 1422b7
			} else
Packit 1422b7
				goto done;
Packit 1422b7
		} else if(*p == 'u' || *p == 'U') {
Packit 1422b7
			++p;
Packit 1422b7
			if(*p == 'g' || *p == 'G') {
Packit 1422b7
				++p;
Packit 1422b7
				month = 8;
Packit 1422b7
			} else
Packit 1422b7
				goto done;
Packit 1422b7
		} else
Packit 1422b7
			goto done;
Packit 1422b7
		break;
Packit 1422b7
	case 's':
Packit 1422b7
	case 'S':
Packit 1422b7
		if(*p == 'e' || *p == 'E') {
Packit 1422b7
			++p;
Packit 1422b7
			if(*p == 'p' || *p == 'P') {
Packit 1422b7
				++p;
Packit 1422b7
				month = 9;
Packit 1422b7
			} else
Packit 1422b7
				goto done;
Packit 1422b7
		} else
Packit 1422b7
			goto done;
Packit 1422b7
		break;
Packit 1422b7
	case 'o':
Packit 1422b7
	case 'O':
Packit 1422b7
		if(*p == 'c' || *p == 'C') {
Packit 1422b7
			++p;
Packit 1422b7
			if(*p == 't' || *p == 'T') {
Packit 1422b7
				++p;
Packit 1422b7
				month = 10;
Packit 1422b7
			} else
Packit 1422b7
				goto done;
Packit 1422b7
		} else
Packit 1422b7
			goto done;
Packit 1422b7
		break;
Packit 1422b7
	case 'n':
Packit 1422b7
	case 'N':
Packit 1422b7
		if(*p == 'o' || *p == 'O') {
Packit 1422b7
Packit 1422b7
			++p;
Packit 1422b7
			if(*p == 'v' || *p == 'V') {
Packit 1422b7
				++p;
Packit 1422b7
				month = 11;
Packit 1422b7
			} else
Packit 1422b7
				goto done;
Packit 1422b7
		} else
Packit 1422b7
			goto done;
Packit 1422b7
		break;
Packit 1422b7
	case 'd':
Packit 1422b7
	case 'D':
Packit 1422b7
		if(*p == 'e' || *p == 'E') {
Packit 1422b7
			++p;
Packit 1422b7
			if(*p == 'c' || *p == 'C') {
Packit 1422b7
				++p;
Packit 1422b7
				month = 12;
Packit 1422b7
			} else
Packit 1422b7
				goto done;
Packit 1422b7
		} else
Packit 1422b7
			goto done;
Packit 1422b7
		break;
Packit 1422b7
	default:
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	len -= 3;
Packit 1422b7
	
Packit 1422b7
	/* done month */
Packit 1422b7
Packit 1422b7
	if(len == 0 || *p++ != ' ')
Packit 1422b7
		goto done;
Packit 1422b7
	--len;
Packit 1422b7
Packit 1422b7
	/* we accept a slightly malformed timestamp with one-digit days. */
Packit 1422b7
	if(*p == ' ') {
Packit 1422b7
		--len;
Packit 1422b7
		++p;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	day = hParseInt(&p, &len;;
Packit 1422b7
	if(day < 1 || day > 31)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	if(len == 0 || *p++ != ' ')
Packit 1422b7
		goto done;
Packit 1422b7
	--len;
Packit 1422b7
Packit 1422b7
	/* time part */
Packit 1422b7
	hour = hParseInt(&p, &len;;
Packit 1422b7
	if(hour > 1970 && hour < 2100) {
Packit 1422b7
		/* if so, we assume this actually is a year. This is a format found
Packit 1422b7
		 * e.g. in Cisco devices.
Packit 1422b7
		 *
Packit 1422b7
		year = hour;
Packit 1422b7
		*/
Packit 1422b7
Packit 1422b7
		/* re-query the hour, this time it must be valid */
Packit 1422b7
		if(len == 0 || *p++ != ' ')
Packit 1422b7
			goto done;
Packit 1422b7
		--len;
Packit 1422b7
		hour = hParseInt(&p, &len;;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if(hour < 0 || hour > 23)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	if(len == 0 || *p++ != ':')
Packit 1422b7
		goto done;
Packit 1422b7
	--len;
Packit 1422b7
	minute = hParseInt(&p, &len;;
Packit 1422b7
	if(minute < 0 || minute > 59)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	if(len == 0 || *p++ != ':')
Packit 1422b7
		goto done;
Packit 1422b7
	--len;
Packit 1422b7
	second = hParseInt(&p, &len;;
Packit 1422b7
	if(second < 0 || second > 60)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	/* we provide support for an extra ":" after the date. While this is an
Packit 1422b7
	 * invalid format, it occurs frequently enough (e.g. with Cisco devices)
Packit 1422b7
	 * to permit it as a valid case. -- rgerhards, 2008-09-12
Packit 1422b7
	 */
Packit 1422b7
	if(len > 0 && *p == ':') {
Packit 1422b7
		++p; /* just skip past it */
Packit 1422b7
		--len;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* we had success, so update parse pointer */
Packit 1422b7
	*parsed = orglen - len;
Packit 1422b7
	if(value != NULL) {
Packit 1422b7
		if(data->fmt_mode == FMT_AS_STRING) {
Packit 1422b7
			*value = json_object_new_string_len(npb->str+(*offs), *parsed);
Packit 1422b7
		} else {
Packit 1422b7
			/* we assume year == current year, so let's obtain current year */
Packit 1422b7
			struct tm tm;
Packit 1422b7
			const time_t curr = time(NULL);
Packit 1422b7
			gmtime_r(&curr, &tm;;
Packit 1422b7
			year = tm.tm_year + 1900;
Packit 1422b7
			int64_t timestamp = syslogTime2time_t(year, month, day,
Packit 1422b7
				hour, minute, second, 0, 0, '+');
Packit 1422b7
			if(data->fmt_mode == FMT_AS_TIMESTAMP_UX_MS) {
Packit 1422b7
				/* we do not have more precise info, just bring
Packit 1422b7
				 * into common format!
Packit 1422b7
				 */
Packit 1422b7
				timestamp *= 1000;
Packit 1422b7
			}
Packit 1422b7
			*value = json_object_new_int64(timestamp);
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
PARSER_Construct(RFC3164Date)
Packit 1422b7
{
Packit 1422b7
	int r = 0;
Packit 1422b7
	struct data_RFC3164Date *data = (struct data_RFC3164Date*) calloc(1, sizeof(struct data_RFC3164Date));
Packit 1422b7
	data->fmt_mode = FMT_AS_STRING;
Packit 1422b7
Packit 1422b7
	if(json == NULL)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	struct json_object_iterator it = json_object_iter_begin(json);
Packit 1422b7
	struct json_object_iterator itEnd = json_object_iter_end(json);
Packit 1422b7
	while (!json_object_iter_equal(&it, &itEnd)) {
Packit 1422b7
		const char *key = json_object_iter_peek_name(&it);
Packit 1422b7
		struct json_object *const val = json_object_iter_peek_value(&it);
Packit 1422b7
		if(!strcmp(key, "format")) {
Packit 1422b7
			const char *fmtmode = json_object_get_string(val);
Packit 1422b7
			if(!strcmp(fmtmode, "timestamp-unix")) {
Packit 1422b7
				data->fmt_mode = FMT_AS_TIMESTAMP_UX;
Packit 1422b7
			} else if(!strcmp(fmtmode, "timestamp-unix-ms")) {
Packit 1422b7
				data->fmt_mode = FMT_AS_TIMESTAMP_UX_MS;
Packit 1422b7
			} else if(!strcmp(fmtmode, "string")) {
Packit 1422b7
				data->fmt_mode = FMT_AS_STRING;
Packit 1422b7
			} else {
Packit 1422b7
				ln_errprintf(ctx, 0, "invalid value for date-rfc3164:format %s",
Packit 1422b7
					fmtmode);
Packit 1422b7
			}
Packit 1422b7
		} else {
Packit 1422b7
			ln_errprintf(ctx, 0, "invalid param for date-rfc3164 %s", key);
Packit 1422b7
		}
Packit 1422b7
		json_object_iter_next(&it);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
done:
Packit 1422b7
	*pdata = data;
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
PARSER_Destruct(RFC3164Date)
Packit 1422b7
{
Packit 1422b7
	free(pdata);
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
struct data_Number {
Packit 1422b7
	int64_t maxval;
Packit 1422b7
	enum FMT_MODE fmt_mode;
Packit 1422b7
};
Packit 1422b7
/**
Packit 1422b7
 * Parse a Number.
Packit 1422b7
 * Note that a number is an abstracted concept. We always represent it
Packit 1422b7
 * as 64 bits (but may later change our mind if performance dictates so).
Packit 1422b7
 */
Packit 1422b7
PARSER_Parse(Number)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i;
Packit 1422b7
	int64_t val = 0;
Packit 1422b7
	struct data_Number *const data = (struct data_Number*) pdata;
Packit 1422b7
Packit 1422b7
	enum FMT_MODE fmt_mode = FMT_AS_STRING;
Packit 1422b7
	int64_t maxval = 0;
Packit 1422b7
	if(data != NULL) {
Packit 1422b7
		fmt_mode = data->fmt_mode;
Packit 1422b7
		maxval = data->maxval;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	assert(npb->str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	c = npb->str;
Packit 1422b7
Packit 1422b7
	for (i = *offs; i < npb->strLen && myisdigit(c[i]); i++)
Packit 1422b7
		val = val * 10 + c[i] - '0';
Packit 1422b7
Packit 1422b7
	if(maxval > 0 && val > maxval) {
Packit 1422b7
		LN_DBGPRINTF(npb->ctx, "number parser: val too large (max %" PRIu64
Packit 1422b7
			     ", actual %" PRIu64 ")",
Packit 1422b7
			     maxval, val);
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if (i == *offs)
Packit 1422b7
		goto done;
Packit 1422b7
	
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = i - *offs;
Packit 1422b7
	if(value != NULL) {
Packit 1422b7
		if(fmt_mode == FMT_AS_STRING) {
Packit 1422b7
			*value = json_object_new_string_len(npb->str+(*offs), *parsed);
Packit 1422b7
		} else {
Packit 1422b7
			*value = json_object_new_int64(val);
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
PARSER_Construct(Number)
Packit 1422b7
{
Packit 1422b7
	int r = 0;
Packit 1422b7
	struct data_Number *data = (struct data_Number*) calloc(1, sizeof(struct data_Number));
Packit 1422b7
	data->fmt_mode = FMT_AS_STRING;
Packit 1422b7
Packit 1422b7
	if(json == NULL)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	struct json_object_iterator it = json_object_iter_begin(json);
Packit 1422b7
	struct json_object_iterator itEnd = json_object_iter_end(json);
Packit 1422b7
	while (!json_object_iter_equal(&it, &itEnd)) {
Packit 1422b7
		const char *key = json_object_iter_peek_name(&it);
Packit 1422b7
		struct json_object *const val = json_object_iter_peek_value(&it);
Packit 1422b7
		if(!strcmp(key, "maxval")) {
Packit 1422b7
			errno = 0;
Packit 1422b7
			data->maxval = json_object_get_int64(val);
Packit 1422b7
			if(errno != 0) {
Packit 1422b7
				ln_errprintf(ctx, errno, "param 'maxval' must be integer but is: %s",
Packit 1422b7
					 json_object_to_json_string(val));
Packit 1422b7
			}
Packit 1422b7
		} else if(!strcmp(key, "format")) {
Packit 1422b7
			const char *fmtmode = json_object_get_string(val);
Packit 1422b7
			if(!strcmp(fmtmode, "number")) {
Packit 1422b7
				data->fmt_mode = FMT_AS_NUMBER;
Packit 1422b7
			} else if(!strcmp(fmtmode, "string")) {
Packit 1422b7
				data->fmt_mode = FMT_AS_STRING;
Packit 1422b7
			} else {
Packit 1422b7
				ln_errprintf(ctx, 0, "invalid value for number:format %s",
Packit 1422b7
					fmtmode);
Packit 1422b7
			}
Packit 1422b7
		} else {
Packit 1422b7
			ln_errprintf(ctx, 0, "invalid param for number: %s", key);
Packit 1422b7
		}
Packit 1422b7
		json_object_iter_next(&it);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
done:
Packit 1422b7
	*pdata = data;
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
PARSER_Destruct(Number)
Packit 1422b7
{
Packit 1422b7
	free(pdata);
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
struct data_Float {
Packit 1422b7
	enum FMT_MODE fmt_mode;
Packit 1422b7
};
Packit 1422b7
/**
Packit 1422b7
 * Parse a Real-number in floating-pt form.
Packit 1422b7
 */
Packit 1422b7
PARSER_Parse(Float)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i;
Packit 1422b7
	const struct data_Float *const data = (struct data_Float*) pdata;
Packit 1422b7
Packit 1422b7
	assert(npb->str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	c = npb->str;
Packit 1422b7
Packit 1422b7
	int isNeg = 0;
Packit 1422b7
	double val = 0;
Packit 1422b7
	int seen_point = 0;
Packit 1422b7
	double frac = 10;
Packit 1422b7
Packit 1422b7
	i = *offs;
Packit 1422b7
Packit 1422b7
	if (c[i] == '-') {
Packit 1422b7
		isNeg = 1;
Packit 1422b7
		i++;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	for (; i < npb->strLen; i++) {
Packit 1422b7
		if (c[i] == '.') {
Packit 1422b7
			if (seen_point != 0)
Packit 1422b7
				break;
Packit 1422b7
			seen_point = 1;
Packit 1422b7
		} else if (myisdigit(c[i])) {
Packit 1422b7
			if(seen_point) {
Packit 1422b7
				val += (c[i] - '0') / frac;
Packit 1422b7
				frac *= 10;
Packit 1422b7
			} else {
Packit 1422b7
				val = val * 10 + c[i] - '0';
Packit 1422b7
			}
Packit 1422b7
		} else {
Packit 1422b7
			break;
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
	if (i == *offs)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	if(isNeg)
Packit 1422b7
		val *= -1;
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = i - *offs;
Packit 1422b7
	if(value != NULL) {
Packit 1422b7
		if(data->fmt_mode == FMT_AS_STRING) {
Packit 1422b7
			*value = json_object_new_string_len(npb->str+(*offs), *parsed);
Packit 1422b7
		} else {
Packit 1422b7
			char *serialized = strndup(npb->str+(*offs), *parsed);
Packit 1422b7
			*value = json_object_new_double_s(val, serialized);
Packit 1422b7
			free(serialized);
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
PARSER_Construct(Float)
Packit 1422b7
{
Packit 1422b7
	int r = 0;
Packit 1422b7
	struct data_Float *data = (struct data_Float*) calloc(1, sizeof(struct data_Float));
Packit 1422b7
	data->fmt_mode = FMT_AS_STRING;
Packit 1422b7
Packit 1422b7
	if(json == NULL)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	struct json_object_iterator it = json_object_iter_begin(json);
Packit 1422b7
	struct json_object_iterator itEnd = json_object_iter_end(json);
Packit 1422b7
	while (!json_object_iter_equal(&it, &itEnd)) {
Packit 1422b7
		const char *key = json_object_iter_peek_name(&it);
Packit 1422b7
		struct json_object *const val = json_object_iter_peek_value(&it);
Packit 1422b7
		if(!strcmp(key, "format")) {
Packit 1422b7
			const char *fmtmode = json_object_get_string(val);
Packit 1422b7
			if(!strcmp(fmtmode, "number")) {
Packit 1422b7
				data->fmt_mode = FMT_AS_NUMBER;
Packit 1422b7
			} else if(!strcmp(fmtmode, "string")) {
Packit 1422b7
				data->fmt_mode = FMT_AS_STRING;
Packit 1422b7
			} else {
Packit 1422b7
				ln_errprintf(ctx, 0, "invalid value for float:format %s",
Packit 1422b7
					fmtmode);
Packit 1422b7
			}
Packit 1422b7
		} else {
Packit 1422b7
			ln_errprintf(ctx, 0, "invalid param for float: %s", key);
Packit 1422b7
		}
Packit 1422b7
		json_object_iter_next(&it);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
done:
Packit 1422b7
	*pdata = data;
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
PARSER_Destruct(Float)
Packit 1422b7
{
Packit 1422b7
	free(pdata);
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
struct data_HexNumber {
Packit 1422b7
	uint64_t maxval;
Packit 1422b7
	enum FMT_MODE fmt_mode;
Packit 1422b7
};
Packit 1422b7
/**
Packit 1422b7
 * Parse a hex Number.
Packit 1422b7
 * A hex number begins with 0x and contains only hex digits until the terminating
Packit 1422b7
 * whitespace. Note that if a non-hex character is deteced inside the number string,
Packit 1422b7
 * this is NOT considered to be a number.
Packit 1422b7
 */
Packit 1422b7
PARSER_Parse(HexNumber)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i = *offs;
Packit 1422b7
	struct data_HexNumber *const data = (struct data_HexNumber*) pdata;
Packit 1422b7
	uint64_t maxval = data->maxval;
Packit 1422b7
Packit 1422b7
	assert(npb->str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	c = npb->str;
Packit 1422b7
Packit 1422b7
	if(c[i] != '0' || c[i+1] != 'x')
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	uint64_t val = 0;
Packit 1422b7
	for (i += 2 ; i < npb->strLen && isxdigit(c[i]); i++) {
Packit 1422b7
		const char digit = tolower(c[i]);
Packit 1422b7
		val *= 16;
Packit 1422b7
		if(digit >= 'a' && digit <= 'f')
Packit 1422b7
			val += digit - 'a' + 10;
Packit 1422b7
		else
Packit 1422b7
			val += digit - '0';
Packit 1422b7
	}
Packit 1422b7
	if (i == *offs || !isspace(c[i]))
Packit 1422b7
		goto done;
Packit 1422b7
	if(maxval > 0 && val > maxval) {
Packit 1422b7
		LN_DBGPRINTF(npb->ctx, "hexnumber parser: val too large (max %" PRIu64
Packit 1422b7
			     ", actual %" PRIu64 ")",
Packit 1422b7
			     maxval, val);
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
	
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = i - *offs;
Packit 1422b7
	if(value != NULL) {
Packit 1422b7
		if(data->fmt_mode == FMT_AS_STRING) {
Packit 1422b7
			*value = json_object_new_string_len(npb->str+(*offs), *parsed);
Packit 1422b7
		} else {
Packit 1422b7
			*value = json_object_new_int64((int64_t) val);
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
PARSER_Construct(HexNumber)
Packit 1422b7
{
Packit 1422b7
	int r = 0;
Packit 1422b7
	struct data_HexNumber *data = (struct data_HexNumber*) calloc(1, sizeof(struct data_HexNumber));
Packit 1422b7
	data->fmt_mode = FMT_AS_STRING;
Packit 1422b7
Packit 1422b7
	if(json == NULL)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	struct json_object_iterator it = json_object_iter_begin(json);
Packit 1422b7
	struct json_object_iterator itEnd = json_object_iter_end(json);
Packit 1422b7
	while (!json_object_iter_equal(&it, &itEnd)) {
Packit 1422b7
		const char *key = json_object_iter_peek_name(&it);
Packit 1422b7
		struct json_object *const val = json_object_iter_peek_value(&it);
Packit 1422b7
		if(!strcmp(key, "maxval")) {
Packit 1422b7
			errno = 0;
Packit 1422b7
			data->maxval = json_object_get_int64(val);
Packit 1422b7
			if(errno != 0) {
Packit 1422b7
				ln_errprintf(ctx, errno, "param 'maxval' must be integer but is: %s",
Packit 1422b7
					 json_object_to_json_string(val));
Packit 1422b7
			}
Packit 1422b7
		} else if(!strcmp(key, "format")) {
Packit 1422b7
			const char *fmtmode = json_object_get_string(val);
Packit 1422b7
			if(!strcmp(fmtmode, "number")) {
Packit 1422b7
				data->fmt_mode = FMT_AS_NUMBER;
Packit 1422b7
			} else if(!strcmp(fmtmode, "string")) {
Packit 1422b7
				data->fmt_mode = FMT_AS_STRING;
Packit 1422b7
			} else {
Packit 1422b7
				ln_errprintf(ctx, 0, "invalid value for hexnumber:format %s",
Packit 1422b7
					fmtmode);
Packit 1422b7
			}
Packit 1422b7
		} else {
Packit 1422b7
			ln_errprintf(ctx, 0, "invalid param for hexnumber: %s", key);
Packit 1422b7
		}
Packit 1422b7
		json_object_iter_next(&it);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
done:
Packit 1422b7
	*pdata = data;
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
PARSER_Destruct(HexNumber)
Packit 1422b7
{
Packit 1422b7
	free(pdata);
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse a kernel timestamp.
Packit 1422b7
 * This is a fixed format, see
Packit 1422b7
 * https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/kernel/printk/printk.c?id=refs/tags/v4.0#n1011
Packit 1422b7
 * This is the code that generates it:
Packit 1422b7
 * sprintf(buf, "[%5lu.%06lu] ",  (unsigned long)ts, rem_nsec / 1000);
Packit 1422b7
 * We accept up to 12 digits for ts, everything above that for sure is
Packit 1422b7
 * no timestamp.
Packit 1422b7
 */
Packit 1422b7
#define LEN_KERNEL_TIMESTAMP 14
Packit 1422b7
PARSER_Parse(KernelTimestamp)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i;
Packit 1422b7
Packit 1422b7
	assert(npb->str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	c = npb->str;
Packit 1422b7
Packit 1422b7
	i = *offs;
Packit 1422b7
	if(c[i] != '[' || i+LEN_KERNEL_TIMESTAMP > npb->strLen
Packit 1422b7
	   || !myisdigit(c[i+1])
Packit 1422b7
	   || !myisdigit(c[i+2])
Packit 1422b7
	   || !myisdigit(c[i+3])
Packit 1422b7
	   || !myisdigit(c[i+4])
Packit 1422b7
	   || !myisdigit(c[i+5])
Packit 1422b7
	   )
Packit 1422b7
		goto done;
Packit 1422b7
	i += 6;
Packit 1422b7
	for(int j = 0 ; j < 7 && i < npb->strLen && myisdigit(c[i]) ; )
Packit 1422b7
		++i, ++j;	/* just scan */
Packit 1422b7
Packit 1422b7
	if(i >= npb->strLen || c[i] != '.')
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	++i; /* skip over '.' */
Packit 1422b7
Packit 1422b7
	if( i+7 > npb->strLen
Packit 1422b7
	   || !myisdigit(c[i+0])
Packit 1422b7
	   || !myisdigit(c[i+1])
Packit 1422b7
	   || !myisdigit(c[i+2])
Packit 1422b7
	   || !myisdigit(c[i+3])
Packit 1422b7
	   || !myisdigit(c[i+4])
Packit 1422b7
	   || !myisdigit(c[i+5])
Packit 1422b7
	   || c[i+6] != ']'
Packit 1422b7
	   )
Packit 1422b7
		goto done;
Packit 1422b7
	i += 7;
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = i - *offs;
Packit 1422b7
	if(value != NULL) {
Packit 1422b7
		*value = json_object_new_string_len(npb->str+(*offs), *parsed);
Packit 1422b7
	}
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse whitespace.
Packit 1422b7
 * This parses all whitespace until the first non-whitespace character
Packit 1422b7
 * is found. This is primarily a tool to skip to the next "word" if
Packit 1422b7
 * the exact number of whitspace characters (and type of whitespace)
Packit 1422b7
 * is not known. The current parsing position MUST be on a whitspace,
Packit 1422b7
 * else the parser does not match.
Packit 1422b7
 * This parser is also a forward-compatibility tool for the upcoming
Packit 1422b7
 * slsa (simple log structure analyser) tool.
Packit 1422b7
 */
Packit 1422b7
PARSER_Parse(Whitespace)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i = *offs;
Packit 1422b7
Packit 1422b7
	assert(npb->str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	c = npb->str;
Packit 1422b7
Packit 1422b7
	if(!isspace(c[i]))
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	for (i++ ; i < npb->strLen && isspace(c[i]); i++);
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = i - *offs;
Packit 1422b7
	if(value != NULL) {
Packit 1422b7
		*value = json_object_new_string_len(npb->str+(*offs), *parsed);
Packit 1422b7
	}
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse a word.
Packit 1422b7
 * A word is a SP-delimited entity. The parser always works, except if
Packit 1422b7
 * the offset is position on a space upon entry.
Packit 1422b7
 */
Packit 1422b7
PARSER_Parse(Word)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i;
Packit 1422b7
Packit 1422b7
	assert(npb->str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	c = npb->str;
Packit 1422b7
	i = *offs;
Packit 1422b7
Packit 1422b7
	/* search end of word */
Packit 1422b7
	while(i < npb->strLen && c[i] != ' ')
Packit 1422b7
		i++;
Packit 1422b7
Packit 1422b7
	if(i == *offs)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = i - *offs;
Packit 1422b7
	if(value != NULL) {
Packit 1422b7
		*value = json_object_new_string_len(npb->str+(*offs), *parsed);
Packit 1422b7
	}
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
struct data_StringTo {
Packit 1422b7
	const char *toFind;
Packit 1422b7
	size_t len;
Packit 1422b7
};
Packit 1422b7
/**
Packit 1422b7
 * Parse everything up to a specific string.
Packit 1422b7
 * swisskid, 2015-01-21
Packit 1422b7
 */
Packit 1422b7
PARSER_Parse(StringTo)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i, j, m;
Packit 1422b7
	int chkstr;
Packit 1422b7
	struct data_StringTo *const data = (struct data_StringTo*) pdata;
Packit 1422b7
	const char *const toFind = data->toFind;
Packit 1422b7
	assert(npb->str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	c = npb->str;
Packit 1422b7
	i = *offs;
Packit 1422b7
	chkstr = 0;
Packit 1422b7
Packit 1422b7
	/* Total hunt for letter */
Packit 1422b7
	while(chkstr == 0 && i < npb->strLen ) {
Packit 1422b7
	    i++;
Packit 1422b7
	    if(c[i] == toFind[0]) {
Packit 1422b7
		/* Found the first letter, now find the rest of the string */
Packit 1422b7
		j = 1;
Packit 1422b7
		m = i+1;
Packit 1422b7
		while(m < npb->strLen && j < data->len ) {
Packit 1422b7
			if(c[m] != toFind[j])
Packit 1422b7
				break;
Packit 1422b7
			if(j == data->len - 1) { /* full match? */
Packit 1422b7
				chkstr = 1;
Packit 1422b7
				break;
Packit 1422b7
			}
Packit 1422b7
			j++;
Packit 1422b7
			m++;
Packit 1422b7
		}
Packit 1422b7
	    }
Packit 1422b7
	}
Packit 1422b7
	if(i == *offs || i == npb->strLen || chkstr != 1)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = i - *offs;
Packit 1422b7
	if(value != NULL) {
Packit 1422b7
		*value = json_object_new_string_len(npb->str+(*offs), *parsed);
Packit 1422b7
	}
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
PARSER_Construct(StringTo)
Packit 1422b7
{
Packit 1422b7
	int r = 0;
Packit 1422b7
	struct data_StringTo *data = (struct data_StringTo*) calloc(1, sizeof(struct data_StringTo));
Packit 1422b7
	struct json_object *ed;
Packit 1422b7
Packit 1422b7
	if(json_object_object_get_ex(json, "extradata", &ed) == 0) {
Packit 1422b7
		ln_errprintf(ctx, 0, "string-to type needs 'extradata' parameter");
Packit 1422b7
		r = LN_BADCONFIG ;
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
	data->toFind = strdup(json_object_get_string(ed));
Packit 1422b7
	data->len = strlen(data->toFind);
Packit 1422b7
Packit 1422b7
	*pdata = data;
Packit 1422b7
done:
Packit 1422b7
	if(r != 0)
Packit 1422b7
		free(data);
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
PARSER_Destruct(StringTo)
Packit 1422b7
{
Packit 1422b7
	struct data_StringTo *data = (struct data_StringTo*) pdata;
Packit 1422b7
	free((void*)data->toFind);
Packit 1422b7
	free(pdata);
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse a alphabetic word.
Packit 1422b7
 * A alpha word is composed of characters for which isalpha returns true.
Packit 1422b7
 * The parser dones if there is no alpha character at all.
Packit 1422b7
 */
Packit 1422b7
PARSER_Parse(Alpha)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i;
Packit 1422b7
Packit 1422b7
	assert(npb->str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	c = npb->str;
Packit 1422b7
	i = *offs;
Packit 1422b7
Packit 1422b7
	/* search end of word */
Packit 1422b7
	while(i < npb->strLen && isalpha(c[i]))
Packit 1422b7
		i++;
Packit 1422b7
Packit 1422b7
	if(i == *offs) {
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = i - *offs;
Packit 1422b7
	if(value != NULL) {
Packit 1422b7
		*value = json_object_new_string_len(npb->str+(*offs), *parsed);
Packit 1422b7
	}
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
struct data_CharTo {
Packit 1422b7
	char *term_chars;
Packit 1422b7
	size_t n_term_chars;
Packit 1422b7
	char *data_for_display;
Packit 1422b7
};
Packit 1422b7
/**
Packit 1422b7
 * Parse everything up to a specific character.
Packit 1422b7
 * The character must be the only char inside extra data passed to the parser.
Packit 1422b7
 * It is considered a format error if
Packit 1422b7
 * a) the to-be-parsed buffer is already positioned on the terminator character
Packit 1422b7
 * b) there is no terminator until the end of the buffer
Packit 1422b7
 * In those cases, the parsers declares itself as not being successful, in all
Packit 1422b7
 * other cases a string is extracted.
Packit 1422b7
 */
Packit 1422b7
PARSER_Parse(CharTo)
Packit 1422b7
	size_t i;
Packit 1422b7
	struct data_CharTo *const data = (struct data_CharTo*) pdata;
Packit 1422b7
Packit 1422b7
	assert(npb->str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	i = *offs;
Packit 1422b7
Packit 1422b7
	/* search end of word */
Packit 1422b7
	int found = 0;
Packit 1422b7
	while(i < npb->strLen && !found) {
Packit 1422b7
		for(size_t j = 0 ; j < data->n_term_chars ; ++j) {
Packit 1422b7
			if(npb->str[i] == data->term_chars[j]) {
Packit 1422b7
				found = 1;
Packit 1422b7
				break;
Packit 1422b7
			}
Packit 1422b7
		}
Packit 1422b7
		if(!found)
Packit 1422b7
			++i;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if(i == *offs || i == npb->strLen || !found)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = i - *offs;
Packit 1422b7
	if(value != NULL) {
Packit 1422b7
		*value = json_object_new_string_len(npb->str+(*offs), *parsed);
Packit 1422b7
	}
Packit 1422b7
	r = 0;
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
PARSER_Construct(CharTo)
Packit 1422b7
{
Packit 1422b7
	int r = 0;
Packit 1422b7
	LN_DBGPRINTF(ctx, "in parser_construct charTo");
Packit 1422b7
	struct data_CharTo *data = (struct data_CharTo*) calloc(1, sizeof(struct data_CharTo));
Packit 1422b7
	struct json_object *ed;
Packit 1422b7
Packit 1422b7
	if(json_object_object_get_ex(json, "extradata", &ed) == 0) {
Packit 1422b7
		ln_errprintf(ctx, 0, "char-to type needs 'extradata' parameter");
Packit 1422b7
		r = LN_BADCONFIG ;
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
	data->term_chars = strdup(json_object_get_string(ed));
Packit 1422b7
	data->n_term_chars = strlen(data->term_chars);
Packit 1422b7
	*pdata = data;
Packit 1422b7
done:
Packit 1422b7
	if(r != 0)
Packit 1422b7
		free(data);
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
PARSER_DataForDisplay(CharTo)
Packit 1422b7
{
Packit 1422b7
	struct data_CharTo *data = (struct data_CharTo*) pdata;
Packit 1422b7
	if(data->data_for_display == NULL) {
Packit 1422b7
		data->data_for_display = malloc(8+data->n_term_chars+2);
Packit 1422b7
		if(data->data_for_display != NULL) {
Packit 1422b7
			memcpy(data->data_for_display, "char-to{", 8);
Packit 1422b7
			size_t i, j;
Packit 1422b7
			for(j = 0, i = 8 ; j < data->n_term_chars ; ++j, ++i) {
Packit 1422b7
				data->data_for_display[i] = data->term_chars[j];
Packit 1422b7
			}
Packit 1422b7
			data->data_for_display[i++] = '}';
Packit 1422b7
			data->data_for_display[i] = '\0';
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
	return (data->data_for_display == NULL ) ? "malloc error" : data->data_for_display;
Packit 1422b7
}
Packit 1422b7
PARSER_Destruct(CharTo)
Packit 1422b7
{
Packit 1422b7
	struct data_CharTo *const data = (struct data_CharTo*) pdata;
Packit 1422b7
	free(data->data_for_display);
Packit 1422b7
	free(data->term_chars);
Packit 1422b7
	free(pdata);
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
Packit 1422b7
struct data_Literal {
Packit 1422b7
	const char *lit;
Packit 1422b7
	const char *json_conf;
Packit 1422b7
};
Packit 1422b7
/**
Packit 1422b7
 * Parse a specific literal.
Packit 1422b7
 */
Packit 1422b7
PARSER_Parse(Literal)
Packit 1422b7
	struct data_Literal *const data = (struct data_Literal*) pdata;
Packit 1422b7
	const char *const lit = data->lit;
Packit 1422b7
	size_t i = *offs;
Packit 1422b7
	size_t j;
Packit 1422b7
Packit 1422b7
	for(j = 0 ; i < npb->strLen ; ++j) {
Packit 1422b7
		if(lit[j] != npb->str[i])
Packit 1422b7
			break;
Packit 1422b7
		++i;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	*parsed = j; /* we must always return how far we parsed! */
Packit 1422b7
	if(lit[j] == '\0') {
Packit 1422b7
		if(value != NULL) {
Packit 1422b7
			*value = json_object_new_string_len(npb->str+(*offs), *parsed);
Packit 1422b7
		}
Packit 1422b7
		r = 0;
Packit 1422b7
	}
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
PARSER_DataForDisplay(Literal)
Packit 1422b7
{
Packit 1422b7
	struct data_Literal *data = (struct data_Literal*) pdata;
Packit 1422b7
	return data->lit;
Packit 1422b7
}
Packit 1422b7
PARSER_JsonConf(Literal)
Packit 1422b7
{
Packit 1422b7
	struct data_Literal *data = (struct data_Literal*) pdata;
Packit 1422b7
	return data->json_conf;
Packit 1422b7
}
Packit 1422b7
PARSER_Construct(Literal)
Packit 1422b7
{
Packit 1422b7
	int r = 0;
Packit 1422b7
	struct data_Literal *data = (struct data_Literal*) calloc(1, sizeof(struct data_Literal));
Packit 1422b7
	struct json_object *text;
Packit 1422b7
Packit 1422b7
	if(json_object_object_get_ex(json, "text", &text) == 0) {
Packit 1422b7
		ln_errprintf(ctx, 0, "literal type needs 'text' parameter");
Packit 1422b7
		r = LN_BADCONFIG ;
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
	data->lit = strdup(json_object_get_string(text));
Packit 1422b7
	data->json_conf = strdup(json_object_to_json_string(json));
Packit 1422b7
Packit 1422b7
	*pdata = data;
Packit 1422b7
done:
Packit 1422b7
	if(r != 0)
Packit 1422b7
		free(data);
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
PARSER_Destruct(Literal)
Packit 1422b7
{
Packit 1422b7
	struct data_Literal *data = (struct data_Literal*) pdata;
Packit 1422b7
	free((void*)data->lit);
Packit 1422b7
	free((void*)data->json_conf);
Packit 1422b7
	free(pdata);
Packit 1422b7
}
Packit 1422b7
/* for path compaction, we need a special handler to combine two
Packit 1422b7
 * literal data elements.
Packit 1422b7
 */
Packit 1422b7
int
Packit 1422b7
ln_combineData_Literal(void *const porg, void *const padd)
Packit 1422b7
{
Packit 1422b7
	struct data_Literal *const __restrict__ org = porg;
Packit 1422b7
	struct data_Literal *const __restrict__ add = padd;
Packit 1422b7
	int r = 0;
Packit 1422b7
	const size_t len = strlen(org->lit);
Packit 1422b7
	const size_t add_len = strlen(add->lit);
Packit 1422b7
	char *const newlit = (char*)realloc((void*)org->lit, len+add_len+1);
Packit 1422b7
	CHKN(newlit);
Packit 1422b7
	org->lit = newlit;
Packit 1422b7
	memcpy((char*)org->lit+len, add->lit, add_len+1);
Packit 1422b7
done:	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
struct data_CharSeparated {
Packit 1422b7
	char *term_chars;
Packit 1422b7
	size_t n_term_chars;
Packit 1422b7
};
Packit 1422b7
/**
Packit 1422b7
 * Parse everything up to a specific character, or up to the end of string.
Packit 1422b7
 * The character must be the only char inside extra data passed to the parser.
Packit 1422b7
 * This parser always returns success.
Packit 1422b7
 * By nature of the parser, it is required that end of string or the separator
Packit 1422b7
 * follows this field in rule.
Packit 1422b7
 */
Packit 1422b7
PARSER_Parse(CharSeparated)
Packit 1422b7
	struct data_CharSeparated *const data = (struct data_CharSeparated*) pdata;
Packit 1422b7
	size_t i;
Packit 1422b7
Packit 1422b7
	assert(npb->str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	i = *offs;
Packit 1422b7
Packit 1422b7
	/* search end of word */
Packit 1422b7
	int found = 0;
Packit 1422b7
	while(i < npb->strLen && !found) {
Packit 1422b7
		for(size_t j = 0 ; j < data->n_term_chars ; ++j) {
Packit 1422b7
			if(npb->str[i] == data->term_chars[j]) {
Packit 1422b7
				found = 1;
Packit 1422b7
				break;
Packit 1422b7
			}
Packit 1422b7
		}
Packit 1422b7
		if(!found)
Packit 1422b7
			++i;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = i - *offs;
Packit 1422b7
	if(value != NULL) {
Packit 1422b7
		*value = json_object_new_string_len(npb->str+(*offs), *parsed);
Packit 1422b7
	}
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
PARSER_Construct(CharSeparated)
Packit 1422b7
{
Packit 1422b7
	int r = 0;
Packit 1422b7
	struct data_CharSeparated *data = (struct data_CharSeparated*) calloc(1, sizeof(struct data_CharSeparated));
Packit 1422b7
	struct json_object *ed;
Packit 1422b7
Packit 1422b7
	if(json_object_object_get_ex(json, "extradata", &ed) == 0) {
Packit 1422b7
		ln_errprintf(ctx, 0, "char-separated type needs 'extradata' parameter");
Packit 1422b7
		r = LN_BADCONFIG ;
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	data->term_chars = strdup(json_object_get_string(ed));
Packit 1422b7
	data->n_term_chars = strlen(data->term_chars);
Packit 1422b7
	*pdata = data;
Packit 1422b7
done:
Packit 1422b7
	if(r != 0)
Packit 1422b7
		free(data);
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
PARSER_Destruct(CharSeparated)
Packit 1422b7
{
Packit 1422b7
	struct data_CharSeparated *const data = (struct data_CharSeparated*) pdata;
Packit 1422b7
	free(data->term_chars);
Packit 1422b7
	free(pdata);
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Just get everything till the end of string.
Packit 1422b7
 */
Packit 1422b7
PARSER_Parse(Rest)
Packit 1422b7
	assert(npb->str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
Packit 1422b7
	/* silence the warning about unused variable */
Packit 1422b7
	(void)npb->str;
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = npb->strLen - *offs;
Packit 1422b7
	if(value != NULL) {
Packit 1422b7
		*value = json_object_new_string_len(npb->str+(*offs), *parsed);
Packit 1422b7
	}
Packit 1422b7
	r = 0;
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse a possibly quoted string. In this initial implementation, escaping of the quote
Packit 1422b7
 * char is not supported. A quoted string is one start starts with a double quote,
Packit 1422b7
 * has some text (not containing double quotes) and ends with the first double
Packit 1422b7
 * quote character seen. The extracted string does NOT include the quote characters.
Packit 1422b7
 * swisskid, 2015-01-21
Packit 1422b7
 */
Packit 1422b7
PARSER_Parse(OpQuotedString)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i;
Packit 1422b7
	char *cstr = NULL;
Packit 1422b7
Packit 1422b7
	assert(npb->str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	c = npb->str;
Packit 1422b7
	i = *offs;
Packit 1422b7
Packit 1422b7
	if(c[i] != '"') {
Packit 1422b7
		while(i < npb->strLen && c[i] != ' ')
Packit 1422b7
			i++;
Packit 1422b7
Packit 1422b7
		if(i == *offs)
Packit 1422b7
			goto done;
Packit 1422b7
Packit 1422b7
		/* success, persist */
Packit 1422b7
		*parsed = i - *offs;
Packit 1422b7
		/* create JSON value to save quoted string contents */
Packit 1422b7
		CHKN(cstr = strndup((char*)c + *offs, *parsed));
Packit 1422b7
	} else {
Packit 1422b7
	    ++i;
Packit 1422b7
Packit 1422b7
	    /* search end of string */
Packit 1422b7
	    while(i < npb->strLen && c[i] != '"')
Packit 1422b7
		    i++;
Packit 1422b7
Packit 1422b7
	    if(i == npb->strLen || c[i] != '"')
Packit 1422b7
		    goto done;
Packit 1422b7
	    /* success, persist */
Packit 1422b7
	    *parsed = i + 1 - *offs; /* "eat" terminal double quote */
Packit 1422b7
	    /* create JSON value to save quoted string contents */
Packit 1422b7
	    CHKN(cstr = strndup((char*)c + *offs + 1, *parsed - 2));
Packit 1422b7
	}
Packit 1422b7
	CHKN(*value = json_object_new_string(cstr));
Packit 1422b7
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	free(cstr);
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse a quoted string. In this initial implementation, escaping of the quote
Packit 1422b7
 * char is not supported. A quoted string is one start starts with a double quote,
Packit 1422b7
 * has some text (not containing double quotes) and ends with the first double
Packit 1422b7
 * quote character seen. The extracted string does NOT include the quote characters.
Packit 1422b7
 * rgerhards, 2011-01-14
Packit 1422b7
 */
Packit 1422b7
PARSER_Parse(QuotedString)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i;
Packit 1422b7
Packit 1422b7
	assert(npb->str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	c = npb->str;
Packit 1422b7
	i = *offs;
Packit 1422b7
	if(i + 2 > npb->strLen)
Packit 1422b7
		goto done;	/* needs at least 2 characters */
Packit 1422b7
Packit 1422b7
	if(c[i] != '"')
Packit 1422b7
		goto done;
Packit 1422b7
	++i;
Packit 1422b7
Packit 1422b7
	/* search end of string */
Packit 1422b7
	while(i < npb->strLen && c[i] != '"')
Packit 1422b7
		i++;
Packit 1422b7
Packit 1422b7
	if(i == npb->strLen || c[i] != '"')
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = i + 1 - *offs; /* "eat" terminal double quote */
Packit 1422b7
	/* create JSON value to save quoted string contents */
Packit 1422b7
	if(value != NULL) {
Packit 1422b7
		*value = json_object_new_string_len(npb->str+(*offs), *parsed);
Packit 1422b7
	}
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse an ISO date, that is YYYY-MM-DD (exactly this format).
Packit 1422b7
 * Note: we do manual loop unrolling -- this is fast AND efficient.
Packit 1422b7
 * rgerhards, 2011-01-14
Packit 1422b7
 */
Packit 1422b7
PARSER_Parse(ISODate)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i;
Packit 1422b7
Packit 1422b7
	assert(npb->str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	c = npb->str;
Packit 1422b7
	i = *offs;
Packit 1422b7
Packit 1422b7
	if(*offs+10 > npb->strLen)
Packit 1422b7
		goto done;	/* if it is not 10 chars, it can't be an ISO date */
Packit 1422b7
Packit 1422b7
	/* year */
Packit 1422b7
	if(!myisdigit(c[i])) goto done;
Packit 1422b7
	if(!myisdigit(c[i+1])) goto done;
Packit 1422b7
	if(!myisdigit(c[i+2])) goto done;
Packit 1422b7
	if(!myisdigit(c[i+3])) goto done;
Packit 1422b7
	if(c[i+4] != '-') goto done;
Packit 1422b7
	/* month */
Packit 1422b7
	if(c[i+5] == '0') {
Packit 1422b7
		if(c[i+6] < '1' || c[i+6] > '9') goto done;
Packit 1422b7
	} else if(c[i+5] == '1') {
Packit 1422b7
		if(c[i+6] < '0' || c[i+6] > '2') goto done;
Packit 1422b7
	} else {
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
	if(c[i+7] != '-') goto done;
Packit 1422b7
	/* day */
Packit 1422b7
	if(c[i+8] == '0') {
Packit 1422b7
		if(c[i+9] < '1' || c[i+9] > '9') goto done;
Packit 1422b7
	} else if(c[i+8] == '1' || c[i+8] == '2') {
Packit 1422b7
		if(!myisdigit(c[i+9])) goto done;
Packit 1422b7
	} else if(c[i+8] == '3') {
Packit 1422b7
		if(c[i+9] != '0' && c[i+9] != '1') goto done;
Packit 1422b7
	} else {
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = 10;
Packit 1422b7
	if(value != NULL) {
Packit 1422b7
		*value = json_object_new_string_len(npb->str+(*offs), *parsed);
Packit 1422b7
	}
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse a Cisco interface spec. Sample for such a spec are:
Packit 1422b7
 *   outside:192.168.52.102/50349
Packit 1422b7
 *   inside:192.168.1.15/56543 (192.168.1.112/54543)
Packit 1422b7
 *   outside:192.168.1.13/50179 (192.168.1.13/50179)(LOCAL\some.user)
Packit 1422b7
 *   outside:192.168.1.25/41850(LOCAL\RG-867G8-DEL88D879BBFFC8)
Packit 1422b7
 *   inside:192.168.1.25/53 (192.168.1.25/53) (some.user)
Packit 1422b7
 *   192.168.1.15/0(LOCAL\RG-867G8-DEL88D879BBFFC8)
Packit 1422b7
 * From this, we conclude the format is:
Packit 1422b7
 *   [interface:]ip/port [SP (ip2/port2)] [[SP](username)]
Packit 1422b7
 * In order to match, this syntax must start on a non-whitespace char
Packit 1422b7
 * other than colon.
Packit 1422b7
 */
Packit 1422b7
PARSER_Parse(CiscoInterfaceSpec)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i;
Packit 1422b7
Packit 1422b7
	assert(npb->str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	c = npb->str;
Packit 1422b7
	i = *offs;
Packit 1422b7
Packit 1422b7
	if(c[i] == ':' || isspace(c[i])) goto done;
Packit 1422b7
Packit 1422b7
	/* first, check if we have an interface. We do this by trying
Packit 1422b7
	 * to detect if we have an IP. If we have, obviously no interface
Packit 1422b7
	 * is present. Otherwise, we check if we have a valid interface.
Packit 1422b7
	 */
Packit 1422b7
	int bHaveInterface = 0;
Packit 1422b7
	size_t idxInterface = 0;
Packit 1422b7
	size_t lenInterface = 0;
Packit 1422b7
	int bHaveIP = 0;
Packit 1422b7
	size_t lenIP;
Packit 1422b7
	size_t idxIP = i;
Packit 1422b7
	if(ln_v2_parseIPv4(npb, &i, NULL, &lenIP, NULL) == 0) {
Packit 1422b7
		bHaveIP = 1;
Packit 1422b7
		i += lenIP - 1; /* position on delimiter */
Packit 1422b7
	} else {
Packit 1422b7
		idxInterface = i;
Packit 1422b7
		while(i < npb->strLen) {
Packit 1422b7
			if(isspace(c[i])) goto done;
Packit 1422b7
			if(c[i] == ':')
Packit 1422b7
				break;
Packit 1422b7
			++i;
Packit 1422b7
		}
Packit 1422b7
		lenInterface = i - idxInterface;
Packit 1422b7
		bHaveInterface = 1;
Packit 1422b7
	}
Packit 1422b7
	if(i == npb->strLen) goto done;
Packit 1422b7
	++i; /* skip over colon */
Packit 1422b7
Packit 1422b7
	/* we now utilize our other parser helpers */
Packit 1422b7
	if(!bHaveIP) {
Packit 1422b7
		idxIP = i;
Packit 1422b7
		if(ln_v2_parseIPv4(npb, &i, NULL, &lenIP, NULL) != 0) goto done;
Packit 1422b7
		i += lenIP;
Packit 1422b7
	}
Packit 1422b7
	if(i == npb->strLen || c[i] != '/') goto done;
Packit 1422b7
	++i; /* skip slash */
Packit 1422b7
	const size_t idxPort = i;
Packit 1422b7
	size_t lenPort;
Packit 1422b7
	if(ln_v2_parseNumber(npb, &i, NULL, &lenPort, NULL) != 0) goto done;
Packit 1422b7
	i += lenPort;
Packit 1422b7
	if(i == npb->strLen) goto success;
Packit 1422b7
Packit 1422b7
	/* check if optional second ip/port is present
Packit 1422b7
	 * We assume we must at least have 5 chars [" (::1)"]
Packit 1422b7
	 */
Packit 1422b7
	int bHaveIP2 = 0;
Packit 1422b7
	size_t idxIP2 = 0, lenIP2 = 0;
Packit 1422b7
	size_t idxPort2 = 0, lenPort2 = 0;
Packit 1422b7
	if(i+5 < npb->strLen && c[i] == ' ' && c[i+1] == '(') {
Packit 1422b7
		size_t iTmp = i+2; /* skip over " (" */
Packit 1422b7
		idxIP2 = iTmp;
Packit 1422b7
		if(ln_v2_parseIPv4(npb, &iTmp, NULL, &lenIP2, NULL) == 0) {
Packit 1422b7
			iTmp += lenIP2;
Packit 1422b7
			if(i < npb->strLen || c[iTmp] == '/') {
Packit 1422b7
				++iTmp; /* skip slash */
Packit 1422b7
				idxPort2 = iTmp;
Packit 1422b7
				if(ln_v2_parseNumber(npb, &iTmp, NULL, &lenPort2, NULL) == 0) {
Packit 1422b7
					iTmp += lenPort2;
Packit 1422b7
					if(iTmp < npb->strLen && c[iTmp] == ')') {
Packit 1422b7
						i = iTmp + 1; /* match, so use new index */
Packit 1422b7
						bHaveIP2 = 1;
Packit 1422b7
					}
Packit 1422b7
				}
Packit 1422b7
			}
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* check if optional username is present
Packit 1422b7
	 * We assume we must at least have 3 chars ["(n)"]
Packit 1422b7
	 */
Packit 1422b7
	int bHaveUser = 0;
Packit 1422b7
	size_t idxUser = 0;
Packit 1422b7
	size_t lenUser = 0;
Packit 1422b7
	if(   (i+2 < npb->strLen && c[i] == '(' && !isspace(c[i+1]) )
Packit 1422b7
	   || (i+3 < npb->strLen && c[i] == ' ' && c[i+1] == '(' && !isspace(c[i+2])) ) {
Packit 1422b7
		idxUser = i + ((c[i] == ' ') ? 2 : 1); /* skip [SP]'(' */
Packit 1422b7
		size_t iTmp = idxUser;
Packit 1422b7
		while(iTmp < npb->strLen && !isspace(c[iTmp]) && c[iTmp] != ')')
Packit 1422b7
			++iTmp; /* just scan */
Packit 1422b7
		if(iTmp < npb->strLen && c[iTmp] == ')') {
Packit 1422b7
			i = iTmp + 1; /* we have a match, so use new index */
Packit 1422b7
			bHaveUser = 1;
Packit 1422b7
			lenUser = iTmp - idxUser;
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* all done, save data */
Packit 1422b7
	if(value == NULL)
Packit 1422b7
		goto success;
Packit 1422b7
Packit 1422b7
	CHKN(*value = json_object_new_object());
Packit 1422b7
	json_object *json;
Packit 1422b7
	if(bHaveInterface) {
Packit 1422b7
		CHKN(json = json_object_new_string_len(c+idxInterface, lenInterface));
Packit 1422b7
		json_object_object_add_ex(*value, "interface", json,
Packit 1422b7
			JSON_C_OBJECT_ADD_KEY_IS_NEW|JSON_C_OBJECT_KEY_IS_CONSTANT);
Packit 1422b7
	}
Packit 1422b7
	CHKN(json = json_object_new_string_len(c+idxIP, lenIP));
Packit 1422b7
	json_object_object_add_ex(*value, "ip", json, JSON_C_OBJECT_ADD_KEY_IS_NEW|JSON_C_OBJECT_KEY_IS_CONSTANT);
Packit 1422b7
	CHKN(json = json_object_new_string_len(c+idxPort, lenPort));
Packit 1422b7
	json_object_object_add_ex(*value, "port", json, JSON_C_OBJECT_ADD_KEY_IS_NEW|JSON_C_OBJECT_KEY_IS_CONSTANT);
Packit 1422b7
	if(bHaveIP2) {
Packit 1422b7
		CHKN(json = json_object_new_string_len(c+idxIP2, lenIP2));
Packit 1422b7
		json_object_object_add_ex(*value, "ip2", json,
Packit 1422b7
			JSON_C_OBJECT_ADD_KEY_IS_NEW|JSON_C_OBJECT_KEY_IS_CONSTANT);
Packit 1422b7
		CHKN(json = json_object_new_string_len(c+idxPort2, lenPort2));
Packit 1422b7
		json_object_object_add_ex(*value, "port2", json,
Packit 1422b7
			JSON_C_OBJECT_ADD_KEY_IS_NEW|JSON_C_OBJECT_KEY_IS_CONSTANT);
Packit 1422b7
	}
Packit 1422b7
	if(bHaveUser) {
Packit 1422b7
		CHKN(json = json_object_new_string_len(c+idxUser, lenUser));
Packit 1422b7
		json_object_object_add_ex(*value, "user", json,
Packit 1422b7
			JSON_C_OBJECT_ADD_KEY_IS_NEW|JSON_C_OBJECT_KEY_IS_CONSTANT);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
success: /* success, persist */
Packit 1422b7
	*parsed = i - *offs;
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	if(r != 0 && value != NULL && *value != NULL) {
Packit 1422b7
		json_object_put(*value);
Packit 1422b7
		*value = NULL; /* to be on the save side */
Packit 1422b7
	}
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse a duration. A duration is similar to a timestamp, except that
Packit 1422b7
 * it tells about time elapsed. As such, hours can be larger than 23
Packit 1422b7
 * and hours may also be specified by a single digit (this, for example,
Packit 1422b7
 * is commonly done in Cisco software).
Packit 1422b7
 * Note: we do manual loop unrolling -- this is fast AND efficient.
Packit 1422b7
 */
Packit 1422b7
PARSER_Parse(Duration)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i;
Packit 1422b7
Packit 1422b7
	assert(npb->str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	c = npb->str;
Packit 1422b7
	i = *offs;
Packit 1422b7
Packit 1422b7
	/* hour is a bit tricky */
Packit 1422b7
	if(!myisdigit(c[i])) goto done;
Packit 1422b7
	++i;
Packit 1422b7
	if(myisdigit(c[i]))
Packit 1422b7
		++i;
Packit 1422b7
	if(c[i] == ':')
Packit 1422b7
		++i;
Packit 1422b7
	else
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	if(i+5 > npb->strLen)
Packit 1422b7
		goto done;/* if it is not 5 chars from here, it can't be us */
Packit 1422b7
Packit 1422b7
	if(c[i] < '0' || c[i] > '5') goto done;
Packit 1422b7
	if(!myisdigit(c[i+1])) goto done;
Packit 1422b7
	if(c[i+2] != ':') goto done;
Packit 1422b7
	if(c[i+3] < '0' || c[i+3] > '5') goto done;
Packit 1422b7
	if(!myisdigit(c[i+4])) goto done;
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = (i + 5) - *offs;
Packit 1422b7
	if(value != NULL) {
Packit 1422b7
		*value = json_object_new_string_len(npb->str+(*offs), *parsed);
Packit 1422b7
	}
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse a timestamp in 24hr format (exactly HH:MM:SS).
Packit 1422b7
 * Note: we do manual loop unrolling -- this is fast AND efficient.
Packit 1422b7
 * rgerhards, 2011-01-14
Packit 1422b7
 */
Packit 1422b7
PARSER_Parse(Time24hr)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i;
Packit 1422b7
Packit 1422b7
	assert(npb->str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	c = npb->str;
Packit 1422b7
	i = *offs;
Packit 1422b7
Packit 1422b7
	if(*offs+8 > npb->strLen)
Packit 1422b7
		goto done;	/* if it is not 8 chars, it can't be us */
Packit 1422b7
Packit 1422b7
	/* hour */
Packit 1422b7
	if(c[i] == '0' || c[i] == '1') {
Packit 1422b7
		if(!myisdigit(c[i+1])) goto done;
Packit 1422b7
	} else if(c[i] == '2') {
Packit 1422b7
		if(c[i+1] < '0' || c[i+1] > '3') goto done;
Packit 1422b7
	} else {
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
	/* TODO: the code below is a duplicate of 24hr parser - create common function */
Packit 1422b7
	if(c[i+2] != ':') goto done;
Packit 1422b7
	if(c[i+3] < '0' || c[i+3] > '5') goto done;
Packit 1422b7
	if(!myisdigit(c[i+4])) goto done;
Packit 1422b7
	if(c[i+5] != ':') goto done;
Packit 1422b7
	if(c[i+6] < '0' || c[i+6] > '5') goto done;
Packit 1422b7
	if(!myisdigit(c[i+7])) goto done;
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = 8;
Packit 1422b7
	if(value != NULL) {
Packit 1422b7
		*value = json_object_new_string_len(npb->str+(*offs), *parsed);
Packit 1422b7
	}
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse a timestamp in 12hr format (exactly HH:MM:SS).
Packit 1422b7
 * Note: we do manual loop unrolling -- this is fast AND efficient.
Packit 1422b7
 * TODO: the code below is a duplicate of 24hr parser - create common function?
Packit 1422b7
 * rgerhards, 2011-01-14
Packit 1422b7
 */
Packit 1422b7
PARSER_Parse(Time12hr)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i;
Packit 1422b7
Packit 1422b7
	assert(npb->str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	c = npb->str;
Packit 1422b7
	i = *offs;
Packit 1422b7
Packit 1422b7
	if(*offs+8 > npb->strLen)
Packit 1422b7
		goto done;	/* if it is not 8 chars, it can't be us */
Packit 1422b7
Packit 1422b7
	/* hour */
Packit 1422b7
	if(c[i] == '0') {
Packit 1422b7
		if(!myisdigit(c[i+1])) goto done;
Packit 1422b7
	} else if(c[i] == '1') {
Packit 1422b7
		if(c[i+1] < '0' || c[i+1] > '2') goto done;
Packit 1422b7
	} else {
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
	if(c[i+2] != ':') goto done;
Packit 1422b7
	if(c[i+3] < '0' || c[i+3] > '5') goto done;
Packit 1422b7
	if(!myisdigit(c[i+4])) goto done;
Packit 1422b7
	if(c[i+5] != ':') goto done;
Packit 1422b7
	if(c[i+6] < '0' || c[i+6] > '5') goto done;
Packit 1422b7
	if(!myisdigit(c[i+7])) goto done;
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = 8;
Packit 1422b7
	if(value != NULL) {
Packit 1422b7
		*value = json_object_new_string_len(npb->str+(*offs), *parsed);
Packit 1422b7
	}
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/* helper to IPv4 address parser, checks the next set of numbers.
Packit 1422b7
 * Syntax 1 to 3 digits, value together not larger than 255.
Packit 1422b7
 * @param[in] npb->str parse buffer
Packit 1422b7
 * @param[in/out] offs offset into buffer, updated if successful
Packit 1422b7
 * @return 0 if OK, 1 otherwise
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
chkIPv4AddrByte(npb_t *const npb, size_t *offs)
Packit 1422b7
{
Packit 1422b7
	int val = 0;
Packit 1422b7
	int r = 1;	/* default: done -- simplifies things */
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i = *offs;
Packit 1422b7
Packit 1422b7
	c = npb->str;
Packit 1422b7
	if(i == npb->strLen || !myisdigit(c[i]))
Packit 1422b7
		goto done;
Packit 1422b7
	val = c[i++] - '0';
Packit 1422b7
	if(i < npb->strLen && myisdigit(c[i])) {
Packit 1422b7
		val = val * 10 + c[i++] - '0';
Packit 1422b7
		if(i < npb->strLen && myisdigit(c[i]))
Packit 1422b7
			val = val * 10 + c[i++] - '0';
Packit 1422b7
	}
Packit 1422b7
	if(val > 255)	/* cannot be a valid IP address byte! */
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	*offs = i;
Packit 1422b7
	r = 0;
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parser for IPv4 addresses.
Packit 1422b7
 */
Packit 1422b7
PARSER_Parse(IPv4)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i;
Packit 1422b7
Packit 1422b7
	assert(npb->str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	i = *offs;
Packit 1422b7
	if(i + 7 > npb->strLen) {
Packit 1422b7
		/* IPv4 addr requires at least 7 characters */
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
	c = npb->str;
Packit 1422b7
Packit 1422b7
	/* byte 1*/
Packit 1422b7
	if(chkIPv4AddrByte(npb, &i) != 0) goto done;
Packit 1422b7
	if(i == npb->strLen || c[i++] != '.') goto done;
Packit 1422b7
	/* byte 2*/
Packit 1422b7
	if(chkIPv4AddrByte(npb, &i) != 0) goto done;
Packit 1422b7
	if(i == npb->strLen || c[i++] != '.') goto done;
Packit 1422b7
	/* byte 3*/
Packit 1422b7
	if(chkIPv4AddrByte(npb, &i) != 0) goto done;
Packit 1422b7
	if(i == npb->strLen || c[i++] != '.') goto done;
Packit 1422b7
	/* byte 4 - we do NOT need any char behind it! */
Packit 1422b7
	if(chkIPv4AddrByte(npb, &i) != 0) goto done;
Packit 1422b7
Packit 1422b7
	/* if we reach this point, we found a valid IP address */
Packit 1422b7
	*parsed = i - *offs;
Packit 1422b7
	if(value != NULL) {
Packit 1422b7
		*value = json_object_new_string_len(npb->str+(*offs), *parsed);
Packit 1422b7
	}
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/* skip past the IPv6 address block, parse pointer is set to
Packit 1422b7
 * first char after the block. Returns an error if already at end
Packit 1422b7
 * of string.
Packit 1422b7
 * @param[in] npb->str parse buffer
Packit 1422b7
 * @param[in/out] offs offset into buffer, updated if successful
Packit 1422b7
 * @return 0 if OK, 1 otherwise
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
skipIPv6AddrBlock(npb_t *const npb,
Packit 1422b7
	size_t *const __restrict__ offs)
Packit 1422b7
{
Packit 1422b7
	int j;
Packit 1422b7
	if(*offs == npb->strLen)
Packit 1422b7
		return 1;
Packit 1422b7
Packit 1422b7
	for(j = 0 ; j < 4  && *offs+j < npb->strLen && isxdigit(npb->str[*offs+j]) ; ++j)
Packit 1422b7
		/*just skip*/ ;
Packit 1422b7
Packit 1422b7
	*offs += j;
Packit 1422b7
	return 0;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parser for IPv6 addresses.
Packit 1422b7
 * Bases on RFC4291 Section 2.2. The address must be followed
Packit 1422b7
 * by whitespace or end-of-string, else it is not considered
Packit 1422b7
 * a valid address. This prevents false positives.
Packit 1422b7
 */
Packit 1422b7
PARSER_Parse(IPv6)
Packit 1422b7
	const char *c;
Packit 1422b7
	size_t i;
Packit 1422b7
	size_t beginBlock; /* last block begin in case we need IPv4 parsing */
Packit 1422b7
	int hasIPv4 = 0;
Packit 1422b7
	int nBlocks = 0; /* how many blocks did we already have? */
Packit 1422b7
	int bHad0Abbrev = 0; /* :: already used? */
Packit 1422b7
Packit 1422b7
	assert(npb->str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	i = *offs;
Packit 1422b7
	if(i + 2 > npb->strLen) {
Packit 1422b7
		/* IPv6 addr requires at least 2 characters ("::") */
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
	c = npb->str;
Packit 1422b7
Packit 1422b7
	/* check that first block is non-empty */
Packit 1422b7
	if(! ( isxdigit(c[i]) || (c[i] == ':' && c[i+1] == ':') ) )
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	/* try for all potential blocks plus one more (so we see errors!) */
Packit 1422b7
	for(int j = 0 ; j < 9 ; ++j) {
Packit 1422b7
		beginBlock = i;
Packit 1422b7
		if(skipIPv6AddrBlock(npb, &i) != 0) goto done;
Packit 1422b7
		nBlocks++;
Packit 1422b7
		if(i == npb->strLen) goto chk_ok;
Packit 1422b7
		if(isspace(c[i])) goto chk_ok;
Packit 1422b7
		if(c[i] == '.'){ /* IPv4 processing! */
Packit 1422b7
			hasIPv4 = 1;
Packit 1422b7
			break;
Packit 1422b7
		}
Packit 1422b7
		if(c[i] != ':') goto done;
Packit 1422b7
		i++; /* "eat" ':' */
Packit 1422b7
		if(i == npb->strLen) goto chk_ok;
Packit 1422b7
		/* check for :: */
Packit 1422b7
		if(bHad0Abbrev) {
Packit 1422b7
			if(c[i] == ':') goto done;
Packit 1422b7
		} else {
Packit 1422b7
			if(c[i] == ':') {
Packit 1422b7
				bHad0Abbrev = 1;
Packit 1422b7
				++i;
Packit 1422b7
				if(i == npb->strLen) goto chk_ok;
Packit 1422b7
			}
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if(hasIPv4) {
Packit 1422b7
		size_t ipv4_parsed;
Packit 1422b7
		--nBlocks;
Packit 1422b7
		/* prevent pure IPv4 address to be recognized */
Packit 1422b7
		if(beginBlock == *offs) goto done;
Packit 1422b7
		i = beginBlock;
Packit 1422b7
		if(ln_v2_parseIPv4(npb, &i, NULL, &ipv4_parsed, NULL) != 0)
Packit 1422b7
			goto done;
Packit 1422b7
		i += ipv4_parsed;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
chk_ok:	/* we are finished parsing, check if things are ok */
Packit 1422b7
	if(nBlocks > 8) goto done;
Packit 1422b7
	if(bHad0Abbrev && nBlocks >= 8) goto done;
Packit 1422b7
	/* now check if trailing block is missing. Note that i is already
Packit 1422b7
	 * on next character, so we need to go two back. Two are always
Packit 1422b7
	 * present, else we would not reach this code here.
Packit 1422b7
	 */
Packit 1422b7
	if(c[i-1] == ':' && c[i-2] != ':') goto done;
Packit 1422b7
Packit 1422b7
	/* if we reach this point, we found a valid IP address */
Packit 1422b7
	*parsed = i - *offs;
Packit 1422b7
	if(value != NULL) {
Packit 1422b7
		*value = json_object_new_string_len(npb->str+(*offs), *parsed);
Packit 1422b7
	}
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/* check if a char is valid inside a name of the iptables motif.
Packit 1422b7
 * We try to keep the set as slim as possible, because the iptables
Packit 1422b7
 * parser may otherwise create a very broad match (especially the
Packit 1422b7
 * inclusion of simple words like "DF" cause grief here).
Packit 1422b7
 * Note: we have taken the permitted set from iptables log samples.
Packit 1422b7
 * Report bugs if we missed some additional rules.
Packit 1422b7
 */
Packit 1422b7
static inline int
Packit 1422b7
isValidIPTablesNameChar(const char c)
Packit 1422b7
{
Packit 1422b7
	/* right now, upper case only is valid */
Packit 1422b7
	return ('A' <= c && c <= 'Z') ? 1 : 0;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/* helper to iptables parser, parses out a a single name=value pair
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
parseIPTablesNameValue(npb_t *const npb,
Packit 1422b7
	size_t *const __restrict__ offs,
Packit 1422b7
	struct json_object *const __restrict__ valroot)
Packit 1422b7
{
Packit 1422b7
	int r = LN_WRONGPARSER;
Packit 1422b7
	size_t i = *offs;
Packit 1422b7
	char *name = NULL;
Packit 1422b7
Packit 1422b7
	const size_t iName = i;
Packit 1422b7
	while(i < npb->strLen && isValidIPTablesNameChar(npb->str[i]))
Packit 1422b7
		++i;
Packit 1422b7
	if(i == iName || (i < npb->strLen && npb->str[i] != '=' && npb->str[i] != ' '))
Packit 1422b7
		goto done; /* no name at all! */
Packit 1422b7
Packit 1422b7
	const ssize_t lenName = i - iName;
Packit 1422b7
Packit 1422b7
	ssize_t iVal = -1;
Packit 1422b7
	size_t lenVal = i - iVal;
Packit 1422b7
	if(i < npb->strLen && npb->str[i] != ' ') {
Packit 1422b7
		/* we have a real value (not just a flag name like "DF") */
Packit 1422b7
		++i; /* skip '=' */
Packit 1422b7
		iVal = i;
Packit 1422b7
		while(i < npb->strLen && !isspace(npb->str[i]))
Packit 1422b7
			++i;
Packit 1422b7
		lenVal = i - iVal;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* parsing OK */
Packit 1422b7
	*offs = i;
Packit 1422b7
	r = 0;
Packit 1422b7
Packit 1422b7
	if(valroot == NULL)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	CHKN(name = malloc(lenName+1));
Packit 1422b7
	memcpy(name, npb->str+iName, lenName);
Packit 1422b7
	name[lenName] = '\0';
Packit 1422b7
	json_object *json;
Packit 1422b7
	if(iVal == -1) {
Packit 1422b7
		json = NULL;
Packit 1422b7
	} else {
Packit 1422b7
		CHKN(json = json_object_new_string_len(npb->str+iVal, lenVal));
Packit 1422b7
	}
Packit 1422b7
	json_object_object_add(valroot, name, json);
Packit 1422b7
done:
Packit 1422b7
	free(name);
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parser for iptables logs (the structured part).
Packit 1422b7
 * This parser is named "v2-iptables" because of a traditional
Packit 1422b7
 * parser named "iptables", which we do not want to replace, at
Packit 1422b7
 * least right now (we may re-think this before the first release).
Packit 1422b7
 * For performance reasons, this works in two stages. In the first
Packit 1422b7
 * stage, we only detect if the motif is correct. The second stage is
Packit 1422b7
 * only called when we know it is. In it, we go once again over the
Packit 1422b7
 * message again and actually extract the data. This is done because
Packit 1422b7
 * data extraction is relatively expensive and in most cases we will
Packit 1422b7
 * have much more frequent mismatches than matches.
Packit 1422b7
 * Note that this motif must have at least one field, otherwise it
Packit 1422b7
 * could detect things that are not iptables to be it. Further limits
Packit 1422b7
 * may be imposed in the future as we see additional need.
Packit 1422b7
 * added 2015-04-30 rgerhards
Packit 1422b7
 */
Packit 1422b7
PARSER_Parse(v2IPTables)
Packit 1422b7
	size_t i = *offs;
Packit 1422b7
	int nfields = 0;
Packit 1422b7
Packit 1422b7
	/* stage one */
Packit 1422b7
	while(i < npb->strLen) {
Packit 1422b7
		CHKR(parseIPTablesNameValue(npb, &i, NULL));
Packit 1422b7
		++nfields;
Packit 1422b7
		/* exactly one SP is permitted between fields */
Packit 1422b7
		if(i < npb->strLen && npb->str[i] == ' ')
Packit 1422b7
			++i;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if(nfields < 2) {
Packit 1422b7
		FAIL(LN_WRONGPARSER);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = i - *offs;
Packit 1422b7
	r = 0;
Packit 1422b7
Packit 1422b7
	/* stage two */
Packit 1422b7
	if(value == NULL)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	i = *offs;
Packit 1422b7
	CHKN(*value = json_object_new_object());
Packit 1422b7
	while(i < npb->strLen) {
Packit 1422b7
		CHKR(parseIPTablesNameValue(npb, &i, *value));
Packit 1422b7
		while(i < npb->strLen && isspace(npb->str[i]))
Packit 1422b7
			++i;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
done:
Packit 1422b7
	if(r != 0 && value != NULL && *value != NULL) {
Packit 1422b7
		json_object_put(*value);
Packit 1422b7
		*value = NULL;
Packit 1422b7
	}
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse JSON. This parser tries to find JSON data inside a message.
Packit 1422b7
 * If it finds valid JSON, it will extract it. Extra data after the
Packit 1422b7
 * JSON is permitted.
Packit 1422b7
 * Note: the json-c JSON parser treats whitespace after the actual
Packit 1422b7
 * json to be part of the json. So in essence, any whitespace is
Packit 1422b7
 * processed by this parser. We use the same semantics to keep things
Packit 1422b7
 * neatly in sync. If json-c changes for some reason or we switch to
Packit 1422b7
 * an alternate json lib, we probably need to be sure to keep that
Packit 1422b7
 * behaviour, and probably emulate it.
Packit 1422b7
 * added 2015-04-28 by rgerhards, v1.1.2
Packit 1422b7
 */
Packit 1422b7
PARSER_Parse(JSON)
Packit 1422b7
	const size_t i = *offs;
Packit 1422b7
	struct json_tokener *tokener = NULL;
Packit 1422b7
Packit 1422b7
	if(npb->str[i] != '{' && npb->str[i] != ']') {
Packit 1422b7
		/* this can't be json, see RFC4627, Sect. 2
Packit 1422b7
		 * see this bug in json-c:
Packit 1422b7
		 * https://github.com/json-c/json-c/issues/181
Packit 1422b7
		 * In any case, it's better to do this quick check,
Packit 1422b7
		 * even if json-c did not have the bug because this
Packit 1422b7
		 * check here is much faster than calling the parser.
Packit 1422b7
		 */
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if((tokener = json_tokener_new()) == NULL)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	struct json_object *const json
Packit 1422b7
		= json_tokener_parse_ex(tokener, npb->str+i, (int) (npb->strLen - i));
Packit 1422b7
Packit 1422b7
	if(json == NULL)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed =  (i + tokener->char_offset) - *offs;
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
Packit 1422b7
	if(value == NULL) {
Packit 1422b7
		json_object_put(json);
Packit 1422b7
	} else {
Packit 1422b7
		*value = json;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
done:
Packit 1422b7
	if(tokener != NULL)
Packit 1422b7
		json_tokener_free(tokener);
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/* check if a char is valid inside a name of a NameValue list
Packit 1422b7
 * The set of valid characters may be extended if there is good
Packit 1422b7
 * need to do so. We have selected the current set carefully, but
Packit 1422b7
 * may have overlooked some cases.
Packit 1422b7
 */
Packit 1422b7
static inline int
Packit 1422b7
isValidNameChar(const char c)
Packit 1422b7
{
Packit 1422b7
	return (isalnum(c)
Packit 1422b7
		|| c == '.'
Packit 1422b7
		|| c == '_'
Packit 1422b7
		|| c == '-'
Packit 1422b7
		) ? 1 : 0;
Packit 1422b7
}
Packit 1422b7
/* helper to NameValue parser, parses out a a single name=value pair
Packit 1422b7
 *
Packit 1422b7
 * name must be alphanumeric characters, value must be non-whitespace
Packit 1422b7
 * characters, if quoted than with symmetric quotes. Supported formats
Packit 1422b7
 * - name=value
Packit 1422b7
 * - name="value"
Packit 1422b7
 * - name='value'
Packit 1422b7
 * Note "name=" is valid and means a field with empty value.
Packit 1422b7
 * TODO: so far, quote characters are not permitted WITHIN quoted values.
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
parseNameValue(npb_t *const npb,
Packit 1422b7
	size_t *const __restrict__ offs,
Packit 1422b7
	struct json_object *const __restrict__ valroot)
Packit 1422b7
{
Packit 1422b7
	int r = LN_WRONGPARSER;
Packit 1422b7
	size_t i = *offs;
Packit 1422b7
	char *name = NULL;
Packit 1422b7
Packit 1422b7
	const size_t iName = i;
Packit 1422b7
	while(i < npb->strLen && isValidNameChar(npb->str[i]))
Packit 1422b7
		++i;
Packit 1422b7
	if(i == iName || npb->str[i] != '=')
Packit 1422b7
		goto done; /* no name at all! */
Packit 1422b7
Packit 1422b7
	const size_t lenName = i - iName;
Packit 1422b7
	++i; /* skip '=' */
Packit 1422b7
Packit 1422b7
	const size_t iVal = i;
Packit 1422b7
	while(i < npb->strLen && !isspace(npb->str[i]))
Packit 1422b7
		++i;
Packit 1422b7
	const size_t lenVal = i - iVal;
Packit 1422b7
Packit 1422b7
	/* parsing OK */
Packit 1422b7
	*offs = i;
Packit 1422b7
	r = 0;
Packit 1422b7
Packit 1422b7
	if(valroot == NULL)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	CHKN(name = malloc(lenName+1));
Packit 1422b7
	memcpy(name, npb->str+iName, lenName);
Packit 1422b7
	name[lenName] = '\0';
Packit 1422b7
	json_object *json;
Packit 1422b7
	CHKN(json = json_object_new_string_len(npb->str+iVal, lenVal));
Packit 1422b7
	json_object_object_add(valroot, name, json);
Packit 1422b7
done:
Packit 1422b7
	free(name);
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse CEE syslog.
Packit 1422b7
 * This essentially is a JSON parser, with additional restrictions:
Packit 1422b7
 * The message must start with "@cee:" and json must immediately follow (whitespace permitted).
Packit 1422b7
 * after the JSON, there must be no other non-whitespace characters.
Packit 1422b7
 * In other words: the message must consist of a single JSON object,
Packit 1422b7
 * only.
Packit 1422b7
 * added 2015-04-28 by rgerhards, v1.1.2
Packit 1422b7
 */
Packit 1422b7
PARSER_Parse(CEESyslog)
Packit 1422b7
	size_t i = *offs;
Packit 1422b7
	struct json_tokener *tokener = NULL;
Packit 1422b7
	struct json_object *json = NULL;
Packit 1422b7
Packit 1422b7
	if(npb->strLen < i + 7  || /* "@cee:{}" is minimum text */
Packit 1422b7
	   npb->str[i]   != '@' ||
Packit 1422b7
	   npb->str[i+1] != 'c' ||
Packit 1422b7
	   npb->str[i+2] != 'e' ||
Packit 1422b7
	   npb->str[i+3] != 'e' ||
Packit 1422b7
	   npb->str[i+4] != ':')
Packit 1422b7
	   	goto done;
Packit 1422b7
	
Packit 1422b7
	/* skip whitespace */
Packit 1422b7
	for(i += 5 ; i < npb->strLen && isspace(npb->str[i]) ; ++i)
Packit 1422b7
		/* just skip */;
Packit 1422b7
Packit 1422b7
	if(i == npb->strLen || npb->str[i] != '{')
Packit 1422b7
		goto done;
Packit 1422b7
		/* note: we do not permit arrays in CEE mode */
Packit 1422b7
Packit 1422b7
	if((tokener = json_tokener_new()) == NULL)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	json = json_tokener_parse_ex(tokener, npb->str+i, (int) (npb->strLen - i));
Packit 1422b7
Packit 1422b7
	if(json == NULL)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	if(i + tokener->char_offset != npb->strLen)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed =  npb->strLen;
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
Packit 1422b7
	if(value != NULL) {
Packit 1422b7
		*value = json;
Packit 1422b7
		json = NULL; /* do NOT free below! */
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
done:
Packit 1422b7
	if(tokener != NULL)
Packit 1422b7
		json_tokener_free(tokener);
Packit 1422b7
	if(json != NULL)
Packit 1422b7
		json_object_put(json);
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parser for name/value pairs.
Packit 1422b7
 * On entry must point to alnum char. All following chars must be
Packit 1422b7
 * name/value pairs delimited by whitespace up until the end of string.
Packit 1422b7
 * For performance reasons, this works in two stages. In the first
Packit 1422b7
 * stage, we only detect if the motif is correct. The second stage is
Packit 1422b7
 * only called when we know it is. In it, we go once again over the
Packit 1422b7
 * message again and actually extract the data. This is done because
Packit 1422b7
 * data extraction is relatively expensive and in most cases we will
Packit 1422b7
 * have much more frequent mismatches than matches.
Packit 1422b7
 * added 2015-04-25 rgerhards
Packit 1422b7
 */
Packit 1422b7
PARSER_Parse(NameValue)
Packit 1422b7
	size_t i = *offs;
Packit 1422b7
Packit 1422b7
	/* stage one */
Packit 1422b7
	while(i < npb->strLen) {
Packit 1422b7
		CHKR(parseNameValue(npb, &i, NULL));
Packit 1422b7
		while(i < npb->strLen && isspace(npb->str[i]))
Packit 1422b7
			++i;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = i - *offs;
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
Packit 1422b7
	/* stage two */
Packit 1422b7
	if(value == NULL)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	i = *offs;
Packit 1422b7
	CHKN(*value = json_object_new_object());
Packit 1422b7
	while(i < npb->strLen) {
Packit 1422b7
		CHKR(parseNameValue(npb, &i, *value));
Packit 1422b7
		while(i < npb->strLen && isspace(npb->str[i]))
Packit 1422b7
			++i;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* TODO: fix mem leak if alloc json fails */
Packit 1422b7
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parse a MAC layer address.
Packit 1422b7
 * The standard (IEEE 802) format for printing MAC-48 addresses in
Packit 1422b7
 * human-friendly form is six groups of two hexadecimal digits,
Packit 1422b7
 * separated by hyphens (-) or colons (:), in transmission order
Packit 1422b7
 * (e.g. 01-23-45-67-89-ab or 01:23:45:67:89:ab ).
Packit 1422b7
 * This form is also commonly used for EUI-64.
Packit 1422b7
 * from: http://en.wikipedia.org/wiki/MAC_address
Packit 1422b7
 *
Packit 1422b7
 * This parser must start on a hex digit.
Packit 1422b7
 * added 2015-05-04 by rgerhards, v1.1.2
Packit 1422b7
 */
Packit 1422b7
PARSER_Parse(MAC48)
Packit 1422b7
	size_t i = *offs;
Packit 1422b7
	char delim;
Packit 1422b7
Packit 1422b7
	if(npb->strLen < i + 17 || /* this motif has exactly 17 characters */
Packit 1422b7
	   !isxdigit(npb->str[i]) ||
Packit 1422b7
	   !isxdigit(npb->str[i+1])
Packit 1422b7
	   )
Packit 1422b7
		FAIL(LN_WRONGPARSER);
Packit 1422b7
Packit 1422b7
	if(npb->str[i+2] == ':')
Packit 1422b7
		delim = ':';
Packit 1422b7
	else if(npb->str[i+2] == '-')
Packit 1422b7
		delim = '-';
Packit 1422b7
	else
Packit 1422b7
		FAIL(LN_WRONGPARSER);
Packit 1422b7
Packit 1422b7
	/* first byte ok */
Packit 1422b7
	if(!isxdigit(npb->str[i+3])  ||
Packit 1422b7
	   !isxdigit(npb->str[i+4])  ||
Packit 1422b7
	   npb->str[i+5] != delim    || /* 2nd byte ok */
Packit 1422b7
	   !isxdigit(npb->str[i+6])  ||
Packit 1422b7
	   !isxdigit(npb->str[i+7])  ||
Packit 1422b7
	   npb->str[i+8] != delim    || /* 3rd byte ok */
Packit 1422b7
	   !isxdigit(npb->str[i+9])  ||
Packit 1422b7
	   !isxdigit(npb->str[i+10]) ||
Packit 1422b7
	   npb->str[i+11] != delim   || /* 4th byte ok */
Packit 1422b7
	   !isxdigit(npb->str[i+12]) ||
Packit 1422b7
	   !isxdigit(npb->str[i+13]) ||
Packit 1422b7
	   npb->str[i+14] != delim   || /* 5th byte ok */
Packit 1422b7
	   !isxdigit(npb->str[i+15]) ||
Packit 1422b7
	   !isxdigit(npb->str[i+16])    /* 6th byte ok */
Packit 1422b7
	   )
Packit 1422b7
		FAIL(LN_WRONGPARSER);
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = 17;
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
Packit 1422b7
	if(value != NULL) {
Packit 1422b7
		CHKN(*value = json_object_new_string_len(npb->str+i, 17));
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/* This parses the extension value and updates the index
Packit 1422b7
 * to point to the end of it.
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
cefParseExtensionValue(npb_t *const npb,
Packit 1422b7
	size_t *__restrict__ iEndVal)
Packit 1422b7
{
Packit 1422b7
	int r = 0;
Packit 1422b7
	size_t i = *iEndVal;
Packit 1422b7
	size_t iLastWordBegin;
Packit 1422b7
	/* first find next unquoted equal sign and record begin of
Packit 1422b7
	 * last word in front of it - this is the actual end of the
Packit 1422b7
	 * current name/value pair and the begin of the next one.
Packit 1422b7
	 */
Packit 1422b7
	int hadSP = 0;
Packit 1422b7
	int inEscape = 0;
Packit 1422b7
	for(iLastWordBegin = 0 ; i < npb->strLen ; ++i) {
Packit 1422b7
		if(inEscape) {
Packit 1422b7
			if(npb->str[i] != '=' &&
Packit 1422b7
			   npb->str[i] != '\\' &&
Packit 1422b7
			   npb->str[i] != 'r' &&
Packit 1422b7
			   npb->str[i] != 'n')
Packit 1422b7
			FAIL(LN_WRONGPARSER);
Packit 1422b7
			inEscape = 0;
Packit 1422b7
		} else {
Packit 1422b7
			if(npb->str[i] == '=') {
Packit 1422b7
				break;
Packit 1422b7
			} else if(npb->str[i] == '\\') {
Packit 1422b7
				inEscape = 1;
Packit 1422b7
			} else if(npb->str[i] == ' ') {
Packit 1422b7
				hadSP = 1;
Packit 1422b7
			} else {
Packit 1422b7
				if(hadSP) {
Packit 1422b7
					iLastWordBegin = i;
Packit 1422b7
					hadSP = 0;
Packit 1422b7
				}
Packit 1422b7
			}
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* Note: iLastWordBegin can never be at offset zero, because
Packit 1422b7
	 * the CEF header starts there!
Packit 1422b7
	 */
Packit 1422b7
	if(i < npb->strLen) {
Packit 1422b7
		*iEndVal = (iLastWordBegin == 0) ? i : iLastWordBegin - 1;
Packit 1422b7
	} else {
Packit 1422b7
		*iEndVal = i;
Packit 1422b7
	}
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/* must be positioned on first char of name, returns index
Packit 1422b7
 * of end of name.
Packit 1422b7
 * Note: ArcSight violates the CEF spec ifself: they generate
Packit 1422b7
 * leading underscores in their extension names, which are
Packit 1422b7
 * definetly not alphanumeric. We still accept them...
Packit 1422b7
 * They also seem to use dots.
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
cefParseName(npb_t *const npb,
Packit 1422b7
	size_t *const __restrict__ i)
Packit 1422b7
{
Packit 1422b7
	int r = 0;
Packit 1422b7
	while(*i < npb->strLen && npb->str[*i] != '=') {
Packit 1422b7
		if(!(isalnum(npb->str[*i]) || npb->str[*i] == '_' || npb->str[*i] == '.'))
Packit 1422b7
			FAIL(LN_WRONGPARSER);
Packit 1422b7
		++(*i);
Packit 1422b7
	}
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/* parse CEF extensions. They are basically name=value
Packit 1422b7
 * pairs with the ugly exception that values may contain
Packit 1422b7
 * spaces but need NOT to be quoted. Thankfully, at least
Packit 1422b7
 * names are specified as being alphanumeric without spaces
Packit 1422b7
 * in them. So we must add a lookahead parser to check if
Packit 1422b7
 * a word is a name (and thus the begin of a new pair) or
Packit 1422b7
 * not. This is done by subroutines.
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
cefParseExtensions(npb_t *const npb,
Packit 1422b7
	size_t *const __restrict__ offs,
Packit 1422b7
	json_object *const __restrict__ jroot)
Packit 1422b7
{
Packit 1422b7
	int r = 0;
Packit 1422b7
	size_t i = *offs;
Packit 1422b7
	size_t iName, lenName;
Packit 1422b7
	size_t iValue, lenValue;
Packit 1422b7
	char *name = NULL;
Packit 1422b7
	char *value = NULL;
Packit 1422b7
Packit 1422b7
	while(i < npb->strLen) {
Packit 1422b7
		while(i < npb->strLen && npb->str[i] == ' ')
Packit 1422b7
			++i;
Packit 1422b7
		iName = i;
Packit 1422b7
		CHKR(cefParseName(npb, &i);;
Packit 1422b7
		if(i+1 >= npb->strLen || npb->str[i] != '=')
Packit 1422b7
			FAIL(LN_WRONGPARSER);
Packit 1422b7
		lenName = i - iName;
Packit 1422b7
		++i; /* skip '=' */
Packit 1422b7
Packit 1422b7
		iValue = i;
Packit 1422b7
		CHKR(cefParseExtensionValue(npb, &i);;
Packit 1422b7
		lenValue = i - iValue;
Packit 1422b7
Packit 1422b7
		++i; /* skip past value */
Packit 1422b7
Packit 1422b7
		if(jroot != NULL) {
Packit 1422b7
			CHKN(name = malloc(sizeof(char) * (lenName + 1)));
Packit 1422b7
			memcpy(name, npb->str+iName, lenName);
Packit 1422b7
			name[lenName] = '\0';
Packit 1422b7
			CHKN(value = malloc(sizeof(char) * (lenValue + 1)));
Packit 1422b7
			/* copy value but escape it */
Packit 1422b7
			size_t iDst = 0;
Packit 1422b7
			for(size_t iSrc = 0 ; iSrc < lenValue ; ++iSrc) {
Packit 1422b7
				if(npb->str[iValue+iSrc] == '\\') {
Packit 1422b7
					++iSrc; /* we know the next char must exist! */
Packit 1422b7
					switch(npb->str[iValue+iSrc]) {
Packit 1422b7
					case '=':	value[iDst] = '=';
Packit 1422b7
							break;
Packit 1422b7
					case 'n':	value[iDst] = '\n';
Packit 1422b7
							break;
Packit 1422b7
					case 'r':	value[iDst] = '\r';
Packit 1422b7
							break;
Packit 1422b7
					case '\\':	value[iDst] = '\\';
Packit 1422b7
							break;
Packit 1422b7
					default:	break;
Packit 1422b7
					}
Packit 1422b7
				} else {
Packit 1422b7
					value[iDst] = npb->str[iValue+iSrc];
Packit 1422b7
				}
Packit 1422b7
				++iDst;
Packit 1422b7
			}
Packit 1422b7
			value[iDst] = '\0';
Packit 1422b7
			json_object *json;
Packit 1422b7
			CHKN(json = json_object_new_string(value));
Packit 1422b7
			json_object_object_add(jroot, name, json);
Packit 1422b7
			free(name); name = NULL;
Packit 1422b7
			free(value); value = NULL;
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	*offs = npb->strLen; /* this parser consume everything or fails */
Packit 1422b7
Packit 1422b7
done:
Packit 1422b7
	free(name);
Packit 1422b7
	free(value);
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/* gets a CEF header field. Must be positioned on the
Packit 1422b7
 * first char after the '|' in front of field.
Packit 1422b7
 * Note that '|' may be escaped as "\|", which also means
Packit 1422b7
 * we need to supprot "\\" (see CEF spec for details).
Packit 1422b7
 * We return the string in *val, if val is non-null. In
Packit 1422b7
 * that case we allocate memory that the caller must free.
Packit 1422b7
 * This is necessary because there are potentially escape
Packit 1422b7
 * sequences inside the string.
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
cefGetHdrField(npb_t *const npb,
Packit 1422b7
	size_t *const __restrict__ offs,
Packit 1422b7
	char **val)
Packit 1422b7
{
Packit 1422b7
	int r = 0;
Packit 1422b7
	size_t i = *offs;
Packit 1422b7
	assert(npb->str[i] != '|');
Packit 1422b7
	while(i < npb->strLen && npb->str[i] != '|') {
Packit 1422b7
		if(npb->str[i] == '\\') {
Packit 1422b7
			++i; /* skip esc char */
Packit 1422b7
			if(npb->str[i] != '\\' && npb->str[i] != '|')
Packit 1422b7
				FAIL(LN_WRONGPARSER);
Packit 1422b7
		}
Packit 1422b7
		++i; /* scan to next delimiter */
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if(npb->str[i] != '|')
Packit 1422b7
		FAIL(LN_WRONGPARSER);
Packit 1422b7
Packit 1422b7
	const size_t iBegin = *offs;
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*offs = i + 1;
Packit 1422b7
Packit 1422b7
	if(val == NULL) {
Packit 1422b7
		r = 0;
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
	
Packit 1422b7
	const size_t len = i - iBegin;
Packit 1422b7
	CHKN(*val = malloc(len + 1));
Packit 1422b7
	size_t iDst = 0;
Packit 1422b7
	for(size_t iSrc = 0 ; iSrc < len ; ++iSrc) {
Packit 1422b7
		if(npb->str[iBegin+iSrc] == '\\')
Packit 1422b7
			++iSrc; /* we already checked above that this is OK! */
Packit 1422b7
		(*val)[iDst++] = npb->str[iBegin+iSrc];
Packit 1422b7
	}
Packit 1422b7
	(*val)[iDst] = 0;
Packit 1422b7
	r = 0;
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parser for ArcSight Common Event Format (CEF) version 0.
Packit 1422b7
 * added 2015-05-05 by rgerhards, v1.1.2
Packit 1422b7
 */
Packit 1422b7
PARSER_Parse(CEF)
Packit 1422b7
	size_t i = *offs;
Packit 1422b7
	char *vendor = NULL;
Packit 1422b7
	char *product = NULL;
Packit 1422b7
	char *version = NULL;
Packit 1422b7
	char *sigID = NULL;
Packit 1422b7
	char *name = NULL;
Packit 1422b7
	char *severity = NULL;
Packit 1422b7
Packit 1422b7
	/* minumum header: "CEF:0|x|x|x|x|x|x|" -->  17 chars */
Packit 1422b7
	if(npb->strLen < i + 17 ||
Packit 1422b7
	   npb->str[i]   != 'C' ||
Packit 1422b7
	   npb->str[i+1] != 'E' ||
Packit 1422b7
	   npb->str[i+2] != 'F' ||
Packit 1422b7
	   npb->str[i+3] != ':' ||
Packit 1422b7
	   npb->str[i+4] != '0' ||
Packit 1422b7
	   npb->str[i+5] != '|'
Packit 1422b7
	   )	FAIL(LN_WRONGPARSER);
Packit 1422b7
	
Packit 1422b7
	i += 6; /* position on '|' */
Packit 1422b7
Packit 1422b7
	CHKR(cefGetHdrField(npb, &i, (value == NULL) ? NULL : &vendor));
Packit 1422b7
	CHKR(cefGetHdrField(npb, &i, (value == NULL) ? NULL : &product));
Packit 1422b7
	CHKR(cefGetHdrField(npb, &i, (value == NULL) ? NULL : &version));
Packit 1422b7
	CHKR(cefGetHdrField(npb, &i, (value == NULL) ? NULL : &sigID));
Packit 1422b7
	CHKR(cefGetHdrField(npb, &i, (value == NULL) ? NULL : &name));
Packit 1422b7
	CHKR(cefGetHdrField(npb, &i, (value == NULL) ? NULL : &severity));
Packit 1422b7
	++i; /* skip over terminal '|' */
Packit 1422b7
Packit 1422b7
	/* OK, we now know we have a good header. Now, we need
Packit 1422b7
	 * to process extensions.
Packit 1422b7
	 * This time, we do NOT pre-process the extension, but rather
Packit 1422b7
	 * persist them directly to JSON. This is contrary to other
Packit 1422b7
	 * parsers, but as the CEF header is pretty unique, this time
Packit 1422b7
	 * it is exteremely unlike we will get a no-match during
Packit 1422b7
	 * extension processing. Even if so, nothing bad happens, as
Packit 1422b7
	 * the extracted data is discarded. But the regular case saves
Packit 1422b7
	 * us processing time and complexity. The only time when we
Packit 1422b7
	 * cannot directly process it is when the caller asks us not
Packit 1422b7
	 * to persist the data. So this must be handled differently.
Packit 1422b7
	 */
Packit 1422b7
	 size_t iBeginExtensions = i;
Packit 1422b7
	 CHKR(cefParseExtensions(npb, &i, NULL));
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = i - *offs;
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
Packit 1422b7
	if(value != NULL) {
Packit 1422b7
		CHKN(*value = json_object_new_object());
Packit 1422b7
		json_object *json;
Packit 1422b7
		CHKN(json = json_object_new_string(vendor));
Packit 1422b7
		json_object_object_add(*value, "DeviceVendor", json);
Packit 1422b7
		CHKN(json = json_object_new_string(product));
Packit 1422b7
		json_object_object_add(*value, "DeviceProduct", json);
Packit 1422b7
		CHKN(json = json_object_new_string(version));
Packit 1422b7
		json_object_object_add(*value, "DeviceVersion", json);
Packit 1422b7
		CHKN(json = json_object_new_string(sigID));
Packit 1422b7
		json_object_object_add(*value, "SignatureID", json);
Packit 1422b7
		CHKN(json = json_object_new_string(name));
Packit 1422b7
		json_object_object_add(*value, "Name", json);
Packit 1422b7
		CHKN(json = json_object_new_string(severity));
Packit 1422b7
		json_object_object_add(*value, "Severity", json);
Packit 1422b7
Packit 1422b7
		json_object *jext;
Packit 1422b7
		CHKN(jext = json_object_new_object());
Packit 1422b7
		json_object_object_add(*value, "Extensions", jext);
Packit 1422b7
Packit 1422b7
		i = iBeginExtensions;
Packit 1422b7
		cefParseExtensions(npb, &i, jext);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
done:
Packit 1422b7
	if(r != 0 && value != NULL && *value != NULL) {
Packit 1422b7
		json_object_put(*value);
Packit 1422b7
		value = NULL;
Packit 1422b7
	}
Packit 1422b7
	free(vendor);
Packit 1422b7
	free(product);
Packit 1422b7
	free(version);
Packit 1422b7
	free(sigID);
Packit 1422b7
	free(name);
Packit 1422b7
	free(severity);
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Parser for Checkpoint LEA on-disk format.
Packit 1422b7
 * added 2015-06-18 by rgerhards, v1.1.2
Packit 1422b7
 */
Packit 1422b7
PARSER_Parse(CheckpointLEA)
Packit 1422b7
	size_t i = *offs;
Packit 1422b7
	size_t iName, lenName;
Packit 1422b7
	size_t iValue, lenValue;
Packit 1422b7
	int foundFields = 0;
Packit 1422b7
	char *name = NULL;
Packit 1422b7
	char *val = NULL;
Packit 1422b7
Packit 1422b7
	while(i < npb->strLen) {
Packit 1422b7
		while(i < npb->strLen && npb->str[i] == ' ') /* skip leading SP */
Packit 1422b7
			++i;
Packit 1422b7
		if(i == npb->strLen) { /* OK if just trailing space */
Packit 1422b7
			if(foundFields == 0)
Packit 1422b7
				FAIL(LN_WRONGPARSER);
Packit 1422b7
			break; /* we are done with the loop, all processed */
Packit 1422b7
		} else {
Packit 1422b7
			++foundFields;
Packit 1422b7
		}
Packit 1422b7
		iName = i;
Packit 1422b7
		/* TODO: do a stricter check? ... but we don't have a spec */
Packit 1422b7
		while(i < npb->strLen && npb->str[i] != ':') {
Packit 1422b7
			++i;
Packit 1422b7
		}
Packit 1422b7
		if(i+1 >= npb->strLen || npb->str[i] != ':')
Packit 1422b7
			FAIL(LN_WRONGPARSER);
Packit 1422b7
		lenName = i - iName;
Packit 1422b7
		++i; /* skip ':' */
Packit 1422b7
Packit 1422b7
		while(i < npb->strLen && npb->str[i] == ' ') /* skip leading SP */
Packit 1422b7
			++i;
Packit 1422b7
		iValue = i;
Packit 1422b7
		while(i < npb->strLen && npb->str[i] != ';') {
Packit 1422b7
			++i;
Packit 1422b7
		}
Packit 1422b7
		if(i+1 > npb->strLen || npb->str[i] != ';')
Packit 1422b7
			FAIL(LN_WRONGPARSER);
Packit 1422b7
		lenValue = i - iValue;
Packit 1422b7
		++i; /* skip ';' */
Packit 1422b7
Packit 1422b7
		if(value != NULL) {
Packit 1422b7
			CHKN(name = malloc(sizeof(char) * (lenName + 1)));
Packit 1422b7
			memcpy(name, npb->str+iName, lenName);
Packit 1422b7
			name[lenName] = '\0';
Packit 1422b7
			CHKN(val = malloc(sizeof(char) * (lenValue + 1)));
Packit 1422b7
			memcpy(val, npb->str+iValue, lenValue);
Packit 1422b7
			val[lenValue] = '\0';
Packit 1422b7
			if(*value == NULL)
Packit 1422b7
				CHKN(*value = json_object_new_object());
Packit 1422b7
			json_object *json;
Packit 1422b7
			CHKN(json = json_object_new_string(val));
Packit 1422b7
			json_object_object_add(*value, name, json);
Packit 1422b7
			free(name); name = NULL;
Packit 1422b7
			free(val); val = NULL;
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed =  i - *offs;
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
Packit 1422b7
done:
Packit 1422b7
	free(name);
Packit 1422b7
	free(val);
Packit 1422b7
	if(r != 0 && value != NULL && *value != NULL) {
Packit 1422b7
		json_object_put(*value);
Packit 1422b7
		value = NULL;
Packit 1422b7
	}
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/* helper to repeat parser constructor: checks that dot field name
Packit 1422b7
 * is only present if there is one field inside the "parser" list.
Packit 1422b7
 * returns 1 if ok, 0 otherwise.
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
chkNoDupeDotInParserDefs(ln_ctx ctx, struct json_object *parsers)
Packit 1422b7
{
Packit 1422b7
	int r = 1;
Packit 1422b7
	int nParsers = 0;
Packit 1422b7
	int nDots = 0;
Packit 1422b7
	if(json_object_get_type(parsers) == json_type_array) {
Packit 1422b7
		const int maxparsers = json_object_array_length(parsers);
Packit 1422b7
		for(int i = 0 ; i < maxparsers ; ++i) {
Packit 1422b7
			++nParsers;
Packit 1422b7
			struct json_object *const parser
Packit 1422b7
				= json_object_array_get_idx(parsers, i);
Packit 1422b7
			struct json_object *fname;
Packit 1422b7
			json_object_object_get_ex(parser, "name", &fname);
Packit 1422b7
			if(fname != NULL) {
Packit 1422b7
				if(!strcmp(json_object_get_string(fname), "."))
Packit 1422b7
					++nDots;
Packit 1422b7
			}
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
	if(nParsers > 1 && nDots > 0) {
Packit 1422b7
		ln_errprintf(ctx, 0, "'repeat' parser supports dot name only "
Packit 1422b7
			"if single parser is used in 'parser' part, invalid "
Packit 1422b7
			"construct: %s", json_object_get_string(parsers));
Packit 1422b7
			r = 0;
Packit 1422b7
	}
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * "repeat" special parser.
Packit 1422b7
 */
Packit 1422b7
PARSER_Parse(Repeat)
Packit 1422b7
	struct data_Repeat *const data = (struct data_Repeat*) pdata;
Packit 1422b7
	struct ln_pdag *endNode = NULL;
Packit 1422b7
	size_t strtoffs = *offs;
Packit 1422b7
	size_t lastKnownGood = strtoffs;
Packit 1422b7
	struct json_object *json_arr = NULL;
Packit 1422b7
	const size_t parsedTo_save = npb->parsedTo;
Packit 1422b7
Packit 1422b7
	do {
Packit 1422b7
		struct json_object *parsed_value = json_object_new_object();
Packit 1422b7
		r = ln_normalizeRec(npb, data->parser, strtoffs, 1,
Packit 1422b7
				    parsed_value, &endNode);
Packit 1422b7
		strtoffs = npb->parsedTo;
Packit 1422b7
		LN_DBGPRINTF(npb->ctx, "repeat parser returns %d, parsed %zu, json: %s",
Packit 1422b7
			r, npb->parsedTo, json_object_to_json_string(parsed_value));
Packit 1422b7
Packit 1422b7
		if(r != 0) {
Packit 1422b7
			json_object_put(parsed_value);
Packit 1422b7
			if(data->permitMismatchInParser) {
Packit 1422b7
				strtoffs = lastKnownGood; /* go back to final match */
Packit 1422b7
				LN_DBGPRINTF(npb->ctx, "mismatch in repeat, "
Packit 1422b7
					"parse ptr back to %zd", strtoffs);
Packit 1422b7
				goto success;
Packit 1422b7
			} else {
Packit 1422b7
				goto done;
Packit 1422b7
			}
Packit 1422b7
		}
Packit 1422b7
Packit 1422b7
		if(json_arr == NULL) {
Packit 1422b7
			json_arr = json_object_new_array();
Packit 1422b7
		}
Packit 1422b7
Packit 1422b7
		/* check for name=".", which means we need to place the
Packit 1422b7
		 * value only into to array. As we do not have direct
Packit 1422b7
		 * access to the key, we loop over our result as a work-
Packit 1422b7
		 * around.
Packit 1422b7
		 */
Packit 1422b7
		struct json_object *toAdd = parsed_value;
Packit 1422b7
		struct json_object_iterator it = json_object_iter_begin(parsed_value);
Packit 1422b7
		struct json_object_iterator itEnd = json_object_iter_end(parsed_value);
Packit 1422b7
		while (!json_object_iter_equal(&it, &itEnd)) {
Packit 1422b7
			const char *key = json_object_iter_peek_name(&it);
Packit 1422b7
			struct json_object *const val = json_object_iter_peek_value(&it);
Packit 1422b7
			if(key[0] == '.' && key[1] == '\0') {
Packit 1422b7
				json_object_get(val); /* inc refcount! */
Packit 1422b7
				toAdd = val;
Packit 1422b7
			}
Packit 1422b7
			json_object_iter_next(&it);
Packit 1422b7
		}
Packit 1422b7
Packit 1422b7
		json_object_array_add(json_arr, toAdd);
Packit 1422b7
		if(toAdd != parsed_value)
Packit 1422b7
			json_object_put(parsed_value);
Packit 1422b7
		LN_DBGPRINTF(npb->ctx, "arr: %s", json_object_to_json_string(json_arr));
Packit 1422b7
Packit 1422b7
		/* now check if we shall continue */
Packit 1422b7
		npb->parsedTo = 0;
Packit 1422b7
		lastKnownGood = strtoffs; /* record pos in case of fail in while */
Packit 1422b7
		r = ln_normalizeRec(npb, data->while_cond, strtoffs, 1, NULL, &endNode);
Packit 1422b7
		LN_DBGPRINTF(npb->ctx, "repeat while returns %d, parsed %zu",
Packit 1422b7
			r, npb->parsedTo);
Packit 1422b7
		if(r == 0)
Packit 1422b7
			strtoffs = npb->parsedTo;
Packit 1422b7
	} while(r == 0);
Packit 1422b7
Packit 1422b7
success:
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = strtoffs - *offs;
Packit 1422b7
	if(value == NULL) {
Packit 1422b7
		json_object_put(json_arr);
Packit 1422b7
	} else {
Packit 1422b7
		*value = json_arr;
Packit 1422b7
	}
Packit 1422b7
	npb->parsedTo = parsedTo_save;
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	if(r != 0 && json_arr != NULL) {
Packit 1422b7
		json_object_put(json_arr);
Packit 1422b7
	}
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
PARSER_Construct(Repeat)
Packit 1422b7
{
Packit 1422b7
	int r = 0;
Packit 1422b7
	struct data_Repeat *data = (struct data_Repeat*) calloc(1, sizeof(struct data_Repeat));
Packit 1422b7
	struct ln_pdag *endnode; /* we need this fo ln_pdagAddParser, which updates its param! */
Packit 1422b7
Packit 1422b7
	if(json == NULL)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	struct json_object_iterator it = json_object_iter_begin(json);
Packit 1422b7
	struct json_object_iterator itEnd = json_object_iter_end(json);
Packit 1422b7
	while (!json_object_iter_equal(&it, &itEnd)) {
Packit 1422b7
		const char *key = json_object_iter_peek_name(&it);
Packit 1422b7
		struct json_object *const val = json_object_iter_peek_value(&it);
Packit 1422b7
		if(!strcmp(key, "parser")) {
Packit 1422b7
			if(chkNoDupeDotInParserDefs(ctx, val) != 1) {
Packit 1422b7
				r = LN_BADCONFIG;
Packit 1422b7
				goto done;
Packit 1422b7
			}
Packit 1422b7
			endnode = data->parser = ln_newPDAG(ctx);
Packit 1422b7
			json_object_get(val); /* prevent free in pdagAddParser */
Packit 1422b7
			CHKR(ln_pdagAddParser(ctx, &endnode, val));
Packit 1422b7
			endnode->flags.isTerminal = 1;
Packit 1422b7
		} else if(!strcmp(key, "while")) {
Packit 1422b7
			endnode = data->while_cond = ln_newPDAG(ctx);
Packit 1422b7
			json_object_get(val); /* prevent free in pdagAddParser */
Packit 1422b7
			CHKR(ln_pdagAddParser(ctx, &endnode, val));
Packit 1422b7
			endnode->flags.isTerminal = 1;
Packit 1422b7
		} else if(!strcasecmp(key, "option.permitMismatchInParser")) {
Packit 1422b7
			data->permitMismatchInParser = json_object_get_boolean(val);
Packit 1422b7
		} else {
Packit 1422b7
			ln_errprintf(ctx, 0, "invalid param for hexnumber: %s",
Packit 1422b7
				 json_object_to_json_string(val));
Packit 1422b7
		}
Packit 1422b7
		json_object_iter_next(&it);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
done:
Packit 1422b7
	if(data->parser == NULL || data->while_cond == NULL) {
Packit 1422b7
		ln_errprintf(ctx, 0, "repeat parser needs 'parser','while' parameters");
Packit 1422b7
		ln_destructRepeat(ctx, data);
Packit 1422b7
		r = LN_BADCONFIG;
Packit 1422b7
	} else {
Packit 1422b7
		*pdata = data;
Packit 1422b7
	}
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
PARSER_Destruct(Repeat)
Packit 1422b7
{
Packit 1422b7
	struct data_Repeat *const data = (struct data_Repeat*) pdata;
Packit 1422b7
	if(data->parser != NULL)
Packit 1422b7
		ln_pdagDelete(data->parser);
Packit 1422b7
	if(data->while_cond != NULL)
Packit 1422b7
		ln_pdagDelete(data->while_cond);
Packit 1422b7
	free(pdata);
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/* string escaping modes */
Packit 1422b7
#define ST_ESC_NONE 0
Packit 1422b7
#define ST_ESC_BACKSLASH 1
Packit 1422b7
#define ST_ESC_DOUBLE 2
Packit 1422b7
#define ST_ESC_BOTH 3
Packit 1422b7
struct data_String {
Packit 1422b7
	enum { ST_QUOTE_AUTO = 0, ST_QUOTE_NONE = 1, ST_QUOTE_REQD = 2 }
Packit 1422b7
		quoteMode;
Packit 1422b7
	struct {
Packit 1422b7
		unsigned strip_quotes : 1;
Packit 1422b7
		unsigned esc_md : 2;
Packit 1422b7
	} flags;
Packit 1422b7
	char qchar_begin;
Packit 1422b7
	char qchar_end;
Packit 1422b7
	char perm_chars[256]; // TODO: make this bit-wise, so we need  only 32 bytes
Packit 1422b7
};
Packit 1422b7
static inline void
Packit 1422b7
stringSetPermittedChar(struct data_String *const data, char c, int val)
Packit 1422b7
{
Packit 1422b7
#if 0
Packit 1422b7
	const int i = (unsigned) c / 8;
Packit 1422b7
	const int shft = (unsigned) c % 8;
Packit 1422b7
	const unsigned mask = ~(1 << shft);
Packit 1422b7
	perm_arr[i] = (perm_arr[i] & (0xff
Packit 1422b7
#endif
Packit 1422b7
	data->perm_chars[(unsigned)c] = val;
Packit 1422b7
}
Packit 1422b7
static inline int
Packit 1422b7
stringIsPermittedChar(struct data_String *const data, char c)
Packit 1422b7
{
Packit 1422b7
	return data->perm_chars[(unsigned)c];
Packit 1422b7
}
Packit 1422b7
static void
Packit 1422b7
stringAddPermittedCharArr(struct data_String *const data,
Packit 1422b7
	const char *const optval)
Packit 1422b7
{
Packit 1422b7
	const size_t nchars = strlen(optval);
Packit 1422b7
	for(size_t i = 0 ; i < nchars ; ++i) {
Packit 1422b7
		stringSetPermittedChar(data, optval[i], 1);
Packit 1422b7
	}
Packit 1422b7
}
Packit 1422b7
static void
Packit 1422b7
stringAddPermittedFromTo(struct data_String *const data,
Packit 1422b7
	const unsigned char from,
Packit 1422b7
	const unsigned char to)
Packit 1422b7
{
Packit 1422b7
	assert(from <= to);
Packit 1422b7
	for(size_t i = from ; i <= to ; ++i) {
Packit 1422b7
		stringSetPermittedChar(data, (char) i, 1);
Packit 1422b7
	}
Packit 1422b7
}
Packit 1422b7
static inline void
Packit 1422b7
stringAddPermittedChars(struct data_String *const data,
Packit 1422b7
	struct json_object *const val)
Packit 1422b7
{
Packit 1422b7
	const char *const optval = json_object_get_string(val);
Packit 1422b7
	if(optval == NULL)
Packit 1422b7
		return;
Packit 1422b7
	stringAddPermittedCharArr(data, optval);
Packit 1422b7
}
Packit 1422b7
static void
Packit 1422b7
stringAddPermittedCharsViaArray(ln_ctx ctx, struct data_String *const data,
Packit 1422b7
	struct json_object *const arr)
Packit 1422b7
{
Packit 1422b7
	const int nelem = json_object_array_length(arr);
Packit 1422b7
	for(int i = 0 ; i < nelem ; ++i) {
Packit 1422b7
		struct json_object *const elem
Packit 1422b7
			= json_object_array_get_idx(arr, i);
Packit 1422b7
		struct json_object_iterator it = json_object_iter_begin(elem);
Packit 1422b7
		struct json_object_iterator itEnd = json_object_iter_end(elem);
Packit 1422b7
		while (!json_object_iter_equal(&it, &itEnd)) {
Packit 1422b7
			const char *key = json_object_iter_peek_name(&it);
Packit 1422b7
			struct json_object *const val = json_object_iter_peek_value(&it);
Packit 1422b7
			if(!strcasecmp(key, "chars")) {
Packit 1422b7
				stringAddPermittedChars(data, val);
Packit 1422b7
			} else if(!strcasecmp(key, "class")) {
Packit 1422b7
				const char *const optval = json_object_get_string(val);
Packit 1422b7
				if(!strcasecmp(optval, "digit")) {
Packit 1422b7
					stringAddPermittedCharArr(data, "0123456789");
Packit 1422b7
				} else if(!strcasecmp(optval, "hexdigit")) {
Packit 1422b7
					stringAddPermittedCharArr(data, "0123456789aAbBcCdDeEfF");
Packit 1422b7
				} else if(!strcasecmp(optval, "alpha")) {
Packit 1422b7
					stringAddPermittedFromTo(data, 'a', 'z');
Packit 1422b7
					stringAddPermittedFromTo(data, 'A', 'Z');
Packit 1422b7
				} else if(!strcasecmp(optval, "alnum")) {
Packit 1422b7
					stringAddPermittedCharArr(data, "0123456789");
Packit 1422b7
					stringAddPermittedFromTo(data, 'a', 'z');
Packit 1422b7
					stringAddPermittedFromTo(data, 'A', 'Z');
Packit 1422b7
				} else {
Packit 1422b7
					ln_errprintf(ctx, 0, "invalid character class '%s'",
Packit 1422b7
						optval);
Packit 1422b7
				}
Packit 1422b7
			}
Packit 1422b7
		json_object_iter_next(&it);
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
}
Packit 1422b7
/**
Packit 1422b7
 * generic string parser
Packit 1422b7
 */
Packit 1422b7
PARSER_Parse(String)
Packit 1422b7
	assert(npb->str != NULL);
Packit 1422b7
	assert(offs != NULL);
Packit 1422b7
	assert(parsed != NULL);
Packit 1422b7
	struct data_String *const data = (struct data_String*) pdata;
Packit 1422b7
	size_t i = *offs;
Packit 1422b7
	int bHaveQuotes = 0;
Packit 1422b7
	int bHadEndQuote = 0;
Packit 1422b7
	int bHadEscape = 0;
Packit 1422b7
Packit 1422b7
	if(i == npb->strLen) goto done;
Packit 1422b7
Packit 1422b7
	if((data->quoteMode == ST_QUOTE_AUTO) && (npb->str[i] == data->qchar_begin)) {
Packit 1422b7
		bHaveQuotes = 1;
Packit 1422b7
		++i;
Packit 1422b7
	} else if(data->quoteMode == ST_QUOTE_REQD) {
Packit 1422b7
		if(npb->str[i] == data->qchar_begin) {
Packit 1422b7
			bHaveQuotes = 1;
Packit 1422b7
			++i;
Packit 1422b7
		} else {
Packit 1422b7
			goto done;
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* scan string */
Packit 1422b7
	while(i < npb->strLen) {
Packit 1422b7
		if(bHaveQuotes) {
Packit 1422b7
			if(npb->str[i] == data->qchar_end) {
Packit 1422b7
				if(data->flags.esc_md == ST_ESC_DOUBLE
Packit 1422b7
				   || data->flags.esc_md == ST_ESC_BOTH) {
Packit 1422b7
					/* may be escaped, need to check! */
Packit 1422b7
					if(i+1 < npb->strLen
Packit 1422b7
					   && npb->str[i+1] == data->qchar_end) {
Packit 1422b7
						bHadEscape = 1;
Packit 1422b7
					   	++i;
Packit 1422b7
					} else { /* not escaped -> terminal */
Packit 1422b7
						bHadEndQuote = 1;
Packit 1422b7
						break;
Packit 1422b7
					}
Packit 1422b7
				} else {
Packit 1422b7
					bHadEndQuote = 1;
Packit 1422b7
					break;
Packit 1422b7
				}
Packit 1422b7
			}
Packit 1422b7
		}
Packit 1422b7
Packit 1422b7
		if(   npb->str[i] == '\\'
Packit 1422b7
		   && i+1 < npb->strLen
Packit 1422b7
		   && (data->flags.esc_md == ST_ESC_BACKSLASH
Packit 1422b7
		       || data->flags.esc_md == ST_ESC_BOTH) ) {
Packit 1422b7
			bHadEscape = 1;
Packit 1422b7
			i++; /* skip esc char */
Packit 1422b7
		}
Packit 1422b7
Packit 1422b7
		/* terminating conditions */
Packit 1422b7
		if(!bHaveQuotes && npb->str[i] == ' ')
Packit 1422b7
			break;
Packit 1422b7
		if(!stringIsPermittedChar(data, npb->str[i]))
Packit 1422b7
			break;
Packit 1422b7
		i++;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if(bHaveQuotes && !bHadEndQuote)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	if(i == *offs)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	const size_t trmChkIdx = (bHaveQuotes) ? i+1 : i;
Packit 1422b7
	if(npb->str[trmChkIdx] != ' ' && trmChkIdx != npb->strLen)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	/* success, persist */
Packit 1422b7
	*parsed = i - *offs;
Packit 1422b7
	if(bHadEndQuote)
Packit 1422b7
		++(*parsed); /* skip quote */
Packit 1422b7
	if(value != NULL) {
Packit 1422b7
		size_t strt;
Packit 1422b7
		size_t len;
Packit 1422b7
		if(bHaveQuotes && data->flags.strip_quotes) {
Packit 1422b7
			strt = *offs + 1;
Packit 1422b7
			len = *parsed - 2; /* del begin AND end quote! */
Packit 1422b7
		} else {
Packit 1422b7
			strt = *offs;
Packit 1422b7
			len = *parsed;
Packit 1422b7
		}
Packit 1422b7
		char *const cstr = strndup(npb->str+strt, len);
Packit 1422b7
		CHKN(cstr);
Packit 1422b7
		if(bHadEscape) {
Packit 1422b7
			/* need to post-process string... */
Packit 1422b7
			for(size_t j = 0 ; cstr[j] != '\0' ; j++) {
Packit 1422b7
				if( (
Packit 1422b7
				        cstr[j] == data->qchar_end
Packit 1422b7
				     && cstr[j+1] == data->qchar_end
Packit 1422b7
				     && (data->flags.esc_md == ST_ESC_DOUBLE
Packit 1422b7
				         || data->flags.esc_md == ST_ESC_BOTH)
Packit 1422b7
				    )
Packit 1422b7
				  ||
Packit 1422b7
				    (
Packit 1422b7
				        cstr[j] == '\\'
Packit 1422b7
				     && (data->flags.esc_md == ST_ESC_BACKSLASH
Packit 1422b7
				         || data->flags.esc_md == ST_ESC_BOTH)
Packit 1422b7
				    ) ) {
Packit 1422b7
					/* we need to remove the escape character */
Packit 1422b7
					memmove(cstr+j, cstr+j+1, len-j);
Packit 1422b7
				}
Packit 1422b7
			}
Packit 1422b7
		}
Packit 1422b7
		*value = json_object_new_string(cstr);
Packit 1422b7
		free(cstr);
Packit 1422b7
	}
Packit 1422b7
	r = 0; /* success */
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
PARSER_Construct(String)
Packit 1422b7
{
Packit 1422b7
	int r = 0;
Packit 1422b7
	struct data_String *const data = (struct data_String*) calloc(1, sizeof(struct data_String));
Packit 1422b7
	data->quoteMode = ST_QUOTE_AUTO;
Packit 1422b7
	data->flags.strip_quotes = 1;
Packit 1422b7
	data->flags.esc_md = ST_ESC_BOTH;
Packit 1422b7
	data->qchar_begin = '"';
Packit 1422b7
	data->qchar_end = '"';
Packit 1422b7
	memset(data->perm_chars, 0xff, sizeof(data->perm_chars));
Packit 1422b7
	
Packit 1422b7
	struct json_object_iterator it = json_object_iter_begin(json);
Packit 1422b7
	struct json_object_iterator itEnd = json_object_iter_end(json);
Packit 1422b7
	while (!json_object_iter_equal(&it, &itEnd)) {
Packit 1422b7
		const char *key = json_object_iter_peek_name(&it);
Packit 1422b7
		struct json_object *const val = json_object_iter_peek_value(&it);
Packit 1422b7
		if(!strcasecmp(key, "quoting.mode")) {
Packit 1422b7
			const char *const optval = json_object_get_string(val);
Packit 1422b7
			if(!strcasecmp(optval, "auto")) {
Packit 1422b7
				data->quoteMode = ST_QUOTE_AUTO;
Packit 1422b7
			} else if(!strcasecmp(optval, "none")) {
Packit 1422b7
				data->quoteMode = ST_QUOTE_NONE;
Packit 1422b7
			} else if(!strcasecmp(optval, "required")) {
Packit 1422b7
				data->quoteMode = ST_QUOTE_REQD;
Packit 1422b7
			} else {
Packit 1422b7
				ln_errprintf(ctx, 0, "invalid quoting.mode for string parser: %s",
Packit 1422b7
					optval);
Packit 1422b7
				r = LN_BADCONFIG;
Packit 1422b7
				goto done;
Packit 1422b7
			}
Packit 1422b7
		} else if(!strcasecmp(key, "quoting.escape.mode")) {
Packit 1422b7
			const char *const optval = json_object_get_string(val);
Packit 1422b7
			if(!strcasecmp(optval, "none")) {
Packit 1422b7
				data->flags.esc_md = ST_ESC_NONE;
Packit 1422b7
			} else if(!strcasecmp(optval, "backslash")) {
Packit 1422b7
				data->flags.esc_md = ST_ESC_BACKSLASH;
Packit 1422b7
			} else if(!strcasecmp(optval, "double")) {
Packit 1422b7
				data->flags.esc_md = ST_ESC_DOUBLE;
Packit 1422b7
			} else if(!strcasecmp(optval, "both")) {
Packit 1422b7
				data->flags.esc_md = ST_ESC_BOTH;
Packit 1422b7
			} else {
Packit 1422b7
				ln_errprintf(ctx, 0, "invalid quoting.escape.mode for string "
Packit 1422b7
					"parser: %s", optval);
Packit 1422b7
				r = LN_BADCONFIG;
Packit 1422b7
				goto done;
Packit 1422b7
			}
Packit 1422b7
		} else if(!strcasecmp(key, "quoting.char.begin")) {
Packit 1422b7
			const char *const optval = json_object_get_string(val);
Packit 1422b7
			if(strlen(optval) != 1) {
Packit 1422b7
				ln_errprintf(ctx, 0, "quoting.char.begin must "
Packit 1422b7
					"be exactly one character but is: '%s'", optval);
Packit 1422b7
				r = LN_BADCONFIG;
Packit 1422b7
				goto done;
Packit 1422b7
			}
Packit 1422b7
			data->qchar_begin = *optval;
Packit 1422b7
		} else if(!strcasecmp(key, "quoting.char.end")) {
Packit 1422b7
			const char *const optval = json_object_get_string(val);
Packit 1422b7
			if(strlen(optval) != 1) {
Packit 1422b7
				ln_errprintf(ctx, 0, "quoting.char.end must "
Packit 1422b7
					"be exactly one character but is: '%s'", optval);
Packit 1422b7
				r = LN_BADCONFIG;
Packit 1422b7
				goto done;
Packit 1422b7
			}
Packit 1422b7
			data->qchar_end = *optval;
Packit 1422b7
		} else if(!strcasecmp(key, "matching.permitted")) {
Packit 1422b7
			memset(data->perm_chars, 0x00, sizeof(data->perm_chars));
Packit 1422b7
			if(json_object_is_type(val, json_type_string)) {
Packit 1422b7
				stringAddPermittedChars(data, val);
Packit 1422b7
			} else if(json_object_is_type(val, json_type_array)) {
Packit 1422b7
				stringAddPermittedCharsViaArray(ctx, data, val);
Packit 1422b7
			} else {
Packit 1422b7
				ln_errprintf(ctx, 0, "matching.permitted is invalid "
Packit 1422b7
					"object type, given as '%s",
Packit 1422b7
					 json_object_to_json_string(val));
Packit 1422b7
			}
Packit 1422b7
		} else {
Packit 1422b7
			ln_errprintf(ctx, 0, "invalid param for hexnumber: %s",
Packit 1422b7
				 json_object_to_json_string(val));
Packit 1422b7
		}
Packit 1422b7
		json_object_iter_next(&it);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if(data->quoteMode == ST_QUOTE_NONE)
Packit 1422b7
		data->flags.esc_md = ST_ESC_NONE;
Packit 1422b7
	*pdata = data;
Packit 1422b7
done:
Packit 1422b7
	if(r != 0) {
Packit 1422b7
		free(data);
Packit 1422b7
	}
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
PARSER_Destruct(String)
Packit 1422b7
{
Packit 1422b7
	free(pdata);
Packit 1422b7
}