summaryrefslogtreecommitdiffstats
path: root/runtime/msg.c
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/msg.c')
-rw-r--r--runtime/msg.c5476
1 files changed, 5476 insertions, 0 deletions
diff --git a/runtime/msg.c b/runtime/msg.c
new file mode 100644
index 0000000..b35bc1d
--- /dev/null
+++ b/runtime/msg.c
@@ -0,0 +1,5476 @@
+/* msg.c
+ * The msg object. Implementation of all msg-related functions
+ *
+ * File begun on 2007-07-13 by RGerhards (extracted from syslogd.c)
+ * This file is under development and has not yet arrived at being fully
+ * self-contained and a real object. So far, it is mostly an excerpt
+ * of the "old" message code without any modifications. However, it
+ * helps to have things at the right place one we go to the meat of it.
+ *
+ * Copyright 2007-2023 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of the rsyslog runtime library.
+ *
+ * The rsyslog runtime library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The rsyslog runtime library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the rsyslog runtime library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution.
+ */
+#include "config.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#define SYSLOG_NAMES
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#ifdef HAVE_SYSINFO_UPTIME
+#include <sys/sysinfo.h>
+#endif
+#include <netdb.h>
+#include <libestr.h>
+#include <json.h>
+#ifdef HAVE_MALLOC_H
+# include <malloc.h>
+#endif
+#ifdef USE_LIBUUID
+# include <uuid/uuid.h>
+#endif
+#include <errno.h>
+#include "rsyslog.h"
+#include "srUtils.h"
+#include "stringbuf.h"
+#include "template.h"
+#include "msg.h"
+#include "datetime.h"
+#include "glbl.h"
+#include "regexp.h"
+#include "atomic.h"
+#include "unicode-helper.h"
+#include "ruleset.h"
+#include "prop.h"
+#include "net.h"
+#include "var.h"
+#include "rsconf.h"
+#include "parserif.h"
+#include "errmsg.h"
+
+#define DEV_DEBUG 0 /* set to 1 to enable very verbose developer debugging messages */
+
+/* inlines */
+extern void msgSetPRI(smsg_t *const __restrict__ pMsg, syslog_pri_t pri);
+
+/* TODO: move the global variable root to the config object - had no time to to it
+ * right now before vacation -- rgerhards, 2013-07-22
+ */
+static pthread_mutex_t glblVars_lock;
+struct json_object *global_var_root = NULL;
+
+/* static data */
+DEFobjStaticHelpers
+DEFobjCurrIf(datetime)
+DEFobjCurrIf(glbl)
+DEFobjCurrIf(regexp)
+DEFobjCurrIf(prop)
+DEFobjCurrIf(net)
+DEFobjCurrIf(var)
+
+static const char *one_digit[10] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" };
+
+static const char *two_digits[100] = {
+ "00", "01", "02", "03", "04", "05", "06", "07", "08", "09",
+ "10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
+ "20", "21", "22", "23", "24", "25", "26", "27", "28", "29",
+ "30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
+ "40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
+ "50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
+ "60", "61", "62", "63", "64", "65", "66", "67", "68", "69",
+ "70", "71", "72", "73", "74", "75", "76", "77", "78", "79",
+ "80", "81", "82", "83", "84", "85", "86", "87", "88", "89",
+ "90", "91", "92", "93", "94", "95", "96", "97", "98", "99"};
+
+static const char *wdayNames[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
+
+/* Table of days in a year, needed for getYearDay */
+static const char *daysInYear[366] = {
+ "001", "002", "003", "004", "005", "006", "007", "008", "009",
+ "010", "011", "012", "013", "014", "015", "016", "017", "018", "019",
+ "020", "021", "022", "023", "024", "025", "026", "027", "028", "029",
+ "030", "031", "032", "033", "034", "035", "036", "037", "038", "039",
+ "040", "041", "042", "043", "044", "045", "046", "047", "048", "049",
+ "050", "051", "052", "053", "054", "055", "056", "057", "058", "059",
+ "060", "061", "062", "063", "064", "065", "066", "067", "068", "069",
+ "070", "071", "072", "073", "074", "075", "076", "077", "078", "079",
+ "080", "081", "082", "083", "084", "085", "086", "087", "088", "089",
+ "090", "091", "092", "093", "094", "095", "096", "097", "098", "099",
+ "100", "101", "102", "103", "104", "105", "106", "107", "108", "109",
+ "110", "111", "112", "113", "114", "115", "116", "117", "118", "119",
+ "120", "121", "122", "123", "124", "125", "126", "127", "128", "129",
+ "130", "131", "132", "133", "134", "135", "136", "137", "138", "139",
+ "140", "141", "142", "143", "144", "145", "146", "147", "148", "149",
+ "150", "151", "152", "153", "154", "155", "156", "157", "158", "159",
+ "160", "161", "162", "163", "164", "165", "166", "167", "168", "169",
+ "170", "171", "172", "173", "174", "175", "176", "177", "178", "179",
+ "180", "181", "182", "183", "184", "185", "186", "187", "188", "189",
+ "190", "191", "192", "193", "194", "195", "196", "197", "198", "199",
+ "200", "201", "202", "203", "204", "205", "206", "207", "208", "209",
+ "210", "211", "212", "213", "214", "215", "216", "217", "218", "219",
+ "220", "221", "222", "223", "224", "225", "226", "227", "228", "229",
+ "230", "231", "232", "233", "234", "235", "236", "237", "238", "239",
+ "240", "241", "242", "243", "244", "245", "246", "247", "248", "249",
+ "250", "251", "252", "253", "254", "255", "256", "257", "258", "259",
+ "260", "261", "262", "263", "264", "265", "266", "267", "268", "269",
+ "270", "271", "272", "273", "274", "275", "276", "277", "278", "279",
+ "280", "281", "282", "283", "284", "285", "286", "287", "288", "289",
+ "290", "291", "292", "293", "294", "295", "296", "297", "298", "299",
+ "300", "301", "302", "303", "304", "305", "306", "307", "308", "309",
+ "310", "311", "312", "313", "314", "315", "316", "317", "318", "319",
+ "320", "321", "322", "323", "324", "325", "326", "327", "328", "329",
+ "330", "331", "332", "333", "334", "335", "336", "337", "338", "339",
+ "340", "341", "342", "343", "344", "345", "346", "347", "348", "349",
+ "350", "351", "352", "353", "354", "355", "356", "357", "358", "359",
+ "360", "361", "362", "363", "364", "365", "366"};
+
+/* The following is a table of supported years. This permits us
+ * to avoid dynamic memory allocation. Note that the time-based
+ * algos need to be upgraded after the year 2099 in any case.
+ * Quite honestly, I don't expect that this is a real problem ;)
+ */
+static const char *years[] = {
+ "1967", "1968", "1969", "1970", "1971", "1972", "1973", "1974",
+ "1975", "1976", "1977", "1978", "1979", "1980", "1981", "1982",
+ "1983", "1984", "1985", "1986", "1987", "1988", "1989", "1990",
+ "1991", "1992", "1993", "1994", "1995", "1996", "1997", "1998",
+ "1999", "2000", "2001", "2002", "2003", "2004", "2005", "2006",
+ "2007", "2008", "2009", "2010", "2011", "2012", "2013", "2014",
+ "2015", "2016", "2017", "2018", "2019", "2020", "2021", "2022",
+ "2023", "2024", "2025", "2026", "2027", "2028", "2029", "2030",
+ "2031", "2032", "2033", "2034", "2035", "2036", "2037", "2038",
+ "2039", "2040", "2041", "2042", "2043", "2044", "2045", "2046",
+ "2047", "2048", "2049", "2050", "2051", "2052", "2053", "2054",
+ "2055", "2056", "2057", "2058", "2059", "2060", "2061", "2062",
+ "2063", "2064", "2065", "2066", "2067", "2068", "2069", "2070",
+ "2071", "2072", "2073", "2074", "2075", "2076", "2077", "2078",
+ "2079", "2080", "2081", "2082", "2083", "2084", "2085", "2086",
+ "2087", "2088", "2089", "2090", "2091", "2092", "2093", "2094",
+ "2095", "2096", "2097", "2098", "2099" };
+
+static struct {
+ uchar *pszName;
+} syslog_pri_names[200] = {
+ { UCHAR_CONSTANT("0") },
+ { UCHAR_CONSTANT("1") },
+ { UCHAR_CONSTANT("2") },
+ { UCHAR_CONSTANT("3") },
+ { UCHAR_CONSTANT("4") },
+ { UCHAR_CONSTANT("5") },
+ { UCHAR_CONSTANT("6") },
+ { UCHAR_CONSTANT("7") },
+ { UCHAR_CONSTANT("8") },
+ { UCHAR_CONSTANT("9") },
+ { UCHAR_CONSTANT("10") },
+ { UCHAR_CONSTANT("11") },
+ { UCHAR_CONSTANT("12") },
+ { UCHAR_CONSTANT("13") },
+ { UCHAR_CONSTANT("14") },
+ { UCHAR_CONSTANT("15") },
+ { UCHAR_CONSTANT("16") },
+ { UCHAR_CONSTANT("17") },
+ { UCHAR_CONSTANT("18") },
+ { UCHAR_CONSTANT("19") },
+ { UCHAR_CONSTANT("20") },
+ { UCHAR_CONSTANT("21") },
+ { UCHAR_CONSTANT("22") },
+ { UCHAR_CONSTANT("23") },
+ { UCHAR_CONSTANT("24") },
+ { UCHAR_CONSTANT("25") },
+ { UCHAR_CONSTANT("26") },
+ { UCHAR_CONSTANT("27") },
+ { UCHAR_CONSTANT("28") },
+ { UCHAR_CONSTANT("29") },
+ { UCHAR_CONSTANT("30") },
+ { UCHAR_CONSTANT("31") },
+ { UCHAR_CONSTANT("32") },
+ { UCHAR_CONSTANT("33") },
+ { UCHAR_CONSTANT("34") },
+ { UCHAR_CONSTANT("35") },
+ { UCHAR_CONSTANT("36") },
+ { UCHAR_CONSTANT("37") },
+ { UCHAR_CONSTANT("38") },
+ { UCHAR_CONSTANT("39") },
+ { UCHAR_CONSTANT("40") },
+ { UCHAR_CONSTANT("41") },
+ { UCHAR_CONSTANT("42") },
+ { UCHAR_CONSTANT("43") },
+ { UCHAR_CONSTANT("44") },
+ { UCHAR_CONSTANT("45") },
+ { UCHAR_CONSTANT("46") },
+ { UCHAR_CONSTANT("47") },
+ { UCHAR_CONSTANT("48") },
+ { UCHAR_CONSTANT("49") },
+ { UCHAR_CONSTANT("50") },
+ { UCHAR_CONSTANT("51") },
+ { UCHAR_CONSTANT("52") },
+ { UCHAR_CONSTANT("53") },
+ { UCHAR_CONSTANT("54") },
+ { UCHAR_CONSTANT("55") },
+ { UCHAR_CONSTANT("56") },
+ { UCHAR_CONSTANT("57") },
+ { UCHAR_CONSTANT("58") },
+ { UCHAR_CONSTANT("59") },
+ { UCHAR_CONSTANT("60") },
+ { UCHAR_CONSTANT("61") },
+ { UCHAR_CONSTANT("62") },
+ { UCHAR_CONSTANT("63") },
+ { UCHAR_CONSTANT("64") },
+ { UCHAR_CONSTANT("65") },
+ { UCHAR_CONSTANT("66") },
+ { UCHAR_CONSTANT("67") },
+ { UCHAR_CONSTANT("68") },
+ { UCHAR_CONSTANT("69") },
+ { UCHAR_CONSTANT("70") },
+ { UCHAR_CONSTANT("71") },
+ { UCHAR_CONSTANT("72") },
+ { UCHAR_CONSTANT("73") },
+ { UCHAR_CONSTANT("74") },
+ { UCHAR_CONSTANT("75") },
+ { UCHAR_CONSTANT("76") },
+ { UCHAR_CONSTANT("77") },
+ { UCHAR_CONSTANT("78") },
+ { UCHAR_CONSTANT("79") },
+ { UCHAR_CONSTANT("80") },
+ { UCHAR_CONSTANT("81") },
+ { UCHAR_CONSTANT("82") },
+ { UCHAR_CONSTANT("83") },
+ { UCHAR_CONSTANT("84") },
+ { UCHAR_CONSTANT("85") },
+ { UCHAR_CONSTANT("86") },
+ { UCHAR_CONSTANT("87") },
+ { UCHAR_CONSTANT("88") },
+ { UCHAR_CONSTANT("89") },
+ { UCHAR_CONSTANT("90") },
+ { UCHAR_CONSTANT("91") },
+ { UCHAR_CONSTANT("92") },
+ { UCHAR_CONSTANT("93") },
+ { UCHAR_CONSTANT("94") },
+ { UCHAR_CONSTANT("95") },
+ { UCHAR_CONSTANT("96") },
+ { UCHAR_CONSTANT("97") },
+ { UCHAR_CONSTANT("98") },
+ { UCHAR_CONSTANT("99") },
+ { UCHAR_CONSTANT("100") },
+ { UCHAR_CONSTANT("101") },
+ { UCHAR_CONSTANT("102") },
+ { UCHAR_CONSTANT("103") },
+ { UCHAR_CONSTANT("104") },
+ { UCHAR_CONSTANT("105") },
+ { UCHAR_CONSTANT("106") },
+ { UCHAR_CONSTANT("107") },
+ { UCHAR_CONSTANT("108") },
+ { UCHAR_CONSTANT("109") },
+ { UCHAR_CONSTANT("110") },
+ { UCHAR_CONSTANT("111") },
+ { UCHAR_CONSTANT("112") },
+ { UCHAR_CONSTANT("113") },
+ { UCHAR_CONSTANT("114") },
+ { UCHAR_CONSTANT("115") },
+ { UCHAR_CONSTANT("116") },
+ { UCHAR_CONSTANT("117") },
+ { UCHAR_CONSTANT("118") },
+ { UCHAR_CONSTANT("119") },
+ { UCHAR_CONSTANT("120") },
+ { UCHAR_CONSTANT("121") },
+ { UCHAR_CONSTANT("122") },
+ { UCHAR_CONSTANT("123") },
+ { UCHAR_CONSTANT("124") },
+ { UCHAR_CONSTANT("125") },
+ { UCHAR_CONSTANT("126") },
+ { UCHAR_CONSTANT("127") },
+ { UCHAR_CONSTANT("128") },
+ { UCHAR_CONSTANT("129") },
+ { UCHAR_CONSTANT("130") },
+ { UCHAR_CONSTANT("131") },
+ { UCHAR_CONSTANT("132") },
+ { UCHAR_CONSTANT("133") },
+ { UCHAR_CONSTANT("134") },
+ { UCHAR_CONSTANT("135") },
+ { UCHAR_CONSTANT("136") },
+ { UCHAR_CONSTANT("137") },
+ { UCHAR_CONSTANT("138") },
+ { UCHAR_CONSTANT("139") },
+ { UCHAR_CONSTANT("140") },
+ { UCHAR_CONSTANT("141") },
+ { UCHAR_CONSTANT("142") },
+ { UCHAR_CONSTANT("143") },
+ { UCHAR_CONSTANT("144") },
+ { UCHAR_CONSTANT("145") },
+ { UCHAR_CONSTANT("146") },
+ { UCHAR_CONSTANT("147") },
+ { UCHAR_CONSTANT("148") },
+ { UCHAR_CONSTANT("149") },
+ { UCHAR_CONSTANT("150") },
+ { UCHAR_CONSTANT("151") },
+ { UCHAR_CONSTANT("152") },
+ { UCHAR_CONSTANT("153") },
+ { UCHAR_CONSTANT("154") },
+ { UCHAR_CONSTANT("155") },
+ { UCHAR_CONSTANT("156") },
+ { UCHAR_CONSTANT("157") },
+ { UCHAR_CONSTANT("158") },
+ { UCHAR_CONSTANT("159") },
+ { UCHAR_CONSTANT("160") },
+ { UCHAR_CONSTANT("161") },
+ { UCHAR_CONSTANT("162") },
+ { UCHAR_CONSTANT("163") },
+ { UCHAR_CONSTANT("164") },
+ { UCHAR_CONSTANT("165") },
+ { UCHAR_CONSTANT("166") },
+ { UCHAR_CONSTANT("167") },
+ { UCHAR_CONSTANT("168") },
+ { UCHAR_CONSTANT("169") },
+ { UCHAR_CONSTANT("170") },
+ { UCHAR_CONSTANT("171") },
+ { UCHAR_CONSTANT("172") },
+ { UCHAR_CONSTANT("173") },
+ { UCHAR_CONSTANT("174") },
+ { UCHAR_CONSTANT("175") },
+ { UCHAR_CONSTANT("176") },
+ { UCHAR_CONSTANT("177") },
+ { UCHAR_CONSTANT("178") },
+ { UCHAR_CONSTANT("179") },
+ { UCHAR_CONSTANT("180") },
+ { UCHAR_CONSTANT("181") },
+ { UCHAR_CONSTANT("182") },
+ { UCHAR_CONSTANT("183") },
+ { UCHAR_CONSTANT("184") },
+ { UCHAR_CONSTANT("185") },
+ { UCHAR_CONSTANT("186") },
+ { UCHAR_CONSTANT("187") },
+ { UCHAR_CONSTANT("188") },
+ { UCHAR_CONSTANT("189") },
+ { UCHAR_CONSTANT("190") },
+ { UCHAR_CONSTANT("191") },
+ { UCHAR_CONSTANT("192") },
+ { UCHAR_CONSTANT("193") },
+ { UCHAR_CONSTANT("194") },
+ { UCHAR_CONSTANT("195") },
+ { UCHAR_CONSTANT("196") },
+ { UCHAR_CONSTANT("197") },
+ { UCHAR_CONSTANT("198") },
+ { UCHAR_CONSTANT("199") }
+ };
+static char hexdigit[16] =
+ {'0', '1', '2', '3', '4', '5', '6', '7', '8',
+ '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+#if defined(_AIX)
+/* AIXPORT : replace facility names with aso and caa only for AIX */
+static const char *syslog_fac_names[LOG_NFACILITIES] = { "kern", "user", "mail", "daemon", "auth", "syslog", "lpr",
+ "news", "uucp", "cron", "authpriv", "ftp", "aso", "audit",
+ "alert", "caa", "local0", "local1", "local2", "local3",
+ "local4", "local5", "local6", "local7", "invld" };
+/* length of the facility names string (for optimizatiions) */
+static short len_syslog_fac_names[LOG_NFACILITIES] = { 4, 4, 4, 6, 4, 6, 3,
+ 4, 4, 4, 8, 3, 3, 5,
+ 5, 3, 6, 6, 6, 6,
+ 6, 6, 6, 6, 5 };
+
+#else
+/*syslog facility names (as of RFC5424) */
+static const char *syslog_fac_names[LOG_NFACILITIES] = { "kern", "user", "mail", "daemon", "auth", "syslog", "lpr",
+ "news", "uucp", "cron", "authpriv", "ftp", "ntp", "audit",
+ "alert", "clock", "local0", "local1", "local2", "local3",
+ "local4", "local5", "local6", "local7", "invld" };
+/* length of the facility names string (for optimizatiions) */
+static short len_syslog_fac_names[LOG_NFACILITIES] = { 4, 4, 4, 6, 4, 6, 3,
+ 4, 4, 4, 8, 3, 3, 5,
+ 5, 5, 6, 6, 6, 6,
+ 6, 6, 6, 6, 5 };
+#endif
+
+/* table of severity names (in numerical order)*/
+static const char *syslog_severity_names[8] = { "emerg", "alert", "crit", "err", "warning", "notice", "info",
+ "debug" };
+static short len_syslog_severity_names[8] = { 5, 5, 4, 3, 7, 6, 4, 5 };
+
+/* numerical values as string - this is the most efficient approach to convert severity
+ * and facility values to a numerical string... -- rgerhars, 2009-06-17
+ */
+
+static const char *syslog_number_names[LOG_NFACILITIES] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
+ "10", "11", "12", "13", "14",
+ "15", "16", "17", "18", "19", "20", "21", "22", "23", "24" };
+
+/* global variables */
+#if defined(HAVE_MALLOC_TRIM) && !defined(HAVE_ATOMIC_BUILTINS)
+static pthread_mutex_t mutTrimCtr; /* mutex to handle malloc trim */
+#endif
+
+/* some forward declarations */
+static int getAPPNAMELen(smsg_t * const pM, sbool bLockMutex);
+static rsRetVal jsonPathFindParent(struct json_object *jroot, uchar *name, uchar *leaf,
+ struct json_object **parent, int bCreate);
+static uchar * jsonPathGetLeaf(uchar *name, int lenName);
+struct json_object *jsonDeepCopy(struct json_object *src);
+static json_bool jsonVarExtract(struct json_object* root, const char *key, struct json_object **value);
+void getRawMsgAfterPRI(smsg_t * const pM, uchar **pBuf, int *piLen);
+
+
+/* the locking and unlocking implementations: */
+static inline void
+MsgLock(smsg_t *pThis)
+{
+ #if DEV_DEBUG == 1
+ dbgprintf("MsgLock(0x%lx)\n", (unsigned long) pThis);
+ #endif
+ pthread_mutex_lock(&pThis->mut);
+}
+static inline void
+MsgUnlock(smsg_t *pThis)
+{
+ #if DEV_DEBUG == 1
+ dbgprintf("MsgUnlock(0x%lx)\n", (unsigned long) pThis);
+ #endif
+ pthread_mutex_unlock(&pThis->mut);
+}
+
+
+/* set RcvFromIP name in msg object WITHOUT calling AddRef.
+ * rgerhards, 2013-01-22
+ */
+static inline void
+MsgSetRcvFromIPWithoutAddRef(smsg_t *pThis, prop_t *new)
+{
+ if(pThis->pRcvFromIP != NULL)
+ prop.Destruct(&pThis->pRcvFromIP);
+ pThis->pRcvFromIP = new;
+}
+
+
+/* set RcvFrom name in msg object WITHOUT calling AddRef.
+ * rgerhards, 2013-01-22
+ */
+static void MsgSetRcvFromWithoutAddRef(smsg_t *pThis, prop_t *new)
+{
+ assert(pThis != NULL);
+
+ if(pThis->msgFlags & NEEDS_DNSRESOL) {
+ if(pThis->rcvFrom.pfrominet != NULL)
+ free(pThis->rcvFrom.pfrominet);
+ pThis->msgFlags &= ~NEEDS_DNSRESOL;
+ } else {
+ if(pThis->rcvFrom.pRcvFrom != NULL)
+ prop.Destruct(&pThis->rcvFrom.pRcvFrom);
+ }
+ pThis->rcvFrom.pRcvFrom = new;
+}
+
+
+/* rgerhards 2012-04-18: set associated ruleset (by ruleset name)
+ * pRuleset pointer inside msg is updated. If ruleset cannot be found,
+ * no update is done and an error message emitted.
+ */
+static void ATTR_NONNULL()
+MsgSetRulesetByName(smsg_t * const pMsg, cstr_t *const rulesetName)
+{
+ uchar *const rs_name = rsCStrGetSzStrNoNULL(rulesetName);
+ const rsRetVal localRet =
+ rulesetGetRuleset(runConf, &(pMsg->pRuleset), rs_name);
+
+ if(localRet != RS_RET_OK) {
+ LogError(0, localRet, "msg: ruleset '%s' could not be found and could not "
+ "be assigned to message object. This possibly leads to the message "
+ "being processed incorrectly. We cannot do anything against this, but "
+ "wanted to let you know.", rs_name);
+ }
+}
+
+/* do a DNS reverse resolution, if not already done, reflect status
+ * rgerhards, 2009-11-16
+ */
+static rsRetVal
+resolveDNS(smsg_t * const pMsg) {
+ rsRetVal localRet;
+ prop_t *propFromHost = NULL;
+ prop_t *ip;
+ prop_t *localName;
+ DEFiRet;
+
+ MsgLock(pMsg);
+ CHKiRet(objUse(net, CORE_COMPONENT));
+ if(pMsg->msgFlags & NEEDS_DNSRESOL) {
+ if (pMsg->msgFlags & PRESERVE_CASE) {
+ localRet = net.cvthname(pMsg->rcvFrom.pfrominet, NULL, &localName, &ip);
+ } else {
+ localRet = net.cvthname(pMsg->rcvFrom.pfrominet, &localName, NULL, &ip);
+ }
+ if(localRet == RS_RET_OK) {
+ /* we pass down the props, so no need for AddRef */
+ MsgSetRcvFromWithoutAddRef(pMsg, localName);
+ MsgSetRcvFromIPWithoutAddRef(pMsg, ip);
+ }
+ }
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ /* best we can do: remove property */
+ MsgSetRcvFromStr(pMsg, UCHAR_CONSTANT(""), 0, &propFromHost);
+ prop.Destruct(&propFromHost);
+ }
+ MsgUnlock(pMsg);
+ if(propFromHost != NULL)
+ prop.Destruct(&propFromHost);
+ RETiRet;
+}
+
+
+void
+getInputName(const smsg_t * const pM, uchar **ppsz, int *const plen)
+{
+ if(pM == NULL || pM->pInputName == NULL) {
+ *ppsz = UCHAR_CONSTANT("");
+ *plen = 0;
+ } else {
+ prop.GetString(pM->pInputName, ppsz, plen);
+ }
+}
+
+
+static uchar*
+getRcvFromIP(smsg_t * const pM)
+{
+ uchar *psz;
+ int len;
+ if(pM == NULL) {
+ psz = UCHAR_CONSTANT("");
+ } else {
+ resolveDNS(pM); /* make sure we have a resolved entry */
+ if(pM->pRcvFromIP == NULL)
+ psz = UCHAR_CONSTANT("");
+ else
+ prop.GetString(pM->pRcvFromIP, &psz, &len);
+ }
+ return psz;
+}
+
+
+/* map a property name (string) to a property ID */
+rsRetVal
+propNameToID(const uchar *const pName, propid_t *const pPropID)
+{
+ DEFiRet;
+
+ /* sometimes there are aliases to the original MonitoWare
+ * property names. These come after || in the ifs below. */
+ if(!strcasecmp((char*) pName, "msg")) {
+ *pPropID = PROP_MSG;
+ } else if(!strcasecmp((char*) pName, "timestamp")
+ || !strcasecmp((char*) pName, "timereported")) {
+ *pPropID = PROP_TIMESTAMP;
+ } else if(!strcasecmp((char*) pName, "hostname") || !strcasecmp((char*) pName, "source")) {
+ *pPropID = PROP_HOSTNAME;
+ } else if(!strcasecmp((char*) pName, "syslogtag")) {
+ *pPropID = PROP_SYSLOGTAG;
+ } else if(!strcasecmp((char*) pName, "rawmsg")) {
+ *pPropID = PROP_RAWMSG;
+ } else if(!strcasecmp((char*) pName, "rawmsg-after-pri")) {
+ *pPropID = PROP_RAWMSG_AFTER_PRI;
+ } else if(!strcasecmp((char*) pName, "inputname")) {
+ *pPropID = PROP_INPUTNAME;
+ } else if(!strcasecmp((char*) pName, "fromhost")) {
+ *pPropID = PROP_FROMHOST;
+ } else if(!strcasecmp((char*) pName, "fromhost-ip")) {
+ *pPropID = PROP_FROMHOST_IP;
+ } else if(!strcasecmp((char*) pName, "pri")) {
+ *pPropID = PROP_PRI;
+ } else if(!strcasecmp((char*) pName, "pri-text")) {
+ *pPropID = PROP_PRI_TEXT;
+ } else if(!strcasecmp((char*) pName, "iut")) {
+ *pPropID = PROP_IUT;
+ } else if(!strcasecmp((char*) pName, "syslogfacility")) {
+ *pPropID = PROP_SYSLOGFACILITY;
+ } else if(!strcasecmp((char*) pName, "syslogfacility-text")) {
+ *pPropID = PROP_SYSLOGFACILITY_TEXT;
+ } else if(!strcasecmp((char*) pName, "syslogseverity") || !strcasecmp((char*) pName, "syslogpriority")) {
+ *pPropID = PROP_SYSLOGSEVERITY;
+ } else if(!strcasecmp((char*) pName, "syslogseverity-text") ||
+ !strcasecmp((char*) pName, "syslogpriority-text")) {
+ *pPropID = PROP_SYSLOGSEVERITY_TEXT;
+ } else if(!strcasecmp((char*) pName, "timegenerated")) {
+ *pPropID = PROP_TIMEGENERATED;
+ } else if(!strcasecmp((char*) pName, "programname")) {
+ *pPropID = PROP_PROGRAMNAME;
+ } else if(!strcasecmp((char*) pName, "protocol-version")) {
+ *pPropID = PROP_PROTOCOL_VERSION;
+ } else if(!strcasecmp((char*) pName, "structured-data")) {
+ *pPropID = PROP_STRUCTURED_DATA;
+ } else if(!strcasecmp((char*) pName, "app-name")) {
+ *pPropID = PROP_APP_NAME;
+ } else if(!strcasecmp((char*) pName, "procid")) {
+ *pPropID = PROP_PROCID;
+ } else if(!strcasecmp((char*) pName, "msgid")) {
+ *pPropID = PROP_MSGID;
+ } else if(!strcasecmp((char*) pName, "jsonmesg")) {
+ *pPropID = PROP_JSONMESG;
+ } else if(!strcasecmp((char*) pName, "parsesuccess")) {
+ *pPropID = PROP_PARSESUCCESS;
+#ifdef USE_LIBUUID
+ } else if(!strcasecmp((char*) pName, "uuid")) {
+ *pPropID = PROP_UUID;
+#endif
+ /* here start system properties (those, that do not relate to the message itself */
+ } else if(!strcasecmp((char*) pName, "$NOW")) {
+ *pPropID = PROP_SYS_NOW;
+ } else if(!strcasecmp((char*) pName, "$YEAR")) {
+ *pPropID = PROP_SYS_YEAR;
+ } else if(!strcasecmp((char*) pName, "$MONTH")) {
+ *pPropID = PROP_SYS_MONTH;
+ } else if(!strcasecmp((char*) pName, "$DAY")) {
+ *pPropID = PROP_SYS_DAY;
+ } else if(!strcasecmp((char*) pName, "$HOUR")) {
+ *pPropID = PROP_SYS_HOUR;
+ } else if(!strcasecmp((char*) pName, "$HHOUR")) {
+ *pPropID = PROP_SYS_HHOUR;
+ } else if(!strcasecmp((char*) pName, "$QHOUR")) {
+ *pPropID = PROP_SYS_QHOUR;
+ } else if(!strcasecmp((char*) pName, "$MINUTE")) {
+ *pPropID = PROP_SYS_MINUTE;
+ } else if(!strcasecmp((char*) pName, "$WDAY")) {
+ *pPropID = PROP_SYS_WDAY;
+ } else if(!strcasecmp((char*) pName, "$now-utc")) {
+ *pPropID = PROP_SYS_NOW_UTC;
+ } else if(!strcasecmp((char*) pName, "$year-utc")) {
+ *pPropID = PROP_SYS_YEAR_UTC;
+ } else if(!strcasecmp((char*) pName, "$month-utc")) {
+ *pPropID = PROP_SYS_MONTH_UTC;
+ } else if(!strcasecmp((char*) pName, "$day-utc")) {
+ *pPropID = PROP_SYS_DAY_UTC;
+ } else if(!strcasecmp((char*) pName, "$hour-utc")) {
+ *pPropID = PROP_SYS_HOUR_UTC;
+ } else if(!strcasecmp((char*) pName, "$hhour-utc")) {
+ *pPropID = PROP_SYS_HHOUR_UTC;
+ } else if(!strcasecmp((char*) pName, "$qhour-utc")) {
+ *pPropID = PROP_SYS_QHOUR_UTC;
+ } else if(!strcasecmp((char*) pName, "$minute-utc")) {
+ *pPropID = PROP_SYS_MINUTE_UTC;
+ } else if(!strcasecmp((char*) pName, "$wday-utc")) {
+ *pPropID = PROP_SYS_WDAY_UTC;
+ } else if(!strcasecmp((char*) pName, "$now-unixtimestamp")) {
+ *pPropID = PROP_SYS_NOW_UXTIMESTAMP;
+ } else if(!strcasecmp((char*) pName, "$MYHOSTNAME")) {
+ *pPropID = PROP_SYS_MYHOSTNAME;
+ } else if(!strcasecmp((char*) pName, "$!all-json")) {
+ *pPropID = PROP_CEE_ALL_JSON;
+ } else if(!strcasecmp((char*) pName, "$!all-json-plain")) {
+ *pPropID = PROP_CEE_ALL_JSON_PLAIN;
+ } else if(!strcasecmp((char*) pName, "$BOM")) {
+ *pPropID = PROP_SYS_BOM;
+ } else if(!strcasecmp((char*) pName, "$UPTIME")) {
+ *pPropID = PROP_SYS_UPTIME;
+ } else if(!strncmp((char*) pName, "$!", 2) || pName[0] == '!') {
+ *pPropID = PROP_CEE;
+ } else if(!strncmp((char*) pName, "$.", 2) || pName[0] == '.') {
+ *pPropID = PROP_LOCAL_VAR;
+ } else if(!strncmp((char*) pName, "$/", 2) || pName[0] == '/') {
+ *pPropID = PROP_GLOBAL_VAR;
+ } else {
+ DBGPRINTF("PROP_INVALID for name '%s'\n", pName);
+ *pPropID = PROP_INVALID;
+ iRet = RS_RET_VAR_NOT_FOUND;
+ }
+
+ RETiRet;
+}
+
+
+/* map a property ID to a name string (useful for displaying) */
+uchar *propIDToName(propid_t propID)
+{
+ switch(propID) {
+ case PROP_MSG:
+ return UCHAR_CONSTANT("msg");
+ case PROP_TIMESTAMP:
+ return UCHAR_CONSTANT("timestamp");
+ case PROP_HOSTNAME:
+ return UCHAR_CONSTANT("hostname");
+ case PROP_SYSLOGTAG:
+ return UCHAR_CONSTANT("syslogtag");
+ case PROP_RAWMSG:
+ return UCHAR_CONSTANT("rawmsg");
+ case PROP_RAWMSG_AFTER_PRI:
+ return UCHAR_CONSTANT("rawmsg-after-pri");
+ case PROP_INPUTNAME:
+ return UCHAR_CONSTANT("inputname");
+ case PROP_FROMHOST:
+ return UCHAR_CONSTANT("fromhost");
+ case PROP_FROMHOST_IP:
+ return UCHAR_CONSTANT("fromhost-ip");
+ case PROP_PRI:
+ return UCHAR_CONSTANT("pri");
+ case PROP_PRI_TEXT:
+ return UCHAR_CONSTANT("pri-text");
+ case PROP_IUT:
+ return UCHAR_CONSTANT("iut");
+ case PROP_SYSLOGFACILITY:
+ return UCHAR_CONSTANT("syslogfacility");
+ case PROP_SYSLOGFACILITY_TEXT:
+ return UCHAR_CONSTANT("syslogfacility-text");
+ case PROP_SYSLOGSEVERITY:
+ return UCHAR_CONSTANT("syslogseverity");
+ case PROP_SYSLOGSEVERITY_TEXT:
+ return UCHAR_CONSTANT("syslogseverity-text");
+ case PROP_TIMEGENERATED:
+ return UCHAR_CONSTANT("timegenerated");
+ case PROP_PROGRAMNAME:
+ return UCHAR_CONSTANT("programname");
+ case PROP_PROTOCOL_VERSION:
+ return UCHAR_CONSTANT("protocol-version");
+ case PROP_STRUCTURED_DATA:
+ return UCHAR_CONSTANT("structured-data");
+ case PROP_APP_NAME:
+ return UCHAR_CONSTANT("app-name");
+ case PROP_PROCID:
+ return UCHAR_CONSTANT("procid");
+ case PROP_MSGID:
+ return UCHAR_CONSTANT("msgid");
+ case PROP_JSONMESG:
+ return UCHAR_CONSTANT("jsonmesg");
+ case PROP_PARSESUCCESS:
+ return UCHAR_CONSTANT("parsesuccess");
+#ifdef USE_LIBUUID
+ case PROP_UUID:
+ return UCHAR_CONSTANT("uuid");
+#endif
+ case PROP_SYS_NOW:
+ return UCHAR_CONSTANT("$NOW");
+ case PROP_SYS_YEAR:
+ return UCHAR_CONSTANT("$YEAR");
+ case PROP_SYS_MONTH:
+ return UCHAR_CONSTANT("$MONTH");
+ case PROP_SYS_DAY:
+ return UCHAR_CONSTANT("$DAY");
+ case PROP_SYS_HOUR:
+ return UCHAR_CONSTANT("$HOUR");
+ case PROP_SYS_HHOUR:
+ return UCHAR_CONSTANT("$HHOUR");
+ case PROP_SYS_QHOUR:
+ return UCHAR_CONSTANT("$QHOUR");
+ case PROP_SYS_MINUTE:
+ return UCHAR_CONSTANT("$MINUTE");
+ case PROP_SYS_NOW_UTC:
+ return UCHAR_CONSTANT("$NOW-UTC");
+ case PROP_SYS_YEAR_UTC:
+ return UCHAR_CONSTANT("$YEAR-UTC");
+ case PROP_SYS_MONTH_UTC:
+ return UCHAR_CONSTANT("$MONTH-UTC");
+ case PROP_SYS_DAY_UTC:
+ return UCHAR_CONSTANT("$DAY-UTC");
+ case PROP_SYS_HOUR_UTC:
+ return UCHAR_CONSTANT("$HOUR-UTC");
+ case PROP_SYS_HHOUR_UTC:
+ return UCHAR_CONSTANT("$HHOUR-UTC");
+ case PROP_SYS_QHOUR_UTC:
+ return UCHAR_CONSTANT("$QHOUR-UTC");
+ case PROP_SYS_MINUTE_UTC:
+ return UCHAR_CONSTANT("$MINUTE-UTC");
+ case PROP_SYS_WDAY:
+ return UCHAR_CONSTANT("$WDAY");
+ case PROP_SYS_WDAY_UTC:
+ return UCHAR_CONSTANT("$WDAY-UTC");
+ case PROP_SYS_NOW_UXTIMESTAMP:
+ return UCHAR_CONSTANT("$NOW-UNIXTIMESTAMP");
+ case PROP_SYS_MYHOSTNAME:
+ return UCHAR_CONSTANT("$MYHOSTNAME");
+ case PROP_CEE_ALL_JSON:
+ return UCHAR_CONSTANT("$!all-json");
+ case PROP_CEE_ALL_JSON_PLAIN:
+ return UCHAR_CONSTANT("$!all-json-plain");
+ case PROP_SYS_BOM:
+ return UCHAR_CONSTANT("$BOM");
+ case PROP_SYS_UPTIME:
+ return UCHAR_CONSTANT("$UPTIME");
+ case PROP_CEE:
+ return UCHAR_CONSTANT("*CEE-based property*");
+ case PROP_LOCAL_VAR:
+ return UCHAR_CONSTANT("*LOCAL_VARIABLE*");
+ case PROP_GLOBAL_VAR:
+ return UCHAR_CONSTANT("*GLOBAL_VARIABLE*");
+ default:
+ return UCHAR_CONSTANT("*invalid property id*");
+ }
+}
+
+
+/* This is common code for all Constructors. It is defined in an
+ * inline'able function so that we can save a function call in the
+ * actual constructors (otherwise, the msgConstruct would need
+ * to call msgConstructWithTime(), which would require a
+ * function call). Now, both can use this inline function. This
+ * enables us to be optimal, but still have the code just once.
+ * the new object or NULL if no such object could be allocated.
+ * An object constructed via this function should only be destroyed
+ * via "msgDestruct()". This constructor does not query system time
+ * itself but rather uses a user-supplied value. This enables the caller
+ * to do some tricks to save processing time (done, for example, in the
+ * udp input).
+ * NOTE: this constructor does NOT call calloc(), as we have many bytes
+ * inside the structure which do not need to be cleared. bzero() will
+ * heavily thrash the cache, so we do the init manually (which also
+ * is the right thing to do with pointers, as they are not neccessarily
+ * a binary 0 on all machines [but today almost always...]).
+ * rgerhards, 2008-10-06
+ */
+static rsRetVal
+msgBaseConstruct(smsg_t **ppThis)
+{
+ DEFiRet;
+ smsg_t *pM;
+
+ assert(ppThis != NULL);
+ CHKmalloc(pM = malloc(sizeof(smsg_t)));
+ objConstructSetObjInfo(pM); /* intialize object helper entities */
+
+ /* initialize members in ORDER they appear in structure (think "cache line"!) */
+ pM->flowCtlType = 0;
+ pM->bParseSuccess = 0;
+ pM->iRefCount = 1;
+ pM->iSeverity = LOG_DEBUG;
+ pM->iFacility = LOG_INVLD;
+ pM->iLenPROGNAME = -1;
+ pM->offAfterPRI = 0;
+ pM->offMSG = -1;
+ pM->iProtocolVersion = 0;
+ pM->msgFlags = 0;
+ pM->iLenRawMsg = 0;
+ pM->iLenMSG = 0;
+ pM->iLenTAG = 0;
+ pM->iLenHOSTNAME = 0;
+ pM->pszRawMsg = NULL;
+ pM->pszHOSTNAME = NULL;
+ pM->pszRcvdAt3164 = NULL;
+ pM->pszRcvdAt3339 = NULL;
+ pM->pszRcvdAt_MySQL = NULL;
+ pM->pszRcvdAt_PgSQL = NULL;
+ pM->pszTIMESTAMP3164 = NULL;
+ pM->pszTIMESTAMP3339 = NULL;
+ pM->pszTIMESTAMP_MySQL = NULL;
+ pM->pszTIMESTAMP_PgSQL = NULL;
+ pM->pszStrucData = NULL;
+ pM->lenStrucData = 0;
+ pM->pCSAPPNAME = NULL;
+ pM->pCSPROCID = NULL;
+ pM->pCSMSGID = NULL;
+ pM->pInputName = NULL;
+ pM->pRcvFromIP = NULL;
+ pM->rcvFrom.pRcvFrom = NULL;
+ pM->pRuleset = NULL;
+ pM->json = NULL;
+ pM->localvars = NULL;
+ pM->dfltTZ[0] = '\0';
+ memset(&pM->tRcvdAt, 0, sizeof(pM->tRcvdAt));
+ memset(&pM->tTIMESTAMP, 0, sizeof(pM->tTIMESTAMP));
+ pM->TAG.pszTAG = NULL;
+ pM->pszTimestamp3164[0] = '\0';
+ pM->pszTimestamp3339[0] = '\0';
+ pM->pszTIMESTAMP_SecFrac[0] = '\0';
+ pM->pszRcvdAt_SecFrac[0] = '\0';
+ pM->pszTIMESTAMP_Unix[0] = '\0';
+ pM->pszRcvdAt_Unix[0] = '\0';
+ pM->pszUUID = NULL;
+ pthread_mutex_init(&pM->mut, NULL);
+
+ #if DEV_DEBUG == 1
+ dbgprintf("msgConstruct\t0x%x, ref 1\n", (int)pM);
+ #endif
+
+ *ppThis = pM;
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* "Constructor" for a msg "object". Returns a pointer to
+ * the new object or NULL if no such object could be allocated.
+ * An object constructed via this function should only be destroyed
+ * via "msgDestruct()". This constructor does not query system time
+ * itself but rather uses a user-supplied value. This enables the caller
+ * to do some tricks to save processing time (done, for example, in the
+ * udp input).
+ * rgerhards, 2008-10-06
+ */
+rsRetVal msgConstructWithTime(smsg_t **ppThis, const struct syslogTime *stTime, const time_t ttGenTime)
+{
+ DEFiRet;
+
+ CHKiRet(msgBaseConstruct(ppThis));
+ (*ppThis)->ttGenTime = ttGenTime;
+ memcpy(&(*ppThis)->tRcvdAt, stTime, sizeof(struct syslogTime));
+ memcpy(&(*ppThis)->tTIMESTAMP, stTime, sizeof(struct syslogTime));
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* "Constructor" for a msg "object". Returns a pointer to
+ * the new object or NULL if no such object could be allocated.
+ * An object constructed via this function should only be destroyed
+ * via "msgDestruct()". This constructor, for historical reasons,
+ * also sets the two timestamps to the current time.
+ */
+rsRetVal msgConstruct(smsg_t **ppThis)
+{
+ DEFiRet;
+
+ CHKiRet(msgBaseConstruct(ppThis));
+ /* we initialize both timestamps to contain the current time, so that they
+ * are consistent. Also, this saves us from doing any further time calls just
+ * to obtain a timestamp. The memcpy() should not really make a difference,
+ * especially as I think there is no codepath currently where it would not be
+ * required (after I have cleaned up the pathes ;)). -- rgerhards, 2008-10-02
+ */
+ datetime.getCurrTime(&((*ppThis)->tRcvdAt), &((*ppThis)->ttGenTime), TIME_IN_LOCALTIME);
+ memcpy(&(*ppThis)->tTIMESTAMP, &(*ppThis)->tRcvdAt, sizeof(struct syslogTime));
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Special msg constructor, to be used when an object is deserialized.
+ * we do only the base init as we know the properties will be set in
+ * any case by the deserializer. We still do the "inexpensive" inits
+ * just to be on the safe side. The whole process needs to be
+ * refactored together with the msg serialization subsystem.
+ */
+rsRetVal
+msgConstructForDeserializer(smsg_t **ppThis)
+{
+ return msgBaseConstruct(ppThis);
+}
+
+
+/* some free handlers for (slightly) complicated cases... All of them may be called
+ * with an empty element.
+ */
+static inline void freeTAG(smsg_t *pThis)
+{
+ if(pThis->iLenTAG >= CONF_TAG_BUFSIZE)
+ free(pThis->TAG.pszTAG);
+}
+static inline void freeHOSTNAME(smsg_t *pThis)
+{
+ if(pThis->iLenHOSTNAME >= CONF_HOSTNAME_BUFSIZE)
+ free(pThis->pszHOSTNAME);
+}
+
+
+rsRetVal msgDestruct(smsg_t **ppThis)
+{
+ DEFiRet;
+ smsg_t *pThis;
+ int currRefCount;
+# ifdef HAVE_MALLOC_TRIM
+ int currCnt;
+# endif
+CODESTARTobjDestruct(msg)
+ #if DEV_DEBUG == 1
+ dbgprintf("msgDestruct\t0x%lx, "
+ "Ref now: %d\n", (unsigned long)pThis, pThis->iRefCount - 1);
+ #endif
+# ifdef HAVE_ATOMIC_BUILTINS
+ currRefCount = ATOMIC_DEC_AND_FETCH(&pThis->iRefCount, NULL);
+# else
+ MsgLock(pThis);
+ currRefCount = --pThis->iRefCount;
+# endif
+ if(currRefCount == 0)
+ {
+ #if DEV_DEBUG == 1
+ dbgprintf("msgDestruct\t0x%lx, RefCount now 0, doing DESTROY\n",
+ (unsigned long)pThis);
+ #endif
+ if(pThis->pszRawMsg != pThis->szRawMsg)
+ free(pThis->pszRawMsg);
+ freeTAG(pThis);
+ freeHOSTNAME(pThis);
+ if(pThis->pInputName != NULL)
+ prop.Destruct(&pThis->pInputName);
+ if((pThis->msgFlags & NEEDS_DNSRESOL) == 0) {
+ if(pThis->rcvFrom.pRcvFrom != NULL)
+ prop.Destruct(&pThis->rcvFrom.pRcvFrom);
+ } else {
+ free(pThis->rcvFrom.pfrominet);
+ }
+ if(pThis->pRcvFromIP != NULL)
+ prop.Destruct(&pThis->pRcvFromIP);
+ free(pThis->pszRcvdAt3164);
+ free(pThis->pszRcvdAt3339);
+ free(pThis->pszRcvdAt_MySQL);
+ free(pThis->pszRcvdAt_PgSQL);
+ free(pThis->pszTIMESTAMP_MySQL);
+ free(pThis->pszTIMESTAMP_PgSQL);
+ free(pThis->pszStrucData);
+ if(pThis->iLenPROGNAME >= CONF_PROGNAME_BUFSIZE)
+ free(pThis->PROGNAME.ptr);
+ if(pThis->pCSAPPNAME != NULL)
+ rsCStrDestruct(&pThis->pCSAPPNAME);
+ if(pThis->pCSPROCID != NULL)
+ rsCStrDestruct(&pThis->pCSPROCID);
+ if(pThis->pCSMSGID != NULL)
+ rsCStrDestruct(&pThis->pCSMSGID);
+ if(pThis->json != NULL)
+ json_object_put(pThis->json);
+ if(pThis->localvars != NULL)
+ json_object_put(pThis->localvars);
+ if(pThis->pszUUID != NULL)
+ free(pThis->pszUUID);
+# ifndef HAVE_ATOMIC_BUILTINS
+ MsgUnlock(pThis);
+# endif
+ pthread_mutex_destroy(&pThis->mut);
+ /* now we need to do our own optimization. Testing has shown that at least the glibc
+ * malloc() subsystem returns memory to the OS far too late in our case. So we need
+ * to help it a bit, by calling malloc_trim(), which will tell the alloc subsystem
+ * to consolidate and return to the OS. We keep 128K for our use, as a safeguard
+ * to too-frequent reallocs. But more importantly, we call this hook only every
+ * 100,000 messages (which is an approximation, as we do not work with atomic
+ * operations on the counter. --- rgerhards, 2009-06-22.
+ */
+# ifdef HAVE_MALLOC_TRIM
+ { /* standard C requires a new block for a new variable definition!
+ * To simplify matters, we use modulo arithmetic and live with the fact
+ * that we trim too often when the counter wraps.
+ */
+ static unsigned iTrimCtr = 1;
+ currCnt = ATOMIC_INC_AND_FETCH_unsigned(&iTrimCtr, &mutTrimCtr);
+ if(currCnt % 100000 == 0) {
+ malloc_trim(128*1024);
+ }
+ }
+# endif
+ } else {
+# ifndef HAVE_ATOMIC_BUILTINS
+ MsgUnlock(pThis);
+# endif
+ pThis = NULL; /* tell framework not to destructing the object! */
+ }
+ENDobjDestruct(msg)
+
+/* The macros below are used in MsgDup(). I use macros
+ * to keep the fuction code somewhat more readyble. It is my
+ * replacement for inline functions in CPP
+ */
+#define tmpCOPYSZ(name) \
+ if(pOld->psz##name != NULL) { \
+ if((pNew->psz##name = srUtilStrDup(pOld->psz##name, pOld->iLen##name)) == NULL) {\
+ msgDestruct(&pNew);\
+ return NULL;\
+ }\
+ pNew->iLen##name = pOld->iLen##name;\
+ }
+
+/* copy the CStr objects.
+ * if the old value is NULL, we do not need to do anything because we
+ * initialized the new value to NULL via calloc().
+ */
+#define tmpCOPYCSTR(name) \
+ if(pOld->pCS##name != NULL) {\
+ if(rsCStrConstructFromCStr(&(pNew->pCS##name), pOld->pCS##name) != RS_RET_OK) {\
+ msgDestruct(&pNew);\
+ return NULL;\
+ }\
+ cstrFinalize(pNew->pCS##name); \
+ }
+/* Constructs a message object by duplicating another one.
+ * Returns NULL if duplication failed. We do not need to lock the
+ * message object here, because a fully-created msg object is never
+ * allowed to be manipulated. For this, MsgDup() must be used, so MsgDup()
+ * can never run into a situation where the message object is being
+ * modified while its content is copied - it's forbidden by definition.
+ * rgerhards, 2007-07-10
+ */
+smsg_t* MsgDup(smsg_t* pOld)
+{
+ smsg_t* pNew;
+ rsRetVal localRet;
+
+ assert(pOld != NULL);
+
+ if(msgConstructWithTime(&pNew, &pOld->tTIMESTAMP, pOld->ttGenTime) != RS_RET_OK) {
+ return NULL;
+ }
+
+ /* now copy the message properties */
+ pNew->iRefCount = 1;
+ pNew->iSeverity = pOld->iSeverity;
+ pNew->iFacility = pOld->iFacility;
+ pNew->msgFlags = pOld->msgFlags;
+ pNew->iProtocolVersion = pOld->iProtocolVersion;
+ pNew->tRcvdAt = pOld->tRcvdAt;
+ pNew->offMSG = pOld->offMSG;
+ pNew->iLenRawMsg = pOld->iLenRawMsg;
+ pNew->iLenMSG = pOld->iLenMSG;
+ pNew->iLenTAG = pOld->iLenTAG;
+ pNew->iLenHOSTNAME = pOld->iLenHOSTNAME;
+ if((pOld->msgFlags & NEEDS_DNSRESOL)) {
+ localRet = msgSetFromSockinfo(pNew, pOld->rcvFrom.pfrominet);
+ if(localRet != RS_RET_OK) {
+ /* if something fails, we accept loss of this property, it is
+ * better than losing the whole message.
+ */
+ pNew->msgFlags &= ~NEEDS_DNSRESOL;
+ pNew->rcvFrom.pRcvFrom = NULL; /* make sure no dangling values */
+ }
+ } else {
+ if(pOld->rcvFrom.pRcvFrom != NULL) {
+ pNew->rcvFrom.pRcvFrom = pOld->rcvFrom.pRcvFrom;
+ prop.AddRef(pNew->rcvFrom.pRcvFrom);
+ }
+ }
+ if(pOld->pRcvFromIP != NULL) {
+ pNew->pRcvFromIP = pOld->pRcvFromIP;
+ prop.AddRef(pNew->pRcvFromIP);
+ }
+ if(pOld->pInputName != NULL) {
+ pNew->pInputName = pOld->pInputName;
+ prop.AddRef(pNew->pInputName);
+ }
+ if(pOld->iLenTAG > 0) {
+ if(pOld->iLenTAG < CONF_TAG_BUFSIZE) {
+ memcpy(pNew->TAG.szBuf, pOld->TAG.szBuf, pOld->iLenTAG + 1);
+ } else {
+ if((pNew->TAG.pszTAG = srUtilStrDup(pOld->TAG.pszTAG, pOld->iLenTAG)) == NULL) {
+ msgDestruct(&pNew);
+ return NULL;
+ }
+ pNew->iLenTAG = pOld->iLenTAG;
+ }
+ }
+ if(pOld->pszRawMsg == pOld->szRawMsg) {
+ memcpy(pNew->szRawMsg, pOld->szRawMsg, pOld->iLenRawMsg + 1);
+ pNew->pszRawMsg = pNew->szRawMsg;
+ } else {
+ tmpCOPYSZ(RawMsg);
+ }
+ if(pOld->pszHOSTNAME == NULL) {
+ pNew->pszHOSTNAME = NULL;
+ } else {
+ if(pOld->iLenHOSTNAME < CONF_HOSTNAME_BUFSIZE) {
+ memcpy(pNew->szHOSTNAME, pOld->szHOSTNAME, pOld->iLenHOSTNAME + 1);
+ pNew->pszHOSTNAME = pNew->szHOSTNAME;
+ } else {
+ tmpCOPYSZ(HOSTNAME);
+ }
+ }
+ if(pOld->pszStrucData == NULL) {
+ pNew->pszStrucData = NULL;
+ } else {
+ pNew->pszStrucData = (uchar*)strdup((char*)pOld->pszStrucData);
+ pNew->lenStrucData = pOld->lenStrucData;
+ }
+
+ tmpCOPYCSTR(APPNAME);
+ tmpCOPYCSTR(PROCID);
+ tmpCOPYCSTR(MSGID);
+
+ if(pOld->json != NULL)
+ pNew->json = jsonDeepCopy(pOld->json);
+ if(pOld->localvars != NULL)
+ pNew->localvars = jsonDeepCopy(pOld->localvars);
+
+ /* we do not copy all other cache properties, as we do not even know
+ * if they are needed once again. So we let them re-create if needed.
+ */
+
+ return pNew;
+}
+#undef tmpCOPYSZ
+#undef tmpCOPYCSTR
+
+
+/* This method serializes a message object. That means the whole
+ * object is modified into text form. That text form is suitable for
+ * later reconstruction of the object by calling MsgDeSerialize().
+ * The most common use case for this method is the creation of an
+ * on-disk representation of the message object.
+ * We do not serialize the cache properties. We re-create them when needed.
+ * This saves us a lot of memory. Performance is no concern, as serializing
+ * is a so slow operation that recration of the caches does not count. Also,
+ * we do not serialize --currently none--, as this is only a helper variable
+ * during msg construction - and never again used later.
+ * rgerhards, 2008-01-03
+ */
+static rsRetVal MsgSerialize(smsg_t *pThis, strm_t *pStrm)
+{
+ uchar *psz;
+ int len;
+ DEFiRet;
+
+ assert(pThis != NULL);
+ assert(pStrm != NULL);
+
+ /* then serialize elements */
+ CHKiRet(obj.BeginSerialize(pStrm, (obj_t*) pThis));
+ objSerializeSCALAR(pStrm, iProtocolVersion, SHORT);
+ objSerializeSCALAR(pStrm, iSeverity, SHORT);
+ objSerializeSCALAR(pStrm, iFacility, SHORT);
+ objSerializeSCALAR(pStrm, msgFlags, INT);
+ objSerializeSCALAR(pStrm, ttGenTime, INT);
+ objSerializeSCALAR(pStrm, tRcvdAt, SYSLOGTIME);
+ objSerializeSCALAR(pStrm, tTIMESTAMP, SYSLOGTIME);
+
+ CHKiRet(obj.SerializeProp(pStrm, UCHAR_CONSTANT("pszTAG"), PROPTYPE_PSZ, (void*)
+ ((pThis->iLenTAG < CONF_TAG_BUFSIZE) ? pThis->TAG.szBuf : pThis->TAG.pszTAG)));
+
+ objSerializePTR(pStrm, pszRawMsg, PSZ);
+ objSerializePTR(pStrm, pszHOSTNAME, PSZ);
+ getInputName(pThis, &psz, &len);
+ CHKiRet(obj.SerializeProp(pStrm, UCHAR_CONSTANT("pszInputName"), PROPTYPE_PSZ, (void*) psz));
+ psz = getRcvFrom(pThis);
+ CHKiRet(obj.SerializeProp(pStrm, UCHAR_CONSTANT("pszRcvFrom"), PROPTYPE_PSZ, (void*) psz));
+ psz = getRcvFromIP(pThis);
+ CHKiRet(obj.SerializeProp(pStrm, UCHAR_CONSTANT("pszRcvFromIP"), PROPTYPE_PSZ, (void*) psz));
+ psz = pThis->pszStrucData;
+ CHKiRet(obj.SerializeProp(pStrm, UCHAR_CONSTANT("pszStrucData"), PROPTYPE_PSZ, (void*) psz));
+ if(pThis->json != NULL) {
+ MsgLock(pThis);
+ psz = (uchar*) json_object_get_string(pThis->json);
+ MsgUnlock(pThis);
+ CHKiRet(obj.SerializeProp(pStrm, UCHAR_CONSTANT("json"), PROPTYPE_PSZ, (void*) psz));
+ }
+ if(pThis->localvars != NULL) {
+ MsgLock(pThis);
+ psz = (uchar*) json_object_get_string(pThis->localvars);
+ MsgUnlock(pThis);
+ CHKiRet(obj.SerializeProp(pStrm, UCHAR_CONSTANT("localvars"), PROPTYPE_PSZ, (void*) psz));
+ }
+
+ objSerializePTR(pStrm, pCSAPPNAME, CSTR);
+ objSerializePTR(pStrm, pCSPROCID, CSTR);
+ objSerializePTR(pStrm, pCSMSGID, CSTR);
+
+ objSerializePTR(pStrm, pszUUID, PSZ);
+
+ if(pThis->pRuleset != NULL) {
+ CHKiRet(obj.SerializeProp(pStrm, UCHAR_CONSTANT("pszRuleset"), PROPTYPE_PSZ,
+ rulesetGetName(pThis->pRuleset)));
+ }
+
+ /* offset must be serialized after pszRawMsg, because we need that to obtain the correct
+ * MSG size.
+ */
+ objSerializeSCALAR(pStrm, offMSG, INT);
+
+ CHKiRet(obj.EndSerialize(pStrm));
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* This is a helper for MsgDeserialize that re-inits the var object. This
+ * whole construct should be replaced, var is really ready to be retired.
+ * But as an interim help during refactoring let's introduce this function
+ * here (and thus NOT as method of var object!). -- rgerhads, 2012-11-06
+ */
+static void
+reinitVar(var_t *pVar)
+{
+ rsCStrDestruct(&pVar->pcsName); /* no longer needed */
+ if(pVar->varType == VARTYPE_STR) {
+ if(pVar->val.pStr != NULL)
+ rsCStrDestruct(&pVar->val.pStr);
+ }
+}
+/* deserialize the message again
+ * we deserialize the properties in the same order that we serialized them. Except
+ * for some checks to cover downlevel version, we do not need to do all these
+ * CPU intense name checkings.
+ */
+#define isProp(name) !rsCStrSzStrCmp(pVar->pcsName, (uchar*) name, sizeof(name) - 1)
+rsRetVal
+MsgDeserialize(smsg_t * const pMsg, strm_t *pStrm)
+{
+ prop_t *myProp;
+ prop_t *propRcvFrom = NULL;
+ prop_t *propRcvFromIP = NULL;
+ struct json_tokener *tokener;
+ var_t *pVar = NULL;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pStrm, strm);
+
+ CHKiRet(var.Construct(&pVar));
+ CHKiRet(var.ConstructFinalize(pVar));
+
+ CHKiRet(objDeserializeProperty(pVar, pStrm));
+ if(isProp("iProtocolVersion")) {
+ setProtocolVersion(pMsg, pVar->val.num);
+ reinitVar(pVar);
+ CHKiRet(objDeserializeProperty(pVar, pStrm));
+ }
+ if(isProp("iSeverity")) {
+ pMsg->iSeverity = pVar->val.num;
+ reinitVar(pVar);
+ CHKiRet(objDeserializeProperty(pVar, pStrm));
+ }
+ if(isProp("iFacility")) {
+ pMsg->iFacility = pVar->val.num;
+ reinitVar(pVar);
+ CHKiRet(objDeserializeProperty(pVar, pStrm));
+ }
+ if(isProp("msgFlags")) {
+ pMsg->msgFlags = pVar->val.num;
+ reinitVar(pVar);
+ CHKiRet(objDeserializeProperty(pVar, pStrm));
+ }
+ if(isProp("ttGenTime")) {
+ pMsg->ttGenTime = pVar->val.num;
+ reinitVar(pVar);
+ CHKiRet(objDeserializeProperty(pVar, pStrm));
+ }
+ if(isProp("tRcvdAt")) {
+ memcpy(&pMsg->tRcvdAt, &pVar->val.vSyslogTime, sizeof(struct syslogTime));
+ reinitVar(pVar);
+ CHKiRet(objDeserializeProperty(pVar, pStrm));
+ }
+ if(isProp("tTIMESTAMP")) {
+ memcpy(&pMsg->tTIMESTAMP, &pVar->val.vSyslogTime, sizeof(struct syslogTime));
+ reinitVar(pVar);
+ CHKiRet(objDeserializeProperty(pVar, pStrm));
+ }
+ if(isProp("pszTAG")) {
+ MsgSetTAG(pMsg, rsCStrGetSzStrNoNULL(pVar->val.pStr), cstrLen(pVar->val.pStr));
+ reinitVar(pVar);
+ CHKiRet(objDeserializeProperty(pVar, pStrm));
+ }
+ if(isProp("pszRawMsg")) {
+ MsgSetRawMsg(pMsg, (char*) rsCStrGetSzStrNoNULL(pVar->val.pStr), cstrLen(pVar->val.pStr));
+ reinitVar(pVar);
+ CHKiRet(objDeserializeProperty(pVar, pStrm));
+ }
+ if(isProp("pszHOSTNAME")) {
+ MsgSetHOSTNAME(pMsg, rsCStrGetSzStrNoNULL(pVar->val.pStr), rsCStrLen(pVar->val.pStr));
+ reinitVar(pVar);
+ CHKiRet(objDeserializeProperty(pVar, pStrm));
+ }
+ if(isProp("pszInputName")) {
+ /* we need to create a property */
+ CHKiRet(prop.Construct(&myProp));
+ CHKiRet(prop.SetString(myProp, rsCStrGetSzStrNoNULL(pVar->val.pStr), rsCStrLen(pVar->val.pStr)));
+ CHKiRet(prop.ConstructFinalize(myProp));
+ MsgSetInputName(pMsg, myProp);
+ prop.Destruct(&myProp);
+ reinitVar(pVar);
+ CHKiRet(objDeserializeProperty(pVar, pStrm));
+ }
+ if(isProp("pszRcvFrom")) {
+ MsgSetRcvFromStr(pMsg, rsCStrGetSzStrNoNULL(pVar->val.pStr), rsCStrLen(pVar->val.pStr), &propRcvFrom);
+ prop.Destruct(&propRcvFrom);
+ reinitVar(pVar);
+ CHKiRet(objDeserializeProperty(pVar, pStrm));
+ }
+ if(isProp("pszRcvFromIP")) {
+ MsgSetRcvFromIPStr(pMsg, rsCStrGetSzStrNoNULL(pVar->val.pStr), rsCStrLen(pVar->val.pStr),
+ &propRcvFromIP);
+ prop.Destruct(&propRcvFromIP);
+ reinitVar(pVar);
+ CHKiRet(objDeserializeProperty(pVar, pStrm));
+ }
+ if(isProp("pszStrucData")) {
+ MsgSetStructuredData(pMsg, (char*) rsCStrGetSzStrNoNULL(pVar->val.pStr));
+ reinitVar(pVar);
+ CHKiRet(objDeserializeProperty(pVar, pStrm));
+ }
+ if(isProp("json")) {
+ tokener = json_tokener_new();
+ pMsg->json = json_tokener_parse_ex(tokener, (char*)rsCStrGetSzStrNoNULL(pVar->val.pStr),
+ cstrLen(pVar->val.pStr));
+ json_tokener_free(tokener);
+ reinitVar(pVar);
+ CHKiRet(objDeserializeProperty(pVar, pStrm));
+ }
+ if(isProp("localvars")) {
+ tokener = json_tokener_new();
+ pMsg->localvars = json_tokener_parse_ex(tokener, (char*)rsCStrGetSzStrNoNULL(pVar->val.pStr),
+ cstrLen(pVar->val.pStr));
+ json_tokener_free(tokener);
+ reinitVar(pVar);
+ CHKiRet(objDeserializeProperty(pVar, pStrm));
+ }
+ if(isProp("pCSAPPNAME")) {
+ MsgSetAPPNAME(pMsg, (char*) rsCStrGetSzStrNoNULL(pVar->val.pStr));
+ reinitVar(pVar);
+ CHKiRet(objDeserializeProperty(pVar, pStrm));
+ }
+ if(isProp("pCSPROCID")) {
+ MsgSetPROCID(pMsg, (char*) rsCStrGetSzStrNoNULL(pVar->val.pStr));
+ reinitVar(pVar);
+ CHKiRet(objDeserializeProperty(pVar, pStrm));
+ }
+ if(isProp("pCSMSGID")) {
+ MsgSetMSGID(pMsg, (char*) rsCStrGetSzStrNoNULL(pVar->val.pStr));
+ reinitVar(pVar);
+ CHKiRet(objDeserializeProperty(pVar, pStrm));
+ }
+ if(isProp("pszUUID")) {
+ pMsg->pszUUID = ustrdup(rsCStrGetSzStrNoNULL(pVar->val.pStr));
+ reinitVar(pVar);
+ CHKiRet(objDeserializeProperty(pVar, pStrm));
+ }
+ if(isProp("pszRuleset")) {
+ MsgSetRulesetByName(pMsg, pVar->val.pStr);
+ reinitVar(pVar);
+ CHKiRet(objDeserializeProperty(pVar, pStrm));
+ }
+ /* "offMSG" must always be our last field, so we use this as an
+ * indicator if the sequence is correct. This is a bit questionable,
+ * but on the other hand it works decently AND we will probably replace
+ * the whole persisted format soon in any case. -- rgerhards, 2012-11-06
+ */
+ if(!isProp("offMSG")) {
+ DBGPRINTF("error property: %s\n", rsCStrGetSzStrNoNULL(pVar->pcsName));
+ ABORT_FINALIZE(RS_RET_DS_PROP_SEQ_ERR);
+ }
+ MsgSetMSGoffs(pMsg, pVar->val.num);
+finalize_it:
+ if(pVar != NULL)
+ var.Destruct(&pVar);
+ if(Debug && iRet != RS_RET_OK) {
+ dbgprintf("MsgDeserialize error %d\n", iRet);
+ }
+ RETiRet;
+}
+#undef isProp
+
+
+/* Increment reference count - see description of the "msg"
+ * structure for details. As a convenience to developers,
+ * this method returns the msg pointer that is passed to it.
+ * It is recommended that it is called as follows:
+ *
+ * pSecondMsgPointer = MsgAddRef(pOrgMsgPointer);
+ */
+smsg_t *MsgAddRef(smsg_t * const pM)
+{
+ assert(pM != NULL);
+# ifdef HAVE_ATOMIC_BUILTINS
+ ATOMIC_INC(&pM->iRefCount, NULL);
+# else
+ MsgLock(pM);
+ pM->iRefCount++;
+ MsgUnlock(pM);
+# endif
+ #if DEV_DEBUG == 1
+ dbgprintf("MsgAddRef\t0x%x done, Ref now: %d\n", (int)pM, pM->iRefCount);
+ #endif
+ return(pM);
+}
+
+
+/* This functions tries to acquire the PROCID from TAG. Its primary use is
+ * when a legacy syslog message has been received and should be forwarded as
+ * syslog-protocol (or the PROCID is requested for any other reason).
+ * In legacy syslog, the PROCID is considered to be the character sequence
+ * between the first [ and the first ]. This usually are digits only, but we
+ * do not check that. However, if there is no closing ], we do not assume we
+ * can obtain a PROCID. Take in mind that not every legacy syslog message
+ * actually has a PROCID.
+ * rgerhards, 2005-11-24
+ * THIS MUST be called with the message lock locked.
+ */
+static rsRetVal acquirePROCIDFromTAG(smsg_t * const pM)
+{
+ register int i;
+ uchar *pszTag;
+ DEFiRet;
+
+ assert(pM != NULL);
+
+ if(pM->pCSPROCID != NULL)
+ return RS_RET_OK; /* we are already done ;) */
+
+ if(msgGetProtocolVersion(pM) != 0)
+ return RS_RET_OK; /* we can only emulate if we have legacy format */
+
+ pszTag = (uchar*) ((pM->iLenTAG < CONF_TAG_BUFSIZE) ? pM->TAG.szBuf : pM->TAG.pszTAG);
+
+ /* find first '['... */
+ i = 0;
+ while((i < pM->iLenTAG) && (pszTag[i] != '['))
+ ++i;
+ if(!(i < pM->iLenTAG))
+ return RS_RET_OK; /* no [, so can not emulate... */
+
+ ++i; /* skip '[' */
+
+ /* now obtain the PROCID string... */
+ CHKiRet(cstrConstruct(&pM->pCSPROCID));
+ while((i < pM->iLenTAG) && (pszTag[i] != ']')) {
+ CHKiRet(cstrAppendChar(pM->pCSPROCID, pszTag[i]));
+ ++i;
+ }
+
+ if(!(i < pM->iLenTAG)) {
+ /* oops... it looked like we had a PROCID, but now it has
+ * turned out this is not true. In this case, we need to free
+ * the buffer and simply return. Note that this is NOT an error
+ * case!
+ */
+ cstrDestruct(&pM->pCSPROCID);
+ FINALIZE;
+ }
+
+ /* OK, finally we could obtain a PROCID. So let's use it ;) */
+ cstrFinalize(pM->pCSPROCID);
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Parse and set the "programname" for a given MSG object. Programname
+ * is a BSD concept, it is the tag without any instance-specific information.
+ * Precisely, the programname is terminated by either (whichever occurs first):
+ * - end of tag
+ * - nonprintable character
+ * - ':'
+ * - '['
+ * - '/'
+ * The above definition has been taken from the FreeBSD syslogd sources.
+ *
+ * The program name is not parsed by default, because it is infrequently-used.
+ * IMPORTANT: A locked message object must be provided, else a crash will occur.
+ * rgerhards, 2005-10-19
+ */
+static rsRetVal
+acquireProgramName(smsg_t * const pM)
+{
+ int i;
+ uchar *pszTag, *pszProgName;
+ DEFiRet;
+
+ assert(pM != NULL);
+ pszTag = (uchar*) ((pM->iLenTAG < CONF_TAG_BUFSIZE) ? pM->TAG.szBuf : pM->TAG.pszTAG);
+ for( i = 0
+ ; (i < pM->iLenTAG) && isprint((int) pszTag[i])
+ && (pszTag[i] != '\0') && (pszTag[i] != ':')
+ && (pszTag[i] != '[')
+ && (runConf->globals.parser.bPermitSlashInProgramname || (pszTag[i] != '/'))
+ ; ++i)
+ ; /* just search end of PROGNAME */
+ if(i < CONF_PROGNAME_BUFSIZE) {
+ pszProgName = pM->PROGNAME.szBuf;
+ } else {
+ CHKmalloc(pM->PROGNAME.ptr = malloc(i+1));
+ pszProgName = pM->PROGNAME.ptr;
+ }
+ memcpy((char*)pszProgName, (char*)pszTag, i);
+ pszProgName[i] = '\0';
+ pM->iLenPROGNAME = i;
+finalize_it:
+ RETiRet;
+}
+
+
+/* Access methods - dumb & easy, not a comment for each ;)
+ */
+void setProtocolVersion(smsg_t * const pM, int iNewVersion)
+{
+ assert(pM != NULL);
+ if(iNewVersion != 0 && iNewVersion != 1) {
+ dbgprintf("Tried to set unsupported protocol version %d - changed to 0.\n", iNewVersion);
+ iNewVersion = 0;
+ }
+ pM->iProtocolVersion = iNewVersion;
+}
+
+/* note: string is taken from constant pool, do NOT free */
+static const char *getProtocolVersionString(smsg_t * const pM)
+{
+ assert(pM != NULL);
+ return(pM->iProtocolVersion ? "1" : "0");
+}
+
+void
+msgSetPRI(smsg_t *const __restrict__ pMsg, syslog_pri_t pri)
+{
+ if(pri > LOG_MAXPRI)
+ pri = LOG_PRI_INVLD;
+ pMsg->iFacility = pri2fac(pri),
+ pMsg->iSeverity = pri2sev(pri);
+}
+
+#ifdef USE_LIBUUID
+/* note: libuuid seems not to be thread-safe, so we need
+ * to get some safeguards in place.
+ */
+static pthread_mutex_t mutUUID = PTHREAD_MUTEX_INITIALIZER;
+
+static void call_uuid_generate(uuid_t uuid)
+{
+ pthread_mutex_lock(&mutUUID);
+ pthread_cleanup_push(mutexCancelCleanup, &mutUUID);
+ uuid_generate(uuid);
+ pthread_cleanup_pop(1);
+}
+
+static void msgSetUUID(smsg_t * const pM)
+{
+ size_t lenRes = sizeof(uuid_t) * 2 + 1;
+ char hex_char [] = "0123456789ABCDEF";
+ unsigned int byte_nbr;
+ uuid_t uuid;
+
+ dbgprintf("[MsgSetUUID] START, lenRes %llu\n", (long long unsigned) lenRes);
+ assert(pM != NULL);
+
+ if((pM->pszUUID = (uchar*) malloc(lenRes)) == NULL) {
+ pM->pszUUID = (uchar *)"";
+ } else {
+ call_uuid_generate(uuid);
+ for (byte_nbr = 0; byte_nbr < sizeof (uuid_t); byte_nbr++) {
+ pM->pszUUID[byte_nbr * 2 + 0] = hex_char[uuid [byte_nbr] >> 4];
+ pM->pszUUID[byte_nbr * 2 + 1] = hex_char[uuid [byte_nbr] & 15];
+ }
+
+ pM->pszUUID[lenRes-1] = '\0';
+ dbgprintf("[MsgSetUUID] UUID : %s LEN: %d \n", pM->pszUUID, (int)lenRes);
+ }
+ dbgprintf("[MsgSetUUID] END\n");
+}
+
+static void getUUID(smsg_t * const pM, uchar **pBuf, int *piLen)
+{
+ dbgprintf("[getUUID] START\n");
+ if(pM == NULL) {
+ dbgprintf("[getUUID] pM is NULL\n");
+ *pBuf= UCHAR_CONSTANT("");
+ *piLen = 0;
+ } else {
+ if(pM->pszUUID == NULL) {
+ dbgprintf("[getUUID] pM->pszUUID is NULL\n");
+ MsgLock(pM);
+ /* re-query, things may have changed in the mean time... */
+ if(pM->pszUUID == NULL)
+ msgSetUUID(pM);
+ MsgUnlock(pM);
+ } else { /* UUID already there we reuse it */
+ dbgprintf("[getUUID] pM->pszUUID already exists\n");
+ }
+ *pBuf = pM->pszUUID;
+ *piLen = sizeof(uuid_t) * 2;
+ }
+ dbgprintf("[getUUID] END\n");
+}
+#endif
+
+int ATTR_NONNULL()
+getRawMsgLen(const smsg_t *const pMsg)
+{
+ return (pMsg->pszRawMsg == NULL) ? 0 : pMsg->iLenRawMsg;
+}
+
+void
+getRawMsg(const smsg_t * const pM, uchar **pBuf, int *piLen)
+{
+ if(pM == NULL) {
+ *pBuf= UCHAR_CONSTANT("");
+ *piLen = 0;
+ } else {
+ if(pM->pszRawMsg == NULL) {
+ *pBuf= UCHAR_CONSTANT("");
+ *piLen = 0;
+ } else {
+ *pBuf = pM->pszRawMsg;
+ *piLen = pM->iLenRawMsg;
+ }
+ }
+}
+
+void
+getRawMsgAfterPRI(smsg_t * const pM, uchar **pBuf, int *piLen)
+{
+ if(pM == NULL) {
+ *pBuf= UCHAR_CONSTANT("");
+ *piLen = 0;
+ } else {
+ if(pM->pszRawMsg == NULL) {
+ *pBuf= UCHAR_CONSTANT("");
+ *piLen = 0;
+ } else {
+ /* unfortunately, pM->offAfterPRI seems NOT to be
+ * correct/consistent in all cases. imuxsock and imudp
+ * seem to have other values than imptcp. Testbench
+ * covers some of that. As a work-around, we caluculate
+ * the value ourselfes here. -- rgerhards, 2015-10-09
+ */
+ size_t offAfterPRI = 0;
+ if(pM->pszRawMsg[0] == '<') { /* do we have a PRI? */
+ if(pM->pszRawMsg[2] == '>')
+ offAfterPRI = 3;
+ else if(pM->pszRawMsg[3] == '>')
+ offAfterPRI = 4;
+ else if(pM->pszRawMsg[4] == '>')
+ offAfterPRI = 5;
+ }
+ *pBuf = pM->pszRawMsg + offAfterPRI;
+ *piLen = pM->iLenRawMsg - offAfterPRI;
+ }
+ }
+}
+
+
+/* note: setMSGLen() is only for friends who really know what they
+ * do. Setting an invalid length can be desasterous!
+ */
+void setMSGLen(smsg_t * const pM, int lenMsg)
+{
+ pM->iLenMSG = lenMsg;
+}
+
+int getMSGLen(smsg_t * const pM)
+{
+ return((pM == NULL) ? 0 : pM->iLenMSG);
+}
+
+uchar *getMSG(smsg_t * const pM)
+{
+ uchar *ret;
+ if(pM == NULL)
+ ret = UCHAR_CONSTANT("");
+ else {
+ if(pM->iLenMSG == 0)
+ ret = UCHAR_CONSTANT("");
+ else
+ ret = pM->pszRawMsg + pM->offMSG;
+ }
+ return ret;
+}
+
+
+/* Get PRI value as integer */
+int
+getPRIi(const smsg_t * const pM)
+{
+ syslog_pri_t pri = (pM->iFacility << 3) + (pM->iSeverity);
+ if(pri > 191)
+ pri = LOG_PRI_INVLD;
+ return pri;
+}
+
+
+/* Get PRI value in text form
+ */
+const char *
+getPRI(smsg_t * const pM)
+{
+ /* PRI is a number in the range 0..191. Thus, we use a simple lookup table to obtain the
+ * string value. It looks a bit clumpsy here in code ;)
+ */
+ int iPRI;
+
+ if(pM == NULL)
+ return "";
+
+ iPRI = getPRIi(pM);
+ return (iPRI > 191) ? "invld" : (char*)syslog_pri_names[iPRI].pszName;
+}
+
+
+static const char *
+formatISOWeekOrYear(enum tplFormatTypes eFmt, struct syslogTime *pTm)
+{
+ if(pTm->year >= 1970 && pTm->year <= 2099) {
+ int isoWeekYear;
+ int isoWeek;
+
+ isoWeek = getISOWeek(pTm, &isoWeekYear);
+
+ if (eFmt == tplFmtISOWeek) {
+ return two_digits[isoWeek];
+ } else {
+ return years[isoWeekYear - 1967];
+ }
+ } else {
+ return "YEAR OUT OF RANGE(1970-2099)";
+ }
+}
+
+const char *
+getTimeReported(smsg_t * const pM, enum tplFormatTypes eFmt)
+{
+ if(pM == NULL)
+ return "";
+
+ switch(eFmt) {
+ case tplFmtDefault:
+ case tplFmtRFC3164Date:
+ case tplFmtRFC3164BuggyDate:
+ MsgLock(pM);
+ if(pM->pszTIMESTAMP3164 == NULL) {
+ pM->pszTIMESTAMP3164 = pM->pszTimestamp3164;
+ datetime.formatTimestamp3164(&pM->tTIMESTAMP, pM->pszTIMESTAMP3164,
+ (eFmt == tplFmtRFC3164BuggyDate));
+ }
+ MsgUnlock(pM);
+ return(pM->pszTIMESTAMP3164);
+ case tplFmtMySQLDate:
+ MsgLock(pM);
+ if(pM->pszTIMESTAMP_MySQL == NULL) {
+ if((pM->pszTIMESTAMP_MySQL = malloc(15)) == NULL) {
+ MsgUnlock(pM);
+ return "";
+ }
+ datetime.formatTimestampToMySQL(&pM->tTIMESTAMP, pM->pszTIMESTAMP_MySQL);
+ }
+ MsgUnlock(pM);
+ return(pM->pszTIMESTAMP_MySQL);
+ case tplFmtPgSQLDate:
+ MsgLock(pM);
+ if(pM->pszTIMESTAMP_PgSQL == NULL) {
+ if((pM->pszTIMESTAMP_PgSQL = malloc(21)) == NULL) {
+ MsgUnlock(pM);
+ return "";
+ }
+ datetime.formatTimestampToPgSQL(&pM->tTIMESTAMP, pM->pszTIMESTAMP_PgSQL);
+ }
+ MsgUnlock(pM);
+ return(pM->pszTIMESTAMP_PgSQL);
+ case tplFmtRFC3339Date:
+ MsgLock(pM);
+ if(pM->pszTIMESTAMP3339 == NULL) {
+ pM->pszTIMESTAMP3339 = pM->pszTimestamp3339;
+ datetime.formatTimestamp3339(&pM->tTIMESTAMP, pM->pszTIMESTAMP3339);
+ }
+ MsgUnlock(pM);
+ return(pM->pszTIMESTAMP3339);
+ case tplFmtUnixDate:
+ MsgLock(pM);
+ if(pM->pszTIMESTAMP_Unix[0] == '\0') {
+ datetime.formatTimestampUnix(&pM->tTIMESTAMP, pM->pszTIMESTAMP_Unix);
+ }
+ MsgUnlock(pM);
+ return(pM->pszTIMESTAMP_Unix);
+ case tplFmtSecFrac:
+ if(pM->pszTIMESTAMP_SecFrac[0] == '\0') {
+ MsgLock(pM);
+ /* re-check, may have changed while we did not hold lock */
+ if(pM->pszTIMESTAMP_SecFrac[0] == '\0') {
+ datetime.formatTimestampSecFrac(&pM->tTIMESTAMP, pM->pszTIMESTAMP_SecFrac);
+ }
+ MsgUnlock(pM);
+ }
+ return(pM->pszTIMESTAMP_SecFrac);
+ case tplFmtWDayName:
+ return wdayNames[getWeekdayNbr(&pM->tTIMESTAMP)];
+ case tplFmtWDay:
+ return one_digit[getWeekdayNbr(&pM->tTIMESTAMP)];
+ case tplFmtMonth:
+ return two_digits[(int)pM->tTIMESTAMP.month];
+ case tplFmtYear:
+ if(pM->tTIMESTAMP.year >= 1967 && pM->tTIMESTAMP.year <= 2099)
+ return years[pM->tTIMESTAMP.year - 1967];
+ else
+ return "YEAR OUT OF RANGE(1967-2099)";
+ case tplFmtDay:
+ return two_digits[(int)pM->tTIMESTAMP.day];
+ case tplFmtHour:
+ return two_digits[(int)pM->tTIMESTAMP.hour];
+ case tplFmtMinute:
+ return two_digits[(int)pM->tTIMESTAMP.minute];
+ case tplFmtSecond:
+ return two_digits[(int)pM->tTIMESTAMP.second];
+ case tplFmtTZOffsHour:
+ return two_digits[(int)pM->tTIMESTAMP.OffsetHour];
+ case tplFmtTZOffsMin:
+ return two_digits[(int)pM->tTIMESTAMP.OffsetMinute];
+ case tplFmtTZOffsDirection:
+ return (pM->tTIMESTAMP.OffsetMode == '+')? "+" : "-";
+ case tplFmtOrdinal:
+ return daysInYear[getOrdinal(&pM->tTIMESTAMP)];
+ case tplFmtWeek:
+ return two_digits[getWeek(&pM->tTIMESTAMP)];
+ case tplFmtISOWeek:
+ case tplFmtISOWeekYear:
+ return formatISOWeekOrYear(eFmt, &pM->tTIMESTAMP);
+ }
+ return "INVALID eFmt OPTION!";
+}
+
+
+
+static const char *getTimeUTC(struct syslogTime *const __restrict__ pTmIn,
+ const enum tplFormatTypes eFmt,
+ unsigned short *const __restrict__ pbMustBeFreed)
+{
+ struct syslogTime tUTC;
+ char *retbuf = NULL;
+
+ timeConvertToUTC(pTmIn, &tUTC);
+ struct syslogTime *const pTm = &tUTC;
+
+ switch(eFmt) {
+ case tplFmtDefault:
+ if((retbuf = malloc(16)) != NULL) {
+ datetime.formatTimestamp3164(pTm, retbuf, 0);
+ }
+ break;
+ case tplFmtMySQLDate:
+ if((retbuf = malloc(15)) != NULL) {
+ datetime.formatTimestampToMySQL(pTm, retbuf);
+ }
+ break;
+ case tplFmtPgSQLDate:
+ if((retbuf = malloc(21)) != NULL) {
+ datetime.formatTimestampToPgSQL(pTm, retbuf);
+ }
+ break;
+ case tplFmtRFC3164Date:
+ case tplFmtRFC3164BuggyDate:
+ if((retbuf = malloc(16)) != NULL) {
+ datetime.formatTimestamp3164(pTm, retbuf, (eFmt == tplFmtRFC3164BuggyDate));
+ }
+ break;
+ case tplFmtRFC3339Date:
+ if((retbuf = malloc(33)) != NULL) {
+ datetime.formatTimestamp3339(pTm, retbuf);
+ }
+ break;
+ case tplFmtUnixDate:
+ if((retbuf = malloc(12)) != NULL) {
+ datetime.formatTimestampUnix(pTm, retbuf);
+ }
+ break;
+ case tplFmtSecFrac:
+ if((retbuf = malloc(7)) != NULL) {
+ datetime.formatTimestampSecFrac(pTm, retbuf);
+ }
+ break;
+ case tplFmtWDayName:
+ retbuf = strdup(wdayNames[getWeekdayNbr(pTm)]);
+ break;
+ case tplFmtWDay:
+ retbuf = strdup(one_digit[getWeekdayNbr(pTm)]);
+ break;
+ case tplFmtMonth:
+ retbuf = strdup(two_digits[(int)pTm->month]);
+ break;
+ case tplFmtYear:
+ if(pTm->year >= 1967 && pTm->year <= 2099)
+ retbuf = strdup(years[pTm->year - 1967]);
+ else
+ retbuf = strdup("YEAR OUT OF RANGE(1967-2099)");
+ break;
+ case tplFmtDay:
+ retbuf = strdup(two_digits[(int)pTm->day]);
+ break;
+ case tplFmtHour:
+ retbuf = strdup(two_digits[(int)pTm->hour]);
+ break;
+ case tplFmtMinute:
+ retbuf = strdup(two_digits[(int)pTm->minute]);
+ break;
+ case tplFmtSecond:
+ retbuf = strdup(two_digits[(int)pTm->second]);
+ break;
+ case tplFmtTZOffsHour:
+ retbuf = strdup(two_digits[(int)pTm->OffsetHour]);
+ break;
+ case tplFmtTZOffsMin:
+ retbuf = strdup(two_digits[(int)pTm->OffsetMinute]);
+ break;
+ case tplFmtTZOffsDirection:
+ retbuf = strdup((pTm->OffsetMode == '+')? "+" : "-");
+ break;
+ case tplFmtOrdinal:
+ retbuf = strdup(daysInYear[getOrdinal(pTm)]);
+ break;
+ case tplFmtWeek:
+ retbuf = strdup(two_digits[getWeek(pTm)]);
+ break;
+ case tplFmtISOWeek:
+ case tplFmtISOWeekYear:
+ retbuf = strdup(formatISOWeekOrYear(eFmt, pTm));
+ break;
+ }
+
+ if(retbuf == NULL) {
+ retbuf = (char*)"internal error: invalid eFmt option or malloc problem";
+ *pbMustBeFreed = 0;
+ } else {
+ *pbMustBeFreed = 1;
+ }
+ return retbuf;
+}
+
+static const char *
+getTimeGenerated(smsg_t *const __restrict__ pM,
+ const enum tplFormatTypes eFmt)
+{
+ struct syslogTime *const pTm = &pM->tRcvdAt;
+ if(pM == NULL)
+ return "";
+
+ switch(eFmt) {
+ case tplFmtDefault:
+ MsgLock(pM);
+ if(pM->pszRcvdAt3164 == NULL) {
+ if((pM->pszRcvdAt3164 = malloc(16)) == NULL) {
+ MsgUnlock(pM);
+ return "";
+ }
+ datetime.formatTimestamp3164(pTm, pM->pszRcvdAt3164, 0);
+ }
+ MsgUnlock(pM);
+ return(pM->pszRcvdAt3164);
+ case tplFmtMySQLDate:
+ MsgLock(pM);
+ if(pM->pszRcvdAt_MySQL == NULL) {
+ if((pM->pszRcvdAt_MySQL = malloc(15)) == NULL) {
+ MsgUnlock(pM);
+ return "";
+ }
+ datetime.formatTimestampToMySQL(pTm, pM->pszRcvdAt_MySQL);
+ }
+ MsgUnlock(pM);
+ return(pM->pszRcvdAt_MySQL);
+ case tplFmtPgSQLDate:
+ MsgLock(pM);
+ if(pM->pszRcvdAt_PgSQL == NULL) {
+ if((pM->pszRcvdAt_PgSQL = malloc(21)) == NULL) {
+ MsgUnlock(pM);
+ return "";
+ }
+ datetime.formatTimestampToPgSQL(pTm, pM->pszRcvdAt_PgSQL);
+ }
+ MsgUnlock(pM);
+ return(pM->pszRcvdAt_PgSQL);
+ case tplFmtRFC3164Date:
+ case tplFmtRFC3164BuggyDate:
+ MsgLock(pM);
+ if(pM->pszRcvdAt3164 == NULL) {
+ if((pM->pszRcvdAt3164 = malloc(16)) == NULL) {
+ MsgUnlock(pM);
+ return "";
+ }
+ datetime.formatTimestamp3164(pTm, pM->pszRcvdAt3164,
+ (eFmt == tplFmtRFC3164BuggyDate));
+ }
+ MsgUnlock(pM);
+ return(pM->pszRcvdAt3164);
+ case tplFmtRFC3339Date:
+ MsgLock(pM);
+ if(pM->pszRcvdAt3339 == NULL) {
+ if((pM->pszRcvdAt3339 = malloc(33)) == NULL) {
+ MsgUnlock(pM);
+ return "";
+ }
+ datetime.formatTimestamp3339(pTm, pM->pszRcvdAt3339);
+ }
+ MsgUnlock(pM);
+ return(pM->pszRcvdAt3339);
+ case tplFmtUnixDate:
+ MsgLock(pM);
+ if(pM->pszRcvdAt_Unix[0] == '\0') {
+ datetime.formatTimestampUnix(pTm, pM->pszRcvdAt_Unix);
+ }
+ MsgUnlock(pM);
+ return(pM->pszRcvdAt_Unix);
+ case tplFmtSecFrac:
+ if(pM->pszRcvdAt_SecFrac[0] == '\0') {
+ MsgLock(pM);
+ /* re-check, may have changed while we did not hold lock */
+ if(pM->pszRcvdAt_SecFrac[0] == '\0') {
+ datetime.formatTimestampSecFrac(pTm, pM->pszRcvdAt_SecFrac);
+ }
+ MsgUnlock(pM);
+ }
+ return(pM->pszRcvdAt_SecFrac);
+ case tplFmtWDayName:
+ return wdayNames[getWeekdayNbr(pTm)];
+ case tplFmtWDay:
+ return one_digit[getWeekdayNbr(pTm)];
+ case tplFmtMonth:
+ return two_digits[(int)pTm->month];
+ case tplFmtYear:
+ if(pTm->year >= 1967 && pTm->year <= 2099)
+ return years[pTm->year - 1967];
+ else
+ return "YEAR OUT OF RANGE(1967-2099)";
+ case tplFmtDay:
+ return two_digits[(int)pTm->day];
+ case tplFmtHour:
+ return two_digits[(int)pTm->hour];
+ case tplFmtMinute:
+ return two_digits[(int)pTm->minute];
+ case tplFmtSecond:
+ return two_digits[(int)pTm->second];
+ case tplFmtTZOffsHour:
+ return two_digits[(int)pTm->OffsetHour];
+ case tplFmtTZOffsMin:
+ return two_digits[(int)pTm->OffsetMinute];
+ case tplFmtTZOffsDirection:
+ return (pTm->OffsetMode == '+')? "+" : "-";
+ case tplFmtOrdinal:
+ return daysInYear[getOrdinal(pTm)];
+ case tplFmtWeek:
+ return two_digits[getWeek(pTm)];
+ case tplFmtISOWeek:
+ case tplFmtISOWeekYear:
+ return formatISOWeekOrYear(eFmt, pTm);
+ }
+ return "INVALID eFmt OPTION!";
+}
+
+
+static const char *getSeverity(smsg_t * const pM)
+{
+ const char *name = NULL;
+
+ if(pM == NULL)
+ return "";
+
+ if(pM->iSeverity > 7) {
+ name = "invld";
+ } else {
+ name = syslog_number_names[pM->iSeverity];
+ }
+
+ return name;
+}
+
+
+static const char *getSeverityStr(smsg_t * const pM)
+{
+ const char *name = NULL;
+
+ if(pM == NULL)
+ return "";
+
+ if(pM->iSeverity > 7) {
+ name = "invld";
+ } else {
+ name = syslog_severity_names[pM->iSeverity];
+ }
+
+ return name;
+}
+
+static const char *getFacility(smsg_t * const pM)
+{
+ const char *name = NULL;
+
+ if(pM == NULL)
+ return "";
+
+ if(pM->iFacility > 23) {
+ name = "invld";
+ } else {
+ name = syslog_number_names[pM->iFacility];
+ }
+
+ return name;
+}
+
+static const char *getFacilityStr(smsg_t * const pM)
+{
+ const char *name = NULL;
+
+ if(pM == NULL)
+ return "";
+
+ if(pM->iFacility > 23) {
+ name = "invld";
+ } else {
+ name = syslog_fac_names[pM->iFacility];
+ }
+
+ return name;
+}
+
+
+/* set flow control state (if not called, the default - NO_DELAY - is used)
+ * This needs no locking because it is only done while the object is
+ * not fully constructed (which also means you must not call this
+ * method after the msg has been handed over to a queue).
+ * rgerhards, 2008-03-14
+ */
+rsRetVal
+MsgSetFlowControlType(smsg_t * const pMsg, flowControl_t eFlowCtl)
+{
+ DEFiRet;
+ assert(pMsg != NULL);
+ assert(eFlowCtl == eFLOWCTL_NO_DELAY || eFlowCtl == eFLOWCTL_LIGHT_DELAY || eFlowCtl == eFLOWCTL_FULL_DELAY);
+
+ pMsg->flowCtlType = eFlowCtl;
+
+ RETiRet;
+}
+
+/* set offset after which PRI in raw msg starts
+ * rgerhards, 2009-06-16
+ */
+rsRetVal
+MsgSetAfterPRIOffs(smsg_t * const pMsg, int offs)
+{
+ assert(pMsg != NULL);
+ pMsg->offAfterPRI = offs;
+ return RS_RET_OK;
+}
+
+
+/* rgerhards 2004-11-24: set APP-NAME in msg object
+ * This is not locked, because it either is called during message
+ * construction (where we need no locking) or later as part of a function
+ * which already obtained the lock. So in general, this function here must
+ * only be called when it it safe to do so without it aquiring a lock.
+ */
+rsRetVal ATTR_NONNULL(1,2)
+MsgSetAPPNAME(smsg_t *__restrict__ const pMsg, const char *pszAPPNAME)
+{
+ DEFiRet;
+ assert(pMsg != NULL);
+ if(pszAPPNAME[0] == '\0') {
+ pszAPPNAME = "-"; /* RFC5424 NIL value */
+ }
+ if(pMsg->pCSAPPNAME == NULL) {
+ /* we need to obtain the object first */
+ CHKiRet(rsCStrConstruct(&pMsg->pCSAPPNAME));
+ }
+ /* if we reach this point, we have the object */
+ CHKiRet(rsCStrSetSzStr(pMsg->pCSAPPNAME, (uchar*) pszAPPNAME));
+ cstrFinalize(pMsg->pCSAPPNAME);
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* rgerhards 2004-11-24: set PROCID in msg object
+ */
+rsRetVal MsgSetPROCID(smsg_t *__restrict__ const pMsg, const char* pszPROCID)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pMsg, msg);
+ if(pMsg->pCSPROCID == NULL) {
+ /* we need to obtain the object first */
+ CHKiRet(cstrConstruct(&pMsg->pCSPROCID));
+ }
+ /* if we reach this point, we have the object */
+ CHKiRet(rsCStrSetSzStr(pMsg->pCSPROCID, (uchar*) pszPROCID));
+ cstrFinalize(pMsg->pCSPROCID);
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* check if we have a procid, and, if not, try to acquire/emulate it.
+ * This must be called WITHOUT the message lock being held.
+ * rgerhards, 2009-06-26
+ */
+static void preparePROCID(smsg_t * const pM, sbool bLockMutex)
+{
+ if(pM->pCSPROCID == NULL) {
+ if(bLockMutex == LOCK_MUTEX)
+ MsgLock(pM);
+ /* re-query, things may have changed in the mean time... */
+ if(pM->pCSPROCID == NULL)
+ acquirePROCIDFromTAG(pM);
+ if(bLockMutex == LOCK_MUTEX)
+ MsgUnlock(pM);
+ }
+}
+
+
+#if 0
+/* rgerhards, 2005-11-24
+ */
+static int getPROCIDLen(smsg_t *pM, sbool bLockMutex)
+{
+ assert(pM != NULL);
+ preparePROCID(pM, bLockMutex);
+ return (pM->pCSPROCID == NULL) ? 1 : rsCStrLen(pM->pCSPROCID);
+}
+#endif
+
+
+/* rgerhards, 2005-11-24
+ */
+char *getPROCID(smsg_t * const pM, sbool bLockMutex)
+{
+ uchar *pszRet;
+
+ ISOBJ_TYPE_assert(pM, msg);
+ if(bLockMutex == LOCK_MUTEX)
+ MsgLock(pM);
+ preparePROCID(pM, MUTEX_ALREADY_LOCKED);
+ if(pM->pCSPROCID == NULL)
+ pszRet = UCHAR_CONSTANT("-");
+ else
+ pszRet = rsCStrGetSzStrNoNULL(pM->pCSPROCID);
+ if(bLockMutex == LOCK_MUTEX)
+ MsgUnlock(pM);
+ return (char*) pszRet;
+}
+
+
+/* rgerhards 2004-11-24: set MSGID in msg object
+ */
+rsRetVal MsgSetMSGID(smsg_t * const pMsg, const char* pszMSGID)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pMsg, msg);
+ if(pMsg->pCSMSGID == NULL) {
+ /* we need to obtain the object first */
+ CHKiRet(rsCStrConstruct(&pMsg->pCSMSGID));
+ }
+ /* if we reach this point, we have the object */
+ CHKiRet(rsCStrSetSzStr(pMsg->pCSMSGID, (uchar*) pszMSGID));
+ cstrFinalize(pMsg->pCSMSGID);
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Return state of last parser. If it had success, "OK" is returned, else
+ * "FAIL". All from the constant pool.
+ */
+static const char *getParseSuccess(smsg_t * const pM)
+{
+ return (pM->bParseSuccess) ? "OK" : "FAIL";
+}
+
+
+/* al, 2011-07-26: LockMsg to avoid race conditions
+ */
+static const char *getMSGID(smsg_t * const pM)
+{
+ if (pM->pCSMSGID == NULL) {
+ return "-";
+ }
+ else {
+ MsgLock(pM);
+ char* pszreturn = (char*) rsCStrGetSzStrNoNULL(pM->pCSMSGID);
+ MsgUnlock(pM);
+ return pszreturn;
+ }
+}
+
+/* rgerhards 2012-03-15: set parser success (an integer, acutally bool)
+ */
+void MsgSetParseSuccess(smsg_t * const pMsg, int bSuccess)
+{
+ assert(pMsg != NULL);
+ pMsg->bParseSuccess = bSuccess;
+}
+
+
+/* return full message as a json string */
+const uchar*
+msgGetJSONMESG(smsg_t *__restrict__ const pMsg)
+{
+ struct json_object *json;
+ struct json_object *jval;
+ uchar *pRes; /* result pointer */
+ rs_size_t bufLen = -1; /* length of string or -1, if not known */
+
+ json = json_object_new_object();
+
+ jval = json_object_new_string((char*)getMSG(pMsg));
+ json_object_object_add(json, "msg", jval);
+
+ getRawMsg(pMsg, &pRes, &bufLen);
+ jval = json_object_new_string((char*)pRes);
+ json_object_object_add(json, "rawmsg", jval);
+
+ pRes = (uchar*)getTimeReported(pMsg, tplFmtRFC3339Date);
+ jval = json_object_new_string((char*)pRes);
+ json_object_object_add(json, "timereported", jval);
+
+ jval = json_object_new_string(getHOSTNAME(pMsg));
+ json_object_object_add(json, "hostname", jval);
+
+ getTAG(pMsg, &pRes, &bufLen, LOCK_MUTEX);
+ jval = json_object_new_string((char*)pRes);
+ json_object_object_add(json, "syslogtag", jval);
+
+ getInputName(pMsg, &pRes, &bufLen);
+ jval = json_object_new_string((char*)pRes);
+ json_object_object_add(json, "inputname", jval);
+
+ jval = json_object_new_string((char*)getRcvFrom(pMsg));
+ json_object_object_add(json, "fromhost", jval);
+
+ jval = json_object_new_string((char*)getRcvFromIP(pMsg));
+ json_object_object_add(json, "fromhost-ip", jval);
+
+ jval = json_object_new_string(getPRI(pMsg));
+ json_object_object_add(json, "pri", jval);
+
+ jval = json_object_new_string(getFacility(pMsg));
+ json_object_object_add(json, "syslogfacility", jval);
+
+ jval = json_object_new_string(getSeverity(pMsg));
+ json_object_object_add(json, "syslogseverity", jval);
+
+ pRes = (uchar*)getTimeGenerated(pMsg, tplFmtRFC3339Date);
+ jval = json_object_new_string((char*)pRes);
+ json_object_object_add(json, "timegenerated", jval);
+
+ jval = json_object_new_string((char*)getProgramName(pMsg, LOCK_MUTEX));
+ json_object_object_add(json, "programname", jval);
+
+ jval = json_object_new_string(getProtocolVersionString(pMsg));
+ json_object_object_add(json, "protocol-version", jval);
+
+ MsgGetStructuredData(pMsg, &pRes, &bufLen);
+ jval = json_object_new_string((char*)pRes);
+ json_object_object_add(json, "structured-data", jval);
+
+ jval = json_object_new_string(getAPPNAME(pMsg, LOCK_MUTEX));
+ json_object_object_add(json, "app-name", jval);
+
+ jval = json_object_new_string(getPROCID(pMsg, LOCK_MUTEX));
+ json_object_object_add(json, "procid", jval);
+
+ jval = json_object_new_string(getMSGID(pMsg));
+ json_object_object_add(json, "msgid", jval);
+
+#ifdef USE_LIBUUID
+ if(pMsg->pszUUID == NULL) {
+ jval = NULL;
+ } else {
+ getUUID(pMsg, &pRes, &bufLen);
+ jval = json_object_new_string((char*)pRes);
+ }
+ json_object_object_add(json, "uuid", jval);
+#endif
+
+ json_object_object_add(json, "$!", json_object_get(pMsg->json));
+
+ pRes = (uchar*) strdup(json_object_get_string(json));
+ json_object_put(json);
+ return pRes;
+}
+
+/* rgerhards 2009-06-12: set associated ruleset
+ */
+void MsgSetRuleset(smsg_t * const pMsg, ruleset_t *pRuleset)
+{
+ assert(pMsg != NULL);
+ pMsg->pRuleset = pRuleset;
+}
+
+
+/* set TAG in msg object
+ * (rewritten 2009-06-18 rgerhards)
+ */
+void MsgSetTAG(smsg_t *__restrict__ const pMsg, const uchar* pszBuf, const size_t lenBuf)
+{
+ uchar *pBuf;
+ assert(pMsg != NULL);
+
+ freeTAG(pMsg);
+
+ pMsg->iLenTAG = lenBuf;
+ if(pMsg->iLenTAG < CONF_TAG_BUFSIZE) {
+ /* small enough: use fixed buffer (faster!) */
+ pBuf = pMsg->TAG.szBuf;
+ } else {
+ if((pBuf = (uchar*) malloc(pMsg->iLenTAG + 1)) == NULL) {
+ /* truncate message, better than completely loosing it... */
+ pBuf = pMsg->TAG.szBuf;
+ pMsg->iLenTAG = CONF_TAG_BUFSIZE - 1;
+ } else {
+ pMsg->TAG.pszTAG = pBuf;
+ }
+ }
+
+ memcpy(pBuf, pszBuf, pMsg->iLenTAG);
+ pBuf[pMsg->iLenTAG] = '\0'; /* this also works with truncation! */
+}
+
+
+/* This function tries to emulate the TAG if none is
+ * set. Its primary purpose is to provide an old-style TAG
+ * when a syslog-protocol message has been received. Then,
+ * the tag is APP-NAME "[" PROCID "]". The function first checks
+ * if there is a TAG and, if not, if it can emulate it.
+ * rgerhards, 2005-11-24
+ */
+static void ATTR_NONNULL(1)
+tryEmulateTAG(smsg_t *const pM, const sbool bLockMutex)
+{
+ size_t lenTAG;
+ uchar bufTAG[CONF_TAG_MAXSIZE];
+ assert(pM != NULL);
+
+ if(bLockMutex == LOCK_MUTEX)
+ MsgLock(pM);
+ if(pM->iLenTAG > 0) {
+ if(bLockMutex == LOCK_MUTEX)
+ MsgUnlock(pM);
+ return; /* done, no need to emulate */
+ }
+
+ if(msgGetProtocolVersion(pM) == 1) {
+ if(!strcmp(getPROCID(pM, MUTEX_ALREADY_LOCKED), "-")) {
+ /* no process ID, use APP-NAME only */
+ MsgSetTAG(pM, (uchar*) getAPPNAME(pM, MUTEX_ALREADY_LOCKED),
+ getAPPNAMELen(pM, MUTEX_ALREADY_LOCKED));
+ } else {
+ /* now we can try to emulate */
+ lenTAG = snprintf((char*)bufTAG, CONF_TAG_MAXSIZE, "%s[%s]",
+ getAPPNAME(pM, MUTEX_ALREADY_LOCKED), getPROCID(pM, MUTEX_ALREADY_LOCKED));
+ bufTAG[sizeof(bufTAG)-1] = '\0'; /* just to make sure... */
+ MsgSetTAG(pM, bufTAG, lenTAG);
+ }
+ /* Signal change in TAG for acquireProgramName */
+ pM->iLenPROGNAME = -1;
+ }
+ if(bLockMutex == LOCK_MUTEX)
+ MsgUnlock(pM);
+}
+
+
+void ATTR_NONNULL(2,3)
+getTAG(smsg_t * const pM, uchar **const ppBuf, int *const piLen, const sbool bLockMutex)
+{
+ if(bLockMutex == LOCK_MUTEX)
+ MsgLock(pM);
+
+ if(pM == NULL) {
+ *ppBuf = UCHAR_CONSTANT("");
+ *piLen = 0;
+ } else {
+ if(pM->iLenTAG == 0)
+ tryEmulateTAG(pM, MUTEX_ALREADY_LOCKED);
+ if(pM->iLenTAG == 0) {
+ *ppBuf = UCHAR_CONSTANT("");
+ *piLen = 0;
+ } else {
+ *ppBuf = (pM->iLenTAG < CONF_TAG_BUFSIZE) ? pM->TAG.szBuf : pM->TAG.pszTAG;
+ *piLen = pM->iLenTAG;
+ }
+ }
+
+ if(bLockMutex == LOCK_MUTEX)
+ MsgUnlock(pM);
+}
+
+
+int getHOSTNAMELen(smsg_t * const pM)
+{
+ if(pM == NULL)
+ return 0;
+ else
+ if(pM->pszHOSTNAME == NULL) {
+ resolveDNS(pM);
+ if(pM->rcvFrom.pRcvFrom == NULL)
+ return 0;
+ else
+ return prop.GetStringLen(pM->rcvFrom.pRcvFrom);
+ } else
+ return pM->iLenHOSTNAME;
+}
+
+
+const char *getHOSTNAME(smsg_t * const pM)
+{
+ if(pM == NULL)
+ return "";
+ else
+ if(pM->pszHOSTNAME == NULL) {
+ resolveDNS(pM);
+ if(pM->rcvFrom.pRcvFrom == NULL) {
+ return "";
+ } else {
+ uchar *psz;
+ int len;
+ prop.GetString(pM->rcvFrom.pRcvFrom, &psz, &len);
+ return (char*) psz;
+ }
+ } else {
+ return (char*) pM->pszHOSTNAME;
+ }
+}
+
+
+uchar *getRcvFrom(smsg_t * const pM)
+{
+ uchar *psz;
+ int len;
+
+ if(pM == NULL) {
+ psz = UCHAR_CONSTANT("");
+ } else {
+ resolveDNS(pM);
+ if(pM->rcvFrom.pRcvFrom == NULL)
+ psz = UCHAR_CONSTANT("");
+ else
+ prop.GetString(pM->rcvFrom.pRcvFrom, &psz, &len);
+ }
+ return psz;
+}
+
+
+/* rgerhards 2004-11-24: set STRUCTURED DATA in msg object
+ */
+rsRetVal MsgSetStructuredData(smsg_t * const pMsg, const char* pszStrucData)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pMsg, msg);
+ free(pMsg->pszStrucData);
+ CHKmalloc(pMsg->pszStrucData = (uchar*)strdup(pszStrucData));
+ pMsg->lenStrucData = strlen(pszStrucData);
+finalize_it:
+ RETiRet;
+}
+
+
+/* get the "STRUCTURED-DATA" as sz string, including length */
+void
+MsgGetStructuredData(smsg_t * const pM, uchar **pBuf, rs_size_t *len)
+{
+ MsgLock(pM);
+ if(pM->pszStrucData == NULL) {
+ *pBuf = UCHAR_CONSTANT("-"),
+ *len = 1;
+ } else {
+ *pBuf = pM->pszStrucData,
+ *len = pM->lenStrucData;
+ }
+ MsgUnlock(pM);
+}
+
+/* get the "programname" as sz string
+ * rgerhards, 2005-10-19
+ */
+uchar * ATTR_NONNULL(1)
+getProgramName(smsg_t *const pM, const sbool bLockMutex)
+{
+ if(bLockMutex == LOCK_MUTEX) {
+ MsgLock(pM);
+ }
+
+ if(pM->iLenPROGNAME == -1) {
+ if(pM->iLenTAG == 0) {
+ uchar *pRes;
+ rs_size_t bufLen = -1;
+ getTAG(pM, &pRes, &bufLen, MUTEX_ALREADY_LOCKED);
+ }
+ acquireProgramName(pM);
+ }
+
+ if(bLockMutex == LOCK_MUTEX) {
+ MsgUnlock(pM);
+ }
+ return (pM->iLenPROGNAME < CONF_PROGNAME_BUFSIZE) ? pM->PROGNAME.szBuf
+ : pM->PROGNAME.ptr;
+}
+
+
+
+/* check if we have a APPNAME, and, if not, try to acquire/emulate it.
+ * rgerhards, 2009-06-26
+ */
+static void ATTR_NONNULL(1)
+prepareAPPNAME(smsg_t *const pM, const sbool bLockMutex)
+{
+ if(pM->pCSAPPNAME == NULL) {
+ if(bLockMutex == LOCK_MUTEX)
+ MsgLock(pM);
+
+ /* re-query as things might have changed during locking */
+ if(pM->pCSAPPNAME == NULL) {
+ if(msgGetProtocolVersion(pM) == 0) {
+ /* only then it makes sense to emulate */
+ MsgSetAPPNAME(pM, (char*)getProgramName(pM, MUTEX_ALREADY_LOCKED));
+ }
+ }
+
+ if(bLockMutex == LOCK_MUTEX)
+ MsgUnlock(pM);
+ }
+}
+
+/* rgerhards, 2005-11-24
+ */
+char *getAPPNAME(smsg_t * const pM, const sbool bLockMutex)
+{
+ uchar *pszRet;
+
+ assert(pM != NULL);
+ if(bLockMutex == LOCK_MUTEX)
+ MsgLock(pM);
+ prepareAPPNAME(pM, MUTEX_ALREADY_LOCKED);
+ if(pM->pCSAPPNAME == NULL)
+ pszRet = UCHAR_CONSTANT("");
+ else
+ pszRet = rsCStrGetSzStrNoNULL(pM->pCSAPPNAME);
+ if(bLockMutex == LOCK_MUTEX)
+ MsgUnlock(pM);
+ return (char*)pszRet;
+}
+
+/* rgerhards, 2005-11-24
+ */
+static int getAPPNAMELen(smsg_t * const pM, const sbool bLockMutex)
+{
+ assert(pM != NULL);
+ prepareAPPNAME(pM, bLockMutex);
+ return (pM->pCSAPPNAME == NULL) ? 0 : rsCStrLen(pM->pCSAPPNAME);
+}
+
+/* rgerhards 2008-09-10: set pszInputName in msg object. This calls AddRef()
+ * on the property, because this must be done in all current cases and there
+ * is no case expected where this may not be necessary.
+ * rgerhards, 2009-06-16
+ */
+void MsgSetInputName(smsg_t *pThis, prop_t *inputName)
+{
+ assert(pThis != NULL);
+
+ prop.AddRef(inputName);
+ if(pThis->pInputName != NULL)
+ prop.Destruct(&pThis->pInputName);
+ pThis->pInputName = inputName;
+}
+
+/* Set default TZ. Note that at most 7 chars are set, as we would
+ * otherwise overrun our buffer!
+ */
+void MsgSetDfltTZ(smsg_t *pThis, char *tz)
+{
+ strncpy(pThis->dfltTZ, tz, 7);
+ pThis->dfltTZ[7] = '\0'; /* ensure 0-Term in case of overflow! */
+}
+
+
+/* Set the pfrominet socket store, so that we can obtain the peer at some
+ * later time. Note that we do not check if pRcvFrom is already set, so this
+ * function must only be called during message creation.
+ * NOTE: msgFlags is NOT set. While this is somewhat a violation of layers,
+ * it is done because it gains us some performance. So the caller must make
+ * sure the message flags are properly maintained. For all current callers,
+ * this is always the case and without extra effort required.
+ * rgerhards, 2009-11-17
+ */
+rsRetVal
+msgSetFromSockinfo(smsg_t *pThis, struct sockaddr_storage *sa){
+ DEFiRet;
+ assert(pThis->rcvFrom.pRcvFrom == NULL);
+
+ CHKmalloc(pThis->rcvFrom.pfrominet = malloc(sizeof(struct sockaddr_storage)));
+ memcpy(pThis->rcvFrom.pfrominet, sa, sizeof(struct sockaddr_storage));
+
+finalize_it:
+ RETiRet;
+}
+
+/* rgerhards 2008-09-10: set RcvFrom name in msg object. This calls AddRef()
+ * on the property, because this must be done in all current cases and there
+ * is no case expected where this may not be necessary.
+ * rgerhards, 2009-06-30
+ */
+void MsgSetRcvFrom(smsg_t *pThis, prop_t *new)
+{
+ prop.AddRef(new);
+ MsgSetRcvFromWithoutAddRef(pThis, new);
+}
+
+
+/* This is used to set the property via a string. This function should not be
+ * called if there is a reliable way for a caller to make sure that the
+ * same name can be used across multiple messages. However, if it can not
+ * ensure that, calling this function is the second best thing, because it
+ * will re-use the previously created property if it contained the same
+ * name (but it works only for the immediate previous).
+ * rgerhards, 2009-06-31
+ */
+void MsgSetRcvFromStr(smsg_t * const pThis, const uchar *psz, const int len, prop_t **ppProp)
+{
+ assert(pThis != NULL);
+ assert(ppProp != NULL);
+
+ prop.CreateOrReuseStringProp(ppProp, psz, len);
+ MsgSetRcvFrom(pThis, *ppProp);
+}
+
+
+/* set RcvFromIP name in msg object. This calls AddRef()
+ * on the property, because this must be done in all current cases and there
+ * is no case expected where this may not be necessary.
+ * rgerhards, 2009-06-30
+ */
+rsRetVal MsgSetRcvFromIP(smsg_t *pThis, prop_t *new)
+{
+ assert(pThis != NULL);
+
+ prop.AddRef(new);
+ MsgSetRcvFromIPWithoutAddRef(pThis, new);
+ return RS_RET_OK;
+}
+
+
+/* This is used to set the property via a string. This function should not be
+ * called if there is a reliable way for a caller to make sure that the
+ * same name can be used across multiple messages. However, if it can not
+ * ensure that, calling this function is the second best thing, because it
+ * will re-use the previously created property if it contained the same
+ * name (but it works only for the immediate previous).
+ * rgerhards, 2009-06-31
+ */
+rsRetVal MsgSetRcvFromIPStr(smsg_t *const pThis, const uchar *psz, const int len, prop_t **ppProp)
+{
+ DEFiRet;
+ assert(pThis != NULL);
+
+ CHKiRet(prop.CreateOrReuseStringProp(ppProp, psz, len));
+ MsgSetRcvFromIP(pThis, *ppProp);
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* rgerhards 2004-11-09: set HOSTNAME in msg object
+ * rgerhards, 2007-06-21:
+ * Does not return anything. If an error occurs, the hostname is
+ * simply not set. I have changed this behaviour. The only problem
+ * we can run into is memory shortage. If we have such, it is better
+ * to loose the hostname than the full message. So we silently ignore
+ * that problem and hope that memory will be available the next time
+ * we need it. The rest of the code already knows how to handle an
+ * unset HOSTNAME.
+ */
+void MsgSetHOSTNAME(smsg_t *pThis, const uchar* pszHOSTNAME, const int lenHOSTNAME)
+{
+ assert(pThis != NULL);
+
+ freeHOSTNAME(pThis);
+
+ pThis->iLenHOSTNAME = lenHOSTNAME;
+ if(pThis->iLenHOSTNAME < CONF_HOSTNAME_BUFSIZE) {
+ /* small enough: use fixed buffer (faster!) */
+ pThis->pszHOSTNAME = pThis->szHOSTNAME;
+ } else if((pThis->pszHOSTNAME = (uchar*) malloc(pThis->iLenHOSTNAME + 1)) == NULL) {
+ /* truncate message, better than completely loosing it... */
+ pThis->pszHOSTNAME = pThis->szHOSTNAME;
+ pThis->iLenHOSTNAME = CONF_HOSTNAME_BUFSIZE - 1;
+ }
+
+ memcpy(pThis->pszHOSTNAME, pszHOSTNAME, pThis->iLenHOSTNAME);
+ pThis->pszHOSTNAME[pThis->iLenHOSTNAME] = '\0'; /* this also works with truncation! */
+}
+
+
+/* set the offset of the MSG part into the raw msg buffer
+ * Note that the offset may be higher than the length of the raw message
+ * (exactly by one). This can happen if we have a message that does not
+ * contain any MSG part.
+ */
+void MsgSetMSGoffs(smsg_t * const pMsg, int offs)
+{
+ ISOBJ_TYPE_assert(pMsg, msg);
+ pMsg->offMSG = offs;
+ if(offs > pMsg->iLenRawMsg) {
+ assert((int)offs - 1 == pMsg->iLenRawMsg);
+ pMsg->iLenMSG = 0;
+ } else {
+ pMsg->iLenMSG = pMsg->iLenRawMsg - offs;
+ }
+}
+
+
+/* replace the MSG part of a message. The update actually takes place inside
+ * rawmsg.
+ * There are two cases: either the new message will be larger than the new msg
+ * or it will be less than or equal. If it is less than or equal, we can utilize
+ * the previous message buffer. If it is larger, we can utilize the smsg_t-included
+ * message buffer if it fits in there. If this is not the case, we need to alloc
+ * a new, larger, chunk and copy over the data to it. Note that this function is
+ * (hopefully) relatively seldom being called, so some performance impact is
+ * uncritical. In any case, pszMSG is copied, so if it was dynamically allocated,
+ * the caller is responsible for freeing it.
+ * rgerhards, 2009-06-23
+ */
+rsRetVal MsgReplaceMSG(smsg_t *pThis, const uchar* pszMSG, int lenMSG)
+{
+ int lenNew;
+ uchar *bufNew;
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, msg);
+ assert(pszMSG != NULL);
+
+ lenNew = pThis->iLenRawMsg + lenMSG - pThis->iLenMSG;
+ if(lenMSG > pThis->iLenMSG && lenNew >= CONF_RAWMSG_BUFSIZE) {
+ /* we have lost our "bet" and need to alloc a new buffer ;) */
+ CHKmalloc(bufNew = malloc(lenNew + 1));
+ memcpy(bufNew, pThis->pszRawMsg, pThis->offMSG);
+ if(pThis->pszRawMsg != pThis->szRawMsg)
+ free(pThis->pszRawMsg);
+ pThis->pszRawMsg = bufNew;
+ }
+
+ if(lenMSG > 0)
+ memcpy(pThis->pszRawMsg + pThis->offMSG, pszMSG, lenMSG);
+ pThis->pszRawMsg[lenNew] = '\0'; /* this also works with truncation! */
+ pThis->iLenRawMsg = lenNew;
+ pThis->iLenMSG = lenMSG;
+
+finalize_it:
+ RETiRet;
+}
+
+/* truncate the (raw) message to configured max size.
+ * The function makes sure that the stored rawmsg remains
+ * properly terminated by '\0'.
+ */
+void ATTR_NONNULL()
+MsgTruncateToMaxSize(smsg_t *const pThis)
+{
+ ISOBJ_TYPE_assert(pThis, msg);
+ const int maxMsgSize = glblGetMaxLine(runConf);
+ assert(pThis->iLenRawMsg > maxMsgSize);
+
+ const int deltaSize = pThis->iLenRawMsg - maxMsgSize;
+ pThis->pszRawMsg[maxMsgSize] = '\0';
+ pThis->iLenRawMsg = maxMsgSize;
+ if(pThis->iLenMSG < deltaSize) {
+ pThis->iLenMSG = 0;
+ } else {
+ pThis->iLenMSG -= deltaSize;
+ }
+}
+
+/* set raw message in message object. Size of message is provided.
+ * The function makes sure that the stored rawmsg is properly
+ * terminated by '\0'.
+ * rgerhards, 2009-06-16
+ */
+void ATTR_NONNULL()
+MsgSetRawMsg(smsg_t *const pThis, const char*const pszRawMsg, const size_t lenMsg)
+{
+ ISOBJ_TYPE_assert(pThis, msg);
+ int deltaSize;
+ if(pThis->pszRawMsg != pThis->szRawMsg)
+ free(pThis->pszRawMsg);
+
+ deltaSize = (int) lenMsg - pThis->iLenRawMsg; /* value < 0 in truncation case! */
+ pThis->iLenRawMsg = lenMsg;
+ if(pThis->iLenRawMsg < CONF_RAWMSG_BUFSIZE) {
+ /* small enough: use fixed buffer (faster!) */
+ pThis->pszRawMsg = pThis->szRawMsg;
+ } else if((pThis->pszRawMsg = (uchar*) malloc(pThis->iLenRawMsg + 1)) == NULL) {
+ /* truncate message, better than completely loosing it... */
+ pThis->pszRawMsg = pThis->szRawMsg;
+ pThis->iLenRawMsg = CONF_RAWMSG_BUFSIZE - 1;
+ }
+
+ memcpy(pThis->pszRawMsg, pszRawMsg, pThis->iLenRawMsg);
+ pThis->pszRawMsg[pThis->iLenRawMsg] = '\0'; /* this also works with truncation! */
+ /* correct other information */
+ if(pThis->iLenRawMsg > pThis->offMSG)
+ pThis->iLenMSG += deltaSize;
+ else
+ pThis->iLenMSG = 0;
+}
+
+
+/* set raw message in message object. Size of message is not provided. This
+ * function should only be used when it is unavoidable (and over time we should
+ * try to remove it altogether).
+ * rgerhards, 2009-06-16
+ */
+void MsgSetRawMsgWOSize(smsg_t * const pMsg, char* pszRawMsg)
+{
+ MsgSetRawMsg(pMsg, pszRawMsg, strlen(pszRawMsg));
+}
+
+
+/* create textual representation of facility and severity.
+ * The variable pRes must point to a user-supplied buffer of
+ * at least 20 characters.
+ */
+static uchar *
+textpri(const smsg_t *const __restrict__ pMsg)
+{
+ int lenfac = len_syslog_fac_names[pMsg->iFacility];
+ int lensev = len_syslog_severity_names[pMsg->iSeverity];
+ int totlen = lenfac + 1 + lensev + 1;
+ char *pRes = malloc(totlen);
+ if(pRes != NULL) {
+ memcpy(pRes, syslog_fac_names[pMsg->iFacility], lenfac);
+ pRes[lenfac] = '.';
+ memcpy(pRes+lenfac+1, syslog_severity_names[pMsg->iSeverity], lensev+1 /* for \0! */);
+ }
+ return (uchar*)pRes;
+}
+
+
+/* This function returns the current date in different
+ * variants. It is used to construct the $NOW series of
+ * system properties. The returned buffer must be freed
+ * by the caller when no longer needed. If the function
+ * can not allocate memory, it returns a NULL pointer.
+ * Added 2007-07-10 rgerhards
+ */
+typedef enum ENOWType { NOW_NOW, NOW_YEAR, NOW_MONTH, NOW_DAY, NOW_HOUR,
+ NOW_HHOUR, NOW_QHOUR, NOW_MINUTE, NOW_WDAY } eNOWType;
+#define tmpBUFSIZE 16 /* size of formatting buffer */
+static uchar *getNOW(eNOWType eNow, struct syslogTime *t, const int inUTC)
+{
+ uchar *pBuf;
+ struct syslogTime tt;
+
+ if((pBuf = (uchar*) malloc(tmpBUFSIZE)) == NULL) {
+ return NULL;
+ }
+
+ if(t == NULL) { /* can happen if called via script engine */
+ datetime.getCurrTime(&tt, NULL, inUTC);
+ t = &tt;
+ }
+
+ if(t->year == 0 || t->inUTC != inUTC) { /* not yet set! */
+ datetime.getCurrTime(t, NULL, inUTC);
+ }
+
+ switch(eNow) {
+ case NOW_NOW:
+ memcpy(pBuf, two_digits[t->year/100], 2);
+ memcpy(pBuf+2, two_digits[t->year%100], 2);
+ pBuf[4] = '-';
+ memcpy(pBuf+5, two_digits[(int)t->month], 2);
+ pBuf[7] = '-';
+ memcpy(pBuf+8, two_digits[(int)t->day], 3);
+ break;
+ case NOW_YEAR:
+ memcpy(pBuf, two_digits[t->year/100], 2);
+ memcpy(pBuf+2, two_digits[t->year%100], 3);
+ break;
+ case NOW_MONTH:
+ memcpy(pBuf, two_digits[(int)t->month], 3);
+ break;
+ case NOW_DAY:
+ memcpy(pBuf, two_digits[(int)t->day], 3);
+ break;
+ case NOW_HOUR:
+ memcpy(pBuf, two_digits[(int)t->hour], 3);
+ break;
+ case NOW_HHOUR:
+ memcpy(pBuf, two_digits[t->minute/30], 3);
+ break;
+ case NOW_QHOUR:
+ memcpy(pBuf, two_digits[t->minute/15], 3);
+ break;
+ case NOW_MINUTE:
+ memcpy(pBuf, two_digits[(int)t->minute], 3);
+ break;
+ case NOW_WDAY:
+ memcpy(pBuf, one_digit[(int)t->wday], 2);
+ break;
+ }
+
+ return(pBuf);
+}
+#undef tmpBUFSIZE /* clean up */
+
+
+/* helper function to obtain correct JSON root and mutex depending on
+ * property type (essentially based on the property id. If a non-json
+ * property id is given the function errors out.
+ * Note well: jroot points to a pointer to a (ptr to a) json object.
+ * This is necessary because the caller needs a pointer to where the
+ * json object pointer is stored, that in turn is necessary because
+ * while the address of the actual pointer stays stable, the actual
+ * content is volatile until the caller has locked the variable tree,
+ * which we DO NOT do to keep calling semantics simple.
+ */
+static rsRetVal ATTR_NONNULL()
+getJSONRootAndMutex(smsg_t *const pMsg, const propid_t id,
+ struct json_object ***const jroot, pthread_mutex_t **const mut)
+{
+ DEFiRet;
+ assert(jroot != NULL); /* asserts also help static analyzer! */
+ assert(mut != NULL);
+ assert(*mut == NULL); /* caller shall have initialized this one! */
+ assert(id == PROP_CEE || id == PROP_LOCAL_VAR || id == PROP_GLOBAL_VAR);
+
+ if(id == PROP_CEE) {
+ *mut = &pMsg->mut;
+ *jroot = &pMsg->json;
+ } else if(id == PROP_LOCAL_VAR) {
+ *mut = &pMsg->mut;
+ *jroot = &pMsg->localvars;
+ } else if(id == PROP_GLOBAL_VAR) {
+ *mut = &glblVars_lock;
+ *jroot = &global_var_root;
+ } else {
+ LogError(0, RS_RET_NON_JSON_PROP, "internal error: "
+ "getJSONRootAndMutex; invalid property id %d", id);
+ iRet = RS_RET_NON_JSON_PROP;
+ }
+
+ RETiRet;
+}
+
+/* basically same function, but does not use property id, but the the
+ * variable name type indicator (char after starting $, e.g. $!myvar --> CEE)
+ */
+static rsRetVal ATTR_NONNULL()
+getJSONRootAndMutexByVarChar(smsg_t *const pMsg, const char c,
+ struct json_object ***const jroot, pthread_mutex_t **const mut)
+{
+ DEFiRet;
+ propid_t id;
+ assert(c == '!' || c == '.' || c == '/');
+
+ switch(c) {
+ case '!':
+ id = PROP_CEE;
+ break;
+ case '.':
+ id = PROP_LOCAL_VAR;
+ break;
+ case '/':
+ id = PROP_GLOBAL_VAR;
+ break;
+ default:
+ LogError(0, RS_RET_NON_JSON_PROP, "internal error: "
+ "getJSONRootAndMutex; invalid indicator char %c(%2.2x)", c, c);
+ ABORT_FINALIZE(RS_RET_NON_JSON_PROP);
+ break;
+ }
+ iRet = getJSONRootAndMutex(pMsg, id, jroot, mut);
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Get a JSON-Property as string value (used for various types of JSON-based vars) */
+rsRetVal
+getJSONPropVal(smsg_t * const pMsg, msgPropDescr_t *pProp, uchar **pRes, rs_size_t *buflen,
+ unsigned short *pbMustBeFreed)
+{
+ uchar *leaf;
+ struct json_object **jroot;
+ struct json_object *parent;
+ struct json_object *field;
+ pthread_mutex_t *mut = NULL;
+ DEFiRet;
+
+ *pRes = NULL;
+ CHKiRet(getJSONRootAndMutex(pMsg, pProp->id, &jroot, &mut));
+ pthread_mutex_lock(mut);
+
+ if(*jroot == NULL) FINALIZE;
+
+ if(!strcmp((char*)pProp->name, "!")) {
+ field = *jroot;
+ } else {
+ leaf = jsonPathGetLeaf(pProp->name, pProp->nameLen);
+ CHKiRet(jsonPathFindParent(*jroot, pProp->name, leaf, &parent, 0));
+ if(jsonVarExtract(parent, (char*)leaf, &field) == FALSE)
+ field = NULL;
+ }
+ if(field != NULL) {
+ *pRes = (uchar*) strdup(json_object_get_string(field));
+ *buflen = (int) ustrlen(*pRes);
+ *pbMustBeFreed = 1;
+ }
+
+finalize_it:
+ if(mut != NULL)
+ pthread_mutex_unlock(mut);
+ if(*pRes == NULL) {
+ /* could not find any value, so set it to empty */
+ *pRes = (unsigned char*)"";
+ *pbMustBeFreed = 0;
+ }
+ RETiRet;
+}
+
+
+/* Get a JSON-based-variable as native json object, except
+ * when it is string type, in which case a string is returned.
+ * This is an optimization to not use JSON when not strictly
+ * necessary. This in turn is helpful, as calling json-c is
+ * *very* expensive due to our need for locking and deep
+ * copies.
+ * The caller needs to check pjson and pcstr: one of them
+ * is non-NULL and contains the return value. Note that
+ * the caller is responsible for freeing the string pointer
+ * it if is being returned.
+ */
+rsRetVal
+msgGetJSONPropJSONorString(smsg_t * const pMsg, msgPropDescr_t *pProp, struct json_object **pjson,
+ uchar **pcstr)
+{
+ struct json_object **jroot;
+ uchar *leaf;
+ struct json_object *parent;
+ pthread_mutex_t *mut = NULL;
+ DEFiRet;
+
+ *pjson = NULL, *pcstr = NULL;
+
+ CHKiRet(getJSONRootAndMutex(pMsg, pProp->id, &jroot, &mut));
+ pthread_mutex_lock(mut);
+ if(!strcmp((char*)pProp->name, "!")) {
+ *pjson = *jroot;
+ FINALIZE;
+ }
+ if(*jroot == NULL) {
+ ABORT_FINALIZE(RS_RET_NOT_FOUND);
+ }
+ leaf = jsonPathGetLeaf(pProp->name, pProp->nameLen);
+ CHKiRet(jsonPathFindParent(*jroot, pProp->name, leaf, &parent, 0));
+ if(jsonVarExtract(parent, (char*)leaf, pjson) == FALSE) {
+ ABORT_FINALIZE(RS_RET_NOT_FOUND);
+ }
+ if(*pjson == NULL) {
+ /* we had a NULL json object and represent this as empty string */
+ *pcstr = (uchar*) strdup("");
+ } else {
+ if(json_object_get_type(*pjson) == json_type_string) {
+ *pcstr = (uchar*) strdup(json_object_get_string(*pjson));
+ *pjson = NULL;
+ }
+ }
+
+finalize_it:
+ /* we need a deep copy, as another thread may modify the object */
+ if(*pjson != NULL)
+ *pjson = jsonDeepCopy(*pjson);
+ if(mut != NULL)
+ pthread_mutex_unlock(mut);
+ RETiRet;
+}
+
+
+
+/* Get a JSON-based-variable as native json object */
+rsRetVal
+msgGetJSONPropJSON(smsg_t * const pMsg, msgPropDescr_t *pProp, struct json_object **pjson)
+{
+ struct json_object **jroot;
+ uchar *leaf;
+ struct json_object *parent;
+ pthread_mutex_t *mut = NULL;
+ DEFiRet;
+
+ *pjson = NULL;
+
+ CHKiRet(getJSONRootAndMutex(pMsg, pProp->id, &jroot, &mut));
+ pthread_mutex_lock(mut);
+
+ if(!strcmp((char*)pProp->name, "!")) {
+ *pjson = *jroot;
+ FINALIZE;
+ }
+ leaf = jsonPathGetLeaf(pProp->name, pProp->nameLen);
+ CHKiRet(jsonPathFindParent(*jroot, pProp->name, leaf, &parent, 0));
+ if(jsonVarExtract(parent, (char*)leaf, pjson) == FALSE) {
+ ABORT_FINALIZE(RS_RET_NOT_FOUND);
+ }
+
+finalize_it:
+ /* we need a deep copy, as another thread may modify the object */
+ if(*pjson != NULL)
+ *pjson = jsonDeepCopy(*pjson);
+ if(mut != NULL)
+ pthread_mutex_unlock(mut);
+ RETiRet;
+}
+
+
+/* Helper for jsonAddVal(), to be called onces we know there are actually
+ * json escapes inside the string. If so, this function takes over.
+ * Splitting the functions permits us to make some performance optimizations.
+ * For further details, see jsonAddVal().
+ */
+static rsRetVal ATTR_NONNULL(1, 4)
+jsonAddVal_escaped(uchar *const pSrc,
+ const unsigned buflen,
+ const unsigned len_none_escaped_head,
+ es_str_t **dst,
+ const int escapeAll)
+{
+ unsigned char c;
+ es_size_t i;
+ char numbuf[4];
+ unsigned ni;
+ unsigned char nc;
+ int j;
+ uchar wrkbuf[100000];
+ size_t dst_realloc_size;
+ size_t dst_size;
+ uchar *dst_base;
+ uchar *dst_w;
+ uchar *newbuf;
+ DEFiRet;
+
+ assert(len_none_escaped_head <= buflen);
+ /* first copy over unescaped head string */
+ if(len_none_escaped_head+10 > sizeof(wrkbuf)) {
+ dst_size = 2 * len_none_escaped_head;
+ CHKmalloc(dst_base = malloc(dst_size));
+ } else {
+ dst_size = sizeof(wrkbuf);
+ dst_base = wrkbuf;
+ }
+ dst_realloc_size = dst_size - 10; /* some buffer for escaping */
+ dst_w = dst_base;
+ memcpy(dst_w, pSrc, len_none_escaped_head);
+ dst_w += len_none_escaped_head;
+
+ /* now do the escaping */
+ for(i = len_none_escaped_head ; i < buflen ; ++i) {
+ const size_t dst_offset = dst_w - dst_base;
+ if(dst_offset >= dst_realloc_size) {
+ const size_t new_size = 2 * dst_size;
+ if(dst_base == wrkbuf) {
+ CHKmalloc(newbuf = malloc(new_size));
+ memcpy(newbuf, dst_base, dst_offset);
+ } else {
+ CHKmalloc(newbuf = realloc(dst_base, new_size));
+ }
+ dst_size = new_size;
+ dst_realloc_size = new_size - 10; /* some buffer for escaping */
+ dst_base = newbuf;
+ dst_w = dst_base + dst_offset;
+ }
+ c = pSrc[i];
+ if( (c >= 0x30 && c <= 0x5b)
+ || (c >= 0x23 && c <= 0x2e)
+ || (c >= 0x5d /* && c <= 0x10FFFF*/)
+ || c == 0x20 || c == 0x21) {
+ /* no need to escape */
+ *dst_w++ = c;
+ } else {
+ /* we must escape, try RFC4627-defined special sequences first */
+ switch(c) {
+ case '\0':
+ *dst_w++ = '\\';
+ *dst_w++ = 'u';
+ *dst_w++ = '0';
+ *dst_w++ = '0';
+ *dst_w++ = '0';
+ *dst_w++ = '0';
+ break;
+ case '\"':
+ *dst_w++ = '\\';
+ *dst_w++ = '"';
+ break;
+ case '/':
+ *dst_w++ = '\\';
+ *dst_w++ = '/';
+ break;
+ case '\\':
+ if (escapeAll == RSFALSE) {
+ ni = i + 1;
+ if (ni <= buflen) {
+ nc = pSrc[ni];
+
+ /* Attempt to not double encode */
+ if ( nc == '"' || nc == '/' || nc == '\\' || nc == 'b' || nc == 'f'
+ || nc == 'n' || nc == 'r' || nc == 't' || nc == 'u') {
+ *dst_w++ = c;
+ *dst_w++ = nc;
+ i = ni;
+ break;
+ }
+ }
+ }
+ *dst_w++ = '\\';
+ *dst_w++ = '\\';
+ break;
+ case '\010':
+ *dst_w++ = '\\';
+ *dst_w++ = 'b';
+ break;
+ case '\014':
+ *dst_w++ = '\\';
+ *dst_w++ = 'f';
+ break;
+ case '\n':
+ *dst_w++ = '\\';
+ *dst_w++ = 'n';
+ break;
+ case '\r':
+ *dst_w++ = '\\';
+ *dst_w++ = 'r';
+ break;
+ case '\t':
+ *dst_w++ = '\\';
+ *dst_w++ = 't';
+ break;
+ default:
+ /* TODO : proper Unicode encoding (see header comment) */
+ for(j = 0 ; j < 4 ; ++j) {
+ numbuf[3-j] = hexdigit[c % 16];
+ c = c / 16;
+ }
+ *dst_w++ = '\\';
+ *dst_w++ = 'u';
+ *dst_w++ = numbuf[0];
+ *dst_w++ = numbuf[1];
+ *dst_w++ = numbuf[2];
+ *dst_w++ = numbuf[3];
+ break;
+ }
+ }
+ }
+ if(*dst == NULL) {
+ *dst = es_newStrFromBuf((char *) dst_base, dst_w - dst_base);
+ } else {
+ es_addBuf(dst, (const char *) dst_base, dst_w - dst_base);
+ }
+finalize_it:
+ if(dst_base != wrkbuf) {
+ free(dst_base);
+ }
+ RETiRet;
+}
+
+
+/* Encode a JSON value and add it to provided string. Note that
+ * the string object may be NULL. In this case, it is created
+ * if and only if escaping is needed. if escapeAll is false, previously
+ * escaped strings are left as is
+ */
+static rsRetVal ATTR_NONNULL(1, 3)
+jsonAddVal(uchar *const pSrc, const unsigned buflen, es_str_t **dst, const int escapeAll)
+{
+ es_size_t i;
+ DEFiRet;
+
+ for(i = 0 ; i < buflen ; ++i) {
+ const uchar c = pSrc[i];
+ if(! ( (c >= 0x30 && c <= 0x5b)
+ || (c >= 0x23 && c <= 0x2e)
+ || (c >= 0x5d /* && c <= 0x10FFFF*/)
+ || c == 0x20 || c == 0x21)
+ ) {
+ iRet = jsonAddVal_escaped(pSrc, buflen, i, dst, escapeAll);
+ FINALIZE;
+ }
+ }
+ if(*dst != NULL) {
+ es_addBuf(dst, (const char *) pSrc, buflen);
+ }
+finalize_it:
+ RETiRet;
+}
+
+
+/* encode a property in JSON escaped format. This is a helper
+ * to MsgGetProp. It needs to update all provided parameters.
+ * For performance reasons, we begin to copy the string only
+ * when we recognice that we actually need to do some escaping.
+ * rgerhards, 2012-03-16
+ */
+static rsRetVal
+jsonEncode(uchar **ppRes, unsigned short *pbMustBeFreed, int *pBufLen, int escapeAll)
+{
+ unsigned buflen;
+ uchar *pSrc;
+ es_str_t *dst = NULL;
+ DEFiRet;
+
+ pSrc = *ppRes;
+ buflen = (*pBufLen == -1) ? (int) ustrlen(pSrc) : *pBufLen;
+ CHKiRet(jsonAddVal(pSrc, buflen, &dst, escapeAll));
+
+ if(dst != NULL) {
+ /* we updated the string and need to replace the
+ * previous data.
+ */
+ if(*pbMustBeFreed)
+ free(*ppRes);
+ *ppRes = (uchar*)es_str2cstr(dst, NULL);
+ *pbMustBeFreed = 1;
+ *pBufLen = -1;
+ es_deleteStr(dst);
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Format a property as JSON field, that means
+ * "name"="value"
+ * where value is JSON-escaped (here we assume that the name
+ * only contains characters from the valid character set).
+ * Note: this function duplicates code from jsonEncode().
+ * TODO: these two functions should be combined, at least if
+ * that makes any sense from a performance PoV - definitely
+ * something to consider at a later stage. rgerhards, 2012-04-19
+ */
+static rsRetVal ATTR_NONNULL()
+jsonField(const struct templateEntry *const pTpe,
+ uchar **const ppRes,
+ unsigned short *const pbMustBeFreed,
+ int *const pBufLen,
+ int escapeAll)
+{
+ unsigned buflen;
+ uchar *pSrc;
+ es_str_t *dst = NULL;
+ int is_numeric = 1;
+ DEFiRet;
+
+ pSrc = *ppRes;
+ buflen = (*pBufLen == -1) ? (int) ustrlen(pSrc) : *pBufLen;
+dbgprintf("jsonEncode: datatype: %u, onEmpty: %u val %*s\n", (unsigned) pTpe->data.field.options.dataType,
+(unsigned) pTpe->data.field.options.onEmpty, buflen, pSrc);
+ if(buflen == 0) {
+ if(pTpe->data.field.options.onEmpty == TPE_DATAEMPTY_SKIP) {
+ FINALIZE;
+ }
+ is_numeric = 0;
+ }
+ /* we hope we have only few escapes... */
+ dst = es_newStr(buflen+pTpe->lenFieldName+15);
+ es_addChar(&dst, '"');
+ es_addBuf(&dst, (char*)pTpe->fieldName, pTpe->lenFieldName);
+ es_addBufConstcstr(&dst, "\":");
+ if(buflen == 0 && pTpe->data.field.options.onEmpty == TPE_DATAEMPTY_NULL) {
+ es_addBufConstcstr(&dst, "null");
+ } else {
+ if(pTpe->data.field.options.dataType == TPE_DATATYPE_AUTO) {
+ for(unsigned i = 0 ; i < buflen ; ++i) {
+ if(pSrc[i] < '0' || pSrc[i] > '9') {
+ is_numeric = 0;
+ break;
+ }
+ }
+ if(!is_numeric) {
+ es_addChar(&dst, '"');
+ }
+ CHKiRet(jsonAddVal(pSrc, buflen, &dst, escapeAll));
+ if(!is_numeric) {
+ es_addChar(&dst, '"');
+ }
+ } else if(pTpe->data.field.options.dataType == TPE_DATATYPE_STRING) {
+ es_addChar(&dst, '"');
+ CHKiRet(jsonAddVal(pSrc, buflen, &dst, escapeAll));
+ es_addChar(&dst, '"');
+ } else if(pTpe->data.field.options.dataType == TPE_DATATYPE_NUMBER) {
+ if(buflen == 0) {
+ es_addChar(&dst, '0');
+ } else {
+ CHKiRet(jsonAddVal(pSrc, buflen, &dst, escapeAll));
+ }
+ } else if(pTpe->data.field.options.dataType == TPE_DATATYPE_BOOL) {
+ if(buflen == 1 && *pSrc == '0') {
+ es_addBufConstcstr(&dst, "false");
+ } else {
+ es_addBufConstcstr(&dst, "true");
+ }
+ }
+ }
+
+ if(*pbMustBeFreed)
+ free(*ppRes);
+ /* we know we do not have \0 chars - so the size does not change */
+ *pBufLen = es_strlen(dst);
+ *ppRes = (uchar*)es_str2cstr(dst, NULL);
+ *pbMustBeFreed = 1;
+ es_deleteStr(dst);
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* This function returns a string-representation of the
+ * requested message property. This is a generic function used
+ * to abstract properties so that these can be easier
+ * queried. Returns NULL if property could not be found.
+ * Actually, this function is a big if..elseif. What it does
+ * is simply to map property names (from MonitorWare) to the
+ * message object data fields.
+ *
+ * In case we need string forms of propertis we do not
+ * yet have in string form, we do a memory allocation that
+ * is sufficiently large (in all cases). Once the string
+ * form has been obtained, it is saved until the Msg object
+ * is finally destroyed. This is so that we save the processing
+ * time in the (likely) case that this property is requested
+ * again. It also saves us a lot of dynamic memory management
+ * issues in the upper layers, because we so can guarantee that
+ * the buffer will remain static AND available during the lifetime
+ * of the object. Please note that both the max size allocation as
+ * well as keeping things in memory might like look like a
+ * waste of memory (some might say it actually is...) - we
+ * deliberately accept this because performance is more important
+ * to us ;)
+ * rgerhards 2004-11-18
+ * Parameter "bMustBeFreed" is set by this function. It tells the
+ * caller whether or not the string returned must be freed by the
+ * caller itself. It is is 0, the caller MUST NOT free it. If it is
+ * 1, the caller MUST free it. Handling this wrongly leads to either
+ * a memory leak of a program abort (do to double-frees or frees on
+ * the constant memory pool). So be careful to do it right.
+ * rgerhards 2004-11-23
+ * regular expression support contributed by Andres Riancho merged
+ * on 2005-09-13
+ * changed so that it now an be called without a template entry (NULL).
+ * In this case, only the (unmodified) property is returned. This will
+ * be used in selector line processing.
+ * rgerhards 2005-09-15
+ */
+/* a quick helper to save some writing: */
+#define RET_OUT_OF_MEMORY { *pbMustBeFreed = 0;\
+ *pPropLen = sizeof("**OUT OF MEMORY**") - 1; \
+ return(UCHAR_CONSTANT("**OUT OF MEMORY**"));}
+uchar *MsgGetProp(smsg_t *__restrict__ const pMsg, struct templateEntry *__restrict__ const pTpe,
+ msgPropDescr_t *pProp, rs_size_t *__restrict__ const pPropLen,
+ unsigned short *__restrict__ const pbMustBeFreed, struct syslogTime * const ttNow)
+{
+ uchar *pRes; /* result pointer */
+ rs_size_t bufLen = -1; /* length of string or -1, if not known */
+ uchar *pBufStart;
+ uchar *pBuf;
+ int iLen;
+ short iOffs;
+ enum tplFormatTypes datefmt;
+ int bDateInUTC;
+
+ assert(pMsg != NULL);
+ assert(pbMustBeFreed != NULL);
+
+#ifdef FEATURE_REGEXP
+ /* Variables necessary for regular expression matching */
+ size_t nmatch = 10;
+ regmatch_t pmatch[10];
+#endif
+
+ *pbMustBeFreed = 0;
+
+ switch(pProp->id) {
+ case PROP_MSG:
+ pRes = getMSG(pMsg);
+ bufLen = getMSGLen(pMsg);
+ break;
+ case PROP_TIMESTAMP:
+ if(pTpe != NULL) {
+ datefmt = pTpe->data.field.eDateFormat;
+ bDateInUTC = pTpe->data.field.options.bDateInUTC;
+ } else {
+ datefmt = tplFmtDefault;
+ bDateInUTC = 0;
+ }
+ if(bDateInUTC) {
+ pRes = (uchar*)getTimeUTC(&pMsg->tTIMESTAMP, datefmt, pbMustBeFreed);
+ } else {
+ pRes = (uchar*)getTimeReported(pMsg, datefmt);
+ }
+ break;
+ case PROP_HOSTNAME:
+ pRes = (uchar*)getHOSTNAME(pMsg);
+ bufLen = getHOSTNAMELen(pMsg);
+ break;
+ case PROP_SYSLOGTAG:
+ getTAG(pMsg, &pRes, &bufLen, LOCK_MUTEX);
+ break;
+ case PROP_RAWMSG:
+ getRawMsg(pMsg, &pRes, &bufLen);
+ break;
+ case PROP_RAWMSG_AFTER_PRI:
+ getRawMsgAfterPRI(pMsg, &pRes, &bufLen);
+ break;
+ case PROP_INPUTNAME:
+ getInputName(pMsg, &pRes, &bufLen);
+ break;
+ case PROP_FROMHOST:
+ pRes = getRcvFrom(pMsg);
+ break;
+ case PROP_FROMHOST_IP:
+ pRes = getRcvFromIP(pMsg);
+ break;
+ case PROP_PRI:
+ pRes = (uchar*)getPRI(pMsg);
+ break;
+ case PROP_PRI_TEXT:
+ pRes = textpri(pMsg);
+ if(pRes == NULL)
+ RET_OUT_OF_MEMORY;
+ *pbMustBeFreed = 1;
+ break;
+ case PROP_IUT:
+ pRes = UCHAR_CONSTANT("1"); /* always 1 for syslog messages (a MonitorWare thing;)) */
+ bufLen = 1;
+ break;
+ case PROP_SYSLOGFACILITY:
+ pRes = (uchar*)getFacility(pMsg);
+ break;
+ case PROP_SYSLOGFACILITY_TEXT:
+ pRes = (uchar*)getFacilityStr(pMsg);
+ break;
+ case PROP_SYSLOGSEVERITY:
+ pRes = (uchar*)getSeverity(pMsg);
+ break;
+ case PROP_SYSLOGSEVERITY_TEXT:
+ pRes = (uchar*)getSeverityStr(pMsg);
+ break;
+ case PROP_TIMEGENERATED:
+ if(pTpe != NULL) {
+ datefmt = pTpe->data.field.eDateFormat;
+ bDateInUTC = pTpe->data.field.options.bDateInUTC;
+ } else {
+ datefmt = tplFmtDefault;
+ bDateInUTC = 0;
+ }
+ if(bDateInUTC) {
+ pRes = (uchar*)getTimeUTC(&pMsg->tRcvdAt, datefmt, pbMustBeFreed);
+ } else {
+ pRes = (uchar*)getTimeGenerated(pMsg, datefmt);
+ }
+ break;
+ case PROP_PROGRAMNAME:
+ pRes = getProgramName(pMsg, LOCK_MUTEX);
+ break;
+ case PROP_PROTOCOL_VERSION:
+ pRes = (uchar*)getProtocolVersionString(pMsg);
+ break;
+ case PROP_STRUCTURED_DATA:
+ MsgGetStructuredData(pMsg, &pRes, &bufLen);
+ break;
+ case PROP_APP_NAME:
+ pRes = (uchar*)getAPPNAME(pMsg, LOCK_MUTEX);
+ break;
+ case PROP_PROCID:
+ pRes = (uchar*)getPROCID(pMsg, LOCK_MUTEX);
+ break;
+ case PROP_MSGID:
+ pRes = (uchar*)getMSGID(pMsg);
+ break;
+ case PROP_JSONMESG:
+ pRes = (uchar*)msgGetJSONMESG(pMsg);
+ *pbMustBeFreed = 1;
+ break;
+#ifdef USE_LIBUUID
+ case PROP_UUID:
+ getUUID(pMsg, &pRes, &bufLen);
+ break;
+#endif
+ case PROP_PARSESUCCESS:
+ pRes = (uchar*)getParseSuccess(pMsg);
+ break;
+ case PROP_SYS_NOW:
+ if((pRes = getNOW(NOW_NOW, ttNow, TIME_IN_LOCALTIME)) == NULL) {
+ RET_OUT_OF_MEMORY;
+ } else {
+ *pbMustBeFreed = 1;
+ bufLen = 10;
+ }
+ break;
+ case PROP_SYS_YEAR:
+ if((pRes = getNOW(NOW_YEAR, ttNow, TIME_IN_LOCALTIME)) == NULL) {
+ RET_OUT_OF_MEMORY;
+ } else {
+ *pbMustBeFreed = 1;
+ bufLen = 4;
+ }
+ break;
+ case PROP_SYS_MONTH:
+ if((pRes = getNOW(NOW_MONTH, ttNow, TIME_IN_LOCALTIME)) == NULL) {
+ RET_OUT_OF_MEMORY;
+ } else {
+ *pbMustBeFreed = 1;
+ bufLen = 2;
+ }
+ break;
+ case PROP_SYS_DAY:
+ if((pRes = getNOW(NOW_DAY, ttNow, TIME_IN_LOCALTIME)) == NULL) {
+ RET_OUT_OF_MEMORY;
+ } else {
+ *pbMustBeFreed = 1;
+ bufLen = 2;
+ }
+ break;
+ case PROP_SYS_HOUR:
+ if((pRes = getNOW(NOW_HOUR, ttNow, TIME_IN_LOCALTIME)) == NULL) {
+ RET_OUT_OF_MEMORY;
+ } else {
+ *pbMustBeFreed = 1;
+ bufLen = 2;
+ }
+ break;
+ case PROP_SYS_HHOUR:
+ if((pRes = getNOW(NOW_HHOUR, ttNow, TIME_IN_LOCALTIME)) == NULL) {
+ RET_OUT_OF_MEMORY;
+ } else {
+ *pbMustBeFreed = 1;
+ bufLen = 2;
+ }
+ break;
+ case PROP_SYS_QHOUR:
+ if((pRes = getNOW(NOW_QHOUR, ttNow, TIME_IN_LOCALTIME)) == NULL) {
+ RET_OUT_OF_MEMORY;
+ } else {
+ *pbMustBeFreed = 1;
+ bufLen = 2;
+ }
+ break;
+ case PROP_SYS_MINUTE:
+ if((pRes = getNOW(NOW_MINUTE, ttNow, TIME_IN_LOCALTIME)) == NULL) {
+ RET_OUT_OF_MEMORY;
+ } else {
+ *pbMustBeFreed = 1;
+ bufLen = 2;
+ }
+ break;
+ case PROP_SYS_NOW_UTC:
+ if((pRes = getNOW(NOW_NOW, ttNow, TIME_IN_UTC)) == NULL) {
+ RET_OUT_OF_MEMORY;
+ } else {
+ *pbMustBeFreed = 1;
+ bufLen = 10;
+ }
+ break;
+ case PROP_SYS_YEAR_UTC:
+ if((pRes = getNOW(NOW_YEAR, ttNow, TIME_IN_UTC)) == NULL) {
+ RET_OUT_OF_MEMORY;
+ } else {
+ *pbMustBeFreed = 1;
+ bufLen = 4;
+ }
+ break;
+ case PROP_SYS_MONTH_UTC:
+ if((pRes = getNOW(NOW_MONTH, ttNow, TIME_IN_UTC)) == NULL) {
+ RET_OUT_OF_MEMORY;
+ } else {
+ *pbMustBeFreed = 1;
+ bufLen = 2;
+ }
+ break;
+ case PROP_SYS_DAY_UTC:
+ if((pRes = getNOW(NOW_DAY, ttNow, TIME_IN_UTC)) == NULL) {
+ RET_OUT_OF_MEMORY;
+ } else {
+ *pbMustBeFreed = 1;
+ bufLen = 2;
+ }
+ break;
+ case PROP_SYS_HOUR_UTC:
+ if((pRes = getNOW(NOW_HOUR, ttNow, TIME_IN_UTC)) == NULL) {
+ RET_OUT_OF_MEMORY;
+ } else {
+ *pbMustBeFreed = 1;
+ bufLen = 2;
+ }
+ break;
+ case PROP_SYS_HHOUR_UTC:
+ if((pRes = getNOW(NOW_HHOUR, ttNow, TIME_IN_UTC)) == NULL) {
+ RET_OUT_OF_MEMORY;
+ } else {
+ *pbMustBeFreed = 1;
+ bufLen = 2;
+ }
+ break;
+ case PROP_SYS_QHOUR_UTC:
+ if((pRes = getNOW(NOW_QHOUR, ttNow, TIME_IN_UTC)) == NULL) {
+ RET_OUT_OF_MEMORY;
+ } else {
+ *pbMustBeFreed = 1;
+ bufLen = 2;
+ }
+ break;
+ case PROP_SYS_MINUTE_UTC:
+ if((pRes = getNOW(NOW_MINUTE, ttNow, TIME_IN_UTC)) == NULL) {
+ RET_OUT_OF_MEMORY;
+ } else {
+ *pbMustBeFreed = 1;
+ bufLen = 2;
+ }
+ break;
+ case PROP_SYS_WDAY:
+ if((pRes = getNOW(NOW_WDAY, ttNow, TIME_IN_LOCALTIME)) == NULL) {
+ RET_OUT_OF_MEMORY;
+ } else {
+ *pbMustBeFreed = 1;
+ bufLen = 1;
+ }
+ break;
+ case PROP_SYS_WDAY_UTC:
+ if((pRes = getNOW(NOW_WDAY, ttNow, TIME_IN_UTC)) == NULL) {
+ RET_OUT_OF_MEMORY;
+ } else {
+ *pbMustBeFreed = 1;
+ bufLen = 1;
+ }
+ break;
+ case PROP_SYS_NOW_UXTIMESTAMP:
+ if((pRes = malloc(16)) == NULL) {
+ RET_OUT_OF_MEMORY;
+ } else {
+ snprintf((char*) pRes, 16-1, "%lld", (long long) getTime(NULL));
+ pRes[16-1] = '\0';
+ *pbMustBeFreed = 1;
+ bufLen = -1;
+ }
+ break;
+ case PROP_SYS_MYHOSTNAME:
+ pRes = glbl.GetLocalHostName();
+ break;
+ case PROP_CEE_ALL_JSON:
+ case PROP_CEE_ALL_JSON_PLAIN:
+ if(pMsg->json == NULL) {
+ pRes = (uchar*) "{}";
+ bufLen = 2;
+ *pbMustBeFreed = 0;
+ } else {
+ const char *jstr;
+ MsgLock(pMsg);
+ int jflag = 0;
+ if(pProp->id == PROP_CEE_ALL_JSON) {
+ jflag = JSON_C_TO_STRING_SPACED;
+ } else if(pProp->id == PROP_CEE_ALL_JSON_PLAIN) {
+ jflag = JSON_C_TO_STRING_PLAIN;
+ }
+ jstr = json_object_to_json_string_ext(pMsg->json, jflag);
+ MsgUnlock(pMsg);
+ if(jstr == NULL) {
+ RET_OUT_OF_MEMORY;
+ }
+ pRes = (uchar*)strdup(jstr);
+ if(pRes == NULL) {
+ RET_OUT_OF_MEMORY;
+ }
+ *pbMustBeFreed = 1;
+ }
+ break;
+ case PROP_CEE:
+ case PROP_LOCAL_VAR:
+ case PROP_GLOBAL_VAR:
+ getJSONPropVal(pMsg, pProp, &pRes, &bufLen, pbMustBeFreed);
+ break;
+ case PROP_SYS_BOM:
+ pRes = (uchar*) "\xEF\xBB\xBF";
+ *pbMustBeFreed = 0;
+ break;
+ case PROP_SYS_UPTIME:
+# ifndef HAVE_SYSINFO_UPTIME
+ /* An alternative on some systems (eg Solaris) is to scan
+ * /var/adm/utmpx for last boot time.
+ */
+ pRes = (uchar*) "UPTIME NOT available on this system";
+ *pbMustBeFreed = 0;
+
+# elif defined(__FreeBSD__)
+
+ {
+ struct timespec tp;
+
+ if((pRes = (uchar*) malloc(32)) == NULL) {
+ RET_OUT_OF_MEMORY;
+ }
+
+ if(clock_gettime(CLOCK_UPTIME, &tp) == -1) {
+ free(pRes);
+ *pPropLen = sizeof("**SYSCALL FAILED**") - 1;
+ return(UCHAR_CONSTANT("**SYSCALL FAILED**"));
+ }
+
+ *pbMustBeFreed = 1;
+
+ snprintf((char*) pRes, 32, "%ld", tp.tv_sec);
+ }
+
+# else
+
+ {
+ struct sysinfo s_info;
+
+ if((pRes = (uchar*) malloc(32)) == NULL) {
+ RET_OUT_OF_MEMORY;
+ }
+
+ if(sysinfo(&s_info) < 0) {
+ free(pRes);
+ *pPropLen = sizeof("**SYSCALL FAILED**") - 1;
+ return(UCHAR_CONSTANT("**SYSCALL FAILED**"));
+ }
+
+ *pbMustBeFreed = 1;
+
+ snprintf((char*) pRes, 32, "%ld", s_info.uptime);
+ }
+# endif
+ break;
+ default:
+ /* there is no point in continuing, we may even otherwise render the
+ * error message unreadable. rgerhards, 2007-07-10
+ */
+ dbgprintf("invalid property id: '%d'\n", pProp->id);
+ *pbMustBeFreed = 0;
+ *pPropLen = sizeof("**INVALID PROPERTY NAME**") - 1;
+ return UCHAR_CONSTANT("**INVALID PROPERTY NAME**");
+ }
+
+ /* If we did not receive a template pointer, we are already done... */
+ if(pTpe == NULL || !pTpe->bComplexProcessing) {
+ *pPropLen = (bufLen == -1) ? (int) ustrlen(pRes) : bufLen;
+ return pRes;
+ }
+
+ /* Now check if we need to make "temporary" transformations (these
+ * are transformations that do not go back into the message -
+ * memory must be allocated for them!).
+ */
+
+ /* substring extraction */
+ /* first we check if we need to extract by field number
+ * rgerhards, 2005-12-22
+ */
+ if(pTpe->data.field.has_fields == 1) {
+ size_t iCurrFld;
+ uchar *pFld;
+ uchar *pFldEnd;
+ /* first, skip to the field in question. The field separator
+ * is always one character and is stored in the template entry.
+ */
+ iCurrFld = 1;
+ pFld = pRes;
+ while(*pFld && iCurrFld < pTpe->data.field.iFieldNr) {
+ /* skip fields until the requested field or end of string is found */
+ while(*pFld && (uchar) *pFld != pTpe->data.field.field_delim)
+ ++pFld; /* skip to field terminator */
+ if(*pFld == pTpe->data.field.field_delim) {
+ ++pFld; /* eat it */
+#ifdef STRICT_GPLV3
+ if (pTpe->data.field.field_expand != 0) {
+ while (*pFld == pTpe->data.field.field_delim) {
+ ++pFld;
+ }
+ }
+#endif
+ ++iCurrFld;
+ }
+ }
+ dbgprintf("field requested %d, field found %d\n", pTpe->data.field.iFieldNr, (int) iCurrFld);
+
+ if(iCurrFld == pTpe->data.field.iFieldNr) {
+ /* field found, now extract it */
+ /* first of all, we need to find the end */
+ pFldEnd = pFld;
+ while(*pFldEnd && *pFldEnd != pTpe->data.field.field_delim)
+ ++pFldEnd;
+ --pFldEnd; /* we are already at the delimiter - so we need to
+ * step back a little not to copy it as part of the field. */
+ /* we got our end pointer, now do the copy */
+ /* TODO: code copied from below, this is a candidate for a separate function */
+ iLen = pFldEnd - pFld + 1; /* the +1 is for an actual char, NOT \0! */
+ pBufStart = pBuf = malloc(iLen + 1);
+ if(pBuf == NULL) {
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ RET_OUT_OF_MEMORY;
+ }
+ /* now copy */
+ memcpy(pBuf, pFld, iLen);
+ bufLen = iLen;
+ pBuf[iLen] = '\0'; /* terminate it */
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ pRes = pBufStart;
+ *pbMustBeFreed = 1;
+ } else {
+ /* field not found, return error */
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ *pbMustBeFreed = 0;
+ *pPropLen = sizeof("**FIELD NOT FOUND**") - 1;
+ return UCHAR_CONSTANT("**FIELD NOT FOUND**");
+ }
+#ifdef FEATURE_REGEXP
+ } else {
+ /* Check for regular expressions */
+ if (pTpe->data.field.has_regex != 0) {
+ if (pTpe->data.field.has_regex == 2) {
+ /* Could not compile regex before! */
+ if (*pbMustBeFreed == 1) {
+ free(pRes);
+ *pbMustBeFreed = 0;
+ }
+ *pPropLen = sizeof("**NO MATCH** **BAD REGULAR EXPRESSION**") - 1;
+ return UCHAR_CONSTANT("**NO MATCH** **BAD REGULAR EXPRESSION**");
+ }
+
+ dbgprintf("string to match for regex is: %s\n", pRes);
+
+ if(objUse(regexp, LM_REGEXP_FILENAME) == RS_RET_OK) {
+ short iTry = 0;
+ uchar bFound = 0;
+ iOffs = 0;
+ /* first see if we find a match, iterating through the series of
+ * potential matches over the string.
+ */
+ while(!bFound) {
+ int iREstat;
+ iREstat = regexp.regexec(&pTpe->data.field.re, (char*)(pRes + iOffs),
+ nmatch, pmatch, 0);
+ dbgprintf("regexec return is %d\n", iREstat);
+ if(iREstat == 0) {
+ if(pmatch[0].rm_so == -1) {
+ dbgprintf("oops ... start offset of successful "
+ "regexec is -1\n");
+ break;
+ }
+ if(iTry == pTpe->data.field.iMatchToUse) {
+ bFound = 1;
+ } else {
+ dbgprintf("regex found at offset %d, new offset %d, "
+ "tries %d\n", iOffs,
+ (int) (iOffs + pmatch[0].rm_eo), iTry);
+ iOffs += pmatch[0].rm_eo;
+ ++iTry;
+ }
+ } else {
+ break;
+ }
+ }
+ dbgprintf("regex: end search, found %d\n", bFound);
+ if(!bFound) {
+ /* we got no match! */
+ if(pTpe->data.field.nomatchAction != TPL_REGEX_NOMATCH_USE_WHOLE_FIELD) {
+ if (*pbMustBeFreed == 1) {
+ free(pRes);
+ *pbMustBeFreed = 0;
+ }
+ if(pTpe->data.field.nomatchAction == TPL_REGEX_NOMATCH_USE_DFLTSTR) {
+ bufLen = sizeof("**NO MATCH**") - 1;
+ pRes = UCHAR_CONSTANT("**NO MATCH**");
+ } else if(pTpe->data.field.nomatchAction ==
+ TPL_REGEX_NOMATCH_USE_ZERO) {
+ bufLen = 1;
+ pRes = UCHAR_CONSTANT("0");
+ } else {
+ bufLen = 0;
+ pRes = UCHAR_CONSTANT("");
+ }
+ }
+ } else {
+ /* Match- but did it match the one we wanted? */
+ /* we got no match! */
+ if(pmatch[pTpe->data.field.iSubMatchToUse].rm_so == -1) {
+ if(pTpe->data.field.nomatchAction !=
+ TPL_REGEX_NOMATCH_USE_WHOLE_FIELD) {
+ if (*pbMustBeFreed == 1) {
+ free(pRes);
+ *pbMustBeFreed = 0;
+ }
+ if(pTpe->data.field.nomatchAction ==
+ TPL_REGEX_NOMATCH_USE_DFLTSTR) {
+ bufLen = sizeof("**NO MATCH**") - 1;
+ pRes = UCHAR_CONSTANT("**NO MATCH**");
+ } else if(pTpe->data.field.nomatchAction ==
+ TPL_REGEX_NOMATCH_USE_ZERO) {
+ bufLen = 1;
+ pRes = UCHAR_CONSTANT("0");
+ } else {
+ bufLen = 0;
+ pRes = UCHAR_CONSTANT("");
+ }
+ }
+ }
+ /* OK, we have a usable match - we now need to malloc pB */
+ int iLenBuf;
+ uchar *pB;
+
+ iLenBuf = pmatch[pTpe->data.field.iSubMatchToUse].rm_eo
+ - pmatch[pTpe->data.field.iSubMatchToUse].rm_so;
+ pB = malloc(iLenBuf + 1);
+
+ if (pB == NULL) {
+ if (*pbMustBeFreed == 1)
+ free(pRes);
+ RET_OUT_OF_MEMORY;
+ }
+
+ /* Lets copy the matched substring to the buffer */
+ memcpy(pB, pRes + iOffs + pmatch[pTpe->data.field.iSubMatchToUse].rm_so,
+ iLenBuf);
+ bufLen = iLenBuf;
+ pB[iLenBuf] = '\0';/* terminate string, did not happen before */
+
+ if (*pbMustBeFreed == 1)
+ free(pRes);
+ pRes = pB;
+ *pbMustBeFreed = 1;
+ }
+ } else {
+ /* we could not load regular expression support. This is quite unexpected at
+ * this stage of processing (after all, the config parser found it), but so
+ * it is. We return an error in that case. -- rgerhards, 2008-03-07
+ */
+ dbgprintf("could not get regexp object pointer, so regexp can not be evaluated\n");
+ if (*pbMustBeFreed == 1) {
+ free(pRes);
+ *pbMustBeFreed = 0;
+ }
+ *pPropLen = sizeof("***REGEXP NOT AVAILABLE***") - 1;
+ return UCHAR_CONSTANT("***REGEXP NOT AVAILABLE***");
+ }
+ }
+#endif /* #ifdef FEATURE_REGEXP */
+ }
+
+ if(pTpe->data.field.iFromPos != 0 || pTpe->data.field.iToPos != 0) {
+ /* we need to obtain a private copy */
+ int iFrom, iTo;
+ uchar *pSb;
+ iFrom = pTpe->data.field.iFromPos;
+ iTo = pTpe->data.field.iToPos;
+ if(bufLen == -1)
+ bufLen = ustrlen(pRes);
+ if(pTpe->data.field.options.bFromPosEndRelative) {
+ iFrom = (bufLen < iFrom) ? 0 : bufLen - iFrom;
+ iTo = (bufLen < iTo)? 0 : bufLen - iTo;
+ } else {
+ /* need to zero-base to and from (they are 1-based!) */
+ if(iFrom > 0)
+ --iFrom;
+ if(iTo > 0) {
+ --iTo;
+ } else if(iTo < 0) {
+ /* note: we ADD negative value, 0-based (-1)! */
+ iTo = bufLen - 1 + iTo;
+ if(iTo < 0) {
+ iTo = 0;
+ }
+ }
+ }
+ if(iFrom >= bufLen) {
+ DBGPRINTF("msgGetProp: iFrom %d >= buflen %d, returning empty string\n",
+ iFrom, bufLen);
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ pRes = (uchar*) "";
+ *pbMustBeFreed = 0;
+ bufLen = 0;
+ } else if(iFrom == 0 && iTo >= bufLen && pTpe->data.field.options.bFixedWidth == 0) {
+ /* in this case, the requested string is a superset of what we already have,
+ * so there is no need to do any processing. This is a frequent case for size-limited
+ * fields like TAG in the default forwarding template (so it is a useful optimization
+ * to check for this condition ;)). -- rgerhards, 2009-07-09
+ */
+ ; /*DO NOTHING*/
+ } else {
+ if(iTo >= bufLen) /* iTo is very large, if no to-position is set in the template! */
+ if (pTpe->data.field.options.bFixedWidth == 0)
+ iTo = bufLen - 1;
+
+ iLen = iTo - iFrom + 1; /* the +1 is for an actual char, NOT \0! */
+ pBufStart = pBuf = malloc(iLen + 1);
+ if(pBuf == NULL) {
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ RET_OUT_OF_MEMORY;
+ }
+ pSb = pRes;
+ if(iFrom) {
+ /* skip to the start of the substring (can't do pointer arithmetic
+ * because the whole string might be smaller!!)
+ */
+ while(*pSb && iFrom) {
+ --iFrom;
+ ++pSb;
+ }
+ }
+ /* OK, we are at the begin - now let's copy... */
+ bufLen = iLen;
+ while(iLen) {
+ if (*pSb) {
+ *pBuf++ = *pSb;
+ ++pSb;
+ } else {
+ *pBuf++ = ' ';
+ }
+ --iLen;
+ }
+ *pBuf = '\0';
+ bufLen -= iLen; /* subtract remaining length if the string was smaller! */
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ pRes = pBufStart;
+ *pbMustBeFreed = 1;
+ }
+ }
+
+ /* now check if we need to do our "SP if first char is non-space" hack logic */
+ if(*pRes && pTpe->data.field.options.bSPIffNo1stSP) {
+ /* here, we always destruct the buffer and return a new one */
+ uchar cFirst = *pRes; /* save first char */
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ pRes = (cFirst == ' ') ? UCHAR_CONSTANT("") : UCHAR_CONSTANT(" ");
+ bufLen = (cFirst == ' ') ? 0 : 1;
+ *pbMustBeFreed = 0;
+ }
+
+ if(*pRes) {
+ /* case conversations (should go after substring, because so we are able to
+ * work on the smallest possible buffer).
+ */
+ if(pTpe->data.field.eCaseConv != tplCaseConvNo) {
+ /* we need to obtain a private copy */
+ if(bufLen == -1)
+ bufLen = ustrlen(pRes);
+ uchar *pBStart;
+ uchar *pB;
+ uchar *pSrc;
+ pBStart = pB = malloc(bufLen + 1);
+ if(pB == NULL) {
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ RET_OUT_OF_MEMORY;
+ }
+ pSrc = pRes;
+ while(*pSrc) {
+ *pB++ = (pTpe->data.field.eCaseConv == tplCaseConvUpper) ?
+ (uchar)toupper((int)*pSrc) : (uchar)tolower((int)*pSrc);
+ /* currently only these two exist */
+ ++pSrc;
+ }
+ *pB = '\0';
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ pRes = pBStart;
+ *pbMustBeFreed = 1;
+ }
+
+ /* now do control character dropping/escaping/replacement
+ * Only one of these can be used. If multiple options are given, the
+ * result is random (though currently there obviously is an order of
+ * preferrence, see code below. But this is NOT guaranteed.
+ * RGerhards, 2006-11-17
+ * We must copy the strings if we modify them, because they may either
+ * point to static memory or may point into the message object, in which
+ * case we would actually modify the original property (which of course
+ * is wrong).
+ * This was found and fixed by varmojefkoj on 2007-09-11
+ */
+ if(pTpe->data.field.options.bDropCC) {
+ int iLenBuf = 0;
+ uchar *pSrc = pRes;
+ uchar *pDstStart;
+ uchar *pDst;
+ uchar bDropped = 0;
+
+ while(*pSrc) {
+ if(!iscntrl((int) *pSrc++))
+ iLenBuf++;
+ else
+ bDropped = 1;
+ }
+
+ if(bDropped) {
+ pDst = pDstStart = malloc(iLenBuf + 1);
+ if(pDst == NULL) {
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ RET_OUT_OF_MEMORY;
+ }
+ for(pSrc = pRes; *pSrc; pSrc++) {
+ if(!iscntrl((int) *pSrc))
+ *pDst++ = *pSrc;
+ }
+ *pDst = '\0';
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ pRes = pDstStart;
+ bufLen = iLenBuf;
+ *pbMustBeFreed = 1;
+ }
+ } else if(pTpe->data.field.options.bSpaceCC) {
+ uchar *pSrc;
+ uchar *pDstStart;
+ uchar *pDst;
+
+ if(*pbMustBeFreed == 1) {
+ /* in this case, we already work on dynamic
+ * memory, so there is no need to copy it - we can
+ * modify it in-place without any harm. This is a
+ * performance optiomization.
+ */
+ for(pDst = pRes; *pDst; pDst++) {
+ if(iscntrl((int) *pDst))
+ *pDst = ' ';
+ }
+ } else {
+ if(bufLen == -1)
+ bufLen = ustrlen(pRes);
+ pDst = pDstStart = malloc(bufLen + 1);
+ if(pDst == NULL) {
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ RET_OUT_OF_MEMORY;
+ }
+ for(pSrc = pRes; *pSrc; pSrc++) {
+ if(iscntrl((int) *pSrc))
+ *pDst++ = ' ';
+ else
+ *pDst++ = *pSrc;
+ }
+ *pDst = '\0';
+ pRes = pDstStart;
+ *pbMustBeFreed = 1;
+ }
+ } else if(pTpe->data.field.options.bEscapeCC) {
+ /* we must first count how many control charactes are
+ * present, because we need this to compute the new string
+ * buffer length. While doing so, we also compute the string
+ * length.
+ */
+ int iNumCC = 0;
+ int iLenBuf = 0;
+ uchar *pSrc;
+ uchar *pB;
+
+ for(pB = pRes ; *pB ; ++pB) {
+ ++iLenBuf;
+ if(iscntrl((int) *pB))
+ ++iNumCC;
+ }
+
+ if(iNumCC > 0) { /* if 0, there is nothing to escape, so we are done */
+ /* OK, let's do the escaping... */
+ uchar *pBStart;
+ uchar szCCEsc[8]; /* buffer for escape sequence */
+ int i;
+
+ iLenBuf += iNumCC * 4;
+ pBStart = pB = malloc(iLenBuf + 1);
+ if(pB == NULL) {
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ RET_OUT_OF_MEMORY;
+ }
+ for(pSrc = pRes; *pSrc; pSrc++) {
+ if(iscntrl((int) *pSrc)) {
+ snprintf((char*)szCCEsc, sizeof(szCCEsc), "#%3.3d", *pSrc);
+ for(i = 0 ; i < 4 ; ++i)
+ *pB++ = szCCEsc[i];
+ } else {
+ *pB++ = *pSrc;
+ }
+ }
+ *pB = '\0';
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ pRes = pBStart;
+ bufLen = -1;
+ *pbMustBeFreed = 1;
+ }
+ }
+ }
+
+ /* Take care of spurious characters to make the property safe
+ * for a path definition
+ */
+ if(pTpe->data.field.options.bSecPathDrop || pTpe->data.field.options.bSecPathReplace) {
+ if(pTpe->data.field.options.bSecPathDrop) {
+ int iLenBuf = 0;
+ uchar *pSrc = pRes;
+ uchar *pDstStart;
+ uchar *pDst;
+ uchar bDropped = 0;
+
+ while(*pSrc) {
+ if(*pSrc++ != '/')
+ iLenBuf++;
+ else
+ bDropped = 1;
+ }
+
+ if(bDropped) {
+ pDst = pDstStart = malloc(iLenBuf + 1);
+ if(pDst == NULL) {
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ RET_OUT_OF_MEMORY;
+ }
+ for(pSrc = pRes; *pSrc; pSrc++) {
+ if(*pSrc != '/')
+ *pDst++ = *pSrc;
+ }
+ *pDst = '\0';
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ pRes = pDstStart;
+ bufLen = -1; /* TODO: can we do better? */
+ *pbMustBeFreed = 1;
+ }
+ } else {
+ uchar *pSrc;
+ uchar *pDstStart;
+ uchar *pDst;
+
+ if(*pbMustBeFreed == 1) {
+ /* here, again, we can modify the string as we already obtained
+ * a private buffer. As we do not change the size of that buffer,
+ * in-place modification is possible. This is a performance
+ * enhancement.
+ */
+ for(pDst = pRes; *pDst; pDst++) {
+ if(*pDst == '/')
+ *pDst++ = '_';
+ }
+ } else {
+ if(bufLen == -1)
+ bufLen = ustrlen(pRes);
+ pDst = pDstStart = malloc(bufLen + 1);
+ if(pDst == NULL) {
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ RET_OUT_OF_MEMORY;
+ }
+ for(pSrc = pRes; *pSrc; pSrc++) {
+ if(*pSrc == '/')
+ *pDst++ = '_';
+ else
+ *pDst++ = *pSrc;
+ }
+ *pDst = '\0';
+ /* we must NOT check if it needs to be freed, because we have done
+ * this in the if above. So if we come to hear, the pSrc string needs
+ * not to be freed (and we do not need to care about it).
+ */
+ pRes = pDstStart;
+ *pbMustBeFreed = 1;
+ }
+ }
+
+ /* check for "." and ".." (note the parenthesis in the if condition!) */
+ if(*pRes == '\0') {
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ pRes = UCHAR_CONSTANT("_");
+ bufLen = 1;
+ *pbMustBeFreed = 0;
+ } else if((*pRes == '.') && (*(pRes + 1) == '\0' || (*(pRes + 1) == '.' && *(pRes + 2) == '\0'))) {
+ uchar *pTmp = pRes;
+
+ if(*(pRes + 1) == '\0')
+ pRes = UCHAR_CONSTANT("_");
+ else
+ pRes = UCHAR_CONSTANT("_.");;
+ if(*pbMustBeFreed == 1)
+ free(pTmp);
+ *pbMustBeFreed = 0;
+ }
+ }
+
+ /* Now drop last LF if present (pls note that this must not be done
+ * if bEscapeCC was set)!
+ */
+ if(pTpe->data.field.options.bDropLastLF && !pTpe->data.field.options.bEscapeCC) {
+ int iLn;
+ uchar *pB;
+ if(bufLen == -1)
+ bufLen = ustrlen(pRes);
+ iLn = bufLen;
+ if(iLn > 0 && *(pRes + iLn - 1) == '\n') {
+ /* we have a LF! */
+ /* check if we need to obtain a private copy */
+ if(*pbMustBeFreed == 0) {
+ /* ok, original copy, need a private one */
+ pB = malloc(iLn + 1);
+ if(pB == NULL) {
+ RET_OUT_OF_MEMORY;
+ }
+ memcpy(pB, pRes, iLn - 1);
+ pRes = pB;
+ *pbMustBeFreed = 1;
+ }
+ *(pRes + iLn - 1) = '\0'; /* drop LF ;) */
+ --bufLen;
+ }
+ }
+
+ /* Now everything is squased as much as possible and more or less ready to
+ * go. This is the perfect place to compress any remaining spaces, if so
+ * instructed by the user/config.
+ */
+ if(pTpe->data.field.options.bCompressSP) {
+ int needCompress = 0;
+ int hadSP = 0;
+ uchar *pB;
+ if(*pbMustBeFreed == 0) {
+ for(pB = pRes ; *pB && needCompress == 0 ; ++pB) {
+ if(*pB == ' ') {
+ if(hadSP) {
+ uchar *const tmp = ustrdup(pRes);
+ if(tmp == NULL)
+ /* better not compress than
+ * loose message. */
+ break;
+ *pbMustBeFreed = 1;
+ pRes = tmp;
+ needCompress = 1;
+ } else {
+ hadSP = 1;
+ }
+ }
+ }
+ } else {
+ /* If we can modify the buffer in any case, we
+ * do NOT check if we actually need to compress,
+ * but "just do it" - that's the quickest way
+ * to get it done.
+ */
+ needCompress = 1;
+ }
+ if(needCompress) {
+ hadSP = 0;
+ uchar *pDst = pRes;
+ int needCopy = 0;
+ for(pB = pRes ; *pB ; ++pB) {
+ if(*pB == ' ') {
+ if(hadSP) {
+ needCopy = 1;
+ } else {
+ hadSP = 1;
+ if(needCopy)
+ *pDst = *pB;
+ ++pDst;
+ }
+ } else {
+ hadSP = 0;
+ if(needCopy)
+ *pDst = *pB;
+ ++pDst;
+ }
+ }
+ *pDst = '\0';
+ bufLen = pDst - pRes;
+ }
+ }
+
+ /* finally, we need to check if the property should be formatted in CSV or JSON.
+ * For CSV we use RFC 4180, and always use double quotes. As of this writing,
+ * this should be the last action carried out on the property, but in the
+ * future there may be reasons to change that. -- rgerhards, 2009-04-02
+ */
+ if(pTpe->data.field.options.bCSV) {
+ /* we need to obtain a private copy, as we need to at least add the double quotes */
+ int iBufLen;
+ uchar *pBStart;
+ uchar *pDst;
+ uchar *pSrc;
+ if(bufLen == -1)
+ bufLen = ustrlen(pRes);
+ iBufLen = bufLen;
+ /* the malloc may be optimized, we currently use the worst case... */
+ pBStart = pDst = malloc(2 * iBufLen + 3);
+ if(pDst == NULL) {
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ RET_OUT_OF_MEMORY;
+ }
+ pSrc = pRes;
+ *pDst++ = '"'; /* starting quote */
+ while(*pSrc) {
+ if(*pSrc == '"')
+ *pDst++ = '"'; /* need to add double double quote (see RFC4180) */
+ *pDst++ = *pSrc++;
+ }
+ *pDst++ = '"'; /* ending quote */
+ *pDst = '\0';
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ pRes = pBStart;
+ bufLen = -1;
+ *pbMustBeFreed = 1;
+ } else if(pTpe->data.field.options.bJSON) {
+ jsonEncode(&pRes, pbMustBeFreed, &bufLen, RSTRUE);
+ } else if(pTpe->data.field.options.bJSONf) {
+ jsonField(pTpe, &pRes, pbMustBeFreed, &bufLen, RSTRUE);
+ } else if(pTpe->data.field.options.bJSONr) {
+ jsonEncode(&pRes, pbMustBeFreed, &bufLen, RSFALSE);
+ } else if(pTpe->data.field.options.bJSONfr) {
+ jsonField(pTpe, &pRes, pbMustBeFreed, &bufLen, RSFALSE);
+ }
+
+ *pPropLen = (bufLen == -1) ? (int) ustrlen(pRes) : bufLen;
+
+ return(pRes);
+}
+
+/* Set a single property based on the JSON object provided. The
+ * property name is extracted from the JSON object.
+ */
+static rsRetVal
+msgSetPropViaJSON(smsg_t *__restrict__ const pMsg, const char *name, struct json_object *json, int sharedReference)
+{
+ const char *psz;
+ int val;
+ prop_t *propFromHost = NULL;
+ prop_t *propRcvFromIP = NULL;
+ int bNeedFree = 1;
+ DEFiRet;
+
+ /* note: json_object_get_string() manages the memory of the returned
+ * string. So we MUST NOT free it!
+ */
+ dbgprintf("DDDD: msgSetPropViaJSON key: '%s'\n", name);
+ if(!strcmp(name, "rawmsg")) {
+ psz = json_object_get_string(json);
+ MsgSetRawMsg(pMsg, psz, strlen(psz));
+ } else if(!strcmp(name, "msg")) {
+ psz = json_object_get_string(json);
+ MsgReplaceMSG(pMsg, (const uchar*)psz, strlen(psz));
+ } else if(!strcmp(name, "syslogtag")) {
+ psz = json_object_get_string(json);
+ MsgSetTAG(pMsg, (const uchar*)psz, strlen(psz));
+ } else if(!strcmp(name, "pri")) {
+ val = json_object_get_int(json);
+ msgSetPRI(pMsg, val);
+ } else if(!strcmp(name, "syslogfacility")) {
+ val = json_object_get_int(json);
+ if(val >= 0 && val <= 24)
+ pMsg->iFacility = val;
+ else
+ DBGPRINTF("mmexternal: invalid fac %d requested -- ignored\n", val);
+ } else if(!strcmp(name, "syslogseverity")) {
+ val = json_object_get_int(json);
+ if(val >= 0 && val <= 7)
+ pMsg->iSeverity = val;
+ else
+ DBGPRINTF("mmexternal: invalid fac %d requested -- ignored\n", val);
+ } else if(!strcmp(name, "procid")) {
+ psz = json_object_get_string(json);
+ MsgSetPROCID(pMsg, psz);
+ } else if(!strcmp(name, "msgid")) {
+ psz = json_object_get_string(json);
+ MsgSetMSGID(pMsg, psz);
+ } else if(!strcmp(name, "structured-data")) {
+ psz = json_object_get_string(json);
+ MsgSetStructuredData(pMsg, psz);
+ } else if(!strcmp(name, "hostname") || !strcmp(name, "source")) {
+ psz = json_object_get_string(json);
+ MsgSetHOSTNAME(pMsg, (const uchar*)psz, strlen(psz));
+ } else if(!strcmp(name, "fromhost")) {
+ psz = json_object_get_string(json);
+ MsgSetRcvFromStr(pMsg, (const uchar*) psz, strlen(psz), &propFromHost);
+ prop.Destruct(&propFromHost);
+ } else if(!strcmp(name, "fromhost-ip")) {
+ psz = json_object_get_string(json);
+ MsgSetRcvFromIPStr(pMsg, (const uchar*)psz, strlen(psz), &propRcvFromIP);
+ prop.Destruct(&propRcvFromIP);
+ } else if(!strcmp(name, "$!")) {
+ /* msgAddJSON expects that it can keep the object without incremeting
+ * the json reference count. So we MUST NOT free (_put) the object in
+ * this case. -- rgerhards, 2018-09-14
+ */
+ bNeedFree = 0;
+ msgAddJSON(pMsg, (uchar*)"!", json, 0, sharedReference);
+ } else {
+ /* we ignore unknown properties */
+ DBGPRINTF("msgSetPropViaJSON: unkonwn property ignored: %s\n",
+ name);
+ }
+
+ if(bNeedFree) {
+ json_object_put(json);
+ }
+
+ RETiRet;
+}
+
+
+/* set message properties based on JSON string. This function does it all,
+ * including parsing the JSON string. If an error is detected, the operation
+ * is aborted at the time of error. Any modifications made before the
+ * error ocurs are still PERSISTED.
+ * This function is meant to support the external message modifiction module
+ * interface. As such, replacing properties is expressively permited. Note that
+ * properties which were derived from the message during parsing are NOT
+ * updated if the underlying (raw)msg property is changed.
+ */
+rsRetVal
+MsgSetPropsViaJSON(smsg_t *__restrict__ const pMsg, const uchar *__restrict__ const jsonstr)
+{
+ struct json_tokener *tokener = NULL;
+ struct json_object *json;
+ const char *errMsg;
+ DEFiRet;
+
+ DBGPRINTF("DDDDDD: JSON string for message mod: '%s'\n", jsonstr);
+ if(!strcmp((char*)jsonstr, "{}")) /* shortcut for a common case */
+ FINALIZE;
+
+ tokener = json_tokener_new();
+
+ json = json_tokener_parse_ex(tokener, (char*)jsonstr, ustrlen(jsonstr));
+ if(Debug) {
+ errMsg = NULL;
+ if(json == NULL) {
+ enum json_tokener_error err;
+
+ err = tokener->err;
+ if(err != json_tokener_continue)
+ errMsg = json_tokener_error_desc(err);
+ else
+ errMsg = "Unterminated input";
+ } else if(!json_object_is_type(json, json_type_object))
+ errMsg = "JSON value is not an object";
+ if(errMsg != NULL) {
+ DBGPRINTF("MsgSetPropsViaJSON: Error parsing JSON '%s': %s\n",
+ jsonstr, errMsg);
+ }
+ }
+ if(json == NULL || !json_object_is_type(json, json_type_object)) {
+ ABORT_FINALIZE(RS_RET_JSON_UNUSABLE);
+ }
+ MsgSetPropsViaJSON_Object(pMsg, json);
+
+finalize_it:
+ if(tokener != NULL)
+ json_tokener_free(tokener);
+ RETiRet;
+}
+
+
+/* Used by MsgSetPropsViaJSON to set properties.
+ * The same as MsgSetPropsViaJSON only that a json object is given and not a string
+ */
+rsRetVal
+MsgSetPropsViaJSON_Object(smsg_t *__restrict__ const pMsg, struct json_object *json)
+{
+ DEFiRet;
+ if(json == NULL || !json_object_is_type(json, json_type_object)) {
+ DBGPRINTF("MsgSetPropsViaJSON_Object: json NULL or not object type\n");
+ ABORT_FINALIZE(RS_RET_JSON_UNUSABLE);
+ }
+ struct json_object_iterator it = json_object_iter_begin(json);
+ struct json_object_iterator itEnd = json_object_iter_end(json);
+ while (!json_object_iter_equal(&it, &itEnd)) {
+ struct json_object *child = json_object_iter_peek_value(&it);
+ json_object_get(child);
+ msgSetPropViaJSON(pMsg, json_object_iter_peek_name(&it),
+ child, 0);
+ json_object_iter_next(&it);
+ }
+ json_object_put(json);
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* get the severity - this is an entry point that
+ * satisfies the base object class getSeverity semantics.
+ * rgerhards, 2008-01-14
+ */
+rsRetVal
+MsgGetSeverity(smsg_t * const pMsg, int *piSeverity)
+{
+ *piSeverity = pMsg->iSeverity;
+ return RS_RET_OK;
+}
+
+
+static uchar *
+jsonPathGetLeaf(uchar *name, int lenName)
+{
+ int i;
+ for(i = lenName ; i >= 0 ; --i)
+ if(i == 0) {
+ if(name[0] == '!' || name[0] == '.' || name[0] == '/')
+ break;
+ } else {
+ if(name[i] == '!')
+ break;
+ }
+ if(name[i] == '!' || name[i] == '.' || name[i] == '/')
+ ++i;
+ return name + i;
+}
+
+static json_bool jsonVarExtract(struct json_object* root, const char *key, struct json_object **value) {
+ char namebuf[MAX_VARIABLE_NAME_LEN];
+ int key_len = strlen(key);
+ char *array_idx_start = strstr(key, "[");
+ char *array_idx_end = NULL;
+ char *array_idx_num_end_discovered = NULL;
+ struct json_object *arr = NULL;
+ if (array_idx_start != NULL) {
+ array_idx_end = strstr(array_idx_start, "]");
+ }
+ if (array_idx_end != NULL && (array_idx_end - key + 1) == key_len) {
+ errno = 0;
+ int idx = (int) strtol(array_idx_start + 1, &array_idx_num_end_discovered, 10);
+ if (errno == 0 && array_idx_num_end_discovered == array_idx_end) {
+ memcpy(namebuf, key, array_idx_start - key);
+ namebuf[array_idx_start - key] = '\0';
+ json_bool found_obj = json_object_object_get_ex(root, namebuf, &arr);
+ if (found_obj && json_object_is_type(arr, json_type_array)) {
+ int len = json_object_array_length(arr);
+ if (len > idx) {
+ *value = json_object_array_get_idx(arr, idx);
+ if (*value != NULL) return TRUE;
+ }
+ return FALSE;
+ }
+ }
+ }
+ return json_object_object_get_ex(root, key, value);
+}
+
+
+static rsRetVal
+jsonPathFindNext(struct json_object *root, uchar *namestart, uchar **name, uchar *leaf,
+ struct json_object **found, int bCreate)
+{
+ uchar namebuf[MAX_VARIABLE_NAME_LEN];
+ struct json_object *json;
+ size_t i;
+ uchar *p = *name;
+ DEFiRet;
+
+ if(*p == '!' || (*name == namestart && (*p == '.' || *p == '/')))
+ ++p;
+ for(i = 0 ; *p && !(p == namestart && (*p == '.' || *p == '/')) && *p != '!'
+ && p != leaf && i < sizeof(namebuf)-1 ; ++i, ++p)
+ namebuf[i] = *p;
+ if(i > 0) {
+ namebuf[i] = '\0';
+ if(jsonVarExtract(root, (char*)namebuf, &json) == FALSE) {
+ json = NULL;
+ }
+ } else
+ json = root;
+ if(json == NULL) {
+ if(!bCreate) {
+ ABORT_FINALIZE(RS_RET_JNAME_INVALID);
+ } else {
+ if (json_object_get_type(root) != json_type_object) {
+ DBGPRINTF("jsonPathFindNext with bCreate: not a container in json path, "
+ "name is '%s'\n", namestart);
+ ABORT_FINALIZE(RS_RET_INVLD_SETOP);
+ }
+ json = json_object_new_object();
+ json_object_object_add(root, (char*)namebuf, json);
+ }
+ }
+
+ *name = p;
+ *found = json;
+finalize_it:
+ RETiRet;
+}
+
+static rsRetVal
+jsonPathFindParent(struct json_object *jroot, uchar *name, uchar *leaf, struct json_object **parent,
+ const int bCreate)
+{
+ uchar *namestart;
+ DEFiRet;
+ namestart = name;
+ *parent = jroot;
+ while(name < leaf-1) {
+ CHKiRet(jsonPathFindNext(*parent, namestart, &name, leaf, parent, bCreate));
+ }
+ if(*parent == NULL)
+ ABORT_FINALIZE(RS_RET_NOT_FOUND);
+finalize_it:
+ RETiRet;
+}
+
+static rsRetVal
+jsonMerge(struct json_object *existing, struct json_object *json)
+{
+ /* TODO: check & handle duplicate names */
+ DEFiRet;
+
+ struct json_object_iterator it = json_object_iter_begin(json);
+ struct json_object_iterator itEnd = json_object_iter_end(json);
+ while (!json_object_iter_equal(&it, &itEnd)) {
+ json_object_object_add(existing, json_object_iter_peek_name(&it),
+ json_object_get(json_object_iter_peek_value(&it)));
+ json_object_iter_next(&it);
+ }
+ /* note: json-c does ref counting. We added all descandants refcounts
+ * in the loop above. So when we now free(_put) the root object, only
+ * root gets freed().
+ */
+ json_object_put(json);
+ RETiRet;
+}
+
+/* find a JSON structure element (field or container doesn't matter). */
+rsRetVal
+jsonFind(smsg_t *const pMsg, msgPropDescr_t *pProp, struct json_object **jsonres)
+{
+ uchar *leaf;
+ struct json_object *parent;
+ struct json_object *field;
+ struct json_object **jroot = NULL;
+ pthread_mutex_t *mut = NULL;
+ DEFiRet;
+
+ CHKiRet(getJSONRootAndMutex(pMsg, pProp->id, &jroot, &mut));
+ pthread_mutex_lock(mut);
+
+ if(*jroot == NULL) {
+ field = NULL;
+ goto finalize_it;
+ }
+
+ if(!strcmp((char*)pProp->name, "!")) {
+ field = *jroot;
+ } else if(!strcmp((char*)pProp->name, ".")) {
+ field = *jroot;
+ } else {
+ leaf = jsonPathGetLeaf(pProp->name, pProp->nameLen);
+ CHKiRet(jsonPathFindParent(*jroot, pProp->name, leaf, &parent, 0));
+ if(jsonVarExtract(parent, (char*)leaf, &field) == FALSE)
+ field = NULL;
+ }
+ *jsonres = field;
+
+finalize_it:
+ if(mut != NULL)
+ pthread_mutex_unlock(mut);
+ RETiRet;
+}
+
+/* check if JSON variable exists (works on terminal var and container) */
+rsRetVal ATTR_NONNULL()
+msgCheckVarExists(smsg_t *const pMsg, msgPropDescr_t *pProp)
+{
+ struct json_object *jsonres = NULL;
+ DEFiRet;
+
+ CHKiRet(jsonFind(pMsg, pProp, &jsonres));
+ if(jsonres == NULL) {
+ iRet = RS_RET_NOT_FOUND;
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+rsRetVal
+msgAddJSON(smsg_t * const pM, uchar *name, struct json_object *json, int force_reset, int sharedReference)
+{
+ /* TODO: error checks! This is a quick&dirty PoC! */
+ struct json_object **jroot;
+ struct json_object *parent, *leafnode;
+ struct json_object *given = NULL;
+ uchar *leaf;
+ pthread_mutex_t *mut = NULL;
+ DEFiRet;
+
+ CHKiRet(getJSONRootAndMutexByVarChar(pM, name[0], &jroot, &mut));
+ pthread_mutex_lock(mut);
+
+ if(name[0] == '/') { /* globl var special handling */
+ if (sharedReference) {
+ given = json;
+ json = jsonDeepCopy(json);
+ json_object_put(given);
+ }
+ }
+
+ if(name[1] == '\0') { /* full tree? */
+ if(*jroot == NULL)
+ *jroot = json;
+ else
+ CHKiRet(jsonMerge(*jroot, json));
+ } else {
+ if(*jroot == NULL) {
+ /* now we need a root obj */
+ *jroot = json_object_new_object();
+ }
+ leaf = jsonPathGetLeaf(name, ustrlen(name));
+ iRet = jsonPathFindParent(*jroot, name, leaf, &parent, 1);
+ if (unlikely(iRet != RS_RET_OK)) {
+ json_object_put(json);
+ FINALIZE;
+ }
+ if (json_object_get_type(parent) != json_type_object) {
+ DBGPRINTF("msgAddJSON: not a container in json path,"
+ "name is '%s'\n", name);
+ json_object_put(json);
+ ABORT_FINALIZE(RS_RET_INVLD_SETOP);
+ }
+ if(jsonVarExtract(parent, (char*)leaf, &leafnode) == FALSE)
+ leafnode = NULL;
+ /* json-c code indicates we can simply replace a
+ * json type. Unfortunaltely, this is not documented
+ * as part of the interface spec. We still use it,
+ * because it speeds up processing. If it does not work
+ * at some point, use
+ * json_object_object_del(parent, (char*)leaf);
+ * before adding. rgerhards, 2012-09-17
+ */
+ if (force_reset || (leafnode == NULL)) {
+ json_object_object_add(parent, (char*)leaf, json);
+ } else {
+ if(json_object_get_type(json) == json_type_object) {
+ CHKiRet(jsonMerge(*jroot, json));
+ } else {
+ /* TODO: improve the code below, however, the current
+ * state is not really bad */
+ if(json_object_get_type(leafnode) == json_type_object) {
+ DBGPRINTF("msgAddJSON: trying to update a container "
+ "node with a leaf, name is %s - "
+ "forbidden", name);
+ json_object_put(json);
+ ABORT_FINALIZE(RS_RET_INVLD_SETOP);
+ }
+ json_object_object_add(parent, (char*)leaf, json);
+ }
+ }
+ }
+
+finalize_it:
+ if(mut != NULL)
+ pthread_mutex_unlock(mut);
+ RETiRet;
+}
+
+
+rsRetVal
+msgDelJSON(smsg_t * const pM, uchar *name)
+{
+ struct json_object **jroot;
+ struct json_object *parent, *leafnode;
+ uchar *leaf;
+ pthread_mutex_t *mut = NULL;
+ DEFiRet;
+
+ CHKiRet(getJSONRootAndMutexByVarChar(pM, name[0], &jroot, &mut));
+ pthread_mutex_lock(mut);
+
+ if(*jroot == NULL) {
+ DBGPRINTF("msgDelJSONVar; jroot empty in unset for property %s\n",
+ name);
+ FINALIZE;
+ }
+
+ if(name[1] == '\0') {
+ /* full tree! Strange, but I think we should permit this. After all,
+ * we trust rsyslog.conf to be written by the admin.
+ */
+ DBGPRINTF("unsetting JSON root object\n");
+ json_object_put(*jroot);
+ *jroot = NULL;
+ } else {
+ leaf = jsonPathGetLeaf(name, ustrlen(name));
+ CHKiRet(jsonPathFindParent(*jroot, name, leaf, &parent, 0));
+ if(jsonVarExtract(parent, (char*)leaf, &leafnode) == FALSE)
+ leafnode = NULL;
+ if(leafnode == NULL) {
+ DBGPRINTF("unset JSON: could not find '%s'\n", name);
+ ABORT_FINALIZE(RS_RET_JNAME_NOTFOUND);
+ } else {
+ DBGPRINTF("deleting JSON value path '%s', "
+ "leaf '%s', type %d\n",
+ name, leaf, json_object_get_type(leafnode));
+ json_object_object_del(parent, (char*)leaf);
+ }
+ }
+
+finalize_it:
+ if(mut != NULL)
+ pthread_mutex_unlock(mut);
+ RETiRet;
+}
+
+/* add Metadata to the message. This is stored in a special JSON
+ * container. Note that only string types are currently supported,
+ * what should pose absolutely no problem with the string-ish nature
+ * of rsyslog metadata.
+ * added 2015-01-09 rgerhards
+ */
+rsRetVal
+msgAddMetadata(smsg_t *const __restrict__ pMsg,
+ uchar *const __restrict__ metaname,
+ uchar *const __restrict__ metaval)
+{
+ DEFiRet;
+ struct json_object *const json = json_object_new_object();
+ CHKmalloc(json);
+ struct json_object *const jval = json_object_new_string((char*)metaval);
+ if(jval == NULL) {
+ json_object_put(json);
+ ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ }
+ json_object_object_add(json, (const char *const)metaname, jval);
+ iRet = msgAddJSON(pMsg, (uchar*)"!metadata", json, 0, 0);
+finalize_it:
+ RETiRet;
+}
+
+rsRetVal
+msgAddMultiMetadata(smsg_t *const __restrict__ pMsg,
+ const uchar ** __restrict__ metaname,
+ const uchar ** __restrict__ metaval,
+ const int count)
+{
+ DEFiRet;
+ int i = 0 ;
+ struct json_object *const json = json_object_new_object();
+ CHKmalloc(json);
+ for ( i = 0 ; i < count ; i++ ) {
+ struct json_object *const jval = json_object_new_string((char*)metaval[i]);
+ if(jval == NULL) {
+ json_object_put(json);
+ ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ }
+ json_object_object_add(json, (const char *const)metaname[i], jval);
+ }
+ iRet = msgAddJSON(pMsg, (uchar*)"!metadata", json, 0, 0);
+finalize_it:
+ RETiRet;
+}
+
+struct json_object *
+jsonDeepCopy(struct json_object *src)
+{
+ struct json_object *dst = NULL, *json;
+ int arrayLen, i;
+
+ if(src == NULL) goto done;
+
+ switch(json_object_get_type(src)) {
+ case json_type_boolean:
+ dst = json_object_new_boolean(json_object_get_boolean(src));
+ break;
+ case json_type_double:
+ dst = json_object_new_double(json_object_get_double(src));
+ break;
+ case json_type_int:
+ dst = json_object_new_int64(json_object_get_int64(src));
+ break;
+ case json_type_string:
+ dst = json_object_new_string(json_object_get_string(src));
+ break;
+ case json_type_object:
+ dst = json_object_new_object();
+ struct json_object_iterator it = json_object_iter_begin(src);
+ struct json_object_iterator itEnd = json_object_iter_end(src);
+ while (!json_object_iter_equal(&it, &itEnd)) {
+ json = jsonDeepCopy(json_object_iter_peek_value(&it));
+ json_object_object_add(dst, json_object_iter_peek_name(&it), json);
+ json_object_iter_next(&it);
+ }
+ break;
+ case json_type_array:
+ arrayLen = json_object_array_length(src);
+ dst = json_object_new_array();
+ for(i = 0 ; i < arrayLen ; ++i) {
+ json = json_object_array_get_idx(src, i);
+ json = jsonDeepCopy(json);
+ json_object_array_add(dst, json);
+ }
+ break;
+ case json_type_null:
+ default:DBGPRINTF("jsonDeepCopy(): error unknown type %d\n",
+ json_object_get_type(src));
+ dst = NULL;
+ break;
+ }
+done: return dst;
+}
+
+
+rsRetVal
+msgSetJSONFromVar(smsg_t * const pMsg, uchar *varname, struct svar *v, int force_reset)
+{
+ struct json_object *json = NULL;
+ char *cstr;
+ DEFiRet;
+ switch(v->datatype) {
+ case 'S':/* string */
+ cstr = es_str2cstr(v->d.estr, NULL);
+ json = json_object_new_string(cstr);
+ free(cstr);
+ break;
+ case 'N':/* number (integer) */
+ json = json_object_new_int64(v->d.n);
+ break;
+ case 'J':/* native JSON */
+ json = jsonDeepCopy(v->d.json);
+ break;
+ default:DBGPRINTF("msgSetJSONFromVar: unsupported datatype %c\n",
+ v->datatype);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+
+ msgAddJSON(pMsg, varname, json, force_reset, 0);
+finalize_it:
+ RETiRet;
+}
+
+rsRetVal
+MsgAddToStructuredData(smsg_t * const pMsg, uchar *toadd, rs_size_t len)
+{
+ uchar *newptr;
+ rs_size_t newlen;
+ int empty;
+ DEFiRet;
+ empty = pMsg->pszStrucData == NULL || pMsg->pszStrucData[0] == '-';
+ newlen = (empty) ? len : pMsg->lenStrucData + len;
+ CHKmalloc(newptr = (uchar*) realloc(pMsg->pszStrucData, newlen+1));
+ if(empty) {
+ memcpy(newptr, toadd, len);
+ } else {
+ memcpy(newptr+pMsg->lenStrucData, toadd, len);
+ }
+ pMsg->pszStrucData = newptr;
+ pMsg->pszStrucData[newlen] = '\0';
+ pMsg->lenStrucData = newlen;
+finalize_it:
+ RETiRet;
+}
+
+
+/* Fill a message propert description. Space must already be alloced
+ * by the caller. This is for efficiency, as we expect this to happen
+ * as part of a larger structure alloc.
+ * Note that CEE/LOCAL_VAR properties can come in either as
+ * "$!xx"/"$.xx" or "!xx"/".xx" - we will unify them here.
+ */
+rsRetVal
+msgPropDescrFill(msgPropDescr_t *pProp, uchar *name, int nameLen)
+{
+ propid_t id;
+ int offs;
+ DEFiRet;
+ if(propNameToID(name, &id) != RS_RET_OK) {
+ parser_errmsg("invalid property '%s'", name);
+ /* now try to find some common error causes */
+ if(!strcasecmp((char*)name, "myhostname"))
+ parser_errmsg("did you mean '$myhostname' instead of '%s'? "
+ "See also: https://www.rsyslog.com/rsyslog-info-1/", name);
+ else if(!strcasecmp((char*)name, "bom"))
+ parser_errmsg("did you mean '$bom' instead of '%s'?"
+ "See also: https://www.rsyslog.com/rsyslog-info-1/", name);
+ else if(!strcasecmp((char*)name, "now"))
+ parser_errmsg("did you mean '$now' instead of '%s'?"
+ "See also: https://www.rsyslog.com/rsyslog-info-1/", name);
+ else if(!strcasecmp((char*)name, "year"))
+ parser_errmsg("did you mean '$year' instead of '%s'?"
+ "See also: https://www.rsyslog.com/rsyslog-info-1/", name);
+ else if(!strcasecmp((char*)name, "month"))
+ parser_errmsg("did you mean '$month' instead of '%s'?"
+ "See also: https://www.rsyslog.com/rsyslog-info-1/", name);
+ else if(!strcasecmp((char*)name, "day"))
+ parser_errmsg("did you mean '$day' instead of '%s'?"
+ "See also: https://www.rsyslog.com/rsyslog-info-1/", name);
+ else if(!strcasecmp((char*)name, "hour"))
+ parser_errmsg("did you mean '$hour' instead of '%s'?"
+ "See also: https://www.rsyslog.com/rsyslog-info-1/", name);
+ else if(!strcasecmp((char*)name, "hhour"))
+ parser_errmsg("did you mean '$hhour' instead of '%s'?"
+ "See also: https://www.rsyslog.com/rsyslog-info-1/", name);
+ else if(!strcasecmp((char*)name, "qhour"))
+ parser_errmsg("did you mean '$qhour' instead of '%s'?"
+ "See also: https://www.rsyslog.com/rsyslog-info-1/", name);
+ else if(!strcasecmp((char*)name, "minute"))
+ parser_errmsg("did you mean '$minute' instead of '%s'?"
+ "See also: https://www.rsyslog.com/rsyslog-info-1/", name);
+ else if(!strcasecmp((char*)name, "now-utc"))
+ parser_errmsg("did you mean '$now-utc' instead of '%s'?"
+ "See also: https://www.rsyslog.com/rsyslog-info-1/", name);
+ else if(!strcasecmp((char*)name, "year-utc"))
+ parser_errmsg("did you mean '$year-utc' instead of '%s'?"
+ "See also: https://www.rsyslog.com/rsyslog-info-1/", name);
+ else if(!strcasecmp((char*)name, "month-utc"))
+ parser_errmsg("did you mean '$month-utc' instead of '%s'?"
+ "See also: https://www.rsyslog.com/rsyslog-info-1/", name);
+ else if(!strcasecmp((char*)name, "day-utc"))
+ parser_errmsg("did you mean '$day-utc' instead of '%s'?"
+ "See also: https://www.rsyslog.com/rsyslog-info-1/", name);
+ else if(!strcasecmp((char*)name, "hour-utc"))
+ parser_errmsg("did you mean '$hour-utc' instead of '%s'?"
+ "See also: https://www.rsyslog.com/rsyslog-info-1/", name);
+ else if(!strcasecmp((char*)name, "hhour-utc"))
+ parser_errmsg("did you mean '$hhour-utc' instead of '%s'?"
+ "See also: https://www.rsyslog.com/rsyslog-info-1/", name);
+ else if(!strcasecmp((char*)name, "qhour-utc"))
+ parser_errmsg("did you mean '$qhour-utc' instead of '%s'?"
+ "See also: https://www.rsyslog.com/rsyslog-info-1/", name);
+ else if(!strcasecmp((char*)name, "minute-utc"))
+ parser_errmsg("did you mean '$minute-utc' instead of '%s'?"
+ "See also: https://www.rsyslog.com/rsyslog-info-1/", name);
+ ABORT_FINALIZE(RS_RET_INVLD_PROP);
+ }
+ if(id == PROP_CEE || id == PROP_LOCAL_VAR || id == PROP_GLOBAL_VAR) {
+ /* in these cases, we need the field name for later processing */
+ /* normalize name: remove $ if present */
+ offs = (name[0] == '$') ? 1 : 0;
+ pProp->name = ustrdup(name + offs);
+ pProp->nameLen = nameLen - offs;
+ /* we patch the root name, so that support functions do not need to
+ * check for different root chars. */
+ pProp->name[0] = '!';
+ }
+ pProp->id = id;
+finalize_it:
+ RETiRet;
+}
+
+void
+msgPropDescrDestruct(msgPropDescr_t *pProp)
+{
+ if(pProp != NULL) {
+ if(pProp->id == PROP_CEE ||
+ pProp->id == PROP_LOCAL_VAR ||
+ pProp->id == PROP_GLOBAL_VAR)
+ free(pProp->name);
+ }
+}
+
+
+/* dummy */
+static rsRetVal msgQueryInterface(interface_t __attribute__((unused)) *i) { return RS_RET_NOT_IMPLEMENTED; }
+
+/* Initialize the message class. Must be called as the very first method
+ * before anything else is called inside this class.
+ * rgerhards, 2008-01-04
+ */
+BEGINObjClassInit(msg, 1, OBJ_IS_CORE_MODULE)
+ pthread_mutex_init(&glblVars_lock, NULL);
+
+ /* request objects we use */
+ CHKiRet(objUse(datetime, CORE_COMPONENT));
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(prop, CORE_COMPONENT));
+ CHKiRet(objUse(var, CORE_COMPONENT));
+
+ /* set our own handlers */
+ OBJSetMethodHandler(objMethod_SERIALIZE, MsgSerialize);
+ /* some more inits */
+# ifdef HAVE_MALLOC_TRIM
+ INIT_ATOMIC_HELPER_MUT(mutTrimCtr);
+# endif
+ENDObjClassInit(msg)