/*
 * Dump packet according to syntax
 *
 * icqsyn Copyright (C) © 2007 Rüdiger Kuhlmann
 *
 * icqsyn is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 dated June, 1991.
 *
 * icqsyn 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 General Public
 * License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this package; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.
 *
 * $Id: icqsyn.c 2634 2008-11-24 22:19:20Z kuhlmann $
 */

#include "climm.h"
#include <assert.h>
#include "util_syntax.h"
#include "conv.h"
#include "packet.h"

user_interface_state uiG = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
Preferences *prG;

const char *ContactPrefStr (Contact *cont, UDWORD flag) { return ""; }
const char  *i18n (int i, const char *s) { return s; }

BOOL DebugReal (UDWORD level, const char *str, ...)  { return 1; }
Contact *ContactScreen (Connection *serv, const char *screen DEBUGPARAM) { return NULL; }
const UDWORD BuildVersionNum = 0;

#undef COLDEBUG
#undef COLNONE
#define COLDEBUG YELLOW
#define COLNONE SGR0

/*
 * Read a complete line from a fd.
 * Returned string may not be free()d.
 */
strc_t UtilIOReadline (FILE *fd)
{
    static str_s str;
    char *p;

    s_init (&str, "", 256);
    while (1)
    {
        str.txt[str.max - 2] = 0;
        if (!fgets (str.txt + str.len, str.max - str.len, fd))
        {
            str.txt[str.len] = '\0';
            if (!str.len)
                return NULL;
            break;
        }
        str.txt[str.max - 1] = '\0';
        str.len = strlen (str.txt);
        if (!str.txt[str.max - 2])
            break;
        s_blow (&str, 128);
    }
    if ((p = strpbrk (str.txt, "\r\n")))
    {
        *p = 0;
        str.len = strlen (str.txt);
    }
    return &str;
}

typedef struct { UWORD fam; UWORD cmd; const char *name; void *f; } SNAC;

static SNAC SNACS[] = {
    {  1,  1, "SRV_SERVICEERR",      NULL },
    {  1,  3, "SRV_FAMILIES",        NULL },
    {  1,  7, "SRV_RATES",           NULL },
    {  1, 10, "SRV_RATEEXCEEDED",    NULL },
    {  1, 11, "SRV_SERVERPAUSE",     NULL },
    {  1, 15, "SRV_REPLYINFO",       NULL },
    {  1, 18, "SRV_MIGRATIONREQ",    NULL },
    {  1, 19, "SRV_MOTD",            NULL },
    {  1, 24, "SRV_FAMILIES2",       NULL },
    {  2,  1, "SRV_LOCATIONERR",     NULL },
    {  2,  3, "SRV_REPLYLOCATION",   NULL },
    {  2,  6, "SRV_USERINFO",        NULL },
    {  3,  1, "SRV_CONTACTERR",      NULL },
    {  3,  3, "SRV_REPLYBUDDY",      NULL },
    {  3, 11, "SRV_USERONLINE",      NULL },
    {  3, 10, "SRV_REFUSE",          NULL },
    {  3, 12, "SRV_USEROFFLINE",     NULL },
    {  4,  1, "SRV_ICBMERR",         NULL },
    {  4,  5, "SRV_REPLYICBM",       NULL },
    {  4,  7, "SRV_RECVMSG",         NULL },
    {  4, 10, "SRV_MISSEDICBM",      NULL },
    {  4, 11, "SRV/CLI_ACKMSG",      NULL },
    {  4, 12, "SRV_SRVACKMSG",       NULL },
    {  9,  1, "SRV_BOSERR",          NULL },
    {  9,  3, "SRV_REPLYBOS",        NULL },
    { 11,  2, "SRV_SETINTERVAL",     NULL },
    { 19,  3, "SRV_REPLYLISTS",      NULL },
    { 19,  6, "SRV_REPLYROSTER",     NULL },
    { 19,  8, "SRV_ROSTERADD",       NULL },
    { 19,  9, "SRV_ROSTERUPDATE",    NULL },
    { 19, 10, "SRV_ROSTERDELETE",    NULL },
    { 19, 14, "SRV_UPDATEACK",       NULL },
    { 19, 15, "SRV_REPLYROSTEROK",   NULL },
    { 19, 17, "SRV_ADDSTART",        NULL },
    { 19, 18, "SRV_ADDEND",          NULL },
    { 19, 25, "SRV_AUTHREQ",         NULL },
    { 19, 27, "SRV_AUTHREPLY",       NULL },
    { 19, 28, "SRV_ADDEDYOU",        NULL },
    { 21,  1, "SRV_TOICQERR",        NULL },
    { 21,  3, "SRV_FROMICQSRV",      NULL },
    { 23,  1, "SRV_REGREFUSED",      NULL },
    { 23,  3, "SRV_REPLYLOGIN",      NULL },
    { 23,  5, "SRV_NEWUIN",          NULL },
    { 23,  7, "SRV_LOGINKEY",        NULL },
    {  1,  2, "CLI_READY",           NULL},
    {  1,  6, "CLI_RATESREQUEST",    NULL},
    {  1,  8, "CLI_ACKRATES",        NULL},
    {  1, 12, "CLI_ACKSERVERPAUSE",  NULL},
    {  1, 14, "CLI_REQINFO",         NULL},
    {  1, 23, "CLI_FAMILIES",        NULL},
    {  1, 30, "CLI_SETSTATUS",       NULL},
    {  2,  2, "CLI_REQLOCATION",     NULL},
    {  2,  4, "CLI_SETUSERINFO",     NULL},
    {  2,  5, "CLI_REQUSERINFO",     NULL},
    {  3,  2, "CLI_REQBUDDY",        NULL},
    {  3,  4, "CLI_ADDCONTACT",      NULL},
    {  3,  5, "CLI_REMCONTACT",      NULL},
    {  4,  2, "CLI_SETICBM",         NULL},
    {  4,  4, "CLI_REQICBM",         NULL},
    {  4,  6, "CLI_SENDMSG",         NULL},
    {  9,  2, "CLI_REQBOS",          NULL},
    {  9,  5, "CLI_ADDVISIBLE",      NULL},
    {  9,  6, "CLI_REMVISIBLE",      NULL},
    {  9,  7, "CLI_ADDINVISIBLE",    NULL},
    {  9,  8, "CLI_REMINVISIBLE",    NULL},
    { 19,  2, "CLI_REQLISTS",        NULL},
    { 19,  4, "CLI_REQROSTER",       NULL},
    { 19,  5, "CLI_CHECKROSTER",     NULL},
    { 19,  7, "CLI_ROSTERACK",       NULL},
    { 19,  8, "CLI_ROSTERADD",       NULL},
    { 19,  9, "CLI_ROSTERUPDATE",    NULL},
    { 19, 10, "CLI_ROSTERDELETE",    NULL},
    { 19, 17, "CLI_ADDSTART",        NULL},
    { 19, 18, "CLI_ADDEND",          NULL},
    { 19, 20, "CLI_GRANTAUTH?",      NULL},
    { 19, 24, "CLI_REQAUTH",         NULL},
    { 19, 26, "CLI_AUTHORIZE",       NULL},
    { 21,  2, "CLI_TOICQSRV",        NULL},
    { 23,  2, "CLI_MD5LOGIN",        NULL},
    { 23,  4, "CLI_REGISTERUSER",    NULL},
    { 23,  6, "CLI_REQLOGIN",        NULL},
    {  0,  0, "_unknown",             NULL}
};

/*
 * Returns the name of the SNAC, or "unknown".
 */
const char *SnacName (UWORD fam, UWORD cmd)
{
    SNAC *s;

    for (s = SNACS; s->fam; s++)
        if (s->fam == fam && s->cmd == cmd)
            return strchr (s->name, '_') + 1;
    return s->name;
}

/*
 * Print a given SNAC.
 */
void SnacPrint (Packet *pak, int out)
{
    UWORD fam, cmd, flag, opos = pak->rpos;
    UDWORD ref, len;

    pak->rpos = 6;
    fam  = PacketReadB2 (pak);
    cmd  = PacketReadB2 (pak);
    flag = PacketReadB2 (pak);
    ref  = PacketReadB4 (pak);
    len  = PacketReadAtB2 (pak, pak->rpos);

    printf ("  %s %sSNAC     (%x,%x) [%s_%s] flags %x ref %lx",
             s_dumpnd (pak->data + 6, flag & 0x8000 ? 10 + len + 2 : 10),
             COLDEBUG, fam, cmd,
             out ? "CLI" : "SRV",
             SnacName (fam, cmd), flag, ref);
    if (flag & 0x8000)
    {
        printf ("   extra (%ld)", len);
        pak->rpos += len + 2;
    }
    printf ("%s\n", COLNONE);

    {
        char *f, *syn = strdup (s_sprintf ("gs%dx%ds", fam, cmd));
        f = PacketDump (pak, syn, COLDEBUG, COLNONE);
        free (syn);
        printf ("%s", s_ind (f));
        free (f);
    }

    pak->rpos = opos;
}

void FlapPrint (Packet *pak, int out)
{
    UWORD opos = pak->rpos;
    UBYTE ch;
    UWORD seq, len;

    pak->rpos = 1;
    ch  = PacketRead1 (pak);
    seq = PacketReadB2 (pak);
    len = PacketReadB2 (pak);

    printf ("%s\n  %s %sFLAP %s ch %d seq %08x length %04x%s\n",
              COLNONE, s_dumpnd (pak->data, 6), COLDEBUG,
              out ? ">>>" : "<<<",
              ch, seq, len, COLNONE);

    if (ch == 2)
        SnacPrint (pak, out);
    else
        printf ("%s", s_ind (s_dump (pak->data + 6, pak->len - 6)));

    pak->rpos = opos;
}


int main (int argc, char **argv)
{
    strc_t line;
    
    ConvInit ();
    Packet *pak[2];
    FILE *f = stdin;
    int nooffset = 0;
    int out = 0;
    const char *l = NULL;
    
    pak[0] = PacketC ();
    pak[1] = PacketC ();

    if (argc > 2 || (argc > 1 && (!strcmp (argv[1], "--help") || !strcmp (argv[1], "-h"))))
    {
        printf ("Usage: %s\n", argv[0]);
        printf ("  Read wireshark saved packet dump and convert into readable dump.\n");
        printf ("  Dump is expected on stdin and written to stdout.\n");
        exit (1);
    }
    if (argc > 1)
    {
        f = fopen (argv[1], "r");
        assert (f);
    }
    
    while ((line = UtilIOReadline (f)))
    {
        l = line->txt;
        if (!nooffset && !out)
            out = 2 + (*l == ' ' ? 0 : 1);
        int i;
        UDWORD hex;
        if (!*l)
            continue;
        if (!strncmp (l, "Incoming", 8) || !strncmp (l, "Outgoing", 8))
        {
            if (PacketWritePos (pak[out&1]) > 0)
            {
                printf (out&1 ? "Outgoing partial v8 server packet:" : "Incoming partial v8 server packet:");
                pak[out&1]->len = PacketReadAtB2 (pak[out&1], 4) + 6;
                FlapPrint (pak[out&1], out&1);
                pak[out&1]->wpos = 0;
                pak[out&1]->rpos = 0;
                pak[out&1]->len = 0;
            }
            nooffset = 1;
            out = 2 + !strncmp (l, "Outgoing", 8);
            continue;
        }
        if (strstr (l, "bytes) received:") || strstr (l, "bytes) sent:"))
        {
            if (PacketWritePos (pak[out&1]) > 0)
            {
                printf (out&1 ? "Outgoing partial v8 server packet:" : "Incoming partial v8 server packet:");
                pak[out&1]->len = PacketReadAtB2 (pak[out&1], 4) + 6;
                FlapPrint (pak[out&1], out&1);
                pak[out&1]->wpos = 0;
                pak[out&1]->rpos = 0;
                pak[out&1]->len = 0;
            }
            out = 2 + (strstr (l, "bytes) sent:") ? 1 : 0);
            continue;
        }
        while (*l == ' ')
            l++;
        while (!nooffset && strchr ("0123456789abcdefABCDEF", *l))
            l++;
        if (*l == ':')
            l++;
        for (i = 0; i < 16; i++)
        {
            if (*l == ' ') l++;
            if (*l == ' ') l++;
            if (!*l || !strchr ("0123456789abcdefABCDEF", *l))
                break;
            if      (*l >= '0' && *l <= '9')  hex = *l - '0';
            else if (*l >= 'a' && *l <= 'f')  hex = *l - 'a' + 10;
            else if (*l >= 'A' && *l <= 'F')  hex = *l - 'A' + 10;
            else assert (0);
            hex *= 16;
            l++;
            if      (*l >= '0' && *l <= '9')  hex += *l - '0';
            else if (*l >= 'a' && *l <= 'f')  hex += *l - 'a' + 10;
            else if (*l >= 'A' && *l <= 'F')  hex += *l - 'A' + 10;
            else assert (0);
            l++;
            PacketWrite1 (pak[out&1], hex);
            if (PacketWritePos (pak[out&1]) >= 6)
            {
                UWORD l = PacketReadAtB2 (pak[out&1], 4);
                if (PacketWritePos (pak[out&1]) >= 6 + l)
                {
                    printf (out&1 ? "Outgoing v8 server packet:" : "Incoming v8 server packet:");
                    
                    FlapPrint (pak[out&1], out&1);
                    pak[out&1]->wpos = 0;
                    pak[out&1]->rpos = 0;
                    pak[out&1]->len = 0;
                }
            }
        }
    }
    return 0;
}
