/*
Copyright 2001-2018 John Wiseman G8BPQ

This file is part of LinBPQ/BPQ32.

LinBPQ/BPQ32 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, either version 3 of the License, or
(at your option) any later version.

LinBPQ/BPQ32 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 LinBPQ/BPQ32.  If not, see http://www.gnu.org/licenses
*/	

// Mail and Chat Server for BPQ32 Packet Switch
//
// lzhuf Routines

#include "bpqmail.h"

BOOL isAMPRMsg(char * Addr);

struct Country * FindCountry(char * Name);
struct UserInfo * FindAMPR();

/**************************************************************
        lzhuf.c
    written by Haruyasu Yoshizaki 1988/11/20
    some minor changes 1989/04/06
    comments translated by Haruhiko Okumura 1989/04/07
    getbit and getbyte modified 1990/03/23 by Paul Edwards
      so that they would work on machines where integers are
      not necessarily 16 bits (although ANSI guarantees a
      minimum of 16).  This program has compiled and run with
      no errors under Turbo C 2.0, Power C, and SAS/C 4.5
      (running on an IBM mainframe under MVS/XA 2.2).  Could
      people please use YYYY/MM/DD date format so that everyone
      in the world can know what format the date is in?
    external storage of filesize changed 1990/04/18 by Paul Edwards to
      Intel's "little endian" rather than a machine-dependant style so
      that files produced on one machine with lzhuf can be decoded on
      any other.  "little endian" style was chosen since lzhuf
      originated on PC's, and therefore they should dictate the
      standard.
    initialization of something predicting spaces changed 1990/04/22 by
      Paul Edwards so that when the compressed file is taken somewhere
      else, it will decode properly, without changing ascii spaces to
      ebcdic spaces.  This was done by changing the ' ' (space literal)
      to 0x20 (which is the far most likely character to occur, if you
      don't know what environment it will be running on.
**************************************************************/
	   
int ReformatSyncMessage(CIRCUIT * conn);

#define int __int16

/* crctab calculated by Mark G. Mendel, Network Systems Corporation */

UCHAR  *infile, *outfile, * endinfile;

short Get()
{
	if (infile == endinfile)
		return 	-1;
	else
		return *(infile++);
}

static unsigned short crctab[256] = {
    0x0000,  0x1021,  0x2042,  0x3063,  0x4084,  0x50a5,  0x60c6,  0x70e7,
    0x8108,  0x9129,  0xa14a,  0xb16b,  0xc18c,  0xd1ad,  0xe1ce,  0xf1ef,
    0x1231,  0x0210,  0x3273,  0x2252,  0x52b5,  0x4294,  0x72f7,  0x62d6,
    0x9339,  0x8318,  0xb37b,  0xa35a,  0xd3bd,  0xc39c,  0xf3ff,  0xe3de,
    0x2462,  0x3443,  0x0420,  0x1401,  0x64e6,  0x74c7,  0x44a4,  0x5485,
    0xa56a,  0xb54b,  0x8528,  0x9509,  0xe5ee,  0xf5cf,  0xc5ac,  0xd58d,
    0x3653,  0x2672,  0x1611,  0x0630,  0x76d7,  0x66f6,  0x5695,  0x46b4,
    0xb75b,  0xa77a,  0x9719,  0x8738,  0xf7df,  0xe7fe,  0xd79d,  0xc7bc,
    0x48c4,  0x58e5,  0x6886,  0x78a7,  0x0840,  0x1861,  0x2802,  0x3823,
    0xc9cc,  0xd9ed,  0xe98e,  0xf9af,  0x8948,  0x9969,  0xa90a,  0xb92b,
    0x5af5,  0x4ad4,  0x7ab7,  0x6a96,  0x1a71,  0x0a50,  0x3a33,  0x2a12,
    0xdbfd,  0xcbdc,  0xfbbf,  0xeb9e,  0x9b79,  0x8b58,  0xbb3b,  0xab1a,
    0x6ca6,  0x7c87,  0x4ce4,  0x5cc5,  0x2c22,  0x3c03,  0x0c60,  0x1c41,
    0xedae,  0xfd8f,  0xcdec,  0xddcd,  0xad2a,  0xbd0b,  0x8d68,  0x9d49,
    0x7e97,  0x6eb6,  0x5ed5,  0x4ef4,  0x3e13,  0x2e32,  0x1e51,  0x0e70,
    0xff9f,  0xefbe,  0xdfdd,  0xcffc,  0xbf1b,  0xaf3a,  0x9f59,  0x8f78,
    0x9188,  0x81a9,  0xb1ca,  0xa1eb,  0xd10c,  0xc12d,  0xf14e,  0xe16f,
    0x1080,  0x00a1,  0x30c2,  0x20e3,  0x5004,  0x4025,  0x7046,  0x6067,
    0x83b9,  0x9398,  0xa3fb,  0xb3da,  0xc33d,  0xd31c,  0xe37f,  0xf35e,
    0x02b1,  0x1290,  0x22f3,  0x32d2,  0x4235,  0x5214,  0x6277,  0x7256,
    0xb5ea,  0xa5cb,  0x95a8,  0x8589,  0xf56e,  0xe54f,  0xd52c,  0xc50d,
	0x34e2,  0x24c3,  0x14a0,  0x0481,  0x7466,  0x6447,  0x5424,  0x4405,
	0xa7db,  0xb7fa,  0x8799,  0x97b8,  0xe75f,  0xf77e,  0xc71d,  0xd73c,
	0x26d3,  0x36f2,  0x0691,  0x16b0,  0x6657,  0x7676,  0x4615,  0x5634,
	0xd94c,  0xc96d,  0xf90e,  0xe92f,  0x99c8,  0x89e9,  0xb98a,  0xa9ab,
	0x5844,  0x4865,  0x7806,  0x6827,  0x18c0,  0x08e1,  0x3882,  0x28a3,
	0xcb7d,  0xdb5c,  0xeb3f,  0xfb1e,  0x8bf9,  0x9bd8,  0xabbb,  0xbb9a,
	0x4a75,  0x5a54,  0x6a37,  0x7a16,  0x0af1,  0x1ad0,  0x2ab3,  0x3a92,
	0xfd2e,  0xed0f,  0xdd6c,  0xcd4d,  0xbdaa,  0xad8b,  0x9de8,  0x8dc9,
	0x7c26,  0x6c07,  0x5c64,  0x4c45,  0x3ca2,  0x2c83,  0x1ce0,  0x0cc1,
	0xef1f,  0xff3e,  0xcf5d,  0xdf7c,  0xaf9b,  0xbfba,  0x8fd9,  0x9ff8,
	0x6e17,  0x7e36,  0x4e55,  0x5e74,  0x2e93,  0x3eb2,  0x0ed1,  0x1ef0
};


#define updcrc(cp, crc) ((crc << 8) ^ crctab[(cp & 0xff) ^ (crc >> 8)])

uint32_t textsize = 0, codesize = 0;
unsigned short crc;
int	version_1;
char wterr[] = "Can't write.";

void Error(char *message)
{
        printf("\n%s\n", message);
        exit(EXIT_FAILURE);
}

/********** LZSS compression **********/

#define N               2048    /* buffer size */
#define F               60      /* lookahead buffer size */
#define THRESHOLD       2
#define NIL             N       /* leaf of tree */

unsigned char
                text_buf[N + F - 1];
static int     match_position, match_length,
                lson[N + 1], rson[N + 257], dad[N + 1];

static int crc_fputc(unsigned short c)
{
	crc = updcrc(c, crc);
	*(outfile++) = (unsigned char)c;
	return 0;
}

short crc_fgetc()
{
	short retour = *(infile++);

	return(retour);
}

void InitTree(void)  /* initialize trees */
{
		int  i;

        for (i = N + 1; i <= N + 256; i++)
                rson[i] = NIL;                  /* root */
        for (i = 0; i < N; i++)
                dad[i] = NIL;                   /* node */
}

static void InsertNode(int r)  /* insert to tree */
{
    int  i, p, cmp;
        unsigned char  *key;
        unsigned c;

        cmp = 1;
        key = &text_buf[r];
        p = N + 1 + key[0];
        rson[r] = lson[r] = NIL;
        match_length = 0;
        for ( ; ; ) {
                if (cmp >= 0) {
                        if (rson[p] != NIL)
                                p = rson[p];
                        else {
                                rson[p] = r;
                                dad[r] = p;
                                return;
                        }
                } else {
                        if (lson[p] != NIL)
                                p = lson[p];
                        else {
                                lson[p] = r;
                                dad[r] = p;
                                return;
                        }
                }
                for (i = 1; i < F; i++)
                        if ((cmp = key[i] - text_buf[p + i]) != 0)
                                break;
                if (i > THRESHOLD) {
                        if (i > match_length) {
                                match_position = ((r - p) & (N - 1)) - 1;
                                if ((match_length = i) >= F)
                                        break;
                        }
                        if (i == match_length) {
                if ((c = ((r - p) & (N-1)) - 1) < (unsigned)match_position) {
                                        match_position = c;
                                }
                        }
                }
        }
        dad[r] = dad[p];
        lson[r] = lson[p];
        rson[r] = rson[p];
        dad[lson[p]] = r;
        dad[rson[p]] = r;
        if (rson[dad[p]] == p)
                rson[dad[p]] = r;
        else
                lson[dad[p]] = r;
        dad[p] = NIL;  /* remove p */
}

static void DeleteNode(int p)  /* remove from tree */
{
    int  q;

        if (dad[p] == NIL)
                return;                 /* not registered */
        if (rson[p] == NIL)
                q = lson[p];
        else
        if (lson[p] == NIL)
                q = rson[p];
        else {
                q = lson[p];
                if (rson[q] != NIL) {
                        do {
                                q = rson[q];
                        } while (rson[q] != NIL);
                        rson[dad[q]] = lson[q];
                        dad[lson[q]] = dad[q];
                        lson[q] = lson[p];
                        dad[lson[p]] = q;
                }
                rson[q] = rson[p];
                dad[rson[p]] = q;
        }
        dad[q] = dad[p];
        if (rson[dad[p]] == p)
                rson[dad[p]] = q;
        else
                lson[dad[p]] = q;
        dad[p] = NIL;
}

/* Huffman coding */

#define N_CHAR          (256 - THRESHOLD + F)
                                /* kinds of characters (character code = 0..N_CHAR-1) */
#define T               (N_CHAR * 2 - 1)        /* size of table */
#define R               (T - 1)                 /* position of root */
#define MAX_FREQ        0x8000          /* updates tree when the */
                                        /* root frequency comes to this value. */
typedef unsigned char uchar;


/* table for encoding and decoding the upper 6 bits of position */

/* for encoding */
uchar p_len[64] = {
        0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05,
        0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06,
        0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
        0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
        0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
        0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
        0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
        0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08
};

uchar p_code[64] = {
        0x00, 0x20, 0x30, 0x40, 0x50, 0x58, 0x60, 0x68,
        0x70, 0x78, 0x80, 0x88, 0x90, 0x94, 0x98, 0x9C,
        0xA0, 0xA4, 0xA8, 0xAC, 0xB0, 0xB4, 0xB8, 0xBC,
        0xC0, 0xC2, 0xC4, 0xC6, 0xC8, 0xCA, 0xCC, 0xCE,
        0xD0, 0xD2, 0xD4, 0xD6, 0xD8, 0xDA, 0xDC, 0xDE,
        0xE0, 0xE2, 0xE4, 0xE6, 0xE8, 0xEA, 0xEC, 0xEE,
        0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
        0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
};

/* for decoding */
uchar d_code[256] = {
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
        0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
        0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
        0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
        0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
        0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
        0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
        0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
        0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
        0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
        0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
        0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A,
        0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
        0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D,
        0x0E, 0x0E, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F,
        0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11,
        0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13,
        0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15,
        0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17,
        0x18, 0x18, 0x19, 0x19, 0x1A, 0x1A, 0x1B, 0x1B,
        0x1C, 0x1C, 0x1D, 0x1D, 0x1E, 0x1E, 0x1F, 0x1F,
        0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23,
        0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27,
        0x28, 0x28, 0x29, 0x29, 0x2A, 0x2A, 0x2B, 0x2B,
        0x2C, 0x2C, 0x2D, 0x2D, 0x2E, 0x2E, 0x2F, 0x2F,
        0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
        0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
};

uchar d_len[256] = {
        0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
        0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
        0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
        0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
        0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
        0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
        0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
        0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
        0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
        0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
        0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
        0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
        0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
        0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
        0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
        0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
        0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
        0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
        0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
        0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
        0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
        0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
        0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
        0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
        0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
        0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
        0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
        0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
        0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
        0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
        0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
        0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
};

unsigned int freq[T + 1]; /* frequency table */

int prnt[T + N_CHAR]; /* pointers to parent nodes, except for the */
                        /* elements [T..T + N_CHAR - 1] which are used to get */
                        /* the positions of leaves corresponding to the codes. */

int son[T];   /* pointers to child nodes (son[], son[] + 1) */

unsigned int getbuf = 0;
uchar getlen = 0;

static int GetBit(void)    /* get one bit */
{
    unsigned int i;

        while (getlen <= 8) {
				if ((i = crc_fgetc()) < 0) i = 0;
                getbuf |= i << (8 - getlen);
                getlen += 8;
        }
        i = getbuf;
        getbuf <<= 1;
        getlen--;
    return (int)((i & 0x8000) >> 15);
}

static int GetByte(void)   /* get one byte */
{
    unsigned int i;

        while (getlen <= 8) {
				if ((i = crc_fgetc()) == 0xffff) i = 0;
                getbuf |= i << (8 - getlen);
                getlen += 8;
        }
        i = getbuf;
        getbuf <<= 8;
        getlen -= 8;
    return (int)((i & 0xff00) >> 8);
}

unsigned int putbuf = 0;
uchar putlen = 0;

static void Putcode(int l, unsigned c)     /* output c bits of code */
{
        putbuf |= c >> putlen;
        if ((putlen += l) >= 8) {
				if (crc_fputc(putbuf >> 8) == EOF) {
                        Error(wterr);
                }
                if ((putlen -= 8) >= 8) {
						if (crc_fputc(putbuf) == EOF) {
                                Error(wterr);
                        }
                        codesize += 2;
                        putlen -= 8;
                        putbuf = c << (l - putlen);
                } else {
                        putbuf <<= 8;
                        codesize++;
                }
        }
}


/* initialization of tree */

int StartHuff(void)
{
	int i, j;

	for (i = 0; i < N_CHAR; i++) {
		freq[i] = 1;
		son[i] = i + T;
		prnt[i + T] = i;
	}
	i = 0; j = N_CHAR;
	while (j <= R) {
		freq[j] = freq[i] + freq[i + 1];
		son[j] = i;
		prnt[i] = prnt[i + 1] = j;
		i += 2; j++;
	}
	freq[T] = 0xffff;
	prnt[R] = 0;

	return 0;
}

/* reconstruction of tree */

static void reconst(void)
{
    int i, j, k;
    unsigned int f, l;

        /* collect leaf nodes in the first half of the table */
        /* and replace the freq by (freq + 1) / 2. */
        j = 0;
        for (i = 0; i < T; i++) {
                if (son[i] >= T) {
                        freq[j] = (freq[i] + 1) / 2;
                        son[j] = son[i];
                        j++;
                }
        }
        /* begin constructing tree by connecting sons */
        for (i = 0, j = N_CHAR; j < T; i += 2, j++) {
                k = i + 1;
                f = freq[j] = freq[i] + freq[k];
                for (k = j - 1; f < freq[k]; k--);
                k++;
                l = (j - k) * 2;
                memmove(&freq[k + 1], &freq[k], l);
                freq[k] = f;
                memmove(&son[k + 1], &son[k], l);
                son[k] = i;
        }
        /* connect prnt */
        for (i = 0; i < T; i++) {
                if ((k = son[i]) >= T) {
                        prnt[k] = i;
                } else {
                        prnt[k] = prnt[k + 1] = i;
                }
        }
}


/* increment frequency of given code by one, and update tree */

static void update(int c)
{
	int i, j, l;
	unsigned int k;

	if (freq[R] == MAX_FREQ) {
		reconst();
	}
	c = prnt[c + T];
	do {
		k = ++freq[c];

		/* if the order is disturbed, exchange nodes */

		l = c + 1;

		if ((unsigned)k > freq[l]) 
		{
			while ((unsigned)k > freq[++l]);
			l--;
			freq[c] = freq[l];
			freq[l] = k;

			i = son[c];
			prnt[i] = l;
			if (i < T) prnt[i + 1] = l;

			j = son[l];
			son[l] = i;

			prnt[j] = c;
			if (j < T) prnt[j + 1] = c;
			son[c] = j;

			c = l;
		}
	} while ((c = prnt[c]) != 0);   /* repeat up to root */
}

unsigned code, len;

static void EncodeChar(unsigned int c)
{
	unsigned int i;
	int j, k;

	i = 0;
	j = 0;
	k = prnt[c + T];

	/* travel from leaf to root */
	do {
		i >>= 1;

		/* if node's address is odd-numbered, choose bigger brother node */
		if (k & 1) i += 0x8000;

		j++;
	} while ((k = prnt[k]) != R);
	Putcode(j, i);
	code = i;
	len = j;
	update(c);
}

static void EncodePosition(unsigned int c)
{
	unsigned int i;

	/* output upper 6 bits by table lookup */
	i = c >> 6;
	Putcode(p_len[i], (unsigned)p_code[i] << 8);

	/* output lower 6 bits verbatim */
	Putcode(6, (c & 0x3f) << 10);
}

static void EncodeEnd(void)
{
	if (putlen) {
		if (crc_fputc(putbuf >> 8) == EOF) {
			Error(wterr);
		}
		codesize++;
	}
}

int DecodeChar(void)
{
	unsigned int c;

	c = son[R];

	/* travel from root to leaf, */
	/* choosing the smaller child node (son[]) if the read bit is 0, */
	/* the bigger (son[]+1} if 1 */
	while (c < T) {
		c += GetBit();
		c = son[c];
	}
	c -= T;
	update(c);
	return (int)c;
}

int DecodePosition(void)
{
	unsigned int i, j, c;

	/* recover upper 6 bits from table */
	i = GetByte();
	c = (unsigned)d_code[i] << 6;
	j = d_len[i];

	/* read lower 6 bits verbatim */
	j -= 2;
	while (j--) {
		i = (i << 1) + GetBit();
	}
	return (int)(c | (i & 0x3f));
}

/* compression */

int32_t Encode(char * in, char * out, int32_t inlen, BOOL B1Protocol, int Compress)
{
	int  i, c, len, r, s, last_match_length;
	unsigned char *ptr;

	putbuf = 0;
	putlen = 0;
	textsize = 0;
	codesize = 0;

	crc = 0;
	outfile = out;

	if (B1Protocol)
		outfile+=2;				// Space for CRC

//	infile = &conn->MailBuffer[2];

	textsize = inlen;

	ptr = (char *)&textsize;

#ifdef __BIG_ENDIAN__
	crc_fputc(*(ptr+3));
	crc_fputc(*(ptr+2));
	crc_fputc(*(ptr+1));
	crc_fputc(*(ptr));
#else
	crc_fputc(*(ptr++));
	crc_fputc(*(ptr++));
	crc_fputc(*(ptr++));
	crc_fputc(*(ptr++));	
#endif

	if (textsize == 0)
		return 0;

	infile = in;
	endinfile = infile + inlen;
	textsize = 0;                   /* rewind and re-read */

	// if using uncompressed just copy in to out
	// may not be optimum but is simple!

	if (Compress)
	{
		StartHuff();
		InitTree();
		s = 0;
		r = N - F;
		for (i = s; i < r; i++)
			text_buf[i] = 0x20;
		for (len = 0; len < F && (c = Get()) != EOF; len++)
			text_buf[r + len] = (unsigned char)c;
		textsize = len;
		for (i = 1; i <= F; i++)
			InsertNode(r - i);
		InsertNode(r);
	
		do
		{
			if (match_length > len)
				match_length = len;
			if (match_length <= THRESHOLD)
			{
				match_length = 1;
				EncodeChar(text_buf[r]);
			}
			else
			{
				EncodeChar(255 - THRESHOLD + match_length);
				EncodePosition(match_position);
			}
			last_match_length = match_length;
		
			for (i = 0; i < last_match_length && (c = Get()) != EOF; i++)
			{
				DeleteNode(s);
			    text_buf[s] = (unsigned char)c;

				if (s < F - 1)
					text_buf[s + N] = (unsigned char)c;
				s = (s + 1) & (N - 1);
				r = (r + 1) & (N - 1);
				InsertNode(r);
			}

			while (i++ < last_match_length)
			{
				DeleteNode(s);
				s = (s + 1) & (N - 1);
				r = (r + 1) & (N - 1);
				if (--len) InsertNode(r);
			}
		}
		while (len > 0);

		EncodeEnd();
	}
	else
	{
		int32_t  n = inlen;
		while (n--)
			crc_fputc(*(infile++));
	
		codesize += inlen;
	}

	if (B1Protocol)
	{
		out[0] = crc & 0xff;
		out[1]= crc >> 8;
		codesize+=2;
	}

	codesize += 4;

	if (Compress)
		Logprintf(LOG_BBS, NULL, '|', "Compressed Message Comp Len %d Msg Len %d CRC %x", 
			codesize, inlen, crc);
	else
		Logprintf(LOG_BBS, NULL, '|', "Uncompressed Message Sent Len %d Msg Len %d CRC %x", 
			codesize, inlen, crc);

	return codesize;
}

BOOL CheckifPacket(char * Via)
{
	char * ptr1, * ptr2;
	
	// Message addressed to a non-winlink address
	// Need to see if real smtp, or a packet address

	// No . in address assume Packet - g8bpq@g8bpq

	ptr1 = strchr(Via, '.');

	if (ptr1 == NULL)
		return TRUE;			// Packet

	// Find Last Element

	ptr2 = strchr(++ptr1, '.');

	while (ptr2)
	{
		ptr1 = ptr2;
		ptr2 = strchr(++ptr1, '.');
	}

	if (ptr1[0] == 0)
		return TRUE;			// Packet

	// ptr1 is last element. If a valid continent, it is a packet message
	// should really accept .WW on end as it is valid 
	if (FindContinent(ptr1))
		return TRUE;			// Packet

	if (FindCountry(ptr1))
		return TRUE;			// Packet

	if ((_stricmp(ptr1, "MARS") == 0) || (_stricmp(ptr1, "USA") == 0) || (_stricmp(ptr1, "WW") == 0))		// MARS used both MARS and USA
		return TRUE;			// Packet

	return FALSE;
}

void Decode(CIRCUIT * conn, int FromSync)  
{
	unsigned char *ptr;
	char * StartofMsg;
	short  i, j, k, r;
	short c;
	uint32_t count;
	unsigned short  crc_read;
	int Index = 0;
	struct FBBHeaderLine * FBBHeader= &conn->FBBHeaders[0];	// The Headers from an FFB forward block
	BOOL NTS = FALSE;

	getbuf = 0;
	getlen = 0;
	textsize = 0;
	codesize = 0;

	infile = &conn->MailBuffer[0];

	crc = 0;

	if (conn->BBSFlags & FBBB1Mode)
	{
		short val;
		uint32_t n;
			
		crc_read = infile[0];
		crc_read |= infile[1] << 8;

		for (n = 2; n < conn->TempMsg->length; n++)
		{
			val = infile[n];
			crc = updcrc(val, crc);
		}
		if (crc != crc_read)
		{
			nodeprintf(conn, "*** Message CRC Error File %x Calc %x\r", crc_read, crc);
			free(conn->MailBuffer);
			conn->MailBufferSize=0;
			conn->MailBuffer=0;
			conn->CloseAfterFlush = 20;			// 2 Secs

			return;
		}

		infile+=2;
	}

	textsize = 0;
	ptr = (char *)&textsize;

#ifdef __BIG_ENDIAN__

	ptr[3] = (unsigned char)crc_fgetc();
	ptr[2] = (unsigned char)crc_fgetc();
	ptr[1] = (unsigned char)crc_fgetc();
	ptr[0] = (unsigned char)crc_fgetc();

#else

	for (i = 0 ; i < sizeof(textsize) ; i++)
		ptr[i] = (unsigned char)crc_fgetc();

#endif

	// Temp fix for duff MACBPQ (Message Length sent big-endian)

	if (textsize > 500000)
	{
		char x[4];
		char y[4];

		memcpy(x, &textsize, 4);
		y[0] = x[3];
		y[1] = x[2];
		y[2] = x[1];
		y[3] = x[0];

		memcpy(&textsize, y, 4);

		if (textsize > 5000000)
		{
			nodeprintf(conn, "*** Message Size Invalid %d\r", textsize);
			Debugprintf("*** Message Size Invalid %d\r", textsize);
			free(conn->MailBuffer);
			conn->MailBufferSize=0;
			conn->MailBuffer=0;
			conn->CloseAfterFlush = 20;			// 2 Secs
			return;
		}
	}

	Logprintf(LOG_BBS, conn, '|', "Uncompressing Message Comp Len %d Msg Len %d CRC %x", 
			conn->TempMsg->length, textsize, crc);
	
	outfile = zalloc(textsize + 10000);		// Lots of space for B2 header manipulations

	if (textsize == 0)
		return;

	// If compressed, decompress

	if (conn->BBSFlags & FBBCompressed)
	{
		StartHuff();
	
		for (i = 0; i < N - F; i++)
			text_buf[i] = 0x20;

		r = N - F;

		for (count = 0; count < textsize; )
		{
			c = DecodeChar();
			if (c < 256)
			{
				*(outfile++) = (unsigned char)c;
				text_buf[r++] = (unsigned char)c;
				r &= (N - 1);
				count++;
			} 
			else
			{
				i = (r - DecodePosition() - 1) & (N - 1);
				j = c - 255 + THRESHOLD;
				for (k = 0; k < j; k++)
				{
					c = text_buf[(i + k) & (N - 1)];
					*(outfile++) = (unsigned char)c;
				  text_buf[r++] = (unsigned char)c;
					r &= (N - 1);
					count++;
				}
			}
		}
		outfile -=count;
	}
	else
	{
		count = textsize;
		memcpy(outfile, infile, textsize);
	}

	free(conn->MailBuffer);
	conn->MailBuffer = outfile;
		
	conn->TempMsg->length = count;

	if (FromSync)
	{
		// Refomat Sync message as if from WLE
		
		if (ReformatSyncMessage(conn) == 0)
			return;

		FBBHeader->B2Message = TRUE;
		FBBHeader->MsgType = 'P';
	}

	if (!FBBHeader->B2Message)
	{
		// With B2 the Type is specified in the body, so can't update stats now
			
		if (FBBHeader->MsgType == 'P')
			Index = PMSG;
		else if (FBBHeader->MsgType == 'B')
			Index = BMSG;
		else if (FBBHeader->MsgType == 'T')
			Index = TMSG;

		conn->UserPointer->Total.MsgsReceived[Index]++;
		conn->UserPointer->Total.BytesForwardedIn[Index] += count;
	}


	if (FBBHeader->B2Message)
	{
		// Parse the Message for B2 From and To info
/*
MID: A3EDD4P00P55
Date: 2009/07/25 10:08
Type: Private
From: SMTP:john.wiseman@ntlworld.com
To: G8BPQ
Subject: RE: RMS Test Messaage
Mbo: SMTP
Body: 214
File: 3556 NOLA.XLS
File: 5566 NEWBOAT.HOMEPORT.JPG

*/
		UCHAR * ptr1, * ptr2, * ptr3;
		__int32 linelen, MsgLen = 0;
		struct MsgInfo * Msg = conn->TempMsg;
		time_t Date;
		char FullTo[100];
		char FullFrom[100];
		char ** RecpTo = NULL;				// May be several Recipients
		char ** HddrTo = NULL;				// May be several Recipients
		char ** Via = NULL;					// May be several Recipients
		__int32 LocalMsg[1000];				// Set if Recipient is a local wl2k address
		char Type[1000];					// Message Type for each dest
		__int32 B2To;						// Offset to To: fields in B2 header
		__int32 Recipients = 0;
		__int32 RMSMsgs = 0, BBSMsgs = 0;
#ifndef LINBPQ
		struct _EXCEPTION_POINTERS exinfo;

		__try {
#endif
		Msg->B2Flags |= B2Msg;

		// Display the whole header for debugging

/*
		ptr1 = strstr(outfile, "\r\n\r\n");

		if (ptr1)
		{
			*ptr1 = 0;
			Debugprintf("B2 Header = %s", outfile);
			*ptr1 = '\r';
		}
*/
		if (_stricmp(conn->Callsign, "RMS") == 0)
			Msg->B2Flags |= FromCMS;

		if (conn->RadioOnlyMode == 'T')
			Msg->B2Flags |= RadioOnlyMsg;
		else if (conn->RadioOnlyMode == 'R')
			Msg->B2Flags |= RadioOnlyFwd;

		ptr1 = outfile;
	Loop:
		ptr2 = strchr(ptr1, '\r');

		linelen = (int)(ptr2 - ptr1);

		if (_memicmp(ptr1, "From:", 5) == 0)
		{
			memcpy(FullFrom, ptr1, linelen);
			FullFrom[linelen] = 0;

			if (conn->Paclink || (conn->RMSExpress && (conn->UserPointer->flags & F_NOWINLINK) == 0))
			{
				// Messages just have the call - need to add @winlink.org

				strcpy(Msg->emailfrom, "@winlink.org");

			}
			if (_memicmp(&ptr1[6], "smtp:", 5) == 0)
			{
				if (_stricmp(conn->Callsign, "RMS") == 0)
				{
					// Swap smtp: to rms: and save originator so we can reply via RMS

					strcpy(Msg->from, "RMS:");
					memcpy(Msg->emailfrom, &ptr1[11], linelen - 11);
				}
				else
				{
					strcpy(Msg->from, "SMTP:");
					memcpy(Msg->emailfrom, &ptr1[11], linelen - 11);
				}
			}
			else
			{
				char SaveFrom[100];
				char * FromHA;

				// B2 From may now contain an @BBS 

				strcpy(SaveFrom, FullFrom);
				
				FromHA = strlop(SaveFrom, '@');

				// If it has an @ it could be an internet address - if it has a dot or longer than
				// 6 chars assume it is.
				// No, could also be packet

				// I don't think you can have an Internet message without a . in @field
				// Why not just use checkifpacket ??
					
				if (FromHA && !CheckifPacket(FromHA))
				{
					// Internet address - set from empty and full email in
					// emailfrom
					
					strcpy(Msg->from, "SMTP:");

					if (strlen(FullFrom) > 46)
						FullFrom[46] = 0;

					strcpy(Msg->emailfrom, &FullFrom[6]);
				}
				else
				{
					// Not an Internet Address so must be a callsign and therefore <= 6 chars

					if (strlen(SaveFrom) > 12)			
						SaveFrom[12] = 0;

					strcpy(Msg->from, &SaveFrom[6]);

					if (FromHA)
					{
						if (strlen(FromHA) > 39) FromHA[39] = 0;
						Msg->emailfrom[0] = '@';
						strcpy(&Msg->emailfrom[1], _strupr(FromHA));
					}
				}

				// Remove any SSID

				ptr3 = strchr(Msg->from, '-');
				if (ptr3) *ptr3 = 0;
			}

			// If from a CMS, and no @ in message, append @winlink.org to the B2 Header.
			// so messages passed via B2 know it is from Winlink

			if ((Msg->B2Flags & FromCMS) && strchr(FullFrom, '@') == NULL)
			{
				// Move Message down buffer - ptr2 is the insertion point

				memmove(ptr2+12, ptr2, count);
				memcpy(ptr2, "@winlink.org", 12);
				count += 12;
				conn->TempMsg->length += 12;

				// Also set Emailfrom, in case read on BBS (eg by outpost)
				
				strcat(Msg->emailfrom, "@winlink.org");
			}

		}
		else if (_memicmp(ptr1, "To:", 3) == 0 || _memicmp(ptr1, "cc:", 3) == 0)
		{
			int toLen;
			
			HddrTo=realloc(HddrTo, (Recipients+1) * sizeof(void *));
			HddrTo[Recipients] = zalloc(100);

			memset(FullTo, 0, 99);
			memcpy(FullTo, &ptr1[4], linelen-4);
			memcpy(HddrTo[Recipients], ptr1, linelen+2);
			LocalMsg[Recipients] = FALSE;
			Type[Recipients] = Msg->type;	// Default to Type from Header

			Logprintf(LOG_BBS, conn, '?', "B2 Msg To: %s", FullTo);

			conn->TempMsg->length -= (int)strlen(HddrTo[Recipients]);

			B2To = (int)(ptr1 - outfile);

			// if ending in AMPR.ORG send via ISP if we have enabled forwarding AMPR

			toLen = (int)strlen(FullTo);

			if (_memicmp(&FullTo[toLen - 8], "ampr.org", 8) == 0)
			{
				// if our domain keep here.
				
				// if not, and SendAMPRDirect set, set as ISP,
				// else set as RMS			
				
				memcpy(Msg->via, FullTo, toLen);

				ptr3 = strchr(FullTo, '@');

				if (ptr3)
				{
					ptr3++;

					if (_stricmp(ptr3, AMPRDomain) == 0)
					{
						// Our Message

						strcpy(Msg->via, FullTo);
						BBSMsgs ++;
						goto BBSMsg;
					}
				}

				if (SendAMPRDirect && FindAMPR())
				{
					strcpy(Msg->via, FullTo);
					strcpy(FullTo,"AMPR");
					BBSMsgs ++;
					goto BBSMsg;
				}
				
				strcpy(FullTo,"RMS");
				RMSMsgs ++;
			}

			if (conn->BPQBBS && !CheckifPacket(FullTo))  // May be an message for RMS being passed to an intermediate BBS
			{
				// Internet address - send via RMS

				strcpy(Msg->via, FullTo);
				strcpy(FullTo,"RMS");
				RMSMsgs ++;
			}
			else
			{
				ptr3 = strchr(FullTo, '@');

				if (ptr3)
				{
					*ptr3++ = 0;
					strcpy(Msg->via, ptr3);
				}
				else
					Msg->via[0] = 0;
			}
		
			if (conn->Paclink)
			{
				Msg->B2Flags |= FromPaclink;

				// Message from paclink

				// Messages to WL2K just have call.
				// Messages to email or BBS addresses have smtp:
			

				if (_memicmp(&ptr1[4], "SMTP:", 5) == 0)
				{
					// See if Packet or SMTP
					
					if (CheckifPacket(Msg->via))	//  If no RMS, don't check for routing to it)
					{
						// Packet Message

						memmove(FullTo, &FullTo[5], strlen(FullTo) - 4);
						_strupr(FullTo);
						_strupr(Msg->via);
						
						// Update the saved to: line (remove the smtp:)

						strcpy(&HddrTo[Recipients][4], &HddrTo[Recipients][9]);
						BBSMsgs++;

					}
					else
					{
						// Internet address - do we send via RMS??

						// ??? Need to see if RMS is available

						memcpy(Msg->via, &ptr1[9], linelen);
						Msg->via[linelen-9] = 0;
						strcpy(FullTo,"RMS");
						RMSMsgs ++;
					}
				}
				else
				{
					if (conn->RadioOnlyMode == 0 && (conn->UserPointer->flags & F_NOWINLINK) == 0)	// treat as Packet Address?
					{
						strcpy(Msg->via, "winlink.org");		// Message for WL2K - add via
						RMSMsgs ++;
						LocalMsg[Recipients] = CheckifLocalRMSUser(FullTo);
					}
					else
					{
						BBSMsgs++;
					}
				}

			}
//			else if (conn->RMSExpress && FindRMS())			//  If no RMS, don't check for routing to it
			else if (conn->RMSExpress)	
			{
				Msg->B2Flags |= FromRMSExpress;

				// Message from RMS Express
				// Messages to WL2K just have call.
				// Messages to email or BBS addresses don't have smtp:
			

				if (Msg->via[0])
				{
					// Has an @ - See if Packet or SMTP. If to our AMPR address, treat as packet
					
					if (CheckifPacket(Msg->via) || _stricmp(Msg->via, AMPRDomain) == 0)
					{
						// Packet Message

						_strupr(FullTo);
						_strupr(Msg->via);
						BBSMsgs++;
					}
					else
					{
						// Internet address - do we send via RMS??

						if (_memicmp(FullTo, "smtp/", 5) == 0 && ISP_Gateway_Enabled)
						{
							memcpy(Msg->via, &ptr1[9], linelen - 9);
							Msg->via[linelen-9] = 0;
							FullTo[0] = Msg->to[0] = 0;
						}
						else if (FindRMS()) // have RMS 
						{
							memcpy(Msg->via, &ptr1[4], linelen);
							Msg->via[linelen-4] = 0;
							strcpy(FullTo,"RMS");
							RMSMsgs ++;
						}
						else if (ISP_Gateway_Enabled)
						{
							memcpy(Msg->via, &ptr1[4], linelen);
							Msg->via[linelen-4] = 0;
							FullTo[0] = Msg->to[0] = 0;
						}
						else if (isAMPRMsg(Msg->via))
							strcpy(Msg->to, "RMS");		// Routing will redirect it
					}
				}
				else
				{
					if (conn->RadioOnlyMode == 0 && (conn->UserPointer->flags & F_NOWINLINK) == 0)	//  Dont default to winlink.org
					{
						strcpy(Msg->via, "winlink.org");		// Message for WL2K - add via
						RMSMsgs ++;
						LocalMsg[Recipients] = CheckifLocalRMSUser(FullTo);
					}
					else
						goto BBSMsg;
				}
			}

			else			// Not Paclink or RMS Express (or no RMS)
			{
				if (_memicmp(&ptr1[4], "SMTP:", 5) == 0)
				{
					// Airmail Sends MARS messages as SMTP
					
					if (CheckifPacket(Msg->via))
					{
						// Packet Message

						memmove(FullTo, &FullTo[5], strlen(FullTo) - 4);
						_strupr(FullTo);
						_strupr(Msg->via);
						
						// Update the saved to: line (remove the smtp:)

						strcpy(&HddrTo[Recipients][4], &HddrTo[Recipients][9]);
						BBSMsgs++;
						goto BBSMsg;
					}

					// If a winlink.org address we need to convert to call

					if (_stricmp(Msg->via, "winlink.org") == 0)
					{
						memmove(FullTo, &FullTo[5], strlen(FullTo) - 4);
						_strupr(FullTo);
						LocalMsg[Recipients] = CheckifLocalRMSUser(FullTo);
					}
					else
					{
						memcpy(Msg->via, &ptr1[9], linelen);
						Msg->via[linelen - 9] = 0;
						strcpy(FullTo,"RMS");
					}
//					FullTo[0] = 0;
				}
				else
				{
		BBSMsg:		
					_strupr(FullTo);
					_strupr(Msg->via);
				}
			}

			if (memcmp(FullTo, "RMS:", 4) == 0)
			{
				// remove RMS and add @winlink.org

				memmove(FullTo, &FullTo[4], strlen(FullTo) - 3);
				strcpy(Msg->via, "winlink.org");
				sprintf(HddrTo[Recipients], "To: %s\r\n", FullTo);
			}

			else if (memcmp(FullTo, "NTS:", 4) == 0)
			{
				// remove NTS and set type 'T'

				memmove(FullTo, &FullTo[4], strlen(FullTo) - 3);		
				Type[Recipients] = 'T';		// NTS
				memmove(HddrTo[Recipients] + 4, HddrTo[Recipients] + 8, 91);

				// Replace Type: Private with Type: Traffic

			}
			else if ((_memicmp(FullTo, "bull/", 5) == 0) || (_memicmp(FullTo, "bull:", 5) == 0))
			{
				// remove bull/ and set type 'B'

				memmove(FullTo, &FullTo[5], strlen(FullTo) - 4);		
				Type[Recipients] = 'B';		// Bulletin
				memmove(HddrTo[Recipients] + 4, HddrTo[Recipients] + 9, 90);

				// Replace Type: Private with Type: Bulletin
				// Have to move rest of header down to make space

			}

			if (strcmp(Msg->via, "RMS") == 0)
			{
				// replace RMS with @winlink.org

				strcpy(Msg->via, "winlink.org");
				sprintf(HddrTo[Recipients], "To: %s@winlink.org\r\n", FullTo);
			}

			if (strlen(FullTo) > 6)
				FullTo[6] = 0;

			strlop(FullTo, '-');

			strcpy(Msg->to, FullTo);

			if (SendBBStoSYSOPCall)
				if (_stricmp(FullTo, BBSName) == 0)
					strcpy(FullTo, SYSOPCall);

			if ((Msg->via[0] == 0 || strcmp(Msg->via, "BPQ") == 0 || strcmp(Msg->via, "BBS") == 0)
				&& (conn->Paclink || conn->RMSExpress))
			{
				// No routing - check @BBS and WP

				struct UserInfo * ToUser = LookupCall(FullTo);

				Msg->via[0] = 0;				// In case BPQ and not found

				if (ToUser)
				{
					// Local User. If Home BBS is specified, use it

					if (ToUser->HomeBBS[0])
					{
						strcpy(Msg->via, ToUser->HomeBBS); 
					}
				}
				else
				{
					WPRecP WP = LookupWP(FullTo);

					if (WP)
					{
						strcpy(Msg->via, WP->first_homebbs);
			
					}
				}

				// Fix To: address in B2 Header

				if (Msg->via[0])
					sprintf(HddrTo[Recipients], "To: %s@%s\r\n", FullTo, Msg->via);
				else
					sprintf(HddrTo[Recipients], "To: %s\r\n", FullTo);

			}

			RecpTo=realloc(RecpTo, (Recipients+1) * sizeof(void *));
			RecpTo[Recipients] = zalloc(10);

			Via=realloc(Via, (Recipients+1) * sizeof(void *));
			Via[Recipients] = zalloc(50);

			strcpy(Via[Recipients], Msg->via);
			strcpy(RecpTo[Recipients++], FullTo);

			// Remove the To: Line from the buffer

			memmove(ptr1, ptr2+2, count);
			goto Loop;
			
		}
		else if (_memicmp(ptr1, "Type:", 4) == 0)
		{
			if (ptr1[6] == 'N')
				Msg->type = 'T';				// NTS
			else
				Msg->type = ptr1[6];
		}
		else if (_memicmp(ptr1, "Body:", 4) == 0)
		{
			MsgLen = atoi(&ptr1[5]);
			StartofMsg = ptr1;
		}
		else if (_memicmp(ptr1, "File:", 5) == 0)
		{
			Msg->B2Flags |= Attachments;
		}
		else if (_memicmp(ptr1, "Date:", 5) == 0)
		{
			struct tm rtime;
			char seps[] = " ,\t\r";

			memset(&rtime, 0, sizeof(struct tm));

			// Date: 2009/07/25 10:08
	
			sscanf(&ptr1[5], "%04d/%02d/%02d %02d:%02d",
					&rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min);

			rtime.tm_year -= 1900;

			Date = mktime(&rtime) - (time_t)_MYTIMEZONE;
	
			if (Date == (time_t)-1)
				Date = time(NULL);

		}

		if (linelen)			// Not Null line
		{
			ptr1 = ptr2 + 2;		// Skip crlf
			goto Loop;
		}

		// Processed all headers


		// If multiple recipents, create one copy for each BBS address, and one for all others (via RMS)
	
		if (Recipients == 0 || HddrTo == NULL)
		{
			Debugprintf("B2 Message with no recipients from %s", conn->Callsign);	
			Logprintf(LOG_BBS, conn, '!', "B2 Message with no recipients from %s", conn->Callsign);
			SetupNextFBBMessage(conn);
			return;
		}
		else
		{
			__int32 i;
			struct MsgInfo * SaveMsg;
			char * SaveBody;
			__int32 SaveMsgLen = count;
			BOOL SentToRMS = FALSE;
			__int32 ToLen;
			char * ToString = malloc(Recipients * 100);
			char * bang;

			SaveMsg = Msg;
			SaveBody = conn->MailBuffer;

			// If from WL2K, create one message for each to: or cc: that is a local user

			if (Msg->B2Flags & FromCMS)
			{
				struct UserInfo * user;
				char Call[10];
				char * ptr;

				for (i = 0; i < Recipients; i++)
				{
					memcpy(Call, &HddrTo[i][4], 9);
					ptr = strchr(Call, 13);
					if (ptr)
						*ptr = 0;

					strlop(Call, '-');
					
					user = LookupCall(Call);

					if (user == 0)
						continue;

					if (strcmp(Call, BBSName) != 0)	// always accept to bbs call
						if ((user->flags & F_POLLRMS) == 0)
							continue;
					
					conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo));
					memcpy(Msg, SaveMsg, sizeof(struct MsgInfo));
	
					conn->MailBuffer = malloc(SaveMsgLen + 1000);
					memcpy(conn->MailBuffer, SaveBody, SaveMsgLen);

					// Add our To: 
		
					ToLen = (int)strlen(HddrTo[i]);

					if (_memicmp(HddrTo[i], "CC", 2) == 0)	// Replace CC: with TO:
						memcpy(HddrTo[i], "To", 2);

					memmove(&conn->MailBuffer[B2To + ToLen], &conn->MailBuffer[B2To], count);
					memcpy(&conn->MailBuffer[B2To], HddrTo[i], ToLen); 

					conn->TempMsg->length += ToLen;

					strcpy(Msg->to, RecpTo[i]);
					strcpy(Msg->via, Via[i]);
					Msg->type = Type[i];
				
					if (Recipients > 1)
						Msg->bid[0] = 0;

					CreateMessageFromBuffer(conn);
				}
			}
			else
			{
				// From a client - Create one copy with all RMS recipients, and one for each packet recipient	

				// Merge all RMS To: lines 

				// If don't add Winlink.org or no RMS BBS keep separate

			ToLen = 0;
			ToString[0] = 0;

			for (i = 0; i < Recipients; i++)
			{
				if (conn->RadioOnlyMode || LocalMsg[i] || (conn->UserPointer->flags & F_NOWINLINK) || FindRMS()== NULL)
				{
					LocalMsg[i] = TRUE;				// Make sure local copy is created
					continue;						// For a local RMS user
				}

				// ?? Should a Bang Path override this ?? - I think so!

				if (strchr(Via[i], '!'))
					continue;
				
				if (_stricmp(Via[i], "WINLINK.ORG") == 0 || _memicmp (&HddrTo[i][4], "SMTP:", 5) == 0 ||
					_stricmp(RecpTo[i], "RMS") == 0)
				{
					if (ToLen == 0)					// First Addr
						memcpy(HddrTo[i], "To", 2);	// In Case CC

					ToLen += (int)strlen(HddrTo[i]);
					strcat(ToString, HddrTo[i]);
				}
			}

			if (ToLen)
			{
				conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo));
				memcpy(Msg, SaveMsg, sizeof(struct MsgInfo));
	
				conn->MailBuffer = malloc(SaveMsgLen + 1000);
				memcpy(conn->MailBuffer, SaveBody, SaveMsgLen);

				// Add all the To: lines back to message

				memmove(&conn->MailBuffer[B2To + ToLen], &conn->MailBuffer[B2To], count);
				memcpy(&conn->MailBuffer[B2To], ToString, ToLen); 

				conn->TempMsg->length += ToLen;


				if (Recipients > 1)
				{
					strcpy(Msg->via, "winlink.org");
					strcpy(Msg->to, "RMS");
					Msg->bid[0] = 0;		// Must Change the BID
				}
				
				// Don't change type, as we don't change the B2 Header for messages to RMS
				//Msg->type = Type[0];
	
				CreateMessageFromBuffer(conn);
			}

			free(ToString);

			for (i = 0; i < Recipients; i++)
			{
				// Only Process Non - RMS Dests or local RMS Users

				// ?? Should a Bang Path override this ?? - I think so!

				if (strchr(Via[i], '!') == 0)
					if (LocalMsg[i] == 0)
						if (_stricmp (Via[i], "WINLINK.ORG") == 0 ||
							_memicmp (&HddrTo[i][4], "SMTP:", 5) == 0 ||
							_stricmp(RecpTo[i], "RMS") == 0)		
						continue;

				conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo));
				memcpy(Msg, SaveMsg, sizeof(struct MsgInfo));
	
				conn->MailBuffer = malloc(SaveMsgLen + 1000);
				memcpy(conn->MailBuffer, SaveBody, SaveMsgLen);

				// Add our To: 

				ptr = HddrTo[i];

				// We removed any nts: or bull: earlier on, 
				// and saved type. We need to set type here, as
				// may be sending to more than one type
				// If message contains Type: Private and not 'P',
				// need to changes to Traffic or Bulletin

				// But we need to remove last bang path if any

				if (strchr(ptr, '!'))
				{
					bang = ptr + strlen(ptr);
	
					while (*(--bang) != '!');		// Find last !

					*(bang++) = 13;					// Need CR;
					*(bang++) = 10;					// Need LF;
					*(bang) = 0;					// remove it;
				}

				ToLen = (int)strlen(ptr);

	//			if (_memicmp(HddrTo[i], "CC", 2) == 0)	// Replace CC: with TO:
					memcpy(HddrTo[i], "To", 2);

				memmove(&conn->MailBuffer[B2To + ToLen], &conn->MailBuffer[B2To], count);
				memcpy(&conn->MailBuffer[B2To], HddrTo[i], ToLen); 

				conn->TempMsg->length += ToLen;

				Msg->type = Type[i];

				ptr = strstr(conn->MailBuffer, "\nType: ");

				if (ptr)
				{
					char * ptrx;

					ptr += 7;

					// This handles a message arriving with bull/ or nts/ overrides

 					if (_memicmp(ptr, "Private", 7) == 0 && Msg->type != 'P')
					{
						if (Msg->type == 'T')
							memcpy(ptr, "Traffic", 7);
						else
						if (Msg->type == 'B')
						{
							// have to make space

							memmove(ptr + 1, ptr, count);
							conn->TempMsg->length++;
							memcpy(ptr, "Bulletin", 8);
						}

						// remove //wl2k from subject

						ptrx = strstr(conn->MailBuffer, "Subject: ");

						if (ptrx && _memicmp(ptrx + 9, "//WL2K ", 7) == 0)
						{
							memmove(ptrx + 9, ptrx + 16, count);
							conn->TempMsg->length -= 7;
							memmove(conn->TempMsg->title, &conn->TempMsg->title[7], strlen(conn->TempMsg->title) - 6);
						}
					}
					// if we are receiving from another BBS rather
					// than WLE or PAT we need to set msgtype from Type:

					Msg->type = ptr[0];		// I think it is safe to do it always
	
					if (Msg->type == 'N')
						Msg->type = 'T';
				}

				strcpy(Msg->to, RecpTo[i]);
				strcpy(Msg->via, Via[i]);
				
				if (i > 0 && Msg->type != 'B')			// Must Change the BID
					Msg->bid[0] = 0;

				// Update Stats

				if (Msg->type == 'P')
					Index = PMSG;
				else if (Msg->type == 'B')
					Index = BMSG;
				else if (Msg->type == 'T')
					Index = TMSG;

				conn->UserPointer->Total.MsgsReceived[Index]++;
				conn->UserPointer->Total.BytesForwardedIn[Index] += MsgLen;

				CreateMessageFromBuffer(conn);
			}
			}	// End not from RMS

			free(SaveMsg);
			free(SaveBody);
			conn->MailBuffer = NULL;
			conn->MailBufferSize=0;

			if (FromSync)
				return;

			SetupNextFBBMessage(conn);
			return;
		}

#ifndef LINBPQ
		}
			#define EXCEPTMSG "Error Decoding B2 Message"
			#include "StdExcept.c"

		BBSputs(conn, "*** Program Error Decoding B2 Message\r");
		Flush(conn);
		conn->CloseAfterFlush = 20;			// 2 Secs
		
		return;
		}
#endif	
	} // end if B2Msg

	CreateMessageFromBuffer(conn);
	SetupNextFBBMessage(conn);
}