/* Copyright (C) 2009 Trend Micro Inc.
 * All right reserved.
 *
 * This program is a free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public
 * License (version 2) as published by the FSF - Free Software
 * Foundation
 */

#include "shared.h"
#include "logcollector.h"

#define MAX_CACHE 16
#define MAX_HEADER 64

/* Compile message from cache and send through queue */
static void audit_send_msg(char **cache, int top, const char *file, int drop_it) {
    int i;
    size_t n = 0;
    size_t z;
    char message[OS_MAXSTR];

    for (i = 0; i < top; i++) {
        z = strlen(cache[i]);

        if (n + z + 1 < OS_MAXSTR) {
            if (n > 0)
                message[n++] = ' ';
            
            strncpy(message + n, cache[i], z);
        }

        n += z;
        free(cache[i]);
    }

    if (!drop_it) {
        message[n] = '\0';

        if (SendMSG(logr_queue, message, file, LOCALFILE_MQ) < 0) {
            merror(QUEUE_SEND, ARGV0);

            if ((logr_queue = StartMQ(DEFAULTQPATH, WRITE)) < 0) {
                ErrorExit(QUEUE_FATAL, ARGV0, DEFAULTQPATH);
            }
        }
    }
}

void *read_audit(int pos, int *rc, int drop_it) {
    char *cache[MAX_CACHE];
    char header[MAX_HEADER] = { '\0' };
    int icache = 0;
    char buffer[OS_MAXSTR];
    char *id;
    char *p;
    size_t z;
    long offset = ftell(logff[pos].fp);
    
    if (offset < 0) {
        merror(FTELL_ERROR, ARGV0, logff[pos].file, errno, strerror(errno));
        return NULL;
    }

    *rc = 0;

    while (fgets(buffer, OS_MAXSTR, logff[pos].fp)) {
        if ((p = strchr(buffer, '\n')))
            *p = '\0';
        else {
            if (strlen(buffer) == OS_MAXSTR - 1) {
                // Message too large, discard line
                while (fgets(buffer, OS_MAXSTR, logff[pos].fp) && !strchr(buffer, '\n'));
            } else {
                debug1("%s: Message not complete. Trying again: '%s'", ARGV0, buffer);
                
                if (fseek(logff[pos].fp, offset, SEEK_SET) < 0) {
                    merror(FSEEK_ERROR, ARGV0, logff[pos].file, errno, strerror(errno));
                    break;
                }
            }

            break;
        }

        // Extract header: "type=\.* msg=audit(\d+.\d+:\d+):"

        if (strncmp(buffer, "type=", 5) || !((id = strstr(buffer + 5, "msg=audit(")) && (p = strstr(id += 10, "): ")))) {
            merror("%s: ERROR: discarding audit message because of invalid syntax", ARGV0);
            break;
        }
        
        z = p - id;

        if (strncmp(id, header, z)) {
            // Current message belongs to another event: send cached messages
            if (icache > 0)
                audit_send_msg(cache, icache, logff[pos].file, drop_it);

            // Store current event
            *cache = strdup(buffer);
            icache = 1;
            strncpy(header, id, z < MAX_HEADER ? z : MAX_HEADER - 1);
        } else {
            // The header is the same: store
            if (icache == MAX_CACHE)
                merror("%s: ERROR: discarding audit message because cache is full", ARGV0);
            else
                cache[icache++] = strdup(buffer);
        }
    }

    if (icache > 0)
        audit_send_msg(cache, icache, logff[pos].file, drop_it);

    return NULL;
}
