/* pmaixforwardedfrom.c
 *
 * this cleans up messages forwarded from AIX
 *
 * instead of actually parsing the message, this modifies the message and then falls through to allow a
 * later parser to handle the now modified message
 *
 * created 2010-12-13 by David Lang based on pmlastmsg
 *
 * This file is part of rsyslog.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *       -or-
 *       see COPYING.ASL20 in the source distribution
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "config.h"
#include "rsyslog.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
#include "conf.h"
#include "syslogd-types.h"
#include "template.h"
#include "msg.h"
#include "module-template.h"
#include "glbl.h"
#include "errmsg.h"
#include "parser.h"
#include "datetime.h"
#include "unicode-helper.h"
#include "rsconf.h"

MODULE_TYPE_PARSER
MODULE_TYPE_NOKEEP;
PARSER_NAME("rsyslog.aixforwardedfrom")

/* internal structures
 */
DEF_PMOD_STATIC_DATA;
DEFobjCurrIf(glbl) DEFobjCurrIf(parser) DEFobjCurrIf(datetime)


    /* static data */
    static int bParseHOSTNAMEandTAG; /* cache for the equally-named global param - performance enhancement */


BEGINisCompatibleWithFeature
    CODESTARTisCompatibleWithFeature;
    if (eFeat == sFEATUREAutomaticSanitazion) iRet = RS_RET_OK;
    if (eFeat == sFEATUREAutomaticPRIParsing) iRet = RS_RET_OK;
ENDisCompatibleWithFeature


BEGINparse
    uchar *p2parse;
    int lenMsg;
    int skipLen = 0;
#define OpeningText "Message forwarded from "
#define OpeningText2 "From "
    CODESTARTparse;
    dbgprintf("Message will now be parsed by fix AIX Forwarded From parser.\n");
    assert(pMsg != NULL);
    assert(pMsg->pszRawMsg != NULL);
    lenMsg = pMsg->iLenRawMsg - pMsg->offAfterPRI;
    /* note: offAfterPRI is already the number of PRI chars (do not add one!) */
    p2parse = pMsg->pszRawMsg + pMsg->offAfterPRI; /* point to start of text, after PRI */

    /* check if this message is of the type we handle in this (very limited) parser */
    /* first, we permit SP */
    while (lenMsg && *p2parse == ' ') {
        --lenMsg;
        ++p2parse;
    }
    if ((unsigned)lenMsg < 24) {
        /* too short, can not be "our" message */
        /* minimum message, 16 character timestamp, 'From ", 1 character name, ': '*/
        ABORT_FINALIZE(RS_RET_COULD_NOT_PARSE);
    }

    /* skip over timestamp */
    lenMsg -= 16;
    p2parse += 16;
    /* if there is the string "Message forwarded from " were the hostname should be */
    if (!strncasecmp((char *)p2parse, OpeningText, sizeof(OpeningText) - 1)) skipLen = 23;
    /* or "From " */
    if (!strncasecmp((char *)p2parse, OpeningText2, sizeof(OpeningText2) - 1)) skipLen = 5;
    DBGPRINTF("pmaixforwardedfrom: skipLen %d\n", skipLen);
    if (!skipLen) {
        /* wrong opening text */
        DBGPRINTF("not a AIX message forwarded from mangled log!\n");
        ABORT_FINALIZE(RS_RET_COULD_NOT_PARSE);
    }
    /* bump the message portion up by skipLen(23 or 5) characters to overwrite the "Message forwarded from
" or "From " with the hostname */
    lenMsg -= skipLen;
    if (lenMsg < 2) {
        dbgprintf("not a AIX message forwarded from message has nothing after header\n");
        ABORT_FINALIZE(RS_RET_COULD_NOT_PARSE);
    }
    memmove(p2parse, p2parse + skipLen, lenMsg);
    *(p2parse + lenMsg) = '\n';
    *(p2parse + lenMsg + 1) = '\0';
    pMsg->iLenRawMsg -= skipLen;
    pMsg->iLenMSG -= skipLen;
    /* now look for the : after the hostname to walk past the hostname, also watch for a space in case this isn't
really an AIX log, but has a similar preamble */
    while (lenMsg && *p2parse != ' ' && *p2parse != ':') {
        --lenMsg;
        ++p2parse;
    }
    if (lenMsg < 1) {
        dbgprintf(
            "not a AIX message forwarded from message has nothing after colon "
            "or no colon at all\n");
        ABORT_FINALIZE(RS_RET_COULD_NOT_PARSE);
    }
    if (lenMsg && *p2parse != ':') {
        DBGPRINTF(
            "not a AIX message forwarded from mangled log but similar enough that the preamble has "
            "been removed\n");
        ABORT_FINALIZE(RS_RET_COULD_NOT_PARSE);
    }
    /* bump the message portion up by one character to overwrite the extra : */
    lenMsg -= 1;
    memmove(p2parse, p2parse + 1, lenMsg);
    *(p2parse + lenMsg) = '\n';
    *(p2parse + lenMsg + 1) = '\0';
    pMsg->iLenRawMsg -= 1;
    pMsg->iLenMSG -= 1;
    /* now, claim to abort so that something else can parse the now modified message */
    DBGPRINTF("pmaixforwardedfrom: new message: [%d]'%s'\n", lenMsg, pMsg->pszRawMsg + pMsg->offAfterPRI);
    ABORT_FINALIZE(RS_RET_COULD_NOT_PARSE);

finalize_it:
ENDparse


BEGINmodExit
    CODESTARTmodExit;
    /* release what we no longer need */
    objRelease(glbl, CORE_COMPONENT);
    objRelease(parser, CORE_COMPONENT);
    objRelease(datetime, CORE_COMPONENT);
ENDmodExit


BEGINqueryEtryPt
    CODESTARTqueryEtryPt;
    CODEqueryEtryPt_STD_PMOD_QUERIES;
    CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES;
ENDqueryEtryPt


BEGINmodInit()
    CODESTARTmodInit;
    *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
    CODEmodInit_QueryRegCFSLineHdlr CHKiRet(objUse(glbl, CORE_COMPONENT));
    CHKiRet(objUse(parser, CORE_COMPONENT));
    CHKiRet(objUse(datetime, CORE_COMPONENT));

    DBGPRINTF("aixforwardedfrom parser init called, compiled with version %s\n", VERSION);
    bParseHOSTNAMEandTAG = glbl.GetParseHOSTNAMEandTAG(loadConf);
    /* cache value, is set only during rsyslogd option processing */


ENDmodInit

/* vim:set ai:
 */
