/* tap-flow.c
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

/* This module provides udp and tcp follow stream capabilities to tshark.
 * It is only used by tshark and not wireshark.
 */

#include "config.h"

#include <stdio.h>
#include <stdlib.h>

#include <epan/sequence_analysis.h>
#include <epan/stat_tap_ui.h>
#include <epan/tap.h>

void register_tap_listener_flow(void);

#define STR_FLOW        "flow,"
#define STR_STANDARD    ",standard"
#define STR_NETWORK     ",network"

WS_NORETURN static void flow_exit(const char *strp)
{
    fprintf(stderr, "tshark: flow - %s\n", strp);
    exit(1);
}

static void
flow_draw(void *arg)
{
    seq_analysis_info_t* flow_info = (seq_analysis_info_t*)arg;

    sequence_analysis_get_nodes(flow_info);

    sequence_analysis_dump_to_file(stdout, flow_info, 0);

    //clean up the data
    sequence_analysis_list_free(flow_info);
    sequence_analysis_info_free(flow_info);
}

static gboolean flow_arg_strncmp(const char **opt_argp, const char *strp)
{
    size_t len = strlen(strp);

    if (strncmp(*opt_argp, strp, len) == 0)
    {
        *opt_argp += len;
        return TRUE;
    }
    return FALSE;
}

static void
flow_arg_mode(const char **opt_argp, seq_analysis_info_t *flow_info)
{
    if (flow_arg_strncmp(opt_argp, STR_STANDARD))
    {
        flow_info->any_addr = 1;
    }
    else if (flow_arg_strncmp(opt_argp, STR_NETWORK))
    {
        flow_info->any_addr = 0;
    }
    else
    {
        flow_exit("Invalid address type.");
    }
}

static void
flow_init(const char *opt_argp, void *userdata)
{
    seq_analysis_info_t *flow_info = g_new0(seq_analysis_info_t, 1);
    GString  *errp;
    register_analysis_t* analysis = (register_analysis_t*)userdata;
    const char *filter=NULL;

    opt_argp += strlen(STR_FLOW);
    opt_argp += strlen(sequence_analysis_get_name(analysis));

    flow_arg_mode(&opt_argp, flow_info);
    if (*opt_argp == ',') {
        filter = opt_argp + 1;
    }

    sequence_analysis_list_free(flow_info);

    errp = register_tap_listener(sequence_analysis_get_tap_listener_name(analysis), flow_info, filter, sequence_analysis_get_tap_flags(analysis),
                                NULL, sequence_analysis_get_packet_func(analysis), flow_draw, NULL);

    if (errp != NULL)
    {
        sequence_analysis_list_free(flow_info);
        sequence_analysis_info_free(flow_info);
        g_string_free(errp, TRUE);
        flow_exit("Error registering tap listener.");
    }
}

static gboolean
flow_register(const void *key _U_, void *value, void *userdata _U_)
{
    register_analysis_t* analysis = (register_analysis_t*)value;
    stat_tap_ui flow_ui;
    GString *cmd_str = g_string_new(STR_FLOW);
    gchar *cli_string;

    g_string_append(cmd_str, sequence_analysis_get_name(analysis));
    cli_string = g_string_free(cmd_str, FALSE);

    flow_ui.group = REGISTER_STAT_GROUP_GENERIC;
    flow_ui.title = NULL;   /* construct this from the protocol info? */
    flow_ui.cli_string = cli_string;
    flow_ui.tap_init_cb = flow_init;
    flow_ui.nparams = 0;
    flow_ui.params = NULL;
    register_stat_tap_ui(&flow_ui, analysis);
    g_free(cli_string);
    return FALSE;
}

void
register_tap_listener_flow(void)
{
    sequence_analysis_table_iterate_tables(flow_register, NULL);
}

/*
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
 *
 * Local Variables:
 * c-basic-offset: 4
 * tab-width: 8
 * indent-tabs-mode: nil
 * End:
 *
 * ex: set shiftwidth=2 tabstop=8 expandtab:
 * :indentSize=2:tabSize=8:noTabs=true:
 */
