diff --git a/APRSStdPages-HPLaptop.c b/APRSStdPages-HPLaptop.c new file mode 100644 index 0000000..7cb38e1 --- /dev/null +++ b/APRSStdPages-HPLaptop.c @@ -0,0 +1,3719 @@ + +#include +#include + +#ifndef WIN32 + +#define _stricmp stricmp +#define _strdup strdup + +int stricmp(const unsigned char * pStr1, const unsigned char *pStr2); + +#endif + +unsigned char aisblue_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x22, + 0x02, 0x03, 0x00, 0x00, 0x00, 0xcd, 0x06, 0x7a, 0x10, 0x00, 0x00, 0x00, + 0x09, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x23, 0xde, 0xb7, 0xa6, 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, + 0x4e, 0x53, 0x00, 0x40, 0xe6, 0xd8, 0x66, 0x00, 0x00, 0x00, 0x01, 0x62, + 0x4b, 0x47, 0x44, 0x00, 0x88, 0x05, 0x1d, 0x48, 0x00, 0x00, 0x00, 0x09, + 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, + 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, + 0x45, 0x07, 0xe6, 0x01, 0x09, 0x0a, 0x35, 0x03, 0x7a, 0x0e, 0x22, 0xdf, + 0x00, 0x00, 0x00, 0x53, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0x60, + 0x80, 0x01, 0x16, 0x3c, 0x94, 0x28, 0x84, 0x92, 0x44, 0xa1, 0x22, 0x1d, + 0xc0, 0x54, 0x16, 0x16, 0x8a, 0x31, 0x2b, 0x00, 0x4c, 0xad, 0x9a, 0x80, + 0x49, 0xb1, 0xae, 0x9a, 0x02, 0xa2, 0xd8, 0x56, 0x2d, 0x41, 0xa6, 0x96, + 0x21, 0xf3, 0xc4, 0x56, 0x2d, 0x05, 0x51, 0x52, 0xab, 0x56, 0x12, 0xa2, + 0xa2, 0x56, 0xad, 0x04, 0x59, 0x98, 0xb5, 0x6a, 0x15, 0x06, 0xc5, 0x08, + 0xa4, 0x02, 0x40, 0xb6, 0xae, 0x02, 0xd9, 0xcb, 0x18, 0x1a, 0x1a, 0x1a, + 0xc0, 0x00, 0x00, 0xd1, 0xed, 0x24, 0x45, 0x35, 0x85, 0xab, 0x6c, 0x00, + 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; +unsigned int aisblue_png_len = 227; +unsigned char aisgreen_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x22, + 0x02, 0x03, 0x00, 0x00, 0x00, 0xcd, 0x06, 0x7a, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x50, 0x4c, 0x54, 0x45, 0x61, 0x62, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x13, 0xfe, 0x00, 0x1b, 0x46, 0xca, 0x60, 0x00, 0x00, 0x00, + 0x01, 0x74, 0x52, 0x4e, 0x53, 0x00, 0x40, 0xe6, 0xd8, 0x66, 0x00, 0x00, + 0x00, 0x01, 0x62, 0x4b, 0x47, 0x44, 0x00, 0x88, 0x05, 0x1d, 0x48, 0x00, + 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, + 0x00, 0x0b, 0x13, 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x00, 0x07, + 0x74, 0x49, 0x4d, 0x45, 0x07, 0xe6, 0x01, 0x09, 0x0a, 0x35, 0x11, 0x89, + 0xb7, 0x53, 0x97, 0x00, 0x00, 0x00, 0x53, 0x49, 0x44, 0x41, 0x54, 0x08, + 0xd7, 0x63, 0x60, 0x80, 0x01, 0x16, 0x3c, 0x94, 0x28, 0x84, 0x92, 0x44, + 0xa1, 0x22, 0x1d, 0xc0, 0x54, 0x16, 0x16, 0x8a, 0x31, 0x2b, 0x00, 0x4c, + 0xad, 0x9a, 0x80, 0x49, 0xb1, 0xae, 0x9a, 0x02, 0xa2, 0xd8, 0x56, 0x2d, + 0x41, 0xa6, 0x96, 0x21, 0xf3, 0xc4, 0x56, 0x2f, 0x05, 0x51, 0x52, 0xab, + 0x56, 0x12, 0xa2, 0xa2, 0x56, 0xad, 0x04, 0x59, 0x98, 0xb5, 0x6a, 0x15, + 0x06, 0xc5, 0x08, 0xa4, 0x02, 0x40, 0xb6, 0xae, 0x02, 0xd9, 0xcb, 0x18, + 0x1a, 0x1a, 0x1a, 0xc0, 0x00, 0x00, 0xd2, 0x45, 0x24, 0x46, 0xf4, 0x40, + 0x4e, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, + 0x60, 0x82 +}; +unsigned int aisgreen_png_len = 230; +unsigned char yellowplane_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x27, + 0x08, 0x03, 0x00, 0x00, 0x00, 0xbb, 0x7d, 0xa1, 0x07, 0x00, 0x00, 0x02, + 0xf7, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0x00, 0x38, 0x44, 0x47, 0x44, + 0x45, 0x46, 0x3b, 0x4c, 0x51, 0x42, 0x4b, 0x4e, 0x3f, 0x4d, 0x51, 0x43, + 0x4e, 0x51, 0x44, 0x4e, 0x51, 0x44, 0x51, 0x54, 0x4b, 0x50, 0x52, 0x42, + 0x54, 0x5a, 0x43, 0x54, 0x5a, 0x4c, 0x52, 0x53, 0x48, 0x53, 0x56, 0x4a, + 0x53, 0x56, 0x4d, 0x53, 0x55, 0x46, 0x57, 0x5c, 0x4b, 0x56, 0x59, 0x4a, + 0x58, 0x5c, 0x4b, 0x58, 0x5b, 0x4f, 0x58, 0x5b, 0x4d, 0x59, 0x5c, 0x4e, + 0x59, 0x5d, 0x53, 0x58, 0x59, 0x50, 0x59, 0x5c, 0x49, 0x5b, 0x5f, 0x4e, + 0x5b, 0x5e, 0x4e, 0x5c, 0x60, 0x52, 0x5b, 0x5e, 0x49, 0x5e, 0x65, 0x4c, + 0x5e, 0x63, 0x52, 0x5d, 0x60, 0x55, 0x5d, 0x5f, 0x53, 0x5e, 0x61, 0x55, + 0x5f, 0x62, 0x53, 0x60, 0x64, 0x54, 0x60, 0x63, 0x54, 0x61, 0x65, 0x55, + 0x61, 0x64, 0x57, 0x61, 0x64, 0x5c, 0x60, 0x60, 0x58, 0x61, 0x64, 0x5a, + 0x61, 0x63, 0x52, 0x63, 0x69, 0x57, 0x62, 0x66, 0x5c, 0x61, 0x63, 0x56, + 0x63, 0x67, 0x5d, 0x62, 0x63, 0x59, 0x63, 0x66, 0x56, 0x64, 0x68, 0x5c, + 0x63, 0x66, 0x5c, 0x64, 0x67, 0x61, 0x65, 0x67, 0x63, 0x65, 0x65, 0x5d, + 0x67, 0x6a, 0x61, 0x66, 0x68, 0x5b, 0x68, 0x6c, 0x63, 0x66, 0x67, 0x5c, + 0x68, 0x6c, 0x59, 0x69, 0x6d, 0x61, 0x67, 0x69, 0x59, 0x69, 0x6f, 0x5e, + 0x68, 0x6b, 0x61, 0x69, 0x6b, 0x61, 0x6a, 0x6d, 0x5a, 0x6c, 0x71, 0x63, + 0x6a, 0x6b, 0x64, 0x6a, 0x6c, 0x66, 0x68, 0x7e, 0x5d, 0x6c, 0x70, 0x66, + 0x6a, 0x6b, 0x65, 0x6b, 0x6d, 0x5b, 0x6f, 0x74, 0x5c, 0x70, 0x75, 0x68, + 0x6d, 0x6e, 0x64, 0x6e, 0x71, 0x5e, 0x70, 0x75, 0x62, 0x6f, 0x73, 0x67, + 0x6e, 0x70, 0x69, 0x6e, 0x6f, 0x69, 0x6e, 0x70, 0x66, 0x6f, 0x72, 0x66, + 0x71, 0x75, 0x6b, 0x70, 0x71, 0x67, 0x71, 0x74, 0x6e, 0x70, 0x70, 0x62, + 0x74, 0x7a, 0x66, 0x74, 0x78, 0x70, 0x72, 0x72, 0x6d, 0x73, 0x75, 0x66, + 0x75, 0x7a, 0x68, 0x75, 0x79, 0x71, 0x73, 0x74, 0x72, 0x74, 0x75, 0x72, + 0x76, 0x77, 0x6c, 0x78, 0x7b, 0x72, 0x77, 0x78, 0x72, 0x77, 0x79, 0x73, + 0x78, 0x79, 0x72, 0x79, 0x7b, 0x72, 0x7b, 0x7e, 0x78, 0x7a, 0x7a, 0x77, + 0x7b, 0x7d, 0x74, 0x7c, 0x7e, 0x75, 0x7c, 0x7e, 0x76, 0x7d, 0x80, 0x77, + 0x7e, 0x80, 0x77, 0x80, 0x82, 0x7c, 0x7f, 0x7f, 0x72, 0x82, 0x87, 0x76, + 0x81, 0x85, 0x7b, 0x80, 0x81, 0x76, 0x83, 0x87, 0x7e, 0x85, 0x87, 0x83, + 0x84, 0x85, 0x7d, 0x87, 0x89, 0x7b, 0x88, 0x8c, 0x7b, 0x89, 0x8e, 0x7e, + 0x8a, 0x8d, 0x7f, 0x8b, 0x8e, 0x88, 0x8b, 0x8b, 0x89, 0x8b, 0x8c, 0x8b, + 0x8c, 0x8c, 0x88, 0x8e, 0x91, 0x85, 0x8f, 0x93, 0x8c, 0x8e, 0x8f, 0x8d, + 0x8f, 0x8f, 0x87, 0x91, 0x94, 0x88, 0x93, 0x97, 0x91, 0x94, 0x96, 0x93, + 0x94, 0x95, 0x8d, 0x96, 0x9a, 0x8d, 0x97, 0x9a, 0x90, 0x97, 0x99, 0x95, + 0x96, 0x96, 0x91, 0x98, 0x9b, 0x94, 0x98, 0x99, 0x91, 0x99, 0x9b, 0x96, + 0x97, 0xa4, 0x93, 0x99, 0x9b, 0x91, 0x9a, 0x9c, 0x98, 0x99, 0x99, 0x93, + 0x9b, 0x9d, 0x97, 0x9c, 0x9d, 0x93, 0x9d, 0xa1, 0x95, 0x9d, 0x9f, 0x96, + 0x9d, 0xa0, 0x98, 0xa0, 0xa3, 0x9d, 0x9f, 0xa0, 0x97, 0xa1, 0xa5, 0xa1, + 0xa1, 0xa1, 0x99, 0xa3, 0xa7, 0x9b, 0xa3, 0xa5, 0xa4, 0xa5, 0xa5, 0x9d, + 0xa7, 0xa9, 0xa6, 0xa8, 0xa8, 0xa2, 0xa9, 0xac, 0xa2, 0xab, 0xad, 0xaa, + 0xab, 0xab, 0xa6, 0xad, 0xb0, 0xa5, 0xaf, 0xb2, 0xab, 0xb3, 0xb6, 0xaf, + 0xb2, 0xb4, 0xae, 0xb5, 0xb8, 0xb4, 0xb4, 0xb4, 0xb0, 0xb6, 0xb8, 0xb1, + 0xb7, 0xba, 0xb2, 0xb7, 0xb9, 0xb4, 0xb7, 0xb9, 0xb2, 0xb8, 0xb9, 0xb2, + 0xb8, 0xba, 0xb3, 0xb8, 0xb9, 0xb3, 0xbb, 0xbe, 0xb4, 0xbb, 0xbd, 0xb5, + 0xbb, 0xbd, 0xb6, 0xbb, 0xbc, 0xb7, 0xbc, 0xbf, 0xb8, 0xbd, 0xbe, 0xbd, + 0xbe, 0xbf, 0xbc, 0xbf, 0xc0, 0xbc, 0xc1, 0xc2, 0xbd, 0xc1, 0xc2, 0xbb, + 0xc2, 0xc5, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xbe, 0xc5, 0xc7, 0xc2, + 0xc4, 0xc5, 0xc3, 0xc4, 0xc4, 0xc2, 0xc6, 0xc7, 0xc4, 0xc6, 0xc7, 0xc3, + 0xc7, 0xc8, 0xc3, 0xc7, 0xc9, 0xc3, 0xc8, 0xca, 0xc4, 0xc9, 0xcb, 0xc6, + 0xca, 0xcb, 0xc6, 0xcc, 0xce, 0xca, 0xcd, 0xce, 0xca, 0xce, 0xd0, 0xcb, + 0xce, 0xcf, 0xca, 0xcf, 0xd1, 0xcb, 0xcf, 0xd0, 0xcb, 0xd0, 0xd2, 0xcd, + 0xd0, 0xd1, 0xcf, 0xd1, 0xd2, 0xd0, 0xd1, 0xd1, 0xce, 0xd2, 0xd3, 0xd0, + 0xd2, 0xd3, 0xd0, 0xd3, 0xd4, 0xd2, 0xd4, 0xd5, 0xd4, 0xd5, 0xd8, 0xd2, + 0xd6, 0xd8, 0xd2, 0xd7, 0xd8, 0xd4, 0xd7, 0xd8, 0xd3, 0xd8, 0xd9, 0xd7, + 0xd7, 0xdd, 0xd6, 0xd8, 0xd9, 0xd6, 0xd8, 0xda, 0xd7, 0xd8, 0xd8, 0xd7, + 0xda, 0xdb, 0xd8, 0xdb, 0xdd, 0xd7, 0xdc, 0xde, 0xd9, 0xdc, 0xdd, 0xda, + 0xdc, 0xdc, 0xdb, 0xdd, 0xdd, 0xdb, 0xdd, 0xde, 0xdc, 0xdf, 0xe0, 0xdd, + 0xdf, 0xe0, 0xdd, 0xe0, 0xe0, 0xde, 0xe1, 0xe2, 0xe0, 0xe1, 0xe2, 0xe0, + 0xe2, 0xe2, 0xe1, 0xe3, 0xe3, 0xe1, 0xe3, 0xe4, 0xe2, 0xe3, 0xe3, 0xe1, + 0xe6, 0xe7, 0xe5, 0xe7, 0xe7, 0xe6, 0xe7, 0xe7, 0xe6, 0xe7, 0xe8, 0xe6, + 0xe8, 0xe9, 0xe7, 0xe8, 0xe9, 0xe4, 0xe9, 0xeb, 0xe6, 0xe9, 0xea, 0xe7, + 0xe9, 0xea, 0xe8, 0xe9, 0xea, 0xe6, 0xea, 0xeb, 0xe9, 0xeb, 0xec, 0xe8, + 0xec, 0xed, 0xea, 0xec, 0xed, 0xeb, 0xec, 0xed, 0xeb, 0xed, 0xed, 0xec, + 0xed, 0xee, 0xec, 0xef, 0xf1, 0xff, 0xff, 0x00, 0x0f, 0x9e, 0xcb, 0x48, + 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, 0x4e, 0x53, 0x00, 0x40, 0xe6, 0xd8, + 0x66, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0e, + 0xc4, 0x00, 0x00, 0x0e, 0xc4, 0x01, 0x95, 0x2b, 0x0e, 0x1b, 0x00, 0x00, + 0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, 0xe6, 0x01, 0x09, 0x0a, 0x37, + 0x0f, 0x41, 0x8e, 0x0c, 0x76, 0x00, 0x00, 0x01, 0xda, 0x49, 0x44, 0x41, + 0x54, 0x38, 0xcb, 0x63, 0x60, 0x20, 0x1b, 0xdc, 0x7a, 0x49, 0x94, 0xb2, + 0x0e, 0x67, 0xf5, 0x43, 0x44, 0x28, 0xdb, 0x14, 0xf7, 0xe7, 0xcf, 0x44, + 0xc2, 0xca, 0xde, 0x86, 0x14, 0xfd, 0xf9, 0x23, 0x7c, 0x90, 0xa0, 0xba, + 0x59, 0xd2, 0x7f, 0x80, 0x60, 0x29, 0x41, 0x75, 0x1b, 0xed, 0x41, 0xea, + 0x7a, 0x08, 0xaa, 0x5b, 0xe9, 0x07, 0x52, 0x17, 0x4e, 0x50, 0xdd, 0xea, + 0x60, 0x90, 0x3a, 0xd7, 0xc7, 0x84, 0xd4, 0x4d, 0x76, 0x06, 0xa9, 0xb3, + 0xb8, 0x4b, 0x48, 0xdd, 0x8a, 0x00, 0x90, 0xba, 0xa0, 0xe3, 0x78, 0x15, + 0xbd, 0x38, 0xb7, 0x25, 0xca, 0x1c, 0xa4, 0x4e, 0xa8, 0xa4, 0xbe, 0xf9, + 0x19, 0x4e, 0x65, 0xf7, 0x1d, 0xcd, 0x0c, 0x18, 0x35, 0x40, 0xea, 0x58, + 0x24, 0x9c, 0x04, 0xf3, 0x70, 0xaa, 0x3b, 0xa5, 0x35, 0xbb, 0x8a, 0x29, + 0x14, 0xa4, 0xce, 0x34, 0x62, 0x49, 0x53, 0xe0, 0x1b, 0x5c, 0xea, 0x76, + 0x59, 0xde, 0xfc, 0x83, 0x00, 0x46, 0xf7, 0x70, 0x28, 0xfb, 0x55, 0x20, + 0xbe, 0x1d, 0x49, 0x1d, 0x7b, 0xcd, 0x25, 0xec, 0xea, 0xe6, 0x08, 0xa4, + 0xee, 0x40, 0x52, 0xf7, 0x47, 0x21, 0x6c, 0x1b, 0x16, 0x55, 0x97, 0x36, + 0x8b, 0xc5, 0xee, 0xfe, 0x83, 0x02, 0xfc, 0x53, 0xa6, 0x9f, 0x44, 0x57, + 0x76, 0xdd, 0xd6, 0xa6, 0xed, 0xcc, 0x1f, 0x34, 0x90, 0x23, 0xe3, 0xb1, + 0xe0, 0x27, 0xb2, 0xaa, 0xcf, 0xab, 0x32, 0xdc, 0xe6, 0xfd, 0xc1, 0x04, + 0xd3, 0xf4, 0xe5, 0xf6, 0xbf, 0x43, 0x28, 0x7b, 0xd8, 0xcd, 0xfa, 0x07, + 0x07, 0xa8, 0x48, 0x6c, 0x38, 0x0c, 0x53, 0xb6, 0x2c, 0x1a, 0x22, 0x56, + 0x69, 0x2f, 0x22, 0xcd, 0xc1, 0x09, 0x62, 0xf1, 0x33, 0x67, 0x27, 0x4b, + 0x79, 0xd5, 0xf5, 0x02, 0x99, 0x53, 0x4c, 0xaa, 0x5f, 0x43, 0x94, 0x3d, + 0xd2, 0x86, 0x28, 0x73, 0xcf, 0x9a, 0x71, 0xe2, 0x68, 0x2e, 0x1f, 0x88, + 0xa9, 0x58, 0xfa, 0xe4, 0xc7, 0xfa, 0x49, 0x8d, 0x3a, 0x36, 0x20, 0x8e, + 0xe6, 0x3a, 0xa8, 0x79, 0x49, 0x20, 0x9e, 0x43, 0xf1, 0x9e, 0x4f, 0x40, + 0xf6, 0x8d, 0x48, 0x10, 0xc7, 0xe0, 0x08, 0x58, 0xe2, 0x74, 0xb9, 0x35, + 0x90, 0xc3, 0xb6, 0x15, 0xa2, 0xec, 0xae, 0xdd, 0x1f, 0x1e, 0xbd, 0x96, + 0x63, 0x10, 0xce, 0x15, 0x63, 0x90, 0x3a, 0x95, 0xe5, 0x50, 0x23, 0x76, + 0x1e, 0x6a, 0xfd, 0x53, 0x08, 0x65, 0x7f, 0x9d, 0xe0, 0xb9, 0xef, 0x01, + 0xcc, 0xad, 0x27, 0x44, 0x41, 0xea, 0x94, 0xe6, 0x23, 0x3c, 0x79, 0xe0, + 0x21, 0x9c, 0x79, 0x15, 0x21, 0xfa, 0x54, 0x15, 0xa4, 0x4e, 0x79, 0x2e, + 0xc1, 0x84, 0x9f, 0x09, 0x52, 0x67, 0x75, 0x9e, 0x90, 0xb2, 0xdb, 0xf1, + 0x20, 0x75, 0xdc, 0x8b, 0x09, 0x28, 0xfb, 0xbe, 0xd0, 0x0e, 0xa4, 0x4e, + 0xbe, 0x8c, 0x40, 0x06, 0x99, 0x6a, 0x98, 0x00, 0x52, 0x57, 0xcb, 0xeb, + 0xb3, 0x06, 0x9f, 0xb2, 0xad, 0x92, 0x31, 0x90, 0x30, 0x9f, 0xa9, 0x2b, + 0x7b, 0x16, 0xb7, 0xb2, 0xe7, 0xde, 0x69, 0xf0, 0x88, 0xcd, 0xef, 0xff, + 0x81, 0x53, 0x5d, 0x3b, 0x72, 0x0a, 0xe0, 0xda, 0x86, 0x53, 0xdd, 0x22, + 0x94, 0xa4, 0xf2, 0x11, 0xa7, 0xba, 0xbd, 0x6a, 0xe9, 0x7d, 0x6b, 0x2f, + 0x76, 0xb9, 0x6c, 0xf8, 0x70, 0xb9, 0xd3, 0xf7, 0x02, 0x1e, 0x7f, 0xbc, + 0xff, 0xf2, 0x0d, 0x48, 0x5e, 0x63, 0xf8, 0xcd, 0xf0, 0xea, 0x0e, 0x03, + 0x4d, 0x01, 0x00, 0x38, 0x16, 0x7f, 0x3f, 0xdc, 0xdc, 0x55, 0xf3, 0x00, + 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; +unsigned int yellowplane_png_len = 1355; + +unsigned char greenplane_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x27, + 0x08, 0x03, 0x00, 0x00, 0x00, 0xbb, 0x7d, 0xa1, 0x07, 0x00, 0x00, 0x02, + 0xf7, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0x00, 0x38, 0x44, 0x47, 0x44, + 0x45, 0x46, 0x3b, 0x4c, 0x51, 0x42, 0x4b, 0x4e, 0x3f, 0x4d, 0x51, 0x43, + 0x4e, 0x51, 0x44, 0x4e, 0x51, 0x44, 0x51, 0x54, 0x4b, 0x50, 0x52, 0x42, + 0x54, 0x5a, 0x43, 0x54, 0x5a, 0x4c, 0x52, 0x53, 0x48, 0x53, 0x56, 0x4a, + 0x53, 0x56, 0x4d, 0x53, 0x55, 0x46, 0x57, 0x5c, 0x4b, 0x56, 0x59, 0x4a, + 0x58, 0x5c, 0x4b, 0x58, 0x5b, 0x4f, 0x58, 0x5b, 0x4d, 0x59, 0x5c, 0x4e, + 0x59, 0x5d, 0x53, 0x58, 0x59, 0x50, 0x59, 0x5c, 0x49, 0x5b, 0x5f, 0x4e, + 0x5b, 0x5e, 0x4e, 0x5c, 0x60, 0x52, 0x5b, 0x5e, 0x49, 0x5e, 0x65, 0x4c, + 0x5e, 0x63, 0x52, 0x5d, 0x60, 0x55, 0x5d, 0x5f, 0x53, 0x5e, 0x61, 0x55, + 0x5f, 0x62, 0x53, 0x60, 0x64, 0x54, 0x60, 0x63, 0x54, 0x61, 0x65, 0x55, + 0x61, 0x64, 0x57, 0x61, 0x64, 0x5c, 0x60, 0x60, 0x58, 0x61, 0x64, 0x5a, + 0x61, 0x63, 0x52, 0x63, 0x69, 0x57, 0x62, 0x66, 0x5c, 0x61, 0x63, 0x56, + 0x63, 0x67, 0x5d, 0x62, 0x63, 0x59, 0x63, 0x66, 0x56, 0x64, 0x68, 0x5c, + 0x63, 0x66, 0x5c, 0x64, 0x67, 0x61, 0x65, 0x67, 0x63, 0x65, 0x65, 0x5d, + 0x67, 0x6a, 0x61, 0x66, 0x68, 0x5b, 0x68, 0x6c, 0x63, 0x66, 0x67, 0x5c, + 0x68, 0x6c, 0x59, 0x69, 0x6d, 0x61, 0x67, 0x69, 0x59, 0x69, 0x6f, 0x5e, + 0x68, 0x6b, 0x61, 0x69, 0x6b, 0x61, 0x6a, 0x6d, 0x5a, 0x6c, 0x71, 0x63, + 0x6a, 0x6b, 0x64, 0x6a, 0x6c, 0x66, 0x68, 0x7e, 0x5d, 0x6c, 0x70, 0x66, + 0x6a, 0x6b, 0x65, 0x6b, 0x6d, 0x5b, 0x6f, 0x74, 0x5c, 0x70, 0x75, 0x68, + 0x6d, 0x6e, 0x64, 0x6e, 0x71, 0x5e, 0x70, 0x75, 0x62, 0x6f, 0x73, 0x67, + 0x6e, 0x70, 0x69, 0x6e, 0x6f, 0x69, 0x6e, 0x70, 0x66, 0x6f, 0x72, 0x66, + 0x71, 0x75, 0x6b, 0x70, 0x71, 0x67, 0x71, 0x74, 0x6e, 0x70, 0x70, 0x62, + 0x74, 0x7a, 0x66, 0x74, 0x78, 0x70, 0x72, 0x72, 0x6d, 0x73, 0x75, 0x66, + 0x75, 0x7a, 0x68, 0x75, 0x79, 0x71, 0x73, 0x74, 0x72, 0x74, 0x75, 0x72, + 0x76, 0x77, 0x6c, 0x78, 0x7b, 0x72, 0x77, 0x78, 0x72, 0x77, 0x79, 0x73, + 0x78, 0x79, 0x72, 0x79, 0x7b, 0x72, 0x7b, 0x7e, 0x78, 0x7a, 0x7a, 0x77, + 0x7b, 0x7d, 0x74, 0x7c, 0x7e, 0x75, 0x7c, 0x7e, 0x76, 0x7d, 0x80, 0x77, + 0x7e, 0x80, 0x77, 0x80, 0x82, 0x7c, 0x7f, 0x7f, 0x72, 0x82, 0x87, 0x76, + 0x81, 0x85, 0x7b, 0x80, 0x81, 0x76, 0x83, 0x87, 0x7e, 0x85, 0x87, 0x83, + 0x84, 0x85, 0x7d, 0x87, 0x89, 0x7b, 0x88, 0x8c, 0x7b, 0x89, 0x8e, 0x7e, + 0x8a, 0x8d, 0x7f, 0x8b, 0x8e, 0x88, 0x8b, 0x8b, 0x89, 0x8b, 0x8c, 0x8b, + 0x8c, 0x8c, 0x88, 0x8e, 0x91, 0x85, 0x8f, 0x93, 0x8c, 0x8e, 0x8f, 0x8d, + 0x8f, 0x8f, 0x87, 0x91, 0x94, 0x88, 0x93, 0x97, 0x91, 0x94, 0x96, 0x93, + 0x94, 0x95, 0x8d, 0x96, 0x9a, 0x8d, 0x97, 0x9a, 0x90, 0x97, 0x99, 0x95, + 0x96, 0x96, 0x91, 0x98, 0x9b, 0x94, 0x98, 0x99, 0x91, 0x99, 0x9b, 0x96, + 0x97, 0xa4, 0x93, 0x99, 0x9b, 0x91, 0x9a, 0x9c, 0x98, 0x99, 0x99, 0x93, + 0x9b, 0x9d, 0x97, 0x9c, 0x9d, 0x93, 0x9d, 0xa1, 0x95, 0x9d, 0x9f, 0x96, + 0x9d, 0xa0, 0x98, 0xa0, 0xa3, 0x9d, 0x9f, 0xa0, 0x97, 0xa1, 0xa5, 0xa1, + 0xa1, 0xa1, 0x99, 0xa3, 0xa7, 0x9b, 0xa3, 0xa5, 0xa4, 0xa5, 0xa5, 0x9d, + 0xa7, 0xa9, 0xa6, 0xa8, 0xa8, 0xa2, 0xa9, 0xac, 0xa2, 0xab, 0xad, 0xaa, + 0xab, 0xab, 0xa6, 0xad, 0xb0, 0xa5, 0xaf, 0xb2, 0xab, 0xb3, 0xb6, 0xaf, + 0xb2, 0xb4, 0xae, 0xb5, 0xb8, 0xb4, 0xb4, 0xb4, 0xb0, 0xb6, 0xb8, 0xb1, + 0xb7, 0xba, 0xb2, 0xb7, 0xb9, 0xb4, 0xb7, 0xb9, 0xb2, 0xb8, 0xb9, 0xb2, + 0xb8, 0xba, 0x00, 0xff, 0x00, 0xb3, 0xb8, 0xb9, 0xb3, 0xbb, 0xbe, 0xb4, + 0xbb, 0xbd, 0xb5, 0xbb, 0xbd, 0xb6, 0xbb, 0xbc, 0xb7, 0xbc, 0xbf, 0xb8, + 0xbd, 0xbe, 0xbd, 0xbe, 0xbf, 0xbc, 0xbf, 0xc0, 0xbc, 0xc1, 0xc2, 0xbd, + 0xc1, 0xc2, 0xbb, 0xc2, 0xc5, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xbe, + 0xc5, 0xc7, 0xc2, 0xc4, 0xc5, 0xc3, 0xc4, 0xc4, 0xc2, 0xc6, 0xc7, 0xc4, + 0xc6, 0xc7, 0xc3, 0xc7, 0xc8, 0xc3, 0xc7, 0xc9, 0xc3, 0xc8, 0xca, 0xc4, + 0xc9, 0xcb, 0xc6, 0xca, 0xcb, 0xc6, 0xcc, 0xce, 0xca, 0xcd, 0xce, 0xca, + 0xce, 0xd0, 0xcb, 0xce, 0xcf, 0xca, 0xcf, 0xd1, 0xcb, 0xcf, 0xd0, 0xcb, + 0xd0, 0xd2, 0xcd, 0xd0, 0xd1, 0xcf, 0xd1, 0xd2, 0xd0, 0xd1, 0xd1, 0xce, + 0xd2, 0xd3, 0xd0, 0xd2, 0xd3, 0xd0, 0xd3, 0xd4, 0xd2, 0xd4, 0xd5, 0xd4, + 0xd5, 0xd8, 0xd2, 0xd6, 0xd8, 0xd2, 0xd7, 0xd8, 0xd4, 0xd7, 0xd8, 0xd3, + 0xd8, 0xd9, 0xd7, 0xd7, 0xdd, 0xd6, 0xd8, 0xd9, 0xd6, 0xd8, 0xda, 0xd7, + 0xd8, 0xd8, 0xd7, 0xda, 0xdb, 0xd8, 0xdb, 0xdd, 0xd7, 0xdc, 0xde, 0xd9, + 0xdc, 0xdd, 0xda, 0xdc, 0xdc, 0xdb, 0xdd, 0xdd, 0xdb, 0xdd, 0xde, 0xdc, + 0xdf, 0xe0, 0xdd, 0xdf, 0xe0, 0xdd, 0xe0, 0xe0, 0xde, 0xe1, 0xe2, 0xe0, + 0xe1, 0xe2, 0xe0, 0xe2, 0xe2, 0xe1, 0xe3, 0xe3, 0xe1, 0xe3, 0xe4, 0xe2, + 0xe3, 0xe3, 0xe1, 0xe6, 0xe7, 0xe5, 0xe7, 0xe7, 0xe6, 0xe7, 0xe7, 0xe6, + 0xe7, 0xe8, 0xe6, 0xe8, 0xe9, 0xe7, 0xe8, 0xe9, 0xe4, 0xe9, 0xeb, 0xe6, + 0xe9, 0xea, 0xe7, 0xe9, 0xea, 0xe8, 0xe9, 0xea, 0xe6, 0xea, 0xeb, 0xe9, + 0xeb, 0xec, 0xe8, 0xec, 0xed, 0xea, 0xec, 0xed, 0xeb, 0xec, 0xed, 0xeb, + 0xed, 0xed, 0xec, 0xed, 0xee, 0xec, 0xef, 0xf1, 0xbe, 0x3f, 0x0a, 0xac, + 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, 0x4e, 0x53, 0x00, 0x40, 0xe6, 0xd8, + 0x66, 0x00, 0x00, 0x00, 0x01, 0x62, 0x4b, 0x47, 0x44, 0x00, 0x88, 0x05, + 0x1d, 0x48, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, + 0x0e, 0xc4, 0x00, 0x00, 0x0e, 0xc4, 0x01, 0x95, 0x2b, 0x0e, 0x1b, 0x00, + 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, 0xe6, 0x01, 0x09, 0x0a, + 0x36, 0x22, 0x1d, 0x4a, 0x61, 0x42, 0x00, 0x00, 0x01, 0xda, 0x49, 0x44, + 0x41, 0x54, 0x38, 0xcb, 0x63, 0x60, 0x20, 0x1b, 0xdc, 0x7e, 0x45, 0x94, + 0xb2, 0x0e, 0x67, 0xf5, 0xc3, 0x44, 0x28, 0xdb, 0x1c, 0xb7, 0x7a, 0xf5, + 0x44, 0xc2, 0xca, 0xde, 0x85, 0x14, 0xad, 0x5e, 0x2d, 0x7c, 0x88, 0xa0, + 0xba, 0x59, 0xd2, 0xab, 0x81, 0x60, 0x29, 0x41, 0x75, 0x9b, 0xec, 0x41, + 0xea, 0x7a, 0x08, 0xaa, 0x5b, 0xe9, 0x07, 0x52, 0x17, 0x4e, 0x50, 0xdd, + 0x9a, 0x60, 0x90, 0x3a, 0xd7, 0x27, 0x84, 0xd4, 0x4d, 0x76, 0x06, 0xa9, + 0xb3, 0xb8, 0x47, 0x48, 0xdd, 0x8a, 0x00, 0x90, 0xba, 0xa0, 0x13, 0x78, + 0x15, 0xbd, 0x3c, 0xbf, 0x35, 0xca, 0x1c, 0xa4, 0x4e, 0xa8, 0xa4, 0xbe, + 0xf9, 0x39, 0x4e, 0x65, 0x0f, 0x1c, 0xcd, 0x0c, 0x18, 0x35, 0x40, 0xea, + 0x58, 0x24, 0x9c, 0x04, 0xf3, 0x70, 0xaa, 0x3b, 0xad, 0x35, 0xbb, 0x8a, + 0x29, 0x14, 0xa4, 0xce, 0x34, 0x62, 0x49, 0x53, 0xe0, 0x5b, 0x5c, 0xea, + 0x76, 0x5b, 0xde, 0x5a, 0x8d, 0x00, 0x46, 0xf7, 0x71, 0x28, 0xfb, 0x5d, + 0x20, 0xbe, 0x03, 0x49, 0x1d, 0x7b, 0xcd, 0x65, 0xec, 0xea, 0xe6, 0x08, + 0xa4, 0xee, 0x44, 0x52, 0xb7, 0x5a, 0x21, 0x6c, 0x3b, 0x16, 0x55, 0x97, + 0xb7, 0x88, 0xc5, 0xee, 0x59, 0x8d, 0x02, 0xfc, 0x53, 0xa6, 0x9f, 0x42, + 0x57, 0x76, 0xc3, 0xd6, 0xa6, 0xed, 0xec, 0x6a, 0x34, 0x90, 0x23, 0xe3, + 0xb1, 0xe0, 0x17, 0xb2, 0xaa, 0x2f, 0xab, 0x32, 0xdc, 0xe6, 0xad, 0xc6, + 0x04, 0xd3, 0xf4, 0xe5, 0x0e, 0xbc, 0x47, 0x28, 0x7b, 0xd4, 0xcd, 0xba, + 0x1a, 0x07, 0xa8, 0x48, 0x6c, 0x38, 0x02, 0x53, 0xb6, 0x2c, 0x1a, 0x22, + 0x56, 0x69, 0x2f, 0x22, 0xcd, 0xc1, 0x09, 0x62, 0xf1, 0x33, 0x67, 0x27, + 0x4b, 0x79, 0xd5, 0xf5, 0x02, 0x99, 0x53, 0x4c, 0xaa, 0xdf, 0x40, 0x94, + 0x3d, 0xd6, 0x86, 0x28, 0x73, 0xcf, 0x9a, 0x71, 0xf2, 0x58, 0x2e, 0x1f, + 0x88, 0xa9, 0x58, 0xfa, 0xf4, 0xe7, 0x86, 0x49, 0x8d, 0x3a, 0x36, 0x20, + 0x8e, 0xe6, 0x7a, 0xa8, 0x79, 0x49, 0x20, 0x9e, 0x43, 0xf1, 0xde, 0xcf, + 0x40, 0xf6, 0xcd, 0x48, 0x10, 0xc7, 0xe0, 0x28, 0x58, 0xe2, 0x4c, 0xb9, + 0x35, 0x90, 0xc3, 0xb6, 0x0d, 0xa2, 0xec, 0x9e, 0xdd, 0x6a, 0x1e, 0xbd, + 0x96, 0xe3, 0x10, 0xce, 0x55, 0x63, 0x90, 0x3a, 0x95, 0xe5, 0x50, 0x23, + 0x76, 0x1d, 0x6e, 0x5d, 0x5d, 0x08, 0x65, 0x7f, 0x9b, 0xe0, 0xb9, 0xff, + 0x21, 0xcc, 0xad, 0x27, 0x45, 0x41, 0xea, 0x94, 0xe6, 0x23, 0x3c, 0x79, + 0xf0, 0x11, 0x9c, 0x79, 0x0d, 0x21, 0xfa, 0x4c, 0x15, 0xa4, 0x4e, 0x79, + 0x2e, 0xc1, 0x84, 0x9f, 0x09, 0x52, 0x67, 0x75, 0x81, 0x90, 0xb2, 0x3b, + 0xf1, 0x20, 0x75, 0xdc, 0x8b, 0x09, 0x28, 0xfb, 0xb1, 0xd0, 0x0e, 0xa4, + 0x4e, 0xbe, 0x8c, 0x40, 0x06, 0x99, 0x6a, 0x98, 0x00, 0x52, 0x57, 0xcb, + 0xeb, 0xb3, 0x16, 0x9f, 0xb2, 0x6d, 0x92, 0x31, 0x90, 0x30, 0x9f, 0xa9, + 0x2b, 0x7b, 0x0e, 0xb7, 0xb2, 0x17, 0xde, 0x69, 0xf0, 0x88, 0xcd, 0xef, + 0xff, 0x89, 0x53, 0x5d, 0x3b, 0x72, 0x0a, 0xe0, 0xda, 0x8e, 0x53, 0xdd, + 0x22, 0x94, 0xa4, 0xf2, 0x09, 0xa7, 0xba, 0x7d, 0x6a, 0xe9, 0x7d, 0xeb, + 0x2e, 0x75, 0xb9, 0x6c, 0xfc, 0x78, 0xa5, 0xd3, 0xf7, 0x22, 0x1e, 0x7f, + 0x7c, 0xf8, 0xfa, 0x1d, 0x48, 0x5e, 0x67, 0xf8, 0xc3, 0xf0, 0xfa, 0x2e, + 0x03, 0x4d, 0x01, 0x00, 0xfc, 0xea, 0x2e, 0xea, 0xf1, 0xce, 0x06, 0x98, + 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; +unsigned int greenplane_png_len = 1368; +unsigned char aisred_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x22, + 0x02, 0x03, 0x00, 0x00, 0x00, 0xcd, 0x06, 0x7a, 0x10, 0x00, 0x00, 0x00, + 0x09, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0xec, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xf9, 0xfb, 0x11, 0x4e, 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, + 0x4e, 0x53, 0x00, 0x40, 0xe6, 0xd8, 0x66, 0x00, 0x00, 0x00, 0x01, 0x62, + 0x4b, 0x47, 0x44, 0x00, 0x88, 0x05, 0x1d, 0x48, 0x00, 0x00, 0x00, 0x09, + 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, + 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, + 0x45, 0x07, 0xe6, 0x01, 0x09, 0x0a, 0x35, 0x30, 0xc5, 0xde, 0x43, 0xc9, + 0x00, 0x00, 0x00, 0x53, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0x60, + 0x80, 0x01, 0x16, 0x3c, 0x94, 0x28, 0x84, 0x92, 0x44, 0xa1, 0x22, 0x1d, + 0xc0, 0x54, 0x16, 0x16, 0x8a, 0x31, 0x2b, 0x00, 0x4c, 0xad, 0x9a, 0x80, + 0x49, 0xb1, 0xae, 0x9a, 0x02, 0xa2, 0xd8, 0x56, 0x2d, 0x41, 0xa6, 0x96, + 0x21, 0xf3, 0xc4, 0x56, 0x2d, 0x05, 0x51, 0x52, 0xab, 0x56, 0x12, 0xa2, + 0xa2, 0x56, 0xad, 0x04, 0x59, 0x98, 0xb5, 0x6a, 0x15, 0x06, 0xc5, 0x08, + 0xa4, 0x02, 0x40, 0xb6, 0xae, 0x02, 0xd9, 0xcb, 0x18, 0x1a, 0x1a, 0x1a, + 0xc0, 0x00, 0x00, 0xd1, 0xed, 0x24, 0x45, 0x35, 0x85, 0xab, 0x6c, 0x00, + 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; +unsigned int aisred_png_len = 227; +unsigned char aistrans_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x22, + 0x01, 0x03, 0x00, 0x00, 0x00, 0x8a, 0xa6, 0x00, 0xc0, 0x00, 0x00, 0x00, + 0x06, 0x50, 0x4c, 0x54, 0x45, 0x6f, 0x72, 0x74, 0x00, 0x00, 0x00, 0x3b, + 0x8a, 0x80, 0x24, 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, 0x4e, 0x53, 0x00, + 0x40, 0xe6, 0xd8, 0x66, 0x00, 0x00, 0x00, 0x01, 0x62, 0x4b, 0x47, 0x44, + 0x00, 0x88, 0x05, 0x1d, 0x48, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, + 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, 0x01, 0x00, 0x9a, + 0x9c, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, 0xe6, + 0x01, 0x09, 0x0a, 0x36, 0x08, 0xc6, 0xf1, 0xa8, 0x94, 0x00, 0x00, 0x00, + 0x43, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0x60, 0x00, 0x01, 0x05, + 0x34, 0x5c, 0x00, 0xc4, 0x01, 0x50, 0x7c, 0x03, 0x88, 0x3b, 0x10, 0x98, + 0xb1, 0x07, 0x88, 0x59, 0x10, 0x98, 0x99, 0x8d, 0x81, 0x81, 0x89, 0x09, + 0x8a, 0x95, 0x20, 0x34, 0x1b, 0x33, 0x03, 0x03, 0x0b, 0x23, 0x26, 0xe6, + 0x61, 0x6c, 0x60, 0xe0, 0x60, 0x40, 0x60, 0x09, 0x86, 0x03, 0x0c, 0x02, + 0x0c, 0x0e, 0x0c, 0xf2, 0xff, 0x0f, 0x00, 0x00, 0x1c, 0xa6, 0x0a, 0x23, + 0x27, 0x5d, 0x69, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, + 0xae, 0x42, 0x60, 0x82 +}; +unsigned int aistrans_png_len = 208; +unsigned char plane_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x27, + 0x08, 0x03, 0x00, 0x00, 0x00, 0xbb, 0x7d, 0xa1, 0x07, 0x00, 0x00, 0x03, + 0x00, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0x00, 0x19, 0x1b, 0x27, 0x2c, + 0x30, 0x41, 0x37, 0x44, 0x49, 0x42, 0x46, 0x48, 0x39, 0x4c, 0x51, 0x41, + 0x4b, 0x4c, 0x40, 0x4d, 0x53, 0x43, 0x4c, 0x58, 0x44, 0x4e, 0x4f, 0x44, + 0x51, 0x56, 0x4c, 0x50, 0x52, 0x4d, 0x51, 0x53, 0x41, 0x55, 0x59, 0x4e, + 0x52, 0x54, 0x49, 0x54, 0x54, 0x4f, 0x53, 0x55, 0x4b, 0x56, 0x57, 0x44, + 0x58, 0x5c, 0x45, 0x59, 0x5e, 0x4b, 0x58, 0x5e, 0x53, 0x57, 0x5a, 0x4f, + 0x59, 0x5a, 0x47, 0x5b, 0x60, 0x4e, 0x5c, 0x61, 0x56, 0x5a, 0x5c, 0x47, + 0x5e, 0x68, 0x4a, 0x5e, 0x62, 0x52, 0x5c, 0x5d, 0x58, 0x5c, 0x5e, 0x53, + 0x5e, 0x5e, 0x54, 0x5f, 0x5f, 0x5a, 0x5e, 0x60, 0x53, 0x60, 0x66, 0x55, + 0x60, 0x60, 0x56, 0x60, 0x61, 0x57, 0x61, 0x62, 0x5c, 0x60, 0x62, 0x50, + 0x64, 0x68, 0x5d, 0x61, 0x63, 0x56, 0x63, 0x69, 0x5e, 0x62, 0x64, 0x59, + 0x64, 0x64, 0x5f, 0x63, 0x65, 0x61, 0x65, 0x67, 0x5c, 0x67, 0x68, 0x63, + 0x66, 0x63, 0x62, 0x66, 0x68, 0x5b, 0x68, 0x6e, 0x55, 0x6a, 0x6e, 0x5d, + 0x68, 0x69, 0x62, 0x67, 0x69, 0x5e, 0x69, 0x6a, 0x5f, 0x6a, 0x6b, 0x64, + 0x68, 0x7c, 0x5e, 0x6b, 0x71, 0x58, 0x6d, 0x71, 0x66, 0x6a, 0x6c, 0x67, + 0x6b, 0x6e, 0x68, 0x6c, 0x6f, 0x63, 0x6e, 0x6f, 0x6d, 0x6b, 0x6f, 0x5c, + 0x70, 0x75, 0x69, 0x6d, 0x70, 0x62, 0x6f, 0x75, 0x65, 0x70, 0x71, 0x6b, + 0x6f, 0x71, 0x64, 0x71, 0x77, 0x66, 0x71, 0x72, 0x6e, 0x70, 0x6d, 0x60, + 0x74, 0x79, 0x6e, 0x72, 0x75, 0x70, 0x72, 0x6f, 0x67, 0x75, 0x7b, 0x6f, + 0x73, 0x76, 0x70, 0x74, 0x77, 0x71, 0x75, 0x78, 0x6d, 0x78, 0x78, 0x73, + 0x77, 0x79, 0x74, 0x78, 0x7b, 0x78, 0x7a, 0x77, 0x71, 0x7c, 0x7d, 0x77, + 0x7b, 0x7e, 0x78, 0x7c, 0x7f, 0x79, 0x7d, 0x80, 0x7c, 0x7e, 0x7b, 0x7b, + 0x7f, 0x82, 0x75, 0x81, 0x81, 0x74, 0x82, 0x88, 0x7c, 0x81, 0x83, 0x75, + 0x83, 0x89, 0x7e, 0x82, 0x85, 0x7f, 0x83, 0x86, 0x80, 0x84, 0x87, 0x83, + 0x85, 0x82, 0x7a, 0x88, 0x8e, 0x7c, 0x88, 0x88, 0x7b, 0x89, 0x8f, 0x83, + 0x87, 0x8a, 0x85, 0x87, 0x84, 0x7e, 0x8a, 0x8a, 0x7f, 0x8b, 0x8b, 0x87, + 0x89, 0x86, 0x86, 0x8a, 0x8c, 0x87, 0x8b, 0x8e, 0x89, 0x8b, 0x88, 0x8a, + 0x8c, 0x89, 0x89, 0x8d, 0x90, 0x84, 0x90, 0x91, 0x8c, 0x8f, 0x8c, 0x8b, + 0x8f, 0x91, 0x8e, 0x90, 0x8d, 0x86, 0x92, 0x93, 0x85, 0x93, 0x99, 0x8e, + 0x92, 0x95, 0x90, 0x92, 0x8f, 0x91, 0x93, 0x90, 0x90, 0x94, 0x97, 0x8b, + 0x97, 0x98, 0x92, 0x96, 0x99, 0x94, 0x96, 0x93, 0x93, 0x98, 0x9a, 0x95, + 0x97, 0xa6, 0x8f, 0x9a, 0x9b, 0x94, 0x99, 0x9b, 0x97, 0x99, 0x96, 0x95, + 0x9a, 0x9c, 0x97, 0x9b, 0x9e, 0x91, 0x9d, 0x9d, 0x99, 0x9c, 0x98, 0x93, + 0x9e, 0x9f, 0x98, 0x9d, 0x9f, 0x9b, 0x9e, 0x9a, 0x9b, 0x9f, 0xa2, 0x96, + 0xa1, 0xa2, 0x9d, 0xa0, 0x9d, 0x9c, 0xa1, 0xa3, 0x9e, 0xa1, 0x9e, 0x9d, + 0xa2, 0xa4, 0x98, 0xa4, 0xa4, 0xa0, 0xa2, 0x9f, 0xa1, 0xa3, 0xa0, 0xa3, + 0xa5, 0xa2, 0x9c, 0xa8, 0xa8, 0xa4, 0xa6, 0xa3, 0xa5, 0xa7, 0xa4, 0xa4, + 0xa8, 0xab, 0xa6, 0xa8, 0xa5, 0xa0, 0xac, 0xad, 0xa9, 0xab, 0xa8, 0xa8, + 0xac, 0xaf, 0xa3, 0xaf, 0xb0, 0xad, 0xaf, 0xac, 0xac, 0xb1, 0xb3, 0xaf, + 0xb2, 0xae, 0xae, 0xb3, 0xb5, 0xb0, 0xb3, 0xaf, 0xb1, 0xb4, 0xb0, 0xb0, + 0xb5, 0xb7, 0xb2, 0xb5, 0xb2, 0xb1, 0xb6, 0xb8, 0xb2, 0xb7, 0xba, 0xb4, + 0xb7, 0xb3, 0xb4, 0xb8, 0xbb, 0xb6, 0xb8, 0xb5, 0xb6, 0xba, 0xbd, 0xb8, + 0xba, 0xb7, 0xb7, 0xbb, 0xbe, 0xb9, 0xbb, 0xb8, 0xb8, 0xbc, 0xbf, 0xbb, + 0xbd, 0xba, 0xba, 0xbf, 0xc1, 0xbd, 0xbf, 0xbc, 0xbc, 0xc1, 0xc3, 0xbd, + 0xc2, 0xc4, 0xbf, 0xc2, 0xbe, 0xc0, 0xc3, 0xbf, 0xbf, 0xc4, 0xc6, 0xc1, + 0xc4, 0xc1, 0xc3, 0xc5, 0xc2, 0xc1, 0xc6, 0xc8, 0xc4, 0xc6, 0xc3, 0xc5, + 0xc7, 0xc4, 0xc3, 0xc8, 0xca, 0xc5, 0xc9, 0xcc, 0xc7, 0xc9, 0xc6, 0xc6, + 0xca, 0xcd, 0xc9, 0xcb, 0xc8, 0xc7, 0xcc, 0xce, 0xca, 0xcc, 0xc9, 0xc9, + 0xce, 0xd0, 0xcb, 0xce, 0xca, 0xca, 0xcf, 0xd1, 0xcb, 0xd0, 0xd2, 0xcd, + 0xd0, 0xcc, 0xcc, 0xd1, 0xd4, 0xce, 0xd1, 0xce, 0xcd, 0xd2, 0xd5, 0xd0, + 0xd2, 0xcf, 0xcf, 0xd3, 0xd6, 0xd1, 0xd3, 0xd0, 0xd0, 0xd4, 0xd7, 0xd3, + 0xd6, 0xd2, 0xd2, 0xd6, 0xd9, 0xd5, 0xd7, 0xd4, 0xd3, 0xd8, 0xda, 0xda, + 0xd7, 0xdb, 0xd4, 0xd9, 0xdb, 0xd6, 0xd9, 0xd5, 0xd5, 0xda, 0xdc, 0xd7, + 0xda, 0xd6, 0xd6, 0xdb, 0xde, 0xd8, 0xdb, 0xd7, 0xd7, 0xdc, 0xdf, 0xd9, + 0xdc, 0xd9, 0xd8, 0xdd, 0xe0, 0xdb, 0xdd, 0xda, 0xdc, 0xde, 0xdb, 0xdb, + 0xe0, 0xe2, 0xde, 0xe0, 0xdd, 0xdf, 0xe1, 0xde, 0xdd, 0xe2, 0xe4, 0xe0, + 0xe3, 0xdf, 0xdf, 0xe4, 0xe6, 0xe1, 0xe4, 0xe0, 0xe2, 0xe5, 0xe1, 0xe1, + 0xe6, 0xe9, 0xe3, 0xe6, 0xe2, 0xe5, 0xe7, 0xe4, 0xe3, 0xe8, 0xeb, 0xe5, + 0xe9, 0xec, 0xe7, 0xe9, 0xe6, 0xe6, 0xea, 0xed, 0xe8, 0xea, 0xe7, 0xe7, + 0xec, 0xee, 0xe8, 0xed, 0xef, 0xea, 0xed, 0xe9, 0xe9, 0xee, 0xf0, 0xeb, + 0xee, 0xea, 0xea, 0xef, 0xf2, 0xed, 0xf0, 0xed, 0xec, 0xf1, 0xf4, 0xef, + 0xf1, 0xee, 0xf0, 0xf2, 0xef, 0xf1, 0xf3, 0xf0, 0xf2, 0xf4, 0xf1, 0xf3, + 0xf6, 0xf2, 0xf4, 0xf7, 0xf3, 0xf5, 0xf8, 0xf4, 0xf6, 0xf9, 0xf6, 0xf8, + 0xfa, 0xf7, 0xf9, 0xfb, 0xf8, 0xfa, 0xfc, 0xf9, 0xfb, 0xfd, 0xfa, 0xfc, + 0xff, 0xfb, 0xff, 0xff, 0xff, 0x17, 0x03, 0x0e, 0x9e, 0x00, 0x00, 0x00, + 0x01, 0x74, 0x52, 0x4e, 0x53, 0x00, 0x40, 0xe6, 0xd8, 0x66, 0x00, 0x00, + 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0e, 0xc4, 0x00, 0x00, + 0x0e, 0xc4, 0x01, 0x95, 0x2b, 0x0e, 0x1b, 0x00, 0x00, 0x00, 0x07, 0x74, + 0x49, 0x4d, 0x45, 0x07, 0xe6, 0x01, 0x09, 0x0a, 0x36, 0x3b, 0x79, 0x21, + 0xc9, 0x82, 0x00, 0x00, 0x02, 0x78, 0x49, 0x44, 0x41, 0x54, 0x38, 0xcb, + 0x63, 0x60, 0x20, 0x1b, 0x5c, 0x7c, 0x40, 0x94, 0xb2, 0x4a, 0x4b, 0x95, + 0x5d, 0x44, 0x28, 0x5b, 0xed, 0xd7, 0x13, 0xdc, 0x44, 0x58, 0xd9, 0x33, + 0x97, 0xd8, 0x3b, 0xd3, 0x44, 0x76, 0x10, 0x54, 0xd7, 0x2f, 0x31, 0xe9, + 0xd7, 0x21, 0xd5, 0x05, 0x04, 0xd5, 0xad, 0x34, 0x5d, 0xf2, 0xed, 0x90, + 0x7e, 0x35, 0x41, 0x75, 0x8b, 0xed, 0xb7, 0xfe, 0x38, 0xe3, 0xe4, 0x49, + 0x58, 0x9d, 0xd3, 0xce, 0xdf, 0x57, 0x22, 0xcd, 0x6f, 0x11, 0x52, 0xd7, + 0x6a, 0xb9, 0xf3, 0xf7, 0x83, 0x18, 0x83, 0x8b, 0x84, 0xd4, 0x2d, 0xb0, + 0xdf, 0xfb, 0xe3, 0x71, 0xba, 0xf3, 0x01, 0xbc, 0x8a, 0xee, 0x1f, 0x5f, + 0xeb, 0xa9, 0xbf, 0xf3, 0xd7, 0x83, 0x34, 0x91, 0xf8, 0x82, 0xb2, 0x7b, + 0x38, 0x95, 0xdd, 0xb0, 0xd0, 0xd5, 0x62, 0x56, 0xdd, 0xf6, 0xfb, 0x71, + 0x16, 0x9b, 0x98, 0x25, 0x7f, 0x04, 0x4e, 0x75, 0x07, 0x55, 0x27, 0x67, + 0xb1, 0xb8, 0x5e, 0xf8, 0xfd, 0x65, 0x89, 0x9e, 0xc7, 0xdc, 0x52, 0xc7, + 0x27, 0xb8, 0xd4, 0x6d, 0x34, 0xbe, 0xf0, 0xbc, 0x77, 0xf3, 0xe7, 0x7f, + 0x7f, 0x1f, 0xae, 0xd9, 0x76, 0x63, 0xae, 0xf6, 0x15, 0x1c, 0xca, 0xde, + 0x46, 0x89, 0x6e, 0xf8, 0xfa, 0xee, 0xfb, 0xdf, 0x7f, 0xff, 0xfe, 0xfd, + 0xf8, 0xfa, 0x79, 0x02, 0x67, 0xce, 0x69, 0xec, 0xea, 0x26, 0x09, 0x05, + 0x6c, 0xfe, 0xf5, 0x0f, 0x0a, 0xbe, 0x6d, 0x8d, 0x95, 0x73, 0x5b, 0x87, + 0x45, 0xd5, 0xe9, 0xd5, 0x22, 0xde, 0x9b, 0x3f, 0xfd, 0x83, 0x83, 0xaf, + 0xfb, 0x7b, 0xec, 0x03, 0xbb, 0x0f, 0xa2, 0x2b, 0x3b, 0x6f, 0x60, 0x5c, + 0x71, 0xec, 0x0b, 0x50, 0x1e, 0x64, 0x2d, 0x98, 0xf8, 0xf3, 0x78, 0x55, + 0x98, 0x8c, 0xdd, 0xf4, 0x77, 0x28, 0xa9, 0x69, 0x71, 0x90, 0xe5, 0xd4, + 0x07, 0x7f, 0xfe, 0xfc, 0x7e, 0xf3, 0xf8, 0xf9, 0x37, 0xa0, 0x59, 0x0f, + 0x6f, 0xbf, 0xfc, 0xfa, 0xfb, 0xc7, 0xe7, 0x9d, 0x3d, 0x9a, 0xd2, 0x5b, + 0x9e, 0x21, 0x94, 0xdd, 0xaa, 0x61, 0xcf, 0xdb, 0xfc, 0xf2, 0xcb, 0x93, + 0xcd, 0xb3, 0xea, 0x8a, 0x4b, 0x4f, 0xfd, 0xfe, 0xbc, 0x2d, 0x33, 0x29, + 0xbd, 0xa5, 0x77, 0xff, 0xbb, 0xcf, 0x67, 0x96, 0x66, 0xf8, 0x15, 0xee, + 0x81, 0xc7, 0x95, 0xa7, 0x7d, 0xe6, 0x89, 0xdb, 0x87, 0x96, 0x64, 0x98, + 0x8a, 0x49, 0x70, 0x71, 0xcf, 0xf8, 0xfa, 0xbd, 0x57, 0x80, 0x35, 0xdc, + 0x5f, 0xc2, 0x3a, 0xbf, 0x76, 0xe7, 0xcd, 0x25, 0x1d, 0x3a, 0xd9, 0xd0, + 0x80, 0xbc, 0xa5, 0x26, 0x39, 0x75, 0xfd, 0x8a, 0x09, 0x0a, 0x96, 0x21, + 0x5d, 0x07, 0xf6, 0x45, 0xf0, 0x2f, 0xf9, 0xfc, 0x65, 0xa2, 0x7c, 0xe2, + 0xdd, 0xd7, 0xcb, 0x9b, 0x4b, 0x35, 0x8c, 0xd3, 0x7a, 0xfa, 0xf2, 0x55, + 0x96, 0x42, 0xcd, 0xf3, 0x8f, 0xeb, 0x29, 0xe2, 0x31, 0x4d, 0x00, 0xbb, + 0xe4, 0xbc, 0xfb, 0xac, 0xcf, 0x5f, 0xfa, 0xb4, 0xf6, 0x41, 0x22, 0x28, + 0xd5, 0xd0, 0xc4, 0x52, 0x96, 0x73, 0x2d, 0x34, 0x1f, 0x1a, 0xa9, 0xf3, + 0x69, 0x94, 0x41, 0x64, 0x18, 0xce, 0x6a, 0xcd, 0xfc, 0xfa, 0x69, 0x82, + 0x12, 0x2c, 0xe1, 0x6f, 0xdc, 0x55, 0x2e, 0x1c, 0x0b, 0x65, 0xbf, 0x6a, + 0xb0, 0xdb, 0x72, 0x1d, 0xe6, 0xd6, 0x03, 0x62, 0x93, 0x3e, 0x7d, 0xed, + 0x51, 0x9a, 0x86, 0xf0, 0xe4, 0x76, 0x44, 0x9a, 0x3d, 0x8b, 0x10, 0xbd, + 0xab, 0xd8, 0xf1, 0xfc, 0xd7, 0x44, 0xc5, 0x29, 0x04, 0x13, 0x7e, 0x70, + 0xf2, 0xed, 0x4f, 0x8d, 0x06, 0xc7, 0x09, 0x16, 0x1a, 0xbe, 0x21, 0x57, + 0xbe, 0x94, 0xf0, 0xce, 0x23, 0xa0, 0xec, 0xf5, 0x6c, 0xa3, 0xe8, 0x8b, + 0x5f, 0xeb, 0xe5, 0x52, 0x08, 0x64, 0x90, 0x36, 0x0d, 0xbf, 0x65, 0x9f, + 0xfe, 0x5d, 0xca, 0xe3, 0xb7, 0x5b, 0x8a, 0x4f, 0xd9, 0x5a, 0x71, 0xaf, + 0x25, 0x1f, 0xff, 0xfd, 0xfe, 0x72, 0x6a, 0x82, 0xba, 0x14, 0x1e, 0x27, + 0x3e, 0xb0, 0x09, 0x3a, 0xf1, 0xe9, 0xce, 0xde, 0x8d, 0x87, 0x5e, 0x9e, + 0x5a, 0x1d, 0xd9, 0xfc, 0x1a, 0x77, 0x91, 0x26, 0xd7, 0xb7, 0x6b, 0x4e, + 0xba, 0xad, 0x8e, 0x75, 0xde, 0x8c, 0xb9, 0xa1, 0xbc, 0xeb, 0x70, 0xaa, + 0x9b, 0x2d, 0x1b, 0xed, 0xcf, 0xcf, 0xc1, 0xc4, 0xa8, 0x3d, 0xcd, 0xc7, + 0x4a, 0x20, 0xe6, 0x19, 0x4e, 0x75, 0x5b, 0x94, 0x03, 0x1b, 0x96, 0x9e, + 0xac, 0x32, 0x5b, 0xf9, 0xec, 0x74, 0xb5, 0xc3, 0x49, 0x7c, 0xa5, 0xdf, + 0x8b, 0x57, 0x40, 0xf2, 0x1c, 0xc3, 0x5b, 0x86, 0xfb, 0x17, 0x19, 0x68, + 0x0a, 0x00, 0x52, 0xa4, 0x31, 0x22, 0xfa, 0xbb, 0x4c, 0x44, 0x00, 0x00, + 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; +unsigned int plane_png_len = 1522; +unsigned char helicopter_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x1f, + 0x04, 0x03, 0x00, 0x00, 0x00, 0xed, 0x80, 0x0b, 0x0a, 0x00, 0x00, 0x00, + 0x15, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xf0, + 0xf8, 0xff, 0xff, 0x00, 0x00, 0xff, 0x20, 0x20, 0x20, 0x20, 0x20, 0xff, + 0xff, 0xff, 0x96, 0xba, 0xb8, 0x46, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, + 0x59, 0x73, 0x00, 0x00, 0x0b, 0x12, 0x00, 0x00, 0x0b, 0x12, 0x01, 0xd2, + 0xdd, 0x7e, 0xfc, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, + 0xe6, 0x01, 0x09, 0x0a, 0x36, 0x2f, 0x63, 0xfb, 0x1d, 0xff, 0x00, 0x00, + 0x00, 0x70, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x9d, 0xd1, 0xc1, 0x0d, + 0x80, 0x20, 0x0c, 0x05, 0x50, 0x2e, 0xde, 0x6d, 0x52, 0x16, 0x20, 0x6e, + 0x00, 0x0b, 0x54, 0x3a, 0x80, 0x3d, 0xb8, 0xff, 0x2a, 0x62, 0x89, 0x26, + 0x68, 0x7b, 0xd0, 0x7f, 0x21, 0x7d, 0x49, 0x39, 0xfc, 0x06, 0x18, 0x33, + 0x87, 0x3f, 0x10, 0xf7, 0x3b, 0x62, 0x83, 0x26, 0xca, 0xe3, 0x0f, 0x0f, + 0x90, 0x3c, 0x40, 0x66, 0xd2, 0xc7, 0x81, 0x36, 0xd6, 0xc2, 0xa4, 0x6a, + 0x02, 0x72, 0xcd, 0x39, 0x17, 0xd6, 0x35, 0x0b, 0xfa, 0x7c, 0x0a, 0x81, + 0x0d, 0x97, 0x14, 0x02, 0x0f, 0xba, 0xf4, 0x0d, 0x07, 0x00, 0xd7, 0x94, + 0x16, 0x02, 0x1f, 0x5a, 0x89, 0xdb, 0xa4, 0x05, 0x7a, 0xa0, 0x24, 0x43, + 0x85, 0x2f, 0xf8, 0x74, 0xca, 0x03, 0x65, 0xe5, 0x3e, 0xf8, 0xf3, 0x53, + 0x78, 0x63, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, + 0x60, 0x82 +}; +unsigned int helicopter_png_len = 242; +unsigned char aisnavaid_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x15, + 0x01, 0x03, 0x00, 0x00, 0x00, 0x93, 0xd9, 0x37, 0xd5, 0x00, 0x00, 0x00, + 0x06, 0x50, 0x4c, 0x54, 0x45, 0x63, 0x65, 0x6c, 0x00, 0x00, 0x00, 0x0b, + 0x25, 0x74, 0xbd, 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, 0x4e, 0x53, 0x00, + 0x40, 0xe6, 0xd8, 0x66, 0x00, 0x00, 0x00, 0x01, 0x62, 0x4b, 0x47, 0x44, + 0x00, 0x88, 0x05, 0x1d, 0x48, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, + 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, 0x01, 0x00, 0x9a, + 0x9c, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, 0xe6, + 0x01, 0x09, 0x0a, 0x22, 0x0e, 0x01, 0x3c, 0xda, 0xf4, 0x00, 0x00, 0x00, + 0x16, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0xf8, 0xff, 0xff, 0x07, + 0x03, 0x0c, 0x3f, 0x28, 0xb0, 0xc0, 0x8a, 0x49, 0x54, 0x03, 0x00, 0x6b, + 0xd8, 0x2d, 0x07, 0x1f, 0xad, 0xa8, 0xbc, 0x00, 0x00, 0x00, 0x00, 0x49, + 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; +unsigned int aisnavaid_png_len = 163; + +unsigned char aiswhite_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x22, + 0x02, 0x03, 0x00, 0x00, 0x00, 0xcd, 0x06, 0x7a, 0x10, 0x00, 0x00, 0x00, + 0x09, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x38, 0x24, 0x6b, 0x47, 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, + 0x4e, 0x53, 0x00, 0x40, 0xe6, 0xd8, 0x66, 0x00, 0x00, 0x00, 0x01, 0x62, + 0x4b, 0x47, 0x44, 0x00, 0x88, 0x05, 0x1d, 0x48, 0x00, 0x00, 0x00, 0x09, + 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, + 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, + 0x45, 0x07, 0xe6, 0x01, 0x09, 0x0a, 0x36, 0x16, 0x3c, 0xfe, 0x95, 0xf7, + 0x00, 0x00, 0x00, 0x53, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0x60, + 0x80, 0x01, 0x16, 0x3c, 0x94, 0x28, 0x84, 0x92, 0x44, 0xa1, 0x22, 0x1d, + 0xc0, 0x54, 0x16, 0x16, 0x8a, 0x31, 0x2b, 0x00, 0x4c, 0xad, 0x9a, 0x80, + 0x49, 0xb1, 0xae, 0x9a, 0x02, 0xa2, 0xd8, 0x56, 0x2d, 0x41, 0xa6, 0x96, + 0x21, 0xf3, 0xc4, 0x56, 0x2d, 0x05, 0x51, 0x52, 0xab, 0x56, 0x12, 0xa2, + 0xa2, 0x56, 0xad, 0x04, 0x59, 0x98, 0xb5, 0x6a, 0x15, 0x06, 0xc5, 0x08, + 0xa4, 0x02, 0x40, 0xb6, 0xae, 0x02, 0xd9, 0xcb, 0x18, 0x1a, 0x1a, 0x1a, + 0xc0, 0x00, 0x00, 0xd1, 0xed, 0x24, 0x45, 0x35, 0x85, 0xab, 0x6c, 0x00, + 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; +unsigned int aiswhite_png_len = 227; +char * get_info_call() +{ + char Msg[] = + "" + "" + "" + "" + "" + "\r\n" + " \r\n" + "\r\n" + "##MY_CALLSIGN##'s BPQ32 Web Server\r\n" + "\r\n" + + "\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Home\r\n" + "All Stations\r\n" + "RF Stations\r\n" + "All WX Stations\r\n" + "RF WX Stations\r\n" + "All Mobile Stations\r\n" + "RF Mobile Stations\r\n" + "All Objects\r\n" + "RF Objects\r\n" + "Information\r\n" + "Node Pages\r\n" + "\r\n" + "\r\n" + + "\r\n" + "

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" + + "

(This page will automatically refresh every five minutes)

\r\n" + + "

Information about\r\n" + "##CALLSIGN##
\r\n" + "Click the callsign to look it up on qrz.com
\r\n" + "Location: ##LOCATION##
\r\n" + "The bearing from ##MY_CALLSIGN## to ##CALLSIGN## is\r\n" + "##BEARING## degrees, the distance is ##DISTANCE## ##MILES_KM##

\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "
Last posit: ##FRAME_HEADER##
##FRAME_INFO##
\r\n" + + "

Status: ##STATUS_TEXT##
\r\n" + "Last heard ##LAST_HEARD## ago

\r\n" + + "

" + + "\r\n" + "\r\n" + "\r\n"; + + return _strdup(Msg);; +} + +char * get_infomobile_call() +{ + char Msg[] = + "" + "" + "" + "" + "" + "\r\n" + " \r\n" + "\r\n" + "##MY_CALLSIGN##'s BPQ32 Web Server\r\n" + "\r\n" + + "\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Home\r\n" + "All Stations\r\n" + "RF Stations\r\n" + "All WX Stations\r\n" + "RF WX Stations\r\n" + "All Mobile Stations\r\n" + "RF Mobile Stations\r\n" + "All Objects\r\n" + "RF Objects\r\n" + "Information\r\n" + "Node Pages\r\n" + "\r\n" + "\r\n" + + "\r\n" + "

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" + + "

(This page will automatically refresh every five minutes)

\r\n" + + "

Information about\r\n" + "##CALLSIGN##
\r\n" + "Click the callsign to look it up on qrz.com
\r\n" + "Location: ##LOCATION##
\r\n" + "The bearing from ##MY_CALLSIGN## to ##CALLSIGN## is\r\n" + "##BEARING## degrees, the distance is ##DISTANCE## ##MILES_KM##

\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "
Last posit: ##FRAME_HEADER##
##FRAME_INFO##
\r\n" + + "

Course ##COURSE## degrees, speed ##SPEED_MPH##mph
" + "Status: ##STATUS_TEXT##
" + + "Last heard ##LAST_HEARD## ago

\r\n" + + "

" + + "\r\n" + "\r\n" + "\r\n"; + + return _strdup(Msg);; +} + +char * get_infoobj_call() +{ + char Msg[] = + "" + "" + "" + "" + "" + "\r\n" + " \r\n" + "\r\n" + "##MY_CALLSIGN##'s BPQ32 Web Server\r\n" + "\r\n" + + "\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Home\r\n" + "All Stations\r\n" + "RF Stations\r\n" + "All WX Stations\r\n" + "RF WX Stations\r\n" + "All Mobile Stations\r\n" + "RF Mobile Stations\r\n" + "All Objects\r\n" + "RF Objects\r\n" + "Information\r\n" + "Node Pages\r\n" + "\r\n" + "\r\n" + + "\r\n" + "

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" + + "

(This page will automatically refresh every five minutes)

\r\n" + + "

Information about Object ##CALLSIGN##

" + + "Location: ##LOCATION##
\r\n" + + "The bearing from ##MY_CALLSIGN## to ##CALLSIGN## is\r\n" + "##BEARING## degrees, the distance is ##DISTANCE## ##MILES_KM##

\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "
Last posit: ##FRAME_HEADER##
##FRAME_INFO##
\r\n" + + "

Last Report: ##LASTPACKET##
" + + "Last heard ##LAST_HEARD## ago

\r\n" + + "

" + + "\r\n" + "\r\n" + "\r\n"; + + return _strdup(Msg);; +} + + +char * get_infowx_call() +{ + char Msg[] = + "" + "" + "" + "" + "" + "\r\n" + " \r\n" + "\r\n" + "##MY_CALLSIGN##'s BPQ32 Web Server\r\n" + "\r\n" + + "\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Home\r\n" + "All Stations\r\n" + "RF Stations\r\n" + "All WX Stations\r\n" + "RF WX Stations\r\n" + "All Mobile Stations\r\n" + "RF Mobile Stations\r\n" + "All Objects\r\n" + "RF Objects\r\n" + "Information\r\n" + "Node Pages\r\n" + "\r\n" + "\r\n" + + "\r\n" + "

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" + + "

(This page will automatically refresh every five minutes)

\r\n" + + "

Information about\r\n" + "##CALLSIGN##
\r\n" + "Click the callsign to look it up on qrz.com
\r\n" + "Location: ##LOCATION##
\r\n" + "The bearing from ##MY_CALLSIGN## to ##CALLSIGN## is\r\n" + "##BEARING## degrees, the distance is ##DISTANCE## ##MILES_KM##

\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "
Last posit: ##FRAME_HEADER##
##FRAME_INFO##
\r\n" + + "

Status: ##STATUS_TEXT##
\r\n" + "Last heard ##LAST_HEARD## ago

\r\n" + + "

Weather Data

\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "
MeasurementImperial
Wind speed##WIND_SPEED_MPH## mph
Wind gust##WIND_GUST_MPH## mph
Wind direction##WIND_DIRECTION## deg
Temperature##TEMPERATURE_F##F
Pressure##PRESSURE_HPA## hPa/mb
Humidity##HUMIDITY##%
Rain last hour##RAIN_HOUR_IN##\"
Rain today##RAIN_TODAY_IN##\"
Rain last 24 hours##RAIN_24_IN##\"
\r\n" + + "

" + + "\r\n" + "\r\n" + "\r\n"; + + return _strdup(Msg);; +} + + +char * get_all() +{ + char Msg[] = + "" + "" + "" + "" + "" + "\r\n" + " \r\n" + "\r\n" + "##MY_CALLSIGN##'s BPQ32 Web Server\r\n" + "\r\n" + + "\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Home\r\n" + "All Stations\r\n" + "RF Stations\r\n" + "All WX Stations\r\n" + "RF WX Stations\r\n" + "All Mobile Stations\r\n" + "RF Mobile Stations\r\n" + "All Objects\r\n" + "RF Objects\r\n" + "Information\r\n" + "Node Pages\r\n" + "\r\n" + "\r\n" + + "\r\n" + "

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" + + "

All Stations

\r\n" + + "

(This page will automatically refresh every five minutes)

\r\n" + + "

The following is a list of all the stations heard in the past ##EXPIRE_TIME## minutes,
both on RF and on the internet.

" + "

There are ##TABLE_COUNT## callsigns in the list,
click a callsign to get an information page for that station.

" + + "" + "##STATION_TABLE##
\r\n" + "\r\n" + "\r\n"; + + return _strdup(Msg);; +} + +char * get_mobileall() +{ + char Msg[] = + "" + "" + "" + "" + "" + "\r\n" + " \r\n" + "\r\n" + "##MY_CALLSIGN##'s BPQ32 Web Server\r\n" + "\r\n" + + "\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Home\r\n" + "All Stations\r\n" + "RF Stations\r\n" + "All WX Stations\r\n" + "RF WX Stations\r\n" + "All Mobile Stations\r\n" + "RF Mobile Stations\r\n" + "All Objects\r\n" + "RF Objects\r\n" + "Information\r\n" + "Node Pages\r\n" + "\r\n" + "\r\n" + + "\r\n" + "

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" + + "

All Mobile Stations

\r\n" + + "

(This page will automatically refresh every five minutes)

\r\n" + + "

The following is a list of all the mobile stations heard in the past ##EXPIRE_TIME## minutes,
both on RF and on the internet.

" + "

There are ##TABLE_COUNT## callsigns in the list,
click a callsign to get an information page for that station.

" + + "" + "##STATION_TABLE##
\r\n" + "\r\n" + "\r\n"; + + return _strdup(Msg);; +} + +char * get_obj() +{ + char Msg[] = + "" + "" + "" + "" + "" + "\r\n" + " \r\n" + "\r\n" + "##MY_CALLSIGN##'s BPQ32 Web Server\r\n" + "\r\n" + + "\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Home\r\n" + "All Stations\r\n" + "RF Stations\r\n" + "All WX Stations\r\n" + "RF WX Stations\r\n" + "All Mobile Stations\r\n" + "RF Mobile Stations\r\n" + "All Objects\r\n" + "RF Objects\r\n" + "Information\r\n" + "Node Pages\r\n" + "\r\n" + "\r\n" + + "\r\n" + "

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" + + "

All Objects

\r\n" + + "

(This page will automatically refresh every five minutes)

\r\n" + + "

The following is a list of all the objects heard in the past ##EXPIRE_TIME## minutes,
both on RF and on the internet.

" + "

There are ##TABLE_COUNT## callsigns in the list,
click a callsign to get an information page for that station.

" + + "" + "##STATION_TABLE##
\r\n" + "\r\n" + "\r\n"; + + return _strdup(Msg);; +} + + +char * get_noinfo() +{ + char Msg[] = + "" + "" + "" + "" + "" + "\r\n" + " \r\n" + "\r\n" + "##MY_CALLSIGN##'s BPQ32 Web Server\r\n" + "\r\n" + + "\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Home\r\n" + "All Stations\r\n" + "RF Stations\r\n" + "All WX Stations\r\n" + "RF WX Stations\r\n" + "All Mobile Stations\r\n" + "RF Mobile Stations\r\n" + "All Objects\r\n" + "RF Objects\r\n" + "Information\r\n" + "Node Pages\r\n" + "\r\n" + "\r\n" + + "\r\n" + "

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" + "

No information is available for ##CALLSIGN##

\r\n" + "\r\n"; + + return _strdup(Msg);; +} + +char * get_wxall() +{ + char Msg[] = + "" + "" + "" + "" + "" + "\r\n" + " \r\n" + "\r\n" + "##MY_CALLSIGN##'s BPQ32 Web Server\r\n" + "\r\n" + + "\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Home\r\n" + "All Stations\r\n" + "RF Stations\r\n" + "All WX Stations\r\n" + "RF WX Stations\r\n" + "All Mobile Stations\r\n" + "RF Mobile Stations\r\n" + "All Objects\r\n" + "RF Objects\r\n" + "Information\r\n" + "Node Pages\r\n" + "\r\n" + "\r\n" + + "\r\n" + "

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" + + "

All WX Stations

\r\n" + + "

(This page will automatically refresh every five minutes)

\r\n" + + "

The following is a list of all the WX stations heard in the past ##EXPIRE_TIME## minutes,
both on RF and on the internet.

" + "

There are ##TABLE_COUNT## callsigns in the list,
click a callsign to get an information page for that station.

" + + "" + "##STATION_TABLE##
\r\n" + "\r\n" + "\r\n"; + + return _strdup(Msg);; +} + +char * get_info() +{ + char Msg[] = + "" + "" + "" + "" + "##MY_CALLSIGN##'s BPQ32 Web Server\r\n" + "\r\n" + + "\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Home\r\n" + "All Stations\r\n" + "RF Stations\r\n" + "All WX Stations\r\n" + "RF WX Stations\r\n" + "All Mobile Stations\r\n" + "RF Mobile Stations\r\n" + "All Objects\r\n" + "RF Objects\r\n" + "Information\r\n" + "Node Pages\r\n" + "\r\n" + "\r\n" + + "\r\n" + "

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" + + "

Information

\r\n" + + "\r\n" + "
\r\n" + "

BPQ-WebServer is a simple plug-in web server for BPQ32. It will dynamically serve a selection of pages created from the data in BPQ32. The pages currently available are -

\r\n" + + "
    " + "
  • index.html - return to the home page
  • \r\n" + "
  • all.html - a list of all the stations heard
  • \r\n" + "
  • allrf.html - a list of all stations heard on RF
  • \r\n" + "
  • mobilesall.html - a list of all mobiles stations heard
  • \r\n" + "
  • mobilesrf.html - a list of all the mobile stations heard on RF
  • \r\n" + "
  • wxall.html - a list of all WX stations heard
  • \r\n" + "
  • wxrf.html - a list of all the WX stations heard on RF
  • \r\n" + "
  • obj.html - a list of all objects heard
  • \r\n" + "
  • objrf.html - a list of all objects heard on RF
  • \r\n" + "
" + + "

The dynamic pages use templates, so the user can make them as pretty or as simple as he wishes, including using graphics.

" + + "

Besides serving dynamic pages, BPQ-WebServer can also serve other pages that you may wish to make available. For example, this page is made available simply by putting it in BPQ-WebServer's HTML folder, and adding a link to it to the other pages.

" + "
\r\n"; + + return _strdup(Msg);; +} + +char * get_allrf() +{ + char Msg[] = + "" + "" + "" + "" + "" + "\r\n" + " \r\n" + "\r\n" + "##MY_CALLSIGN##'s BPQ32 Web Server\r\n" + "\r\n" + + "\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Home\r\n" + "All Stations\r\n" + "RF Stations\r\n" + "All WX Stations\r\n" + "RF WX Stations\r\n" + "All Mobile Stations\r\n" + "RF Mobile Stations\r\n" + "All Objects\r\n" + "RF Objects\r\n" + "Information\r\n" + "Node Pages\r\n" + "\r\n" + "\r\n" + + "\r\n" + "

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" + + "

RF Stations

\r\n" + + "

(This page will automatically refresh every five minutes)

\r\n" + + "

The following is a list of all the stations heard on RF in the past ##EXPIRE_TIME## minutes.

" + + "

'*' after a callsign means that it was heard via a digi

" + + "" + "" + "" + "" + "
The list only includes callsigns heard on RF, direct or via digipeaters.
It does not include callsigns heard on the internet,
or heard as third-party RF traffic via IGATEs.
" + + "

There are ##TABLE_COUNT## callsigns in the list,
click a callsign to get an information page for that station.

" + + "\r\n" + "\r\n" + "\r\n" + "##STATION_TABLE##
CallsignSymbolLocation##MILES_KM##BearingLast heard
\r\n" + + "\r\n" + "\r\n"; + + return _strdup(Msg);; +} + +char * get_mobilesrf() +{ + char Msg[] = + "" + "" + "" + "" + "" + "\r\n" + " \r\n" + "\r\n" + "##MY_CALLSIGN##'s BPQ32 Web Server\r\n" + "\r\n" + + "\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Home\r\n" + "All Stations\r\n" + "RF Stations\r\n" + "All WX Stations\r\n" + "RF WX Stations\r\n" + "All Mobile Stations\r\n" + "RF Mobile Stations\r\n" + "All Objects\r\n" + "RF Objects\r\n" + "Information\r\n" + "Node Pages\r\n" + "\r\n" + "\r\n" + + "\r\n" + "

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" + + "

Mobile RF Stations

\r\n" + + "

(This page will automatically refresh every five minutes)

\r\n" + + "

The following is a list of all the mobile stations heard on RF in the past ##EXPIRE_TIME## minutes.

" + + "

'*' after a callsign means that it was heard via a digi

" + + // "" + // "" + // "" + // "" + // "
The list only includes callsigns heard on RF, direct or via digipeaters.
It does not include callsigns heard on the internet,
or heard as third-party RF traffic via IGATEs.
" + + "

There are ##TABLE_COUNT## callsigns in the list,
click a callsign to get an information page for that station.

" + + "\r\n" + "\r\n" + "\r\n" + "##STATION_TABLE##
CallsignSymbolLocation##MILES_KM##BearingLast heard
\r\n" + + "\r\n" + "\r\n"; + + return _strdup(Msg);; +} + +char * get_objrf() +{ + char Msg[] = + "" + "" + "" + "" + "" + "\r\n" + " \r\n" + "\r\n" + "##MY_CALLSIGN##'s BPQ32 Web Server\r\n" + "\r\n" + + "\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Home\r\n" + "All Stations\r\n" + "RF Stations\r\n" + "All WX Stations\r\n" + "RF WX Stations\r\n" + "All Mobile Stations\r\n" + "RF Mobile Stations\r\n" + "All Objects\r\n" + "RF Objects\r\n" + "Information\r\n" + "Node Pages\r\n" + "\r\n" + "\r\n" + + "\r\n" + "

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" + + "

RF Objects

\r\n" + + "

(This page will automatically refresh every five minutes)

\r\n" + + "

The following is a list of all the objects heard on RF in the past ##EXPIRE_TIME## minutes.

" + + "

'*' after a callsign means that it was heard via a digi

" + + // "" + // "" + // "" + // "" + // "
The list only includes callsigns heard on RF, direct or via digipeaters.
It does not include callsigns heard on the internet,
or heard as third-party RF traffic via IGATEs.
" + + "

There are ##TABLE_COUNT## callsigns in the list,
click a callsign to get an information page for that station.

" + + "\r\n" + "\r\n" + "\r\n" + "##STATION_TABLE##
CallsignSymbolLocation##MILES_KM##BearingLast heard
\r\n" + + "\r\n" + "\r\n"; + + return _strdup(Msg);; +} + +char * get_wxrf() +{ + char Msg[] = + "" + "" + "" + "" + "" + "\r\n" + " \r\n" + "\r\n" + "##MY_CALLSIGN##'s BPQ32 Web Server\r\n" + "\r\n" + + "\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Home\r\n" + "All Stations\r\n" + "RF Stations\r\n" + "All WX Stations\r\n" + "RF WX Stations\r\n" + "All Mobile Stations\r\n" + "RF Mobile Stations\r\n" + "All Objects\r\n" + "RF Objects\r\n" + "Information\r\n" + "Node Pages\r\n" + "\r\n" + "\r\n" + + "\r\n" + "

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" + + "

RF WX Stations

\r\n" + + "

(This page will automatically refresh every five minutes)

\r\n" + + "

The following is a list of all the WX stations heard on RF in the past ##EXPIRE_TIME## minutes.

" + + "

'*' after a callsign means that it was heard via a digi

" + + // "" + // "" + // "" + // "" + // "
The list only includes callsigns heard on RF, direct or via digipeaters.
It does not include callsigns heard on the internet,
or heard as third-party RF traffic via IGATEs.
" + + "

There are ##TABLE_COUNT## callsigns in the list,
click a callsign to get an information page for that station.

" + + "\r\n" + "\r\n" + "\r\n" + "##STATION_TABLE##
CallsignSymbolLocation##MILES_KM##BearingLast heard
\r\n" + + "\r\n" + "\r\n"; + + return _strdup(Msg);; +} + +char * get_favicon(int * Len) +{ + unsigned char * ptr; + + unsigned char Msg[] = + { + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x20, 0x20, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xE8, 0x02, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x28, 0x00, + 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0xC0, 0xC0, + 0xC0, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xB0, 0x00, 0x00, 0x0B, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0x00, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xB0, 0x0B, 0xB0, 0x00, 0x0B, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xB0, 0x0B, 0xBB, 0xB0, 0x0B, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xB0, 0x0B, 0xBB, 0xB0, + 0x0B, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xB0, 0x0B, 0xBB, 0xB0, 0x0B, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xB0, 0x0B, 0xBB, 0xB0, 0x0B, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xB0, 0x0B, 0xBB, 0xB0, + 0x0B, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, + 0xBB, 0x00, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0x00, 0xBB, 0xBB, 0xBB, 0xBB, 0xB0, 0x00, 0x0B, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0x00, 0x00, 0x00, 0x0B, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0x00, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0x00, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, + 0x00, 0x00, 0x0B, 0xBB, 0x00, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0x00, 0xBB, 0x00, 0x00, 0x00, 0x0B, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0x00, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, + 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, 0x00, 0x00, 0x0B, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, + 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0x00, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, + 0x00, 0x00, 0x0B, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 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, 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, 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, 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, + }; + +#define favicon_LENGTH 0x000002FE + + ptr = malloc(favicon_LENGTH); + memcpy(ptr, Msg, favicon_LENGTH); + + return ptr; +} + +static unsigned char Clouds[] = +{ + 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, + 0x02, 0x01, 0x00, 0x48, 0x00, 0x48, 0x00, 0x00, 0xFF, 0xE1, 0x08, 0x03, + 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x4D, 0x4D, 0x00, 0x2A, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x07, 0x01, 0x12, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x1A, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x62, 0x01, 0x1B, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x6A, 0x01, 0x28, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x02, 0x00, 0x00, 0x01, 0x31, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, + 0x00, 0x00, 0x00, 0x72, 0x01, 0x32, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, + 0x00, 0x00, 0x00, 0x86, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x9C, 0x00, 0x00, 0x00, 0xC8, 0x00, 0x00, 0x00, 0x48, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, + 0x41, 0x64, 0x6F, 0x62, 0x65, 0x20, 0x50, 0x68, 0x6F, 0x74, 0x6F, 0x73, + 0x68, 0x6F, 0x70, 0x20, 0x37, 0x2E, 0x30, 0x00, 0x32, 0x30, 0x31, 0x32, + 0x3A, 0x30, 0x35, 0x3A, 0x33, 0x30, 0x20, 0x31, 0x32, 0x3A, 0x33, 0x38, + 0x3A, 0x31, 0x32, 0x00, 0x00, 0x00, 0x00, 0x03, 0xA0, 0x01, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x00, 0x00, 0xA0, 0x02, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0xF4, 0xA0, 0x03, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0xF4, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x06, 0x00, 0x00, 0x01, 0x1A, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x01, 0x16, 0x01, 0x1B, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x01, 0x1E, 0x01, 0x28, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x02, 0x00, 0x00, 0x02, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x01, 0x26, 0x02, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x06, 0xD5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, + 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, + 0x02, 0x01, 0x00, 0x48, 0x00, 0x48, 0x00, 0x00, 0xFF, 0xED, 0x00, 0x0C, + 0x41, 0x64, 0x6F, 0x62, 0x65, 0x5F, 0x43, 0x4D, 0x00, 0x02, 0xFF, 0xEE, + 0x00, 0x0E, 0x41, 0x64, 0x6F, 0x62, 0x65, 0x00, 0x64, 0x80, 0x00, 0x00, + 0x00, 0x01, 0xFF, 0xDB, 0x00, 0x84, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x09, + 0x08, 0x0C, 0x09, 0x09, 0x0C, 0x11, 0x0B, 0x0A, 0x0B, 0x11, 0x15, 0x0F, + 0x0C, 0x0C, 0x0F, 0x15, 0x18, 0x13, 0x13, 0x15, 0x13, 0x13, 0x18, 0x11, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x11, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x01, + 0x0D, 0x0B, 0x0B, 0x0D, 0x0E, 0x0D, 0x10, 0x0E, 0x0E, 0x10, 0x14, 0x0E, + 0x0E, 0x0E, 0x14, 0x14, 0x0E, 0x0E, 0x0E, 0x0E, 0x14, 0x11, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x11, 0x11, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x11, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0x80, 0x00, + 0x80, 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF, + 0xDD, 0x00, 0x04, 0x00, 0x08, 0xFF, 0xC4, 0x01, 0x3F, 0x00, 0x00, 0x01, + 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x01, 0x02, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0A, 0x0B, 0x01, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x10, 0x00, 0x01, 0x04, 0x01, + 0x03, 0x02, 0x04, 0x02, 0x05, 0x07, 0x06, 0x08, 0x05, 0x03, 0x0C, 0x33, + 0x01, 0x00, 0x02, 0x11, 0x03, 0x04, 0x21, 0x12, 0x31, 0x05, 0x41, 0x51, + 0x61, 0x13, 0x22, 0x71, 0x81, 0x32, 0x06, 0x14, 0x91, 0xA1, 0xB1, 0x42, + 0x23, 0x24, 0x15, 0x52, 0xC1, 0x62, 0x33, 0x34, 0x72, 0x82, 0xD1, 0x43, + 0x07, 0x25, 0x92, 0x53, 0xF0, 0xE1, 0xF1, 0x63, 0x73, 0x35, 0x16, 0xA2, + 0xB2, 0x83, 0x26, 0x44, 0x93, 0x54, 0x64, 0x45, 0xC2, 0xA3, 0x74, 0x36, + 0x17, 0xD2, 0x55, 0xE2, 0x65, 0xF2, 0xB3, 0x84, 0xC3, 0xD3, 0x75, 0xE3, + 0xF3, 0x46, 0x27, 0x94, 0xA4, 0x85, 0xB4, 0x95, 0xC4, 0xD4, 0xE4, 0xF4, + 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5, 0x56, 0x66, 0x76, 0x86, 0x96, 0xA6, + 0xB6, 0xC6, 0xD6, 0xE6, 0xF6, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, + 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7, 0x11, 0x00, 0x02, 0x02, 0x01, 0x02, + 0x04, 0x04, 0x03, 0x04, 0x05, 0x06, 0x07, 0x07, 0x06, 0x05, 0x35, 0x01, + 0x00, 0x02, 0x11, 0x03, 0x21, 0x31, 0x12, 0x04, 0x41, 0x51, 0x61, 0x71, + 0x22, 0x13, 0x05, 0x32, 0x81, 0x91, 0x14, 0xA1, 0xB1, 0x42, 0x23, 0xC1, + 0x52, 0xD1, 0xF0, 0x33, 0x24, 0x62, 0xE1, 0x72, 0x82, 0x92, 0x43, 0x53, + 0x15, 0x63, 0x73, 0x34, 0xF1, 0x25, 0x06, 0x16, 0xA2, 0xB2, 0x83, 0x07, + 0x26, 0x35, 0xC2, 0xD2, 0x44, 0x93, 0x54, 0xA3, 0x17, 0x64, 0x45, 0x55, + 0x36, 0x74, 0x65, 0xE2, 0xF2, 0xB3, 0x84, 0xC3, 0xD3, 0x75, 0xE3, 0xF3, + 0x46, 0x94, 0xA4, 0x85, 0xB4, 0x95, 0xC4, 0xD4, 0xE4, 0xF4, 0xA5, 0xB5, + 0xC5, 0xD5, 0xE5, 0xF5, 0x56, 0x66, 0x76, 0x86, 0x96, 0xA6, 0xB6, 0xC6, + 0xD6, 0xE6, 0xF6, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xA7, + 0xB7, 0xC7, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, + 0x11, 0x00, 0x3F, 0x00, 0xF4, 0x22, 0x09, 0x44, 0xAC, 0x18, 0xD1, 0x40, + 0x14, 0x7A, 0x84, 0x85, 0x29, 0x61, 0x0C, 0x60, 0xF8, 0x28, 0xB8, 0x4A, + 0xB0, 0x5A, 0x10, 0x8B, 0x7D, 0xDE, 0x09, 0x02, 0x92, 0x1A, 0xEE, 0x08, + 0x64, 0x42, 0xB2, 0xF0, 0x10, 0x6C, 0x6C, 0x09, 0x1A, 0xA2, 0x0A, 0xD2, + 0x1A, 0xEE, 0x74, 0x21, 0xEE, 0x51, 0xB9, 0xE4, 0x18, 0x00, 0x94, 0x22, + 0xF7, 0x07, 0x42, 0x78, 0x0B, 0x09, 0x4F, 0x29, 0xA5, 0x44, 0x6E, 0x70, + 0xD0, 0x29, 0x32, 0xB7, 0x1E, 0x78, 0x49, 0x4A, 0xE5, 0x49, 0xA0, 0xA9, + 0x06, 0x34, 0x09, 0xF0, 0x4C, 0xDE, 0x50, 0x4D, 0x32, 0x68, 0x46, 0x63, + 0x47, 0x7D, 0x14, 0x1A, 0xE8, 0x10, 0x8A, 0xD3, 0x25, 0x02, 0x90, 0xCB, + 0x60, 0x09, 0xE5, 0x46, 0x4F, 0x8A, 0x52, 0x7B, 0xA0, 0x9B, 0x7F, 0xFF, + 0xD0, 0xF4, 0x26, 0xB4, 0xF7, 0x56, 0x2B, 0x8F, 0x04, 0x1D, 0xE0, 0xF0, + 0x21, 0x15, 0xA6, 0x02, 0x98, 0xB0, 0x84, 0xC3, 0x85, 0x02, 0x07, 0x25, + 0x20, 0xFD, 0x14, 0x6C, 0x70, 0x23, 0x44, 0xDA, 0x4A, 0x2B, 0x1D, 0xAA, + 0x03, 0xAC, 0x04, 0xC1, 0x45, 0xB1, 0x84, 0x83, 0xE0, 0xAA, 0xB9, 0xAE, + 0x13, 0xF9, 0x53, 0x82, 0xD2, 0x56, 0x24, 0xB9, 0xDA, 0x08, 0x08, 0xAD, + 0xC6, 0x6C, 0x4B, 0xBE, 0xF5, 0x0A, 0xD8, 0x49, 0x93, 0xC0, 0xE0, 0x23, + 0x4F, 0xB7, 0x50, 0x89, 0x40, 0x44, 0xF0, 0xD6, 0x98, 0x89, 0xF3, 0x50, + 0xB2, 0xC0, 0xC0, 0x48, 0x10, 0xA5, 0x6B, 0xD8, 0xC3, 0x3A, 0x94, 0x17, + 0x58, 0xCB, 0x3E, 0x7D, 0x91, 0x08, 0x25, 0x1F, 0xAE, 0x1C, 0x76, 0xEB, + 0x25, 0x11, 0xB3, 0xA4, 0x4A, 0x42, 0x80, 0x06, 0xE5, 0x30, 0xD8, 0x8D, + 0x11, 0x40, 0xBE, 0xA9, 0x2B, 0x73, 0x46, 0x84, 0x4C, 0xA2, 0xFB, 0x47, + 0x65, 0x0A, 0xF9, 0x82, 0x34, 0x45, 0x21, 0xA1, 0x34, 0xAE, 0x0C, 0x80, + 0xDD, 0xC0, 0x48, 0x80, 0x07, 0x1A, 0xA8, 0xB2, 0xC6, 0x8E, 0xFA, 0x29, + 0x7A, 0x80, 0xA0, 0x97, 0xFF, 0xD1, 0xF4, 0x1A, 0xCC, 0x8D, 0x51, 0x37, + 0x82, 0x15, 0x2A, 0xAE, 0x9E, 0x55, 0x86, 0x49, 0x20, 0x8E, 0x14, 0xE4, + 0x35, 0xC1, 0x6C, 0x86, 0xCB, 0x7C, 0xD0, 0x5C, 0xE8, 0x3A, 0xA2, 0x87, + 0x10, 0x39, 0x55, 0xAD, 0x81, 0x24, 0x94, 0x02, 0x4B, 0x37, 0x5B, 0xB4, + 0x6B, 0xAC, 0xA1, 0xB5, 0xBB, 0xFE, 0x03, 0x55, 0x16, 0x89, 0x1A, 0xF7, + 0xE1, 0x11, 0xB0, 0xC6, 0xA2, 0x8B, 0x58, 0x80, 0x38, 0xF6, 0xA8, 0x97, + 0x1D, 0xB3, 0xBA, 0x0F, 0x82, 0x4E, 0xD7, 0xC8, 0x1E, 0xC9, 0xBD, 0x22, + 0x62, 0x52, 0x55, 0xA2, 0xB2, 0xB9, 0x1A, 0x19, 0x43, 0x2C, 0xF2, 0xD5, + 0x5D, 0x35, 0xD7, 0xB7, 0x68, 0x10, 0x50, 0x36, 0x41, 0x23, 0xF1, 0x4E, + 0x05, 0x04, 0x2E, 0xC7, 0x43, 0x7C, 0xD2, 0x0E, 0x6C, 0x68, 0xA0, 0x46, + 0xC9, 0xF1, 0x48, 0x81, 0x00, 0xFD, 0xE9, 0x52, 0x2D, 0x6F, 0x59, 0xD3, + 0x00, 0xC2, 0x9F, 0xAA, 0xF8, 0xFA, 0x48, 0x24, 0x7B, 0x92, 0x6D, 0x80, + 0x18, 0x76, 0x90, 0x95, 0x22, 0xD9, 0xFA, 0x86, 0x79, 0xD4, 0x27, 0x6B, + 0xDC, 0x4F, 0x2A, 0xBB, 0xAD, 0x6C, 0x94, 0x99, 0x76, 0xB2, 0x8D, 0x2A, + 0xDF, 0xFF, 0xD2, 0xED, 0x68, 0x68, 0x8F, 0x35, 0x66, 0xB7, 0x96, 0x13, + 0xAE, 0x8A, 0xB3, 0x76, 0x31, 0x93, 0x30, 0x67, 0x84, 0xDE, 0xA8, 0x2E, + 0xD4, 0xAB, 0x24, 0x5B, 0x50, 0x1A, 0x6F, 0x7A, 0xA1, 0xDD, 0xE5, 0x02, + 0xE7, 0x01, 0xA3, 0x8F, 0xC9, 0x53, 0xB3, 0x20, 0xB5, 0xE4, 0xB7, 0x8F, + 0x04, 0x3F, 0x5D, 0xD6, 0x76, 0x44, 0x41, 0x46, 0x6D, 0xC6, 0xD9, 0xAE, + 0x9C, 0x22, 0x87, 0xB7, 0xBF, 0x2A, 0xA3, 0x09, 0xDB, 0x10, 0x3F, 0x8A, + 0x98, 0x7B, 0x82, 0x44, 0x28, 0x49, 0xB6, 0x4B, 0x42, 0x94, 0x83, 0x0A, + 0xA8, 0xB0, 0x11, 0x12, 0x65, 0x19, 0x91, 0x09, 0xB4, 0xB8, 0x16, 0x76, + 0x4F, 0xE6, 0xEA, 0x7B, 0x21, 0x7B, 0x86, 0xAE, 0xD3, 0xE2, 0xA7, 0xEA, + 0x11, 0x12, 0x9A, 0xDB, 0x18, 0x5B, 0xA9, 0x94, 0x94, 0x85, 0xF1, 0x3E, + 0xEE, 0xE8, 0x86, 0xB9, 0x60, 0x11, 0x03, 0xC5, 0x0C, 0x34, 0x1D, 0x47, + 0x6E, 0xC5, 0x11, 0xB6, 0xB8, 0x0D, 0x46, 0x83, 0x84, 0x4A, 0x02, 0x27, + 0x8F, 0x4C, 0x6B, 0xCA, 0xA9, 0x6D, 0x85, 0xDF, 0x04, 0x7B, 0x8E, 0xF3, + 0xA6, 0x84, 0x94, 0xDE, 0x80, 0x03, 0xDC, 0x7E, 0x49, 0xC1, 0x69, 0xD5, + 0xA2, 0x5E, 0x67, 0x5F, 0xBD, 0x16, 0xA0, 0x4E, 0xBF, 0x72, 0xB4, 0xCC, + 0x6A, 0xCB, 0x74, 0x4E, 0x68, 0xD9, 0x03, 0x90, 0x8F, 0x10, 0x5B, 0xC2, + 0x5F, 0xFF, 0xD3, 0xEA, 0xCD, 0xA7, 0xE8, 0x84, 0xDB, 0xFC, 0x79, 0x4F, + 0xE9, 0x38, 0xA7, 0xF4, 0x65, 0x5C, 0xD1, 0xA1, 0xAA, 0x33, 0xA9, 0x48, + 0x13, 0xD9, 0x17, 0xD1, 0x69, 0xD4, 0x9D, 0x14, 0x0E, 0x86, 0x3C, 0x38, + 0x45, 0x54, 0xB7, 0xA8, 0x41, 0xD3, 0x94, 0x41, 0x69, 0xEE, 0x50, 0x5C, + 0xD2, 0x99, 0xAC, 0x74, 0xF9, 0x25, 0xA2, 0xAC, 0xB6, 0x1B, 0x71, 0x07, + 0x8F, 0x9A, 0x28, 0xB1, 0xC6, 0x35, 0x55, 0x64, 0x8E, 0x79, 0x44, 0x63, + 0x87, 0x32, 0x81, 0x09, 0x05, 0x3B, 0x9C, 0xE3, 0xAC, 0xA6, 0x13, 0x3A, + 0x73, 0xE2, 0xA2, 0x1C, 0x0F, 0x74, 0x4A, 0xE0, 0x90, 0x0F, 0x74, 0x17, + 0x32, 0x60, 0x23, 0xCD, 0x4C, 0x26, 0x88, 0x30, 0x91, 0x29, 0xA9, 0x46, + 0xE6, 0x80, 0xE9, 0x8E, 0x39, 0x51, 0x71, 0xDD, 0xA7, 0x08, 0xA5, 0x30, + 0x69, 0x3C, 0x04, 0x6D, 0x0C, 0x01, 0xD9, 0xC7, 0x3E, 0x2A, 0x0F, 0x79, + 0x8E, 0x54, 0x9E, 0xD2, 0x0A, 0x0B, 0xDA, 0x4A, 0x21, 0x05, 0xFF, 0xD4, + 0xEC, 0xC3, 0x44, 0x6A, 0x96, 0xE0, 0x3C, 0xD5, 0x6F, 0xB4, 0x87, 0x1D, + 0x24, 0xA2, 0x56, 0x4B, 0xCC, 0x42, 0xB7, 0x4D, 0x3B, 0x1D, 0x12, 0x68, + 0x75, 0xF0, 0x50, 0x73, 0x9B, 0xD8, 0x29, 0xBD, 0x9B, 0x47, 0x28, 0x71, + 0xDD, 0x20, 0xA2, 0xC6, 0x0F, 0x60, 0x9F, 0x67, 0x92, 0x93, 0x66, 0x51, + 0x01, 0x8E, 0x44, 0xA5, 0x6A, 0xA4, 0x05, 0x93, 0xCA, 0x67, 0x6D, 0x6A, + 0x23, 0xCE, 0xBA, 0x28, 0x47, 0xF2, 0x44, 0xA2, 0x82, 0x15, 0x59, 0x9F, + 0x29, 0xF1, 0x56, 0x2B, 0x60, 0xE4, 0x99, 0xF2, 0x55, 0xB7, 0x86, 0xC4, + 0x85, 0x2F, 0xB4, 0xBF, 0x81, 0xF8, 0xA0, 0x6D, 0x40, 0x80, 0xDB, 0x0E, + 0x05, 0x22, 0x55, 0x56, 0xD9, 0xAC, 0xCA, 0x28, 0xB0, 0x21, 0x4B, 0xAD, + 0x28, 0x1B, 0xB8, 0x45, 0xAD, 0xA5, 0xBC, 0xF7, 0x55, 0x3E, 0xD0, 0x1A, + 0xA4, 0xCC, 0xC6, 0x9D, 0x0F, 0x28, 0x10, 0x54, 0x08, 0x4D, 0x68, 0x07, + 0x8D, 0x4A, 0x09, 0x69, 0xF0, 0x4E, 0xEB, 0xC7, 0x68, 0x3E, 0x68, 0x67, + 0x29, 0xAD, 0x69, 0x9F, 0x92, 0x20, 0x15, 0x12, 0x1F, 0xFF, 0xD5, 0xEB, + 0x46, 0x33, 0x87, 0x01, 0x3F, 0xA7, 0x6B, 0x75, 0x06, 0x07, 0xE2, 0xAD, + 0x8D, 0xC3, 0x8E, 0xE9, 0xB6, 0x13, 0xCA, 0xB5, 0xC4, 0xD4, 0xE1, 0x0D, + 0x33, 0xEA, 0x18, 0x92, 0xA5, 0xC0, 0x8E, 0xEA, 0xDF, 0xA2, 0xD3, 0xD9, + 0x2F, 0x48, 0x01, 0xC2, 0x5C, 0x4A, 0xE1, 0x2D, 0x51, 0x1F, 0x34, 0xE6, + 0xC0, 0x02, 0x31, 0xAC, 0x14, 0x3B, 0x18, 0x47, 0x01, 0x2B, 0x0A, 0xA2, + 0x87, 0x79, 0x9D, 0x54, 0x1D, 0x6F, 0x75, 0x37, 0x53, 0x61, 0xD6, 0x14, + 0x0D, 0x64, 0x09, 0x21, 0x3B, 0x45, 0xA6, 0xD1, 0xBD, 0xFB, 0xB5, 0x01, + 0x38, 0x32, 0x3F, 0x82, 0x44, 0x38, 0xE8, 0x02, 0x23, 0x2B, 0x9F, 0x2F, + 0x8A, 0x56, 0x8A, 0x60, 0x1D, 0x1A, 0xA4, 0x6D, 0x8F, 0x8A, 0x28, 0xA9, + 0xDD, 0xB5, 0x1F, 0x82, 0x4E, 0xA8, 0x1D, 0x74, 0x94, 0xAC, 0x2A, 0x8A, + 0x22, 0xF9, 0x52, 0x68, 0xF2, 0x92, 0xA4, 0x6B, 0x20, 0x79, 0xF9, 0x26, + 0x1B, 0x87, 0x23, 0x44, 0xAD, 0x34, 0xC1, 0xAC, 0x71, 0x76, 0xB2, 0x8A, + 0x71, 0x89, 0xD7, 0x52, 0x54, 0x98, 0x78, 0x85, 0x65, 0x80, 0xC2, 0x06, + 0x49, 0x11, 0x0F, 0xFF, 0xD6, 0xEF, 0x9A, 0xDF, 0x15, 0x20, 0xDF, 0x24, + 0xFC, 0x15, 0x36, 0x89, 0x53, 0x5B, 0x05, 0x30, 0xDA, 0xA2, 0x5A, 0x8E, + 0x5A, 0xA0, 0x5A, 0x95, 0xA6, 0x90, 0x96, 0xC2, 0x81, 0x00, 0xA2, 0xB8, + 0x28, 0x22, 0xB5, 0x19, 0x60, 0x88, 0x84, 0x27, 0xD6, 0x22, 0x00, 0xE1, + 0x58, 0x25, 0x0C, 0xA2, 0x10, 0x40, 0x43, 0xB1, 0xA7, 0xE9, 0x69, 0x0A, + 0x62, 0xB1, 0x13, 0x32, 0x12, 0x21, 0x38, 0x24, 0x70, 0x8D, 0xA2, 0x91, + 0x19, 0x07, 0x44, 0xA1, 0xDF, 0xEC, 0x44, 0x30, 0x90, 0x84, 0xAD, 0x54, + 0xC1, 0xAD, 0x77, 0x8A, 0x23, 0x6B, 0x61, 0x3E, 0xED, 0x54, 0x80, 0x08, + 0x83, 0x6F, 0x64, 0x09, 0x48, 0x0C, 0x05, 0x4D, 0xEC, 0x11, 0x03, 0x23, + 0x84, 0xED, 0x08, 0x80, 0x21, 0x69, 0x01, 0xFF, 0xD9, 0xFF, 0xED, 0x0C, + 0x98, 0x50, 0x68, 0x6F, 0x74, 0x6F, 0x73, 0x68, 0x6F, 0x70, 0x20, 0x33, + 0x2E, 0x30, 0x00, 0x38, 0x42, 0x49, 0x4D, 0x04, 0x25, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x42, 0x49, 0x4D, 0x03, + 0xED, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x38, + 0x42, 0x49, 0x4D, 0x04, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, + 0x00, 0x38, 0x42, 0x49, 0x4D, 0x04, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x1E, 0x38, 0x42, 0x49, 0x4D, 0x04, 0x19, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1E, 0x38, 0x42, 0x49, + 0x4D, 0x03, 0xF3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x38, 0x42, 0x49, 0x4D, 0x04, + 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x38, 0x42, 0x49, + 0x4D, 0x27, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x38, 0x42, 0x49, 0x4D, 0x03, + 0xF5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x2F, 0x66, 0x66, 0x00, + 0x01, 0x00, 0x6C, 0x66, 0x66, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x2F, 0x66, 0x66, 0x00, 0x01, 0x00, 0xA1, 0x99, 0x9A, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x32, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x35, 0x00, 0x00, 0x00, 0x01, 0x00, 0x2D, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x38, 0x42, 0x49, 0x4D, 0x03, + 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xE8, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, + 0xE8, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x03, 0xE8, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xE8, 0x00, 0x00, 0x38, + 0x42, 0x49, 0x4D, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x02, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x38, 0x42, 0x49, 0x4D, 0x04, 0x1E, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x38, 0x42, 0x49, 0x4D, 0x04, + 0x1A, 0x00, 0x00, 0x00, 0x00, 0x03, 0x41, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF4, 0x00, + 0x00, 0x01, 0xF4, 0x00, 0x00, 0x00, 0x06, 0x00, 0x63, 0x00, 0x6C, 0x00, + 0x6F, 0x00, 0x75, 0x00, 0x64, 0x00, 0x73, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF4, 0x00, 0x00, 0x01, 0xF4, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, 0x75, + 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x62, 0x6F, + 0x75, 0x6E, 0x64, 0x73, 0x4F, 0x62, 0x6A, 0x63, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x63, 0x74, 0x31, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x54, 0x6F, 0x70, 0x20, 0x6C, 0x6F, + 0x6E, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4C, 0x65, + 0x66, 0x74, 0x6C, 0x6F, 0x6E, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x42, 0x74, 0x6F, 0x6D, 0x6C, 0x6F, 0x6E, 0x67, 0x00, 0x00, + 0x01, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x52, 0x67, 0x68, 0x74, 0x6C, 0x6F, + 0x6E, 0x67, 0x00, 0x00, 0x01, 0xF4, 0x00, 0x00, 0x00, 0x06, 0x73, 0x6C, + 0x69, 0x63, 0x65, 0x73, 0x56, 0x6C, 0x4C, 0x73, 0x00, 0x00, 0x00, 0x01, + 0x4F, 0x62, 0x6A, 0x63, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x05, 0x73, 0x6C, 0x69, 0x63, 0x65, 0x00, 0x00, 0x00, 0x12, 0x00, + 0x00, 0x00, 0x07, 0x73, 0x6C, 0x69, 0x63, 0x65, 0x49, 0x44, 0x6C, 0x6F, + 0x6E, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x67, 0x72, + 0x6F, 0x75, 0x70, 0x49, 0x44, 0x6C, 0x6F, 0x6E, 0x67, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x6F, 0x72, 0x69, 0x67, 0x69, 0x6E, 0x65, + 0x6E, 0x75, 0x6D, 0x00, 0x00, 0x00, 0x0C, 0x45, 0x53, 0x6C, 0x69, 0x63, + 0x65, 0x4F, 0x72, 0x69, 0x67, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x0D, 0x61, + 0x75, 0x74, 0x6F, 0x47, 0x65, 0x6E, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, + 0x00, 0x00, 0x00, 0x00, 0x54, 0x79, 0x70, 0x65, 0x65, 0x6E, 0x75, 0x6D, + 0x00, 0x00, 0x00, 0x0A, 0x45, 0x53, 0x6C, 0x69, 0x63, 0x65, 0x54, 0x79, + 0x70, 0x65, 0x00, 0x00, 0x00, 0x00, 0x49, 0x6D, 0x67, 0x20, 0x00, 0x00, + 0x00, 0x06, 0x62, 0x6F, 0x75, 0x6E, 0x64, 0x73, 0x4F, 0x62, 0x6A, 0x63, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x63, + 0x74, 0x31, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x54, 0x6F, + 0x70, 0x20, 0x6C, 0x6F, 0x6E, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x4C, 0x65, 0x66, 0x74, 0x6C, 0x6F, 0x6E, 0x67, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x74, 0x6F, 0x6D, 0x6C, 0x6F, + 0x6E, 0x67, 0x00, 0x00, 0x01, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x52, 0x67, + 0x68, 0x74, 0x6C, 0x6F, 0x6E, 0x67, 0x00, 0x00, 0x01, 0xF4, 0x00, 0x00, + 0x00, 0x03, 0x75, 0x72, 0x6C, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, 0x75, 0x6C, 0x6C, 0x54, + 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4D, 0x73, 0x67, 0x65, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x61, 0x6C, 0x74, 0x54, 0x61, + 0x67, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x63, 0x65, 0x6C, 0x6C, 0x54, 0x65, 0x78, 0x74, 0x49, + 0x73, 0x48, 0x54, 0x4D, 0x4C, 0x62, 0x6F, 0x6F, 0x6C, 0x01, 0x00, 0x00, + 0x00, 0x08, 0x63, 0x65, 0x6C, 0x6C, 0x54, 0x65, 0x78, 0x74, 0x54, 0x45, + 0x58, 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, + 0x68, 0x6F, 0x72, 0x7A, 0x41, 0x6C, 0x69, 0x67, 0x6E, 0x65, 0x6E, 0x75, + 0x6D, 0x00, 0x00, 0x00, 0x0F, 0x45, 0x53, 0x6C, 0x69, 0x63, 0x65, 0x48, + 0x6F, 0x72, 0x7A, 0x41, 0x6C, 0x69, 0x67, 0x6E, 0x00, 0x00, 0x00, 0x07, + 0x64, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x00, 0x00, 0x00, 0x09, 0x76, + 0x65, 0x72, 0x74, 0x41, 0x6C, 0x69, 0x67, 0x6E, 0x65, 0x6E, 0x75, 0x6D, + 0x00, 0x00, 0x00, 0x0F, 0x45, 0x53, 0x6C, 0x69, 0x63, 0x65, 0x56, 0x65, + 0x72, 0x74, 0x41, 0x6C, 0x69, 0x67, 0x6E, 0x00, 0x00, 0x00, 0x07, 0x64, + 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x00, 0x00, 0x00, 0x0B, 0x62, 0x67, + 0x43, 0x6F, 0x6C, 0x6F, 0x72, 0x54, 0x79, 0x70, 0x65, 0x65, 0x6E, 0x75, + 0x6D, 0x00, 0x00, 0x00, 0x11, 0x45, 0x53, 0x6C, 0x69, 0x63, 0x65, 0x42, + 0x47, 0x43, 0x6F, 0x6C, 0x6F, 0x72, 0x54, 0x79, 0x70, 0x65, 0x00, 0x00, + 0x00, 0x00, 0x4E, 0x6F, 0x6E, 0x65, 0x00, 0x00, 0x00, 0x09, 0x74, 0x6F, + 0x70, 0x4F, 0x75, 0x74, 0x73, 0x65, 0x74, 0x6C, 0x6F, 0x6E, 0x67, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x6C, 0x65, 0x66, 0x74, 0x4F, + 0x75, 0x74, 0x73, 0x65, 0x74, 0x6C, 0x6F, 0x6E, 0x67, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0C, 0x62, 0x6F, 0x74, 0x74, 0x6F, 0x6D, 0x4F, + 0x75, 0x74, 0x73, 0x65, 0x74, 0x6C, 0x6F, 0x6E, 0x67, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0B, 0x72, 0x69, 0x67, 0x68, 0x74, 0x4F, 0x75, + 0x74, 0x73, 0x65, 0x74, 0x6C, 0x6F, 0x6E, 0x67, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x42, 0x49, 0x4D, 0x04, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x38, 0x42, 0x49, 0x4D, 0x04, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x38, 0x42, 0x49, 0x4D, 0x04, + 0x0C, 0x00, 0x00, 0x00, 0x00, 0x06, 0xF1, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x00, 0x06, 0xD5, 0x00, 0x18, 0x00, 0x01, 0xFF, + 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x02, + 0x01, 0x00, 0x48, 0x00, 0x48, 0x00, 0x00, 0xFF, 0xED, 0x00, 0x0C, 0x41, + 0x64, 0x6F, 0x62, 0x65, 0x5F, 0x43, 0x4D, 0x00, 0x02, 0xFF, 0xEE, 0x00, + 0x0E, 0x41, 0x64, 0x6F, 0x62, 0x65, 0x00, 0x64, 0x80, 0x00, 0x00, 0x00, + 0x01, 0xFF, 0xDB, 0x00, 0x84, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x09, 0x08, + 0x0C, 0x09, 0x09, 0x0C, 0x11, 0x0B, 0x0A, 0x0B, 0x11, 0x15, 0x0F, 0x0C, + 0x0C, 0x0F, 0x15, 0x18, 0x13, 0x13, 0x15, 0x13, 0x13, 0x18, 0x11, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x11, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x01, 0x0D, + 0x0B, 0x0B, 0x0D, 0x0E, 0x0D, 0x10, 0x0E, 0x0E, 0x10, 0x14, 0x0E, 0x0E, + 0x0E, 0x14, 0x14, 0x0E, 0x0E, 0x0E, 0x0E, 0x14, 0x11, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x11, 0x11, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x11, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0x80, 0x00, 0x80, + 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF, 0xDD, + 0x00, 0x04, 0x00, 0x08, 0xFF, 0xC4, 0x01, 0x3F, 0x00, 0x00, 0x01, 0x05, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x01, 0x02, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, + 0x0B, 0x01, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x10, 0x00, 0x01, 0x04, 0x01, 0x03, + 0x02, 0x04, 0x02, 0x05, 0x07, 0x06, 0x08, 0x05, 0x03, 0x0C, 0x33, 0x01, + 0x00, 0x02, 0x11, 0x03, 0x04, 0x21, 0x12, 0x31, 0x05, 0x41, 0x51, 0x61, + 0x13, 0x22, 0x71, 0x81, 0x32, 0x06, 0x14, 0x91, 0xA1, 0xB1, 0x42, 0x23, + 0x24, 0x15, 0x52, 0xC1, 0x62, 0x33, 0x34, 0x72, 0x82, 0xD1, 0x43, 0x07, + 0x25, 0x92, 0x53, 0xF0, 0xE1, 0xF1, 0x63, 0x73, 0x35, 0x16, 0xA2, 0xB2, + 0x83, 0x26, 0x44, 0x93, 0x54, 0x64, 0x45, 0xC2, 0xA3, 0x74, 0x36, 0x17, + 0xD2, 0x55, 0xE2, 0x65, 0xF2, 0xB3, 0x84, 0xC3, 0xD3, 0x75, 0xE3, 0xF3, + 0x46, 0x27, 0x94, 0xA4, 0x85, 0xB4, 0x95, 0xC4, 0xD4, 0xE4, 0xF4, 0xA5, + 0xB5, 0xC5, 0xD5, 0xE5, 0xF5, 0x56, 0x66, 0x76, 0x86, 0x96, 0xA6, 0xB6, + 0xC6, 0xD6, 0xE6, 0xF6, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xA7, + 0xB7, 0xC7, 0xD7, 0xE7, 0xF7, 0x11, 0x00, 0x02, 0x02, 0x01, 0x02, 0x04, + 0x04, 0x03, 0x04, 0x05, 0x06, 0x07, 0x07, 0x06, 0x05, 0x35, 0x01, 0x00, + 0x02, 0x11, 0x03, 0x21, 0x31, 0x12, 0x04, 0x41, 0x51, 0x61, 0x71, 0x22, + 0x13, 0x05, 0x32, 0x81, 0x91, 0x14, 0xA1, 0xB1, 0x42, 0x23, 0xC1, 0x52, + 0xD1, 0xF0, 0x33, 0x24, 0x62, 0xE1, 0x72, 0x82, 0x92, 0x43, 0x53, 0x15, + 0x63, 0x73, 0x34, 0xF1, 0x25, 0x06, 0x16, 0xA2, 0xB2, 0x83, 0x07, 0x26, + 0x35, 0xC2, 0xD2, 0x44, 0x93, 0x54, 0xA3, 0x17, 0x64, 0x45, 0x55, 0x36, + 0x74, 0x65, 0xE2, 0xF2, 0xB3, 0x84, 0xC3, 0xD3, 0x75, 0xE3, 0xF3, 0x46, + 0x94, 0xA4, 0x85, 0xB4, 0x95, 0xC4, 0xD4, 0xE4, 0xF4, 0xA5, 0xB5, 0xC5, + 0xD5, 0xE5, 0xF5, 0x56, 0x66, 0x76, 0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, + 0xE6, 0xF6, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xA7, 0xB7, + 0xC7, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, + 0x00, 0x3F, 0x00, 0xF4, 0x22, 0x09, 0x44, 0xAC, 0x18, 0xD1, 0x40, 0x14, + 0x7A, 0x84, 0x85, 0x29, 0x61, 0x0C, 0x60, 0xF8, 0x28, 0xB8, 0x4A, 0xB0, + 0x5A, 0x10, 0x8B, 0x7D, 0xDE, 0x09, 0x02, 0x92, 0x1A, 0xEE, 0x08, 0x64, + 0x42, 0xB2, 0xF0, 0x10, 0x6C, 0x6C, 0x09, 0x1A, 0xA2, 0x0A, 0xD2, 0x1A, + 0xEE, 0x74, 0x21, 0xEE, 0x51, 0xB9, 0xE4, 0x18, 0x00, 0x94, 0x22, 0xF7, + 0x07, 0x42, 0x78, 0x0B, 0x09, 0x4F, 0x29, 0xA5, 0x44, 0x6E, 0x70, 0xD0, + 0x29, 0x32, 0xB7, 0x1E, 0x78, 0x49, 0x4A, 0xE5, 0x49, 0xA0, 0xA9, 0x06, + 0x34, 0x09, 0xF0, 0x4C, 0xDE, 0x50, 0x4D, 0x32, 0x68, 0x46, 0x63, 0x47, + 0x7D, 0x14, 0x1A, 0xE8, 0x10, 0x8A, 0xD3, 0x25, 0x02, 0x90, 0xCB, 0x60, + 0x09, 0xE5, 0x46, 0x4F, 0x8A, 0x52, 0x7B, 0xA0, 0x9B, 0x7F, 0xFF, 0xD0, + 0xF4, 0x26, 0xB4, 0xF7, 0x56, 0x2B, 0x8F, 0x04, 0x1D, 0xE0, 0xF0, 0x21, + 0x15, 0xA6, 0x02, 0x98, 0xB0, 0x84, 0xC3, 0x85, 0x02, 0x07, 0x25, 0x20, + 0xFD, 0x14, 0x6C, 0x70, 0x23, 0x44, 0xDA, 0x4A, 0x2B, 0x1D, 0xAA, 0x03, + 0xAC, 0x04, 0xC1, 0x45, 0xB1, 0x84, 0x83, 0xE0, 0xAA, 0xB9, 0xAE, 0x13, + 0xF9, 0x53, 0x82, 0xD2, 0x56, 0x24, 0xB9, 0xDA, 0x08, 0x08, 0xAD, 0xC6, + 0x6C, 0x4B, 0xBE, 0xF5, 0x0A, 0xD8, 0x49, 0x93, 0xC0, 0xE0, 0x23, 0x4F, + 0xB7, 0x50, 0x89, 0x40, 0x44, 0xF0, 0xD6, 0x98, 0x89, 0xF3, 0x50, 0xB2, + 0xC0, 0xC0, 0x48, 0x10, 0xA5, 0x6B, 0xD8, 0xC3, 0x3A, 0x94, 0x17, 0x58, + 0xCB, 0x3E, 0x7D, 0x91, 0x08, 0x25, 0x1F, 0xAE, 0x1C, 0x76, 0xEB, 0x25, + 0x11, 0xB3, 0xA4, 0x4A, 0x42, 0x80, 0x06, 0xE5, 0x30, 0xD8, 0x8D, 0x11, + 0x40, 0xBE, 0xA9, 0x2B, 0x73, 0x46, 0x84, 0x4C, 0xA2, 0xFB, 0x47, 0x65, + 0x0A, 0xF9, 0x82, 0x34, 0x45, 0x21, 0xA1, 0x34, 0xAE, 0x0C, 0x80, 0xDD, + 0xC0, 0x48, 0x80, 0x07, 0x1A, 0xA8, 0xB2, 0xC6, 0x8E, 0xFA, 0x29, 0x7A, + 0x80, 0xA0, 0x97, 0xFF, 0xD1, 0xF4, 0x1A, 0xCC, 0x8D, 0x51, 0x37, 0x82, + 0x15, 0x2A, 0xAE, 0x9E, 0x55, 0x86, 0x49, 0x20, 0x8E, 0x14, 0xE4, 0x35, + 0xC1, 0x6C, 0x86, 0xCB, 0x7C, 0xD0, 0x5C, 0xE8, 0x3A, 0xA2, 0x87, 0x10, + 0x39, 0x55, 0xAD, 0x81, 0x24, 0x94, 0x02, 0x4B, 0x37, 0x5B, 0xB4, 0x6B, + 0xAC, 0xA1, 0xB5, 0xBB, 0xFE, 0x03, 0x55, 0x16, 0x89, 0x1A, 0xF7, 0xE1, + 0x11, 0xB0, 0xC6, 0xA2, 0x8B, 0x58, 0x80, 0x38, 0xF6, 0xA8, 0x97, 0x1D, + 0xB3, 0xBA, 0x0F, 0x82, 0x4E, 0xD7, 0xC8, 0x1E, 0xC9, 0xBD, 0x22, 0x62, + 0x52, 0x55, 0xA2, 0xB2, 0xB9, 0x1A, 0x19, 0x43, 0x2C, 0xF2, 0xD5, 0x5D, + 0x35, 0xD7, 0xB7, 0x68, 0x10, 0x50, 0x36, 0x41, 0x23, 0xF1, 0x4E, 0x05, + 0x04, 0x2E, 0xC7, 0x43, 0x7C, 0xD2, 0x0E, 0x6C, 0x68, 0xA0, 0x46, 0xC9, + 0xF1, 0x48, 0x81, 0x00, 0xFD, 0xE9, 0x52, 0x2D, 0x6F, 0x59, 0xD3, 0x00, + 0xC2, 0x9F, 0xAA, 0xF8, 0xFA, 0x48, 0x24, 0x7B, 0x92, 0x6D, 0x80, 0x18, + 0x76, 0x90, 0x95, 0x22, 0xD9, 0xFA, 0x86, 0x79, 0xD4, 0x27, 0x6B, 0xDC, + 0x4F, 0x2A, 0xBB, 0xAD, 0x6C, 0x94, 0x99, 0x76, 0xB2, 0x8D, 0x2A, 0xDF, + 0xFF, 0xD2, 0xED, 0x68, 0x68, 0x8F, 0x35, 0x66, 0xB7, 0x96, 0x13, 0xAE, + 0x8A, 0xB3, 0x76, 0x31, 0x93, 0x30, 0x67, 0x84, 0xDE, 0xA8, 0x2E, 0xD4, + 0xAB, 0x24, 0x5B, 0x50, 0x1A, 0x6F, 0x7A, 0xA1, 0xDD, 0xE5, 0x02, 0xE7, + 0x01, 0xA3, 0x8F, 0xC9, 0x53, 0xB3, 0x20, 0xB5, 0xE4, 0xB7, 0x8F, 0x04, + 0x3F, 0x5D, 0xD6, 0x76, 0x44, 0x41, 0x46, 0x6D, 0xC6, 0xD9, 0xAE, 0x9C, + 0x22, 0x87, 0xB7, 0xBF, 0x2A, 0xA3, 0x09, 0xDB, 0x10, 0x3F, 0x8A, 0x98, + 0x7B, 0x82, 0x44, 0x28, 0x49, 0xB6, 0x4B, 0x42, 0x94, 0x83, 0x0A, 0xA8, + 0xB0, 0x11, 0x12, 0x65, 0x19, 0x91, 0x09, 0xB4, 0xB8, 0x16, 0x76, 0x4F, + 0xE6, 0xEA, 0x7B, 0x21, 0x7B, 0x86, 0xAE, 0xD3, 0xE2, 0xA7, 0xEA, 0x11, + 0x12, 0x9A, 0xDB, 0x18, 0x5B, 0xA9, 0x94, 0x94, 0x85, 0xF1, 0x3E, 0xEE, + 0xE8, 0x86, 0xB9, 0x60, 0x11, 0x03, 0xC5, 0x0C, 0x34, 0x1D, 0x47, 0x6E, + 0xC5, 0x11, 0xB6, 0xB8, 0x0D, 0x46, 0x83, 0x84, 0x4A, 0x02, 0x27, 0x8F, + 0x4C, 0x6B, 0xCA, 0xA9, 0x6D, 0x85, 0xDF, 0x04, 0x7B, 0x8E, 0xF3, 0xA6, + 0x84, 0x94, 0xDE, 0x80, 0x03, 0xDC, 0x7E, 0x49, 0xC1, 0x69, 0xD5, 0xA2, + 0x5E, 0x67, 0x5F, 0xBD, 0x16, 0xA0, 0x4E, 0xBF, 0x72, 0xB4, 0xCC, 0x6A, + 0xCB, 0x74, 0x4E, 0x68, 0xD9, 0x03, 0x90, 0x8F, 0x10, 0x5B, 0xC2, 0x5F, + 0xFF, 0xD3, 0xEA, 0xCD, 0xA7, 0xE8, 0x84, 0xDB, 0xFC, 0x79, 0x4F, 0xE9, + 0x38, 0xA7, 0xF4, 0x65, 0x5C, 0xD1, 0xA1, 0xAA, 0x33, 0xA9, 0x48, 0x13, + 0xD9, 0x17, 0xD1, 0x69, 0xD4, 0x9D, 0x14, 0x0E, 0x86, 0x3C, 0x38, 0x45, + 0x54, 0xB7, 0xA8, 0x41, 0xD3, 0x94, 0x41, 0x69, 0xEE, 0x50, 0x5C, 0xD2, + 0x99, 0xAC, 0x74, 0xF9, 0x25, 0xA2, 0xAC, 0xB6, 0x1B, 0x71, 0x07, 0x8F, + 0x9A, 0x28, 0xB1, 0xC6, 0x35, 0x55, 0x64, 0x8E, 0x79, 0x44, 0x63, 0x87, + 0x32, 0x81, 0x09, 0x05, 0x3B, 0x9C, 0xE3, 0xAC, 0xA6, 0x13, 0x3A, 0x73, + 0xE2, 0xA2, 0x1C, 0x0F, 0x74, 0x4A, 0xE0, 0x90, 0x0F, 0x74, 0x17, 0x32, + 0x60, 0x23, 0xCD, 0x4C, 0x26, 0x88, 0x30, 0x91, 0x29, 0xA9, 0x46, 0xE6, + 0x80, 0xE9, 0x8E, 0x39, 0x51, 0x71, 0xDD, 0xA7, 0x08, 0xA5, 0x30, 0x69, + 0x3C, 0x04, 0x6D, 0x0C, 0x01, 0xD9, 0xC7, 0x3E, 0x2A, 0x0F, 0x79, 0x8E, + 0x54, 0x9E, 0xD2, 0x0A, 0x0B, 0xDA, 0x4A, 0x21, 0x05, 0xFF, 0xD4, 0xEC, + 0xC3, 0x44, 0x6A, 0x96, 0xE0, 0x3C, 0xD5, 0x6F, 0xB4, 0x87, 0x1D, 0x24, + 0xA2, 0x56, 0x4B, 0xCC, 0x42, 0xB7, 0x4D, 0x3B, 0x1D, 0x12, 0x68, 0x75, + 0xF0, 0x50, 0x73, 0x9B, 0xD8, 0x29, 0xBD, 0x9B, 0x47, 0x28, 0x71, 0xDD, + 0x20, 0xA2, 0xC6, 0x0F, 0x60, 0x9F, 0x67, 0x92, 0x93, 0x66, 0x51, 0x01, + 0x8E, 0x44, 0xA5, 0x6A, 0xA4, 0x05, 0x93, 0xCA, 0x67, 0x6D, 0x6A, 0x23, + 0xCE, 0xBA, 0x28, 0x47, 0xF2, 0x44, 0xA2, 0x82, 0x15, 0x59, 0x9F, 0x29, + 0xF1, 0x56, 0x2B, 0x60, 0xE4, 0x99, 0xF2, 0x55, 0xB7, 0x86, 0xC4, 0x85, + 0x2F, 0xB4, 0xBF, 0x81, 0xF8, 0xA0, 0x6D, 0x40, 0x80, 0xDB, 0x0E, 0x05, + 0x22, 0x55, 0x56, 0xD9, 0xAC, 0xCA, 0x28, 0xB0, 0x21, 0x4B, 0xAD, 0x28, + 0x1B, 0xB8, 0x45, 0xAD, 0xA5, 0xBC, 0xF7, 0x55, 0x3E, 0xD0, 0x1A, 0xA4, + 0xCC, 0xC6, 0x9D, 0x0F, 0x28, 0x10, 0x54, 0x08, 0x4D, 0x68, 0x07, 0x8D, + 0x4A, 0x09, 0x69, 0xF0, 0x4E, 0xEB, 0xC7, 0x68, 0x3E, 0x68, 0x67, 0x29, + 0xAD, 0x69, 0x9F, 0x92, 0x20, 0x15, 0x12, 0x1F, 0xFF, 0xD5, 0xEB, 0x46, + 0x33, 0x87, 0x01, 0x3F, 0xA7, 0x6B, 0x75, 0x06, 0x07, 0xE2, 0xAD, 0x8D, + 0xC3, 0x8E, 0xE9, 0xB6, 0x13, 0xCA, 0xB5, 0xC4, 0xD4, 0xE1, 0x0D, 0x33, + 0xEA, 0x18, 0x92, 0xA5, 0xC0, 0x8E, 0xEA, 0xDF, 0xA2, 0xD3, 0xD9, 0x2F, + 0x48, 0x01, 0xC2, 0x5C, 0x4A, 0xE1, 0x2D, 0x51, 0x1F, 0x34, 0xE6, 0xC0, + 0x02, 0x31, 0xAC, 0x14, 0x3B, 0x18, 0x47, 0x01, 0x2B, 0x0A, 0xA2, 0x87, + 0x79, 0x9D, 0x54, 0x1D, 0x6F, 0x75, 0x37, 0x53, 0x61, 0xD6, 0x14, 0x0D, + 0x64, 0x09, 0x21, 0x3B, 0x45, 0xA6, 0xD1, 0xBD, 0xFB, 0xB5, 0x01, 0x38, + 0x32, 0x3F, 0x82, 0x44, 0x38, 0xE8, 0x02, 0x23, 0x2B, 0x9F, 0x2F, 0x8A, + 0x56, 0x8A, 0x60, 0x1D, 0x1A, 0xA4, 0x6D, 0x8F, 0x8A, 0x28, 0xA9, 0xDD, + 0xB5, 0x1F, 0x82, 0x4E, 0xA8, 0x1D, 0x74, 0x94, 0xAC, 0x2A, 0x8A, 0x22, + 0xF9, 0x52, 0x68, 0xF2, 0x92, 0xA4, 0x6B, 0x20, 0x79, 0xF9, 0x26, 0x1B, + 0x87, 0x23, 0x44, 0xAD, 0x34, 0xC1, 0xAC, 0x71, 0x76, 0xB2, 0x8A, 0x71, + 0x89, 0xD7, 0x52, 0x54, 0x98, 0x78, 0x85, 0x65, 0x80, 0xC2, 0x06, 0x49, + 0x11, 0x0F, 0xFF, 0xD6, 0xEF, 0x9A, 0xDF, 0x15, 0x20, 0xDF, 0x24, 0xFC, + 0x15, 0x36, 0x89, 0x53, 0x5B, 0x05, 0x30, 0xDA, 0xA2, 0x5A, 0x8E, 0x5A, + 0xA0, 0x5A, 0x95, 0xA6, 0x90, 0x96, 0xC2, 0x81, 0x00, 0xA2, 0xB8, 0x28, + 0x22, 0xB5, 0x19, 0x60, 0x88, 0x84, 0x27, 0xD6, 0x22, 0x00, 0xE1, 0x58, + 0x25, 0x0C, 0xA2, 0x10, 0x40, 0x43, 0xB1, 0xA7, 0xE9, 0x69, 0x0A, 0x62, + 0xB1, 0x13, 0x32, 0x12, 0x21, 0x38, 0x24, 0x70, 0x8D, 0xA2, 0x91, 0x19, + 0x07, 0x44, 0xA1, 0xDF, 0xEC, 0x44, 0x30, 0x90, 0x84, 0xAD, 0x54, 0xC1, + 0xAD, 0x77, 0x8A, 0x23, 0x6B, 0x61, 0x3E, 0xED, 0x54, 0x80, 0x08, 0x83, + 0x6F, 0x64, 0x09, 0x48, 0x0C, 0x05, 0x4D, 0xEC, 0x11, 0x03, 0x23, 0x84, + 0xED, 0x08, 0x80, 0x21, 0x69, 0x01, 0xFF, 0xD9, 0x00, 0x38, 0x42, 0x49, + 0x4D, 0x04, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x41, 0x00, 0x64, 0x00, 0x6F, + 0x00, 0x62, 0x00, 0x65, 0x00, 0x20, 0x00, 0x50, 0x00, 0x68, 0x00, 0x6F, + 0x00, 0x74, 0x00, 0x6F, 0x00, 0x73, 0x00, 0x68, 0x00, 0x6F, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x13, 0x00, 0x41, 0x00, 0x64, 0x00, 0x6F, 0x00, 0x62, + 0x00, 0x65, 0x00, 0x20, 0x00, 0x50, 0x00, 0x68, 0x00, 0x6F, 0x00, 0x74, + 0x00, 0x6F, 0x00, 0x73, 0x00, 0x68, 0x00, 0x6F, 0x00, 0x70, 0x00, 0x20, + 0x00, 0x37, 0x00, 0x2E, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x38, + 0x42, 0x49, 0x4D, 0x04, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0xFF, 0xE1, 0x12, 0x48, 0x68, + 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x6E, 0x73, 0x2E, 0x61, 0x64, 0x6F, + 0x62, 0x65, 0x2E, 0x63, 0x6F, 0x6D, 0x2F, 0x78, 0x61, 0x70, 0x2F, 0x31, + 0x2E, 0x30, 0x2F, 0x00, 0x3C, 0x3F, 0x78, 0x70, 0x61, 0x63, 0x6B, 0x65, + 0x74, 0x20, 0x62, 0x65, 0x67, 0x69, 0x6E, 0x3D, 0x27, 0xEF, 0xBB, 0xBF, + 0x27, 0x20, 0x69, 0x64, 0x3D, 0x27, 0x57, 0x35, 0x4D, 0x30, 0x4D, 0x70, + 0x43, 0x65, 0x68, 0x69, 0x48, 0x7A, 0x72, 0x65, 0x53, 0x7A, 0x4E, 0x54, + 0x63, 0x7A, 0x6B, 0x63, 0x39, 0x64, 0x27, 0x3F, 0x3E, 0x0A, 0x3C, 0x3F, + 0x61, 0x64, 0x6F, 0x62, 0x65, 0x2D, 0x78, 0x61, 0x70, 0x2D, 0x66, 0x69, + 0x6C, 0x74, 0x65, 0x72, 0x73, 0x20, 0x65, 0x73, 0x63, 0x3D, 0x22, 0x43, + 0x52, 0x22, 0x3F, 0x3E, 0x0A, 0x3C, 0x78, 0x3A, 0x78, 0x61, 0x70, 0x6D, + 0x65, 0x74, 0x61, 0x20, 0x78, 0x6D, 0x6C, 0x6E, 0x73, 0x3A, 0x78, 0x3D, + 0x27, 0x61, 0x64, 0x6F, 0x62, 0x65, 0x3A, 0x6E, 0x73, 0x3A, 0x6D, 0x65, + 0x74, 0x61, 0x2F, 0x27, 0x20, 0x78, 0x3A, 0x78, 0x61, 0x70, 0x74, 0x6B, + 0x3D, 0x27, 0x58, 0x4D, 0x50, 0x20, 0x74, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, + 0x74, 0x20, 0x32, 0x2E, 0x38, 0x2E, 0x32, 0x2D, 0x33, 0x33, 0x2C, 0x20, + 0x66, 0x72, 0x61, 0x6D, 0x65, 0x77, 0x6F, 0x72, 0x6B, 0x20, 0x31, 0x2E, + 0x35, 0x27, 0x3E, 0x0A, 0x3C, 0x72, 0x64, 0x66, 0x3A, 0x52, 0x44, 0x46, + 0x20, 0x78, 0x6D, 0x6C, 0x6E, 0x73, 0x3A, 0x72, 0x64, 0x66, 0x3D, 0x27, + 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x77, 0x77, 0x77, 0x2E, 0x77, + 0x33, 0x2E, 0x6F, 0x72, 0x67, 0x2F, 0x31, 0x39, 0x39, 0x39, 0x2F, 0x30, + 0x32, 0x2F, 0x32, 0x32, 0x2D, 0x72, 0x64, 0x66, 0x2D, 0x73, 0x79, 0x6E, + 0x74, 0x61, 0x78, 0x2D, 0x6E, 0x73, 0x23, 0x27, 0x20, 0x78, 0x6D, 0x6C, + 0x6E, 0x73, 0x3A, 0x69, 0x58, 0x3D, 0x27, 0x68, 0x74, 0x74, 0x70, 0x3A, + 0x2F, 0x2F, 0x6E, 0x73, 0x2E, 0x61, 0x64, 0x6F, 0x62, 0x65, 0x2E, 0x63, + 0x6F, 0x6D, 0x2F, 0x69, 0x58, 0x2F, 0x31, 0x2E, 0x30, 0x2F, 0x27, 0x3E, + 0x0A, 0x0A, 0x20, 0x3C, 0x72, 0x64, 0x66, 0x3A, 0x44, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x61, 0x62, 0x6F, 0x75, + 0x74, 0x3D, 0x27, 0x75, 0x75, 0x69, 0x64, 0x3A, 0x65, 0x35, 0x35, 0x35, + 0x61, 0x39, 0x65, 0x35, 0x2D, 0x61, 0x61, 0x38, 0x65, 0x2D, 0x31, 0x31, + 0x65, 0x31, 0x2D, 0x62, 0x30, 0x62, 0x33, 0x2D, 0x65, 0x62, 0x31, 0x39, + 0x32, 0x32, 0x62, 0x38, 0x35, 0x34, 0x30, 0x35, 0x27, 0x0A, 0x20, 0x20, + 0x78, 0x6D, 0x6C, 0x6E, 0x73, 0x3A, 0x78, 0x61, 0x70, 0x4D, 0x4D, 0x3D, + 0x27, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x6E, 0x73, 0x2E, 0x61, + 0x64, 0x6F, 0x62, 0x65, 0x2E, 0x63, 0x6F, 0x6D, 0x2F, 0x78, 0x61, 0x70, + 0x2F, 0x31, 0x2E, 0x30, 0x2F, 0x6D, 0x6D, 0x2F, 0x27, 0x3E, 0x0A, 0x20, + 0x20, 0x3C, 0x78, 0x61, 0x70, 0x4D, 0x4D, 0x3A, 0x44, 0x6F, 0x63, 0x75, + 0x6D, 0x65, 0x6E, 0x74, 0x49, 0x44, 0x3E, 0x61, 0x64, 0x6F, 0x62, 0x65, + 0x3A, 0x64, 0x6F, 0x63, 0x69, 0x64, 0x3A, 0x70, 0x68, 0x6F, 0x74, 0x6F, + 0x73, 0x68, 0x6F, 0x70, 0x3A, 0x65, 0x35, 0x35, 0x35, 0x61, 0x39, 0x65, + 0x31, 0x2D, 0x61, 0x61, 0x38, 0x65, 0x2D, 0x31, 0x31, 0x65, 0x31, 0x2D, + 0x62, 0x30, 0x62, 0x33, 0x2D, 0x65, 0x62, 0x31, 0x39, 0x32, 0x32, 0x62, + 0x38, 0x35, 0x34, 0x30, 0x35, 0x3C, 0x2F, 0x78, 0x61, 0x70, 0x4D, 0x4D, + 0x3A, 0x44, 0x6F, 0x63, 0x75, 0x6D, 0x65, 0x6E, 0x74, 0x49, 0x44, 0x3E, + 0x0A, 0x20, 0x3C, 0x2F, 0x72, 0x64, 0x66, 0x3A, 0x44, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x3E, 0x0A, 0x0A, 0x3C, 0x2F, + 0x72, 0x64, 0x66, 0x3A, 0x52, 0x44, 0x46, 0x3E, 0x0A, 0x3C, 0x2F, 0x78, + 0x3A, 0x78, 0x61, 0x70, 0x6D, 0x65, 0x74, 0x61, 0x3E, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x0A, 0x3C, 0x3F, 0x78, 0x70, 0x61, 0x63, 0x6B, 0x65, 0x74, 0x20, + 0x65, 0x6E, 0x64, 0x3D, 0x27, 0x77, 0x27, 0x3F, 0x3E, 0xFF, 0xEE, 0x00, + 0x0E, 0x41, 0x64, 0x6F, 0x62, 0x65, 0x00, 0x64, 0x80, 0x00, 0x00, 0x00, + 0x01, 0xFF, 0xDB, 0x00, 0x84, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x09, 0x08, + 0x0C, 0x09, 0x09, 0x0C, 0x11, 0x0B, 0x0A, 0x0B, 0x11, 0x15, 0x0F, 0x0C, + 0x0C, 0x0F, 0x15, 0x18, 0x13, 0x13, 0x15, 0x13, 0x13, 0x18, 0x11, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x11, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x01, 0x0D, + 0x0B, 0x0B, 0x0D, 0x0E, 0x0D, 0x10, 0x0E, 0x0E, 0x10, 0x14, 0x0E, 0x0E, + 0x0E, 0x14, 0x14, 0x0E, 0x0E, 0x0E, 0x0E, 0x14, 0x11, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x11, 0x11, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x11, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x01, 0xF4, 0x01, 0xF4, + 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF, 0xDD, + 0x00, 0x04, 0x00, 0x20, 0xFF, 0xC4, 0x01, 0x3F, 0x00, 0x00, 0x01, 0x05, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x01, 0x02, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, + 0x0B, 0x01, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x10, 0x00, 0x01, 0x04, 0x01, 0x03, + 0x02, 0x04, 0x02, 0x05, 0x07, 0x06, 0x08, 0x05, 0x03, 0x0C, 0x33, 0x01, + 0x00, 0x02, 0x11, 0x03, 0x04, 0x21, 0x12, 0x31, 0x05, 0x41, 0x51, 0x61, + 0x13, 0x22, 0x71, 0x81, 0x32, 0x06, 0x14, 0x91, 0xA1, 0xB1, 0x42, 0x23, + 0x24, 0x15, 0x52, 0xC1, 0x62, 0x33, 0x34, 0x72, 0x82, 0xD1, 0x43, 0x07, + 0x25, 0x92, 0x53, 0xF0, 0xE1, 0xF1, 0x63, 0x73, 0x35, 0x16, 0xA2, 0xB2, + 0x83, 0x26, 0x44, 0x93, 0x54, 0x64, 0x45, 0xC2, 0xA3, 0x74, 0x36, 0x17, + 0xD2, 0x55, 0xE2, 0x65, 0xF2, 0xB3, 0x84, 0xC3, 0xD3, 0x75, 0xE3, 0xF3, + 0x46, 0x27, 0x94, 0xA4, 0x85, 0xB4, 0x95, 0xC4, 0xD4, 0xE4, 0xF4, 0xA5, + 0xB5, 0xC5, 0xD5, 0xE5, 0xF5, 0x56, 0x66, 0x76, 0x86, 0x96, 0xA6, 0xB6, + 0xC6, 0xD6, 0xE6, 0xF6, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xA7, + 0xB7, 0xC7, 0xD7, 0xE7, 0xF7, 0x11, 0x00, 0x02, 0x02, 0x01, 0x02, 0x04, + 0x04, 0x03, 0x04, 0x05, 0x06, 0x07, 0x07, 0x06, 0x05, 0x35, 0x01, 0x00, + 0x02, 0x11, 0x03, 0x21, 0x31, 0x12, 0x04, 0x41, 0x51, 0x61, 0x71, 0x22, + 0x13, 0x05, 0x32, 0x81, 0x91, 0x14, 0xA1, 0xB1, 0x42, 0x23, 0xC1, 0x52, + 0xD1, 0xF0, 0x33, 0x24, 0x62, 0xE1, 0x72, 0x82, 0x92, 0x43, 0x53, 0x15, + 0x63, 0x73, 0x34, 0xF1, 0x25, 0x06, 0x16, 0xA2, 0xB2, 0x83, 0x07, 0x26, + 0x35, 0xC2, 0xD2, 0x44, 0x93, 0x54, 0xA3, 0x17, 0x64, 0x45, 0x55, 0x36, + 0x74, 0x65, 0xE2, 0xF2, 0xB3, 0x84, 0xC3, 0xD3, 0x75, 0xE3, 0xF3, 0x46, + 0x94, 0xA4, 0x85, 0xB4, 0x95, 0xC4, 0xD4, 0xE4, 0xF4, 0xA5, 0xB5, 0xC5, + 0xD5, 0xE5, 0xF5, 0x56, 0x66, 0x76, 0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, + 0xE6, 0xF6, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xA7, 0xB7, + 0xC7, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, + 0x00, 0x3F, 0x00, 0xF4, 0x24, 0xC9, 0xD3, 0x15, 0x33, 0x02, 0xC5, 0x20, + 0x98, 0xA7, 0x09, 0x29, 0x90, 0x53, 0x0A, 0x01, 0x4C, 0x24, 0x96, 0x61, + 0x24, 0xC9, 0xD0, 0x4A, 0x92, 0x49, 0x24, 0x94, 0xA4, 0xD0, 0x9D, 0x24, + 0x94, 0xB2, 0x89, 0x52, 0x2A, 0x25, 0x24, 0x2C, 0x53, 0x27, 0x29, 0x8A, + 0x2A, 0x59, 0x44, 0xA9, 0x14, 0xC5, 0x24, 0x31, 0x2A, 0x25, 0x49, 0x32, + 0x28, 0x62, 0x92, 0x74, 0xC9, 0x29, 0x64, 0x92, 0x4C, 0x52, 0x52, 0xC5, + 0x32, 0x72, 0x98, 0xA2, 0x85, 0x93, 0x14, 0x89, 0x4C, 0x4A, 0x4A, 0x5D, + 0x34, 0xA6, 0x49, 0x24, 0x2F, 0x29, 0x4A, 0x69, 0x49, 0x15, 0x29, 0x24, + 0x92, 0x49, 0x4B, 0x24, 0x92, 0x49, 0x29, 0x49, 0x24, 0x92, 0x4A, 0x52, + 0x49, 0x24, 0x92, 0x94, 0x99, 0x24, 0x92, 0x52, 0x92, 0x49, 0x3A, 0x4A, + 0x5D, 0x38, 0x4C, 0x13, 0xA4, 0xA5, 0x27, 0x4C, 0x9C, 0x20, 0x95, 0xD3, + 0xA6, 0x09, 0xD2, 0x52, 0xE9, 0x24, 0x14, 0xA1, 0x25, 0x28, 0x05, 0x28, + 0x48, 0x05, 0x20, 0xD4, 0x12, 0xB4, 0x25, 0x0A, 0x61, 0xA5, 0x3E, 0xD4, + 0x13, 0x4C, 0x00, 0x52, 0x4A, 0x13, 0x80, 0x92, 0x94, 0x02, 0x78, 0x49, + 0x24, 0x92, 0xA4, 0x92, 0x49, 0x25, 0x2C, 0x92, 0x7E, 0xE9, 0x24, 0xA7, + 0xFF, 0xD0, 0xF4, 0x24, 0x88, 0x4A, 0x12, 0x85, 0x33, 0x0B, 0x18, 0x4E, + 0x02, 0x78, 0x4E, 0x92, 0x95, 0x0A, 0x4D, 0x09, 0x94, 0xDA, 0x82, 0x97, + 0x84, 0xA1, 0x48, 0x27, 0x84, 0x92, 0xC1, 0x25, 0x22, 0x13, 0x24, 0xA5, + 0x93, 0x29, 0x42, 0x45, 0x25, 0x31, 0x4C, 0xA4, 0x99, 0x24, 0x31, 0x84, + 0xC4, 0x29, 0x24, 0x42, 0x4A, 0x60, 0x42, 0x89, 0x0A, 0x70, 0x98, 0x84, + 0x50, 0xC0, 0x85, 0x12, 0x11, 0x08, 0x51, 0x29, 0x29, 0x81, 0x4C, 0xA4, + 0x53, 0x22, 0x86, 0x29, 0x8A, 0x91, 0x51, 0x29, 0x29, 0x89, 0x51, 0x25, + 0x39, 0x51, 0x25, 0x14, 0x28, 0x94, 0xD2, 0x98, 0x94, 0xD2, 0x8A, 0x17, + 0x94, 0xA5, 0x46, 0x53, 0xA4, 0x85, 0xD2, 0x4D, 0x29, 0x24, 0xA5, 0xD2, + 0x4C, 0x94, 0xA4, 0x95, 0xD2, 0x4D, 0x29, 0x4A, 0x4A, 0x5D, 0x32, 0x49, + 0x12, 0x92, 0x95, 0x29, 0x26, 0x4A, 0x52, 0x52, 0xE9, 0x24, 0x9D, 0x25, + 0x29, 0x28, 0x4E, 0x13, 0xA0, 0xA5, 0x04, 0x92, 0x52, 0x09, 0x25, 0x68, + 0x4E, 0x94, 0x27, 0xDA, 0x92, 0x94, 0x13, 0xA5, 0xB4, 0xA7, 0x0D, 0x29, + 0x29, 0x41, 0x48, 0x24, 0x1A, 0x9F, 0x6A, 0x09, 0x5C, 0x15, 0x36, 0xA8, + 0x06, 0x95, 0x2D, 0x40, 0x41, 0x21, 0x20, 0x21, 0x3E, 0x8A, 0x0D, 0x53, + 0x49, 0x2B, 0x10, 0xA2, 0xA4, 0x98, 0xA4, 0xA5, 0x4A, 0x64, 0x92, 0x49, + 0x0A, 0x49, 0x24, 0x92, 0x52, 0x92, 0x49, 0x24, 0x94, 0xFF, 0x00, 0xFF, + 0xD1, 0xF4, 0x24, 0xEA, 0x29, 0xC2, 0x99, 0x81, 0x92, 0x50, 0x94, 0x84, + 0x92, 0x4A, 0xE0, 0x22, 0x00, 0x86, 0x11, 0x1A, 0x50, 0x2A, 0x0C, 0x80, + 0x4F, 0x09, 0xC2, 0x94, 0x04, 0x12, 0xC2, 0x12, 0x85, 0x32, 0xD4, 0xD0, + 0x95, 0xA5, 0x86, 0xD4, 0x88, 0x52, 0x84, 0x92, 0x42, 0x32, 0x21, 0x45, + 0x11, 0xCA, 0x05, 0x15, 0x2C, 0x9F, 0x94, 0x80, 0x4F, 0x10, 0x92, 0x98, + 0x90, 0xA2, 0x42, 0x99, 0xE5, 0x31, 0x09, 0x21, 0x19, 0x0A, 0x24, 0x22, + 0x15, 0x12, 0x25, 0x15, 0x23, 0x51, 0x44, 0x2D, 0x51, 0x20, 0x84, 0x51, + 0x4C, 0x0A, 0x89, 0x52, 0x2A, 0x05, 0x24, 0x31, 0x28, 0x6E, 0x2A, 0x6E, + 0x42, 0x71, 0x44, 0x2D, 0x2A, 0x94, 0xC4, 0xA6, 0x95, 0x12, 0x8A, 0x19, + 0x4A, 0x79, 0x43, 0x94, 0xB7, 0x22, 0xA4, 0x92, 0x90, 0x2A, 0x1B, 0x93, + 0x82, 0x92, 0x99, 0xA4, 0x9A, 0x52, 0x94, 0x14, 0xBA, 0x69, 0x4C, 0x4A, + 0x69, 0x49, 0x4C, 0xE5, 0x28, 0x25, 0x44, 0x4A, 0x23, 0x02, 0x49, 0x63, + 0x05, 0x38, 0x0A, 0x65, 0xA1, 0x20, 0x12, 0xB5, 0x53, 0x18, 0x29, 0xC0, + 0x28, 0x81, 0xA9, 0xC8, 0x08, 0x5A, 0x69, 0x84, 0x42, 0x8A, 0x21, 0x1A, + 0x28, 0x10, 0x92, 0x94, 0x14, 0x82, 0x82, 0x90, 0x49, 0x4C, 0x82, 0x70, + 0x52, 0x09, 0xD2, 0x53, 0x30, 0x9E, 0x14, 0x01, 0x85, 0x2D, 0xC8, 0x25, + 0x74, 0xE1, 0x44, 0x15, 0x24, 0x92, 0xBC, 0xA4, 0x13, 0x27, 0x08, 0x29, + 0x96, 0x81, 0x2D, 0xC5, 0x32, 0x64, 0x94, 0xCB, 0x72, 0x45, 0xC1, 0x45, + 0x24, 0x95, 0x6C, 0xA5, 0x31, 0x29, 0xA5, 0x32, 0x4A, 0x5E, 0x53, 0xCA, + 0x64, 0x92, 0x52, 0xF2, 0x92, 0x49, 0x24, 0xA7, 0xFF, 0xD2, 0xF4, 0x10, + 0x9F, 0x44, 0xC9, 0xD4, 0xCC, 0x0B, 0xA7, 0x05, 0x45, 0x3A, 0x4A, 0x66, + 0x35, 0x44, 0x6B, 0x50, 0x9A, 0x8A, 0xD2, 0x50, 0x29, 0x09, 0x00, 0x52, + 0x50, 0x05, 0x48, 0x14, 0x0A, 0x57, 0x48, 0x94, 0xD2, 0x92, 0x09, 0x51, + 0x51, 0x2A, 0x69, 0x8A, 0x4A, 0x46, 0xE5, 0x15, 0x22, 0xA2, 0x9C, 0x85, + 0x04, 0xE5, 0x32, 0x49, 0x29, 0x68, 0x4C, 0x53, 0xA8, 0x94, 0x94, 0xC4, + 0xA4, 0x92, 0x74, 0x50, 0xC6, 0x54, 0x5C, 0x25, 0x48, 0x84, 0xC4, 0xA4, + 0xA4, 0x2E, 0x69, 0x43, 0x76, 0x88, 0xEE, 0xE1, 0x09, 0xC1, 0x10, 0xB4, + 0xA1, 0x79, 0x41, 0x71, 0x56, 0x0B, 0x25, 0x09, 0xD5, 0xA7, 0x05, 0xA5, + 0x0C, 0x95, 0x30, 0xA5, 0xE9, 0x78, 0x27, 0xD8, 0x95, 0xA2, 0x91, 0x38, + 0x1E, 0xC8, 0x7A, 0xCA, 0x3B, 0x99, 0xE0, 0xA1, 0xB0, 0xA2, 0x82, 0x18, + 0x82, 0xA4, 0x13, 0xB6, 0xB5, 0x31, 0x5A, 0x49, 0xA6, 0x20, 0x15, 0x2D, + 0xA5, 0x48, 0x30, 0x85, 0x30, 0xD0, 0x82, 0x69, 0x16, 0xC4, 0xE1, 0xA8, + 0xBB, 0x53, 0x6D, 0x4A, 0xD5, 0x4C, 0x43, 0x41, 0x52, 0x0D, 0x85, 0x26, + 0x85, 0x20, 0x02, 0x16, 0x96, 0x30, 0x9C, 0x00, 0xA4, 0x40, 0x4C, 0x52, + 0x52, 0xE2, 0x13, 0x14, 0xA5, 0x31, 0x28, 0x29, 0x44, 0xA8, 0x92, 0x98, + 0xB9, 0x34, 0x94, 0x54, 0xB8, 0x52, 0xF8, 0x28, 0xEA, 0x53, 0x8D, 0x12, + 0x53, 0x24, 0xF2, 0x98, 0x14, 0x81, 0x49, 0x4B, 0x82, 0xA4, 0xA2, 0x9C, + 0x20, 0x96, 0x40, 0xA9, 0x4A, 0x8A, 0x49, 0x29, 0x98, 0x52, 0x10, 0xA0, + 0x14, 0x81, 0x41, 0x2C, 0xE0, 0x28, 0x94, 0xFA, 0x26, 0x49, 0x4B, 0x24, + 0x9D, 0x24, 0x94, 0xB2, 0x65, 0x28, 0x4C, 0x8A, 0x14, 0x92, 0x78, 0x4D, + 0x09, 0x29, 0x49, 0x25, 0xA2, 0x49, 0x29, 0xFF, 0xD3, 0xF4, 0x09, 0x48, + 0x14, 0xC9, 0xC0, 0x53, 0xB5, 0xD7, 0x95, 0x21, 0xAA, 0x88, 0x53, 0x01, + 0x04, 0xB2, 0x01, 0x4C, 0x4A, 0x88, 0x85, 0x36, 0xA0, 0x96, 0x40, 0x14, + 0x82, 0x79, 0x4E, 0x82, 0x54, 0x13, 0xA6, 0x09, 0xD0, 0x4A, 0x93, 0x14, + 0xE9, 0x92, 0x53, 0x17, 0x28, 0xA9, 0x9D, 0x53, 0x42, 0x28, 0x63, 0x09, + 0x29, 0x42, 0x62, 0x8A, 0x98, 0x15, 0x12, 0xA4, 0x54, 0x0A, 0x48, 0x50, + 0x48, 0xA6, 0x9D, 0x14, 0x0B, 0x91, 0x53, 0x22, 0x54, 0x09, 0x48, 0x94, + 0xD2, 0x92, 0x16, 0x82, 0x98, 0xB4, 0x29, 0x4A, 0x8B, 0x9C, 0x11, 0x53, + 0x12, 0x04, 0x21, 0x18, 0x95, 0x37, 0xB8, 0x42, 0x14, 0xF7, 0x44, 0x2D, + 0x2C, 0xE0, 0x28, 0x9D, 0x52, 0x12, 0x54, 0xDA, 0xD4, 0x94, 0xC0, 0x34, + 0xA7, 0x35, 0xA2, 0x86, 0x29, 0x86, 0x4A, 0x16, 0x9A, 0x6B, 0x0A, 0xE1, + 0x4D, 0xAD, 0x47, 0x35, 0xC2, 0x6D, 0xA9, 0x5A, 0xA9, 0x1F, 0xA6, 0x0A, + 0x6D, 0x84, 0x22, 0xBB, 0x45, 0x02, 0xE0, 0x92, 0x98, 0xC2, 0x7D, 0xA9, + 0xE5, 0x38, 0x84, 0x94, 0xC2, 0x0A, 0x92, 0x4E, 0x50, 0x2E, 0x80, 0x92, + 0x19, 0x39, 0xC0, 0x04, 0x32, 0xE5, 0x17, 0x3D, 0x36, 0xE4, 0x69, 0x56, + 0xCE, 0x53, 0x12, 0xA3, 0xB9, 0x44, 0x90, 0x8A, 0x2D, 0x72, 0x52, 0x92, + 0x9A, 0x25, 0x38, 0x09, 0x29, 0x70, 0xE2, 0x13, 0x87, 0x25, 0xB4, 0x26, + 0x49, 0x4C, 0xC1, 0x52, 0x0A, 0x0D, 0x53, 0x08, 0x25, 0x9B, 0x42, 0x9E, + 0xD5, 0x06, 0x14, 0x50, 0xE0, 0x82, 0x43, 0x12, 0xC4, 0xB6, 0xA2, 0x03, + 0x29, 0x42, 0x09, 0xA6, 0x00, 0x27, 0x88, 0x52, 0xD1, 0x32, 0x4A, 0x5C, + 0x29, 0x6D, 0x51, 0x05, 0x4C, 0x1D, 0x12, 0x53, 0x12, 0x13, 0x15, 0x22, + 0x52, 0x84, 0x94, 0xC1, 0x3E, 0xD4, 0xF0, 0xA4, 0x21, 0x25, 0x31, 0x84, + 0xC4, 0x22, 0x40, 0x50, 0x29, 0x29, 0x8F, 0x74, 0x92, 0xEE, 0x92, 0x28, + 0x7F, 0xFF, 0xD4, 0xF4, 0x18, 0x94, 0xFB, 0x54, 0x83, 0x52, 0x2A, 0x66, + 0x0A, 0x58, 0x05, 0x24, 0xC1, 0x48, 0x24, 0xA5, 0xC2, 0x94, 0xA8, 0xA9, + 0x35, 0x04, 0xAE, 0x25, 0x48, 0x25, 0x01, 0x24, 0x92, 0xCA, 0x53, 0x85, + 0x1D, 0x14, 0x82, 0x05, 0x4B, 0xA8, 0x95, 0x24, 0xD0, 0x82, 0x58, 0xA6, + 0x52, 0x21, 0x32, 0x28, 0x59, 0x45, 0xC5, 0x39, 0x2A, 0x04, 0xA2, 0xA6, + 0x2E, 0x2A, 0x04, 0xA9, 0x15, 0x02, 0x8A, 0x16, 0x95, 0x12, 0x53, 0x98, + 0x51, 0x25, 0x24, 0x30, 0x25, 0x2D, 0xC9, 0x9C, 0x54, 0x51, 0x43, 0x30, + 0xF2, 0xA2, 0xE7, 0xA6, 0xEC, 0x98, 0xA2, 0x86, 0x26, 0x4A, 0x40, 0x15, + 0x20, 0x97, 0x09, 0x29, 0x70, 0x11, 0x58, 0xD5, 0x06, 0x19, 0x46, 0x10, + 0x81, 0x48, 0x5C, 0x31, 0x48, 0x02, 0x12, 0x0E, 0x4E, 0x5C, 0x10, 0x5C, + 0xB1, 0x1E, 0x29, 0x88, 0x09, 0x17, 0x28, 0x12, 0x52, 0x43, 0x17, 0x84, + 0x2E, 0xE8, 0x84, 0xA8, 0x10, 0x88, 0x41, 0x5B, 0x72, 0x7D, 0xC5, 0x46, + 0x14, 0xA0, 0x22, 0x86, 0x25, 0xCA, 0x2E, 0x70, 0x53, 0x20, 0x21, 0x3C, + 0x88, 0x49, 0x05, 0x13, 0xDC, 0x86, 0x5E, 0xE5, 0x23, 0x2A, 0x24, 0x27, + 0x2D, 0x56, 0xF4, 0xFB, 0xE4, 0xA8, 0x10, 0x53, 0xB4, 0x19, 0x49, 0x49, + 0xDA, 0x4C, 0x29, 0x02, 0xA0, 0xD4, 0x46, 0xE8, 0x82, 0xE0, 0xAD, 0xC7, + 0x84, 0xE0, 0x27, 0x89, 0xE1, 0x38, 0x49, 0x4A, 0x01, 0x38, 0x49, 0x21, + 0x28, 0x25, 0x90, 0x4E, 0x0A, 0x68, 0x29, 0xC0, 0x49, 0x49, 0x1A, 0x4A, + 0x91, 0x25, 0x45, 0xAA, 0x52, 0x82, 0x56, 0xDC, 0x9E, 0x54, 0x53, 0x80, + 0x92, 0x97, 0x95, 0x20, 0x54, 0x42, 0x98, 0x84, 0x12, 0xB8, 0x52, 0x01, + 0x33, 0x54, 0xF4, 0x84, 0x92, 0xC2, 0x0A, 0x68, 0x2A, 0x64, 0xA6, 0x84, + 0x94, 0xC4, 0xCA, 0x89, 0x95, 0x32, 0x13, 0x6D, 0x49, 0x0C, 0x35, 0x49, + 0x4B, 0x6A, 0x49, 0x2A, 0x9F, 0xFF, 0xD5, 0xF4, 0x60, 0x12, 0xDA, 0x12, + 0xDC, 0x9B, 0x72, 0x99, 0x85, 0x47, 0x44, 0xA5, 0x31, 0x29, 0x82, 0x48, + 0x66, 0x35, 0x44, 0x6A, 0x1B, 0x54, 0xC2, 0x09, 0x67, 0x29, 0x4A, 0x8A, + 0x52, 0x92, 0x59, 0x05, 0x20, 0x54, 0x42, 0x52, 0x82, 0x99, 0xCA, 0x69, + 0x51, 0x25, 0x29, 0x4A, 0x94, 0xC9, 0x45, 0xC9, 0xE5, 0x33, 0x92, 0x53, + 0x02, 0x54, 0x0A, 0x91, 0x4C, 0x8A, 0x18, 0x94, 0x37, 0x29, 0x95, 0x12, + 0x35, 0x45, 0x08, 0xDD, 0xC2, 0x19, 0x25, 0x19, 0xCD, 0x42, 0x70, 0x45, + 0x05, 0x84, 0xA5, 0xC2, 0x63, 0x29, 0x6B, 0x08, 0xA1, 0x44, 0x84, 0xDC, + 0xEA, 0x9A, 0x13, 0xA4, 0xA5, 0xB5, 0x09, 0xF5, 0x29, 0x19, 0x52, 0x63, + 0x67, 0x54, 0x94, 0xCA, 0xB6, 0x9E, 0x51, 0x44, 0x26, 0x1A, 0x04, 0xDB, + 0xA5, 0x04, 0xA4, 0x1C, 0x68, 0x9E, 0x09, 0x0A, 0x2D, 0x30, 0x96, 0xE2, + 0x82, 0x54, 0x41, 0x50, 0x32, 0xA5, 0xBB, 0x54, 0xFB, 0x41, 0x49, 0x48, + 0x88, 0x3D, 0x93, 0x06, 0xF8, 0xA2, 0x3B, 0x40, 0xA0, 0x4A, 0x28, 0x5F, + 0x68, 0x4C, 0x40, 0x0A, 0x32, 0x91, 0x72, 0x4A, 0x62, 0xE5, 0x02, 0x25, + 0x49, 0xCA, 0x28, 0xAD, 0x28, 0xDC, 0x02, 0x8C, 0x22, 0x91, 0x29, 0xBD, + 0x39, 0x45, 0x14, 0x8F, 0x64, 0xA4, 0x1A, 0x8C, 0x18, 0x91, 0x6A, 0x56, + 0xAA, 0x60, 0x1A, 0xA6, 0x02, 0x70, 0x13, 0x80, 0x92, 0x54, 0x04, 0x25, + 0x09, 0xCA, 0x64, 0x12, 0xC9, 0xA1, 0x11, 0xA0, 0x28, 0x37, 0x44, 0xF2, + 0x52, 0x53, 0x38, 0x0A, 0x41, 0xA8, 0x61, 0x11, 0xA5, 0x04, 0xAF, 0xB5, + 0x2D, 0xAA, 0x4D, 0x0A, 0x44, 0x20, 0x96, 0x00, 0x25, 0x0A, 0x6A, 0x25, + 0x25, 0x31, 0x21, 0x38, 0x4E, 0x92, 0x4A, 0x64, 0xD5, 0x30, 0x86, 0x0A, + 0x98, 0x29, 0x29, 0x74, 0x93, 0x12, 0x9B, 0x72, 0x49, 0x64, 0x99, 0x2D, + 0xC9, 0x89, 0x49, 0x4A, 0x84, 0x92, 0x94, 0x92, 0x43, 0xFF, 0xD6, 0xF4, + 0x1D, 0xC9, 0xE5, 0x44, 0xC2, 0x75, 0x3B, 0x5D, 0x79, 0x52, 0x01, 0x40, + 0x29, 0x04, 0x12, 0x90, 0x29, 0x05, 0x16, 0xA7, 0xD5, 0x04, 0xAF, 0x29, + 0x4A, 0x8A, 0x52, 0x92, 0x99, 0x02, 0x9E, 0x54, 0x41, 0x49, 0x25, 0x32, + 0x4A, 0x54, 0x65, 0x22, 0x52, 0x53, 0x3D, 0xCA, 0x24, 0xA8, 0x92, 0x9B, + 0x72, 0x54, 0xAB, 0x5E, 0x53, 0x26, 0x94, 0xE8, 0xA9, 0x44, 0x26, 0xDA, + 0x9E, 0x52, 0x25, 0x25, 0x31, 0x23, 0x44, 0x37, 0x34, 0x22, 0xF6, 0x42, + 0x78, 0x84, 0x90, 0x50, 0xB9, 0xA9, 0x80, 0x21, 0x12, 0x67, 0xB2, 0x73, + 0xF4, 0x74, 0x46, 0xD1, 0x48, 0x5D, 0xA7, 0x29, 0xB9, 0x0A, 0x4E, 0x32, + 0x91, 0x6E, 0x82, 0x11, 0x42, 0xC0, 0x4F, 0x6E, 0x11, 0x18, 0x21, 0x30, + 0x02, 0x25, 0x4C, 0x44, 0x20, 0x95, 0x41, 0x51, 0x1F, 0x49, 0x14, 0x44, + 0x6A, 0x91, 0x0D, 0x41, 0x34, 0xB0, 0x88, 0xD1, 0x47, 0x50, 0x53, 0x81, + 0x1C, 0x27, 0x29, 0x29, 0x62, 0x53, 0x6F, 0x01, 0x28, 0x51, 0x73, 0x80, + 0x45, 0x0A, 0x7B, 0xA5, 0x57, 0x71, 0x32, 0xA6, 0x4A, 0x83, 0xA0, 0xA2, + 0x10, 0x58, 0x7A, 0x84, 0x26, 0x0F, 0x32, 0x99, 0xDC, 0xA6, 0x82, 0x78, + 0x45, 0x6D, 0xA4, 0xF5, 0x12, 0xDE, 0x10, 0xA0, 0x85, 0x20, 0x95, 0x2A, + 0xD2, 0xB4, 0xCA, 0x20, 0x08, 0x0C, 0x3A, 0xA3, 0x02, 0x91, 0x48, 0x65, + 0x09, 0x88, 0x53, 0x6C, 0x14, 0xE5, 0x92, 0x82, 0x50, 0x93, 0xAA, 0x70, + 0x14, 0x8D, 0x71, 0xAA, 0x5A, 0x24, 0xA5, 0x94, 0x60, 0xA2, 0x42, 0x68, + 0x49, 0x4B, 0x34, 0x15, 0x28, 0x48, 0x0D, 0x54, 0xB4, 0x49, 0x4B, 0x34, + 0x22, 0x00, 0xA2, 0x21, 0x48, 0x10, 0x82, 0x43, 0x36, 0xA9, 0x97, 0x04, + 0x30, 0x42, 0x79, 0x94, 0x12, 0xBC, 0xA8, 0x92, 0x9E, 0x53, 0x14, 0x94, + 0xC7, 0x74, 0x24, 0x5E, 0x53, 0x39, 0x42, 0x75, 0x45, 0x09, 0x1A, 0xE5, + 0x20, 0xF4, 0x12, 0xE4, 0xB7, 0x25, 0x4A, 0xB4, 0xDB, 0xA5, 0x29, 0x43, + 0x0E, 0x4F, 0xBD, 0x2A, 0x55, 0xA4, 0x94, 0xA5, 0x0F, 0x78, 0x4B, 0x70, + 0x49, 0x56, 0x93, 0x71, 0x49, 0x43, 0x70, 0x49, 0x25, 0x3F, 0xFF, 0xD7, + 0xEF, 0x9A, 0x88, 0x10, 0x5A, 0x51, 0x01, 0x53, 0x96, 0xB8, 0x66, 0x21, + 0x38, 0x4C, 0x9C, 0x20, 0x96, 0x63, 0x84, 0xE0, 0xA1, 0xEE, 0x52, 0x05, + 0x24, 0xA8, 0xA4, 0x98, 0x94, 0x92, 0x43, 0x20, 0xA4, 0x02, 0x66, 0xA9, + 0x84, 0x12, 0xC6, 0x14, 0x5C, 0x88, 0x50, 0xDC, 0x92, 0x98, 0x95, 0x14, + 0x89, 0x4D, 0x28, 0xA1, 0x94, 0xA5, 0x2A, 0x32, 0x94, 0xA4, 0xA6, 0x52, + 0x94, 0xA8, 0xA4, 0x4A, 0x4A, 0x67, 0x28, 0x4F, 0xE7, 0x54, 0x8B, 0xA1, + 0x45, 0xCE, 0x25, 0x25, 0x5A, 0xA5, 0x24, 0xC1, 0x2E, 0x51, 0x42, 0xC1, + 0xB3, 0xAA, 0x90, 0xAE, 0x75, 0x4E, 0x01, 0x52, 0x69, 0x09, 0x29, 0x89, + 0x61, 0xE1, 0x37, 0x05, 0x49, 0xC5, 0x30, 0x49, 0x2B, 0x13, 0x01, 0x29, + 0x48, 0xB4, 0x95, 0x12, 0x08, 0x49, 0x0C, 0xF7, 0x42, 0x89, 0x77, 0x82, + 0x60, 0x09, 0x4E, 0x6B, 0x29, 0x29, 0x8E, 0xF3, 0x08, 0x66, 0x49, 0xD5, + 0x4C, 0x88, 0xD1, 0x3E, 0xD0, 0x42, 0x28, 0x44, 0x65, 0x41, 0xC0, 0xA3, + 0x16, 0xE8, 0xA0, 0x44, 0xE8, 0x92, 0x0B, 0x5E, 0x25, 0x4C, 0x08, 0x4E, + 0x5A, 0x25, 0x2E, 0xE8, 0xA1, 0x50, 0x98, 0x80, 0xA7, 0x1A, 0x26, 0x21, + 0x25, 0x31, 0x68, 0x84, 0x46, 0x92, 0x86, 0x7C, 0x94, 0xDB, 0x21, 0x24, + 0x84, 0xC0, 0xE8, 0xA6, 0x0A, 0x1B, 0x22, 0x75, 0x53, 0x25, 0x04, 0xA8, + 0x92, 0x74, 0x51, 0x80, 0x91, 0x29, 0x83, 0x92, 0x53, 0x20, 0x14, 0xA0, + 0x42, 0x8C, 0xA5, 0x29, 0x29, 0x62, 0x96, 0xE0, 0x13, 0x39, 0xC8, 0x72, + 0x51, 0x45, 0xA5, 0x2E, 0x4D, 0xBD, 0x0C, 0x98, 0x4D, 0x32, 0x95, 0x2A, + 0xD2, 0x7A, 0xA5, 0x48, 0x5A, 0x84, 0x9C, 0x25, 0x4A, 0xB4, 0x9E, 0xA9, + 0x4B, 0xD5, 0x50, 0x48, 0xA5, 0x4A, 0xB6, 0x46, 0xC4, 0xDB, 0x94, 0x0A, + 0x64, 0xA9, 0x56, 0xCC, 0x94, 0xD2, 0x53, 0x4A, 0x52, 0x92, 0x99, 0x6F, + 0x29, 0xB7, 0x15, 0x19, 0x4D, 0x32, 0x92, 0x2D, 0x9E, 0xE2, 0xA4, 0x1C, + 0x54, 0x02, 0x90, 0x49, 0x36, 0xCE, 0x74, 0x49, 0x46, 0x74, 0x49, 0x04, + 0xBF, 0xFF, 0xD0, 0xEE, 0xDA, 0x88, 0x10, 0xC1, 0x53, 0x05, 0x58, 0x6B, + 0x33, 0x05, 0x3C, 0xA8, 0x82, 0x94, 0xA0, 0x96, 0x52, 0x9C, 0x15, 0x09, + 0x4A, 0x52, 0x55, 0xB3, 0x94, 0xE0, 0xA1, 0xCA, 0x93, 0x42, 0x4A, 0x4C, + 0xD5, 0x30, 0x14, 0x18, 0x54, 0xC1, 0x4D, 0x2B, 0x94, 0x42, 0x83, 0x82, + 0x20, 0x51, 0x74, 0x24, 0xA4, 0x04, 0x28, 0x95, 0x27, 0x94, 0x2D, 0xC9, + 0xCB, 0x4B, 0x29, 0x48, 0x15, 0x09, 0x4B, 0x74, 0x24, 0xA4, 0xB2, 0xA0, + 0xE2, 0xA3, 0xBE, 0x52, 0x71, 0x94, 0xA9, 0x56, 0xB1, 0x71, 0x51, 0x94, + 0xD0, 0x65, 0x39, 0x28, 0xA1, 0x98, 0x3A, 0x29, 0x35, 0xB2, 0x14, 0x1A, + 0x8A, 0xDD, 0x10, 0x29, 0x0C, 0x83, 0x40, 0x0A, 0x24, 0x29, 0x82, 0x3B, + 0xA8, 0xBB, 0x94, 0x12, 0xB4, 0x28, 0xE8, 0x91, 0x72, 0x89, 0xE5, 0x14, + 0x32, 0x04, 0x05, 0x07, 0x9D, 0x52, 0x71, 0x51, 0x99, 0x4A, 0x91, 0x6C, + 0xDA, 0xEF, 0x15, 0x22, 0xFD, 0x10, 0x8A, 0x89, 0x72, 0x34, 0xAB, 0x66, + 0xE2, 0x0A, 0x40, 0x00, 0x84, 0x5C, 0x65, 0x3B, 0x49, 0x4A, 0x95, 0x69, + 0x49, 0x04, 0x28, 0x6D, 0x4E, 0x13, 0xED, 0x29, 0x29, 0x0B, 0x80, 0xEC, + 0x9B, 0x6A, 0x9B, 0x80, 0x09, 0xA5, 0x14, 0x2C, 0x98, 0xF9, 0x29, 0x25, + 0x09, 0x21, 0x18, 0x06, 0x54, 0xC0, 0x4E, 0x02, 0x53, 0x09, 0x29, 0x93, + 0x74, 0x53, 0x90, 0x50, 0x83, 0x94, 0x81, 0x42, 0x93, 0x6B, 0xB9, 0x46, + 0x40, 0x49, 0xDA, 0xA8, 0xC2, 0x2A, 0x48, 0x1C, 0x0A, 0x45, 0x0D, 0x3C, + 0x94, 0x95, 0x6A, 0x72, 0x8C, 0xA7, 0x95, 0x12, 0x92, 0x16, 0x71, 0x4C, + 0x0A, 0x63, 0xCA, 0x78, 0x45, 0x0C, 0x81, 0x52, 0x0A, 0x01, 0x48, 0x24, + 0x96, 0x52, 0x91, 0x4C, 0x9C, 0x20, 0xA5, 0xB5, 0x51, 0x28, 0x88, 0x6F, + 0xE5, 0x25, 0x2C, 0x0A, 0x97, 0x65, 0x0E, 0x13, 0xCA, 0x2A, 0x5D, 0x20, + 0x9A, 0x52, 0x94, 0x94, 0xC8, 0x15, 0x20, 0x50, 0xE4, 0xCA, 0x9B, 0x4A, + 0x4A, 0x66, 0x92, 0x7E, 0xC9, 0x20, 0x97, 0xFF, 0xD1, 0xEE, 0x03, 0x94, + 0x83, 0x90, 0x03, 0x94, 0xC3, 0x95, 0x9A, 0x6A, 0x02, 0x9C, 0x15, 0x20, + 0x84, 0xD2, 0xA6, 0x1C, 0x82, 0xE6, 0x49, 0xA5, 0x2E, 0x52, 0x84, 0x94, + 0xC9, 0xA5, 0x11, 0xA5, 0x09, 0xA1, 0x15, 0x81, 0x02, 0x90, 0xC8, 0x15, + 0x30, 0x54, 0x21, 0x3E, 0xA1, 0x05, 0xCC, 0xCB, 0xA0, 0x28, 0x39, 0xC9, + 0x6A, 0x98, 0x8D, 0x12, 0x52, 0x27, 0x21, 0x98, 0x45, 0x70, 0x95, 0x02, + 0xCD, 0x51, 0x5A, 0x58, 0xC4, 0x85, 0x12, 0xA6, 0x44, 0x21, 0xB9, 0x10, + 0x85, 0x82, 0x9B, 0x44, 0xA1, 0xB6, 0x65, 0x10, 0x24, 0xA5, 0xCB, 0x61, + 0x46, 0x24, 0xA9, 0x1E, 0x13, 0x00, 0x92, 0x97, 0x68, 0xF1, 0x46, 0x6C, + 0x14, 0x36, 0x89, 0x53, 0x80, 0x10, 0x5C, 0x19, 0x18, 0x0A, 0x24, 0x4F, + 0x09, 0x89, 0x4C, 0x5E, 0x21, 0x05, 0x5A, 0xCE, 0x30, 0xA0, 0x5C, 0x53, + 0xF2, 0xA2, 0x9C, 0xB5, 0x6F, 0x8A, 0x8F, 0x74, 0x43, 0x0A, 0x1C, 0xA4, + 0xA5, 0xFB, 0x28, 0x16, 0xA2, 0x80, 0x13, 0x16, 0xCA, 0x4A, 0x46, 0x1A, + 0x49, 0x45, 0x15, 0x24, 0xD6, 0xC2, 0x33, 0x48, 0x29, 0x12, 0xA0, 0x18, + 0x8A, 0xCA, 0x45, 0x88, 0xA0, 0x84, 0xC5, 0x0B, 0x5D, 0x4D, 0x67, 0xB5, + 0x08, 0x85, 0x65, 0xE2, 0x4A, 0x13, 0x9B, 0x08, 0x82, 0xB4, 0x84, 0x60, + 0x27, 0x00, 0xA4, 0xA4, 0x11, 0x43, 0x12, 0x21, 0x44, 0xA2, 0x6D, 0x94, + 0xC5, 0xA9, 0x29, 0x80, 0x10, 0xA4, 0x13, 0x42, 0x49, 0x21, 0x79, 0x09, + 0x93, 0x6A, 0xA4, 0xD1, 0xE2, 0x92, 0x56, 0xDA, 0x98, 0x84, 0x4D, 0x14, + 0x1D, 0x29, 0x28, 0xAD, 0x09, 0x88, 0x09, 0xF5, 0x4C, 0xED, 0x02, 0x48, + 0x62, 0x40, 0x4A, 0x14, 0x75, 0x52, 0x01, 0x14, 0x2B, 0xB2, 0x70, 0x53, + 0x14, 0xD2, 0x92, 0x52, 0x4A, 0x90, 0x84, 0x20, 0x51, 0x1A, 0x82, 0x82, + 0xEE, 0x43, 0x71, 0x52, 0x79, 0x43, 0x73, 0xD2, 0x0A, 0x2B, 0xCC, 0xA6, + 0x85, 0x02, 0xEF, 0x05, 0x20, 0xF0, 0x8A, 0x14, 0x4C, 0x26, 0xDC, 0x99, + 0xC5, 0x46, 0x51, 0xA5, 0x5A, 0x50, 0x42, 0x93, 0x4A, 0x0B, 0x4A, 0x90, + 0x30, 0x85, 0x2A, 0xD3, 0xEE, 0xD1, 0x24, 0x3D, 0xC1, 0x24, 0xA9, 0x36, + 0xFF, 0x00, 0xFF, 0xD2, 0xEB, 0xC1, 0x45, 0x08, 0x2D, 0x44, 0x0A, 0xD9, + 0x69, 0x84, 0x8D, 0x72, 0x98, 0x28, 0x60, 0x29, 0x84, 0xD4, 0x84, 0x8D, + 0x72, 0x20, 0x84, 0x10, 0x11, 0x18, 0x25, 0x02, 0xB8, 0x24, 0x01, 0x4C, + 0x04, 0x9A, 0xD4, 0xE4, 0x26, 0xAE, 0x0B, 0xA7, 0x27, 0x44, 0xC1, 0x22, + 0x42, 0x49, 0x50, 0x72, 0x62, 0x54, 0x49, 0x51, 0x94, 0x91, 0x6B, 0xA6, + 0x2E, 0x09, 0x89, 0x1E, 0x2A, 0x2E, 0xF1, 0x45, 0x16, 0xC5, 0xCE, 0x01, + 0x08, 0x9D, 0x53, 0xB8, 0xCA, 0x84, 0x90, 0x9C, 0x02, 0xD2, 0x52, 0x35, + 0x4F, 0x44, 0x30, 0x54, 0xA6, 0x50, 0x4B, 0x25, 0x28, 0x0A, 0x21, 0x3C, + 0xA4, 0xA6, 0x61, 0x31, 0x25, 0x46, 0x61, 0x29, 0x41, 0x36, 0xB9, 0x2A, + 0x30, 0x94, 0xA4, 0x25, 0x14, 0x28, 0x02, 0x91, 0x01, 0x3A, 0x62, 0x0A, + 0x4A, 0x59, 0x38, 0x6A, 0x60, 0x35, 0x44, 0x68, 0x09, 0x29, 0x6D, 0xA9, + 0x6D, 0x53, 0x51, 0x28, 0x25, 0x41, 0xA9, 0xD2, 0x09, 0xD2, 0x52, 0xC4, + 0x94, 0x83, 0x8A, 0x45, 0x45, 0x25, 0x32, 0x9D, 0x54, 0x1F, 0xC2, 0x74, + 0x8A, 0x48, 0x43, 0x09, 0xE1, 0x48, 0xC4, 0xA5, 0xCA, 0x2A, 0x58, 0x6A, + 0x91, 0x09, 0xE2, 0x13, 0x3C, 0xA4, 0xA6, 0x05, 0x46, 0x42, 0x91, 0x43, + 0x74, 0xA2, 0xB4, 0xAE, 0x4A, 0x76, 0xA8, 0x29, 0xB5, 0x25, 0x32, 0x94, + 0xC5, 0x3C, 0x26, 0x76, 0x89, 0x25, 0x64, 0xC6, 0x0A, 0x69, 0x4C, 0x8A, + 0x15, 0x09, 0xC0, 0x4A, 0x12, 0x49, 0x4B, 0x11, 0xD9, 0x41, 0xC2, 0x11, + 0x44, 0x26, 0x70, 0x05, 0x25, 0x22, 0x69, 0xD7, 0x55, 0x2D, 0xEA, 0x2E, + 0xE5, 0x40, 0xB9, 0x1A, 0x45, 0xB3, 0x2F, 0x25, 0x44, 0x95, 0x02, 0xE5, + 0x12, 0xE2, 0x95, 0x22, 0xD9, 0x94, 0x83, 0x90, 0xCB, 0xBB, 0x26, 0xDC, + 0x8D, 0x22, 0xD9, 0xB9, 0xDA, 0xE8, 0x90, 0x72, 0x82, 0x53, 0x08, 0xA2, + 0xD2, 0x87, 0x27, 0x94, 0x30, 0x54, 0x82, 0x09, 0xB4, 0x92, 0x92, 0x82, + 0x49, 0x2A, 0xDF, 0xFF, 0xD3, 0xEB, 0x9A, 0x11, 0x58, 0x14, 0x1B, 0x08, + 0xAD, 0x56, 0x8B, 0x4C, 0x32, 0x01, 0x4B, 0xB2, 0x68, 0x53, 0x6B, 0x74, + 0xD5, 0x05, 0xC1, 0x6D, 0x54, 0xD8, 0x60, 0xA8, 0xB8, 0x24, 0x01, 0x41, + 0x29, 0xDB, 0x62, 0x9E, 0xF4, 0x01, 0x3D, 0xD4, 0xC6, 0xA8, 0x52, 0x6D, + 0x26, 0xE0, 0x94, 0x94, 0x30, 0x42, 0x96, 0xE0, 0x38, 0x49, 0x36, 0xB2, + 0x62, 0x0A, 0x5B, 0xF5, 0x48, 0xBA, 0x42, 0x48, 0x60, 0x65, 0x44, 0xEE, + 0x53, 0x95, 0x12, 0x51, 0x42, 0x32, 0x13, 0x10, 0x93, 0x8A, 0x62, 0x74, + 0x45, 0x0A, 0x94, 0xA6, 0x14, 0x4B, 0x92, 0xDC, 0x92, 0x2D, 0x28, 0x72, + 0x79, 0x43, 0x69, 0x52, 0x05, 0x2A, 0x4D, 0xA4, 0x02, 0x53, 0xC2, 0x83, + 0x5C, 0xA7, 0xB9, 0x04, 0xAF, 0x01, 0x3C, 0x28, 0x87, 0x29, 0xC8, 0x84, + 0x14, 0xB7, 0x29, 0x14, 0xF2, 0xA2, 0x4A, 0x49, 0x5E, 0x13, 0xB5, 0x46, + 0x53, 0x82, 0x92, 0x19, 0x15, 0x12, 0xA5, 0xCA, 0x68, 0x49, 0x2A, 0x49, + 0x3A, 0x74, 0x94, 0xC0, 0xA6, 0x52, 0x72, 0x81, 0x45, 0x0B, 0x17, 0x28, + 0x97, 0x26, 0x71, 0x51, 0x25, 0x2A, 0x45, 0xB2, 0x05, 0x38, 0x50, 0x68, + 0x25, 0x48, 0x22, 0xA6, 0x52, 0x14, 0x0A, 0x91, 0x0A, 0x25, 0x25, 0x31, + 0x25, 0x47, 0x95, 0x3D, 0x23, 0x54, 0xD0, 0x3B, 0x24, 0x86, 0x30, 0xA6, + 0xC6, 0xA4, 0x14, 0x80, 0x29, 0x24, 0x32, 0xDA, 0x21, 0x41, 0xCD, 0x95, + 0x36, 0x92, 0x98, 0xF2, 0x82, 0x5A, 0xE5, 0xA4, 0x27, 0x1A, 0x1D, 0x51, + 0x8B, 0x74, 0x43, 0x2D, 0xD5, 0x1B, 0x5B, 0x4A, 0xD3, 0x94, 0xDA, 0x29, + 0x46, 0xD0, 0xA1, 0x29, 0x29, 0x72, 0x44, 0x28, 0x13, 0x09, 0x38, 0xA1, + 0x39, 0xC6, 0x51, 0x01, 0x04, 0xA9, 0xE4, 0x21, 0x94, 0x89, 0x51, 0x94, + 0xE5, 0xA4, 0xA8, 0xA8, 0x97, 0x29, 0x1E, 0x10, 0x89, 0x44, 0x21, 0x79, + 0x94, 0xE8, 0x61, 0xCA, 0x5B, 0x91, 0xA4, 0x32, 0x05, 0x29, 0x4C, 0x0A, + 0x48, 0x29, 0x9B, 0x51, 0x02, 0x1B, 0x25, 0x15, 0x02, 0x90, 0xAE, 0xC9, + 0x25, 0x26, 0x12, 0x49, 0x2F, 0xFF, 0xD4, 0xEC, 0x5B, 0x0A, 0x6C, 0xE5, + 0x04, 0x13, 0xCA, 0x2B, 0x78, 0x56, 0x8B, 0x4D, 0x30, 0x2A, 0x60, 0xA1, + 0x84, 0xED, 0x3E, 0x28, 0x2E, 0x64, 0xE3, 0x29, 0x4A, 0x62, 0x3C, 0x14, + 0x4B, 0x88, 0x49, 0x49, 0x03, 0x93, 0xEE, 0x84, 0x1D, 0xDA, 0xA4, 0x49, + 0x4A, 0x95, 0x69, 0x0B, 0xF5, 0x52, 0x6B, 0x87, 0x28, 0x0E, 0x72, 0x61, + 0x62, 0x54, 0xAB, 0x6C, 0xC8, 0x2A, 0x4D, 0x21, 0x00, 0x3D, 0x3E, 0xF8, + 0x08, 0x52, 0x6D, 0x33, 0xA3, 0x94, 0x17, 0xB8, 0x04, 0xCE, 0xB4, 0xA1, + 0x3D, 0xC4, 0xA2, 0x02, 0x09, 0x51, 0x7A, 0x62, 0xF4, 0x3D, 0xCA, 0x24, + 0xCA, 0x75, 0x2C, 0xB4, 0xBB, 0xA5, 0x38, 0x50, 0x68, 0x2A, 0x44, 0x14, + 0x92, 0xC8, 0x15, 0x30, 0x50, 0x81, 0x52, 0x05, 0x05, 0x24, 0x94, 0xE0, + 0xA8, 0x02, 0x9C, 0x14, 0x13, 0x6C, 0xC1, 0x44, 0x69, 0xD1, 0x09, 0x3C, + 0xA5, 0x49, 0xB4, 0xA4, 0x85, 0x12, 0xA2, 0x0A, 0x72, 0x50, 0x4A, 0xA5, + 0x38, 0x2A, 0x12, 0x90, 0x72, 0x34, 0x8B, 0x4C, 0x1C, 0x9B, 0x72, 0x80, + 0x29, 0xD0, 0x4D, 0xB3, 0x0E, 0x52, 0x94, 0x36, 0x85, 0x20, 0x50, 0x52, + 0xE5, 0x40, 0xA2, 0x26, 0x84, 0x94, 0x88, 0xD7, 0x29, 0x7A, 0x68, 0xD2, + 0x13, 0xE8, 0x8D, 0xAA, 0x90, 0x06, 0x42, 0x5A, 0x22, 0x90, 0x86, 0xF6, + 0xF8, 0x24, 0xA6, 0x24, 0xA8, 0x98, 0x2A, 0x2E, 0x4C, 0x1C, 0x42, 0x2B, + 0x6D, 0x93, 0xB8, 0x43, 0x2E, 0x85, 0x27, 0x19, 0x51, 0x21, 0x15, 0x15, + 0x35, 0xC4, 0x95, 0x66, 0xBE, 0x15, 0x76, 0x84, 0x7A, 0xA4, 0x20, 0x52, + 0x19, 0x96, 0xA8, 0x96, 0xED, 0xE5, 0x1B, 0x70, 0x08, 0x64, 0x97, 0x26, + 0xAE, 0x63, 0x1B, 0x93, 0x6D, 0x85, 0x30, 0x08, 0x3A, 0xA1, 0xD8, 0xE2, + 0x25, 0x14, 0x23, 0xB3, 0x94, 0x27, 0x10, 0x02, 0x91, 0x71, 0x28, 0x6F, + 0x4E, 0x0B, 0x09, 0x46, 0xF7, 0x6A, 0xA0, 0x49, 0xE5, 0x4C, 0x85, 0x10, + 0x13, 0x96, 0xB1, 0x25, 0x44, 0x8E, 0xE8, 0xA5, 0xA0, 0x26, 0x20, 0x94, + 0x95, 0x48, 0xA4, 0xA8, 0x22, 0x3B, 0x42, 0x96, 0xDD, 0xC3, 0x44, 0x50, + 0x84, 0xB6, 0x38, 0x4B, 0x58, 0x46, 0xD8, 0x78, 0x4F, 0xB5, 0xBB, 0x7C, + 0xD2, 0xB5, 0x52, 0x1D, 0x54, 0xD8, 0x27, 0x44, 0xFE, 0x9A, 0x9B, 0x18, + 0x67, 0x54, 0x89, 0x55, 0x2E, 0x1A, 0x42, 0x94, 0x29, 0xE8, 0x98, 0x93, + 0x28, 0x5A, 0xEA, 0x5F, 0x6E, 0x89, 0x26, 0x97, 0x24, 0x82, 0x9F, 0xFF, + 0xD5, 0xEC, 0x83, 0x77, 0x71, 0xA2, 0x9B, 0x1B, 0xA2, 0x61, 0xCC, 0x84, + 0x66, 0x01, 0xB5, 0x5A, 0x2D, 0x40, 0x11, 0xEA, 0x0A, 0x70, 0x7C, 0x14, + 0xDC, 0x04, 0x4F, 0x74, 0x2D, 0xC3, 0x80, 0x82, 0x97, 0xDE, 0x5B, 0xCA, + 0x8B, 0x9C, 0x49, 0xF2, 0x4F, 0xA1, 0xE5, 0x41, 0xCE, 0x00, 0xA2, 0x86, + 0x6D, 0xD7, 0x95, 0x28, 0x50, 0x04, 0x15, 0x30, 0x44, 0x24, 0x96, 0x2F, + 0x28, 0x45, 0xD0, 0xA4, 0xF7, 0x4A, 0x0B, 0x89, 0x44, 0x2D, 0x25, 0x2B, + 0x6C, 0x4E, 0x6D, 0x0A, 0xB6, 0xE2, 0x13, 0x7A, 0x84, 0x23, 0x48, 0xE2, + 0x6C, 0x17, 0x26, 0xDE, 0xAB, 0x9B, 0x49, 0xE5, 0x36, 0xE4, 0xA9, 0x5C, + 0x49, 0xCB, 0x82, 0x8C, 0xEA, 0xA1, 0xB9, 0x38, 0x49, 0x16, 0x9D, 0x8E, + 0x08, 0x9B, 0x82, 0xAE, 0x14, 0xDB, 0x28, 0x10, 0xB8, 0x14, 0x9A, 0x14, + 0x81, 0x50, 0xD5, 0x3C, 0xA4, 0xA6, 0x60, 0xA7, 0x94, 0x39, 0x4E, 0x0A, + 0x49, 0x49, 0xB9, 0x20, 0x50, 0xE5, 0x48, 0x14, 0x14, 0x94, 0x25, 0xAA, + 0x80, 0x2A, 0x5B, 0x90, 0x4A, 0xC4, 0x14, 0xED, 0x6F, 0x9A, 0x53, 0x29, + 0xC2, 0x4A, 0x64, 0x02, 0x9C, 0x28, 0x29, 0x83, 0x28, 0x25, 0x74, 0xDB, + 0xA1, 0x2D, 0x53, 0x77, 0x49, 0x2C, 0xC3, 0x92, 0x2E, 0x51, 0xDD, 0xA2, + 0x89, 0x72, 0x54, 0xAB, 0x48, 0x35, 0x2A, 0x53, 0x08, 0x2D, 0x72, 0x20, + 0x74, 0xA4, 0xA5, 0xCE, 0xA1, 0x40, 0x95, 0x30, 0x54, 0x1F, 0x11, 0x28, + 0x05, 0x23, 0x70, 0xEE, 0x86, 0x42, 0x99, 0x2A, 0x33, 0x29, 0xCB, 0x4B, + 0x18, 0x29, 0x44, 0x15, 0x38, 0x4C, 0xE6, 0xA2, 0xA5, 0x08, 0x44, 0x6B, + 0xC0, 0x90, 0x85, 0x09, 0x4C, 0x21, 0x4A, 0x05, 0x36, 0xE0, 0x88, 0x1C, + 0x15, 0x5D, 0xC5, 0x4D, 0xA5, 0x22, 0x12, 0x0A, 0x77, 0x38, 0x76, 0x41, + 0xB2, 0x0F, 0x74, 0xC5, 0xE5, 0x0C, 0xBB, 0x54, 0x80, 0x51, 0x2B, 0x3B, + 0xCB, 0xEF, 0x41, 0x79, 0x32, 0x8A, 0x78, 0x41, 0x70, 0x32, 0x9C, 0x16, + 0x16, 0x32, 0x65, 0x48, 0xC9, 0x09, 0x35, 0xA8, 0x81, 0xBE, 0x48, 0xA8, + 0x23, 0x0D, 0x31, 0xAA, 0x5B, 0x62, 0x04, 0x23, 0x86, 0xA7, 0x2D, 0x1F, + 0x34, 0x2D, 0x34, 0xD6, 0x75, 0x40, 0xF6, 0x4E, 0xDA, 0xA0, 0xEA, 0x8A, + 0xEE, 0x53, 0xB4, 0x18, 0x94, 0xAD, 0x14, 0xC0, 0xD6, 0x99, 0xD5, 0x77, + 0x89, 0x47, 0x00, 0x42, 0x72, 0xD3, 0xC1, 0x28, 0x5A, 0x69, 0xAB, 0xB3, + 0x58, 0x4E, 0x18, 0x40, 0x56, 0x3D, 0x30, 0x9B, 0x66, 0x88, 0xDA, 0xA9, + 0x0E, 0x91, 0xE6, 0xA5, 0x09, 0xCB, 0x61, 0x3B, 0x48, 0x8D, 0x52, 0x53, + 0x18, 0xD2, 0x23, 0x54, 0x94, 0xF4, 0x49, 0x05, 0x3F, 0xFF, 0xD6, 0xED, + 0x41, 0x0A, 0x5B, 0xFC, 0x10, 0x37, 0x6B, 0xE6, 0x9C, 0x38, 0xC2, 0xB5, + 0x4D, 0x3B, 0x66, 0xF7, 0x94, 0x30, 0xED, 0x53, 0xB9, 0xC2, 0x10, 0xC1, + 0xD5, 0x10, 0x10, 0x4B, 0x32, 0x4A, 0x89, 0x24, 0xA7, 0x26, 0x54, 0x51, + 0x43, 0x36, 0xBA, 0x13, 0x97, 0x21, 0x02, 0x9E, 0x52, 0xA5, 0x5A, 0xEE, + 0x72, 0x19, 0x76, 0xA9, 0xC9, 0x43, 0x29, 0x20, 0x95, 0x12, 0xA2, 0x4A, + 0x92, 0x68, 0x45, 0x0C, 0x21, 0x48, 0x04, 0xF0, 0xA4, 0x20, 0x23, 0x6A, + 0x5C, 0x30, 0xA9, 0x06, 0xA4, 0x1C, 0x53, 0xEE, 0x09, 0xA9, 0x64, 0xD1, + 0xE2, 0xA6, 0x21, 0x08, 0xBD, 0x20, 0xE8, 0x49, 0x36, 0x9A, 0x25, 0x34, + 0x28, 0x0B, 0x0F, 0x75, 0x20, 0xF0, 0x82, 0x6D, 0x96, 0xD2, 0x42, 0x68, + 0x52, 0x0F, 0x85, 0x12, 0xE0, 0x52, 0x52, 0x93, 0xA8, 0xCA, 0x79, 0x49, + 0x4C, 0x81, 0x4F, 0x2A, 0x20, 0xA7, 0x00, 0xA0, 0x96, 0x60, 0x92, 0xA4, + 0x09, 0x0A, 0x21, 0x4B, 0x44, 0x92, 0xC8, 0x12, 0x54, 0x9A, 0xE8, 0x43, + 0x05, 0x22, 0xE0, 0x82, 0xAD, 0x3C, 0xC8, 0x51, 0x3A, 0xA1, 0x8B, 0x14, + 0xB7, 0x21, 0x49, 0xB5, 0xCE, 0x8A, 0x24, 0x84, 0xC6, 0xC0, 0xA0, 0xE7, + 0xA3, 0x48, 0x25, 0x90, 0x76, 0xAA, 0x61, 0xD0, 0x83, 0x23, 0x94, 0xC5, + 0xE8, 0xD2, 0xAD, 0x39, 0x79, 0x50, 0x2E, 0x28, 0x7B, 0xCA, 0x40, 0x99, + 0xD5, 0x2A, 0x55, 0xB2, 0x2E, 0x4D, 0xF0, 0x48, 0x90, 0x9A, 0x42, 0x48, + 0x66, 0x09, 0x52, 0xEC, 0x86, 0x0A, 0x23, 0x50, 0x48, 0x58, 0xC4, 0x28, + 0xC2, 0x24, 0x04, 0xC9, 0x29, 0x88, 0x6E, 0xAA, 0x70, 0x98, 0x1D, 0x54, + 0xB4, 0x49, 0x41, 0x19, 0x25, 0x0E, 0x61, 0x15, 0xC1, 0x08, 0x84, 0x42, + 0x0A, 0xC5, 0xCA, 0x1A, 0x12, 0x9D, 0xC0, 0xA7, 0x6B, 0x51, 0x43, 0x26, + 0x80, 0x88, 0x1A, 0x14, 0x40, 0x52, 0xEC, 0x82, 0x42, 0xC7, 0x45, 0x1D, + 0xC9, 0xDC, 0x86, 0x52, 0x52, 0xE5, 0xC9, 0xDA, 0x44, 0xA8, 0x04, 0xFC, + 0x22, 0x84, 0xC2, 0x09, 0xF2, 0x53, 0x20, 0x46, 0x88, 0x2D, 0x28, 0x93, + 0xA1, 0x09, 0xAB, 0x97, 0x01, 0x3A, 0x8E, 0xE0, 0x02, 0x6D, 0xE9, 0x2A, + 0xD4, 0xE6, 0xA8, 0x6D, 0xD7, 0xC2, 0x14, 0x8D, 0x8A, 0x25, 0xC1, 0x14, + 0x2A, 0x04, 0xCA, 0x4A, 0x3B, 0xF5, 0x84, 0x92, 0x55, 0xBF, 0xFF, 0xD7, + 0xEB, 0xA6, 0x12, 0xDE, 0xA0, 0x5C, 0xA2, 0x4A, 0xB9, 0x4D, 0x1B, 0x66, + 0x4A, 0x8C, 0xA8, 0xEE, 0x4D, 0x24, 0xA3, 0x48, 0xB4, 0x9B, 0x92, 0x25, + 0x40, 0x12, 0x9E, 0x52, 0x53, 0x29, 0x4D, 0x29, 0xA5, 0x29, 0x49, 0x4A, + 0x29, 0x92, 0x29, 0x24, 0xA5, 0x24, 0x92, 0x49, 0x29, 0x49, 0x92, 0x29, + 0x24, 0xA5, 0xD3, 0xA8, 0xA7, 0x49, 0x4B, 0xA4, 0x9A, 0x52, 0x94, 0x94, + 0xBC, 0xA9, 0x07, 0x21, 0x92, 0x90, 0x25, 0x25, 0x5A, 0x69, 0x4E, 0x0A, + 0x10, 0x25, 0x4E, 0x50, 0x4D, 0xB3, 0x05, 0x29, 0x50, 0x94, 0x89, 0x49, + 0x56, 0xCF, 0x74, 0x29, 0x36, 0xC4, 0x09, 0x4E, 0x0A, 0x54, 0xAB, 0x6C, + 0x6F, 0x94, 0xB7, 0x94, 0x10, 0xE5, 0x20, 0x50, 0xA4, 0xDA, 0x5D, 0xE5, + 0x34, 0xA8, 0xCA, 0x79, 0x49, 0x4B, 0x82, 0x65, 0x4A, 0x4A, 0x80, 0x52, + 0x41, 0x2B, 0x19, 0x4C, 0xA4, 0x98, 0xA4, 0xA6, 0x29, 0xC2, 0x49, 0xA1, + 0x14, 0x32, 0x81, 0xE2, 0x91, 0xF0, 0x51, 0x4A, 0x12, 0x52, 0xE9, 0x00, + 0x52, 0x01, 0x4C, 0x04, 0x92, 0xA0, 0xA6, 0x14, 0x42, 0x70, 0x82, 0x99, + 0xA6, 0x4C, 0x9D, 0x04, 0xA8, 0x27, 0x4C, 0xA4, 0x12, 0x53, 0x12, 0x0A, + 0x81, 0x08, 0xA6, 0x21, 0x40, 0xA4, 0xA4, 0x4E, 0x09, 0x82, 0x99, 0x0A, + 0x31, 0xAA, 0x28, 0x66, 0x08, 0x48, 0x90, 0x54, 0x7B, 0x68, 0xA2, 0x4E, + 0xA9, 0x29, 0x99, 0x28, 0x66, 0x0F, 0x09, 0xF5, 0x29, 0x35, 0xBA, 0xA2, + 0xA6, 0x20, 0x42, 0x90, 0x6E, 0xA9, 0xF6, 0xA7, 0x02, 0x35, 0x49, 0x4B, + 0xB4, 0x01, 0xAA, 0x45, 0xDA, 0x28, 0x92, 0x12, 0x88, 0x12, 0x82, 0x94, + 0x5C, 0x90, 0x04, 0xEA, 0x99, 0x3E, 0xE8, 0x45, 0x4B, 0x3B, 0x45, 0x02, + 0x54, 0xDC, 0x54, 0x1D, 0xC2, 0x48, 0x2C, 0x65, 0x25, 0x19, 0x49, 0x14, + 0x5B, 0xFF, 0xD0, 0xEA, 0x25, 0x31, 0x94, 0xE9, 0x95, 0xD7, 0x3D, 0x88, + 0x52, 0x10, 0x98, 0x27, 0x49, 0x4B, 0xA6, 0x25, 0x24, 0x88, 0x49, 0x4A, + 0x94, 0xE0, 0xA8, 0xC2, 0x74, 0x94, 0xB9, 0x4C, 0x9D, 0x34, 0xA4, 0x95, + 0x24, 0x94, 0xA4, 0x92, 0x16, 0x4E, 0xA4, 0x04, 0xA4, 0x52, 0x4A, 0xC0, + 0x24, 0x42, 0x70, 0x91, 0x49, 0x4C, 0x13, 0x4A, 0x72, 0x99, 0x14, 0x29, + 0x48, 0x28, 0x25, 0x30, 0x92, 0x92, 0x82, 0x12, 0x94, 0x30, 0x52, 0xDC, + 0x85, 0x2A, 0xD2, 0x4A, 0x52, 0xA0, 0x0A, 0x98, 0x29, 0x25, 0x6D, 0x53, + 0xC2, 0x49, 0x24, 0xA6, 0x41, 0x48, 0x28, 0x05, 0x29, 0x41, 0x21, 0x98, + 0x2A, 0x41, 0x0E, 0x54, 0x81, 0x41, 0x2C, 0xD3, 0xCA, 0x8C, 0xA7, 0x49, + 0x4B, 0xA4, 0x53, 0x4A, 0x52, 0x82, 0x54, 0x92, 0x49, 0x22, 0xA5, 0x27, + 0x01, 0x24, 0xE1, 0x05, 0x2E, 0x02, 0x78, 0x48, 0x27, 0x09, 0x29, 0x50, + 0x9E, 0x12, 0x4E, 0x82, 0x54, 0x92, 0x74, 0x92, 0x52, 0xC9, 0xD3, 0x24, + 0x92, 0x97, 0x51, 0x21, 0x3A, 0x62, 0x92, 0x96, 0x2D, 0x4C, 0x42, 0x94, + 0x26, 0x45, 0x08, 0xCC, 0x84, 0xA1, 0x4C, 0xA8, 0xC2, 0x4A, 0x58, 0x18, + 0x4E, 0x94, 0x27, 0x84, 0x94, 0xAE, 0x12, 0x24, 0x26, 0x29, 0x8A, 0x4A, + 0x62, 0x60, 0x14, 0xE5, 0xC9, 0x26, 0x28, 0xA1, 0x69, 0x48, 0x19, 0x49, + 0x34, 0xA4, 0xA5, 0x38, 0xE8, 0xA1, 0x29, 0x12, 0xA0, 0x4E, 0xA8, 0x80, + 0x82, 0x57, 0x9D, 0x52, 0x51, 0x92, 0x92, 0x28, 0x7F, 0xFF, 0xD1, 0xEA, + 0x08, 0x48, 0x05, 0x38, 0x4D, 0x0A, 0xEB, 0x41, 0x84, 0x27, 0x85, 0x28, + 0x4E, 0x18, 0x52, 0x55, 0x31, 0x00, 0x24, 0x42, 0x28, 0x68, 0xF9, 0xA6, + 0x2D, 0x42, 0xD5, 0x48, 0xB6, 0x9F, 0x04, 0xE1, 0x88, 0xA1, 0xA9, 0xF6, + 0xA5, 0x69, 0xA4, 0x5B, 0x02, 0x62, 0xC2, 0x8C, 0x1B, 0xAA, 0x7D, 0xA0, + 0xA5, 0x6A, 0xA4, 0x02, 0xA9, 0x52, 0xF4, 0xA3, 0x94, 0x70, 0x00, 0x51, + 0x72, 0x56, 0xAA, 0x47, 0x11, 0xC2, 0x81, 0xD5, 0x49, 0xCA, 0x29, 0x20, + 0xA8, 0x24, 0x52, 0x48, 0xA2, 0xA6, 0x05, 0x32, 0x9C, 0x14, 0xC4, 0x24, + 0x86, 0x2A, 0x30, 0xA7, 0x09, 0x8A, 0x2A, 0x63, 0xC2, 0x61, 0xCA, 0x91, + 0x09, 0xC3, 0x52, 0x52, 0xE1, 0x38, 0x48, 0x35, 0x39, 0x08, 0x29, 0x70, + 0x52, 0x4D, 0x05, 0x2D, 0x52, 0x4A, 0xEA, 0x41, 0x41, 0x38, 0x29, 0x29, + 0x9A, 0x90, 0x51, 0x69, 0x52, 0x08, 0x25, 0x90, 0x49, 0x32, 0x79, 0x41, + 0x2B, 0xA7, 0x4C, 0x0A, 0x79, 0x49, 0x4A, 0x84, 0xA1, 0x34, 0xA7, 0x05, + 0x24, 0xAE, 0x14, 0x80, 0x4D, 0x29, 0xC2, 0x0A, 0x66, 0x02, 0x50, 0xA4, + 0xD4, 0xEE, 0x08, 0x25, 0x82, 0x70, 0x53, 0x24, 0x92, 0x99, 0x24, 0x99, + 0x24, 0x94, 0xA4, 0xC9, 0x14, 0xC9, 0x29, 0x74, 0x93, 0x05, 0x24, 0x94, + 0xB1, 0x4C, 0x9D, 0x32, 0x2A, 0x59, 0x28, 0x49, 0x24, 0x90, 0xA4, 0xC9, + 0xD2, 0x49, 0x4B, 0x15, 0x12, 0x14, 0x93, 0x10, 0x92, 0x98, 0x26, 0x2A, + 0x64, 0x26, 0x84, 0x54, 0xC5, 0x44, 0xA9, 0xC2, 0x89, 0x09, 0x21, 0x19, + 0x51, 0x21, 0x13, 0x6A, 0x68, 0x45, 0x14, 0xC2, 0x12, 0x52, 0x84, 0x91, + 0x45, 0x3F, 0xFF, 0xD2, 0xEB, 0xA1, 0x48, 0x34, 0x42, 0x50, 0x9C, 0x2B, + 0x76, 0xD2, 0x63, 0x0D, 0x09, 0xE5, 0x22, 0xD4, 0x82, 0x4A, 0x50, 0x09, + 0x42, 0x98, 0x88, 0x48, 0x42, 0x56, 0xAA, 0x62, 0x02, 0x44, 0x29, 0x81, + 0x29, 0xF6, 0xA1, 0x69, 0xA4, 0x62, 0x52, 0x06, 0x14, 0xA0, 0x28, 0x91, + 0x1C, 0x24, 0xA5, 0x12, 0xA2, 0x52, 0x24, 0xCA, 0x70, 0x09, 0x45, 0x0C, + 0x1C, 0x02, 0x81, 0x08, 0xA5, 0xA9, 0x06, 0x23, 0x6A, 0xA4, 0x30, 0x53, + 0xC2, 0x2C, 0x05, 0x03, 0x09, 0x5A, 0x29, 0x8C, 0x26, 0x21, 0x49, 0x24, + 0x94, 0xC6, 0x13, 0x6D, 0x53, 0x49, 0x2B, 0x45, 0x30, 0xDA, 0x9C, 0x05, + 0x28, 0x4A, 0x12, 0x55, 0x2D, 0x09, 0xE1, 0x20, 0x92, 0x49, 0x58, 0x84, + 0xCA, 0x44, 0x28, 0x90, 0x92, 0x16, 0x4E, 0xA2, 0x9C, 0x04, 0x54, 0xC8, + 0x15, 0x2D, 0xCA, 0x21, 0x24, 0x12, 0xCB, 0x72, 0x6D, 0xC9, 0x8A, 0x52, + 0x92, 0x19, 0x87, 0x29, 0x02, 0x86, 0x0A, 0x90, 0x29, 0x25, 0x94, 0xA9, + 0x05, 0x10, 0x92, 0x09, 0x66, 0x9C, 0x39, 0x40, 0x14, 0xE0, 0xC9, 0x49, + 0x49, 0xD8, 0xE5, 0x32, 0x84, 0xD4, 0x40, 0x0A, 0x69, 0x5C, 0x16, 0x29, + 0x24, 0x44, 0x24, 0x92, 0x94, 0x92, 0x50, 0x92, 0x49, 0x59, 0x34, 0x29, + 0x42, 0x64, 0x90, 0xA8, 0x49, 0x24, 0x92, 0x52, 0x93, 0x27, 0x49, 0x25, + 0x2D, 0x09, 0x27, 0x49, 0x25, 0x28, 0x09, 0x52, 0xD8, 0x9D, 0x81, 0x11, + 0x0B, 0x48, 0x08, 0xBD, 0x35, 0x12, 0xC4, 0x78, 0x43, 0x72, 0x56, 0xAA, + 0x42, 0x5A, 0x98, 0x84, 0x42, 0x14, 0x0A, 0x28, 0xA6, 0x24, 0x28, 0x90, + 0xA6, 0x98, 0x84, 0x50, 0xC0, 0x85, 0x12, 0x11, 0x21, 0x34, 0x22, 0x86, + 0x10, 0x92, 0x9C, 0x24, 0x92, 0xA9, 0xFF, 0xD3, 0xEC, 0x53, 0xB5, 0x25, + 0x20, 0xD5, 0x69, 0xA6, 0xA8, 0x05, 0x2D, 0x89, 0xE2, 0x12, 0x98, 0x49, + 0x2B, 0x6D, 0x84, 0xB4, 0x4A, 0x74, 0x4C, 0x4C, 0x24, 0xA6, 0x40, 0xA4, + 0x4A, 0x81, 0x72, 0x6D, 0xC9, 0x29, 0x92, 0x64, 0xD2, 0x9C, 0x10, 0x92, + 0x15, 0x09, 0x27, 0x91, 0xD9, 0x31, 0x29, 0x29, 0x7D, 0xAA, 0x26, 0x42, + 0x45, 0xE4, 0x28, 0x97, 0x92, 0x92, 0x94, 0x75, 0x51, 0x20, 0x26, 0x25, + 0x34, 0x94, 0x50, 0xBA, 0x62, 0x94, 0x14, 0xA1, 0x14, 0x2C, 0xA4, 0x13, + 0x42, 0x70, 0x12, 0x52, 0xE0, 0x25, 0x09, 0x27, 0x41, 0x2B, 0x42, 0x65, + 0x24, 0xA1, 0x25, 0x30, 0x84, 0x8B, 0x54, 0xE1, 0x32, 0x2A, 0xA6, 0x01, + 0x8A, 0x41, 0x8A, 0x41, 0x29, 0x4A, 0xD5, 0x4C, 0x4B, 0x53, 0x42, 0x91, + 0x4D, 0xB9, 0x24, 0x31, 0x21, 0x32, 0x44, 0xA6, 0x94, 0x90, 0xC8, 0x29, + 0x88, 0x50, 0x12, 0x78, 0x52, 0x00, 0xA4, 0x90, 0xCA, 0x53, 0xCA, 0x4D, + 0x68, 0x8D, 0x54, 0x83, 0x42, 0x09, 0xA5, 0x80, 0x44, 0x63, 0x0A, 0x76, + 0x88, 0x44, 0x05, 0x02, 0x52, 0x02, 0x80, 0x4F, 0x2A, 0x25, 0xCA, 0x32, + 0x82, 0x52, 0x13, 0x29, 0x94, 0x41, 0x4F, 0x29, 0x25, 0x74, 0x92, 0x4D, + 0x29, 0x29, 0x72, 0x99, 0x32, 0x49, 0x21, 0x49, 0x4A, 0x69, 0x4A, 0x52, + 0x52, 0xE9, 0x15, 0x20, 0x13, 0xC0, 0x49, 0x54, 0xC1, 0x48, 0x04, 0xF0, + 0x9F, 0x69, 0xEC, 0x92, 0x69, 0x41, 0x4D, 0xA9, 0x9A, 0x13, 0x80, 0x82, + 0x57, 0x3C, 0x28, 0x10, 0x89, 0x09, 0x89, 0x08, 0x29, 0x11, 0x6A, 0x19, + 0x84, 0x67, 0x4A, 0x11, 0x09, 0xC1, 0x05, 0x82, 0x62, 0xA5, 0xB4, 0xA8, + 0xC2, 0x28, 0x59, 0x24, 0xF0, 0x94, 0x14, 0x90, 0xB2, 0x49, 0xE1, 0x24, + 0x94, 0xFF, 0x00, 0xFF, 0xD4, 0xEC, 0xD3, 0xEE, 0x51, 0x71, 0x0A, 0x3B, + 0x95, 0xA6, 0xA2, 0x49, 0x51, 0x73, 0x94, 0x65, 0x34, 0xA5, 0x48, 0xB6, + 0x5B, 0x93, 0xB8, 0xCA, 0x1C, 0xA9, 0x07, 0x24, 0xA5, 0x42, 0x49, 0x12, + 0x9B, 0x59, 0x45, 0x4B, 0xEA, 0x92, 0x53, 0x1A, 0x26, 0x28, 0x29, 0x94, + 0xA6, 0x2A, 0x29, 0x4A, 0x4A, 0x51, 0x4C, 0x91, 0x29, 0xA5, 0x14, 0x29, + 0x20, 0x0A, 0x49, 0xE4, 0x22, 0xA5, 0x04, 0xE9, 0x93, 0xA0, 0xA5, 0x42, + 0x96, 0xD4, 0x82, 0x70, 0x82, 0x56, 0xDA, 0x96, 0xD5, 0x24, 0xF0, 0x52, + 0x55, 0x30, 0x84, 0xB6, 0xA2, 0x06, 0x94, 0xCE, 0x05, 0x2B, 0x55, 0x30, + 0xDA, 0x13, 0x42, 0x72, 0x9A, 0x52, 0x52, 0xD0, 0x98, 0x94, 0xE9, 0x88, + 0x45, 0x0C, 0x4B, 0x82, 0x81, 0x72, 0x99, 0x62, 0x60, 0xCD, 0x75, 0x45, + 0x06, 0xD8, 0x4C, 0xF0, 0x90, 0x61, 0x94, 0x5D, 0x80, 0x26, 0xD1, 0x2B, + 0x45, 0x2D, 0x04, 0x27, 0xDC, 0x53, 0x81, 0x29, 0xF6, 0xC2, 0x49, 0x53, + 0x49, 0xEE, 0x8A, 0xD5, 0x06, 0x80, 0xA7, 0xB8, 0x04, 0x0A, 0x43, 0x31, + 0x2A, 0x4A, 0x2D, 0x7A, 0x94, 0xCA, 0x6A, 0xE5, 0x1F, 0x35, 0x02, 0x61, + 0x3B, 0x8F, 0x9A, 0x13, 0x88, 0x05, 0x10, 0x82, 0xCF, 0x72, 0x90, 0x72, + 0x00, 0x2A, 0x61, 0xC9, 0x52, 0xAD, 0x34, 0xA5, 0x28, 0x60, 0xA7, 0x94, + 0x12, 0xCA, 0x52, 0x94, 0xC9, 0xD2, 0x52, 0xD0, 0x9C, 0x24, 0x92, 0x4A, + 0x5C, 0x15, 0x31, 0x08, 0x63, 0x94, 0x49, 0xD1, 0x24, 0x86, 0x41, 0x4D, + 0xA0, 0x20, 0xEE, 0x0A, 0x4D, 0x72, 0x09, 0x09, 0x0F, 0x29, 0x4A, 0x8C, + 0xCA, 0x79, 0x41, 0x4B, 0x92, 0x86, 0x65, 0x48, 0x94, 0xDA, 0x22, 0xA6, + 0x24, 0xA6, 0x2A, 0x44, 0x05, 0x14, 0x90, 0xB2, 0x8E, 0xC9, 0x4E, 0x4C, + 0x26, 0x0F, 0x45, 0x4A, 0xDA, 0x02, 0x73, 0x09, 0xC1, 0x4C, 0x52, 0x53, + 0x0E, 0xFC, 0x24, 0x9E, 0x44, 0xA4, 0x92, 0x1F, 0xFF, 0xD5, 0xEB, 0x8B, + 0xB5, 0x4C, 0x5C, 0x10, 0x4D, 0x89, 0x7A, 0x81, 0x5C, 0xA6, 0x8D, 0xA6, + 0xDC, 0x13, 0x6E, 0x41, 0xDE, 0x13, 0x87, 0x25, 0x4A, 0xB4, 0xB3, 0x29, + 0x03, 0xAA, 0x80, 0x72, 0x94, 0x94, 0x93, 0x69, 0x01, 0x95, 0x30, 0xA0, + 0xD9, 0x44, 0x03, 0x54, 0xD2, 0x95, 0xB6, 0x84, 0xE5, 0xA2, 0x14, 0xC3, + 0x53, 0x38, 0x14, 0x93, 0x48, 0x88, 0x51, 0x32, 0xA6, 0xE2, 0xA1, 0x25, + 0x14, 0x2D, 0x09, 0xA1, 0x49, 0x24, 0x90, 0xC6, 0x12, 0x52, 0x84, 0xD0, + 0x92, 0x94, 0x92, 0x49, 0xD2, 0x52, 0xE1, 0x38, 0x4C, 0x14, 0xE1, 0x04, + 0xAC, 0xA4, 0x1C, 0xA3, 0x09, 0x88, 0x29, 0x29, 0x30, 0x72, 0x89, 0xD5, + 0x40, 0x27, 0xDC, 0x61, 0x2A, 0x4D, 0xAC, 0xE0, 0x50, 0xD4, 0x89, 0x51, + 0x21, 0x14, 0x15, 0x12, 0x98, 0x3B, 0x54, 0x93, 0x42, 0x48, 0x65, 0x25, + 0x29, 0x0A, 0x24, 0x25, 0x09, 0x29, 0x96, 0xE0, 0xA2, 0x4C, 0xA6, 0x84, + 0xA1, 0x14, 0x32, 0x07, 0x44, 0x89, 0x50, 0xE1, 0x22, 0xE2, 0x92, 0xAD, + 0x9B, 0x5C, 0x9C, 0xBA, 0x50, 0xC3, 0x92, 0x2F, 0x09, 0x2A, 0xD2, 0x6E, + 0x29, 0x7A, 0x87, 0xC5, 0x0B, 0x7A, 0x60, 0x65, 0x2A, 0x55, 0xA4, 0x2F, + 0x25, 0x29, 0x51, 0x0A, 0x40, 0x68, 0x92, 0x99, 0x02, 0xA6, 0x10, 0x82, + 0x90, 0x28, 0x25, 0x28, 0x29, 0x4A, 0x80, 0x29, 0xC3, 0x90, 0x4B, 0x39, + 0x4F, 0x2A, 0x12, 0x94, 0xA4, 0xA6, 0x72, 0x9E, 0x50, 0xE5, 0x36, 0xE4, + 0x95, 0x69, 0x66, 0x12, 0x0F, 0x42, 0xDC, 0x53, 0x17, 0x25, 0x4A, 0xB4, + 0xA5, 0xFA, 0xA7, 0x0F, 0xF1, 0x55, 0xF7, 0xC1, 0x4B, 0xD5, 0x08, 0xD2, + 0xAD, 0xB4, 0x1E, 0xA5, 0xBD, 0x55, 0x6D, 0xA1, 0x4C, 0x58, 0x10, 0xA4, + 0xF1, 0x36, 0x37, 0x26, 0xDC, 0x84, 0x2C, 0x52, 0x2E, 0x04, 0x21, 0x49, + 0xB5, 0xDC, 0xF8, 0x4D, 0xBE, 0x50, 0xDC, 0xE2, 0x9A, 0x7C, 0x51, 0xA4, + 0x5B, 0x32, 0x65, 0x32, 0x81, 0x78, 0x0A, 0x2E, 0xB2, 0x78, 0x4A, 0x91, + 0x69, 0xC3, 0x82, 0x77, 0x3F, 0x45, 0x5C, 0x3D, 0x27, 0x5B, 0x09, 0x52, + 0xAD, 0x9E, 0xED, 0x52, 0x55, 0xFD, 0x5D, 0x52, 0x46, 0x91, 0xC4, 0xFF, + 0x00, 0xFF, 0xD6, 0xE8, 0x35, 0x52, 0x82, 0xA4, 0x1A, 0xA5, 0xB6, 0x15, + 0xDB, 0x73, 0xE9, 0x88, 0x0A, 0x40, 0x29, 0x00, 0x13, 0xC0, 0x4A, 0xD3, + 0x4B, 0x00, 0xA6, 0xD0, 0x98, 0x27, 0x0E, 0x01, 0x04, 0xA7, 0xAC, 0x0E, + 0xE8, 0x82, 0x15, 0x60, 0xF8, 0x4B, 0xD5, 0x84, 0xDA, 0x5D, 0x6D, 0xA2, + 0xE0, 0xA2, 0xE7, 0x20, 0x8B, 0x27, 0x95, 0x17, 0x58, 0x95, 0x26, 0xD9, + 0xB9, 0xC1, 0x46, 0x50, 0xF7, 0xEA, 0x9C, 0x39, 0x1A, 0x5B, 0x6C, 0xD2, + 0x4D, 0x29, 0xC2, 0x49, 0x5E, 0x13, 0x27, 0x4C, 0x92, 0x96, 0x4E, 0x92, + 0x50, 0x92, 0x17, 0x05, 0x48, 0x15, 0x10, 0x9C, 0x20, 0x96, 0x69, 0x68, + 0xA3, 0x29, 0xD2, 0x4A, 0x88, 0x4C, 0x94, 0xA6, 0x2E, 0x49, 0x0A, 0x85, + 0x12, 0x96, 0xE4, 0xB7, 0x22, 0xA5, 0x42, 0x50, 0x94, 0xA4, 0x92, 0x95, + 0x0A, 0x3A, 0x27, 0x24, 0x26, 0x90, 0x92, 0x14, 0x91, 0x4B, 0x44, 0x8C, + 0x24, 0xA6, 0x25, 0x0D, 0xC7, 0xC1, 0x4D, 0xCA, 0x05, 0x15, 0xA5, 0x69, + 0x4A, 0x0A, 0x62, 0x9C, 0x14, 0x54, 0xBC, 0x14, 0x80, 0x84, 0xF3, 0x09, + 0x72, 0x92, 0x95, 0x29, 0xF7, 0x15, 0x12, 0x13, 0x80, 0x92, 0x99, 0x02, + 0xA4, 0x10, 0xD4, 0x9A, 0x82, 0x43, 0x30, 0x53, 0x82, 0xA2, 0x9A, 0x52, + 0x4A, 0x49, 0x4C, 0x1C, 0xA3, 0x29, 0xA6, 0x10, 0x55, 0xA5, 0x2A, 0x24, + 0xA8, 0xEE, 0x4B, 0x70, 0x49, 0x4B, 0xEE, 0x51, 0x2E, 0x29, 0x12, 0x14, + 0x09, 0x45, 0x05, 0x72, 0xE5, 0x12, 0x53, 0xCA, 0x6E, 0xE9, 0x21, 0x40, + 0x90, 0x54, 0xDA, 0xE2, 0xA3, 0xB7, 0x45, 0x26, 0x88, 0x49, 0x4C, 0xC3, + 0x93, 0xEF, 0x3E, 0x28, 0x4E, 0x74, 0x28, 0x1B, 0x09, 0xE1, 0x2A, 0x4D, + 0xB6, 0x0B, 0xC7, 0x8A, 0x6D, 0xE3, 0xB2, 0xAF, 0x2E, 0x3C, 0xA9, 0x00, + 0x52, 0xA4, 0x5A, 0x57, 0x3A, 0x54, 0x0A, 0x61, 0x29, 0x14, 0x94, 0xAD, + 0xCA, 0x0E, 0x24, 0xA7, 0x2A, 0x3A, 0xA2, 0x86, 0x3E, 0xE9, 0x49, 0x4D, + 0x24, 0x6D, 0x54, 0xFF, 0x00, 0xFF, 0xD7, 0xEA, 0x76, 0x27, 0xD8, 0x8A, + 0x18, 0xA5, 0xB3, 0x45, 0x6E, 0xDA, 0x54, 0x80, 0x35, 0x3C, 0x22, 0x6C, + 0x4B, 0x6A, 0x56, 0xAA, 0x44, 0x42, 0x81, 0x06, 0x55, 0x80, 0xC9, 0x48, + 0xD4, 0x07, 0x64, 0xAD, 0x54, 0x80, 0x07, 0x14, 0xFB, 0x61, 0x17, 0x6C, + 0x28, 0xBA, 0x51, 0xB5, 0x53, 0x0E, 0xC9, 0xB5, 0x52, 0x2C, 0x29, 0xC3, + 0x60, 0x24, 0x8A, 0x47, 0x05, 0x4C, 0x04, 0xA3, 0x55, 0x20, 0x92, 0xA9, + 0x93, 0x53, 0xA6, 0x09, 0x1D, 0x10, 0x5C, 0xA2, 0xA3, 0x25, 0x24, 0xE9, + 0x21, 0x70, 0x9E, 0x13, 0x04, 0xF2, 0x92, 0x57, 0x49, 0x34, 0xA5, 0x28, + 0x29, 0x92, 0x52, 0xA1, 0xB9, 0x2D, 0xC9, 0x52, 0x99, 0x12, 0xA0, 0xE2, + 0x98, 0x95, 0x12, 0x51, 0xA4, 0x12, 0xBC, 0xA5, 0xB9, 0x42, 0x4A, 0x70, + 0x8A, 0x2D, 0x98, 0x29, 0xC9, 0x51, 0x4C, 0x4A, 0x49, 0x51, 0x70, 0x4D, + 0xB9, 0x46, 0x53, 0x4A, 0x48, 0xB6, 0x5B, 0xD3, 0xEE, 0x42, 0x25, 0x29, + 0xD1, 0x14, 0x5B, 0x32, 0x65, 0x44, 0x94, 0xC4, 0x94, 0xDA, 0xA4, 0xA5, + 0x12, 0x9D, 0xAE, 0x95, 0x02, 0x13, 0xB5, 0x24, 0x24, 0x29, 0x00, 0x9B, + 0x54, 0xFA, 0xC2, 0x49, 0x52, 0x79, 0x51, 0x82, 0x90, 0x09, 0x29, 0x79, + 0x52, 0x06, 0x14, 0x53, 0xA4, 0xA5, 0xF7, 0x25, 0x2A, 0x25, 0xC9, 0x20, + 0xA6, 0x41, 0xD0, 0x9C, 0xBC, 0x76, 0x43, 0x77, 0x0A, 0x32, 0x95, 0x2A, + 0xD2, 0x6E, 0x4E, 0x20, 0xA1, 0x84, 0xB7, 0x24, 0xAB, 0x48, 0x48, 0x09, + 0xB4, 0x2A, 0x32, 0x94, 0xA4, 0xA6, 0x49, 0x0F, 0x34, 0xC1, 0x49, 0x24, + 0xAE, 0x9E, 0x54, 0x64, 0xA4, 0x64, 0xA4, 0xA5, 0x39, 0xBD, 0xD0, 0xC8, + 0x85, 0x32, 0x0A, 0x6D, 0xA5, 0x24, 0x10, 0xC4, 0x29, 0xB5, 0x20, 0xD5, + 0x36, 0x35, 0x25, 0x00, 0xB6, 0xD4, 0xE1, 0x88, 0x80, 0x29, 0x86, 0xA1, + 0x6B, 0xA9, 0x07, 0xA4, 0x9F, 0xD3, 0x01, 0x1A, 0x13, 0x16, 0xCA, 0x56, + 0xAA, 0x43, 0xB4, 0x4A, 0x48, 0x9B, 0x12, 0x4A, 0xD5, 0x4F, 0xFF, 0xD0, + 0xED, 0x8B, 0x52, 0x0C, 0x48, 0xA7, 0x69, 0x56, 0x5A, 0xAB, 0x8A, 0xD2, + 0x35, 0x85, 0x30, 0xE4, 0xCE, 0x72, 0x09, 0x47, 0xB4, 0x04, 0xA1, 0x39, + 0x29, 0xB7, 0x14, 0x50, 0xC1, 0xC0, 0x28, 0x96, 0xA2, 0x19, 0x29, 0x6D, + 0x29, 0x5A, 0x29, 0x09, 0x61, 0x4B, 0x69, 0xEE, 0x8D, 0xB1, 0x36, 0xC4, + 0x6D, 0x54, 0x84, 0xB5, 0x3C, 0x04, 0x5D, 0x85, 0x37, 0xA6, 0x95, 0xAA, + 0x91, 0x41, 0x4B, 0x69, 0x46, 0x0C, 0x4F, 0xB5, 0x2B, 0x55, 0x21, 0xDA, + 0x52, 0xDA, 0x8D, 0xB5, 0x2D, 0xA9, 0x5A, 0xA9, 0x0E, 0xD4, 0xD0, 0x51, + 0x8B, 0x53, 0x40, 0x4A, 0xD5, 0x48, 0xA0, 0xA5, 0x05, 0x13, 0x6A, 0x50, + 0x95, 0xA2, 0x91, 0x42, 0x62, 0x11, 0x4B, 0x53, 0x10, 0x8D, 0xAA, 0x91, + 0x96, 0xA6, 0xDA, 0xA6, 0x54, 0x4A, 0x48, 0x62, 0x42, 0x8C, 0xA9, 0x39, + 0x40, 0x84, 0x50, 0x57, 0xDC, 0x9A, 0x54, 0x53, 0xF0, 0x92, 0x14, 0x99, + 0x4B, 0x94, 0xD0, 0x92, 0x98, 0x99, 0x4D, 0x05, 0x4A, 0x14, 0xA1, 0x1B, + 0x53, 0x00, 0xDF, 0x14, 0xA0, 0xA9, 0x42, 0x45, 0x2B, 0x53, 0x08, 0x4A, + 0x13, 0x92, 0x13, 0x84, 0x94, 0xC7, 0x50, 0xA4, 0x25, 0x38, 0x0A, 0x40, + 0x04, 0x95, 0x4B, 0x76, 0x4C, 0xA4, 0x79, 0x4D, 0xDD, 0x05, 0x2B, 0x6A, + 0x7D, 0xBA, 0x25, 0xB8, 0xA7, 0x0E, 0x49, 0x28, 0xF6, 0x92, 0x54, 0xB6, + 0xE8, 0x88, 0x1A, 0x0A, 0x62, 0xD0, 0x95, 0xAA, 0x90, 0xBA, 0x41, 0x4D, + 0x05, 0x14, 0xB5, 0x47, 0x6A, 0x36, 0x8A, 0x61, 0x05, 0x48, 0x35, 0x48, + 0x35, 0x3C, 0x21, 0x6A, 0xA6, 0x10, 0x52, 0xDA, 0x89, 0xB5, 0x23, 0x09, + 0x5A, 0x69, 0x88, 0x10, 0x9C, 0x04, 0x8A, 0x70, 0xE4, 0x94, 0xA8, 0x52, + 0x01, 0x30, 0x29, 0xD0, 0x4A, 0x93, 0x42, 0x74, 0xE1, 0x25, 0x28, 0x35, + 0x38, 0x09, 0xC0, 0x4E, 0x02, 0x16, 0x95, 0xDA, 0x11, 0x00, 0x4C, 0x14, + 0x82, 0x09, 0x5B, 0x6A, 0x70, 0xD9, 0x4E, 0x13, 0xA4, 0x9A, 0x5B, 0xD3, + 0x09, 0x27, 0x49, 0x25, 0x3F, 0xFF, 0xD1, 0xED, 0xE1, 0x34, 0x29, 0xC2, + 0x50, 0xAC, 0x35, 0x98, 0xA6, 0x32, 0xA5, 0x09, 0x6D, 0x94, 0x94, 0xC5, + 0x3C, 0x27, 0x0D, 0x4F, 0x09, 0x29, 0x68, 0x4F, 0x09, 0xE1, 0x3C, 0x24, + 0xA6, 0x30, 0x94, 0x29, 0x42, 0x78, 0x41, 0x2C, 0x21, 0x28, 0x53, 0x84, + 0xA1, 0x25, 0x30, 0x84, 0xA1, 0x4E, 0x13, 0x42, 0x4A, 0x63, 0x09, 0x88, + 0x53, 0x85, 0x12, 0x8A, 0x18, 0x90, 0x9A, 0x14, 0x92, 0x84, 0x94, 0xC2, + 0x12, 0x85, 0x38, 0x4C, 0x42, 0x36, 0x8A, 0x60, 0x42, 0x81, 0x45, 0x20, + 0xA8, 0x16, 0xA4, 0x82, 0x89, 0xC5, 0x40, 0xCA, 0x29, 0x6A, 0x8E, 0xD4, + 0xE4, 0x23, 0x32, 0xA0, 0x8E, 0x6B, 0x94, 0xBD, 0x24, 0xAD, 0x14, 0x82, + 0x13, 0xC2, 0x21, 0xA8, 0xA5, 0xB0, 0x84, 0x6D, 0x54, 0xC1, 0x25, 0x2D, + 0xA5, 0x2D, 0xA5, 0x05, 0x53, 0x08, 0x4C, 0x53, 0xB9, 0x44, 0x82, 0x8A, + 0x16, 0x94, 0xC4, 0xA7, 0xD5, 0x30, 0xD4, 0xA4, 0x85, 0xBB, 0xA9, 0xB4, + 0x26, 0xD8, 0xA4, 0x02, 0x49, 0x01, 0x96, 0x89, 0xC0, 0x94, 0xC1, 0x15, + 0xA0, 0x42, 0x09, 0x46, 0x5A, 0x9A, 0x11, 0x88, 0x09, 0xA0, 0x25, 0x6A, + 0xA4, 0x50, 0x9C, 0x22, 0x16, 0xA4, 0x18, 0x95, 0xAA, 0x98, 0x04, 0xFA, + 0x22, 0x6C, 0x01, 0x2D, 0xA0, 0xA5, 0x69, 0xA4, 0x70, 0x91, 0x03, 0xB2, + 0x9E, 0xC0, 0x13, 0x7B, 0x52, 0xB5, 0x52, 0x38, 0x29, 0x00, 0x42, 0x9C, + 0x6A, 0x9C, 0x35, 0x2B, 0x55, 0x30, 0x84, 0xC4, 0x22, 0x90, 0x02, 0x83, + 0x92, 0x53, 0x08, 0x4D, 0x21, 0x3C, 0x4A, 0x6D, 0x85, 0x14, 0x2E, 0x0A, + 0x70, 0xE4, 0xC1, 0xA9, 0xE1, 0x05, 0x32, 0x04, 0x27, 0x0A, 0x09, 0xC2, + 0x49, 0x66, 0x9C, 0x28, 0xA9, 0x09, 0x41, 0x4C, 0x81, 0x52, 0x0A, 0x20, + 0x29, 0x80, 0x82, 0x59, 0x04, 0xE9, 0x00, 0x9D, 0x05, 0xCB, 0x24, 0xA4, + 0x92, 0x4A, 0x7F, 0xFF, 0xD2, 0xEE, 0xA1, 0x3C, 0x24, 0x9D, 0x4E, 0xD7, + 0x63, 0x09, 0xE1, 0x4A, 0x12, 0xDA, 0x92, 0x96, 0x84, 0xA1, 0x4A, 0x02, + 0x50, 0x92, 0x96, 0x84, 0xA1, 0x4A, 0x12, 0x49, 0x4B, 0x42, 0x50, 0xA4, + 0x99, 0x25, 0x2D, 0x09, 0x42, 0x74, 0x92, 0x4A, 0xD0, 0x9A, 0x13, 0xA6, + 0x29, 0x21, 0x64, 0xC4, 0x27, 0x4C, 0x92, 0x96, 0x84, 0xA1, 0x3C, 0x24, + 0x92, 0x96, 0x84, 0xA1, 0x3A, 0x49, 0x29, 0x89, 0x0A, 0x24, 0x29, 0xA4, + 0x8A, 0x11, 0x16, 0xA8, 0x46, 0xA8, 0xC4, 0x26, 0x2D, 0x4A, 0xD1, 0x4C, + 0x02, 0x4A, 0x61, 0xA9, 0x10, 0x8D, 0xAA, 0x98, 0x18, 0x43, 0x25, 0x14, + 0xB1, 0x0C, 0xB7, 0x54, 0x90, 0x56, 0x4C, 0xE5, 0x3D, 0xAA, 0x2E, 0x6A, + 0x2A, 0xA4, 0x25, 0xA2, 0x53, 0x86, 0x4A, 0x91, 0x69, 0x4E, 0xD0, 0x51, + 0xB4, 0x52, 0x33, 0x5A, 0x6D, 0xA0, 0x23, 0x98, 0x50, 0x2D, 0x4A, 0xD5, + 0x4C, 0x04, 0x15, 0x20, 0xC0, 0x9F, 0x6A, 0x90, 0x1A, 0x21, 0x6A, 0xA6, + 0x21, 0x9D, 0xD4, 0xA0, 0x24, 0x02, 0x78, 0x29, 0x25, 0x6D, 0x52, 0x82, + 0xA7, 0xAA, 0x89, 0x3E, 0x29, 0x29, 0x40, 0x88, 0xD5, 0x48, 0x42, 0x82, + 0x40, 0x84, 0x94, 0xCD, 0xDC, 0x28, 0x14, 0xF3, 0x29, 0x8A, 0x4A, 0x5C, + 0x04, 0xC5, 0xA5, 0x2D, 0xC9, 0xC3, 0xA5, 0x25, 0x2C, 0x34, 0x52, 0x05, + 0x44, 0xA6, 0x0E, 0x01, 0x25, 0x33, 0x21, 0x47, 0x68, 0x4E, 0x5C, 0x90, + 0x72, 0x4A, 0x5B, 0x60, 0x4C, 0x5A, 0xA6, 0x0A, 0x72, 0x92, 0x90, 0xED, + 0x48, 0xB6, 0x78, 0x46, 0x00, 0x14, 0xB6, 0x78, 0x25, 0x6A, 0xA4, 0x3B, + 0x4A, 0x70, 0xD4, 0x6F, 0x4C, 0xA7, 0x15, 0xC2, 0x56, 0xAA, 0x42, 0x02, + 0x90, 0x08, 0x9B, 0x12, 0xD8, 0x85, 0xAA, 0x96, 0x01, 0x4C, 0x26, 0x01, + 0x4F, 0x6A, 0x45, 0x21, 0x70, 0x9E, 0x12, 0x01, 0x3A, 0x09, 0x5A, 0x12, + 0x4E, 0x92, 0x4A, 0x7F, 0xFF, 0xD3, 0xEF, 0x13, 0x84, 0xF0, 0x94, 0x29, + 0xDA, 0xEB, 0xA4, 0x92, 0x78, 0x41, 0x2C, 0x75, 0x4E, 0x14, 0xA1, 0x28, + 0x49, 0x4B, 0x42, 0x50, 0xA5, 0x09, 0x42, 0x4A, 0x62, 0x92, 0x94, 0x25, + 0x09, 0x29, 0x8C, 0x25, 0x0A, 0x50, 0x9A, 0x12, 0x53, 0x14, 0xD0, 0xA7, + 0x09, 0xA1, 0x25, 0x31, 0x84, 0xA1, 0x3C, 0x24, 0x8A, 0x98, 0x90, 0x99, + 0x48, 0xA6, 0x21, 0x24, 0x31, 0x49, 0x3A, 0x64, 0x94, 0xA8, 0x4A, 0x12, + 0x49, 0x25, 0x2D, 0x09, 0xA1, 0x3A, 0x49, 0x29, 0x68, 0x29, 0x27, 0x49, + 0x25, 0x31, 0x2A, 0x24, 0x05, 0x22, 0x98, 0xA2, 0x86, 0x30, 0x14, 0x0A, + 0x99, 0x09, 0xA1, 0x24, 0x23, 0xD5, 0x38, 0x4E, 0x5A, 0x98, 0x84, 0x50, + 0xA4, 0xE1, 0xA1, 0x47, 0x55, 0x20, 0xE4, 0x94, 0xAD, 0x81, 0x3E, 0xC4, + 0xDB, 0x94, 0x83, 0xA7, 0x94, 0x92, 0xAD, 0xA0, 0x25, 0xB4, 0x29, 0x88, + 0x29, 0x10, 0x85, 0xAA, 0x98, 0x91, 0xA2, 0x19, 0x6E, 0xA8, 0x84, 0x21, + 0x94, 0x42, 0x8B, 0x12, 0x9A, 0x13, 0x94, 0xD0, 0x8A, 0x15, 0xAA, 0x7D, + 0x53, 0x80, 0x9D, 0x05, 0x31, 0xDA, 0x98, 0x68, 0xA6, 0x94, 0x24, 0xA6, + 0x04, 0xCA, 0x50, 0xA7, 0x09, 0x42, 0x4A, 0xA6, 0x29, 0xE0, 0x29, 0x6D, + 0x4A, 0x12, 0x55, 0x28, 0x37, 0xC1, 0x39, 0x6A, 0x76, 0x88, 0x52, 0x29, + 0x26, 0x98, 0xB5, 0xA8, 0xCD, 0x68, 0x50, 0x10, 0x14, 0xA5, 0x02, 0x95, + 0xC8, 0x01, 0x31, 0x12, 0x90, 0xD5, 0x3A, 0x0A, 0x5A, 0x13, 0x16, 0xA9, + 0x29, 0x00, 0x8A, 0x98, 0x06, 0xA9, 0x84, 0xB6, 0xA7, 0x01, 0x04, 0xAC, + 0x92, 0x94, 0x25, 0x09, 0x29, 0x8A, 0x4A, 0x50, 0x92, 0x4A, 0x7F, 0xFF, + 0xD4, 0xF4, 0x08, 0x4A, 0x13, 0xA4, 0xA6, 0x60, 0x52, 0x74, 0xC9, 0xD2, + 0x52, 0x93, 0xA4, 0x92, 0x49, 0x52, 0x49, 0x27, 0x49, 0x4B, 0x25, 0x09, + 0xD2, 0x49, 0x4B, 0x24, 0x9E, 0x12, 0x84, 0x94, 0xC5, 0x32, 0x92, 0x64, + 0x94, 0xC5, 0x24, 0xF0, 0x98, 0xA4, 0x85, 0x8A, 0x64, 0xE5, 0x32, 0x4A, + 0x58, 0xA6, 0x4E, 0x98, 0xA2, 0x85, 0x92, 0x4E, 0x92, 0x4A, 0x5A, 0x12, + 0x4E, 0x99, 0x25, 0x29, 0x28, 0x49, 0x24, 0x94, 0xB1, 0x0A, 0x24, 0x29, + 0x14, 0xC9, 0x29, 0x89, 0x09, 0x93, 0x94, 0xC5, 0x14, 0x2C, 0x42, 0x62, + 0x13, 0xA4, 0x92, 0x98, 0x42, 0x50, 0x14, 0x8A, 0x8A, 0x28, 0x5A, 0x13, + 0x84, 0x93, 0x84, 0x94, 0xCC, 0x29, 0x21, 0x82, 0x53, 0xCA, 0x09, 0x5C, + 0x85, 0x02, 0x14, 0xE5, 0x44, 0xA4, 0xA6, 0x10, 0x94, 0x29, 0x27, 0x80, + 0x8A, 0x18, 0x42, 0x74, 0xF0, 0x94, 0x24, 0xA5, 0xA1, 0x3A, 0x74, 0x90, + 0x52, 0xD0, 0x9C, 0x04, 0xE9, 0x42, 0x49, 0x54, 0x25, 0x09, 0xE1, 0x3C, + 0x24, 0xA6, 0x30, 0x9C, 0x05, 0x28, 0x09, 0xE1, 0x25, 0x2D, 0x09, 0xE1, + 0x3C, 0x27, 0x84, 0x12, 0xC6, 0x13, 0xC4, 0x27, 0x80, 0x92, 0x4A, 0x50, + 0x52, 0x10, 0x99, 0x3A, 0x4A, 0x5D, 0x3A, 0x41, 0x3A, 0x09, 0x54, 0x25, + 0x09, 0x27, 0x49, 0x4B, 0x42, 0x49, 0xE1, 0x24, 0x94, 0xFF, 0x00, 0xFF, + 0xD5, 0xF4, 0x14, 0x93, 0x95, 0x12, 0xA6, 0x60, 0x5D, 0x3A, 0x8C, 0xA7, + 0x49, 0x4C, 0x93, 0xA8, 0x85, 0x20, 0x92, 0x54, 0x9E, 0x13, 0xC2, 0x74, + 0x14, 0xB4, 0x25, 0x09, 0xE1, 0x24, 0x94, 0xC5, 0x22, 0x9D, 0x24, 0x94, + 0xC5, 0x34, 0x29, 0x42, 0x68, 0x45, 0x4C, 0x61, 0x31, 0x52, 0x29, 0x8A, + 0x4A, 0x62, 0x98, 0xA9, 0x14, 0xC5, 0x24, 0x30, 0x4C, 0xA4, 0x54, 0x4A, + 0x28, 0x52, 0x49, 0x26, 0x49, 0x4B, 0xA6, 0x49, 0x24, 0x94, 0xA4, 0x92, + 0x4D, 0x29, 0x29, 0x4A, 0x25, 0x39, 0x4C, 0x92, 0x16, 0x29, 0x92, 0x49, + 0x15, 0x2C, 0x92, 0x49, 0x24, 0xA5, 0x93, 0x42, 0x92, 0x64, 0x94, 0xB2, + 0x49, 0x14, 0x92, 0x42, 0xE9, 0x26, 0x4E, 0x92, 0x94, 0x92, 0x49, 0x24, + 0x95, 0x92, 0x49, 0x24, 0x90, 0xA4, 0xE9, 0x92, 0x45, 0x4B, 0xA7, 0x4C, + 0x92, 0x0A, 0x5D, 0x3A, 0x64, 0xE9, 0x25, 0x70, 0x9D, 0x32, 0x74, 0x94, + 0xB8, 0x53, 0xD1, 0x0C, 0x29, 0x02, 0x82, 0x59, 0x82, 0x12, 0x30, 0xA0, + 0x13, 0xA4, 0xA5, 0xD2, 0x49, 0x24, 0x94, 0xA0, 0x9D, 0x20, 0xA4, 0x92, + 0x94, 0x13, 0x84, 0xA1, 0x3C, 0x20, 0x95, 0x24, 0x94, 0x27, 0x49, 0x4B, + 0x24, 0x9D, 0x24, 0x92, 0xFF, 0x00, 0xFF, 0xD6, 0xF4, 0x24, 0xC5, 0x7C, + 0xD2, 0x92, 0x99, 0x81, 0xFA, 0x55, 0x20, 0xBE, 0x6A, 0x49, 0x25, 0x3F, + 0x4B, 0x85, 0x31, 0x0B, 0xE6, 0x54, 0x92, 0x4B, 0xF4, 0xE0, 0x4E, 0xBE, + 0x62, 0x49, 0x04, 0xBF, 0x4E, 0xA4, 0xBE, 0x62, 0x49, 0x25, 0x3F, 0x4E, + 0x24, 0xBE, 0x63, 0x49, 0x24, 0x3F, 0x4D, 0xA6, 0x2B, 0xE6, 0x54, 0x92, + 0x53, 0xF4, 0xC9, 0x4C, 0x57, 0xCC, 0xE9, 0x22, 0xA7, 0xE9, 0x62, 0x98, + 0xAF, 0x9A, 0x92, 0x49, 0x0F, 0xD2, 0x65, 0x31, 0x5F, 0x36, 0xA4, 0x8A, + 0x1F, 0xA4, 0x12, 0x5F, 0x37, 0xA4, 0x92, 0x9F, 0xA4, 0x0A, 0x45, 0x7C, + 0xDE, 0x92, 0x4A, 0x7E, 0x8F, 0x4C, 0xBE, 0x71, 0x49, 0x24, 0x3F, 0x46, + 0x94, 0xC5, 0x7C, 0xE6, 0x92, 0x2A, 0x7E, 0x8B, 0x49, 0x7C, 0xE8, 0x92, + 0x4A, 0x7E, 0x8A, 0x4E, 0xBE, 0x74, 0x49, 0x25, 0x3F, 0x45, 0x26, 0x5F, + 0x3B, 0x24, 0x92, 0x1F, 0xA2, 0x53, 0x2F, 0x9D, 0xD2, 0x49, 0x4F, 0xD1, + 0x09, 0xD7, 0xCE, 0xC9, 0x22, 0xA7, 0xE8, 0x94, 0x97, 0xCE, 0xC9, 0x20, + 0x97, 0xE8, 0x84, 0x97, 0xCE, 0xE9, 0x24, 0x87, 0xE8, 0x84, 0x97, 0xCE, + 0xE9, 0x22, 0xA7, 0xE8, 0x94, 0xEB, 0xE7, 0x54, 0x90, 0x53, 0xF4, 0x52, + 0x75, 0xF3, 0xA2, 0x49, 0x25, 0xFA, 0x31, 0x3A, 0xF9, 0xC9, 0x24, 0x94, + 0xFD, 0x1C, 0x90, 0x5F, 0x38, 0xA4, 0x92, 0x9F, 0xA3, 0xC2, 0x92, 0xF9, + 0xB9, 0x24, 0x12, 0xFD, 0x22, 0x9D, 0x7C, 0xDA, 0x92, 0x4A, 0x7E, 0x93, + 0x0A, 0x41, 0x7C, 0xD4, 0x92, 0x09, 0x7E, 0x96, 0x4E, 0xBE, 0x68, 0x49, + 0x25, 0x3F, 0x4B, 0xA4, 0xBE, 0x68, 0x49, 0x24, 0xBF, 0x4B, 0xA4, 0xBE, + 0x68, 0x49, 0x25, 0x3F, 0xFF, 0xD9, +}; + +#define IMAGE_LENGTH 0x000049F2 + +char * get_clouds(int * Len) +{ + unsigned char * ptr; + + ptr = malloc(IMAGE_LENGTH); + memcpy(ptr, Clouds, IMAGE_LENGTH); + *Len = IMAGE_LENGTH; + + return ptr; +} + +char * get_ais_blue(int * Len) +{ + unsigned char * ptr; + + ptr = malloc(aisblue_png_len); + memcpy(ptr, aisblue_png, aisblue_png_len); + *Len = IMAGE_LENGTH; + + return ptr; +} + +char * get_ais_navaid(int * Len) +{ + unsigned char * ptr; + + ptr = malloc(aisnavaid_png_len); + memcpy(ptr, aisnavaid_png, aisnavaid_png_len); + *Len = aisnavaid_png_len; + + return ptr; +} + +char * get_ais_green(int * Len) +{ + unsigned char * ptr; + + ptr = malloc(aisgreen_png_len); + memcpy(ptr, aisgreen_png, aisgreen_png_len); + *Len = aisgreen_png_len; + + return ptr; +} + +char * get_ais_red(int * Len) +{ + unsigned char * ptr; + + ptr = malloc(aisred_png_len); + memcpy(ptr, aisred_png, aisred_png_len); + *Len = aisred_png_len; + + return ptr; +} + +char * get_ais_white(int * Len) +{ + unsigned char * ptr; + + ptr = malloc(aiswhite_png_len); + memcpy(ptr, aiswhite_png, aiswhite_png_len); + *Len = aiswhite_png_len; + + return ptr; +} + +char * get_ais_trans(int * Len) +{ + unsigned char * ptr; + + ptr = malloc(aistrans_png_len); + memcpy(ptr, aistrans_png, aistrans_png_len); + *Len = aistrans_png_len; + + return ptr; +} + + +char * get_greenplane(int * Len) +{ + unsigned char * ptr; + + ptr = malloc(greenplane_png_len); + memcpy(ptr, greenplane_png, greenplane_png_len); + *Len = greenplane_png_len; + + return ptr; +} + +char * get_yellowplane(int * Len) +{ + unsigned char * ptr; + + ptr = malloc(yellowplane_png_len); + memcpy(ptr, yellowplane_png, yellowplane_png_len); + *Len = yellowplane_png_len; + + return ptr; +} + +char * get_helicopter(int * Len) +{ + unsigned char * ptr; + + ptr = malloc(helicopter_png_len); + memcpy(ptr, helicopter_png, helicopter_png_len); + *Len = helicopter_png_len; + + return ptr; +} + + +char * get_plane(int * Len) +{ + unsigned char * ptr; + + ptr = malloc(plane_png_len); + memcpy(ptr, plane_png, plane_png_len); + *Len = plane_png_len; + + return ptr; +} + + + + +char * get_aprs() +{ + char Msg[] = + +"\n" +"\n" +"\n" +"\n" +"\n" +"\n" +"G8BPQ APRS Display\n" +"\n" + +"\n" +"\n" + + +//"\n" +//"\n" +"\n" +"\n" +"\n" +"\n" +" \n" +"\n" +"\n" +"\n" +"\n" +"\n" +"
\n" +"\n" +""; + + + return _strdup(Msg);; +} + + +char * get_rotatedMarker() +{ + char Msg[] = + +"(function() {\n" +" // save these original methods before they are overwritten\n" +" var proto_initIcon = L.Marker.prototype._initIcon;\n" +" var proto_setPos = L.Marker.prototype._setPos;\n" +"\n" +" var oldIE = (L.DomUtil.TRANSFORM === 'msTransform');\n" +"\n" +" L.Marker.addInitHook(function () {\n" +" var iconOptions = this.options.icon && this.options.icon.options;\n" +" var iconAnchor = iconOptions && this.options.icon.options.iconAnchor;\n" +" if (iconAnchor) {\n" +" iconAnchor = (iconAnchor[0] + 'px ' + iconAnchor[1] + 'px');\n" +" }\n" +" this.options.rotationOrigin = this.options.rotationOrigin || iconAnchor || 'center bottom' ;\n" +" this.options.rotationAngle = this.options.rotationAngle || 0;\n" +"\n" +" // Ensure marker keeps rotated during dragging\n" +" this.on('drag', function(e) { e.target._applyRotation(); });\n" +" });\n" +"\n" +" L.Marker.include({\n" +" _initIcon: function() {\n" +" proto_initIcon.call(this);\n" +" },\n" +"\n" +" _setPos: function (pos) {\n" +" proto_setPos.call(this, pos);\n" +" this._applyRotation();\n" +" },\n" +"\n" +" _applyRotation: function () {\n" +" if(this.options.rotationAngle) {\n" +" this._icon.style[L.DomUtil.TRANSFORM+'Origin'] = this.options.rotationOrigin;\n" +"\n" +" if(oldIE) {\n" +" // for IE 9, use the 2D rotation\n" +" this._icon.style[L.DomUtil.TRANSFORM] = 'rotate(' + this.options.rotationAngle + 'deg)';\n" +" } else {\n" +" // for modern browsers, prefer the 3D accelerated version\n" +" this._icon.style[L.DomUtil.TRANSFORM] += ' rotateZ(' + this.options.rotationAngle + 'deg)';\n" +" }\n" +" }\n" +" },\n" +"\n" +" setRotationAngle: function(angle) {\n" +" this.options.rotationAngle = angle;\n" +" this.update();\n" +" return this;\n" +" },\n" +"\n" +" setRotationOrigin: function(origin) {\n" +" this.options.rotationOrigin = origin;\n" +" this.update();\n" +" return this;\n" +" }\n" +" });\n" +"})();"; + + + return _strdup(Msg);; +} + + + +char * GetStandardPage(char * FN, int * Len) +{ + if (_stricmp(FN, "aprs.html") == 0) + return get_aprs(); + + if (_stricmp(FN, "leaflet.rotatedMarker.js") == 0) + return get_rotatedMarker(); + + if (_stricmp(FN, "info_call.html") == 0) + return get_info_call(); + + if (_stricmp(FN, "infomobile_call.html") == 0) + return get_infomobile_call(); + + if (_stricmp(FN, "infoobj_call.html") == 0) + return get_infoobj_call(); + + if (_stricmp(FN, "infowx_call.html") == 0) + return get_infowx_call(); + + if (_stricmp(FN, "all.html") == 0) + return get_all(); + + if (_stricmp(FN, "allrf.html") == 0) + return get_allrf(); + + if (_stricmp(FN, "wxall.html") == 0) + return get_wxall(); + + if (_stricmp(FN, "wxrf.html") == 0) + return get_wxrf(); + + if (_stricmp(FN, "obj.html") == 0) + return get_obj(); + + if (_stricmp(FN, "objrf.html") == 0) + return get_objrf(); + + if (_stricmp(FN, "mobilesall.html") == 0) + return get_mobileall(); + + if (_stricmp(FN, "mobilesrf.html") == 0) + return get_mobilesrf(); + + if (_stricmp(FN, "info.html") == 0) + return get_info(); + + if (_stricmp(FN, "noinfo.html") == 0) + return get_noinfo(); + + if (_stricmp(FN, "favicion.ico") == 0) + return get_favicon(Len); + + if (_stricmp(FN, "Images/clouds.jpg") == 0) + return get_clouds(Len); + + if (_stricmp(FN, "aisnavaid.png") == 0) + return get_ais_navaid(Len); + + if (_stricmp(FN, "aisgreen.png") == 0) + return get_ais_green(Len); + + if (_stricmp(FN, "aisblue.png") == 0) + return get_ais_blue(Len); + + if (_stricmp(FN, "aisred.png") == 0) + return get_ais_red(Len); + + if (_stricmp(FN, "aistrans.png") == 0) + return get_ais_trans(Len); + + if (_stricmp(FN, "aiswhite.png") == 0) + return get_ais_white(Len); + + if (_stricmp(FN, "greenplane.png") == 0) + return get_greenplane(Len); + + if (_stricmp(FN, "yellowplane.png") == 0) + return get_yellowplane(Len); + + if (_stricmp(FN, "plane.png") == 0) + return get_plane(Len); + + if (_stricmp(FN, "helicopter.png") == 0) + return get_helicopter(Len); + + return 0; +} + diff --git a/BPQMail-HPLaptop.c b/BPQMail-HPLaptop.c new file mode 100644 index 0000000..344dc5f --- /dev/null +++ b/BPQMail-HPLaptop.c @@ -0,0 +1,3644 @@ +// Mail and Chat Server for BPQ32 Packet Switch +// +// + +// Version 1.0.0.17re + +// Split Messasge, User and BBS Editing from Main Config. +// Add word wrap to Console input and output +// Flash Console on chat user connect +// Fix processing Name response in chat mode +// Fix processing of *RTL from station not defined as a Chat Node +// Fix overlength lines ln List responses +// Housekeeping expires BIDs +// Killing a message removes it from the forwarding counts + +// Version 1.0.0.18 + +// Save User Database when name is entered or updated so it is not lost on a crash +// Fix Protocol Error in Compressed Forwarding when switching direction +// Add Housekeeping results dialog. + +// Version 1.0.0.19 + +// Allow PACLEN in forward scripts. +// Store and forward messages with CRLF as line ends +// Send Disconnect after FQ ( for LinFBB) +// "Last Listed" is saved if MailChat is closed without closing Console +// Maximum acceptable message length can be specified (in Forwarding Config) + +// Version 1.0.0.20 + +// Fix error in saving forwarding config (introduced in .19) +// Limit size of FBB forwarding block. +// Clear old connection (instead of new) if duplicate connect on Chat Node-Node link +// Send FA for Compressed Mail (was sending FB for both Compressed and Uncompressed) + +// Version 1.0.0.21 + +// Fix Connect Script Processing (wasn't waiting for CONNECTED from last step) +// Implement Defer +// Fix MBL-style forwarding +// Fix Add User (Params were not saved) +// Add SC (Send Copy) Command +// Accept call@bbs as well as call @ bbs + +// Version 1.0.0.22 + +// Implement RB RP LN LR LF LN L$ Commands. +// Implement QTH and ZIP Commands. +// Entering an empty Title cancels the message. +// Uses HomeBBS field to set @ field for local users. +// Creates basic WP Database. +// Uses WP to lookup @ field for non-local calls. +// Console "Actions" Menu renamed "Options". +// Excluded flag is actioned. +// Asks user to set HomeBBS if not already set. +// Fix "Shrinking Message" problem, where message got shorter each time it was read Initroduced in .19). +// Flash Server window when anyone connects to chat (If Console Option "Flash on Chat User Connect" set). + +// Version 1.0.0.23 + +// Fix R: line scan bug + +// Version 1.0.0.24 + +// Fix closing console window on 'B'. +// Fix Message Creation time. +// Enable Delete function in WP edit dialog + +// Version 1.0.0.25 + +// Implement K< and K> commands +// Experimental support for B1 and B2 forwarding +// Experimental UI System +// Fix extracting QTH from WP updates + +// Version 1.0.0.26 + +// Add YN etc responses for FBB B1/B2 + +// Version 1.0.0.27 + +// Fix crash if NULL received as start of a packet. +// Add Save WP command +// Make B2 flag BBS-specific. +// Implement B2 Send + +// Version 1.0.0.28 + +// Fix parsing of smtp to addresses - eg smtp:john.wiseman@cantab.net +// Flag messages as Held if smtp server rejects from or to addresses +// Fix Kill to (K> Call) +// Edit Message dialog shows latest first +// Add chat debug window to try to track down occasional chat connection problems + +// Version 1.0.0.29 + +// Add loads of try/excspt + +// Version 1.0.0.30 + +// Writes Debug output to LOG_DEBUG_X and Monitor Window + +// Version 1.0.0.32 + +// Allow use of GoogleMail for ISP functions +// Accept SYSOP as alias for SYSOPCall - ie user can do SP SYSOP, and it will appear in sysop's LM, RM, etc +// Email Housekeeping Results to SYSOP + +// Version 1.0.0.33 + +// Housekeeping now runs at Maintenance Time. Maintenance Interval removed. +// Allow multiple numbers on R and K commands +// Fix L command with single number +// Log if Forward count is out of step with messages to forward. +// UI Processing improved and F< command implemented + +// Version 1.0.0.34 + +// Semaphore Chat Messages +// Display Semaphore Clashes +// More Program Error Traps +// Kill Messages more than BIDLifetime old + +// Version 1.0.0.35 + +// Test for Mike - Remove B1 check from Parse_SID + +// Version 1.0.0.36 + +// Fix calculation of Housekeeping Time. +// Set dialog box background explicitly. +// Remove tray entry for chat debug window. +// Add date to log file name. +// Add Actions Menu option to disable logging. +// Fix size of main window when it changes between versions. + +// Version 1.0.0.37 + +// Implement Paging. +// Fix L< command (was giving no messages). +// Implement LR LR mmm-nnn LR nnn- (and L nnn-) +// KM should no longer kill SYSOP bulls. +// ISP interfaces allows SMTP Auth to be configured +// SMTP Client would fail to send any more messages if a connection failed + +// Version 1.0.0.38 + +// Don't include killed messages in L commands (except LK!) +// Implement l@ +// Add forwarding timebands +// Allow resizing of main window. +// Add Ver command. + +// Version 1.0.1.1 + +// First Public Beta + +// Fix part line handling in Console +// Maintenance deletes old log files. +// Add option to delete files to the recycle bin. + +// Version 1.0.2.1 + +// Allow all Node SYSOP commands in connect scripts. +// Implement FBB B1 Protocol with Resume +// Make FBB Max Block size settable for each BBS. +// Add extra logging when Chat Sessions refused. +// Fix Crash on invalid housekeeping override. +// Add Hold Messages option. +// Trap CRT Errors +// Sort Actions/Start Forwarding List + +// Version 1.0.2.2 + +// Fill in gaps in BBS Number sequence +// Fix PE if ctext contains } +// Run Houskeeping at startup if previous Housekeeping was missed + +// Version 1.0.2.3 + +// Add configured nodes to /p listing + +// Version 1.0.2.4 + +// Fix RMS (it wanted B2 not B12) +// Send messages if available after rejecting all proposals +// Dont try to send msg back to originator. + +// Version 1.0.2.5 + +// Fix timeband processing when none specified. +// Improved Chat Help display. +// Add helpful responses to /n /q and /t + +// Version 1.0.2.6 + +// Kill Personal WP messages after processing +// Make sure a node doesnt try to "join" or "leave" a node as a user. +// More tracing to try to track down lost topic links. +// Add command recall to Console +// Show users in new topic when changing topic +// Add Send From Clipboard" Action + +// Version 1.0.2.7 + +// Hold messages from the future, or with invalid dates. +// Add KH (kill held) command. +// Send Message to SYSOP when a new user connects. + +// Version 1.0.2.8 + +// Don't reject personal message on Dup BID unless we already have an unforwarded copy. +// Hold Looping messages. +// Warn SYSOP of held messages. + +// Version 1.0.2.9 + +// Close connecton on receipt of *** DONE (MBL style forwarding). +// Improved validation in link_drop (Chat Node) +// Change to welcome prompt and Msg Header for Outpost. +// Fix Connect Script processing for KA Nodes + +// Version 1.0.3.1 + +// Fix incorrect sending of NO - BID. +// Fix problems caused by a user being connected to more than one chat node. +// Show idle time on Chat /u display. +// Rewrite forwarding by HA. +// Add "Bad Words" Test. +// Add reason for holding to SYSOP "Message Held" Message. +// Make topics case-insensitive. +// Allow SR for smtp mail. +// Try to fix some user's "Add User" problem. + + +// Version 1.0.3.2 + +// Fix program error when prcessing - response in FBB forwarding. +// Fix code to flag messages as sent. + + +// Version 1.0.3.3 + +// Attempt to fix message loop on topic_change +// Fix loop if compressed size is greater than 32K when receiving with B1 protocol. +// Fix selection of B1 + +// Version 1.0.3.4 + +// Add "KISS ONLY" Flag to R: Lines (Needs Node Version 4.10.12 (4.10l) or above) +// Add Basic NNTP Interface +// Fix possible loop in lzhuf encode + +// Version 1.0.3.5 + +// Fix forwarding of Held Messages +// More attempts to fix Chat crashes. +// Limit join/leave problem with mismatched nodes. +// Add Chat Node Monitoring System. +// Change order of elements in nntp addresses (now to.at, was at.to) + +// Version 1.0.3.6 + +// Restart and Exit if too many errors +// Fix forwarding of killed messages. +// Fix Forwarding to PaKet. +// Fix problem if BBS signon contains words from the "Fail" list + +// Version 1.0.3.7 + +// re-fix loop if compressed size is greater than 32K - reintroduced in 1.0.3.4 +// Add last message to edit users +// Change Console and Monitor Buffer sizes +// Don't flag msg as 'Y' on read if it was Held or Killed + +// Version 1.0.3.8 + +// Don't connect if all messages for a BBS are held. +// Hold message if From or To are missing. +// Fix parsing of /n and /q commands +// fix possible loop on changing name or qth + +// Version 1.0.3.9 + +// More Chat fixes and monitoring +// Added additional console for chat + +// Version 1.0.3.10 + +// Fix for corruption of CIrcuit-Node chain. + +// Version 1.0.3.11 + +// Fix flow control for SMTP and NNTP + +// Version 1.0.3.12 + +// Fix crash in SendChatStatus if no Chat Links Defined. +// Disable Chat Mode if there is no ApplCall for ChatApplNum, +// Add Edit Message to Manage Messages Dialog +// NNTP needs authentication + + +// Version 1.0.3.13 + +// Fix Chat ApplCall warning when ChatAppl = 0 +// Add NNTP NEWGROUPS Command +// Fix MBL Forwarding (remove extra > prompt after SP) + +// Version 1.0.3.14 + +// Fix topic switch code. +// Send SYSOP messages on POP3 interface if User SYSOP flag is set. +// NNTP only needs Authentication for posting, not reading. + +// Version 1.0.3.15 + +// Fix reset of First to Forward after househeeping + +// Version 1.0.3.16 + +// Fix check of HA for terminating WW +// MBL Mode remove extra > prompts +// Fix program error if WP record has unexpected format +// Connect Script changes for WINMOR +// Fix typo in unconfigured node has connected message + +// Version 1.0.3.17 + +// Fix forwarding of Personals + +// Version 1.0.3.18 + +// Fix detection of misconfigured nodes to work with new nodes. +// Limit connection attempt rate when a chat node is unavailable. +// Fix Program Error on long input lines (> ~250 chars). + +// Version 1.0.3.19 + +// Fix Restart of B2 mode transfers. +// Fix error if other end offers B1 and you are configured for B2 only. + + +// Version 1.0.3.20 + +// Fix Paging in Chat Mode. +// Report Node Versions. + +// Version 1.0.3.21 + +// Check node is not already known when processing OK +// Add option to suppress emailing of housekeeping results + +// Version 1.0.3.22 + +// Correct Version processing when user connects via the network +// Add time controlled forwarding scripts + +// Version 1.0.3.23 + +// Changes to RMS forwarding + +// Version 1.0.3.24 + +// Fix RMS: from SMTP interface +// Accept RMS/ instead of RMS: for Thunderbird + +// Version 1.0.3.25 + +// Accept smtp: addresses from smtp client, and route to ISP gateway. +// Set FROM address of messages from RMS that are delivered to smtp client so a reply will go back via RMS. + +// Version 1.0.3.26 + +// Improve display of rms and smtp messages in message lists and message display. + +// Version 1.0.3.27 + +// Correct code that prevents mail being retured to originating BBS. +// Tidy stuck Nodes and Topics when all links close +// Fix B2 handling of @ to TO Address. + +// Version 1.0.3.28 + +// Ensure user Record for the BBS Call has BBS bit set. +// Don't send messages addressed @winlink.org if addressee is a local user with Poll RMS set. +// Add user configurable welcome messages. + +// Version 1.0.3.29 + +// Add AUTH feature to Rig Control + +// Version 1.0.3.30 + +// Process Paclink Header (;FW:) + +// Version 1.0.3.31 + +// Process Messages with attachments. +// Add inactivity timeout to Chat Console sessions. + +// Version 1.0.3.32 + +// Fix for Paclink > BBS Addresses + +// Version 1.0.3.33 + +// Fix multiple transfers per session for B2. +// Kill messages eent to paclink. +// Add option to forward messages on arrival. + +// Version 1.0.3.34 + +// Fix bbs addresses to winlink. +// Fix adding @winlink.org to imcoming paclink msgs + +// Version 1.0.3.35 + +// Fix bbs addresses to winlink. (Again) + +// Version 1.0.3.36 + +// Restart changes for RMS/paclink + +// Version 1.0.3.37 + +// Fix for RMS Express forwarding + +// Version 1.0.3.38 + +// Fixes for smtp and lower case packet addresses from Airmail +// Fix missing > afer NO - Bid in MBL mode + +// Version 1.0.3.39 + +// Use ;FW: for RMS polling. + +// Version 1.0.3.40 + +// Add ELSE Option to connect scripts. + +// Version 1.0.3.41 + +// Improved handling of Multiple Addresses +// Add user colours to chat. + +// Version 1.0.3.42 + +// Poll multiple SSID's for RMS +// Colour support for BPQTEerminal +// New /C chat command to toggle colour on or off. + +// Version 1.0.3.43 + +// Add SKIPPROMPT command to forward scripts + +// Version 1.0.4.1 + +// Non - Beta Release +// Fix possible crash/corruption with long B2 messages + +// Version 1.0.4.2 + +// Add @winlink.org to the B2 From addresss if it is just a callsign +// Route Flood Bulls on TO as well as @ + +// Version 1.0.4.3 + +// Handle Packet Addresses from RMS Express +// Fix for Housekeeping B$ messages + +// Version 1.0.4.4 + +// Remove B2 header and all but the Body part from messages forwared using MBL +// Fix handling of ;FW: from RMS Express + +// Version 1.0.4.5 + +// Disable Paging on forwarding sessions. +// Kill Msgs sent to RMS Exxpress +// Add Name to Chat *** Joined msg + +// Version 1.0.4.6 + +// Pass smtp:winlink.org messages from Airmail to local user check +// Only apply local user check to RMS: messages @winlink.org +// Check locally input smtp: messages for local winlink.org users +// Provide facility to allow only one connect on a port + +// Version 1.0.4.8 + +// Only reset last listed on L or LR commands. + +// Version 1.0.4.9 + +// Fix error in handling smtp: messages to winlink.org addresses from Airmail + +// Version 1.0.4.10 + +// Fix Badwords processing +// Add Connect Script PAUSE command + +// Version 1.0.4.11 + +// Suppress display and listing of held messages +// Add option to exclude SYSOP messages from LM, KM, etc +// Fix crash whan receiving messages with long lines via plain text forwarding + +// Version 1.0.4.12 Jul 2010 + +// Route P messages on AT +// Allow Applications above 8 + +// Version 1.0.4.13 Aug 2010 + +// Fix TidyString for addresses of form John Wiseman +// Add Try/Except around socket routines + +// Version 1.0.4.14 Aug 2010 + +// Trap "Error - TNC Not Ready" in forward script response +// Fix restart after program error +// Add INFO command +// Add SYSOP-configurable HELP Text. + +// Version 1.0.4.15 Aug 2010 + +// Semaphore Connect/Disconnect +// Semaphore RemoveTempBIDS + +// Version 1.0.4.16 Aug 2010 + +// Remove prompt after receiving unrecognised line in MBL mode. (for MSYS) + +// Version 1.0.4.17 Aug 2010 + +// Fix receiving multiple messages in FBB Uncompressed Mode +// Try to trap phantom chat node connections +// Add delay to close + + +// Version 1.0.4.18 Aug 2010 + +// Add "Send SYSTEM messages to SYSOP Call" Option +// set fwd bit on local winlink.org msgs if user is a BBS +// add winlink.org to from address of messages from WL2K that don't already have an @ + +// Version 1.0.4.19 Sept 2010 + +// Build a B2 From: address if possible, so RMS Express can reply to packet messages. +// Fix handling of addresses from WL2K with SSID's +// L@ now only matches up to length of input string. +// Remove "Type H for help" from login prompt. + +// Version 1.0.4.20 Sept 2010 + +// Process FBB 'E' response +// Handle FROM addresses with an @BBS +// Fix FROM addresses with @ on end. +// Extend delay before close after sending FQ on winmor/pactor sessions. + +// Version 1.0.4.21 Sept 2010 + +// Fix handling B2 From: with an HA +// Add "Expert User" welcome message. + +// Version 1.0.4.22 Sept 2010 + +// Version 1.0.4.23 Oct 2010 + +// Add Dup message supression +// Dont change B2 from if going to RMS + +// Version 1.0.4.24 Oct 2010 + +// Add "Save Registry Config" command +// Add forwarding on wildcarded TO for NTS +// Add option to force text mode forwarding +// Define new users as a temporaty BBS if SID received in reply to Name prompt +// Reduce delay before sending close after sending FQ on pactor sessions +// Fix processing of MIME boundary from GMail + +// Send /ex instead of ctrl/z for text mode forwarding +// Send [WL2K-BPQ... SID if user flagged as RMS Express +// Fix Chat Map reporting when more than one AXIP port +// Add Message State D for NTS Messages +// Forward messages in priority order - T, P, B +// Add Reject and Hold Filters +// Fix holding messages to local RMS users when received as part of a multiple addressee message + +// Version 1.0.4.25 Nov 2010 + +// Renumbered for release +// Add option to save Registry Config during Housekeeping + +// Version 1.0.4.26 Nov 2010 + +// Fix F> loop when doing MBL forwarding between BPQ BBSes +// Allow multiple To: addresses, separated by ; +// Allow Houskeeping Lifetime Overrides to apply to Unsent Messages. +// Set Unforwarded Bulls to status '$' +// Accept MARS and USA as continent codes for MARS Packet Addresses +// Add option to send Non-delivery notifications. + +// Version 1.0.4.27 Dec 2010 + +// Add MSGTYPES fwd file option + +// Version 1.0.4.28 Dec 2010 + +// Renumbered to for release + +// Version 1.0.4.30 Dec 2010 + +// Fix rescan requeuing where bull was rejected by a BBS +// Fiz flagging bulls received by NNTP with $ if they need to be forwarded. +// Add Chat Keepalive option. +// Fix bug in non-delivery notification. + +// Version 1.0.4.32 Jan 2011 + +// Allow "Send from Clipboard" to send to rms: or smtp: +// Allow messages received via SMTP to be bulls (TO preceeded by bull/) or NTS (to nnnnn@NTSXX or nnnnn@NTSXX.NTS) +// Fix corruption of messages converted to B2 if body contains binary data +// Fix occasional program error when forwarding B2 messages +// Limit FBB protocol data blocks to 250 to try to fix restart problem. +// Add F2 to F5 to open windows. + +// Version 1.0.4.33 Jan 2011 + +// Fix holding old bulls with forwarding info. + +// Version 1.0.4.33 Jan 2011 + +// Prevent transfer restarting after a program error. +// Allow Housekeeping to kill held messages. + +// Version 1.0.4.35 Jan 2011 + +// Add Size limits for P and T messages to MSGTYPES command +// Fix Error in MBL processing when blank lines received (introduced in .33) +// Trap possible PE in Send_MON_Datagram +// Don't use paging on chat sessions + +// Version 1.0.4.36 Jan 2011 + +// Fix error after handling first FBB block. +// Add $X and $x welcome message options. + +// Version 1.0.4.37 Jan 2011 + +// Change L command not to list the last message if no new ones are available +// Add LC I I@ IH IZ commands +// Add option to send warning to sysop if forwarded P or T message has nowhere to go +// Fixes for Winpack Compressed Download +// Fix Houskeeping when "Apply Overrides to Unsent Bulls" is set. +// Add console copy/paste. +// Add "No Bulls" Option. +// Add "Mail For" Beacon. +// Tidied up Tab order in config dialogs to help text-to-speech programs. +// Limit MaxMsgno to 99000. + +// Version 1.0.4.38 Feb 2011 + +// Renumbered for release + +// Version 1.0.4.40 April 2011 + +// Add POLLRMS command + +// Changes for Vista/Win7 (registry key change) +// Workaround for changes to RMS Express +// Fix AUTH bug in SMTP server +// Add filter to Edit Messages dialog + +// Version 1.0.4.41 April 2011 + +// Extend B2 proposals to other BPQMail systems so Reject Filter will work. +// Add Edit User Command +// Use internal Registry Save routine instead of Regedit +// Fix Start Forward/All +// Allow Winpack Compressed Upload/Download if PMS flag set (as well as BBS flag) +// Add FWD SYSOP command +// Fix security on POLLRMS command +// Add AUTH command +// Leave selection in same place after Delete User +// Combine SMTP server messages to multiple WL2K addresses into one message to WL2k +// Add option to show name as well as call on Chat messages +// Fix program error if you try to define more than 80 BBS's + +// Version 1.0.4.45 October 2011 + +// Changes to program error reporting. +// BBS "Returh to Node" command added +// Move config to "Standard" location (BPQ Directory/BPQMailChat) . +// Fix crash if "Edit Message" clicked with no message selected. + +// Version 1.0.4.46 October 2011 + +// Fix BaseDir test when BaseDir ends with \ or / +// Fix long BaseDir values (>50 chars) + +// Version 1.4.47.1 January 2012 + +// Call CloseBPQ32 on exit +// Add option to flash window instead of sounding bell on Chat Connects +// Add ShowRMS SYSOP command +// Update WP with I records from R: lines +// Send WP Updates +// Fix Paclen on Pactor-like sessions +// Fix SID and Prompt when RMS Express User is set +// Try to stop loop in Program Error/Restarting code +// Trap "UNABLE TO CONNECT" response in connect script +// Add facility to print messages or save them to a text file + +// Version 1.4.48.1 January 2012 + +// Add Send Message (as well as Send from Clipboard) +// Fix Email From: Address when forwaring using B2 +// Send WP from BBSCALL not SYSOPCALL +// Send Chat Map reports via BPQ32.dll + + +// Version 1.4.49.1 February 2012 + + +// Fix Setting Paclink mode on SNOS connects +// Remove creation of debugging file for each message +// Add Message Export and Import functions +// All printing of more than one message at a time +// Add command to toggle "Expert" status + +// Version 1.4.50.1 February 2012 + +// Fix forwarding to RMS Express users +// Route messages received via B2 to an Internet email address to RMS +// Add Reverse Poll interval +// Add full FROM address to POP3 messages +// Include HOMEBBS command in Help + + +// Version 1.4.51.1 June 2012 + +// Allow bulls to be sent from RMS Express. +// Handle BASE64 and Quoted-printable encoding of single part messages +// Work round for RMS Express "All proposals rejected" Bug. + +// Version 1.4.52.1 August 2012 + +// Fix size limit on B2 To List when sending to multiple dests +// Fix initialisation of DIRMES.SYS control record +// Allow use of Tracker and UZ7HO ports for UI messages + +// Version 1.4.53.1 September 2012 + +// Fix crash if R: line with out a CR found. + +// Version 1.4.54.1 ?? 2012 + +// Add configurable prompts +// Fix KISS-Only Test +// Send EHLO instead of HELO when Authentication is needed on SMTP session +// Add option to use local tome for bbs forwarding config +// Allow comment lines (; or @) or single space in fwd scripts +// Fix loss of forwarding info if SAVE is clicked before selecting a call + +// Version 1.4.55.1 June 2013 + +// Add option to remove users that have not connected for a long time. +// Add l@ smtp: +// Fix From: sent to POP3 Client when meaages is from RMS +// Display Email From on Manage Messages + +// Version 1.4.56.1 July 2013 + +// Add timeout +// Verify prompts +// Add IDLETIME command + + + +// Version 1.4.57.1 + +// Change default IDLETIME +// Fix display of BBS's in Web "Manage Messages" +// Add separate househeeping lifetines for T messages +// Don't change flag on forwarded or delivered messages if they sre subsequently read +// Speed up processing, mainly to stop RMS Express timing out when connecting via Telnet +// Don't append winlink.org to RMS Express or Paclink addresses if RMS is not configured +// Fix receiving NTS messages via B2 +// Add option to send "Mail For", but not FBB Headers +// Fix corruption caused with Subject longer than 60 bytes reveived from Winlink systems +// Fix Endian bug in FBB Compression code + + +// Version 1.4.58.1 + +// Change control of appending winlink.org to RMS Express or Paclink addresses to a user flag +// Lookup HomeBBS and WP for calls without a via received from RMS Express or Paclink +// Treat call@bpq as request to look up address in Home BBS/WP for messages received from RMS Express or Paclink +// Collect stats by message type +// Fix Non-Delivery notifications to SMTP messages +// Add Message Type Stats to BBS Trafic Report +// Add "Batch forward to email" +// Add EXPORT command +// Allow more BBS records +// Allow lower case connect scripts +// Fix POP3 LIST command +// Fix MIME Multipart Alternate with first part Base64 or Quoted Printable encoding +// Fix duplicates of SP SYSOP@WW Messages +// Add command line option (tidymail) to delete redundant Mail files +// Add command line option (nohomebbs) to suppress HomeBBS prompt + +// 59 April 2014 + +// Add FLARQ Mail Mode +// Fix possible crash saving restart data +// Add script command ADDLF for connect scripts over Telnet +// Add recogniton of URONODE connected message +// Add option to stop Name prompt +// Add new RMS Express users with "RMS Express User" flag set +// Validate HTML Pages +// Add NTS swap file +// Add basic File list and read functions +// Fix Traffic report + +// 60 + +// Fix security hole in readfile + +// 61 August 2014 +// Set Messages to NTS:nnnnn@NTSXX to type 'T' and remove NTS +// Dont treat "Attempting downlink" as a failure +// Add option to read messages during a list +// Fix crash during message renumber on MAC +// Timeout response to SID to try to avoid hang on an incomplete connection. +// Save config in file instead of registry +// Fix Manage Messages "EXPORT" option and check filename on EXPORT command +// Fix reverse forward prompt in MBL mode. +// Fix From address in POP3 messages where path is @winlink.org +// Fix possible program error in T message procesing +// Add MaxAge param (for incoming Bulls) + + +//62 November 2014 +// Add ZIP and Permit Bulls flag to Manage Users +// Allow users to kill their own B and anyone to kill T messages +// Improve saving of "Last Listed" +// Fix LL when paging +// Send Date received in R: Line (should fix B2 message restarts) +// Fix occasional crash in terminal part line processing +// Add "SKIPCON" forwarding command to handle nodes that include "Connected" in their CTEXT +// Fix possible retry loop when message is deferred (FBB '=' response); +// Don't remove Attachments from received bulls. + +//63 Feb 2015 + +// Fix creating Bulls from RMS Express messages. +// Fix PE if message with no To: received. +// Fix setting "RMS Express User" flag on new connects from RMS Express +// Fix deleting 'T' messages downloaded by RMS Express +// Include MPS messages in count of messages to forward. +// Add new Welcome Message variable $F for messages to forward +// Fix setting Type in B2 header when usong NTS: or BULL: +// Remove trailing spaces from BID when Creating Message from Clipboard. +// Improved handling of FBB B1/B2 Restarts. + +//64 September 2015 + +// Fix Message Type in msgs from RMS Express to Internet +// Reopen Monitor window if open when program list closed +// Only apply NTS alias file to NTS Messages +// Fix failure to store some encrypted ISP passwords +// Allow EDITUSER to change "RMS Express User" flag +// Fix reporting of Config File errors +// Fix Finding MPS Messages (First to Forward was being used incorrectly) +// Add "Save Attachment" to Web Mgmt Interface +// Support Secure Signon on Forwarding sessions to CMS +// Save Forwarding config when BBS flag on user is cleared +// Pass internally generated SYSOP messages through routing process +// Add POP3 TOP command. +// Don't set 'T' messages to 'Y' when read. +// Add optional temporary connect script on "FWD NOW" command +// Add automatic import facility +// Accept RMS mail to BBS Call even if "Poll RMS" not set. + +// 65 November 2015 + +// Fix loading Housekeeping value for forwarded bulls. +// Fix re-using Fwd script override in timer driven forwarding. +// Add ampr.org handling +// Add "Dont forward" match on TO address for NTS +// Allow listing a combinatiom of state and type, such as LNT or LPF +// Fix handling ISP messages from gmail without a '+' +// Add basic WebMail support + +// 66 + +// Autoimport messages as Dummy Call, not SYSOP Call +// Add "My Messages" display option to WebMail +// Create .csv extract of User List during hourekeeping. +// Fix processing of NTS Alising of @ Addresses +// Don't reroute Delivered NTS Messages +// Add option to stop users killing T messages +// Add multicast Receive +// Fix initialising new message database format field +// Fix "Forward Messages to BBS Call" option. +// Add Filter WP Bulls option and allow multiple WP "TO" addresses +// Fix deleting P WP messages for other stations +// Fix saving blank lines in forwarding config +// Fix paging on L@ and l< +// Fix removing DELETE from IMPORT XXX DELETE and allow multiple IMPORT lines in script +// Run DeleteRedundantMessages before renumbering messages +// Connect script now tries ELSE lines if prompt not received from remote BBS +// Send connecting call instead of BBS Name when connecting to CMS server. +// Add BID filter to Manage Messages +// Fix handling of over long suject lines in IMPORT +// Allow comments before ELSE in connect script +// Add Copy and Clear to Multicast Window +// Fix possible duplicate messages with MBL forwarding +// Set "Permit EMail" on IMPORT dummy User. +// Fix repeated running of housekeeping if clock is stepped forward. +// Fix corruption of CMS Pass field by Web interface +// Kill B2 WP bulls if FilterWPBulls set +// Include Message Type in BPQ B2 proposal extensions + +// 6.0.14.1 July 2017 + +// Fix corruption of BBSNumber if RMS Ex User and BBS both checked +// Tread B messages without an AT as Flood. +// Make sure Message headers are always saved to disk when a message status changes +// Reject message instead of failing session if TO address too long in FBB forwarding +// Fix error when FBB restart data exactly fills a packet. +// Fix possible generation of msg number zero in send nondlivery notification +// Fix problem with Web "Manage Messages" when stray message number zero appears +// Fix Crash in AMPR forward when host missing from VIA +// Fix possible addition of an spurious password entry to the ;FW: line when connecting to CMS +// Fix test for Status "D" in forward check. +// Don't cancel AUTH on SMTP RSET +// Fix "nowhere to go" message on some messages sent to smtp addresses +// Add @ from Home BBS or WP is not spcified in "Send from Clipboard" + +// 6.0.15.1 Feb 2018 + +// Fix PE if Filename missing from FILE connect script command +// Suppress reporting errors after receiving FQ +// Fix problem caused by trailing spaces on callsign in WP database +// Support mixed case WINLINK Passwords + +// 6.0.16.1 March 2018 + +// Make sure messages sent to WL2K don;'t have @ on from: address +// If message to saildocs add R: line as an X header instead of to body +// Close session if more than 4 Invalid Commmad responses sent +// Report TOP in POP3 CAPA list. Allows POP3 to work with Windows Mail client + +// 6.0.17.1 November 2018 + +// Add source routing using ! eg sp g8bpq@winlink.org!gm8bpq to send via RMS on gm8bpq +// Accept an internet email address without rms: or smtp: +// Fix "Forward messages for BBS Call" when TO isn't BBS Call +// Accept NNTP commands in either case +// Add NNTP BODY command +// Timeout POP or SMTP TCP connections that are open too long +// Add YAPP support +// Fix connect script when Node CTEXT contains "} BBS " +// Fix handling null H Route +// Detect and correct duplicate BBS Numbers +// Fix problem if BBS requests FBB blocked forwarding without compression (ie SID of F without B) +// Fix crash if YAPP entered without filenmame and send BBS prompt after YAPP error messages +// Add support for Winlink HTML Forms to WebMail interface +// Update B2 header when using NTS alias file with B2 messages + +// 6.0.18.1 January 2019 + +// Ensure callsigns in WP database are upper case. +// Various fixes for Webmail +// Fix sending direct to ampr.org addresses +// Use SYSOP Call as default for Webmail if set +// Preparations for 64 bit version + + +// 6.0.19.1 September 2019 + +// Trap missing HTML reply Template or HTML files +// Fix case problems in HTML Templates +// Fix setting To call on reply to HTML messages +// More preparations for 64 bit including saving WP info as a text file. +// Set "RMS Express User" when a new user connects using PAT +// Increace maximum length on Forwarding Alias string in Web interface +// Expand multiaddress messages from Winlink Express if "Don't add @Winlink.org" set or no RMS BBS +// Fix program error if READ used without a filename +// Trap reject messages from Winlink CMS +// Fix "delete to recycle bin" on Linux +// Handle Radio Only Messages (-T or -R suffix on calling station) +// Fix program error on saving empty Alias list on Web Forwarding page +// Add REQDIR and REQFIL +// Experimental Blocked Uncompressed forwarding +// Security fix for YAPP +// Fix WebMail Cancel Send Message +// Fix processing Hold Message response from Winlink Express + +// 6.0.20.1 April 2020 + +// Improvments to YAPP +// Add Copy forwarding config +// Add Next and Previous buttons to Webmail message read screen +// Move HTML templates from HTMLPages to inline code. +// Fix Paclen on YAPP send +// Fix bug in handling "RMS Express User" +// Fix WINPACK compressed forwarding +// Add option to send P messages to more than one BBS +// Add "Default to Don't Add WINLINK.ORG" Config option +// Re-read Badwords.sys during Housekeeping +// Add BID Hold and Reject Filters +// On SMTP Send try HELO if EHLO rejected +// Allow SID response timeout to be configured per BBS +// Fix sending bulls with PAT +// Set "Forward Messages to BBS Call" when routing Bulls on TO +// Add option to send Mail For Message to APRS +// Fix WP update +// Fix Holding messages from Webmail Interface +// Add RMR command +// Add REROUTEMSGS BBS SYSOP command +// Disable null passwords and check Exclude flag in Webmail Signin +// Add basic Webmail logging + +// 6.0.21.1 December 2020 + +// Remove nulls from displayed messages. +// Fix Holding messages from SMTP and POP3 Interfaces +// Various fixes for handling messages to/from Internet email addresses +// Fix saving Email From field in Manage Messages +// Fix sending WL2K traffic reports via TriMode. +// Fix removing successive CR from Webmail Message display +// Fix Wildcarded @ forwarding +// Fix message type when receiving NTS Msgs form Airmail +// Fix address on SERVICE messages from Winlink +// Add multiple TO processing to Webmail non-template messages +// Don't backup config file if reading it fails +// Include Port and Freq on Connected log record +// Make sure welcome mesages don't end in > +// Allow flagging unread T messages as Delivered +// Replace \ with # in forward script so commands starting with # can be sent +// Fix forwarding NTS on TO field +// Fix possible crash in text mode forwarding +// Allow decimals of days in P message lifetimes and allow Houskeeping interval to be configured +// Add DOHOUSEKEEPING sysop command +// Add MARS continent code +// Try to trap 'zombie' BBS Sessions +// On Linux if "Delete to Recycle Bin" is set move deleted messages and logs to directory Deleted under current directory. +// Fix corruption of message length when reading R2 message via Read command +// Fix paging on List command and add new combinations of List options +// Fix NNTP list and LC command when bulls are killed + +// 6.0.22.1 August 2021 + +// Fix flagging messages with attachments as read. +// Fix possible corruption of WP database and subsequent crash on reloading. +// Fix format of Web Manage Messages display +// Include SETNEXTMESSAGENUMBER in SYSOP Help Message +// Fix occasional "Incoming Connect from SWITCH" +// Fix L> with numeric dests +// Improved diagnostic for MailTCP select() error. +// Clear "RMS Express User" if user is changed to a BBS +// Fix saving Window positions on exit +// Fix parsing ReplyTemplate name in Webmail +// Handle multiple addressees for WebMail Forms messages to packet stations +// Add option to allow only known users to connect +// Add basic callsign validation to From address +// Add option to forward a user's messages to Winlink +// Move User config to main config file. +// Update message status whne reading a Forms Webmail message +// Speed up killing multiple messages +// Allow SendWL2KFW as well as the (incorrect)SendWL2KPM command + +// 6.0.23.1 June 2022 + +// Fix crash when ; added to call in send commands +// Allow smtp/ override for messages from RMS Express to send via ISP gateway +// Send Internet email from RMS Express to ISP Gateway if enabled and RMS BBS not configured +// Recompiled for Web Interface changes in Node +// Add RMS Relay SYNC Mode (.17) +// Add Protocol changes for Relay RO forwarding +// Add SendWL2KPM command to connect script to allow users other than RMS to send ;FW: string to RMS Relay +// Fix B2 Header Date in Webmail message with sttachments. +// Fix bug when using YAPP with VARA (.27) +// Allow SendWL2KFW as well as the (incorrect)SendWL2KPM command +// Add mechsnism to send bbs log records to qttermtcp. (32) +// Add MFJ forwarding Mode (No @BBS on send) +// Fix handling CR/LF split over packet boundaries +// Add Header and Footers for Webmail Send (42) +// Fix Maintenance Interval in LinBPQ (53) +// Add RMS: to valid from addresses (.56) +// Fix Web management on Android deviced (.58) +// Disconnect immediately if "Invalid Command" "*** Protocol Error" or "Already Connected" received (.70) +// Check Badword and Reject filters before processing WP Messages + +// 6.0.24.1 ?? 2022 + +// Fix ' in Webmail subject (8) +// Change web buttons to white on black when pressed (10) +// Add auto-refresh option to Webmail index page (25) +// Fix displaying help and info files with crlf line endings on Linux (28) +// Improve validation of extended FC message (32) +// Improve WP check for SYSTEM as a callsihn (33) +// Improvements to RMS Relay SYNC mode (47) +// Fix BID Hold and Reject filters + + + +#include "bpqmail.h" +#define MAIL +#include "Versions.h" + +#include "GetVersion.h" + +#define MAX_LOADSTRING 100 + +typedef int (WINAPI FAR *FARPROCX)(); +typedef int (WINAPI FAR *FARPROCZ)(); + +FARPROCX pDllBPQTRACE; +FARPROCZ pGetLOC; +FARPROCX pRefreshWebMailIndex; +FARPROCX pRunEventProgram; + +BOOL WINE = FALSE; + +INT_PTR CALLBACK UserEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +INT_PTR CALLBACK MsgEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +INT_PTR CALLBACK FwdEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +INT_PTR CALLBACK WPEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + +VOID SetupNTSAliases(char * FN); + +HKEY REGTREE = HKEY_LOCAL_MACHINE; // Default +char * REGTREETEXT = "HKEY_LOCAL_MACHINE"; + +// Global Variables: +HINSTANCE hInst; // current instance +TCHAR szTitle[MAX_LOADSTRING]; // The title bar text +TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name + +extern int LastVer[4]; // In case we need to do somthing the first time a version is run + +UINT BPQMsg; + +HWND MainWnd; +HWND hWndSess; +RECT MainRect; +HMENU hActionMenu; +static HMENU hMenu; +HMENU hDisMenu; // Disconnect Menu Handle +HMENU hFWDMenu; // Forward Menu Handle + +int SessX, SessY, SessWidth; // Params for Session Window + +char szBuff[80]; + +#define MaxSockets 64 + +int _MYTIMEZONE = 0; + +ConnectionInfo Connections[MaxSockets+1]; + +//struct SEM AllocSemaphore = {0, 0}; +//struct SEM ConSemaphore = {0, 0}; +//struct SEM OutputSEM = {0, 0}; + +//struct UserInfo ** UserRecPtr=NULL; +//int NumberofUsers=0; + +//struct UserInfo * BBSChain = NULL; // Chain of users that are BBSes + +//struct MsgInfo ** MsgHddrPtr=NULL; +//int NumberofMessages=0; + +//int FirstMessageIndextoForward=0; // Lowest Message wirh a forward bit set - limits search + +//BIDRec ** BIDRecPtr=NULL; +//int NumberofBIDs=0; + +extern BIDRec ** TempBIDRecPtr; +//int NumberofTempBIDs=0; + +//WPRec ** WPRecPtr=NULL; +//int NumberofWPrecs=0; + +extern char ** BadWords; +//int NumberofBadWords=0; +extern char * BadFile; + +//int LatestMsg = 0; +//struct SEM MsgNoSemaphore = {0, 0}; // For locking updates to LatestMsg +//int HighestBBSNumber = 0; + +//int MaxMsgno = 60000; +//int BidLifetime = 60; +//int MaintInterval = 24; +//int MaintTime = 0; +//int UserLifetime = 0; + + +BOOL cfgMinToTray; + +BOOL DisconnectOnClose; + +extern char PasswordMsg[100]; + +char cfgHOSTPROMPT[100]; + +char cfgCTEXT[100]; + +char cfgLOCALECHO[100]; + +char AttemptsMsg[]; +char disMsg[]; + +char LoginMsg[]; + +char BlankCall[]; + + +ULONG BBSApplMask; +ULONG ChatApplMask; + +int BBSApplNum; + +//int StartStream=0; +int NumberofStreams; +int MaxStreams; + +extern char BBSSID[]; +extern char ChatSID[]; + +extern char NewUserPrompt[100]; + +extern char * WelcomeMsg; +extern char * NewWelcomeMsg; +extern char * ExpertWelcomeMsg; + +extern char * Prompt; +extern char * NewPrompt; +extern char * ExpertPrompt; + +extern BOOL DontNeedHomeBBS; + +char BBSName[100]; +char MailForText[100]; + +char SignoffMsg[100]; + +char AbortedMsg[100]; + +extern char UserDatabaseName[MAX_PATH]; +extern char UserDatabasePath[MAX_PATH]; + +extern char MsgDatabasePath[MAX_PATH]; +extern char MsgDatabaseName[MAX_PATH]; + +extern char BIDDatabasePath[MAX_PATH]; +extern char BIDDatabaseName[MAX_PATH]; + +extern char WPDatabasePath[MAX_PATH]; +extern char WPDatabaseName[MAX_PATH]; + +extern char BadWordsPath[MAX_PATH]; +extern char BadWordsName[MAX_PATH]; + +char NTSAliasesPath[MAX_PATH]; +extern char NTSAliasesName[MAX_PATH]; + +char BaseDir[MAX_PATH]; +char BaseDirRaw[MAX_PATH]; // As set in registry - may contain %NAME% + +char MailDir[MAX_PATH]; + +char RlineVer[50]; + +extern BOOL KISSOnly; + +extern BOOL OpenMon; + +extern struct ALIAS ** NTSAliases; + +extern int EnableUI; +extern int RefuseBulls; +extern int SendSYStoSYSOPCall; +extern int SendBBStoSYSOPCall; +extern int DontHoldNewUsers; +extern int ForwardToMe; + +extern int MailForInterval; + +char zeros[NBMASK]; // For forward bitmask tests + +time_t MaintClock; // Time to run housekeeping + +struct MsgInfo * MsgnotoMsg[100000]; // Message Number to Message Slot List. + +// Filter Params + +char ** RejFrom; // Reject on FROM Call +char ** RejTo; // Reject on TO Call +char ** RejAt; // Reject on AT Call +char ** RejBID; // Reject on BID + +char ** HoldFrom; // Hold on FROM Call +char ** HoldTo; // Hold on TO Call +char ** HoldAt; // Hold on AT Call +char ** HoldBID; // Hold on BID + + +// Send WP Params + +BOOL SendWP; +char SendWPVIA[81]; +char SendWPTO[11]; +int SendWPType; + + +int ProgramErrors = 0; + +UCHAR BPQDirectory[260] = ""; + + +// Forward declarations of functions included in this code module: +ATOM MyRegisterClass(HINSTANCE hInstance); +ATOM RegisterMainWindowClass(HINSTANCE hInstance); +BOOL InitInstance(HINSTANCE, int); +LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); +INT_PTR CALLBACK ClpMsgDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +INT_PTR CALLBACK SendMsgDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +INT_PTR CALLBACK ChatMapDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + +unsigned long _beginthread( void( *start_address )(VOID * DParam), + unsigned stack_size, VOID * DParam); + +VOID SendMailForThread(VOID * Param); +BOOL CreatePipeThread(); +int DeleteRedundantMessages(); +VOID BBSSlowTimer(); +VOID CopyConfigFile(char * ConfigName); +BOOL CreateMulticastConsole(); +char * CheckToAddress(CIRCUIT * conn, char * Addr); +BOOL CheckifPacket(char * Via); +int GetHTMLForms(); + +struct _EXCEPTION_POINTERS exinfox; + +CONTEXT ContextRecord; +EXCEPTION_RECORD ExceptionRecord; + +DWORD Stack[16]; + +BOOL Restarting = FALSE; + +Dump_Process_State(struct _EXCEPTION_POINTERS * exinfo, char * Msg) +{ + unsigned int SPPtr; + unsigned int SPVal; + + memcpy(&ContextRecord, exinfo->ContextRecord, sizeof(ContextRecord)); + memcpy(&ExceptionRecord, exinfo->ExceptionRecord, sizeof(ExceptionRecord)); + + SPPtr = ContextRecord.Esp; + + Debugprintf("BPQMail *** Program Error %x at %x in %s", + ExceptionRecord.ExceptionCode, ExceptionRecord.ExceptionAddress, Msg); + + + __asm{ + + mov eax, SPPtr + mov SPVal,eax + lea edi,Stack + mov esi,eax + mov ecx,64 + rep movsb + + } + + Debugprintf("EAX %x EBX %x ECX %x EDX %x ESI %x EDI %x ESP %x", + ContextRecord.Eax, ContextRecord.Ebx, ContextRecord.Ecx, + ContextRecord.Edx, ContextRecord.Esi, ContextRecord.Edi, SPVal); + + Debugprintf("Stack:"); + + Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ", + SPVal, Stack[0], Stack[1], Stack[2], Stack[3], Stack[4], Stack[5], Stack[6], Stack[7]); + + Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ", + SPVal+32, Stack[8], Stack[9], Stack[10], Stack[11], Stack[12], Stack[13], Stack[14], Stack[15]); + +} + + + +void myInvalidParameterHandler(const wchar_t* expression, + const wchar_t* function, + const wchar_t* file, + unsigned int line, + uintptr_t pReserved) +{ + Logprintf(LOG_DEBUG_X, NULL, '!', "*** Error **** C Run Time Invalid Parameter Handler Called"); + + if (expression && function && file) + { + Logprintf(LOG_DEBUG_X, NULL, '!', "Expression = %S", expression); + Logprintf(LOG_DEBUG_X, NULL, '!', "Function %S", function); + Logprintf(LOG_DEBUG_X, NULL, '!', "File %S Line %d", file, line); + } +} + +// If program gets too many program errors, it will restart itself and shut down + +VOID CheckProgramErrors() +{ + STARTUPINFO SInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION + char ProgName[256]; + + if (Restarting) + exit(0); // Make sure can't loop in restarting + + ProgramErrors++; + + if (ProgramErrors > 25) + { + Restarting = TRUE; + + Logprintf(LOG_DEBUG_X, NULL, '!', "Too Many Program Errors - Closing"); + + if (cfgMinToTray) + { + DeleteTrayMenuItem(MainWnd); + if (ConsHeader[0]->hConsole) + DeleteTrayMenuItem(ConsHeader[0]->hConsole); + if (ConsHeader[1]->hConsole) + DeleteTrayMenuItem(ConsHeader[1]->hConsole); + if (hMonitor) + DeleteTrayMenuItem(hMonitor); + } + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + GetModuleFileName(NULL, ProgName, 256); + + Debugprintf("Attempting to Restart %s", ProgName); + + CreateProcess(ProgName, "MailChat.exe WAIT", NULL, NULL, FALSE, 0, NULL, NULL, &SInfo, &PInfo); + + exit(0); + } +} + + +VOID WriteMiniDump() +{ +#ifdef WIN32 + + HANDLE hFile; + BOOL ret; + char FN[256]; + + sprintf(FN, "%s/Logs/MiniDump%x.dmp", GetBPQDirectory(), time(NULL)); + + hFile = CreateFile(FN, GENERIC_READ | GENERIC_WRITE, + 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE)) + { + // Create the minidump + + ret = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), + hFile, MiniDumpNormal, 0, 0, 0 ); + + if(!ret) + Debugprintf("MiniDumpWriteDump failed. Error: %u", GetLastError()); + else + Debugprintf("Minidump %s created.", FN); + CloseHandle(hFile); + } +#endif +} + + +void GetSemaphore(struct SEM * Semaphore, int ID) +{ + // + // Wait for it to be free + // +#ifdef WIN32 + if (Semaphore->Flag != 0) + { + Semaphore->Clashes++; + } +loop1: + + while (Semaphore->Flag != 0) + { + Sleep(10); + } + + // + // try to get semaphore + // + + _asm{ + + mov eax,1 + mov ebx, Semaphore + xchg [ebx],eax // this instruction is locked + + cmp eax,0 + jne loop1 // someone else got it - try again +; +; ok, weve got the semaphore +; + } +#else + + while (Semaphore->Flag) + usleep(10000); + + Semaphore->Flag = 1; + +#endif + return; +} + +void FreeSemaphore(struct SEM * Semaphore) +{ + Semaphore->Flag = 0; + + return; +} + +char * CmdLine; + +extern int configSaved; + +int APIENTRY WinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPTSTR lpCmdLine, + int nCmdShow) +{ + MSG msg; + HACCEL hAccelTable; + int BPQStream, n; + struct UserInfo * user; + struct _EXCEPTION_POINTERS exinfo; + _invalid_parameter_handler oldHandler, newHandler; + char Msg[100]; + int i = 60; + struct NNTPRec * NNTPREC; + struct NNTPRec * SaveNNTPREC; + + CmdLine = _strdup(lpCmdLine); + _strlwr(CmdLine); + + if (_stricmp(lpCmdLine, "Wait") == 0) // If AutoRestart then Delay 60 Secs + { + hWnd = CreateWindow("STATIC", "Mail Restarting after Failure - Please Wait", 0, + CW_USEDEFAULT, 100, 550, 70, + NULL, NULL, hInstance, NULL); + + ShowWindow(hWnd, nCmdShow); + + while (i-- > 0) + { + sprintf(Msg, "Mail Restarting after Failure - Please Wait %d secs.", i); + SetWindowText(hWnd, Msg); + + Sleep(1000); + } + + DestroyWindow(hWnd); + } + + __try { + + // Trap CRT Errors + + newHandler = myInvalidParameterHandler; + oldHandler = _set_invalid_parameter_handler(newHandler); + + // Initialize global strings + LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); + LoadString(hInstance, IDC_BPQMailChat, szWindowClass, MAX_LOADSTRING); + MyRegisterClass(hInstance); + + // Perform application initialization: + + if (!InitInstance (hInstance, nCmdShow)) + { + return FALSE; + } + + hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_BPQMailChat)); + + // Main message loop: + + Logprintf(LOG_DEBUG_X, NULL, '!', "Program Starting"); + Logprintf(LOG_BBS, NULL, '!', "BPQMail Starting"); + Debugprintf("BPQMail Starting"); + + if (pDllBPQTRACE == 0) + Logprintf(LOG_BBS, NULL, '!', "Remote Monitor Log not available - update BPQ32.dll to enable"); + + + } My__except_Routine("Init"); + + while (GetMessage(&msg, NULL, 0, 0)) + { + __try + { + if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + #define EXCEPTMSG "GetMessageLoop" + #include "StdExcept.c" + + CheckProgramErrors(); + } + } + + __try + { + for (n = 0; n < NumberofStreams; n++) + { + BPQStream=Connections[n].BPQStream; + + if (BPQStream) + { + SetAppl(BPQStream, 0, 0); + Disconnect(BPQStream); + DeallocateStream(BPQStream); + } + } + + + hWnd = CreateWindow("STATIC", "Mail Closing - Please Wait", 0, + 150, 200, 350, 40, NULL, NULL, hInstance, NULL); + + ShowWindow(hWnd, nCmdShow); + + Sleep(1000); // A bit of time for links to close + + DestroyWindow(hWnd); + + if (ConsHeader[0]->hConsole) + DestroyWindow(ConsHeader[0]->hConsole); + if (ConsHeader[1]->hConsole) + DestroyWindow(ConsHeader[1]->hConsole); + if (hMonitor) + { + DestroyWindow(hMonitor); + hMonitor = (HWND)1; // For status Save + } + + +// SaveUserDatabase(); + SaveMessageDatabase(); + SaveBIDDatabase(); + + configSaved = 1; + SaveConfig(ConfigName); + + if (cfgMinToTray) + { + DeleteTrayMenuItem(MainWnd); + if (ConsHeader[0]->hConsole) + DeleteTrayMenuItem(ConsHeader[0]->hConsole); + if (ConsHeader[1]->hConsole) + DeleteTrayMenuItem(ConsHeader[1]->hConsole); + if (hMonitor) + DeleteTrayMenuItem(hMonitor); + } + + // Free all allocated memory + + for (n = 0; n <= NumberofUsers; n++) + { + user = UserRecPtr[n]; + + if (user->ForwardingInfo) + { + FreeForwardingStruct(user); + free(user->ForwardingInfo); + } + + free(user->Temp); + + free(user); + } + + free(UserRecPtr); + + for (n = 0; n <= NumberofMessages; n++) + free(MsgHddrPtr[n]); + + free(MsgHddrPtr); + + for (n = 0; n <= NumberofWPrecs; n++) + free(WPRecPtr[n]); + + free(WPRecPtr); + + for (n = 0; n <= NumberofBIDs; n++) + free(BIDRecPtr[n]); + + free(BIDRecPtr); + + if (TempBIDRecPtr) + free(TempBIDRecPtr); + + NNTPREC = FirstNNTPRec; + + while (NNTPREC) + { + SaveNNTPREC = NNTPREC->Next; + free(NNTPREC); + NNTPREC = SaveNNTPREC; + } + + if (BadWords) free(BadWords); + if (BadFile) free(BadFile); + + n = 0; + + if (Aliases) + { + while(Aliases[n]) + { + free(Aliases[n]->Dest); + free(Aliases[n]); + n++; + } + + free(Aliases); + FreeList(AliasText); + } + + n = 0; + + if (NTSAliases) + { + while(NTSAliases[n]) + { + free(NTSAliases[n]->Dest); + free(NTSAliases[n]); + n++; + } + + free(NTSAliases); + } + + FreeOverrides(); + + FreeList(RejFrom); + FreeList(RejTo); + FreeList(RejAt); + FreeList(RejBID); + FreeList(HoldFrom); + FreeList(HoldTo); + FreeList(HoldAt); + FreeList(HoldBID); + FreeList(SendWPAddrs); + + Free_UI(); + + for (n=1; n<20; n++) + { + if (MyElements[n]) free(MyElements[n]); + } + + free(WelcomeMsg); + free(NewWelcomeMsg); + free(ExpertWelcomeMsg); + + free(Prompt); + free(NewPrompt); + free(ExpertPrompt); + + FreeWebMailMallocs(); + + free(CmdLine); + + _CrtDumpMemoryLeaks(); + + } + My__except_Routine("Close Processing"); + + CloseBPQ32(); // Close Ext Drivers if last bpq32 process + + return (int) msg.wParam; +} + + + +// +// FUNCTION: MyRegisterClass() +// +// PURPOSE: Registers the window class. +// +// COMMENTS: +// +// This function and its usage are only necessary if you want this code +// to be compatible with Win32 systems prior to the 'RegisterClassEx' +// function that was added to Windows 95. It is important to call this function +// so that the application will get 'well formed' small icons associated +// with it. +// +// +#define BGCOLOUR RGB(236,233,216) +//#define BGCOLOUR RGB(245,245,245) + +HBRUSH bgBrush; + +ATOM MyRegisterClass(HINSTANCE hInstance) +{ + WNDCLASSEX wcex; + + bgBrush = CreateSolidBrush(BGCOLOUR); + + wcex.cbSize = sizeof(WNDCLASSEX); + + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = WndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = DLGWINDOWEXTRA; + wcex.hInstance = hInstance; + wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(BPQICON)); + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + wcex.hbrBackground = bgBrush; + wcex.lpszMenuName = MAKEINTRESOURCE(IDC_BPQMailChat); + wcex.lpszClassName = szWindowClass; + wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(BPQICON)); + + return RegisterClassEx(&wcex); +} + + +// +// FUNCTION: InitInstance(HINSTANCE, int) +// +// PURPOSE: Saves instance handle and creates main window +// +// COMMENTS: +// +// In this function, we save the instance handle in a global variable and +// create and display the main program window. +// + +HWND hWnd; + +int AXIPPort = 0; + +char LOC[7] = ""; + +BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) +{ + char Title[80]; + WSADATA WsaData; + HMENU hTopMenu; // handle of menu + HKEY hKey=0; + int retCode; + RECT InitRect; + RECT SessRect; + struct _EXCEPTION_POINTERS exinfo; + + HMODULE ExtDriver = LoadLibrary("bpq32.dll"); + + if (ExtDriver) + { + pDllBPQTRACE = GetProcAddress(ExtDriver,"_DllBPQTRACE@8"); + pGetLOC = GetProcAddress(ExtDriver,"_GetLOC@0"); + pRefreshWebMailIndex = GetProcAddress(ExtDriver,"_RefreshWebMailIndex@0"); + pRunEventProgram = GetProcAddress(ExtDriver,"_RunEventProgram@8"); + + if (pGetLOC) + { + char * pLOC = (char *)pGetLOC(); + memcpy(LOC, pLOC, 6); + } + } + + // See if running under WINE + + retCode = RegOpenKeyEx (HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine", 0, KEY_QUERY_VALUE, &hKey); + + if (retCode == ERROR_SUCCESS) + { + RegCloseKey(hKey); + WINE =TRUE; + Debugprintf("Running under WINE"); + } + + + REGTREE = GetRegistryKey(); + REGTREETEXT = GetRegistryKeyText(); + + Sleep(1000); + + { + int n; + struct _EXTPORTDATA * PORTVEC; + + KISSOnly = TRUE; + + for (n=1; n <= GetNumberofPorts(); n++) + { + PORTVEC = (struct _EXTPORTDATA * )GetPortTableEntryFromSlot(n); + + if (PORTVEC->PORTCONTROL.PORTTYPE == 16) // EXTERNAL + { + if (_memicmp(PORTVEC->PORT_DLL_NAME, "TELNET", 6) == 0) + KISSOnly = FALSE; + + if (PORTVEC->PORTCONTROL.PROTOCOL != 10) // Pactor/WINMOR + KISSOnly = FALSE; + + if (AXIPPort == 0) + { + if (_memicmp(PORTVEC->PORT_DLL_NAME, "BPQAXIP", 7) == 0) + { + AXIPPort = PORTVEC->PORTCONTROL.PORTNUMBER; + KISSOnly = FALSE; + } + } + } + } + } + + hInst = hInstance; + + hWnd=CreateDialog(hInst,szWindowClass,0,NULL); + + if (!hWnd) + { + return FALSE; + } + + MainWnd = hWnd; + + GetVersionInfo(NULL); + + sprintf(Title,"G8BPQ Mail Server Version %s", VersionString); + + sprintf(RlineVer, "BPQ%s%d.%d.%d", (KISSOnly) ? "K" : "", Ver[0], Ver[1], Ver[2]); + + SetWindowText(hWnd,Title); + + hWndSess = GetDlgItem(hWnd, 100); + + GetWindowRect(hWnd, &InitRect); + GetWindowRect(hWndSess, &SessRect); + + SessX = SessRect.left - InitRect.left ; + SessY = SessRect.top -InitRect.top; + SessWidth = SessRect.right - SessRect.left; + + // Get handles for updating menu items + + hTopMenu=GetMenu(MainWnd); + hActionMenu=GetSubMenu(hTopMenu,0); + + hFWDMenu=GetSubMenu(hActionMenu,0); + hMenu=GetSubMenu(hActionMenu,1); + hDisMenu=GetSubMenu(hActionMenu,2); + + CheckTimer(); + + cfgMinToTray = GetMinimizetoTrayFlag(); + + if ((nCmdShow == SW_SHOWMINIMIZED) || (nCmdShow == SW_SHOWMINNOACTIVE)) + if (cfgMinToTray) + { + ShowWindow(hWnd, SW_HIDE); + } + else + { + ShowWindow(hWnd, nCmdShow); + } + else + ShowWindow(hWnd, nCmdShow); + + UpdateWindow(hWnd); + + WSAStartup(MAKEWORD(2, 0), &WsaData); + + __try { + + return Initialise(); + + }My__except_Routine("Initialise"); + + return FALSE; +} + +// +// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM) +// +// PURPOSE: Processes messages for the main window. +// +// + + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + PAINTSTRUCT ps; + HDC hdc; + int state,change; + ConnectionInfo * conn; + struct _EXCEPTION_POINTERS exinfo; + + + if (message == BPQMsg) + { + if (lParam & BPQMonitorAvail) + { + __try + { + DoBBSMonitorData(wParam); + } + My__except_Routine("DoMonitorData"); + + return 0; + + } + if (lParam & BPQDataAvail) + { + // Dont trap error at this level - let Node error handler pick it up +// __try +// { + DoReceivedData(wParam); +// } +// My__except_Routine("DoReceivedData") + return 0; + } + if (lParam & BPQStateChange) + { + // Get current Session State. Any state changed is ACK'ed + // automatically. See BPQHOST functions 4 and 5. + + __try + { + SessionState(wParam, &state, &change); + + if (change == 1) + { + if (state == 1) // Connected + { + GetSemaphore(&ConSemaphore, 0); + __try {Connected(wParam);} + My__except_Routine("Connected"); + FreeSemaphore(&ConSemaphore); + } + else + { + GetSemaphore(&ConSemaphore, 0); + __try{Disconnected(wParam);} + My__except_Routine("Disconnected"); + FreeSemaphore(&ConSemaphore); + } + } + } + My__except_Routine("DoStateChange"); + + } + + return 0; + } + + + switch (message) + { + + case WM_KEYUP: + + switch (wParam) + { + case VK_F2: + CreateConsole(-1); + return 0; + + case VK_F3: + CreateMulticastConsole(); + return 0; + + case VK_F4: + CreateMonitor(); + return 0; + + case VK_TAB: + return TRUE; + + break; + + + + } + return 0; + + case WM_TIMER: + + if (wParam == 1) // Slow = 10 secs + { + __try + { + time_t NOW = time(NULL); + struct tm * tm; + RefreshMainWindow(); + CheckTimer(); + TCPTimer(); + BBSSlowTimer(); + FWDTimerProc(); + if (MaintClock < NOW) + { + while (MaintClock < NOW) // in case large time step + MaintClock += MaintInterval * 3600; + + Debugprintf("|Enter HouseKeeping"); + DoHouseKeeping(FALSE); + } + tm = gmtime(&NOW); + + if (tm->tm_wday == 0) // Sunday + { + if (GenerateTrafficReport && (LastTrafficTime + 86400) < NOW) + { + CreateBBSTrafficReport(); + LastTrafficTime = NOW; + } + } + } + My__except_Routine("Slow Timer"); + } + else + __try + { + TrytoSend(); + TCPFastTimer(); + } + My__except_Routine("TrytoSend"); + + return (0); + + + case WM_CTLCOLORDLG: + return (LONG)bgBrush; + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + return (LONG)bgBrush; + } + + case WM_INITMENUPOPUP: + + if (wParam == (WPARAM)hActionMenu) + { + if (IsClipboardFormatAvailable(CF_TEXT)) + EnableMenuItem(hActionMenu,ID_ACTIONS_SENDMSGFROMCLIPBOARD, MF_BYCOMMAND | MF_ENABLED); + else + EnableMenuItem(hActionMenu,ID_ACTIONS_SENDMSGFROMCLIPBOARD, MF_BYCOMMAND | MF_GRAYED ); + + return TRUE; + } + + if (wParam == (WPARAM)hFWDMenu) + { + // Set up Forward Menu + + struct UserInfo * user; + char MenuLine[30]; + + for (user = BBSChain; user; user = user->BBSNext) + { + sprintf(MenuLine, "%s %d Msgs", user->Call, CountMessagestoForward(user)); + + if (ModifyMenu(hFWDMenu, IDM_FORWARD_ALL + user->BBSNumber, + MF_BYCOMMAND | MF_STRING, IDM_FORWARD_ALL + user->BBSNumber, MenuLine) == 0) + + AppendMenu(hFWDMenu, MF_STRING,IDM_FORWARD_ALL + user->BBSNumber, MenuLine); + } + return TRUE; + } + + if (wParam == (WPARAM)hDisMenu) + { + // Set up Disconnect Menu + + CIRCUIT * conn; + char MenuLine[30]; + int n; + + for (n = 0; n <= NumberofStreams-1; n++) + { + conn=&Connections[n]; + + RemoveMenu(hDisMenu, IDM_DISCONNECT + n, MF_BYCOMMAND); + + if (conn->Active) + { + sprintf_s(MenuLine, 30, "%d %s", conn->BPQStream, conn->Callsign); + AppendMenu(hDisMenu, MF_STRING, IDM_DISCONNECT + n, MenuLine); + } + } + return TRUE; + } + break; + + + case WM_COMMAND: + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + // Parse the menu selections: + + if (wmEvent == LBN_DBLCLK) + + break; + + if (wmId >= IDM_DISCONNECT && wmId < IDM_DISCONNECT+MaxSockets+1) + { + // disconnect user + + conn=&Connections[wmId-IDM_DISCONNECT]; + + if (conn->Active) + { + Disconnect(conn->BPQStream); + } + } + + if (wmId >= IDM_FORWARD_ALL && wmId < IDM_FORWARD_ALL + 100) + { + StartForwarding(wmId - IDM_FORWARD_ALL, NULL); + return 0; + } + + switch (wmId) + { + case IDM_LOGBBS: + + ToggleParam(hMenu, hWnd, &LogBBS, IDM_LOGBBS); + break; + + case IDM_LOGCHAT: + + ToggleParam(hMenu, hWnd, &LogCHAT, IDM_LOGCHAT); + break; + + case IDM_LOGTCP: + + ToggleParam(hMenu, hWnd, &LogTCP, IDM_LOGTCP); + break; + + case IDM_HOUSEKEEPING: + + DoHouseKeeping(TRUE); + + break; + + case IDM_CONSOLE: + + CreateConsole(-1); + break; + + case IDM_MCMONITOR: + + CreateMulticastConsole(); + break; + + case IDM_MONITOR: + + CreateMonitor(); + break; + + case RESCANMSGS: + + ReRouteMessages(); + break; + + case IDM_IMPORT: + + ImportMessages(NULL, "", FALSE); + break; + + case IDM_ABOUT: + DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); + break; + + case ID_HELP_ONLINEHELP: + + ShellExecute(hWnd,"open", + "http://www.cantab.net/users/john.wiseman/Documents/MailServer.html", + "", NULL, SW_SHOWNORMAL); + + break; + + case IDM_CONFIG: + DialogBox(hInst, MAKEINTRESOURCE(IDD_CONFIG), hWnd, ConfigWndProc); + break; + + case IDM_USERS: + DialogBox(hInst, MAKEINTRESOURCE(IDD_USEREDIT), hWnd, UserEditDialogProc); + break; + + case IDM_FWD: + DialogBox(hInst, MAKEINTRESOURCE(IDD_FORWARDING), hWnd, FwdEditDialogProc); + break; + + case IDM_MESSAGES: + DialogBox(hInst, MAKEINTRESOURCE(IDD_MSGEDIT), hWnd, MsgEditDialogProc); + break; + + case IDM_WP: + DialogBox(hInst, MAKEINTRESOURCE(IDD_EDITWP), hWnd, WPEditDialogProc); + break; + + case ID_ACTIONS_SENDMSGFROMCLIPBOARD: + DialogBox(hInst, MAKEINTRESOURCE(IDD_MSGFROMCLIPBOARD), hWnd, ClpMsgDialogProc); + break; + + case ID_ACTIONS_SENDMESSAGE: + DialogBox(hInst, MAKEINTRESOURCE(IDD_MSGFROMCLIPBOARD), hWnd, SendMsgDialogProc); + break; + + case ID_MULTICAST: + + MulticastRX = !MulticastRX; + CheckMenuItem(hActionMenu, ID_MULTICAST, (MulticastRX) ? MF_CHECKED : MF_UNCHECKED); + break; + + case IDM_EXIT: + DestroyWindow(hWnd); + break; + + + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + break; + + case WM_SIZE: + + if (wParam == SIZE_MINIMIZED) + if (cfgMinToTray) + return ShowWindow(hWnd, SW_HIDE); + + return (0); + + + case WM_SIZING: + { + LPRECT lprc = (LPRECT) lParam; + int Height = lprc->bottom-lprc->top; + int Width = lprc->right-lprc->left; + + MoveWindow(hWndSess, 0, 30, SessWidth, Height - 100, TRUE); + + return TRUE; + } + + + case WM_PAINT: + hdc = BeginPaint(hWnd, &ps); + // TODO: Add any drawing code here... + EndPaint(hWnd, &ps); + break; + + case WM_DESTROY: + + GetWindowRect(MainWnd, &MainRect); // For save soutine + if (ConsHeader[0]->hConsole) + GetWindowRect(ConsHeader[0]->hConsole, &ConsHeader[0]->ConsoleRect); // For save soutine + if (ConsHeader[1]->hConsole) + GetWindowRect(ConsHeader[1]->hConsole, &ConsHeader[1]->ConsoleRect); // For save soutine + if (hMonitor) + GetWindowRect(hMonitor, &MonitorRect); // For save soutine + + KillTimer(hWnd,1); + KillTimer(hWnd,2); + PostQuitMessage(0); + break; + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + return 0; +} + +INT_PTR CALLBACK SendMsgDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + + SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "B"); + SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "P"); + SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "T"); + + SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_SETCURSEL, 0, 0); + + return TRUE; + + case WM_SIZING: + { + HWND hWndEdit = GetDlgItem(hDlg, IDC_EDIT1); + + LPRECT lprc = (LPRECT) lParam; + int Height = lprc->bottom-lprc->top; + int Width = lprc->right-lprc->left; + + MoveWindow(hWndEdit, 5, 90, Width-20, Height - 140, TRUE); + + return TRUE; + } + + case WM_COMMAND: + + if (LOWORD(wParam) == IDSEND) + { + char status [3]; + struct MsgInfo * Msg; + char * via = NULL; + char BID[13]; + char FileList[32768]; + BIDRec * BIDRec; + int MsgLen; + char * MailBuffer; + char MsgFile[MAX_PATH]; + HANDLE hFile = INVALID_HANDLE_VALUE; + int WriteLen=0; + char HDest[61]; + char Destcopy[61]; + char * Vptr; + char * FileName[100]; + int FileLen[100]; + char * FileBody[100]; + int n, Files = 0; + int TotalFileSize = 0; + char * NewMsg; + + GetDlgItemText(hDlg, IDC_MSGTO, HDest, 60); + strcpy(Destcopy, HDest); + + GetDlgItemText(hDlg, IDC_MSGBID, BID, 13); + strlop(BID, ' '); + + GetDlgItemText(hDlg, IDC_ATTACHMENTS, FileList, 32767); + + // if there are attachments, check that they can be opened ane read + + n = 0; + + if (FileList[0]) + { + FILE * Handle; + struct stat STAT; + char * ptr1 = FileList, * ptr2; + + while(ptr1 && ptr1[0]) + { + ptr2 = strchr(ptr1, ';'); + + if (ptr2) + *(ptr2++) = 0; + + FileName[n++] = ptr1; + + ptr1 = ptr2; + } + + FileName[n] = 0; + + // read the files + + Files = n; + n = 0; + + while (FileName[n]) + { + if (stat(FileName[n], &STAT) == -1) + { + char ErrorMessage[512]; + sprintf(ErrorMessage,"Can't find file %s", FileName[n]); + MessageBox(NULL, ErrorMessage, "BPQMail", MB_ICONERROR); + return TRUE; + } + + FileLen[n] = STAT.st_size; + + Handle = fopen(FileName[n], "rb"); + + if (Handle == NULL) + { + char ErrorMessage[512]; + sprintf(ErrorMessage,"Can't open file %s", FileName[n]); + MessageBox(NULL, ErrorMessage, "BPQMail", MB_ICONERROR); + return TRUE; + } + + FileBody[n] = malloc(FileLen[n]+1); + + fread(FileBody[n], 1, FileLen[n], Handle); + + fclose(Handle); + + TotalFileSize += FileLen[n]; + n++; + } + } + + if (strlen(HDest) == 0) + { + MessageBox(NULL, "To: Call Missing!", "BPQMail", MB_ICONERROR); + return TRUE; + } + + if (strlen(BID)) + { + if (LookupBID(BID)) + { + // Duplicate bid + + MessageBox(NULL, "Duplicate BID", "BPQMail", MB_ICONERROR); + return TRUE; + } + } + + Msg = AllocateMsgRecord(); + + // Set number here so they remain in sequence + + Msg->number = ++LatestMsg; + MsgnotoMsg[Msg->number] = Msg; + + strcpy(Msg->from, SYSOPCall); + + Vptr = strlop(Destcopy, '@'); + + if (Vptr == 0 && strchr(Destcopy, '!')) // Bang route without @ + { + Vptr = strchr(Destcopy, '!'); + strcpy(Msg->via, Vptr); + strlop(Destcopy, '!'); + + if (strlen(Destcopy) > 6) + memcpy(Msg->to, Destcopy, 6); + else + strcpy(Msg->to, Destcopy); + goto gotAddr; + } + + if (strlen(Destcopy) > 6) + memcpy(Msg->to, Destcopy, 6); + else + strcpy(Msg->to, Destcopy); + + _strupr(Msg->to); + + if (_memicmp(HDest, "rms:", 4) == 0 || _memicmp(HDest, "rms/", 4) == 0) + { + Vptr = HDest; + memmove(HDest, &HDest[4], strlen(HDest)); + strcpy(Msg->to, "RMS"); + + } + else if (_memicmp(HDest, "smtp:", 5) == 0) + { + if (ISP_Gateway_Enabled) + { + Vptr = HDest; + memmove(HDest, &HDest[5], strlen(HDest)); + Msg->to[0] = 0; + } + } + else if (Vptr) + { + // If looks like a valid email address, treat as such + + int tolen = (Vptr - Destcopy) - 1; + + if (tolen > 6 || !CheckifPacket(Vptr)) + { + // Assume Email address + + Vptr = HDest; + + if (FindRMS() || strchr(Vptr, '!')) // have RMS or source route + strcpy(Msg->to, "RMS"); + else if (ISP_Gateway_Enabled) + Msg->to[0] = 0; + else + { + MessageBox(NULL, "Sending to Internet Email not available", "BPQMail", MB_ICONERROR); + return TRUE; + } + } + } + if (Vptr) + { + if (strlen(Vptr) > 40) + Vptr[40] = 0; + + strcpy(Msg->via, Vptr); + } +gotAddr: + GetDlgItemText(hDlg, IDC_MSGTITLE, Msg->title, 61); + GetDlgItemText(hDlg, IDC_MSGTYPE, status, 2); + Msg->type = status[0]; + Msg->status = 'N'; + + if (strlen(BID) == 0) + sprintf_s(BID, sizeof(BID), "%d_%s", LatestMsg, BBSName); + + strcpy(Msg->bid, BID); + + Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + BIDRec = AllocateBIDRecord(); + + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + MsgLen = SendDlgItemMessage(hDlg, IDC_EDIT1, WM_GETTEXTLENGTH, 0 ,0); + + MailBuffer = malloc(MsgLen + TotalFileSize + 2000); // Allow for a B2 Header if attachments + + if (Files) + { + char DateString[80]; + struct tm * tm; + + char Type[16] = "Private"; + + // Get Type + + if (Msg->type == 'B') + strcpy(Type, "Bulletin"); + else if (Msg->type == 'T') + strcpy(Type, "Traffic"); + + // Create a B2 Message + + // B2 Header + + NewMsg = MailBuffer + 1000; + + tm = gmtime((time_t *)&Msg->datecreated); + + sprintf(DateString, "%04d/%02d/%02d %02d:%02d", + tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + // Remove last Source Route + + if (strchr(HDest, '!')) + { + char * bang = HDest + strlen(HDest); + + while (*(--bang) != '!'); // Find last ! + + *(bang) = 0; // remove it; + } + + NewMsg += sprintf(NewMsg, + "MID: %s\r\nDate: %s\r\nType: %s\r\nFrom: %s\r\nTo: %s\r\nSubject: %s\r\nMbo: %s\r\n", + Msg->bid, DateString, Type, Msg->from, HDest, Msg->title, BBSName); + + + NewMsg += sprintf(NewMsg, "Body: %d\r\n", MsgLen); + + for (n = 0; n < Files; n++) + { + char * p = FileName[n], * q; + + // Remove any path + + q = strchr(p, '\\'); + + while (q) + { + if (q) + *q++ = 0; + p = q; + q = strchr(p, '\\'); + } + + NewMsg += sprintf(NewMsg, "File: %d %s\r\n", FileLen[n], p); + } + + NewMsg += sprintf(NewMsg, "\r\n"); + GetDlgItemText(hDlg, IDC_EDIT1, NewMsg, MsgLen+1); + NewMsg += MsgLen; + NewMsg += sprintf(NewMsg, "\r\n"); + + for (n = 0; n < Files; n++) + { + memcpy(NewMsg, FileBody[n], FileLen[n]); + NewMsg += FileLen[n]; + free(FileBody[n]); + NewMsg += sprintf(NewMsg, "\r\n"); + } + + Msg->length = NewMsg - (MailBuffer + 1000); + NewMsg = MailBuffer + 1000; + Msg->B2Flags = B2Msg | Attachments; + } + + else + { + GetDlgItemText(hDlg, IDC_EDIT1, MailBuffer, MsgLen+1); + Msg->length = MsgLen; + NewMsg = MailBuffer; + } + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); + + hFile = CreateFile(MsgFile, + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (hFile != INVALID_HANDLE_VALUE) + { + WriteFile(hFile, NewMsg, Msg->length, &WriteLen, NULL); + CloseHandle(hFile); + } + + free(MailBuffer); + + MatchMessagetoBBSList(Msg, 0); + + BuildNNTPList(Msg); // Build NNTP Groups list + + SaveMessageDatabase(); + SaveBIDDatabase(); + + EndDialog(hDlg, LOWORD(wParam)); + + return TRUE; + } + + + if (LOWORD(wParam) == IDSelectFiles) + { + char FileNames[2048]; + char FullFileNames[32768]; + OPENFILENAME Ofn; + int err; + + FileNames[0] = 0; + + memset(&Ofn, 0, sizeof(Ofn)); + + Ofn.lStructSize = sizeof(OPENFILENAME); + Ofn.hInstance = hInst; + Ofn.hwndOwner = hDlg; + Ofn.lpstrFilter = NULL; + Ofn.lpstrFile= FileNames; + Ofn.nMaxFile = 2048; + Ofn.lpstrFileTitle = NULL; + Ofn.nMaxFileTitle = 0; + Ofn.lpstrInitialDir = (LPSTR)NULL; + Ofn.Flags = OFN_SHOWHELP | OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT | OFN_EXPLORER; + Ofn.lpstrTitle = NULL;//; + + if (GetOpenFileName(&Ofn)) + { + // if one is selected, a single string is returned, if more than one, a single + // path, followed by all the strings, duuble null terminated. + + char * Names[101]; // Allow up to 100 names + int n = 0; + char * ptr = FileNames; + + while (*ptr) + { + Names[n++] = ptr; + ptr += strlen(ptr); + ptr++; + } + + GetDlgItemText(hDlg, IDC_ATTACHMENTS, FullFileNames, 32768); + + if (strlen(FullFileNames)) + strcat(FullFileNames, ";"); + + if (n == 1) + { + // Single Select + + strcat(FullFileNames, FileNames); + } + else + { + int i = 1; + + while(i < n) + { + strcat(FullFileNames, Names[0]); + strcat(FullFileNames, "\\"); + strcat(FullFileNames, Names[i]); + i++; + if (i < n) + strcat(FullFileNames, ";"); + } + } + SetDlgItemText(hDlg, IDC_ATTACHMENTS, FullFileNames); + } + else + err = GetLastError(); + return (INT_PTR)TRUE; + } + + + if (LOWORD(wParam) == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + + return (INT_PTR)TRUE; + + break; + } + return (INT_PTR)FALSE; +} + +INT_PTR CALLBACK ClpMsgDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + HGLOBAL hglb; + LPTSTR lptstr; + + switch (message) + { + case WM_INITDIALOG: + + SetWindowText(hDlg, "Send Message from Clipboard"); + + if (!IsClipboardFormatAvailable(CF_TEXT)) + break; + + if (!OpenClipboard(hDlg)) + break; + + hglb = GetClipboardData(CF_TEXT); + + if (hglb != NULL) + { + lptstr = GlobalLock(hglb); + + if (lptstr != NULL) + { + SetDlgItemText(hDlg, IDC_EDIT1, lptstr); + GlobalUnlock(hglb); + } + } + CloseClipboard(); + } + + return SendMsgDialogProc(hDlg, message, wParam, lParam); + +} + +// Message handler for about box. +INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + UNREFERENCED_PARAMETER(lParam); + switch (message) + { + case WM_INITDIALOG: + return (INT_PTR)TRUE; + + case WM_COMMAND: + if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + return (INT_PTR)TRUE; + + break; + } + return (INT_PTR)FALSE; +} + +SMTPMsgs = 0; + +int RefreshMainWindow() +{ + char msg[80]; + CIRCUIT * conn; + int i,n, SYSOPMsgs = 0, HeldMsgs = 0; + time_t now; + struct tm * tm; + char tim[20]; + + SendDlgItemMessage(MainWnd,100,LB_RESETCONTENT,0,0); + + SMTPMsgs = 0; + + for (n = 0; n < NumberofStreams; n++) + { + conn=&Connections[n]; + + if (!conn->Active) + { + strcpy(msg,"Idle"); + } + else + { + { + if (conn->UserPointer == 0) + strcpy(msg,"Logging in"); + else + { + i=sprintf_s(msg, sizeof(msg), "%-10s %-10s %2d %-10s%5d", + conn->UserPointer->Name, conn->UserPointer->Call, conn->BPQStream, + "BBS", conn->OutputQueueLength - conn->OutputGetPointer); + } + } + } + SendDlgItemMessage(MainWnd,100,LB_ADDSTRING,0,(LPARAM)msg); + } + + SetDlgItemInt(hWnd, IDC_MSGS, NumberofMessages, FALSE); + + n = 0; + + for (i=1; i <= NumberofMessages; i++) + { + if (MsgHddrPtr[i]->status == 'N') + { + if (_stricmp(MsgHddrPtr[i]->to, SYSOPCall) == 0 || _stricmp(MsgHddrPtr[i]->to, "SYSOP") == 0) + SYSOPMsgs++; + else + if (MsgHddrPtr[i]->to[0] == 0) + SMTPMsgs++; + } + else + { + if (MsgHddrPtr[i]->status == 'H') + HeldMsgs++; + } + } + + SetDlgItemInt(hWnd, IDC_SYSOPMSGS, SYSOPMsgs, FALSE); + SetDlgItemInt(hWnd, IDC_HELD, HeldMsgs, FALSE); + SetDlgItemInt(hWnd, IDC_SMTP, SMTPMsgs, FALSE); + + SetDlgItemInt(hWnd, IDC_MSGSEM, MsgNoSemaphore.Clashes, FALSE); + SetDlgItemInt(hWnd, IDC_ALLOCSEM, AllocSemaphore.Clashes, FALSE); + SetDlgItemInt(hWnd, IDC_CONSEM, ConSemaphore.Clashes, FALSE); + + now = time(NULL); + + tm = gmtime(&now); + sprintf_s(tim, sizeof(tim), "%02d:%02d", tm->tm_hour, tm->tm_min); + SetDlgItemText(hWnd, IDC_UTC, tim); + + tm = localtime(&now); + sprintf_s(tim, sizeof(tim), "%02d:%02d", tm->tm_hour, tm->tm_min); + SetDlgItemText(hWnd, IDC_LOCAL, tim); + + + return 0; +} + +#define MAX_PENDING_CONNECTS 4 + +#define VERSION_MAJOR 2 +#define VERSION_MINOR 0 + +SOCKADDR_IN local_sin; /* Local socket - internet style */ + +PSOCKADDR_IN psin; + +SOCKET sock; + + + +BOOL Initialise() +{ + int i, len; + ConnectionInfo * conn; + struct UserInfo * user = NULL; + HKEY hKey=0; + char * ptr1; + int Attrs, ret; + char msg[500]; + TIME_ZONE_INFORMATION TimeZoneInformation; + struct stat STAT; + + GetTimeZoneInformation(&TimeZoneInformation); + + _tzset(); + _MYTIMEZONE = timezone; + _MYTIMEZONE = TimeZoneInformation.Bias * 60; + + // Register message for posting by BPQDLL + + BPQMsg = RegisterWindowMessage(BPQWinMsg); + + // See if we need to warn of possible problem with BaseDir moved by installer + + strcpy(BPQDirectory, GetBPQDirectory()); + + sprintf(BaseDir, "%s/BPQMailChat", BPQDirectory); + + len = strlen(BaseDir); + ptr1 = BaseDir; + + while (*ptr1) + { + if (*(ptr1) == '/') *(ptr1) = '\\'; + ptr1++; + } + + // Make Sure BASEDIR Exists + + Attrs = GetFileAttributes(BaseDir); + + if (Attrs == -1) + { + sprintf_s(msg, sizeof(msg), "Base Directory %s not found - should it be created?", BaseDir); + ret = MessageBox(NULL, msg, "BPQMail", MB_YESNO); + + if (ret == IDYES) + { + ret = CreateDirectory(BaseDir, NULL); + if (ret == 0) + { + MessageBox(NULL, "Failed to created Base Directory - exiting", "BPQMail", MB_ICONSTOP); + return FALSE; + } + } + else + { + MessageBox(NULL, "Can't Continue without a Base Directory - exiting", "BPQMailChat", MB_ICONSTOP); + return FALSE; + } + } + else + { + if (!(Attrs & FILE_ATTRIBUTE_DIRECTORY)) + { + sprintf_s(msg, sizeof(msg), "Base Directory %s is a file not a directory - exiting", BaseDir); + ret = MessageBox(NULL, msg, "BPQMail", MB_ICONSTOP); + + return FALSE; + } + } + + initUTF8(); + + // Set up file and directory names + + strcpy(UserDatabasePath, BaseDir); + strcat(UserDatabasePath, "\\"); + strcat(UserDatabasePath, UserDatabaseName); + + strcpy(MsgDatabasePath, BaseDir); + strcat(MsgDatabasePath, "\\"); + strcat(MsgDatabasePath, MsgDatabaseName); + + strcpy(BIDDatabasePath, BaseDir); + strcat(BIDDatabasePath, "\\"); + strcat(BIDDatabasePath, BIDDatabaseName); + + strcpy(WPDatabasePath, BaseDir); + strcat(WPDatabasePath, "\\"); + strcat(WPDatabasePath, WPDatabaseName); + + strcpy(BadWordsPath, BaseDir); + strcat(BadWordsPath, "\\"); + strcat(BadWordsPath, BadWordsName); + + strcpy(NTSAliasesPath, BaseDir); + strcat(NTSAliasesPath, "/"); + strcat(NTSAliasesPath, NTSAliasesName); + + strcpy(MailDir, BaseDir); + strcat(MailDir, "\\"); + strcat(MailDir, "Mail"); + + CreateDirectory(MailDir, NULL); // Just in case + + strcpy(ConfigName, BaseDir); + strcat(ConfigName, "\\"); + strcat(ConfigName, "BPQMail.cfg"); + + UsingingRegConfig = FALSE; + + // if config file exists use it else try to get from Registry + + if (stat(ConfigName, &STAT) == -1) + { + UsingingRegConfig = TRUE; + + if (GetConfigFromRegistry()) + { + SaveConfig(ConfigName); + } + else + { + int retCode; + + strcpy(BBSName, GetNodeCall()); + strlop(BBSName, '-'); + strlop(BBSName, ' '); + + sprintf(msg, "No configuration found - Dummy Config created"); + + retCode = MessageBox(NULL, msg, "BPQMailChat", MB_OKCANCEL); + + if (retCode == IDCANCEL) + return FALSE; + + SaveConfig(ConfigName); + } + } + + if (GetConfig(ConfigName) == EXIT_FAILURE) + { + ret = MessageBox(NULL, + "BBS Config File seems corrupt - check before continuing", "BPQMail", MB_ICONSTOP); + return FALSE; + } + + // Got a Config File + + if (MainRect.right < 100 || MainRect.bottom < 100) + { + GetWindowRect(MainWnd, &MainRect); + } + + MoveWindow(MainWnd, MainRect.left, MainRect.top, MainRect.right-MainRect.left, MainRect.bottom-MainRect.top, TRUE); + + if (OpenMon) + CreateMonitor(); + + BBSApplMask = 1<<(BBSApplNum-1); + + ShowWindow(GetDlgItem(MainWnd, 901), SW_HIDE); + ShowWindow(GetDlgItem(MainWnd, 902), SW_HIDE); + ShowWindow(GetDlgItem(MainWnd, 903), SW_HIDE); + + // Make backup copies of Databases + + CopyBIDDatabase(); + CopyMessageDatabase(); + CopyUserDatabase(); + CopyWPDatabase(); + + SetupMyHA(); + SetupFwdAliases(); + SetupNTSAliases(NTSAliasesPath); + + GetWPDatabase(); + GetMessageDatabase(); + GetUserDatabase(); + GetBIDDatabase(); + GetBadWordFile(); + GetHTMLForms(); + + UsingingRegConfig = FALSE; + + // Make sure SYSOPCALL is set + + if (SYSOPCall[0] == 0) + strcpy(SYSOPCall, BBSName); + + // Make sure there is a user record for the BBS, with BBS bit set. + + user = LookupCall(BBSName); + + if (user == NULL) + { + user = AllocateUserRecord(BBSName); + user->Temp = zalloc(sizeof (struct TempUserInfo)); + } + + if ((user->flags & F_BBS) == 0) + { + // Not Defined as a BBS + + if (SetupNewBBS(user)) + user->flags |= F_BBS; + } + + // if forwarding AMPR mail make sure User/BBS AMPR exists + + if (SendAMPRDirect) + { + BOOL NeedSave = FALSE; + + user = LookupCall("AMPR"); + + if (user == NULL) + { + user = AllocateUserRecord("AMPR"); + user->Temp = zalloc(sizeof (struct TempUserInfo)); + NeedSave = TRUE; + } + + if ((user->flags & F_BBS) == 0) + { + // Not Defined as a BBS + + if (SetupNewBBS(user)) + user->flags |= F_BBS; + NeedSave = TRUE; + } + + if (NeedSave) + SaveUserDatabase(); + } + + // Allocate Streams + + for (i=0; i < MaxStreams; i++) + { + conn = &Connections[i]; + conn->BPQStream = FindFreeStream(); + + if (conn->BPQStream == 255) break; + + NumberofStreams++; + + BPQSetHandle(conn->BPQStream, hWnd); + + SetAppl(conn->BPQStream, (i == 0 && EnableUI) ? 0x82 : 2, BBSApplMask | ChatApplMask); + Disconnect(conn->BPQStream); + } + + InitialiseTCP(); + + InitialiseNNTP(); + + SetupListenSet(); // Master set of listening sockets + + if (BBSApplNum) + { + SetupUIInterface(); + if (MailForInterval) + _beginthread(SendMailForThread, 0, 0); + } + + if (cfgMinToTray) + { + AddTrayMenuItem(MainWnd, "Mail Server"); + } + + SetTimer(hWnd,1,10000,NULL); // Slow Timer (10 Secs) + SetTimer(hWnd,2,100,NULL); // Send to Node and TCP Poll (100 ms) + + // Calulate time to run Housekeeping + { + struct tm *tm; + time_t now; + + now = time(NULL); + + tm = gmtime(&now); + + tm->tm_hour = MaintTime / 100; + tm->tm_min = MaintTime % 100; + tm->tm_sec = 0; + + MaintClock = _mkgmtime(tm); + + while (MaintClock < now) + MaintClock += MaintInterval * 3600; + + Debugprintf("Maint Clock %lld NOW %lld Time to HouseKeeping %lld", (long long)MaintClock, (long long)now, (long long)(MaintClock - now)); + + if (LastHouseKeepingTime) + { + if ((now - LastHouseKeepingTime) > MaintInterval * 3600) + { + DoHouseKeeping(FALSE); + } + } + } + + if (strstr(CmdLine, "tidymail")) + DeleteRedundantMessages(); + + if (strstr(CmdLine, "nohomebbs")) + DontNeedHomeBBS = TRUE; + + if (strstr(CmdLine, "DontCheckFromCall")) + DontCheckFromCall = TRUE; + + CheckMenuItem(hMenu,IDM_LOGBBS, (LogBBS) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,IDM_LOGTCP, (LogTCP) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,IDM_LOGCHAT, (LogCHAT) ? MF_CHECKED : MF_UNCHECKED); + + RefreshMainWindow(); + +// CreateWPReport(); + + CreatePipeThread(); + + return TRUE; +} + +int ConnectState(Stream) +{ + int state; + + SessionStateNoAck(Stream, &state); + return state; +} +UCHAR * EncodeCall(UCHAR * Call) +{ + static char axcall[10]; + + ConvToAX25(Call, axcall); + return &axcall[0]; + +} + +/* +VOID FindNextRMSUser(struct BBSForwardingInfo * FWDInfo) +{ + struct UserInfo * user; + + int i = FWDInfo->UserIndex; + + if (i == -1) + { + FWDInfo->UserIndex = FWDInfo->UserCall[0] = 0; // Not scanning users + } + + for (i++; i <= NumberofUsers; i++) + { + user = UserRecPtr[i]; + + if (user->flags & F_POLLRMS) + { + FWDInfo->UserIndex = i; + strcpy(FWDInfo->UserCall, user->Call); + FWDInfo->FwdTimer = FWDInfo->FwdInterval - 20; + return ; + } + } + + // Finished Scan + + FWDInfo->UserIndex = FWDInfo->FwdTimer = FWDInfo->UserCall[0] = 0; +} +*/ + +#ifndef NEWROUTING + +VOID SetupHAddreses(struct BBSForwardingInfo * ForwardingInfo) +{ +} +VOID SetupMyHA() +{ +} +VOID SetupFwdAliases() +{ +} + +int MatchMessagetoBBSList(struct MsgInfo * Msg, CIRCUIT * conn) +{ + struct UserInfo * bbs; + struct BBSForwardingInfo * ForwardingInfo; + char ATBBS[41]; + char * HRoute; + int Count =0; + + strcpy(ATBBS, Msg->via); + HRoute = strlop(ATBBS, '.'); + + if (Msg->type == 'P') + { + // P messages are only sent to one BBS, but check the TO and AT of all BBSs before routing on HA + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + ForwardingInfo = bbs->ForwardingInfo; + + if (CheckBBSToList(Msg, bbs, ForwardingInfo)) + { + if (_stricmp(bbs->Call, BBSName) != 0) // Dont forward to ourself - already here! + { + if ((conn == NULL) || (_stricmp(conn->UserPointer->Call, bbs->Call) != 0)) // Dont send back + { + set_fwd_bit(Msg->fbbs, bbs->BBSNumber); + ForwardingInfo->MsgCount++; + } + } + return 1; + } + } + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + ForwardingInfo = bbs->ForwardingInfo; + + if (CheckBBSAtList(Msg, ForwardingInfo, ATBBS)) + { + if (_stricmp(bbs->Call, BBSName) != 0) // Dont forward to ourself - already here! + { + if ((conn == NULL) || (_stricmp(conn->UserPointer->Call, bbs->Call) != 0)) // Dont send back + { + set_fwd_bit(Msg->fbbs, bbs->BBSNumber); + ForwardingInfo->MsgCount++; + } + } + return 1; + } + } + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + ForwardingInfo = bbs->ForwardingInfo; + + if (CheckBBSHList(Msg, bbs, ForwardingInfo, ATBBS, HRoute)) + { + if (_stricmp(bbs->Call, BBSName) != 0) // Dont forward to ourself - already here! + { + if ((conn == NULL) || (_stricmp(conn->UserPointer->Call, bbs->Call) != 0)) // Dont send back + { + set_fwd_bit(Msg->fbbs, bbs->BBSNumber); + ForwardingInfo->MsgCount++; + } + } + return 1; + } + } + + return FALSE; + } + + // Bulls go to all matching BBSs, so the order of checking doesn't matter + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + ForwardingInfo = bbs->ForwardingInfo; + + if (CheckABBS(Msg, bbs, ForwardingInfo, ATBBS, HRoute)) + { + if (_stricmp(bbs->Call, BBSName) != 0) // Dont forward to ourself - already here! + { + if ((conn == NULL) || (_stricmp(conn->UserPointer->Call, bbs->Call) != 0)) // Dont send back + { + set_fwd_bit(Msg->fbbs, bbs->BBSNumber); + ForwardingInfo->MsgCount++; + } + } + Count++; + } + } + + return Count; +} +BOOL CheckABBS(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char * HRoute) +{ + char ** Calls; + char ** HRoutes; + int i, j; + + if (strcmp(ATBBS, bbs->Call) == 0) // @BBS = BBS + return TRUE; + + // Check TO distributions + + if (ForwardingInfo->TOCalls) + { + Calls = ForwardingInfo->TOCalls; + + while(Calls[0]) + { + if (strcmp(Calls[0], Msg->to) == 0) + return TRUE; + + Calls++; + } + } + + // Check AT distributions + + if (ForwardingInfo->ATCalls) + { + Calls = ForwardingInfo->ATCalls; + + while(Calls[0]) + { + if (strcmp(Calls[0], ATBBS) == 0) + return TRUE; + + Calls++; + } + } + if ((HRoute) && (ForwardingInfo->Haddresses)) + { + // Match on Routes + + HRoutes = ForwardingInfo->Haddresses; + + while(HRoutes[0]) + { + i = strlen(HRoutes[0]) - 1; + j = strlen(HRoute) - 1; + + while ((i >= 0) && (j >= 0)) // Until one string rus out + { + if (HRoutes[0][i--] != HRoute[j--]) // Compare backwards + goto next; + } + + return TRUE; + next: + HRoutes++; + } + } + + + return FALSE; + +} + +BOOL CheckBBSToList(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo) +{ + char ** Calls; + + // Check TO distributions + + if (ForwardingInfo->TOCalls) + { + Calls = ForwardingInfo->TOCalls; + + while(Calls[0]) + { + if (strcmp(Calls[0], Msg->to) == 0) + return TRUE; + + Calls++; + } + } + return FALSE; +} + +BOOL CheckBBSAtList(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS) +{ + char ** Calls; + + // Check AT distributions + + if (strcmp(ATBBS, bbs->Call) == 0) // @BBS = BBS + return TRUE; + + if (ForwardingInfo->ATCalls) + { + Calls = ForwardingInfo->ATCalls; + + while(Calls[0]) + { + if (strcmp(Calls[0], ATBBS) == 0) + return TRUE; + + Calls++; + } + } + return FALSE; +} + +BOOL CheckBBSHList(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char * HRoute) +{ + char ** HRoutes; + int i, j; + + if ((HRoute) && (ForwardingInfo->Haddresses)) + { + // Match on Routes + + HRoutes = ForwardingInfo->Haddresses; + + while(HRoutes[0]) + { + i = strlen(HRoutes[0]) - 1; + j = strlen(HRoute) - 1; + + while ((i >= 0) && (j >= 0)) // Until one string rus out + { + if (HRoutes[0][i--] != HRoute[j--]) // Compare backwards + goto next; + } + + return TRUE; + next: + HRoutes++; + } + } + return FALSE; +} + +#endif + +char * strlop(char * buf, char delim) +{ + // Terminate buf at delim, and return rest of string + + char * ptr; + + if (buf == NULL) return NULL; // Protect + + ptr = strchr(buf, delim); + + if (ptr == NULL) return NULL; + + *(ptr)++=0; + + return ptr; +} diff --git a/Bpq32-HPLaptop-2.c b/Bpq32-HPLaptop-2.c new file mode 100644 index 0000000..0308f62 --- /dev/null +++ b/Bpq32-HPLaptop-2.c @@ -0,0 +1,6603 @@ +/* +Copyright 2001-2022 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 +*/ +// +// 409l Oct 2001 Fix l3timeout for KISS +// +// 409m Oct 2001 Fix Crossband Digi +// +// 409n May 2002 Change error handling on load ext DLL + +// 409p March 2005 Allow Multidigit COM Ports (kiss.c) + +// 409r August 2005 Treat NULL string in Registry as use current directory +// Allow shutdown to close BPQ Applications + +// 409s October 2005 Add DLL:Export entries to API for BPQTNC2 + +// 409t January 2006 +// +// Add API for Perl "GetPerlMsg" +// Add API for BPQ1632 "GETBPQAPI" - returns address of Assembler API routine +// Add Registry Entry "BPQ Directory". If present, overrides "Config File Location" +// Add New API "GetBPQDirectory" - Returns location of config file +// Add New API "ChangeSessionCallsign" - equivalent to "*** linked to" command +// Rename BPQNODES to BPQNODES.dat +// New API "GetAttachedProcesses" - returns number of processes connected. +// Warn if user trys to close Console Window. +// Add Debug entries to record Process Attach/Detach +// Fix recovery following closure of first process + +// 409t Beta 2 February 2006 +// +// Add API Entry "GetPortNumber" +// +// 409u February 2006 +// +// Fix crash if allocate/deallocate called with stream=0 +// Add API to ch +// Display config file path +// Fix saving of Locked Node flag +// Added SAVENODES SYSOP command +// +// 409u 2 March 2006 +// +// Fix SetupBPQDirectory +// Add CopyBPQDirectory (for Basic Programs) +// +// 409u 3 March 2006 +// +// Release streams on DLL unload + +// 409v October 2006 +// +// Support Minimize to Tray for all BPQ progams +// Implement L4 application callsigns + +// 410 November 2006 +// +// Modified to compile with C++ 2005 Express Edition +// Make MCOM MTX MMASK local variables +// +// 410a January 2007 +// +// Add program name to Attach-Detach messages +// Attempt to detect processes which have died +// Fix bug in NETROM and IFrame decode which would cause crash if frame was corrupt +// Add BCALL - origin call for Beacons +// Fix KISS ACKMODE ACK processing +// + +// 410b November 2007 +// +// Allow CTEXT of up to 510, and enforce PACLEN, fragmenting if necessary + +// 410c December 2007 + +// Fix problem with NT introduced in V410a +// Display location of DLL on Console + +// 410d January 2008 + +// Fix crash in DLL Init caused by long path to program +// Invoke Appl2 alias on C command (if enabled) +// Allow C command to be disabled +// Remove debug trap in GETRAWFRAME +// Validate Alias of directly connected node, mainly for KPC3 DISABL Problem +// Move Port statup code out of DLLInit (mainly for perl) +// Changes to allow Load/Unload of bpq32.dll by appl +// CloseBPQ32 API added +// Ext Driver Close routes called +// Changes to release Mutex + +// 410e May 2008 + +// Fix missing SSID on last call of UNPROTO string (CONVTOAX25 in main.asm) +// Fix VCOM Driver (RX Len was 1 byte too long) +// Fix possible crash on L4CODE if L4DACK received out of sequence +// Add basic IP decoding + +// 410f October 2008 + +// Add IP Gateway +// Add Multiport DIGI capability +// Add GetPortDescription API +// Fix potential hangs if RNR lost +// Fix problem if External driver failes to load +// Put pushad/popad round _INITIALISEPORTS (main.asm) +// Add APIs GetApplCallVB and GetPortDescription (mainly for RMS) +// Ensure Route Qual is updated if Port Qual changed +// Add Reload Option, plus menu items for DUMP and SAVENODES + +// 410g December 2008 + +// Restore API Exports BPQHOSTAPIPTR and MONDECODEPTR (accidentally deleted) +// Fix changed init of BPQDirectory (accidentally changed) +// Fix Checks for lost processes (accidentally deleted) +// Support HDLC Cards on W2K and above +// Delete Tray List entries for crashed processes +// Add Option to NODES command to sort by Callsign +// Add options to save or clear BPQNODES before Reconfig. +// Fix Reconfig in Win98 +// Monitor buffering tweaks +// Fix Init for large (>64k) tables +// Fix Nodes count in Stats + +// 410h January 2009 + +// Add Start Minimized Option +// Changes to KISS for WIn98 Virtual COM +// Open \\.\com instead of //./COM +// Extra Dignostics + +// 410i Febuary 2009 + +// Revert KISS Changes +// Save Window positions + +// 410j June 2009 + +// Fix tidying of window List when program crashed +// Add Max Nodes to Stats +// Don't update APPLnALIAS with received NODES info +// Fix MH display in other timezones +// Fix Possible crash when processing NETROM type Zero frames (eg NRR) +// Basic INP3 Stuff +// Add extra diagnostics to Lost Process detection +// Process Netrom Record Route frames. + +// 410k June 2009 + +// Fix calculation of %retries in extended ROUTES display +// Fix corruption of ROUTES table + +// 410l October 2009 + +// Add GetVersionString API call. +// Add GetPortTableEntry API call +// Keep links to neighbouring nodes open + +// Build 2 + +// Fix PE in NOROUTETODEST (missing POP EBX) + +// 410m November 2009 + +// Changes for PACTOR and WINMOR to support the ATTACH command +// Enable INP3 if configured on a route. +// Fix count of nodes in Stats Display +// Overwrite the worst quality unused route if a call is received from a node not in your +// table when the table is full + +// Build 5 + +// Rig Control Interface +// Limit KAM VHF attach and RADIO commands to authorised programs (MailChat and BPQTerminal) + +// Build 6 + +// Fix reading INP3 Flag from BPQNODES + +// Build 7 + +// Add MAXHOPS and MAXRTT config options + +// Build 8 + +// Fix INP3 deletion of Application Nodes. +// Fix GETCALLSIGN for Pactor Sessions +// Add N Call* to display all SSID's of a call +// Fix flow control on Pactor sessions. + +// Build 9 + +// HDLC Support for XP +// Add AUTH routines + +// Build 10 + +// Fix handling commands split over more that one packet. + +// Build 11 + +// Attach cmd changes for winmor disconnecting state +// Option Interlock Winmor/Pactor ports + +// Build 12 + +// Add APPLS export for winmor +// Handle commands ending CR LF + +// Build 13 + +// Incorporate Rig Control in Kernel + +// Build 14 + +// Fix config reload for Rig COntrol + +// 410n March 2010 + +// Implement C P via PACTOR/WINMOR (for Airmail) + +// Build 2 + +// Don't flip SSID bits on Downlink Connect if uplink is Pactor/WINMOR +// Fix resetting IDLE Timer on Pactor/WINMOR sessions +// Send L4 KEEPLI messages based on IDLETIME + +// 410o July 2010 + +// Read bpqcfg.txt instead of .bin +// Support 32 bit MMASK (Allowing 32 Ports) +// Support 32 bit _APPLMASK (Allowing 32 Applications) +// Allow more commands +// Allow longer command aliases +// Fix logic error in RIGControl Port Initialisation (wasn't always raising RTS and DTR +// Clear RIGControl RTS and DTR on close + +// 410o Build 2 August 2010 + +// Fix couple of errors in config (needed APPLICATIONS and BBSCALL/ALIAS/QUAL) +// Fix Kenwood Rig Control when more than one message received at once. +// Save minimzed state of Rigcontrol Window + +// 410o Build 3 August 2010 + +// Fix reporting of set errors in scan to a random session + +// 410o Build 4 August 2010 + +// Change All xxx Ports are in use to no xxxx Ports are available if there are no sessions with _APPLMASK +// Fix validation of TRANSDELAY + +// 410o Build 5 August 2010 + +// Add Repeater Shift and Set Data Mode options to Rigcontrol (for ICOM only) +// Add WINMOR and SCS Pactor mode control option to RigControl +// Extend INFOMSG to 2000 bytes +// Improve Scan freq change lock (check both SCS and WINMOR Ports) + +// 410o Build 6 September 2010 + +// Incorporate IPGateway in main code. +// Fix GetSessionInfo for Pactor/Winmor Ports +// Add Antenna Selection to RigControl +// Allow Bandwidth options on RADIO command line (as well as in Scan definitions) + +// 410o Build 7 September 2010 + +// Move rigconrtol display to driver windows +// Move rigcontrol config to driver config. +// Allow driver and IPGateway config info in bpq32.cfg +// Move IPGateway, AXIP, VKISS, AGW and WINMOR drivers into bpq32.dll +// Add option to reread IP Gateway config. +// Fix Reinit after process with timer closes (error in TellSessions). + +// 410p Build 2 October 2010 + +// Move KAM and SCS drivers to bpq32.dll + +// 410p Build 3 October 2010 + +// Support more than one axip port. + +// 410p Build 4 October 2010 + +// Dynamically load psapi.dll (for 98/ME) + +// 410p Build 5 October 2010 + +// Incorporate TelnetServer +// Fix AXIP ReRead Config +// Report AXIP accept() fails to syslog, not a popup. + +// 410p Build 6 October 2010 + +// Includes HAL support +// Changes to Pactor Drivers disconnect code +// AXIP now sends with source port = dest port, unless overridden by SOURCEPORT param +// Config now checks for duplicate port definitions +// Add Node Map reporting +// Fix WINMOR deferred disconnect. +// Report Pactor PORTCALL to WL2K instead of RMS Applcall + +// 410p Build 7 October 2010 + +// Add In/Out flag to Map reporting, and report centre, not dial +// Write Telnet log to BPQ Directory +// Add Port to AXIP resolver display +// Send Reports to update.g8bpq.net:81 +// Add support for FT100 to Rigcontrol +// Add timeout to Rigcontrol PTT +// Add Save Registry Command + +// 410p Build 8 November 2010 + +// Add NOKEEPALIVES Port Param +// Renumbered for release + +// 410p Build 9 November 2010 + +// Get Bandwith for map report from WL2K Report Command +// Fix freq display for FT100 (was KHz, not MHz) +// Don't try to change SCS mode whilst initialising +// Allow reporting of Lat/Lon as well as Locator +// Fix Telnet Log Name +// Fix starting with Minimized windows when Minimizetotray isn't set +// Extra Program Error trapping in SessionControl +// Fix reporting same freq with different bandwidths at different times. +// Code changes to support SCS Robust Packet Mode. +// Add FT2000 to Rigcontrol +// Only Send CTEXT to connects to Node (not to connects to an Application Call) + +// Released as Build 10 + +// 410p Build 11 January 2011 + +// Fix MH Update for SCS Outgoing Calls +// Add Direct CMS Access to TelnetServer +// Restructure DISCONNECT processing to run in Timer owning process + +// 410p Build 12 January 2011 + +// Add option for Hardware PTT to use a different com port from the scan port +// Add CAT PTT for Yaesu 897 (and maybe others) +// Fix RMS Packet ports busy after restart +// Fix CMS Telnet with MAXSESSIONS > 10 + +// 410p Build 13 January 2011 + +// Fix loss of buffers in TelnetServer +// Add CMS logging. +// Add non - Promiscuous mode option for BPQETHER + +// 410p Build 14 January 2011 + +// Add support for BPQTermTCP +// Allow more that one FBBPORT +// Allow Telnet FBB mode sessions to send CRLF as well as CR on user and pass msgs +// Add session length to CMS Telnet logging. +// Return Secure Session Flag from GetConnectionInfo +// Show Uptime as dd/hh/mm + +// 4.10.16.17 March 2011 + +// Add "Close all programs" command +// Add BPQ Program Directory registry key +// Use HKEY_CURRENT_USER on Vista and above (and move registry if necessary) +// Time out IP Gateway ARP entries, and only reload ax.25 ARP entries +// Add support for SCS Tracker HF Modes +// Fix WL2K Reporting +// Report Version to WL2K +// Add Driver to support Tracker with multiple sessions (but no scanning, wl2k report, etc) + + +// Above released as 5.0.0.1 + +// 5.2.0.1 + +// Add caching of CMS Server IP addresses +// Initialise TNC State on Pactor Dialogs +// Add Shortened (6 digit) AUTH mode. +// Update MH with all frames (not just I/UI) +// Add IPV6 Support for TelnetServer and AXIP +// Fix TNC OK Test for Tracker +// Fix crash in CMS mode if terminal disconnects while tcp commect in progress +// Add WL2K reporting for Robust Packet +// Add option to suppress WL2K reporting for specific frequencies +// Fix Timeband processing for Rig Control +// New Driver for SCS Tracker allowing multiple connects, so Tracker can be used for user access +// New Driver for V4 TNC + +// 5.2.1.3 October 2011 + +// Combine busy detector on Interlocked Ports (SCS PTC, WINMOR or KAM) +// Improved program error logging +// WL2K reporting changed to new format agreed with Lee Inman + +// 5.2.3.1 January 2012 + +// Connects from the console to an APPLCALL or APPLALIAS now invoke any Command Alias that has been defined. +// Fix reporting of Tracker freqs to WL2K. +// Fix Tracker monitoring setup (sending M UISC) +// Fix possible call/application routing error on RP +// Changes for P4Dragon +// Include APRS Digi/IGate +// Tracker monitoring now includes DIGIS +// Support sending UI frames using SCSTRACKER, SCTRKMULTI and UZ7HO drivers +// Include driver for UZ7HO soundcard modem. +// Accept DRIVER as well as DLLNAME, and COMPORT as well as IOADDR in bpq32.cfg. COMPORT is decimal +// No longer supports separate config files, or BPQTELNETSERVER.exe +// Improved flow control for Telnet CMS Sessions +// Fix handling Config file without a newline after last line +// Add non - Promiscuous mode option for BPQETHER +// Change Console Window to a Dialog Box. +// Fix possible corruption and loss of buffers in Tracker drivers +// Add Beacon After Session option to Tracker and UZ7HO Drivers +// Rewrite RigControl and add "Reread Config Command" +// Support User Mode VCOM Driver for VKISS ports + +// 5.2.4.1 January 2012 + +// Remove CR from Telnet User and Password Prompts +// Add Rigcontrol to UZ7HO driver +// Fix corruption of Free Buffer Count by Rigcontol +// Fix WINMOR and V4 PTT +// Add MultiPSK Driver +// Add SendBeacon export for BPQAPRS +// Add SendChatReport function +// Fix check on length of Port Config ID String with trailing spaces +// Fix interlock when Port Number <> Port Slot +// Add NETROMCALL for L3 Activity +// Add support for APRS Application +// Fix Telnet with FBBPORT and no TCPPORT +// Add Reread APRS Config +// Fix switching to Pactor after scanning in normal packet mode (PTC) + +// 5.2.5.1 February 2012 + +// Stop reading Password file. +// Add extra MPSK commands +// Fix MPSK Transparency +// Make LOCATOR command compulsory +// Add MobileBeaconInterval APRS param +// Send Course and Speed when APRS is using GPS +// Fix Robust Packet reporting in PTC driver +// Fix corruption of some MIC-E APRS packets + +// 5.2.6.1 February 2012 + +// Convert to MDI presentation of BPQ32.dll windows +// Send APRS Status packets +// Send QUIT not EXIT in PTC Init +// Implement new WL2K reporting format and include traffic reporting info in CMS signon +// New WL2KREPORT format +// Prevent loops when APPL alias refers to itself +// Add RigControl for Flex radios and ICOM IC-M710 Marine radio + +// 5.2.7.1 + +// Fix opening more thn one console window on Win98 +// Change method of configuring multiple timelots on WL2K reporting +// Add option to update WK2K Sysop Database +// Add Web server +// Add UIONLY port option + +// 5.2.7.2 + +// Fix handling TelnetServer packets over 500 bytes in normal mode + +// 5.2.7.3 + +// Fix Igate handling packets from UIView + +// 5.2.7.4 + +// Prototype Baycom driver. + +// 5.2.7.5 + +// Set WK2K group ref to MARS (3) if using a MARS service code + +// 5.2.7.7 + +// Check for programs calling CloseBPQ32 when holding semaphore +// Try/Except round Status Timer Processing + +// 5.2.7.8 + +// More Try/Except round Timer Processing + +// 5.2.7.9 + +// Enable RX in Baycom, and remove test loopback in tx + +// 5.2.7.10 + +// Try/Except round ProcessHTTPMessage + +// 5.2.7.11 + +// BAYCOM tweaks + +// 5.2.7.13 + +// Release semaphore after program error in Timer Processing +// Check fro valid dest in REFRESHROUTE + + +// Add TNC-X KISSOPTION (includes the ACKMODE bytes in the checksum( + +// Version 5.2.9.1 Sept 2012 + +// Fix using KISS ports with COMn > 16 +// Add "KISS over UDP" driver for PI as a TNC concentrator + +// Version 6.0.1.1 + +// Convert to C for linux portability +// Try to speed up kiss polling + +// Version 6.0.2.1 + +// Fix operation on Win98 +// Fix callsign error with AGWtoBPQ +// Fix PTT problem with WINMOR +// Fix Reread telnet config +// Add Secure CMS signon +// Fix error in cashing addresses of CMS servers +// Fix Port Number when using Send Raw. +// Fix PE in KISS driver if invalid subchannel received +// Fix Orignal address of beacons +// Speed up Telnet port monitoring. +// Add TNC Emulators +// Add CountFramesQueuedOnStream API +// Limit number of frames that can be queued on a session. +// Add XDIGI feature +// Add Winmor Robust Mode switching for compatibility with new Winmor TNC +// Move most APRS code from BPQAPRS to here +// Stop corruption caused by overlong KISS frames + +// Version 6.0.3.1 + +// Add starting/killing WINMOR TNC on remote host +// Fix Program Error when APRS Item or Object name is same as call of reporting station +// Dont digi a frame that we have already digi'ed +// Add ChangeSessionIdleTime API +// Add WK2KSYSOP Command +// Add IDLETIME Command +// Fix Errors in RELAYAPPL processing +// Fix PE cauaed by invalid Rigcontrol Line + +// Version 6.0.4.1 + +// Add frequency dependent autoconnect appls for SCS Pactor +// Fix DED Monitoring of I and UI with no data +// Include AGWPE Emulator (from AGWtoBPQ) +// accept DEL (Hex 7F) as backspace in Telnet +// Fix re-running resolver on re-read AXIP config +// Speed up processing, mainly for Telnet Sessions +// Fix APRS init on restart of bpq32.exe +// Change to 2 stop bits +// Fix scrolling of WINMOR trace window +// Fix Crash when ueing DED TNC Emulator +// Fix Disconnect when using BPQDED2 Driver with Telnet Sessions +// Allow HOST applications even when CMS option is disabled +// Fix processing of APRS DIGIMAP command with no targets (didn't suppress default settings) + +// Version 6.0.5.1 January 2014 + +// Add UTF8 conversion mode to Telnet (converts non-UTF-8 chars to UTF-8) +// Add "Clear" option to MH command +// Add "Connect to RMS Relay" Option +// Revert to one stop bit on serial ports, explictly set two on FT2000 rig control +// Fix routing of first call in Robust Packet +// Add Options to switch input source on rigs with build in soundcards (sor far only IC7100 and Kenwood 590) +// Add RTS>CAT PTT option for Sound Card rigs +// Add Clear Nodes Option (NODE DEL ALL) +// SCS Pactor can set differeant APPLCALLS when scanning. +// Fix possible Scan hangup after a manual requency change with SCS Pactor +// Accept Scan entry of W0 to disable WINMOR on that frequency +// Fix corruption of NETROMCALL by SIMPLE config command +// Enforce Pactor Levels +// Add Telnet outward connect +// Add Relay/Trimode Emulation +// Fix V4 Driver +// Add PTT Mux +// Add Locked ARP Entries (via bpq32.cfg) +// Fix IDLETIME node command +// Fix STAY param on connect +// Add STAY option to Attach and Application Commands +// Fix crash on copying a large AXIP MH Window +// Fix possible crash when bpq32.exe dies +// Fix DIGIPORT for UI frames + +// Version 6.0.6.1 April 2014 + +// FLDigi Interface +// Fix "All CMS Servers are inaccessible" message so Mail Forwarding ELSE works. +// Validate INP3 messages to try to prevent crash +// Fix possible crash if an overlarge KISS frame is received +// Fix error in AXR command +// Add LF to Telnet Outward Connect signin if NEEDLF added to connect line +// Add CBELL to TNC21 emulator +// Add sent objects and third party messages to APRS Dup List +// Incorporate UIUtil +// Use Memory Mapped file to pass APRS info to BPQAPRS, and process APRS HTTP in BPQ32 +// Improvements to FLDIGI interlocking +// Fix TNC State Display for Tracker +// Cache CMS Addresses on LinBPQ +// Fix count error on DED Driver when handling 256 byte packets +// Add basic SNMP interface for MRTG +// Fix memory loss from getaddrinfo +// Process "BUSY" response from Tracker +// Handle serial port writes that don't accept all the data +// Trap Error 10038 and try to reopen socket +// Fix crash if overlong command line received + +// Version 6.0.7.1 Aptil 2014 +// Fix RigContol with no frequencies for Kenwood and Yaesu +// Add busy check to FLDIGI connects + +// Version 6.0.8.1 August 2014 + +// Use HKEY_CURRENT_USER on all OS versions +// Fix crash when APRS symbol is a space. +// Fixes for FT847 CAT +// Fix display of 3rd byte of FRMR +// Add "DEFAULT ROBUST" and "FORCE ROBUST" commands to SCSPactor Driver +// Fix possible memory corruption in WINMOR driver +// Fix FT2000 Modes +// Use new WL2K reporting system (Web API Based) +// APRS Server now cycles through hosts if DNS returns more than one +// BPQ32 can now start and stop FLDIGI +// Fix loss of AXIP Resolver when running more than one AXIP port + +// Version 6.0.9.1 November 2014 + +// Fix setting NOKEEPALIVE flag on route created from incoming L3 message +// Ignore NODES from locked route with quality 0 +// Fix seting source port in AXIP +// Fix Dual Stack (IPV4/V6) on Linux. +// Fix RELAYSOCK if IPv6 is enabled. +// Add support for FT1000 +// Fix hang when APRS Messaging packet received on RF +// Attempt to normalize Node qualies when stations use widely differing Route qualities +// Add NODES VIA command to display nodes reachable via a specified neighbour +// Fix applying "DisconnectOnClose" setting on HOST API connects (Telnet Server) +// Fix buffering large messages in Telnet Host API +// Fix occasional crash in terminal part line processing +// Add "NoFallback" command to Telnet server to disable "fallback to Relay" +// Improved support for APPLCALL scanning with Pactor +// MAXBUFFS config statement is no longer needed. +// Fix USEAPPLCALLS with Tracker when connect to APPLCALL fails +// Implement LISTEN and CQ commands +// FLDIGI driver can now start FLDIGI on a remote system. +// Add IGNOREUNLOCKEDROUTES parameter +// Fix error if too many Telnet server connections + +// Version 6.0.10.1 Feb 2015 + +// Fix crash if corrupt HTML request received. +// Allow SSID's of 'R' and 'T' on non-ax.25 ports for WL2K Radio Only network. +// Make HTTP server HTTP Version 1.1 complient - use persistent conections and close after 2.5 mins +// Add INP3ONLY flag. +// Fix program error if enter UNPROTO without a destination path +// Show client IP address on HTTP sessions in Telnet Server +// Reduce frequency and number of attempts to connect to routes when Keepalives or INP3 is set +// Add FT990 RigControl support, fix FT1000MP support. +// Support ARMV5 processors +// Changes to support LinBPQ APRS Client +// Add IC7410 to supported Soundcard rigs +// Add CAT PTT to NMEA type (for ICOM Marine Radios_ +// Fix ACKMODE +// Add KISS over TCP +// Support ACKMode on VKISS +// Improved reporting of configuration file format errors +// Experimental driver to support ARQ sessions using UI frames + +// Version 6.0.11.1 September 2015 + +// Fixes for IPGateway configuration and Virtual Circuit Mode +// Separate Portmapper from IPGateway +// Add PING Command +// Add ARDOP Driver +// Add basic APPLCALL support for PTC-PRO/Dragon 7800 Packet (using MYALIAS) +// Add "VeryOldMode" for KAM Version 5.02 +// Add KISS over TCP Slave Mode. +// Support Pactor and Packet on P4Dragon on one port +// Add "Remote Staton Quality" to Web ROUTES display +// Add Virtual Host option for IPGateway NET44 Encap +// Add NAT for local hosts to IPGateway +// Fix setting filter from RADIO command for IC7410 +// Add Memory Channel Scanning for ICOM Radios +// Try to reopen Rig Control port if it fails (could be unplugged USB) +// Fix restoring position of Monitor Window +// Stop Codec on Winmor and ARDOP when an interlocked port is attached (instead of listen false) +// Support APRS beacons in RP mode on Dragon// +// Change Virtual MAC address on IPGateway to include last octet of IP Address +// Fix "NOS Fragmentation" in IP over ax.25 Virtual Circuit Mode +// Fix sending I frames before L2 session is up +// Fix Flow control on Telnet outbound sessions. +// Fix reporting of unterminatred comments in config +// Add option for RigControl to not change mode on FT100/FT990/FT1000 +// Add "Attach and Connect" for Telnet ports + +// Version 6.0.12.1 November 2015 + +// Fix logging of IP addresses for connects to FBBPORT +// Allow lower case user and passwords in Telnet "Attach and Connect" +// Fix possible hang in KISS over TCP Slave mode +// Fix duplicating LinBPQ process if running ARDOP fails +// Allow lower case command aliases and increase alias length to 48 +// Fix saving long IP frames pending ARP resolution +// Fix dropping last entry from a RIP44 message. +// Fix displaying Digis in MH list +// Add port name to Monitor config screen port list +// Fix APRS command display filter and add port filter +// Support port names in BPQTermTCP Monitor config +// Add FINDBUFFS command to dump lost buffers to Debugview/Syslog +// Buffer Web Mgmt Edit Config output +// Add WebMail Support +// Fix not closing APRS Send WX file. +// Add RUN option to APRS Config to start APRS Client +// LinBPQ run FindLostBuffers and exit if QCOUNT < 5 +// Close and reopen ARDOP connection if nothing received for 90 secs +// Add facility to bridge traffic between ports (similar to APRS Bridge but for all frame types) +// Add KISSOPTION TRACKER to set SCS Tracker into KISS Mode + +// 6.0.13.1 + +// Allow /ex to exit UNPROTO mode +// Support ARQBW commands. +// Support IC735 +// Fix sending ARDOP beacons after a busy holdoff +// Enable BPQDED driver to beacon via non-ax.25 ports. +// Fix channel number in UZ7HO monitoring +// Add SATGate mode to APRSIS Code. +// Fix crash caused by overlong user name in telnet logon +// Add option to log L4 connects +// Add AUTOADDQuiet mode to AXIP. +// Add EXCLUDE processing +// Support WinmorControl in UZ7HO driver and fix starting TNC on Linux +// Convert calls in MAP entries to upper case. +// Support Linux COM Port names for APRS GPS +// Fix using NETROM serial protocol on ASYNC Port +// Fix setting MYLEVEL by scanner after manual level change. +// Add DEBUGLOG config param to SCS Pactor Driver to log serial port traffic +// Uue #myl to set SCS Pactor MYLEVEL, and add checklevel command +// Add Multicast RX interface to FLDIGI Driver +// Fix processing application aliases to a connect command. +// Fix Buffer loss if radio connected to PTC rig port but BPQ not configured to use it +// Save backups of bpq32.cfg when editing with Web interface and report old and new length +// Add DD command to SCS Pactor, and use it for forced disconnect. +// Add ARDOP mode select to scan config +// ARDOP changes for ARDOP V 0.5+ +// Flip SSID bits on UZ7HO downlink connects + + +// Version 6.0.14.1 + +// Fix Socket leak in ARDOP and FLDIGI drivers. +// Add option to change CMS Server hostname +// ARDOP Changes for 0.8.0+ +// Discard Terminal Keepalive message (two nulls) in ARDOP command hander +// Allow parameters to be passed to ARDOP TNC when starting it +// Fix Web update of Beacon params +// Retry connects to KISS ports after failure +// Add support for ARDOP Serial Interface Native mode. +// Fix gating APRS-IS Messages to RF +// Fix Beacons when PORTNUM used +// Make sure old monitor flag is cleared for TermTCP sessions +// Add CI-V antenna control for IC746 +// Don't allow ARDOP beacons when connected +// Add support for ARDOP Serial over I2C +// Fix possble crash when using manual RADIO messages +// Save out of sequence L2 frames for possible reuse after retry +// Add KISS command to send KISS control frame to TNC +// Stop removing unused digis from packets sent to APRS-IS + +// Processing of ARDOP PING and PINGACK responses +// Handle changed encoding of WL2K update responses. +// Allow anonymous logon to telnet +// Don't use APPL= for RP Calls in Dragon Single mode. +// Add basic messaging page to APRS Web Server +// Add debug log option to SCSTracker and TrkMulti Driver +// Support REBOOT command on LinBPQ +// Allow LISTEN command on all ports that support ax.25 monitoring + +// Version 6.0.15.1 Feb 2018 + +// partial support for ax.25 V2.2 +// Add MHU and MHL commands and MH filter option +// Fix scan interlock with ARDOP +// Add Input source seiect for IC7300 +// Remove % transparency from web terminal signon message +// Fix L4 Connects In count on stats +// Fix crash caused by corrupt CMSInfo.txt +// Add Input peaks display to ARDOP status window +// Add options to show time in local and distances in KM on APRS Web pages +// Add VARA support +// Fix WINMOR Busy left set when port Suspended +// Add ARDOP-Packet Support +// Add Antenna Switching for TS 480 +// Fix possible crash in Web Terminal +// Support different Code Pages on Console sessions +// Use new Winlink API interface (api.winlink.org) +// Support USB/ACC switching on TS590SG +// Fix scanning when ARDOP or WINMOR is used without an Interlocked Pactor port. +// Set NODECALL to first Application Callsign if NODE=0 and BBSCALL not set. +// Add RIGCONTROL TUNE and POWER commands for some ICOM and Kenwwod rigs +// Fix timing out ARDOP PENDING Lock +// Support mixed case WINLINK Passwords +// Add TUNE and POWER Rigcontol Commands for some radios +// ADD LOCALTIME and DISPKM options to APRS Digi/Igate + +// 6.0.16.1 March 2018 + +// Fix Setting data mode and filter for IC7300 radios +// Add VARA to WL2KREPORT +// Add trace to SCS Tracker status window +// Fix possible hang in IPGATEWAY +// Add BeacontoIS parameter to APRSDIGI. Allows you to stop sending beacons to APRS-IS. +// Fix sending CTEXT on WINMOR sessions + +// 6.0.17.1 November 2018 + +// Change WINMOR Restart after connection to Restart after Failure and add same option to ARDOP and VARA +// Add Abort Connection to WINMOR and VARA Interfaces +// Reinstate accidentally removed CMS Access logging +// Fix MH CLEAR +// Fix corruption of NODE table if NODES received from station with null alias +// Fix loss of buffer if session closed with something in PARTCMDBUFFER +// Fix Spurious GUARD ZONE CORRUPT message in IP Code. +// Remove "reread bpq32.cfg and reconfigure" menu options +// Add support for PTT using CM108 based soundcard interfaces +// Datestamp Telnet log files and delete old Telnet and CMSAcces logs + +// 6.0.18.1 January 2019 + +// Fix validation of NODES broadcasts +// Fix HIDENODES +// Check for failure to reread config on axip reconfigure +// Fix crash if STOPPORT or STARTPORT used on KISS over TCP port +// Send Beacons from BCALL or PORTCALL if configured +// Fix possible corruption of last entry in MH display +// Ensure RTS/DTR is down when opening PTT Port +// Remove RECONFIG command +// Preparations for 64 bit version + +// 6.0.19 Sept 2019 +// Fix UZ7HO interlock +// Add commands to set Centre Frequency and Modem with UZ7HO Soundmodem (on Windows only) +// Add option to save and restore MH lists and SAVEMH command +// Add Frequency (if known) to UZ7HO MH lists +// Add Gateway option to Telnet for PAT +// Try to fix SCS Tracker recovery +// Ensure RTS/DTR is down on CAT port if using that line for PTT +// Experimental APRS Messaging in Kernel +// Add Rigcontrol on remote PC's using WinmorControl +// ADD VARAFM and VARAFM96 WL2KREPORT modes +// Fix WL2K sysop update for new Winlink API +// Fix APRS when using PORTNUM higher than the number of ports +// Add Serial Port Type +// Add option to linbpq to log APRS-IS messages. +// Send WL2K Session Reports +// Drop Tunneled Packets from 44.192 - 44.255 +// Log incoming Telnet Connects +// Add IPV4: and IPV6: overrides on AXIP Resolver. +// Add SessionTimeLimit to HF sessions (ARDOP, SCSPactor, WINMOR, VARA) +// Add RADIO FREQ command to display current frequency + +// 6.0.20 April 2020 + +// Trap and reject YAPP file transfer request. +// Fix possible overrun of TCP to Node Buffer +// Fix possible crash if APRS WX file doesn't have a terminating newline +// Change communication with BPQAPRS.exe to restore old message popup behaviour +// Preparation for 64 bit version +// Improve flow control on SCS Dragon +// Fragment messages from network links to L2 links with smaller paclen +// Change WL2K report rate to once every two hours +// Add PASS, CTEXT and CMSG commands and Stream Switch support to TNC2 Emulator +// Add SessionTimeLimit command to HF drivers (ARDOP, SCSPactor, WINMOR, VARA) +// Add links to Ports Web Manangement Page to open individual Driver windows +// Add STOPPORT/STARTPORT support to ARDOP, KAM and SCSPactor drivers +// Add CLOSE and OPEN RADIO command so Rigcontrol port can be freed fpr other use. +// Don't try to send WL2K Traffic report if Internet is down +// Move WL2K Traffic reporting to a separate thread so it doesn't block if it can't connect to server +// ADD AGWAPPL config command to set application number. AGWMASK is still supported +// Register Node Alias with UZ7HO Driver +// Register calls when UZ7HO TNC Restarts and at intervals afterwards +// Fix crash when no IOADDR or COMPORT in async port definition +// Fix Crash with Paclink-Unix when parsing ; VE7SPR-10 DE N7NIX QTC 1 +// Only apply BBSFLAG=NOBBS to APPPLICATION 1 +// Add RIGREONFIG command +// fix APRS RECONFIG on LinBPQ +// Fix Web Terminal scroll to end problem on some browsers +// Add PTT_SETS_INPUT option for IC7600 +// Add TELRECONFIG command to reread users or whole config +// Enforce PACLEN on UZ7HO ports +// Fix PACLEN on Command Output. +// Retry axip resolver if it fails at startup +// Fix AGWAPI connect via digis +// Fix Select() for Linux in MultiPSK, UZ7HO and V4 drivers +// Limit APRS OBJECT length to 80 chars +// UZ7HO disconnect incoming call if no free streams +// Improve response to REJ (no F) followed by RR (F). +// Try to prevent more than MAXFRAME frames outstanding when transmitting +// Allow more than one instance of APRS on Linux +// Stop APRS digi by originating station +// Send driver window trace to main monitor system +// Improve handling of IPOLL messages +// Fix setting end of address bit on dest call on connects to listening sessions +// Set default BBS and CHAT application number and number of streams on LinBPQ +// Support #include in bpq32.cfg processing + +// Version 6.0.21 14 December 2020 + +// Fix occasional missing newlines in some node command reponses +// More 64 bit fixes +// Add option to stop setting PDUPLEX param in SCSPACTOR +// Try to fix buffer loss +// Remove extra space from APRS position reports +// Suppress VARA IAMALIVE messages +// Add display and control of QtSoundModem modems +// Only send "No CMS connection available" message if fallbacktorelay is set. +// Add HAMLIB backend and emulator support to RIGCONTROL +// Ensure all beacons are sent even with very short beacon intervals +// Add VARA500 WL2K Reporting Mode +// Fix problem with prpcessing frame collector +// Temporarily disable L2 and L4 collectors till I can find problem +// Fix possible problem with interactive RADIO commands not giving a response, +// Incease maximum length of NODE command responses to handle maximum length INFO message, +// Allow WL2KREPORT in CONFIG section of UZ7HO port config. +// Fix program error in processing hamlib frame +// Save RestartAfterFailure option for VARA +// Check callsign has a winlink account before sending WL2KREPORT messages +// Add Bandwidth control to VARA scanning +// Renable L2 collector +// Fix TNCPORT reconnect on Linux +// Add SecureTelnet option to limit telnet outward connect to sysop mode sessions or Application Aliases +// Add option to suppress sending call to application in Telnet HOST API +// Add FT991A support to RigControl +// Use background.jpg for Edit Config page +// Send OK response to SCS Pactor commands starting with # +// Resend ICOM PTT OFF command after 30 seconds +// Add WXCall to APRS config +// Fixes for AEAPactor +// Allow PTTMUX to use real or com0com com ports +// Fix monitoring with AGW Emulator +// Derive approx position from packets on APRS ports with a valid 6 char location +// Fix corruption of APRS message lists if the station table fills up. +// Don't accept empty username or password on Relay sessions. +// Fix occasional empty Nodes broadcasts +// Add Digis to UZ7HO Port MH list +// Add PERMITTEDAPPLS port param +// Fix WK2K Session Record Reporting for Airmail and some Pactor Modes. +// Fix handling AX/IP (proto 93) frames +// Fix possible corruption sending APRS messages +// Allow Telnet connections to be made using Connect command as well as Attach then Connect +// Fix Cancel Sysop Signin +// Save axip resolver info and restore on restart +// Add Transparent mode to Telnet Server HOST API +// Fix Tracker driver if WL2KREPRRT is in main config section +// SNMP InOctets count corrected to include all frames and encoding of zero values fixed. +// Change IP Gateway to exclude handling bits of 44 Net sold to Amazon +// Fix crash in Web terminal when processing very long lines + +// Version 6.0.22.1 August 2021 + +// Fix bug in KAM TNCEMULATOR +// Add WinRPR Driver (DED over TCP) +// Fix handling of VARA config commands FM1200 and FM9600 +// Improve Web Termanal Line folding +// Add StartTNC to WinRPR driver +// Add support for VARA2750 Mode +// Add support for VARA connects via a VARA Digipeater +// Add digis to SCSTracker and WinRPR MHeard +// Separate RIGCONTROL config from PORT config and add RigControl window +// Fix crash when a Windows HID device doesn't have a product_string +// Changes to VARA TNC connection and restart process +// Trigger FALLBACKTORELAY if attempt to connect to all CMS servers fail. +// Fix saving part lines in adif log and Winlink Session reporting +// Add port specific CTEXT +// Add FRMR monitoring to UZ7HO driver +// Add audio input switching for IC7610 +// Include Rigcontrol Support for IC-F8101E +// Process any response to KISS command +// Fix NODE ADD command +// Add noUpdate flag to AXIP MAP +// Fix clearing NOFALLBACK flag in Telnet Server +// Allow connects to RMS Relay running on another host +// Allow use of Power setting in Rigcontol scan lines for Kenwood radios +// Prevent problems caused by using "CMS" as a Node Alias +// Include standard APRS Station pages in code +// Fix VALIDCALLS processing in HF drivers +// Send Netrom Link reports to Node Map +// Add REALTELNET mode to Telnet Outward Connect +// Fix using S (Stay) parameter on Telnet connects when using CMDPORT and C HOST +// Add Default frequency to rigcontrol to set a freq/mode to return to after a connection +// Fix long (> 60 seconds) scan intervals +// Improved debugging of stuck semaphores +// Fix potential securiby bug in BPQ Web server +// Send Chat Updates to chatupdate.g8bpq.net port 81 +// Add ReportRelayTraffic to Telnet config to send WL2K traffic reports for connections to RELAY +// Add experimental Mode reporting +// Add SendTandRtoRelay param to SCS Pactor, ARDOP and VARA drivers to divert calls to CMS for -T and -R to RELAY +// Add UPNP Support + +// Version 6.0.23.1 June 2022 + +// Add option to control which applcalls are enabled in VARA +// Add support for rtl_udp to Rig Control +// Fix Telnet Auto Conneect to Application when using TermTCP or Web Terminal +// Allow setting css styles for Web Terminal +// And Kill TNC and Kill and Restart TNC commands to Web Driver Windows +// More flexible RigControl for split frequency operation, eg for QO100 +// Increase stack size for ProcessHTMLMessage (.11) +// Fix HTML Content-Type on images (.12) +// Add AIS and ADSB Support (.13) +// Compress web pages (.14) +// Change minidump routine and close after program error (.15) +// Add RMS Relay SYNC Mode (.17) +// Changes for compatibility with Winlink Hybrid +// Add Rigcontrol CMD feature to Yaesu code (21) +// More diagnostic code +// Trap potential buffer overrun in ax/tcp code +// Fix possible hang in UZ7HO driver if connect takes a long time to succeed or fail +// Add FLRIG as backend for RigControl (.24) +// Fix bug in compressing some management web pages +// Fix bugs in AGW Emulator (.25) +// Add more PTT_Sets_Freq options for split frequency working (.26) +// Allow RIGCONTROL using Radio Number (Rnn) as well as Port (.26) +// Fix Telnet negotiation and backspace processing (.29) +// Fix VARA Mode change when scanning (.30) +// Add Web Mgmt Log Display (.33) +// Fix crash when connecting to RELAY when CMS=0 (.36) +// Send OK to user for manual freq changes with hamlib or flrig +// Fix Rigcontrol leaving port disabled when using an empty timeband +// Fix processing of backspace in Telnet character processing (.40) +// Increase max size of connect script +// Fix HAMLIB Slave Thread control +// Add processing of VARA mode responses and display of VARA Mode (41) +// Fix crash when VARA session aborted on LinBPQ (43) +// Fix handling port selector (2:call or p2 call) on SCS PTC packet ports (44) +// Include APRS Map web page +// Add Enable/Disable to KAMPACTOR scan control (use P0 or P1) (45) +// Add Basic DRATS interface (46) +// Fix MYCALLS on VARA (49) +// Add FreeData driver (51) +// Add additonal Rigcontrol options for QO100 (51) +// Set Content-Type: application/pdf for pdf files downloaded via web interface (51) +// Fix sending large compressed web messages (52) +// Fix freq display when using flrig or hamlib backends to rigcontrol +// Change VARA Driver to send ABORT when Session Time limit expires +// Add Chat Log to Web Logs display +// Fix possible buffer loss in RigControl +// Allow hosts on local lan to be treated as secure +// Improve validation of data sent to Winlink SessionAdd API call +// Add support for FreeDATA modem. +// Add GetLOC API Call +// Change Leaflet link in aprs map. +// Add Connect Log (64) +// Fix crash when Resolve CMS Servers returns ipv6 addresses +// Fix Reporting P4 sessions to Winlink (68) +// Add support for FreeBSD (68) +// Fix Rigcontrol PTCPORT (69) +// Set TNC Emulator sessions as secure (72) +// Fix not always detecting loss of FLRIG (73) +// Add ? and * wildcards to NODES command (74) +// Add Port RADIO config parameter (74) + +// Version 6.0.24.1 ?? + +// Apply NODES command wildcard to alias as well a call (2) +// Add STOPPORT/STARTPORT to VARA Driver (2) +// Add bandwidth setting to FLRIG interface. (2) +// Fix N VIA (3) +// Fix NODE ADD and NODE DEL (4) +// Improvements to FLRIG Rigcontrol backend (6, 7) +// Fix UZ7HO Window Title Update +// Reject L2 calls with a blank from call (8) +// Update WinRPR Window header with BPQ Port Description (8) +// Fix error in blank call code (9) +// Change web buttons to white on black when pressed (10) +// Fix Port CTEXT paclen on Tracker and WinRPR drivers (11) +// Add RADIO PTT command for testing PTT (11) +// Fix using APPLCALLs on SCSTracker RP call (12) +// Add Rigcntol Web Page (13) +// Fix scan bandwidth change with ARDOPOFDM (13) +// Fix setting Min Pactor Level in SCSPactor (13) +// Fix length of commands sent via CMD_TO_APPL flag (14) +// Add filter by quality option to N display (15) +// Fix VARA Mode reporting to WL2K (16) +// Add FLRIG POWER and TUNE commands (18) +// Fix crash when processing "C " without a call in UZ7HO, FLDIGI or MULTIPSK drivers (19) +// FLDIGI improvements (19) +// Fix hang at start if Telnet port Number > Number of Telnet Streams (20) +// Fix processing C command if first port driver is SCSPACTROR (20) +// Fix crash in UZ7HO driver if bad raw frame received (21) +// Fix using FLARQ chat mode with FLDIGI ddriover (22) +// Fixed to KISSHF driver (23) +// Fix for application buffer loss (24) +// Add Web Sockets auto-refresh option for Webmail index page (25) +// Fix FREEDATA driver for compatibility with FreeData TNC version 0.6.4-alpha.3 (25) +// Add SmartID for bridged frames - Send ID only if packets sent recently (26) +// Add option to save and restore received APRS messages (27) +// Add mechanism to run a user program on certain events (27) +// If BeacontoIS is zero don't Gate any of our messages received locally to APRS-IS (28) +// Add Node Help command (28) +// Add APRS Igate RXOnly option (29) +// Fix RMC message handling with prefixes other than GP (29) +// Add GPSD support for APRS (30) +// Attempt to fix Tracker/WinRPR reconnect code (30) +// Changes to FreeDATA - Don't use deamon and add txlevel and send text commands (31) +// Fix interactive commands in tracker driver (33) +// Fix SESSIONTIMELIMIT processing +// Add STOPPORT/STARTPORT for UZ7HO driver +// Fix processing of extended QtSM 'g' frame (36) +// Allow setting just freq on Yaseu rigs (37) +// Enable KISSHF driver on Linux (40) +// Allow AISHOST and ADSBHOST to be a name as well as an address (41) +// Fix Interlock of incoming UZ7HO connections (41) +// Disable VARA Actions menu if not sysop (41) +// Fix Port CTEXT on UZ7HO B C or D channels (42) +// Fix repeated trigger of SessionTimeLimit (43) +// Fix posible memory corruption in UpateMH (44) +// Add PHG to APRS beacons (45) +// Dont send DM to stations in exclude list(45) +// Improvements to RMS Relay SYNC Mode (46) +// Check L4 connects against EXCLUDE list (47) +// Add vaidation of LOC in WL2K Session Reports (49) +// Change gpsd support for compatibility with Share Gps (50) + + +#define CKernel + +#include "Versions.h" + +#define _CRT_SECURE_NO_DEPRECATE + +#pragma data_seg("_BPQDATA") + +#include "time.h" +#include "stdio.h" +#include + +#include "compatbits.h" + +#include "AsmStrucs.h" + +#include "SHELLAPI.H" +#include "kernelresource.h" + +#include +#include +#include "BPQTermMDI.h" + +#include "GetVersion.h" + +#define DllImport __declspec( dllimport ) + +#define CheckGuardZone() _CheckGuardZone(__FILE__, __LINE__) +void _CheckGuardZone(char * File, int Line); + +#define CHECKLOADED 0 +#define SETAPPLFLAGS 1 +#define SENDBPQFRAME 2 +#define GETBPQFRAME 3 +#define GETSTREAMSTATUS 4 +#define CLEARSTREAMSTATUS 5 +#define BPQCONDIS 6 +#define GETBUFFERSTATUS 7 +#define GETCONNECTIONINFO 8 +#define BPQRETURN 9 // GETCALLS +//#define RAWTX 10 //IE KISS TYPE DATA +#define GETRAWFRAME 11 +#define UPDATESWITCH 12 +#define BPQALLOC 13 +//#define SENDNETFRAME 14 +#define GETTIME 15 + +extern short NUMBEROFPORTS; +extern long PORTENTRYLEN; +extern long LINKTABLELEN; +extern struct PORTCONTROL * PORTTABLE; +extern void * FREE_Q; +extern UINT APPL_Q; // Queue of frames for APRS Appl + +extern TRANSPORTENTRY * L4TABLE; +extern UCHAR NEXTID; +extern DWORD MAXCIRCUITS; +extern DWORD L4DEFAULTWINDOW; +extern DWORD L4T1; +extern APPLCALLS APPLCALLTABLE[]; +extern char * APPLS; + +extern struct WL2KInfo * WL2KReports; + +extern int NUMBEROFTNCPORTS; + + +void * VCOMExtInit(struct PORTCONTROL * PortEntry); +void * AXIPExtInit(struct PORTCONTROL * PortEntry); +void * SCSExtInit(struct PORTCONTROL * PortEntry); +void * AEAExtInit(struct PORTCONTROL * PortEntry); +void * KAMExtInit(struct PORTCONTROL * PortEntry); +void * HALExtInit(struct PORTCONTROL * PortEntry); +void * ETHERExtInit(struct PORTCONTROL * PortEntry); +void * AGWExtInit(struct PORTCONTROL * PortEntry); +void * WinmorExtInit(EXTPORTDATA * PortEntry); +void * TelnetExtInit(EXTPORTDATA * PortEntry); +//void * SoundModemExtInit(EXTPORTDATA * PortEntry); +void * TrackerExtInit(EXTPORTDATA * PortEntry); +void * TrackerMExtInit(EXTPORTDATA * PortEntry); +void * V4ExtInit(EXTPORTDATA * PortEntry); +void * UZ7HOExtInit(EXTPORTDATA * PortEntry); +void * MPSKExtInit(EXTPORTDATA * PortEntry); +void * FLDigiExtInit(EXTPORTDATA * PortEntry); +void * UIARQExtInit(EXTPORTDATA * PortEntry); +void * SerialExtInit(EXTPORTDATA * PortEntry); +void * ARDOPExtInit(EXTPORTDATA * PortEntry); +void * VARAExtInit(EXTPORTDATA * PortEntry); +void * KISSHFExtInit(EXTPORTDATA * PortEntry); +void * WinRPRExtInit(EXTPORTDATA * PortEntry); +void * HSMODEMExtInit(EXTPORTDATA * PortEntry); +void * FreeDataExtInit(EXTPORTDATA * PortEntry); + +extern char * ConfigBuffer; // Config Area +VOID REMOVENODE(dest_list * DEST); +DllExport int ConvFromAX25(unsigned char * incall,unsigned char * outcall); +DllExport int ConvToAX25(unsigned char * incall,unsigned char * outcall); +VOID GetUIConfig(); +VOID ADIFWriteFreqList(); +void SaveAIS(); +void initAIS(); +void initADSB(); + +extern BOOL ADIFLogEnabled; + +int CloseOnError = 0; + +char UIClassName[]="UIMAINWINDOW"; // the main window class name + +HWND UIhWnd; + +extern char AUTOSAVE; +extern char AUTOSAVEMH; + +extern char MYNODECALL; // 10 chars,not null terminated + +extern QCOUNT; +extern BPQVECSTRUC BPQHOSTVECTOR[]; +#define BPQHOSTSTREAMS 64 +#define IPHOSTVECTOR BPQHOSTVECTOR[BPQHOSTSTREAMS + 3] + +extern char * CONFIGFILENAME; + +DllExport BPQVECSTRUC * BPQHOSTVECPTR; + +extern int DATABASESTART; + +extern struct ROUTE * NEIGHBOURS; +extern int ROUTE_LEN; +extern int MAXNEIGHBOURS; + +extern struct DEST_LIST * DESTS; // NODE LIST +extern int DEST_LIST_LEN; +extern int MAXDESTS; // MAX NODES IN SYSTEM + +extern struct _LINKTABLE * LINKS; +extern int LINK_TABLE_LEN; +extern int MAXLINKS; + + +extern int BPQHOSTAPI(); +extern int INITIALISEPORTS(); +extern int TIMERINTERRUPT(); +extern int MONDECODE(); +extern int BPQMONOPTIONS(); +extern char PWTEXT[]; +extern char PWLen; + +extern int FINDFREEDESTINATION(); +extern int RAWTX(); +extern int RELBUFF(); +extern int SENDNETFRAME(); +extern char MYCALL[]; // 7 chars, ax.25 format + +extern HWND hIPResWnd; +extern BOOL IPMinimized; + +extern int NODESINPROGRESS; +extern VOID * CURRENTNODE; + + +BOOL Start(); + +VOID SaveWindowPos(int port); +VOID SaveAXIPWindowPos(int port); +VOID SetupRTFHddr(); +DllExport VOID APIENTRY CreateNewTrayIcon(); +int DoReceivedData(int Stream); +int DoStateChange(int Stream); +int DoMonData(int Stream); +struct ConsoleInfo * CreateChildWindow(int Stream, BOOL DuringInit); +CloseHostSessions(); +SaveHostSessions(); +VOID SaveBPQ32Windows(); +VOID CloseDriverWindow(int port); +VOID CheckWL2KReportTimer(); +VOID SetApplPorts(); +VOID WriteMiniDump(); +VOID FindLostBuffers(); +BOOL InitializeTNCEmulator(); +VOID TNCTimer(); +char * strlop(char * buf, char delim); + +DllExport int APIENTRY Get_APPLMASK(int Stream); +DllExport int APIENTRY GetStreamPID(int Stream); +DllExport int APIENTRY GetApplFlags(int Stream); +DllExport int APIENTRY GetApplNum(int Stream); +DllExport BOOL APIENTRY GetAllocationState(int Stream); +DllExport int APIENTRY GetMsg(int stream, char * msg, int * len, int * count ); +DllExport int APIENTRY RXCount(int Stream); +DllExport int APIENTRY TXCount(int Stream); +DllExport int APIENTRY MONCount(int Stream); +DllExport int APIENTRY GetCallsign(int stream, char * callsign); +DllExport VOID APIENTRY RelBuff(VOID * Msg); +void SaveMH(); +void DRATSPoll(); + +#define C_Q_ADD(s, b) _C_Q_ADD(s, b, __FILE__, __LINE__); +int _C_Q_ADD(VOID *PQ, VOID *PBUFF, char * File, int Line); + +VOID SetWindowTextSupport(); +int WritetoConsoleSupport(char * buff); +VOID PMClose(); +VOID MySetWindowText(HWND hWnd, char * Msg); +BOOL CreateMonitorWindow(char * MonSize); +VOID FormatTime3(char * Time, time_t cTime); + +char EXCEPTMSG[80] = ""; + +char SIGNONMSG[128] = ""; +char SESSIONHDDR[80] = ""; +int SESSHDDRLEN = 0; + +BOOL IncludesMail = FALSE; +BOOL IncludesChat = FALSE; // Set if pgram is running - used for Web Page Index + + +char WL2KCall[10]; +char WL2KLoc[7]; + +extern char LOCATOR[]; // Locator for Reporting - may be Maidenhead or LAT:LON +extern char MAPCOMMENT[]; // Locator for Reporting - may be Maidenhead or LAT:LON +extern char LOC[7]; // Maidenhead Locator for Reporting +extern char ReportDest[7]; + +VOID __cdecl Debugprintf(const char * format, ...); +VOID __cdecl Consoleprintf(const char * format, ...); + +DllExport int APIENTRY CloseBPQ32(); +DllExport char * APIENTRY GetLOC(); +DllExport int APIENTRY SessionControl(int stream, int command, int param); + +int DoRefreshWebMailIndex(); + +BOOL APIENTRY Init_IP(); +BOOL APIENTRY Poll_IP(); + +BOOL APIENTRY Init_PM(); +BOOL APIENTRY Poll_PM(); + +BOOL APIENTRY Init_APRS(); +BOOL APIENTRY Poll_APRS(); +VOID HTTPTimer(); + +BOOL APIENTRY Rig_Init(); +BOOL APIENTRY Rig_Close(); +BOOL Rig_Poll(); + +VOID IPClose(); +VOID APRSClose(); +VOID CloseTNCEmulator(); + +VOID Poll_AGW(); +BOOL AGWAPIInit(); +int AGWAPITerminate(); + +int * Flag = (int *)&Flag; // for Dump Analysis +int MAJORVERSION=4; +int MINORVERSION=9; + +struct SEM Semaphore = {0, 0, 0, 0}; +struct SEM APISemaphore = {0, 0, 0, 0}; +int SemHeldByAPI = 0; +int LastSemGets = 0; +UINT Sem_eax = 0; +UINT Sem_ebx = 0; +UINT Sem_ecx = 0; +UINT Sem_edx = 0; +UINT Sem_esi = 0; +UINT Sem_edi = 0; + +void GetSemaphore(struct SEM * Semaphore, int ID); +void FreeSemaphore(struct SEM * Semaphore); + +DllExport void * BPQHOSTAPIPTR = &BPQHOSTAPI; +//DllExport long MONDECODEPTR = (long)&MONDECODE; + +extern UCHAR BPQDirectory[]; +extern UCHAR LogDirectory[]; +extern UCHAR BPQProgramDirectory[]; + +static char BPQWinMsg[] = "BPQWindowMessage"; + +static char ClassName[] = "BPQMAINWINDOW"; + +HKEY REGTREE = HKEY_CURRENT_USER; +char REGTREETEXT[100] = "HKEY_CURRENT_USER"; + +UINT BPQMsg=0; + +#define MAXLINELEN 120 +#define MAXSCREENLEN 50 + +#define BGCOLOUR RGB(236,233,216) + +HBRUSH bgBrush = NULL; + +//int LINELEN=120; +//int SCREENLEN=50; + +//char Screen[MAXLINELEN*MAXSCREENLEN]={0}; + +//int lineno=0; +//int col=0; + +#define REPORTINTERVAL 15 * 549; // Magic Ticks Per Minute for PC's nominal 100 ms timer +int ReportTimer = 0; + +HANDLE OpenConfigFile(char * file); + +VOID SetupBPQDirectory(); +VOID SendLocation(); + +//uintptr_t _beginthread(void(*start_address)(), unsigned stack_size, int arglist); + +#define TRAY_ICON_ID 1 // ID number for the Notify Icon +#define MY_TRAY_ICON_MESSAGE WM_APP // the message ID sent to our window + +NOTIFYICONDATA niData; + +int SetupConsoleWindow(); + +BOOL StartMinimized=FALSE; +BOOL MinimizetoTray=TRUE; + +BOOL StatusMinimized = FALSE; +BOOL ConsoleMinimized = FALSE; + +HMENU trayMenu=0; + +HWND hConsWnd = NULL, hWndCons = NULL, hWndBG = NULL, ClientWnd = NULL, FrameWnd = NULL, StatusWnd = NULL; + +BOOL FrameMaximized = FALSE; + +BOOL IGateEnabled = TRUE; +extern int ISDelayTimer; // Time before trying to reopen APRS-IS link +extern int ISPort; + +UINT * WINMORTraceQ = NULL; +UINT * SetWindowTextQ = NULL; + +static RECT Rect = {100,100,400,400}; // Console Window Position +RECT FRect = {100,100,800,600}; // Frame +static RECT StatusRect = {100,100,850,500}; // Status Window + +DllExport int APIENTRY DumpSystem(); +DllExport int APIENTRY SaveNodes (); +DllExport int APIENTRY ClearNodes (); +DllExport int APIENTRY SetupTrayIcon(); + +#define Q_REM(s) _Q_REM(s, __FILE__, __LINE__) + +VOID * _Q_REM(VOID *Q, char * File, int Line); + +UINT ReleaseBuffer(UINT *BUFF); + + +VOID CALLBACK TimerProc(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime ); + +DllExport int APIENTRY DeallocateStream(int stream); + +int VECTORLENGTH = sizeof (struct _BPQVECSTRUC); + +int FirstEntry = 1; +BOOL CloseLast = TRUE; // If the user started BPQ32.exe, don't close it when other programs close +BOOL Closing = FALSE; // Set if Close All called - prevents respawning bpq32.exe + +BOOL BPQ32_EXE; // Set if Process is running BPQ32.exe. Not initialised. + // Used to Kill surplus BPQ32.exe processes + +DWORD Our_PID; // Our Process ID - local variable + +void * InitDone = 0; +int FirstInitDone = 0; +int PerlReinit = 0; +UINT_PTR TimerHandle = 0; +UINT_PTR SessHandle = 0; + +BOOL EventsEnabled = 0; + +unsigned int TimerInst = 0xffffffff; + +HANDLE hInstance = 0; + +int AttachedProcesses = 0; +int AttachingProcess = 0; +HINSTANCE hIPModule = 0; +HINSTANCE hRigModule = 0; + +BOOL ReconfigFlag = FALSE; +BOOL RigReconfigFlag = FALSE; +BOOL APRSReconfigFlag = FALSE; +BOOL CloseAllNeeded = FALSE; +BOOL NeedWebMailRefresh = FALSE; + +int AttachedPIDList[100] = {0}; + +HWND hWndArray[100] = {0}; +int PIDArray[100] = {0}; +char PopupText[30][100] = {""}; + +// Next 3 should be uninitialised so they are local to each process + +UCHAR MCOM; +UCHAR MTX; +ULONG MMASK; +UCHAR MUIONLY; + +UCHAR AuthorisedProgram; // Local Variable. Set if Program is on secure list + +char pgm[256]; // Uninitialised so per process + +HANDLE Mutex; + +BOOL PartLine = FALSE; +int pindex = 0; +DWORD * WritetoConsoleQ; + + +LARGE_INTEGER lpFrequency = {0}; +LARGE_INTEGER lastRunTime; +LARGE_INTEGER currentTime; + +int ticksPerMillisec; +int interval; + + +VOID CALLBACK SetupTermSessions(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime); + + +TIMERPROC lpTimerFunc = (TIMERPROC) TimerProc; +TIMERPROC lpSetupTermSessions = (TIMERPROC) SetupTermSessions; + + +BOOL ProcessConfig(); +VOID FreeConfig(); + +DllExport int APIENTRY WritetoConsole(char * buff); + +BOOLEAN CheckifBPQ32isLoaded(); +BOOLEAN StartBPQ32(); +DllExport VOID APIENTRY Send_AX(VOID * Block, DWORD len, UCHAR Port); +BOOL LoadIPDriver(); +BOOL Send_IP(VOID * Block, DWORD len); +VOID CheckforLostProcesses(); +BOOL LoadRigDriver(); +VOID SaveConfig(); +VOID CreateRegBackup(); +VOID ResolveUpdateThread(); +VOID OpenReportingSockets(); +DllExport VOID APIENTRY CloseAllPrograms(); +DllExport BOOL APIENTRY SaveReg(char * KeyIn, HANDLE hFile); +int upnpClose(); + +BOOL IPActive = FALSE; +extern BOOL IPRequired; +BOOL PMActive = FALSE; +extern BOOL PMRequired; +BOOL RigRequired = TRUE; +BOOL RigActive = FALSE; +BOOL APRSActive = FALSE; +BOOL AGWActive = FALSE; +BOOL needAIS = FALSE; +int needADSB = 0; + +extern int AGWPort; + +Tell_Sessions(); + + +typedef int (WINAPI FAR *FARPROCX)(); + +FARPROCX CreateToolHelp32SnapShotPtr; +FARPROCX Process32Firstptr; +FARPROCX Process32Nextptr; + +void LoadToolHelperRoutines() +{ + HINSTANCE ExtDriver=0; + int err; + char msg[100]; + + ExtDriver=LoadLibrary("kernel32.dll"); + + if (ExtDriver == NULL) + { + err=GetLastError(); + sprintf(msg,"BPQ32 Error loading kernel32.dll - Error code %d\n", err); + OutputDebugString(msg); + return; + } + + CreateToolHelp32SnapShotPtr = (FARPROCX)GetProcAddress(ExtDriver,"CreateToolhelp32Snapshot"); + Process32Firstptr = (FARPROCX)GetProcAddress(ExtDriver,"Process32First"); + Process32Nextptr = (FARPROCX)GetProcAddress(ExtDriver,"Process32Next"); + + if (CreateToolHelp32SnapShotPtr == 0) + { + err=GetLastError(); + sprintf(msg,"BPQ32 Error getting CreateToolhelp32Snapshot entry point - Error code %d\n", err); + OutputDebugString(msg); + return; + } +} + +BOOL GetProcess(int ProcessID, char * Program) +{ + HANDLE hProcessSnap; + PROCESSENTRY32 pe32; + int p; + + if (CreateToolHelp32SnapShotPtr==0) + { + return (TRUE); // Routine not available + } + // Take a snapshot of all processes in the system. + hProcessSnap = (HANDLE)CreateToolHelp32SnapShotPtr(TH32CS_SNAPPROCESS, 0); + if( hProcessSnap == INVALID_HANDLE_VALUE ) + { + OutputDebugString( "CreateToolhelp32Snapshot (of processes) Failed\n" ); + return( FALSE ); + } + + // Set the size of the structure before using it. + pe32.dwSize = sizeof( PROCESSENTRY32 ); + + // Retrieve information about the first process, + // and exit if unsuccessful + if( !Process32Firstptr( hProcessSnap, &pe32 ) ) + { + OutputDebugString( "Process32First Failed\n" ); // Show cause of failure + CloseHandle( hProcessSnap ); // Must clean up the snapshot object! + return( FALSE ); + } + + // Now walk the snapshot of processes, and + // display information about each process in turn + do + { + if (ProcessID==pe32.th32ProcessID) + { + // if running on 98, program contains the full path - remove it + + for (p = (int)strlen(pe32.szExeFile); p >= 0; p--) + { + if (pe32.szExeFile[p]=='\\') + { + break; + } + } + p++; + + sprintf(Program,"%s", &pe32.szExeFile[p]); + CloseHandle( hProcessSnap ); + return( TRUE ); + } + + } while( Process32Nextptr( hProcessSnap, &pe32 ) ); + + + sprintf(Program,"PID %d Not Found", ProcessID); + CloseHandle( hProcessSnap ); + return(FALSE); +} + +BOOL IsProcess(int ProcessID) +{ + // Check that Process exists + + HANDLE hProcessSnap; + PROCESSENTRY32 pe32; + + if (CreateToolHelp32SnapShotPtr==0) return (TRUE); // Routine not available + + hProcessSnap = (HANDLE)CreateToolHelp32SnapShotPtr(TH32CS_SNAPPROCESS, 0); + + if( hProcessSnap == INVALID_HANDLE_VALUE ) + { + OutputDebugString( "CreateToolhelp32Snapshot (of processes) Failed\n" ); + return(TRUE); // Don't know, so assume ok + } + + pe32.dwSize = sizeof( PROCESSENTRY32 ); + + if( !Process32Firstptr( hProcessSnap, &pe32 ) ) + { + OutputDebugString( "Process32First Failed\n" ); // Show cause of failure + CloseHandle( hProcessSnap ); // Must clean up the snapshot object! + return(TRUE); // Don't know, so assume ok + } + + do + { + if (ProcessID==pe32.th32ProcessID) + { + CloseHandle( hProcessSnap ); + return( TRUE ); + } + + } while( Process32Nextptr( hProcessSnap, &pe32 ) ); + + CloseHandle( hProcessSnap ); + return(FALSE); +} + +#include "DbgHelp.h" + +VOID MonitorThread(int x) +{ + // Thread to detect killed processes. Runs in process owning timer. + + // Obviously can't detect loss of timer owning thread! + + do + { + if (Semaphore.Gets == LastSemGets && Semaphore.Flag) + { + // It is stuck - try to release + + Debugprintf ("Semaphore locked - Process ID = %d, Held By %d", + Semaphore.SemProcessID, SemHeldByAPI); + + // Write a minidump + + WriteMiniDump(); + + Semaphore.Flag = 0; + } + + LastSemGets = Semaphore.Gets; + + Sleep(30000); + CheckforLostProcesses(); + + } while (TRUE); +} + +VOID CheckforLostProcesses() +{ + UCHAR buff[100]; + char Log[80]; + int i, n, ProcessID; + + for (n=0; n < AttachedProcesses; n++) + { + ProcessID=AttachedPIDList[n]; + + if (!IsProcess(ProcessID)) + { + // Process has died - Treat as a detach + + sprintf(Log,"BPQ32 Process %d Died\n", ProcessID); + OutputDebugString(Log); + + // Remove Tray Icon Entry + + for( i = 0; i < 100; ++i ) + { + if (PIDArray[i] == ProcessID) + { + hWndArray[i] = 0; + sprintf(Log,"BPQ32 Removing Tray Item %s\n", PopupText[i]); + OutputDebugString(Log); + DeleteMenu(trayMenu,TRAYBASEID+i,MF_BYCOMMAND); + } + } + + // If process had the semaphore, release it + + if (Semaphore.Flag == 1 && ProcessID == Semaphore.SemProcessID) + { + OutputDebugString("BPQ32 Process was holding Semaphore - attempting recovery\r\n"); + Debugprintf("Last Sem Call %d %x %x %x %x %x %x", SemHeldByAPI, + Sem_eax, Sem_ebx, Sem_ecx, Sem_edx, Sem_esi, Sem_edi); + + Semaphore.Flag = 0; + SemHeldByAPI = 0; + } + + for (i=1;i<65;i++) + { + if (BPQHOSTVECTOR[i-1].STREAMOWNER == AttachedPIDList[n]) + { + DeallocateStream(i); + } + } + + if (TimerInst == ProcessID) + { + KillTimer(NULL,TimerHandle); + TimerHandle=0; + TimerInst=0xffffffff; +// Tell_Sessions(); + OutputDebugString("BPQ32 Process was running timer \n"); + + if (MinimizetoTray) + Shell_NotifyIcon(NIM_DELETE,&niData); + + + } + + // Remove this entry from PID List + + for (i=n; i< AttachedProcesses; i++) + { + AttachedPIDList[i]=AttachedPIDList[i+1]; + } + AttachedProcesses--; + + sprintf(buff,"BPQ32 Lost Process - %d Process(es) Attached\n", AttachedProcesses); + OutputDebugString(buff); + } + } +} +VOID MonitorTimerThread(int x) +{ + // Thread to detect killed timer process. Runs in all other BPQ32 processes. + + do { + + Sleep(60000); + + if (TimerInst != 0xffffffff && !IsProcess(TimerInst)) + { + // Timer owning Process has died - Force a new timer to be created + // New timer thread will detect lost process and tidy up + + Debugprintf("BPQ32 Process %d with Timer died", TimerInst); + + // If process was holding the semaphore, release it + + if (Semaphore.Flag == 1 && TimerInst == Semaphore.SemProcessID) + { + OutputDebugString("BPQ32 Process was holding Semaphore - attempting recovery\r\n"); + Debugprintf("Last Sem Call %d %x %x %x %x %x %x", SemHeldByAPI, + Sem_eax, Sem_ebx, Sem_ecx, Sem_edx, Sem_esi, Sem_edi); + Semaphore.Flag = 0; + SemHeldByAPI = 0; + } + +// KillTimer(NULL,TimerHandle); +// TimerHandle=0; +// TimerInst=0xffffffff; +// Tell_Sessions(); + + CheckforLostProcesses(); // Normally only done in timer thread, which is now dead + + // Timer can only run in BPQ32.exe + + TimerInst=0xffffffff; // So we dont keep doing it + TimerHandle = 0; // So new process attaches + + if (Closing == FALSE && AttachingProcess == FALSE) + { + OutputDebugString("BPQ32 Reloading BPQ32.exe\n"); + StartBPQ32(); + } + +// if (MinimizetoTray) +// Shell_NotifyIcon(NIM_DELETE,&niData); + } + + } while (TRUE); +} + +VOID WritetoTraceSupport(struct TNCINFO * TNC, char * Msg, int Len); + +VOID TimerProcX(); + +VOID CALLBACK TimerProc( + HWND hwnd, // handle of window for timer messages + UINT uMsg, // WM_TIMER message + UINT idEvent, // timer identifier + DWORD dwTime) // current system time +{ + KillTimer(NULL,TimerHandle); + TimerProcX(); + TimerHandle = SetTimer(NULL,0,100,lpTimerFunc); +} +VOID TimerProcX() +{ + struct _EXCEPTION_POINTERS exinfo; + + // + // Get semaphore before proceeeding + // + + GetSemaphore(&Semaphore, 2); + + // Get time since last run + + QueryPerformanceCounter(¤tTime); + + interval = (int)(currentTime.QuadPart - lastRunTime.QuadPart) / ticksPerMillisec; + lastRunTime.QuadPart = currentTime.QuadPart; + + //Debugprintf("%d", interval); + + // Process WINMORTraceQ + + while (WINMORTraceQ) + { + UINT * Buffer = Q_REM(&WINMORTraceQ); + struct TNCINFO * TNC = (struct TNCINFO * )Buffer[1]; + int Len = Buffer[2]; + char * Msg = (char *)&Buffer[3]; + + WritetoTraceSupport(TNC, Msg, Len); + RelBuff(Buffer); + } + + if (SetWindowTextQ) + SetWindowTextSupport(); + + while (WritetoConsoleQ) + { + UINT * Buffer = Q_REM(&WritetoConsoleQ); + WritetoConsoleSupport((char *)&Buffer[2]); + RelBuff(Buffer); + } + + strcpy(EXCEPTMSG, "Timer ReconfigProcessing"); + + __try + { + + if (trayMenu == NULL) + SetupTrayIcon(); + + // See if reconfigure requested + + if (CloseAllNeeded) + { + CloseAllNeeded = FALSE; + CloseAllPrograms(); + } + + if (ReconfigFlag) + { + // Only do it it timer owning process, or we could get in a real mess! + + if(TimerInst == GetCurrentProcessId()) + { + int i; + BPQVECSTRUC * HOSTVEC; + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + WSADATA WsaData; // receives data from WSAStartup + RECT cRect; + + ReconfigFlag = FALSE; + + SetupBPQDirectory(); + + WritetoConsole("Reconfiguring ...\n\n"); + OutputDebugString("BPQ32 Reconfiguring ...\n"); + + GetWindowRect(FrameWnd, &FRect); + + SaveWindowPos(40); // Rigcontrol + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External + { + if (PORTVEC->PORT_EXT_ADDR) + { + SaveWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + SaveAXIPWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + CloseDriverWindow(PORTVEC->PORTCONTROL.PORTNUMBER); + PORTVEC->PORT_EXT_ADDR(5,PORTVEC->PORTCONTROL.PORTNUMBER, NULL); // Close External Ports + } + } + PORTVEC->PORTCONTROL.PORTCLOSECODE(&PORTVEC->PORTCONTROL); + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + + IPClose(); + PMClose(); + APRSClose(); + Rig_Close(); + CloseTNCEmulator(); + if (AGWActive) + AGWAPITerminate(); + + WSACleanup(); + + WL2KReports = NULL; + + Sleep(2000); + + WSAStartup(MAKEWORD(2, 0), &WsaData); + + Consoleprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); + Consoleprintf(VerCopyright); + + Start(); + + INITIALISEPORTS(); // Restart Ports + + SetApplPorts(); + + FreeConfig(); + + for (i=1; i<68; i++) // Include Telnet, APRS and IP Vec + { + HOSTVEC=&BPQHOSTVECTOR[i-1]; + + HOSTVEC->HOSTTRACEQ=0; // Clear header (pool has been reinitialized + + if (HOSTVEC->HOSTSESSION !=0) + { + // Had a connection + + HOSTVEC->HOSTSESSION=0; + HOSTVEC->HOSTFLAGS |=3; // Disconnected + + PostMessage(HOSTVEC->HOSTHANDLE, BPQMsg, i, 4); + } + } + + // Free the APRS Appl Q + + APPL_Q = 0; + + OpenReportingSockets(); + + WritetoConsole("\n\nReconfiguration Complete\n"); + + if (IPRequired) IPActive = Init_IP(); + if (PMRequired) PMActive = Init_PM(); + + APRSActive = Init_APRS(); + + if (ISPort == 0) + IGateEnabled = 0; + + CheckDlgButton(hConsWnd, IDC_ENIGATE, IGateEnabled); + + GetClientRect(hConsWnd, &cRect); + MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); + if (APRSActive) + MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); + else + { + ShowWindow(GetDlgItem(hConsWnd, IDC_GPS), SW_HIDE); + MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); + } + InvalidateRect(hConsWnd, NULL, TRUE); + + RigActive = Rig_Init(); + + if (NUMBEROFTNCPORTS) + { + FreeSemaphore(&Semaphore); + InitializeTNCEmulator(); + GetSemaphore(&Semaphore, 0); + } + + FreeSemaphore(&Semaphore); + AGWActive = AGWAPIInit(); + GetSemaphore(&Semaphore, 0); + + OutputDebugString("BPQ32 Reconfiguration Complete\n"); + } + } + + + if (RigReconfigFlag) + { + // Only do it it timer owning process, or we could get in a real mess! + + if(TimerInst == GetCurrentProcessId()) + { + RigReconfigFlag = FALSE; + CloseDriverWindow(40); + Rig_Close(); + Sleep(6000); // Allow any CATPTT, HAMLIB and FLRIG threads to close + RigActive = Rig_Init(); + + WritetoConsole("Rigcontrol Reconfiguration Complete\n"); + } + } + + if (APRSReconfigFlag) + { + // Only do it it timer owning process, or we could get in a real mess! + + if(TimerInst == GetCurrentProcessId()) + { + APRSReconfigFlag = FALSE; + APRSClose(); + APRSActive = Init_APRS(); + + WritetoConsole("APRS Reconfiguration Complete\n"); + } + } + + } + #include "StdExcept.c" + + if (Semaphore.Flag && Semaphore.SemProcessID == GetCurrentProcessId()) + FreeSemaphore(&Semaphore); + + } + + strcpy(EXCEPTMSG, "Timer Processing"); + + __try + { + if (IPActive) Poll_IP(); + if (PMActive) Poll_PM(); + if (RigActive) Rig_Poll(); + + if (NeedWebMailRefresh) + DoRefreshWebMailIndex(); + + CheckGuardZone(); + + if (APRSActive) + { + Poll_APRS(); + CheckGuardZone(); + } + + CheckWL2KReportTimer(); + + CheckGuardZone(); + + TIMERINTERRUPT(); + + CheckGuardZone(); + + FreeSemaphore(&Semaphore); // SendLocation needs to get the semaphore + + if (NUMBEROFTNCPORTS) + TNCTimer(); + + if (AGWActive) + Poll_AGW(); + + DRATSPoll(); + + CheckGuardZone(); + + strcpy(EXCEPTMSG, "HTTP Timer Processing"); + + HTTPTimer(); + + CheckGuardZone(); + + strcpy(EXCEPTMSG, "WL2K Report Timer Processing"); + + if (ReportTimer) + { + ReportTimer--; + + if (ReportTimer == 0) + { + ReportTimer = REPORTINTERVAL; + SendLocation(); + } + } + } + + #include "StdExcept.c" + + if (Semaphore.Flag && Semaphore.SemProcessID == GetCurrentProcessId()) + FreeSemaphore(&Semaphore); + + } + + CheckGuardZone(); + + return; +} + +HANDLE NPHandle; + +int (WINAPI FAR *GetModuleFileNameExPtr)() = NULL; +int (WINAPI FAR *EnumProcessesPtr)() = NULL; + +FirstInit() +{ + WSADATA WsaData; // receives data from WSAStartup + HINSTANCE ExtDriver=0; + RECT cRect; + + + // First Time Ports and Timer init + + // Moved from DLLINIT to sort out perl problem, and meet MS Guidelines on minimising DLLMain + + // Call wsastartup - most systems need winsock, and duplicate statups could be a problem + + WSAStartup(MAKEWORD(2, 0), &WsaData); + + // Load Psapi.dll if possible + + ExtDriver=LoadLibrary("Psapi.dll"); + + SetupTrayIcon(); + + if (ExtDriver) + { + GetModuleFileNameExPtr = (FARPROCX)GetProcAddress(ExtDriver,"GetModuleFileNameExA"); + EnumProcessesPtr = (FARPROCX)GetProcAddress(ExtDriver,"EnumProcesses"); + } + INITIALISEPORTS(); + + OpenReportingSockets(); + + WritetoConsole("\n"); + WritetoConsole("Port Initialisation Complete\n"); + + if (IPRequired) IPActive = Init_IP(); + if (PMRequired) PMActive = Init_PM(); + + APRSActive = Init_APRS(); + + if (APRSActive) + { + hWndBG = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 0,0,40,546, hConsWnd, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Enable IGate", WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, + 8,0,90,24, hConsWnd, (HMENU)-1, hInstance, NULL); + + CreateWindowEx(0, "BUTTON", "", WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP, + 95,1,18,24, hConsWnd, (HMENU)IDC_ENIGATE, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "IGate State - Disconnected", + WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, 125, 0, 195, 24, hConsWnd, (HMENU)IGATESTATE, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "IGATE Stats - Msgs 0 Local Stns 0", + WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, 320, 0, 240, 24, hConsWnd, (HMENU)IGATESTATS, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "GPS Off", + WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, 560, 0, 80, 24, hConsWnd, (HMENU)IDC_GPS, hInstance, NULL); + } + + if (ISPort == 0) + IGateEnabled = 0; + + CheckDlgButton(hConsWnd, IDC_ENIGATE, IGateEnabled); + + GetClientRect(hConsWnd, &cRect); + MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); + if (APRSActive) + MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); + else + { + ShowWindow(GetDlgItem(hConsWnd, IDC_GPS), SW_HIDE); + MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); + } + InvalidateRect(hConsWnd, NULL, TRUE); + + RigActive = Rig_Init(); + + _beginthread(MonitorThread,0,0); + + TimerHandle=SetTimer(NULL,0,100,lpTimerFunc); + TimerInst=GetCurrentProcessId(); + SessHandle = SetTimer(NULL, 0, 5000, lpSetupTermSessions); + + // If ARIF reporting is enabled write a Trimode Like ini for RMS Analyser + + if (ADIFLogEnabled) + ADIFWriteFreqList(); + + OutputDebugString("BPQ32 Port Initialisation Complete\n"); + + if (needAIS) + initAIS(); + + if (needADSB) + initADSB(); + + return 0; +} + +Check_Timer() +{ + if (Closing) + return 0; + + if (Semaphore.Flag) + return 0; + + if (InitDone == (void *)-1) + { + GetSemaphore(&Semaphore, 3); + Sleep(15000); + FreeSemaphore(&Semaphore); + exit (0); + } + + if (FirstInitDone == 0) + { + GetSemaphore(&Semaphore, 3); + + if (_stricmp(pgm, "bpq32.exe") == 0) + { + FirstInit(); + FreeSemaphore(&Semaphore); + if (NUMBEROFTNCPORTS) + InitializeTNCEmulator(); + + AGWActive = AGWAPIInit(); + FirstInitDone=1; // Only init in BPQ32.exe + return 0; + } + else + { + FreeSemaphore(&Semaphore); + return 0; + } + } + + if (TimerHandle == 0 && FirstInitDone == 1) + { + WSADATA WsaData; // receives data from WSAStartup + HINSTANCE ExtDriver=0; + RECT cRect; + + // Only attach timer to bpq32.exe + + if (_stricmp(pgm, "bpq32.exe") != 0) + { + return 0; + } + + GetSemaphore(&Semaphore, 3); + OutputDebugString("BPQ32 Reinitialising External Ports and Attaching Timer\n"); + + if (!ProcessConfig()) + { + ShowWindow(hConsWnd, SW_RESTORE); + SendMessage(hConsWnd, WM_PAINT, 0, 0); + SetForegroundWindow(hConsWnd); + + InitDone = (void *)-1; + FreeSemaphore(&Semaphore); + + MessageBox(NULL,"Configuration File Error","BPQ32",MB_ICONSTOP); + + exit (0); + } + + GetVersionInfo("bpq32.dll"); + + SetupConsoleWindow(); + + Consoleprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); + Consoleprintf(VerCopyright); + Consoleprintf("Reinitialising..."); + + SetupBPQDirectory(); + + Sleep(1000); // Allow time for sockets to close + + WSAStartup(MAKEWORD(2, 0), &WsaData); + + // Load Psapi.dll if possible + + ExtDriver = LoadLibrary("Psapi.dll"); + + SetupTrayIcon(); + + if (ExtDriver) + { + GetModuleFileNameExPtr = (FARPROCX)GetProcAddress(ExtDriver,"GetModuleFileNameExA"); + EnumProcessesPtr = (FARPROCX)GetProcAddress(ExtDriver,"EnumProcesses"); + } + + Start(); + + INITIALISEPORTS(); + + OpenReportingSockets(); + + NODESINPROGRESS = 0; + CURRENTNODE = 0; + + SetApplPorts(); + + WritetoConsole("\n\nPort Reinitialisation Complete\n"); + + BPQMsg = RegisterWindowMessage(BPQWinMsg); + + CreateMutex(NULL,TRUE,"BPQLOCKMUTEX"); + +// NPHandle=CreateNamedPipe("\\\\.\\pipe\\BPQ32pipe", +// PIPE_ACCESS_DUPLEX,0,64,4096,4096,1000,NULL); + + if (IPRequired) IPActive = Init_IP(); + if (PMRequired) PMActive = Init_PM(); + + RigActive = Rig_Init(); + APRSActive = Init_APRS(); + + if (ISPort == 0) + IGateEnabled = 0; + + CheckDlgButton(hConsWnd, IDC_ENIGATE, IGateEnabled); + + GetClientRect(hConsWnd, &cRect); + MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); + + if (APRSActive) + MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); + else + { + ShowWindow(GetDlgItem(hConsWnd, IDC_GPS), SW_HIDE); + MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); + } + InvalidateRect(hConsWnd, NULL, TRUE); + + FreeConfig(); + + _beginthread(MonitorThread,0,0); + + ReportTimer = 0; + + OpenReportingSockets(); + + FreeSemaphore(&Semaphore); + + if (NUMBEROFTNCPORTS) + InitializeTNCEmulator(); + + AGWActive = AGWAPIInit(); + + if (StartMinimized) + if (MinimizetoTray) + ShowWindow(FrameWnd, SW_HIDE); + else + ShowWindow(FrameWnd, SW_SHOWMINIMIZED); + else + ShowWindow(FrameWnd, SW_RESTORE); + + TimerHandle=SetTimer(NULL,0,100,lpTimerFunc); + TimerInst=GetCurrentProcessId(); + SessHandle = SetTimer(NULL, 0, 5000, lpSetupTermSessions); + + return (1); + } + + return (0); +} + +DllExport INT APIENTRY CheckTimer() +{ + return Check_Timer(); +} + +Tell_Sessions() +{ + // + // Post a message to all listening sessions, so they call the + // API, and cause a new timer to be allocated + // + HWND hWnd; + int i; + + for (i=1;i<65;i++) + { + if (BPQHOSTVECTOR[i-1].HOSTFLAGS & 0x80) + { + hWnd = BPQHOSTVECTOR[i-1].HOSTHANDLE; + PostMessage(hWnd, BPQMsg,i, 1); + PostMessage(hWnd, BPQMsg,i, 2); + } + } + return (0); +} + +BOOL APIENTRY DllMain(HANDLE hInst, DWORD ul_reason_being_called, LPVOID lpReserved) +{ + DWORD n; + char buf[350]; + + int i; + unsigned int ProcessID; + + OSVERSIONINFO osvi; + + memset(&osvi, 0, sizeof(OSVERSIONINFO)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + + GetVersionEx(&osvi); + + + switch( ul_reason_being_called ) + { + case DLL_PROCESS_ATTACH: + + if (sizeof(HDLCDATA) > PORTENTRYLEN + 200) // 200 bytes of Hardwaredata + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"BPQ32 Too much HDLC data - Recompile","BPQ32", MB_OK); + return 0; + } + + if (sizeof(LINKTABLE) != LINK_TABLE_LEN) + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"L2 LINK Table .c and .asm mismatch - fix and rebuild","BPQ32", MB_OK); + return 0; + } + if (sizeof(struct ROUTE) != ROUTE_LEN) + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"ROUTE Table .c and .asm mismatch - fix and rebuild", "BPQ32", MB_OK); + return 0; + } + + if (sizeof(struct DEST_LIST) != DEST_LIST_LEN) + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"NODES Table .c and .asm mismatch - fix and rebuild", "BPQ32", MB_OK); + return 0; + } + + GetSemaphore(&Semaphore, 4); + + BPQHOSTVECPTR = &BPQHOSTVECTOR[0]; + + LoadToolHelperRoutines(); + + Our_PID = GetCurrentProcessId(); + + QueryPerformanceFrequency(&lpFrequency); + + ticksPerMillisec = (int)lpFrequency.QuadPart / 1000; + + lastRunTime.QuadPart = lpFrequency.QuadPart; + + GetProcess(Our_PID, pgm); + + if (_stricmp(pgm, "regsvr32.exe") == 0 || _stricmp(pgm, "bpqcontrol.exe") == 0) + { + AttachedProcesses++; // We will get a detach + FreeSemaphore(&Semaphore); + return 1; + } + + if (_stricmp(pgm,"BPQ32.exe") == 0) + BPQ32_EXE = TRUE; + + if (_stricmp(pgm,"BPQMailChat.exe") == 0) + IncludesMail = TRUE; + + if (_stricmp(pgm,"BPQMail.exe") == 0) + IncludesMail = TRUE; + + if (_stricmp(pgm,"BPQChat.exe") == 0) + IncludesChat = TRUE; + + if (FirstEntry) // If loaded by BPQ32.exe, dont close it at end + { + FirstEntry = 0; + if (BPQ32_EXE) + CloseLast = FALSE; + } + else + { + if (BPQ32_EXE && AttachingProcess == 0) + { + AttachedProcesses++; // We will get a detach + FreeSemaphore(&Semaphore); + MessageBox(NULL,"BPQ32.exe is already running\r\n\r\nIt should only be run once", "BPQ32", MB_OK); + return 0; + } + } + + if (_stricmp(pgm,"BPQTelnetServer.exe") == 0) + { + MessageBox(NULL,"BPQTelnetServer is no longer supported\r\n\r\nUse the TelnetServer in BPQ32.dll", "BPQ32", MB_OK); + AttachedProcesses++; // We will get a detach + FreeSemaphore(&Semaphore); + return 0; + } + + if (_stricmp(pgm,"BPQUIUtil.exe") == 0) + { + MessageBox(NULL,"BPQUIUtil is now part of BPQ32.dll\r\nBPQUIUtil.exe cannot be run\r\n", "BPQ32", MB_OK); + AttachedProcesses++; // We will get a detach + FreeSemaphore(&Semaphore); + return 0; + } + + if (_stricmp(pgm,"BPQMailChat.exe") == 0) + { + MessageBox(NULL,"BPQMailChat is obsolete. Run BPQMail.exe and/or BPQChat.exe instead", "BPQ32", MB_OK); + AttachedProcesses++; // We will get a detach + FreeSemaphore(&Semaphore); + return 0; + } + AuthorisedProgram = TRUE; + + if (InitDone == 0) + { +// #pragma warning(push) +// #pragma warning(disable : 4996) + +// if (_winver < 0x0600) +// #pragma warning(pop) +// { +// // Below Vista +// +// REGTREE = HKEY_LOCAL_MACHINE; +// strcpy(REGTREETEXT, "HKEY_LOCAL_MACHINE"); +// } + + hInstance=hInst; + + Mutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"BPQLOCKMUTEX"); + + if (Mutex != NULL) + { + OutputDebugString("Another BPQ32.dll is loaded\n"); + i=MessageBox(NULL,"BPQ32 DLL already loaded from another directory\nIf you REALLY want this, hit OK, else hit Cancel","BPQ32",MB_OKCANCEL); + FreeSemaphore(&Semaphore); + + if (i != IDOK) return (0); + + CloseHandle(Mutex); + } + + if (!BPQ32_EXE) + { + if (CheckifBPQ32isLoaded() == FALSE) // Start BPQ32.exe if needed + { + // Wasn't Loaded, so we have started it, and should let it init system + + goto SkipInit; + } + } + + GetVersionInfo("bpq32.dll"); + + sprintf (SIGNONMSG, "G8BPQ AX25 Packet Switch System Version %s %s\r\n%s\r\n", + TextVerstring, Datestring, VerCopyright); + + SESSHDDRLEN = sprintf(SESSIONHDDR, "G8BPQ Network System %s for Win32 (", TextVerstring); + + SetupConsoleWindow(); + SetupBPQDirectory(); + + if (!ProcessConfig()) + { + StartMinimized = FALSE; + MinimizetoTray = FALSE; + ShowWindow(FrameWnd, SW_MAXIMIZE); + ShowWindow(hConsWnd, SW_MAXIMIZE); + ShowWindow(StatusWnd, SW_HIDE); + + SendMessage(hConsWnd, WM_PAINT, 0, 0); + SetForegroundWindow(hConsWnd); + + InitDone = (void *)-1; + FreeSemaphore(&Semaphore); + + MessageBox(NULL,"Configuration File Error\r\nProgram will close in 15 seconds","BPQ32",MB_ICONSTOP); + + return (0); + } + + Consoleprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); + Consoleprintf(VerCopyright); + + if (Start() !=0) + { + Sleep(3000); + FreeSemaphore(&Semaphore); + return (0); + } + else + { + SetApplPorts(); + + GetUIConfig(); + + InitDone = &InitDone; + BPQMsg = RegisterWindowMessage(BPQWinMsg); +// TimerHandle=SetTimer(NULL,0,100,lpTimerFunc); +// TimerInst=GetCurrentProcessId(); + +/* Mutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"BPQLOCKMUTEX"); + + if (Mutex != NULL) + { + OutputDebugString("Another BPQ32.dll is loaded\n"); + MessageBox(NULL,"BPQ32 DLL already loaded from another directory","BPQ32",MB_ICONSTOP); + FreeSemaphore(&Semaphore); + return (0); + } + +*/ + Mutex=CreateMutex(NULL,TRUE,"BPQLOCKMUTEX"); + +// CreatePipe(&H1,&H2,NULL,1000); + +// GetLastError(); + +// NPHandle=CreateNamedPipe("\\\\.\\pipe\\BPQ32pipe", +// PIPE_ACCESS_DUPLEX,0,64,4096,4096,1000,NULL); + +// GetLastError(); + +/* + // + // Read SYSOP password + // + + if (PWTEXT[0] == 0) + { + handle = OpenConfigFile("PASSWORD.BPQ"); + + if (handle == INVALID_HANDLE_VALUE) + { + WritetoConsole("Can't open PASSWORD.BPQ\n"); + PWLen=0; + PWTEXT[0]=0; + } + else + { + ReadFile(handle,PWTEXT,78,&n,NULL); + CloseHandle(handle); + } + } +*/ + for (i=0;PWTEXT[i] > 0x20;i++); //Scan for cr or null + PWLen=i; + + } + } + else + { + if (InitDone != &InitDone) + { + MessageBox(NULL,"BPQ32 DLL already loaded at another address","BPQ32",MB_ICONSTOP); + FreeSemaphore(&Semaphore); + return (0); + } + } + + // Run timer monitor thread in all processes - it is possible for the TImer thread not to be the first thread +SkipInit: + + _beginthread(MonitorTimerThread,0,0); + + FreeSemaphore(&Semaphore); + + AttachedPIDList[AttachedProcesses++] = GetCurrentProcessId(); + + if (_stricmp(pgm,"bpq32.exe") == 0 && AttachingProcess == 1) AttachingProcess = 0; + + GetProcess(GetCurrentProcessId(),pgm); + n=sprintf(buf,"BPQ32 DLL Attach complete - Program %s - %d Process(es) Attached\n",pgm,AttachedProcesses); + OutputDebugString(buf); + + // Set up local variables + + MCOM=1; + MTX=1; + MMASK=0xffffffff; + +// if (StartMinimized) +// if (MinimizetoTray) +// ShowWindow(FrameWnd, SW_HIDE); +// else +// ShowWindow(FrameWnd, SW_SHOWMINIMIZED); +// else +// ShowWindow(FrameWnd, SW_RESTORE); + + return 1; + + case DLL_THREAD_ATTACH: + + return 1; + + case DLL_THREAD_DETACH: + + return 1; + + case DLL_PROCESS_DETACH: + + if (_stricmp(pgm,"BPQMailChat.exe") == 0) + IncludesMail = FALSE; + + if (_stricmp(pgm,"BPQChat.exe") == 0) + IncludesChat = FALSE; + + ProcessID=GetCurrentProcessId(); + + Debugprintf("BPQ32 Process %d Detaching", ProcessID); + + // Release any streams that the app has failed to release + + for (i=1;i<65;i++) + { + if (BPQHOSTVECTOR[i-1].STREAMOWNER == ProcessID) + { + // If connected, disconnect + + SessionControl(i, 2, 0); + DeallocateStream(i); + } + } + + // Remove any Tray Icon Entries + + for( i = 0; i < 100; ++i ) + { + if (PIDArray[i] == ProcessID) + { + char Log[80]; + hWndArray[i] = 0; + sprintf(Log,"BPQ32 Removing Tray Item %s\n", PopupText[i]); + OutputDebugString(Log); + DeleteMenu(trayMenu,TRAYBASEID+i,MF_BYCOMMAND); + } + } + + if (Mutex) CloseHandle(Mutex); + + // Remove our entry from PID List + + for (i=0; i< AttachedProcesses; i++) + if (AttachedPIDList[i] == ProcessID) + break; + + for (; i< AttachedProcesses; i++) + { + AttachedPIDList[i]=AttachedPIDList[i+1]; + } + + AttachedProcesses--; + + if (TimerInst == ProcessID) + { + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + + OutputDebugString("BPQ32 Process with Timer closing\n"); + + // Call Port Close Routines + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External + { + if (PORTVEC->PORT_EXT_ADDR && PORTVEC->DLLhandle == NULL) // Don't call if real .dll - it's not there! + { + SaveWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + SaveAXIPWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + PORTVEC->PORT_EXT_ADDR(5,PORTVEC->PORTCONTROL.PORTNUMBER, NULL); // Close External Ports + } + } + + PORTVEC->PORTCONTROL.PORTCLOSECODE(&PORTVEC->PORTCONTROL); + + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + + + IPClose(); + PMClose(); + APRSClose(); + Rig_Close(); + CloseTNCEmulator(); + if (AGWActive) + AGWAPITerminate(); + + upnpClose(); + + WSACleanup(); + WSAGetLastError(); + + if (MinimizetoTray) + Shell_NotifyIcon(NIM_DELETE,&niData); + + if (hConsWnd) DestroyWindow(hConsWnd); + + KillTimer(NULL,TimerHandle); + TimerHandle=0; + TimerInst=0xffffffff; + + if (AttachedProcesses && Closing == FALSE && AttachingProcess == 0) // Other processes + { + OutputDebugString("BPQ32 Reloading BPQ32.exe\n"); + StartBPQ32(); + } + } + else + { + // Not Timer Process + + if (AttachedProcesses == 1 && CloseLast) // Only bpq32.exe left + { + Debugprintf("Only BPQ32.exe running - close it"); + CloseAllNeeded = TRUE; + } + } + + if (AttachedProcesses < 2) + { + if (AUTOSAVE == 1) + SaveNodes(); + if (AUTOSAVEMH) + SaveMH(); + + if (needAIS) + SaveAIS(); + } + if (AttachedProcesses == 0) + { + Closing = TRUE; + KillTimer(NULL,TimerHandle); + + if (MinimizetoTray) + Shell_NotifyIcon(NIM_DELETE,&niData); + + // Unload External Drivers + + { + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10 && PORTVEC->DLLhandle) + FreeLibrary(PORTVEC->DLLhandle); + + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + } + } + + GetProcess(GetCurrentProcessId(),pgm); + n=sprintf(buf,"BPQ32 DLL Detach complete - Program %s - %d Process(es) Attached\n",pgm,AttachedProcesses); + OutputDebugString(buf); + + return 1; + } + return 1; +} + +DllExport int APIENTRY CloseBPQ32() +{ + // Unload External Drivers + + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + int i; + int ProcessID = GetCurrentProcessId(); + + if (Semaphore.Flag == 1 && ProcessID == Semaphore.SemProcessID) + { + OutputDebugString("BPQ32 Process holding Semaphore called CloseBPQ32 - attempting recovery\r\n"); + Debugprintf("Last Sem Call %d %x %x %x %x %x %x", SemHeldByAPI, + Sem_eax, Sem_ebx, Sem_ecx, Sem_edx, Sem_esi, Sem_edi); + + Semaphore.Flag = 0; + SemHeldByAPI = 0; + } + + if (TimerInst == ProcessID) + { + OutputDebugString("BPQ32 Process with Timer called CloseBPQ32\n"); + + if (MinimizetoTray) + Shell_NotifyIcon(NIM_DELETE,&niData); + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External + { + if (PORTVEC->PORT_EXT_ADDR) + { + PORTVEC->PORT_EXT_ADDR(5,PORTVEC->PORTCONTROL.PORTNUMBER, NULL); + } + } + PORTVEC->PORTCONTROL.PORTCLOSECODE(&PORTVEC->PORTCONTROL); + + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + + KillTimer(NULL,TimerHandle); + TimerHandle=0; + TimerInst=0xffffffff; + + IPClose(); + PMClose(); + APRSClose(); + Rig_Close(); + if (AGWActive) + AGWAPITerminate(); + + upnpClose(); + + CloseTNCEmulator(); + WSACleanup(); + + if (hConsWnd) DestroyWindow(hConsWnd); + + Debugprintf("AttachedProcesses %d ", AttachedProcesses); + + if (AttachedProcesses > 1 && Closing == FALSE && AttachingProcess == 0) // Other processes + { + OutputDebugString("BPQ32 Reloading BPQ32.exe\n"); + StartBPQ32(); + } + } + + return 0; +} + +BOOL CopyReg(HKEY hKeyIn, HKEY hKeyOut); + +VOID SetupBPQDirectory() +{ + HKEY hKey = 0; + HKEY hKeyIn = 0; + HKEY hKeyOut = 0; + int disp; + int retCode,Type,Vallen=MAX_PATH,i; + char msg[512]; + char ValfromReg[MAX_PATH] = ""; + char DLLName[256]="Not Known"; + char LogDir[256]; + char Time[64]; + +/* +•NT4 was/is '4' +•Win 95 is 4.00.950 +•Win 98 is 4.10.1998 +•Win 98 SE is 4.10.2222 +•Win ME is 4.90.3000 +•2000 is NT 5.0.2195 +•XP is actually 5.1 +•Vista is 6.0 +•Win7 is 6.1 + + i = _osver; / Build + i = _winmajor; + i = _winminor; +*/ +/* +#pragma warning(push) +#pragma warning(disable : 4996) + +if (_winver < 0x0600) +#pragma warning(pop) + { + // Below Vista + + REGTREE = HKEY_LOCAL_MACHINE; + strcpy(REGTREETEXT, "HKEY_LOCAL_MACHINE"); + ValfromReg[0] = 0; + } + else +*/ + { + if (_stricmp(pgm, "regsvr32.exe") == 0) + { + Debugprintf("BPQ32 loaded by regsvr32.exe - Registry not copied"); + } + else + { + // If necessary, move reg from HKEY_LOCAL_MACHINE to HKEY_CURRENT_USER + + retCode = RegOpenKeyEx (HKEY_LOCAL_MACHINE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, + KEY_READ, + &hKeyIn); + + retCode = RegCreateKeyEx(HKEY_CURRENT_USER, "SOFTWARE\\G8BPQ\\BPQ32", 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKeyOut, &disp); + + // See if Version Key exists in HKEY_CURRENT_USER - if it does, we have already done the copy + + Vallen = MAX_PATH; + retCode = RegQueryValueEx(hKeyOut, "Version" ,0 , &Type,(UCHAR *)&msg, &Vallen); + + if (retCode != ERROR_SUCCESS) + if (hKeyIn) + CopyReg(hKeyIn, hKeyOut); + + RegCloseKey(hKeyIn); + RegCloseKey(hKeyOut); + } + } + + GetModuleFileName(hInstance,DLLName,256); + + BPQDirectory[0]=0; + + retCode = RegOpenKeyEx (REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, + KEY_QUERY_VALUE, + &hKey); + + if (retCode == ERROR_SUCCESS) + { + // Try "BPQ Directory" + + Vallen = MAX_PATH; + retCode = RegQueryValueEx(hKey,"BPQ Directory",0, + &Type,(UCHAR *)&ValfromReg,&Vallen); + + if (retCode == ERROR_SUCCESS) + { + if (strlen(ValfromReg) == 2 && ValfromReg[0] == '"' && ValfromReg[1] == '"') + ValfromReg[0]=0; + } + + if (ValfromReg[0] == 0) + { + // BPQ Directory absent or = "" - try "Config File Location" + + Vallen = MAX_PATH; + + retCode = RegQueryValueEx(hKey,"Config File Location",0, + &Type,(UCHAR *)&ValfromReg,&Vallen); + + if (retCode == ERROR_SUCCESS) + { + if (strlen(ValfromReg) == 2 && ValfromReg[0] == '"' && ValfromReg[1] == '"') + ValfromReg[0]=0; + } + } + + if (ValfromReg[0] == 0) GetCurrentDirectory(MAX_PATH, ValfromReg); + + // Get StartMinimized and MinimizetoTray flags + + Vallen = 4; + retCode = RegQueryValueEx(hKey, "Start Minimized", 0, &Type, (UCHAR *)&StartMinimized, &Vallen); + + Vallen = 4; + retCode = RegQueryValueEx(hKey, "Minimize to Tray", 0, &Type, (UCHAR *)&MinimizetoTray, &Vallen); + + ExpandEnvironmentStrings(ValfromReg, BPQDirectory, MAX_PATH); + + // Also get "BPQ Program Directory" + + ValfromReg[0] = 0; + Vallen = MAX_PATH; + + retCode = RegQueryValueEx(hKey, "BPQ Program Directory",0 , &Type, (UCHAR *)&ValfromReg, &Vallen); + + if (retCode == ERROR_SUCCESS) + ExpandEnvironmentStrings(ValfromReg, BPQProgramDirectory, MAX_PATH); + + // And Log Directory + + ValfromReg[0] = 0; + Vallen = MAX_PATH; + + retCode = RegQueryValueEx(hKey, "Log Directory",0 , &Type, (UCHAR *)&ValfromReg, &Vallen); + + if (retCode == ERROR_SUCCESS) + ExpandEnvironmentStrings(ValfromReg, LogDirectory, MAX_PATH); + + RegCloseKey(hKey); + } + + if (LogDirectory[0] == 0) + strcpy(LogDirectory, BPQDirectory); + + if (BPQProgramDirectory[0] == 0) + strcpy(BPQProgramDirectory, BPQDirectory); + + sprintf(msg,"BPQ32 Ver %s Loaded from: %s by %s\n", VersionString, DLLName, pgm); + WritetoConsole(msg); + OutputDebugString(msg); + FormatTime3(Time, time(NULL)); + sprintf(msg,"Loaded %s\n", Time); + WritetoConsole(msg); + OutputDebugString(msg); + +#pragma warning(push) +#pragma warning(disable : 4996) + +#if _MSC_VER >= 1400 + +#define _winmajor 6 +#define _winminor 0 + +#endif + + i=sprintf(msg,"Windows Ver %d.%d, Using Registry Key %s\n" ,_winmajor, _winminor, REGTREETEXT); + +#pragma warning(pop) + + WritetoConsole(msg); + OutputDebugString(msg); + + i=sprintf(msg,"BPQ32 Using config from: %s\n\n",BPQDirectory); + WritetoConsole(&msg[6]); + msg[i-1]=0; + OutputDebugString(msg); + + // Don't write the Version Key if loaded by regsvr32.exe (Installer is running with Admin rights, + // so will write the wrong tree on ) + + if (_stricmp(pgm, "regsvr32.exe") == 0) + { + Debugprintf("BPQ32 loaded by regsvr32.exe - Version String not written"); + } + else + { + retCode = RegCreateKeyEx(REGTREE, "SOFTWARE\\G8BPQ\\BPQ32", 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + sprintf(msg,"%d,%d,%d,%d", Ver[0], Ver[1], Ver[2], Ver[3]); + retCode = RegSetValueEx(hKey, "Version",0, REG_SZ,(BYTE *)msg, strlen(msg) + 1); + + RegCloseKey(hKey); + } + + // Make sure Logs Directory exists + + sprintf(LogDir, "%s/Logs", LogDirectory); + + CreateDirectory(LogDir, NULL); + + return; +} + +HANDLE OpenConfigFile(char *fn) +{ + HANDLE handle; + UCHAR Value[MAX_PATH]; + FILETIME LastWriteTime; + SYSTEMTIME Time; + char Msg[256]; + + + // If no directory, use current + if (BPQDirectory[0] == 0) + { + strcpy(Value,fn); + } + else + { + strcpy(Value,BPQDirectory); + strcat(Value,"\\"); + strcat(Value,fn); + } + + handle = CreateFile(Value, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + + GetFileTime(handle, NULL, NULL, &LastWriteTime); + FileTimeToSystemTime(&LastWriteTime, &Time); + + sprintf(Msg,"BPQ32 Config File %s Created %.2d:%.2d %d/%.2d/%.2d\n", Value, + Time.wHour, Time.wMinute, Time.wYear, Time.wMonth, Time.wDay); + + OutputDebugString(Msg); + + return(handle); +} + +#ifdef _WIN64 +int BPQHOSTAPI() +{ + return 0; +} +#endif + + +DllExport int APIENTRY GETBPQAPI() +{ + return (int)BPQHOSTAPI; +} + +//DllExport UINT APIENTRY GETMONDECODE() +//{ +// return (UINT)MONDECODE; +//} + + +DllExport INT APIENTRY BPQAPI(int Fn, char * params) +{ + +/* +; +; BPQ HOST MODE SUPPORT CODE +; +; 22/11/95 +; +; MOVED FROM TNCODE.ASM COS CONITIONALS WERE GETTING TOO COMPLICATED +; (OS2 VERSION HAD UPSET KANT VERISON +; +; +*/ + + +/* + + BPQHOSTPORT: +; +; SPECIAL INTERFACE, MAINLY FOR EXTERNAL HOST MODE SUPPORT PROGS +; +; COMMANDS SUPPORTED ARE +; +; AH = 0 Get node/switch version number and description. On return +; AH='B',AL='P',BH='Q',BL=' ' +; DH = major version number and DL = minor version number. +; +; +; AH = 1 Set application mask to value in DL (or even DX if 16 +; applications are ever to be supported). +; +; Set application flag(s) to value in CL (or CX). +; whether user gets connected/disconnected messages issued +; by the node etc. +; +; +; AH = 2 Send frame in ES:SI (length CX) +; +; +; AH = 3 Receive frame into buffer at ES:DI, length of frame returned +; in CX. BX returns the number of outstanding frames still to +; be received (ie. after this one) or zero if no more frames +; (ie. this is last one). +; +; +; +; AH = 4 Get stream status. Returns: +; +; CX = 0 if stream disconnected or CX = 1 if stream connected +; DX = 0 if no change of state since last read, or DX = 1 if +; the connected/disconnected state has changed since +; last read (ie. delta-stream status). +; +; +; +; AH = 6 Session control. +; +; CX = 0 Conneect - _APPLMASK in DL +; CX = 1 connect +; CX = 2 disconnect +; CX = 3 return user to node +; +; +; AH = 7 Get buffer counts for stream. Returns: +; +; AX = number of status change messages to be received +; BX = number of frames queued for receive +; CX = number of un-acked frames to be sent +; DX = number of buffers left in node +; SI = number of trace frames queued for receive +; +;AH = 8 Port control/information. Called with a stream number +; in AL returns: +; +; AL = Radio port on which channel is connected (or zero) +; AH = SESSION TYPE BITS +; BX = L2 paclen for the radio port +; CX = L2 maxframe for the radio port +; DX = L4 window size (if L4 circuit, or zero) +; ES:DI = CALLSIGN + +;AH = 9 Fetch node/application callsign & alias. AL = application +; number: +; +; 0 = node +; 1 = BBS +; 2 = HOST +; 3 = SYSOP etc. etc. +; +; Returns string with alias & callsign or application name in +; user's buffer pointed to by ES:SI length CX. For example: +; +; "WORCS:G8TIC" or "TICPMS:G8TIC-10". +; +; +; AH = 10 Unproto transmit frame. Data pointed to by ES:SI, of +; length CX, is transmitted as a HDLC frame on the radio +; port (not stream) in AL. +; +; +; AH = 11 Get Trace (RAW Data) Frame into ES:DI, +; Length to CX, Timestamp to AX +; +; +; AH = 12 Update Switch. At the moment only Beacon Text may be updated +; DX = Function +; 1=update BT. ES:SI, Len CX = Text +; 2=kick off nodes broadcast +; +; AH = 13 Allocate/deallocate stream +; If AL=0, return first free stream +; If AL>0, CL=1, Allocate stream. If aleady allocated, +; return CX nonzero, else allocate, and return CX=0 +; If AL>0, CL=2, Release stream +; +; +; AH = 14 Internal Interface for IP Router +; +; Send frame - to NETROM L3 if DL=0 +; to L2 Session if DL<>0 +; +; +; AH = 15 Get interval timer + + +*/ + + + switch(Fn) + { + + case CHECKLOADED: + + params[0]=MAJORVERSION; + params[1]=MINORVERSION; + params[2]=QCOUNT; + + return (1); + } + return 0; +} + +DllExport int APIENTRY InitSwitch() +{ + return (0); +} + +/*DllExport int APIENTRY SwitchTimer() +{ + GetSemaphore((&Semaphore); + + TIMERINTERRUPT(); + + FreeSemaphore(&Semaphore); + + return (0); +} +*/ +DllExport int APIENTRY GetFreeBuffs() +{ +// Returns number of free buffers +// (BPQHOST function 7 (part)). + return (QCOUNT); +} + +DllExport UCHAR * APIENTRY GetNodeCall() +{ + return (&MYNODECALL); +} + + +DllExport UCHAR * APIENTRY GetNodeAlias() +{ + return (&MYALIASTEXT[0]); +} + +DllExport UCHAR * APIENTRY GetBBSCall() +{ + return (UCHAR *)(&APPLCALLTABLE[0].APPLCALL_TEXT); +} + + +DllExport UCHAR * APIENTRY GetBBSAlias() +{ + return (UCHAR *)(&APPLCALLTABLE[0].APPLALIAS_TEXT); +} + +DllExport VOID APIENTRY GetApplCallVB(int Appl, char * ApplCall) +{ + if (Appl < 1 || Appl > NumberofAppls ) return; + + strncpy(ApplCall,(char *)&APPLCALLTABLE[Appl-1].APPLCALL_TEXT, 10); +} + +BOOL UpdateNodesForApp(int Appl); + +DllExport BOOL APIENTRY SetApplCall(int Appl, char * NewCall) +{ + char Call[10]=" "; + int i; + + if (Appl < 1 || Appl > NumberofAppls ) return FALSE; + + i=strlen(NewCall); + + if (i > 10) i=10; + + strncpy(Call,NewCall,i); + + strncpy((char *)&APPLCALLTABLE[Appl-1].APPLCALL_TEXT,Call,10); + + if (!ConvToAX25(Call,APPLCALLTABLE[Appl-1].APPLCALL)) return FALSE; + + UpdateNodesForApp(Appl); + + return TRUE; + +} + +DllExport BOOL APIENTRY SetApplAlias(int Appl, char * NewCall) +{ + char Call[10]=" "; + int i; + + if (Appl < 1 || Appl > NumberofAppls ) return FALSE; + + i=strlen(NewCall); + + if (i > 10) i=10; + + strncpy(Call,NewCall,i); + + strncpy((char *)&APPLCALLTABLE[Appl-1].APPLALIAS_TEXT,Call,10); + + if (!ConvToAX25(Call,APPLCALLTABLE[Appl-1].APPLALIAS)) return FALSE; + + UpdateNodesForApp(Appl); + + return TRUE; + +} + + + +DllExport BOOL APIENTRY SetApplQual(int Appl, int NewQual) +{ + if (Appl < 1 || Appl > NumberofAppls ) return FALSE; + + APPLCALLTABLE[Appl-1].APPLQUAL=NewQual; + + UpdateNodesForApp(Appl); + + return TRUE; + +} + + +BOOL UpdateNodesForApp(int Appl) +{ + int App=Appl-1; + int DestLen = sizeof (struct DEST_LIST); + int n = MAXDESTS; + + struct DEST_LIST * DEST = APPLCALLTABLE[App].NODEPOINTER; + APPLCALLS * APPL=&APPLCALLTABLE[App]; + + if (DEST == NULL) + { + // No dest at the moment. If we have valid call and Qual, create an entry + + if (APPLCALLTABLE[App].APPLQUAL == 0) return FALSE; + + if (APPLCALLTABLE[App].APPLCALL[0] < 41) return FALSE; + + + GetSemaphore(&Semaphore, 5); + + DEST = DESTS; + + while (n--) + { + if (DEST->DEST_CALL[0] == 0) // Spare + break; + } + + if (n == 0) + { + // no dests + + FreeSemaphore(&Semaphore); + return FALSE; + } + + NUMBEROFNODES++; + APPL->NODEPOINTER = DEST; + + memmove (DEST->DEST_CALL,APPL->APPLCALL,13); + + DEST->DEST_STATE=0x80; // SPECIAL ENTRY + + DEST->NRROUTE[0].ROUT_QUALITY = (BYTE)APPL->APPLQUAL; + DEST->NRROUTE[0].ROUT_OBSCOUNT = 255; + + FreeSemaphore(&Semaphore); + + return TRUE; + } + + // We have a destination. If Quality is zero, remove it, else update it + + if (APPLCALLTABLE[App].APPLQUAL == 0) + { + GetSemaphore(&Semaphore, 6); + + REMOVENODE(DEST); // Clear buffers, Remove from Sorted Nodes chain, and zap entry + + APPL->NODEPOINTER=NULL; + + FreeSemaphore(&Semaphore); + return FALSE; + + } + + if (APPLCALLTABLE[App].APPLCALL[0] < 41) return FALSE; + + GetSemaphore(&Semaphore, 7); + + memmove (DEST->DEST_CALL,APPL->APPLCALL,13); + + DEST->DEST_STATE=0x80; // SPECIAL ENTRY + + DEST->NRROUTE[0].ROUT_QUALITY = (BYTE)APPL->APPLQUAL; + DEST->NRROUTE[0].ROUT_OBSCOUNT = 255; + + FreeSemaphore(&Semaphore); + return TRUE; + +} + + +DllExport UCHAR * APIENTRY GetSignOnMsg() +{ + return (&SIGNONMSG[0]); +} + + +DllExport HKEY APIENTRY GetRegistryKey() +{ + return REGTREE; +} + +DllExport char * APIENTRY GetRegistryKeyText() +{ + return REGTREETEXT;; +} + +DllExport UCHAR * APIENTRY GetBPQDirectory() +{ + while (BPQDirectory[0] == 0) + { + Debugprintf("BPQ Directory not set up - waiting"); + Sleep(1000); + } + return (&BPQDirectory[0]); +} + +DllExport UCHAR * APIENTRY GetProgramDirectory() +{ + return (&BPQProgramDirectory[0]); +} + +DllExport UCHAR * APIENTRY GetLogDirectory() +{ + return (&LogDirectory[0]); +} + +// Version for Visual Basic + +DllExport char * APIENTRY CopyBPQDirectory(char * dir) +{ + return (strcpy(dir,BPQDirectory)); +} + +DllExport int APIENTRY GetMsgPerl(int stream, char * msg) +{ + int len,count; + + GetMsg(stream, msg, &len, &count ); + + return len; +} + +int Rig_Command(int Session, char * Command); + +BOOL Rig_CommandInt(int Session, char * Command) +{ + return Rig_Command(Session, Command); +} + +DllExport int APIENTRY BPQSetHandle(int Stream, HWND hWnd) +{ + BPQHOSTVECTOR[Stream-1].HOSTHANDLE=hWnd; + return (0); +} + +#define L4USER 0 + +BPQVECSTRUC * PORTVEC ; + +VOID * InitializeExtDriver(PEXTPORTDATA PORTVEC) +{ + HINSTANCE ExtDriver=0; + char msg[128]; + int err=0; + HKEY hKey=0; + UCHAR Value[MAX_PATH]; + + // If no directory, use current + + if (BPQDirectory[0] == 0) + { + strcpy(Value,PORTVEC->PORT_DLL_NAME); + } + else + { + strcpy(Value,BPQDirectory); + strcat(Value,"\\"); + strcat(Value,PORTVEC->PORT_DLL_NAME); + } + + // Several Drivers are now built into bpq32.dll + + _strupr(Value); + + if (strstr(Value, "BPQVKISS")) + return VCOMExtInit; + + if (strstr(Value, "BPQAXIP")) + return AXIPExtInit; + + if (strstr(Value, "BPQETHER")) + return ETHERExtInit; + + if (strstr(Value, "BPQTOAGW")) + return AGWExtInit; + + if (strstr(Value, "AEAPACTOR")) + return AEAExtInit; + + if (strstr(Value, "HALDRIVER")) + return HALExtInit; + + if (strstr(Value, "KAMPACTOR")) + return KAMExtInit; + + if (strstr(Value, "SCSPACTOR")) + return SCSExtInit; + + if (strstr(Value, "WINMOR")) + return WinmorExtInit; + + if (strstr(Value, "V4")) + return V4ExtInit; + + if (strstr(Value, "TELNET")) + return TelnetExtInit; + +// if (strstr(Value, "SOUNDMODEM")) +// return SoundModemExtInit; + + if (strstr(Value, "SCSTRACKER")) + return TrackerExtInit; + + if (strstr(Value, "TRKMULTI")) + return TrackerMExtInit; + + if (strstr(Value, "UZ7HO")) + return UZ7HOExtInit; + + if (strstr(Value, "MULTIPSK")) + return MPSKExtInit; + + if (strstr(Value, "FLDIGI")) + return FLDigiExtInit; + + if (strstr(Value, "UIARQ")) + return UIARQExtInit; + +// if (strstr(Value, "BAYCOM")) +// return (UINT) BaycomExtInit; + + if (strstr(Value, "VARA")) + return VARAExtInit; + + if (strstr(Value, "ARDOP")) + return ARDOPExtInit; + + if (strstr(Value, "SERIAL")) + return SerialExtInit; + + if (strstr(Value, "KISSHF")) + return KISSHFExtInit; + + if (strstr(Value, "WINRPR")) + return WinRPRExtInit; + + if (strstr(Value, "HSMODEM")) + return HSMODEMExtInit; + + if (strstr(Value, "FREEDATA")) + return FreeDataExtInit; + + ExtDriver = LoadLibrary(Value); + + if (ExtDriver == NULL) + { + err=GetLastError(); + + sprintf(msg,"Error loading Driver %s - Error code %d", + PORTVEC->PORT_DLL_NAME,err); + + MessageBox(NULL,msg,"BPQ32",MB_ICONSTOP); + + return(0); + } + + PORTVEC->DLLhandle=ExtDriver; + + return (GetProcAddress(ExtDriver,"_ExtInit@4")); + +} + +/* +_DATABASE LABEL BYTE + +FILLER DB 14 DUP (0) ; PROTECTION AGENST BUFFER PROBLEMS! + DB MAJORVERSION,MINORVERSION +_NEIGHBOURS DD 0 + DW TYPE ROUTE +_MAXNEIGHBOURS DW 20 ; MAX ADJACENT NODES + +_DESTS DD 0 ; NODE LIST + DW TYPE DEST_LIST +MAXDESTS DW 100 ; MAX NODES IN SYSTEM +*/ + + +DllExport int APIENTRY GetAttachedProcesses() +{ + return (AttachedProcesses); +} + +DllExport int * APIENTRY GetAttachedProcessList() +{ + return (&AttachedPIDList[0]); +} + +DllExport int * APIENTRY SaveNodesSupport() +{ + return (&DATABASESTART); +} + +// +// Internal BPQNODES support +// + +#define UCHAR unsigned char + +/* +ROUTE ADD G1HTL-1 2 200 0 0 0 +ROUTE ADD G4IRX-3 2 200 0 0 0 +NODE ADD MAPPLY:G1HTL-1 G1HTL-1 2 200 G4IRX-3 2 98 +NODE ADD NOT:GB7NOT G1HTL-1 2 199 G4IRX-3 2 98 + +*/ + +struct DEST_LIST * Dests; +struct ROUTE * Routes; + +int MaxNodes; +int MaxRoutes; +int NodeLen; +int RouteLen; + +int count; +int cursor; + +int len,i; + +ULONG cnt; +char Normcall[10]; +char Portcall[10]; +char Alias[7]; + +char line[100]; + +HANDLE handle; + +int APIENTRY Restart() +{ + int i, Count = AttachedProcesses; + HANDLE hProc; + DWORD PID; + + for (i = 0; i < Count; i++) + { + PID = AttachedPIDList[i]; + + // Kill Timer Owner last + + if (TimerInst != PID) + { + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, PID); + + if (hProc) + { + TerminateProcess(hProc, 0); + CloseHandle(hProc); + } + } + } + + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, TimerInst); + + if (hProc) + { + TerminateProcess(hProc, 0); + CloseHandle(hProc); + } + + + return 0; +} + +int APIENTRY Reboot() +{ + // Run shutdown -r -f + + STARTUPINFO SInfo; + PROCESS_INFORMATION PInfo; + char Cmd[] = "shutdown -r -f"; + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + return CreateProcess(NULL, Cmd, NULL, NULL, FALSE,0 ,NULL ,NULL, &SInfo, &PInfo); +} +/* +int APIENTRY Reconfig() +{ + if (!ProcessConfig()) + { + return (0); + } + SaveNodes(); + WritetoConsole("Nodes Saved\n"); + ReconfigFlag=TRUE; + WritetoConsole("Reconfig requested ... Waiting for Timer Poll\n"); + return 1; +} +*/ +// Code to support minimizing all BPQ Apps to a single Tray ICON + +// As we can't minimize the console window to the tray, I'll use an ordinary +// window instead. This also gives me somewhere to post the messages to + + +char AppName[] = "BPQ32"; +char Title[80] = "BPQ32.dll Console"; + +int NewLine(); + +char FrameClassName[] = TEXT("MdiFrame"); + +HWND ClientWnd; //This stores the MDI client area window handle + +LOGFONT LFTTYFONT ; + +HFONT hFont ; + +HMENU hPopMenu, hWndMenu; +HMENU hMainFrameMenu = NULL; +HMENU hBaseMenu = NULL; +HMENU hConsMenu = NULL; +HMENU hTermMenu = NULL; +HMENU hMonMenu = NULL; +HMENU hTermActMenu, hTermCfgMenu, hTermEdtMenu, hTermHlpMenu; +HMENU hMonActMenu, hMonCfgMenu, hMonEdtMenu, hMonHlpMenu; + + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +LRESULT CALLBACK StatusWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + +DllExport int APIENTRY DeleteTrayMenuItem(HWND hWnd); + +#define BPQMonitorAvail 1 +#define BPQDataAvail 2 +#define BPQStateChange 4 + +VOID GetJSONValue(char * _REPLYBUFFER, char * Name, char * Value); +SOCKET OpenWL2KHTTPSock(); +SendHTTPRequest(SOCKET sock, char * Request, char * Params, int Len, char * Return); + +BOOL GetWL2KSYSOPInfo(char * Call, char * _REPLYBUFFER); +BOOL UpdateWL2KSYSOPInfo(char * Call, char * SQL); + + +static INT_PTR CALLBACK ConfigWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + { + char _REPLYBUFFER[1000] = ""; + char Value[1000]; + + if (GetWL2KSYSOPInfo(WL2KCall, _REPLYBUFFER)) + { +// if (strstr(_REPLYBUFFER, "\"ErrorMessage\":") == 0) + + GetJSONValue(_REPLYBUFFER, "\"SysopName\":", Value); + SetDlgItemText(hDlg, NAME, Value); + + GetJSONValue(_REPLYBUFFER, "\"GridSquare\":", Value); + SetDlgItemText(hDlg, IDC_Locator, Value); + + GetJSONValue(_REPLYBUFFER, "\"StreetAddress1\":", Value); + SetDlgItemText(hDlg, ADDR1, Value); + + GetJSONValue(_REPLYBUFFER, "\"StreetAddress2\":", Value); + SetDlgItemText(hDlg, ADDR2, Value); + + GetJSONValue(_REPLYBUFFER, "\"City\":", Value); + SetDlgItemText(hDlg, CITY, Value); + + GetJSONValue(_REPLYBUFFER, "\"State\":", Value); + SetDlgItemText(hDlg, STATE, Value); + + GetJSONValue(_REPLYBUFFER, "\"Country\":", Value); + SetDlgItemText(hDlg, COUNTRY, Value); + + GetJSONValue(_REPLYBUFFER, "\"PostalCode\":", Value); + SetDlgItemText(hDlg, POSTCODE, Value); + + GetJSONValue(_REPLYBUFFER, "\"Email\":", Value); + SetDlgItemText(hDlg, EMAIL, Value); + + GetJSONValue(_REPLYBUFFER, "\"Website\":", Value); + SetDlgItemText(hDlg, WEBSITE, Value); + + GetJSONValue(_REPLYBUFFER, "\"Phones\":", Value); + SetDlgItemText(hDlg, PHONE, Value); + + GetJSONValue(_REPLYBUFFER, "\"Comments\":", Value); + SetDlgItemText(hDlg, ADDITIONALDATA, Value); + + } + + return (INT_PTR)TRUE; + } + case WM_COMMAND: + + switch(LOWORD(wParam)) + { + + case ID_SAVE: + { + char Name[100]; + char PasswordText[100]; + char LocatorText[100]; + char Addr1[100]; + char Addr2[100]; + char City[100]; + char State[100]; + char Country[100]; + char PostCode[100]; + char Email[100]; + char Website[100]; + char Phone[100]; + char Data[100]; + + SOCKET sock; + + int Len; + char Message[2048]; + char Reply[2048] = ""; + + + GetDlgItemText(hDlg, NAME, Name, 99); + GetDlgItemText(hDlg, IDC_Password, PasswordText, 99); + GetDlgItemText(hDlg, IDC_Locator, LocatorText, 99); + GetDlgItemText(hDlg, ADDR1, Addr1, 99); + GetDlgItemText(hDlg, ADDR2, Addr2, 99); + GetDlgItemText(hDlg, CITY, City, 99); + GetDlgItemText(hDlg, STATE, State, 99); + GetDlgItemText(hDlg, COUNTRY, Country, 99); + GetDlgItemText(hDlg, POSTCODE, PostCode, 99); + GetDlgItemText(hDlg, EMAIL, Email, 99); + GetDlgItemText(hDlg, WEBSITE, Website, 99); + GetDlgItemText(hDlg, PHONE, Phone, 99); + GetDlgItemText(hDlg, ADDITIONALDATA, Data, 99); + + +//{"Callsign":"String","GridSquare":"String","SysopName":"String", +//"StreetAddress1":"String","StreetAddress2":"String","City":"String", +//"State":"String","Country":"String","PostalCode":"String","Email":"String", +//"Phones":"String","Website":"String","Comments":"String"} + + Len = sprintf(Message, + "\"Callsign\":\"%s\"," + "\"Password\":\"%s\"," + "\"GridSquare\":\"%s\"," + "\"SysopName\":\"%s\"," + "\"StreetAddress1\":\"%s\"," + "\"StreetAddress2\":\"%s\"," + "\"City\":\"%s\"," + "\"State\":\"%s\"," + "\"Country\":\"%s\"," + "\"PostalCode\":\"%s\"," + "\"Email\":\"%s\"," + "\"Phones\":\"%s\"," + "\"Website\":\"%s\"," + "\"Comments\":\"%s\"", + + WL2KCall, PasswordText, LocatorText, Name, Addr1, Addr2, City, State, Country, PostCode, Email, Phone, Website, Data); + + Debugprintf("Sending %s", Message); + + sock = OpenWL2KHTTPSock(); + + if (sock) + { + char * ptr; + + SendHTTPRequest(sock, + "/sysop/add", Message, Len, Reply); + + ptr = strstr(Reply, "\"ErrorCode\":"); + + if (ptr) + { + ptr = strstr(ptr, "Message"); + if (ptr) + { + ptr += 10; + strlop(ptr, '"'); + MessageBox(NULL ,ptr, "Error", MB_OK); + } + } + else + MessageBox(NULL, "Sysop Record Updated", "BPQ32", MB_OK); + + } + closesocket(sock); + } + + case ID_CANCEL: + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + break; + } + } + return (INT_PTR)FALSE; +} + + + +LRESULT CALLBACK UIWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +VOID WINAPI OnTabbedDialogInit(HWND hDlg); + +LRESULT CALLBACK FrameWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + POINT pos; + BOOL ret; + + CLIENTCREATESTRUCT MDIClientCreateStruct; // Structure to be used for MDI client area + //HWND m_hwndSystemInformation = 0; + + if (message == BPQMsg) + { + if (lParam & BPQDataAvail) + DoReceivedData(wParam); + + if (lParam & BPQMonitorAvail) + DoMonData(wParam); + + if (lParam & BPQStateChange) + DoStateChange(wParam); + + return (0); + } + + switch (message) + { + case MY_TRAY_ICON_MESSAGE: + + switch(lParam) + { + case WM_RBUTTONUP: + case WM_LBUTTONUP: + + GetCursorPos(&pos); + + // SetForegroundWindow(FrameWnd); + + TrackPopupMenu(trayMenu, 0, pos.x, pos.y, 0, FrameWnd, 0); + return 0; + } + + break; + + case WM_CTLCOLORDLG: + return (LONG)bgBrush; + + case WM_SIZING: + case WM_SIZE: + + SendMessage(ClientWnd, WM_MDIICONARRANGE, 0 ,0); + break; + + case WM_NCCREATE: + + ret = DefFrameProc(hWnd, ClientWnd, message, wParam, lParam); + return TRUE; + + case WM_CREATE: + + // On creation of main frame, create the MDI client area + + MDIClientCreateStruct.hWindowMenu = NULL; + MDIClientCreateStruct.idFirstChild = IDM_FIRSTCHILD; + + ClientWnd = CreateWindow(TEXT("MDICLIENT"), // predefined value for MDI client area + NULL, // no caption required + WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE, + 0, // No need to give any x/y or height/width since this client + // will just be used to get client windows created, effectively + // in the main window we will be seeing the mainframe window client area itself. + 0, + 0, + 0, + hWnd, + NULL, + hInstance, + (void *) &MDIClientCreateStruct); + + + return 0; + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + if (wmId >= TRAYBASEID && wmId < (TRAYBASEID + 100)) + { + handle=hWndArray[wmId-TRAYBASEID]; + + if (handle == FrameWnd) + ShowWindow(handle, SW_NORMAL); + + if (handle == FrameWnd && FrameMaximized == TRUE) + PostMessage(handle, WM_SYSCOMMAND, SC_MAXIMIZE, 0); + else + PostMessage(handle, WM_SYSCOMMAND, SC_RESTORE, 0); + + SetForegroundWindow(handle); + return 0; + } + + switch(wmId) + { + struct ConsoleInfo * Cinfo = NULL; + + case ID_NEWWINDOW: + Cinfo = CreateChildWindow(0, FALSE); + if (Cinfo) + SendMessage(ClientWnd, WM_MDIACTIVATE, (WPARAM)Cinfo->hConsole, 0); + break; + + case ID_WINDOWS_CASCADE: + SendMessage(ClientWnd, WM_MDICASCADE, 0, 0); + return 0; + + case ID_WINDOWS_TILE: + SendMessage(ClientWnd, WM_MDITILE , MDITILE_HORIZONTAL, 0); + return 0; + + case BPQCLOSEALL: + CloseAllPrograms(); + // SendMessage(ClientWnd, WM_MDIICONARRANGE, 0 ,0); + + return 0; + + case BPQUICONFIG: + { + int err, i=0; + char Title[80]; + WNDCLASS wc; + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = UIWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON) ); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + + wc.lpszMenuName = NULL; + wc.lpszClassName = UIClassName; + + RegisterClass(&wc); + + UIhWnd = CreateDialog(hInstance, UIClassName, 0, NULL); + + if (!UIhWnd) + { + err=GetLastError(); + return FALSE; + } + + wsprintf(Title,"BPQ32 Beacon Configuration"); + MySetWindowText(UIhWnd, Title); + ShowWindow(UIhWnd, SW_NORMAL); + + OnTabbedDialogInit(UIhWnd); // Set up pages + + // UpdateWindow(UIhWnd); + return 0; + } + + + case IDD_WL2KSYSOP: + + if (WL2KCall[0] == 0) + { + MessageBox(NULL,"WL2K Reporting is not configured","BPQ32", MB_OK); + break; + } + + DialogBox(hInstance, MAKEINTRESOURCE(IDD_WL2KSYSOP), hWnd, ConfigWndProc); + break; + + + // Handle MDI Window commands + + default: + { + if(wmId >= IDM_FIRSTCHILD) + { + DefFrameProc(hWnd, ClientWnd, message, wParam, lParam); + } + else + { + HWND hChild = (HWND)SendMessage(ClientWnd, WM_MDIGETACTIVE,0,0); + + if(hChild) + SendMessage(hChild, WM_COMMAND, wParam, lParam); + } + } + } + + break; + + case WM_INITMENUPOPUP: + { + HWND hChild = (HWND)SendMessage(ClientWnd, WM_MDIGETACTIVE,0,0); + + if(hChild) + SendMessage(hChild, WM_INITMENUPOPUP, wParam, lParam); + } + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case SC_MAXIMIZE: + + FrameMaximized = TRUE; + break; + + case SC_RESTORE: + + FrameMaximized = FALSE; + break; + + case SC_MINIMIZE: + + if (MinimizetoTray) + { + ShowWindow(hWnd, SW_HIDE); + return TRUE; + } + } + + return (DefFrameProc(hWnd, ClientWnd, message, wParam, lParam)); + + case WM_CLOSE: + + PostQuitMessage(0); + + if (MinimizetoTray) + DeleteTrayMenuItem(hWnd); + + break; + + default: + return (DefFrameProc(hWnd, ClientWnd, message, wParam, lParam)); + + } + return (DefFrameProc(hWnd, ClientWnd, message, wParam, lParam)); +} + +int OffsetH, OffsetW; + +int SetupConsoleWindow() +{ + WNDCLASS wc; + int i; + int retCode, Type, Vallen; + HKEY hKey=0; + char Size[80]; + WNDCLASSEX wndclassMainFrame; + RECT CRect; + + retCode = RegOpenKeyEx (REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, + KEY_QUERY_VALUE, + &hKey); + + if (retCode == ERROR_SUCCESS) + { + Vallen=80; + + retCode = RegQueryValueEx(hKey,"FrameWindowSize",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + if (retCode == ERROR_SUCCESS) + sscanf(Size,"%d,%d,%d,%d",&FRect.left,&FRect.right,&FRect.top,&FRect.bottom); + + if (FRect.top < - 500 || FRect.left < - 500) + { + FRect.left = 0; + FRect.top = 0; + FRect.right = 600; + FRect.bottom = 400; + } + + + Vallen=80; + retCode = RegQueryValueEx(hKey,"WindowSize",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + if (retCode == ERROR_SUCCESS) + sscanf(Size,"%d,%d,%d,%d,%d",&Rect.left,&Rect.right,&Rect.top,&Rect.bottom, &ConsoleMinimized); + + if (Rect.top < - 500 || Rect.left < - 500) + { + Rect.left = 0; + Rect.top = 0; + Rect.right = 600; + Rect.bottom = 400; + } + + Vallen=80; + + retCode = RegQueryValueEx(hKey,"StatusWindowSize",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + if (retCode == ERROR_SUCCESS) + sscanf(Size, "%d,%d,%d,%d,%d", &StatusRect.left, &StatusRect.right, + &StatusRect.top, &StatusRect.bottom, &StatusMinimized); + + if (StatusRect.top < - 500 || StatusRect.left < - 500) + { + StatusRect.left = 0; + StatusRect.top = 0; + StatusRect.right = 850; + StatusRect.bottom = 500; + } + + + // Get StartMinimized and MinimizetoTray flags + + Vallen = 4; + retCode = RegQueryValueEx(hKey, "Start Minimized", 0, &Type, (UCHAR *)&StartMinimized, &Vallen); + + Vallen = 4; + retCode = RegQueryValueEx(hKey, "Minimize to Tray", 0, &Type, (UCHAR *)&MinimizetoTray, &Vallen); + } + + wndclassMainFrame.cbSize = sizeof(WNDCLASSEX); + wndclassMainFrame.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + wndclassMainFrame.lpfnWndProc = FrameWndProc; + wndclassMainFrame.cbClsExtra = 0; + wndclassMainFrame.cbWndExtra = 0; + wndclassMainFrame.hInstance = hInstance; + wndclassMainFrame.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON)); + wndclassMainFrame.hCursor = LoadCursor(NULL, IDC_ARROW); + wndclassMainFrame.hbrBackground = (HBRUSH) GetStockObject(GRAY_BRUSH); + wndclassMainFrame.lpszMenuName = NULL; + wndclassMainFrame.lpszClassName = FrameClassName; + wndclassMainFrame.hIconSm = NULL; + + if(!RegisterClassEx(&wndclassMainFrame)) + { + return 0; + } + + pindex = 0; + PartLine = FALSE; + + bgBrush = CreateSolidBrush(BGCOLOUR); + +// hMainFrameMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MAINFRAME_MENU)); + + hBaseMenu = LoadMenu(hInstance, MAKEINTRESOURCE(CONS_MENU)); + hConsMenu = GetSubMenu(hBaseMenu, 1); + hWndMenu = GetSubMenu(hBaseMenu, 0); + + hTermMenu = LoadMenu(hInstance, MAKEINTRESOURCE(TERM_MENU)); + hTermActMenu = GetSubMenu(hTermMenu, 1); + hTermCfgMenu = GetSubMenu(hTermMenu, 2); + hTermEdtMenu = GetSubMenu(hTermMenu, 3); + hTermHlpMenu = GetSubMenu(hTermMenu, 4); + + hMonMenu = LoadMenu(hInstance, MAKEINTRESOURCE(MON_MENU)); + hMonCfgMenu = GetSubMenu(hMonMenu, 1); + hMonEdtMenu = GetSubMenu(hMonMenu, 2); + hMonHlpMenu = GetSubMenu(hMonMenu, 3); + + hMainFrameMenu = CreateMenu(); + AppendMenu(hMainFrameMenu, MF_STRING + MF_POPUP, (UINT)hWndMenu, "Window"); + + //Create the main MDI frame window + + ClientWnd = NULL; + + FrameWnd = CreateWindow(FrameClassName, + "BPQ32 Console", + WS_OVERLAPPEDWINDOW |WS_CLIPCHILDREN, + FRect.left, + FRect.top, + FRect.right - FRect.left, + FRect.bottom - FRect.top, + NULL, // handle to parent window + hMainFrameMenu, // handle to menu + hInstance, // handle to the instance of module + NULL); // Long pointer to a value to be passed to the window through the + // CREATESTRUCT structure passed in the lParam parameter the WM_CREATE message + + + // Get Client Params + + if (FrameWnd == 0) + { + Debugprintf("SetupConsoleWindow Create Frame failed %d", GetLastError()); + return 0; + } + + ShowWindow(FrameWnd, SW_RESTORE); + + + GetWindowRect(FrameWnd, &FRect); + OffsetH = FRect.bottom - FRect.top; + OffsetW = FRect.right - FRect.left; + GetClientRect(FrameWnd, &CRect); + OffsetH -= CRect.bottom; + OffsetW -= CRect.right; + OffsetH -= 4; + + // Create Console Window + + wc.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + wc.lpfnWndProc = (WNDPROC)WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(BPQICON)); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wc.lpszMenuName = 0; + wc.lpszClassName = ClassName; + + i=RegisterClass(&wc); + + sprintf (Title, "BPQ32.dll Console Version %s", VersionString); + + hConsWnd = CreateMDIWindow(ClassName, "Console", 0, + 0,0,0,0, ClientWnd, hInstance, 1234); + + i = GetLastError(); + + if (!hConsWnd) { + return (FALSE); + } + + wc.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + wc.lpfnWndProc = (WNDPROC)StatusWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(BPQICON)); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wc.lpszMenuName = 0; + wc.lpszClassName = "Status"; + + i=RegisterClass(&wc); + + if (StatusRect.top < OffsetH) // Make sure not off top of MDI frame + { + int Error = OffsetH - StatusRect.top; + StatusRect.top += Error; + StatusRect.bottom += Error; + } + + StatusWnd = CreateMDIWindow("Status", "Stream Status", 0, + StatusRect.left, StatusRect.top, StatusRect.right - StatusRect.left, + StatusRect.bottom - StatusRect.top, ClientWnd, hInstance, 1234); + + SetTimer(StatusWnd, 1, 1000, NULL); + + hPopMenu = GetSubMenu(hBaseMenu, 1) ; + + if (MinimizetoTray) + CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_CHECKED); + else + CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_UNCHECKED); + + if (StartMinimized) + CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_CHECKED); + else + CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_UNCHECKED); + + DrawMenuBar(hConsWnd); + + // setup default font information + + LFTTYFONT.lfHeight = 12; + LFTTYFONT.lfWidth = 8 ; + LFTTYFONT.lfEscapement = 0 ; + LFTTYFONT.lfOrientation = 0 ; + LFTTYFONT.lfWeight = 0 ; + LFTTYFONT.lfItalic = 0 ; + LFTTYFONT.lfUnderline = 0 ; + LFTTYFONT.lfStrikeOut = 0 ; + LFTTYFONT.lfCharSet = 0; + LFTTYFONT.lfOutPrecision = OUT_DEFAULT_PRECIS ; + LFTTYFONT.lfClipPrecision = CLIP_DEFAULT_PRECIS ; + LFTTYFONT.lfQuality = DEFAULT_QUALITY ; + LFTTYFONT.lfPitchAndFamily = FIXED_PITCH; + lstrcpy(LFTTYFONT.lfFaceName, "FIXEDSYS" ) ; + + hFont = CreateFontIndirect(&LFTTYFONT) ; + + SetWindowText(hConsWnd,Title); + + if (Rect.right < 100 || Rect.bottom < 100) + { + GetWindowRect(hConsWnd, &Rect); + } + + if (Rect.top < OffsetH) // Make sure not off top of MDI frame + { + int Error = OffsetH - Rect.top; + Rect.top += Error; + Rect.bottom += Error; + } + + + MoveWindow(hConsWnd, Rect.left - (OffsetW /2), Rect.top - OffsetH, Rect.right-Rect.left, Rect.bottom-Rect.top, TRUE); + + MoveWindow(StatusWnd, StatusRect.left - (OffsetW /2), StatusRect.top - OffsetH, + StatusRect.right-StatusRect.left, StatusRect.bottom-StatusRect.top, TRUE); + + hWndCons = CreateWindowEx(WS_EX_CLIENTEDGE, "LISTBOX", "", + WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | LBS_NOSEL | WS_VSCROLL | WS_HSCROLL, + Rect.left, Rect.top, Rect.right - Rect.left, Rect.bottom - Rect.top, + hConsWnd, NULL, hInstance, NULL); + +// SendMessage(hWndCons, WM_SETFONT, hFont, 0); + + SendMessage(hWndCons, LB_SETHORIZONTALEXTENT , 1000, 0); + + if (ConsoleMinimized) + ShowWindow(hConsWnd, SW_SHOWMINIMIZED); + else + ShowWindow(hConsWnd, SW_RESTORE); + + if (StatusMinimized) + ShowWindow(StatusWnd, SW_SHOWMINIMIZED); + else + ShowWindow(StatusWnd, SW_RESTORE); + + ShowWindow(FrameWnd, SW_RESTORE); + + + LoadLibrary("riched20.dll"); + + if (StartMinimized) + if (MinimizetoTray) + ShowWindow(FrameWnd, SW_HIDE); + else + ShowWindow(FrameWnd, SW_SHOWMINIMIZED); + else + ShowWindow(FrameWnd, SW_RESTORE); + + CreateMonitorWindow(Size); + + return 0; +} + +DllExport int APIENTRY SetupTrayIcon() +{ + if (MinimizetoTray == 0) + return 0; + + trayMenu = CreatePopupMenu(); + + for( i = 0; i < 100; ++i ) + { + if (strcmp(PopupText[i],"BPQ32 Console") == 0) + { + hWndArray[i] = FrameWnd; + goto doneit; + } + } + + for( i = 0; i < 100; ++i ) + { + if (hWndArray[i] == 0) + { + hWndArray[i] = FrameWnd; + strcpy(PopupText[i],"BPQ32 Console"); + break; + } + } +doneit: + + for( i = 0; i < 100; ++i ) + { + if (hWndArray[i] != 0) + AppendMenu(trayMenu,MF_STRING,TRAYBASEID+i,PopupText[i]); + } + + // Set up Tray ICON + + ZeroMemory(&niData,sizeof(NOTIFYICONDATA)); + + niData.cbSize = sizeof(NOTIFYICONDATA); + + // the ID number can be any UINT you choose and will + // be used to identify your icon in later calls to + // Shell_NotifyIcon + + niData.uID = TRAY_ICON_ID; + + // state which structure members are valid + // here you can also choose the style of tooltip + // window if any - specifying a balloon window: + // NIF_INFO is a little more complicated + + strcpy(niData.szTip,"BPQ32 Windows"); + + niData.uFlags = NIF_ICON|NIF_MESSAGE|NIF_TIP; + + // load the icon note: you should destroy the icon + // after the call to Shell_NotifyIcon + + niData.hIcon = + + //LoadIcon(NULL, IDI_APPLICATION); + + (HICON)LoadImage( hInstance, + MAKEINTRESOURCE(BPQICON), + IMAGE_ICON, + GetSystemMetrics(SM_CXSMICON), + GetSystemMetrics(SM_CYSMICON), + LR_DEFAULTCOLOR); + + + // set the window you want to receive event messages + + niData.hWnd = FrameWnd; + + // set the message to send + // note: the message value should be in the + // range of WM_APP through 0xBFFF + + niData.uCallbackMessage = MY_TRAY_ICON_MESSAGE; + + // Call Shell_NotifyIcon. NIM_ADD adds a new tray icon + + if (Shell_NotifyIcon(NIM_ADD,&niData)) + Debugprintf("BPQ32 Create Tray Icon Ok"); +// else +// Debugprintf("BPQ32 Create Tray Icon failed %d", GetLastError()); + + return 0; +} + +VOID SaveConfig() +{ + HKEY hKey=0; + int retCode, disp; + + retCode = RegCreateKeyEx(REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, // Reserved + 0, // Class + 0, // Options + KEY_ALL_ACCESS, + NULL, // Security Attrs + &hKey, + &disp); + + if (retCode == ERROR_SUCCESS) + { + retCode = RegSetValueEx(hKey, "Start Minimized", 0, REG_DWORD, (UCHAR *)&StartMinimized, 4); + retCode = RegSetValueEx(hKey, "Minimize to Tray", 0, REG_DWORD, (UCHAR *)&MinimizetoTray, 4); + } +} + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + POINT pos; + HWND handle; + RECT cRect; + + switch (message) + { + case WM_MDIACTIVATE: + + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + // GetSubMenu function should retrieve a handle to the drop-down menu or submenu. + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)hConsMenu, "Actions"); + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); + } + else + { + // Deactivate + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + } + + DrawMenuBar(FrameWnd); + + return TRUE; //DefMDIChildProc(hWnd, message, wParam, lParam); + + case MY_TRAY_ICON_MESSAGE: + + switch(lParam) + { + case WM_RBUTTONUP: + case WM_LBUTTONUP: + + GetCursorPos(&pos); + + SetForegroundWindow(hWnd); + + TrackPopupMenu(trayMenu, 0, pos.x, pos.y, 0, hWnd, 0); + return 0; + } + + break; + + case WM_CTLCOLORDLG: + return (LONG)bgBrush; + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + if (wmId == IDC_ENIGATE) + { + int retCode, disp; + HKEY hKey=0; + + IGateEnabled = IsDlgButtonChecked(hWnd, IDC_ENIGATE); + + if (IGateEnabled) + ISDelayTimer = 60; + + retCode = RegCreateKeyEx(REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, // Reserved + 0, // Class + 0, // Options + KEY_ALL_ACCESS, + NULL, // Security Attrs + &hKey, + &disp); + + if (retCode == ERROR_SUCCESS) + { + retCode = RegSetValueEx(hKey,"IGateEnabled", 0 , REG_DWORD,(BYTE *)&IGateEnabled, 4); + RegCloseKey(hKey); + } + + return 0; + } + + if (wmId == BPQSAVENODES) + { + SaveNodes(); + WritetoConsole("Nodes Saved\n"); + return 0; + } + if (wmId == BPQCLEARRECONFIG) + { + if (!ProcessConfig()) + { + MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); + return (0); + } + + ClearNodes(); + WritetoConsole("Nodes file Cleared\n"); + ReconfigFlag=TRUE; + WritetoConsole("Reconfig requested ... Waiting for Timer Poll\n"); + return 0; + } + if (wmId == BPQRECONFIG) + { + if (!ProcessConfig()) + { + MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); + return (0); + } + SaveNodes(); + WritetoConsole("Nodes Saved\n"); + ReconfigFlag=TRUE; + WritetoConsole("Reconfig requested ... Waiting for Timer Poll\n"); + return 0; + } + + if (wmId == SCANRECONFIG) + { + if (!ProcessConfig()) + { + MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); + return (0); + } + + RigReconfigFlag = TRUE; + WritetoConsole("Rigcontrol Reconfig requested ... Waiting for Timer Poll\n"); + return 0; + } + + if (wmId == APRSRECONFIG) + { + if (!ProcessConfig()) + { + MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); + return (0); + } + + APRSReconfigFlag=TRUE; + WritetoConsole("APRS Reconfig requested ... Waiting for Timer Poll\n"); + return 0; + } + if (wmId == BPQDUMP) + { + DumpSystem(); + return 0; + } + + if (wmId == BPQCLOSEALL) + { + CloseAllPrograms(); + return 0; + } + + if (wmId == BPQUICONFIG) + { + int err, i=0; + char Title[80]; + WNDCLASS wc; + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = UIWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON) ); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + + wc.lpszMenuName = NULL; + wc.lpszClassName = UIClassName; + + RegisterClass(&wc); + + UIhWnd = CreateDialog(hInstance, UIClassName,0,NULL); + + if (!UIhWnd) + { + err=GetLastError(); + return FALSE; + } + + wsprintf(Title,"BPQ32 Beacon Utility Version"); + MySetWindowText(UIhWnd, Title); + return 0; + } + + if (wmId == BPQSAVEREG) + { + CreateRegBackup(); + return 0; + } + + if (wmId == BPQMINTOTRAY) + { + MinimizetoTray = !MinimizetoTray; + + if (MinimizetoTray) + CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_CHECKED); + else + CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_UNCHECKED); + + SaveConfig(); + return 0; + } + + if (wmId == BPQSTARTMIN) + { + StartMinimized = !StartMinimized; + + if (StartMinimized) + CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_CHECKED); + else + CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_UNCHECKED); + + SaveConfig(); + return 0; + } + + if (wmId >= TRAYBASEID && wmId < (TRAYBASEID + 100)) + { + handle=hWndArray[wmId-TRAYBASEID]; + + if (handle == FrameWnd && FrameMaximized == TRUE) + PostMessage(handle, WM_SYSCOMMAND, SC_MAXIMIZE, 0); + else + PostMessage(handle, WM_SYSCOMMAND, SC_RESTORE, 0); + + SetForegroundWindow(handle); + return 0; + } + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case SC_MINIMIZE: + + ConsoleMinimized = TRUE; + break; + + case SC_RESTORE: + + ConsoleMinimized = FALSE; + SendMessage(ClientWnd, WM_MDIRESTORE, (WPARAM)hWnd, 0); + + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + + case WM_SIZE: + + GetClientRect(hWnd, &cRect); + + MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); + + if (APRSActive) + MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); + else + MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); + +// InvalidateRect(hWnd, NULL, TRUE); + break; + +/* + case WM_PAINT: + + hdc = BeginPaint (hWnd, &ps); + + hOldFont = SelectObject( hdc, hFont) ; + + for (i=0; i 300) + len = 300; + + memcpy(&buffptr[2], buff, len + 1); + + C_Q_ADD(&WritetoConsoleQ, buffptr); + + return 0; +} + +int WritetoConsoleSupport(char * buff) +{ + + int len=strlen(buff); + char Temp[2000]= ""; + char * ptr; + + if (PartLine) + { + SendMessage(hWndCons, LB_GETTEXT, pindex, (LPARAM)(LPCTSTR) Temp); + SendMessage(hWndCons, LB_DELETESTRING, pindex, 0); + PartLine = FALSE; + } + + if ((strlen(Temp) + strlen(buff)) > 1990) + Temp[0] = 0; // Should never have anything this long + + strcat(Temp, buff); + + ptr = strchr(Temp, '\n'); + + if (ptr) + *ptr = 0; + else + PartLine = TRUE; + + pindex=SendMessage(hWndCons, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR) Temp); + return 0; + } + +DllExport VOID APIENTRY BPQOutputDebugString(char * String) +{ + OutputDebugString(String); + return; + } + +HANDLE handle; +char fn[]="BPQDUMP"; +ULONG cnt; +char * stack; +//char screen[1920]; +//COORD ReadCoord; + +#define DATABYTES 400000 + +extern UCHAR DATAAREA[]; + +DllExport int APIENTRY DumpSystem() +{ + char fn[200]; + char Msg[250]; + + sprintf(fn,"%s\\BPQDUMP",BPQDirectory); + + handle = CreateFile(fn, + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + +#ifndef _WIN64 + + _asm { + + mov stack,esp + } + + WriteFile(handle,stack,128,&cnt,NULL); +#endif + +// WriteFile(handle,Screen,MAXLINELEN*MAXSCREENLEN,&cnt,NULL); + + WriteFile(handle,DATAAREA, DATABYTES,&cnt,NULL); + + CloseHandle(handle); + + sprintf(Msg, "Dump to %s Completed\n", fn); + WritetoConsole(Msg); + + FindLostBuffers(); + + return (0); +} + +BOOLEAN CheckifBPQ32isLoaded() +{ + HANDLE Mutex; + + // See if BPQ32 is running - if we create it in the NTVDM address space by + // loading bpq32.dll it will not work. + + Mutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"BPQLOCKMUTEX"); + + if (Mutex == NULL) + { + if (AttachingProcess == 0) // Already starting BPQ32 + { + OutputDebugString("BPQ32 No other bpq32 programs running - Loading BPQ32.exe\n"); + StartBPQ32(); + } + return FALSE; + } + + CloseHandle(Mutex); + + return TRUE; +} + +BOOLEAN StartBPQ32() +{ + UCHAR Value[100]; + + char bpq[]="BPQ32.exe"; + char *fn=(char *)&bpq; + HKEY hKey=0; + int ret,Type,Vallen=99; + + char Errbuff[100]; + char buff[20]; + + STARTUPINFO StartupInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION ProcessInformation; // pointer to PROCESS_INFORMATION + + AttachingProcess = 1; + +// Get address of BPQ Directory + + Value[0]=0; + + ret = RegOpenKeyEx (REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, + KEY_QUERY_VALUE, + &hKey); + + if (ret == ERROR_SUCCESS) + { + ret = RegQueryValueEx(hKey, "BPQ Program Directory", 0, &Type,(UCHAR *)&Value, &Vallen); + + if (ret == ERROR_SUCCESS) + { + if (strlen(Value) == 2 && Value[0] == '"' && Value[1] == '"') + Value[0]=0; + } + + + if (Value[0] == 0) + { + + // BPQ Directory absent or = "" - "try Config File Location" + + ret = RegQueryValueEx(hKey,"BPQ Directory",0, + &Type,(UCHAR *)&Value,&Vallen); + + if (ret == ERROR_SUCCESS) + { + if (strlen(Value) == 2 && Value[0] == '"' && Value[1] == '"') + Value[0]=0; + } + + } + RegCloseKey(hKey); + } + + if (Value[0] == 0) + { + strcpy(Value,fn); + } + else + { + strcat(Value,"\\"); + strcat(Value,fn); + } + + StartupInfo.cb=sizeof(StartupInfo); + StartupInfo.lpReserved=NULL; + StartupInfo.lpDesktop=NULL; + StartupInfo.lpTitle=NULL; + StartupInfo.dwFlags=0; + StartupInfo.cbReserved2=0; + StartupInfo.lpReserved2=NULL; + + if (!CreateProcess(Value,NULL,NULL,NULL,FALSE, + CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP, + NULL,NULL,&StartupInfo,&ProcessInformation)) + { + ret=GetLastError(); + + _itoa(ret,buff,10); + + strcpy(Errbuff, "BPQ32 Load "); + strcat(Errbuff,Value); + strcat(Errbuff," failed "); + strcat(Errbuff,buff); + OutputDebugString(Errbuff); + AttachingProcess = 0; + return FALSE; + } + + return TRUE; +} + + +DllExport BPQVECSTRUC * APIENTRY GetIPVectorAddr() +{ + return &IPHOSTVECTOR; +} + +DllExport UINT APIENTRY GETSENDNETFRAMEADDR() +{ + return (UINT)&SENDNETFRAME; +} + +DllExport VOID APIENTRY RelBuff(VOID * Msg) +{ + UINT * pointer, * BUFF = Msg; + + if (Semaphore.Flag == 0) + Debugprintf("ReleaseBuffer called without semaphore"); + + pointer = FREE_Q; + + *BUFF =(UINT)pointer; + + FREE_Q = BUFF; + + QCOUNT++; + + return; +} + +extern int MINBUFFCOUNT; + +DllExport VOID * APIENTRY GetBuff() +{ + UINT * Temp = Q_REM(&FREE_Q); + + if (Semaphore.Flag == 0) + Debugprintf("GetBuff called without semaphore"); + + if (Temp) + { + QCOUNT--; + + if (QCOUNT < MINBUFFCOUNT) + MINBUFFCOUNT = QCOUNT; + } + + return Temp; +} + + +VOID __cdecl Debugprintf(const char * format, ...) +{ + char Mess[10000]; + va_list(arglist); + + va_start(arglist, format); + vsprintf(Mess, format, arglist); + strcat(Mess, "\r\n"); + OutputDebugString(Mess); + + return; +} + +unsigned short int compute_crc(unsigned char *buf, int txlen); + +extern SOCKADDR_IN reportdest; + +extern SOCKET ReportSocket; + +extern SOCKADDR_IN Chatreportdest; + +DllExport VOID APIENTRY SendChatReport(SOCKET ChatReportSocket, char * buff, int txlen) +{ + unsigned short int crc = compute_crc(buff, txlen); + + crc ^= 0xffff; + + buff[txlen++] = (crc&0xff); + buff[txlen++] = (crc>>8); + + sendto(ChatReportSocket, buff, txlen, 0, (LPSOCKADDR)&Chatreportdest, sizeof(Chatreportdest)); +} + +VOID CreateRegBackup() +{ + char Backup1[MAX_PATH]; + char Backup2[MAX_PATH]; + char RegFileName[MAX_PATH]; + char Msg[80]; + HANDLE handle; + int len, written; + char RegLine[300]; + +// SHELLEXECUTEINFO sei; +// STARTUPINFO SInfo; +// PROCESS_INFORMATION PInfo; + + sprintf(RegFileName, "%s\\BPQ32.reg", BPQDirectory); + + // Keep 4 Generations + + strcpy(Backup2, RegFileName); + strcat(Backup2, ".bak.3"); + + strcpy(Backup1, RegFileName); + strcat(Backup1, ".bak.2"); + + DeleteFile(Backup2); // Remove old .bak.3 + MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 + + strcpy(Backup2, RegFileName); + strcat(Backup2, ".bak.1"); + + MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 + + strcpy(Backup1, RegFileName); + strcat(Backup1, ".bak"); + + MoveFile(Backup1, Backup2); //Move .bak to .bak.1 + + strcpy(Backup2, RegFileName); + strcat(Backup2, ".bak"); + + CopyFile(RegFileName, Backup2, FALSE); // Copy to .bak + + handle = CreateFile(RegFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if (handle == INVALID_HANDLE_VALUE) + { + sprintf(Msg, "Failed to open Registry Save File\n"); + WritetoConsole(Msg); + return; + } + + len = sprintf(RegLine, "Windows Registry Editor Version 5.00\r\n\r\n"); + WriteFile(handle, RegLine, len, &written, NULL); + + if (SaveReg("Software\\G8BPQ\\BPQ32", handle)) + WritetoConsole("Registry Save complete\n"); + else + WritetoConsole("Registry Save failed\n"); + + CloseHandle(handle); + return ; +/* + + if (REGTREE == HKEY_LOCAL_MACHINE) // < Vista + { + sprintf(cmd, + "regedit /E \"%s\\BPQ32.reg\" %s\\Software\\G8BPQ\\BPQ32", BPQDirectory, REGTREETEXT); + + ZeroMemory(&SInfo, sizeof(SInfo)); + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + if (CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0 ,NULL, NULL, &SInfo, &PInfo) == 0) + { + sprintf(Msg, "Error: CreateProcess for regedit failed 0%d\n", GetLastError() ); + WritetoConsole(Msg); + return; + } + } + else + { + + sprintf(cmd, + "/E \"%s\\BPQ32.reg\" %s\\Software\\G8BPQ\\BPQ32", BPQDirectory, REGTREETEXT); + + ZeroMemory(&sei, sizeof(sei)); + + sei.cbSize = sizeof(SHELLEXECUTEINFOW); + sei.hwnd = hWnd; + sei.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI; + sei.lpVerb = "runas"; + sei.lpFile = "regedit.exe"; + sei.lpParameters = cmd; + sei.nShow = SW_SHOWNORMAL; + + if (!ShellExecuteEx(&sei)) + { + sprintf(Msg, "Error: ShellExecuteEx for regedit failed %d\n", GetLastError() ); + WritetoConsole(Msg); + return; + } + } + + sprintf(Msg, "Registry Save Initiated\n", fn); + WritetoConsole(Msg); + + return ; +*/ +} + +BOOL CALLBACK EnumForCloseProc(HWND hwnd, LPARAM lParam) +{ + struct TNCINFO * TNC = (struct TNCINFO *)lParam; + UINT ProcessId; + + GetWindowThreadProcessId(hwnd, &ProcessId); + + for (i=0; i< AttachedProcesses; i++) + { + if (AttachedPIDList[i] == ProcessId) + { + Debugprintf("BPQ32 Close All Closing PID %d", ProcessId); + PostMessage(hwnd, WM_CLOSE, 1, 1); + // AttachedPIDList[i] = 0; // So we don't do it again + break; + } + } + + return (TRUE); +} +DllExport BOOL APIENTRY RestoreFrameWindow() +{ + return ShowWindow(FrameWnd, SW_RESTORE); +} + +DllExport VOID APIENTRY CreateNewTrayIcon() +{ + Shell_NotifyIcon(NIM_DELETE,&niData); + trayMenu = NULL; +} + +DllExport VOID APIENTRY CloseAllPrograms() +{ +// HANDLE hProc; + + // Close all attached BPQ32 programs + + Closing = TRUE; + + ShowWindow(FrameWnd, SW_RESTORE); + + GetWindowRect(FrameWnd, &FRect); + + SaveBPQ32Windows(); + CloseHostSessions(); + + if (AttachedProcesses == 1) + CloseBPQ32(); + + Debugprintf("BPQ32 Close All Processes %d PIDS %d %d %d %d", AttachedProcesses, AttachedPIDList[0], + AttachedPIDList[1], AttachedPIDList[2], AttachedPIDList[3]); + + if (MinimizetoTray) + Shell_NotifyIcon(NIM_DELETE,&niData); + + EnumWindows(EnumForCloseProc, (LPARAM)NULL); +} + +#define MAX_KEY_LENGTH 255 +#define MAX_VALUE_NAME 16383 +#define MAX_VALUE_DATA 65536 + +BOOL CopyReg(HKEY hKeyIn, HKEY hKeyOut) +{ + TCHAR achKey[MAX_KEY_LENGTH]; // buffer for subkey name + DWORD cbName; // size of name string + TCHAR achClass[MAX_PATH] = TEXT(""); // buffer for class name + DWORD cchClassName = MAX_PATH; // size of class string + DWORD cSubKeys=0; // number of subkeys + DWORD cbMaxSubKey; // longest subkey size + DWORD cchMaxClass; // longest class string + DWORD cValues; // number of values for key + DWORD cchMaxValue; // longest value name + DWORD cbMaxValueData; // longest value data + DWORD cbSecurityDescriptor; // size of security descriptor + FILETIME ftLastWriteTime; // last write time + + DWORD i, retCode; + + TCHAR achValue[MAX_VALUE_NAME]; + DWORD cchValue = MAX_VALUE_NAME; + + // Get the class name and the value count. + retCode = RegQueryInfoKey( + hKeyIn, // key handle + achClass, // buffer for class name + &cchClassName, // size of class string + NULL, // reserved + &cSubKeys, // number of subkeys + &cbMaxSubKey, // longest subkey size + &cchMaxClass, // longest class string + &cValues, // number of values for this key + &cchMaxValue, // longest value name + &cbMaxValueData, // longest value data + &cbSecurityDescriptor, // security descriptor + &ftLastWriteTime); // last write time + + // Enumerate the subkeys, until RegEnumKeyEx fails. + + if (cSubKeys) + { + Debugprintf( "\nNumber of subkeys: %d\n", cSubKeys); + + for (i=0; i 76) + { + len = sprintf(RegLine, "%s\\\r\n", RegLine); + WriteFile(hFile, RegLine, len, &written, NULL); + strcpy(RegLine, " "); + len = 2; + } + + len = sprintf(RegLine, "%s%02x,", RegLine, Value[k]); + } + RegLine[--len] = 0x0d; + RegLine[++len] = 0x0a; + len++; + + break; + + case REG_DWORD: //( 4 ) // 32-bit number +// case REG_DWORD_LITTLE_ENDIAN: //( 4 ) // 32-bit number (same as REG_DWORD) + + memcpy(&Intval, Value, 4); + len = sprintf(RegLine, "\"%s\"=dword:%08x\r\n", achValue, Intval); + break; + + case REG_DWORD_BIG_ENDIAN: //( 5 ) // 32-bit number + break; + case REG_LINK: //( 6 ) // Symbolic Link (unicode) + break; + case REG_MULTI_SZ: //( 7 ) // Multiple Unicode strings + + len = sprintf(RegLine, "\"%s\"=hex(7):%02x,00,", achValue, Value[0]); + for (k = 1; k < ValLen; k++) + { + if (len > 76) + { + len = sprintf(RegLine, "%s\\\r\n", RegLine); + WriteFile(hFile, RegLine, len, &written, NULL); + strcpy(RegLine, " "); + len = 2; + } + len = sprintf(RegLine, "%s%02x,", RegLine, Value[k]); + if (len > 76) + { + len = sprintf(RegLine, "%s\\\r\n", RegLine); + WriteFile(hFile, RegLine, len, &written, NULL); + strcpy(RegLine, " "); + } + len = sprintf(RegLine, "%s00,", RegLine); + } + + RegLine[--len] = 0x0d; + RegLine[++len] = 0x0a; + len++; + break; + + case REG_RESOURCE_LIST: //( 8 ) // Resource list in the resource map + break; + case REG_FULL_RESOURCE_DESCRIPTOR: //( 9 ) // Resource list in the hardware description + break; + case REG_RESOURCE_REQUIREMENTS_LIST://( 10 ) + break; + case REG_QWORD: //( 11 ) // 64-bit number +// case REG_QWORD_LITTLE_ENDIAN: //( 11 ) // 64-bit number (same as REG_QWORD) + break; + + } + + WriteFile(hFile, RegLine, len, &written, NULL); + } + } + } + + WriteFile(hFile, "\r\n", 2, &written, NULL); + + // Enumerate the subkeys, until RegEnumKeyEx fails. + + if (cSubKeys) + { + for (i=0; i> 1; + } + + Flags=GetApplFlags(i); + + if (OneBits > 1) + sprintf(&NewScreen[(i+1)*54],"%2d%s%3d %3d %3d %03x %3x %10s%-20s", + i, flag, RXCount(i), TXCount(i), MONCount(i), Mask, Flags, callsign, + BPQHOSTVECTOR[i-1].PgmName); + else + sprintf(&NewScreen[(i+1)*54],"%2d%s%3d %3d %3d %3d %3x %10s%-20s", + i, flag, RXCount(i), TXCount(i), MONCount(i), AppNumber, Flags, callsign, + BPQHOSTVECTOR[i-1].PgmName); + + } + } + + #include "StdExcept.c" + + if (Semaphore.Flag && Semaphore.SemProcessID == GetCurrentProcessId()) + FreeSemaphore(&Semaphore); + + } + + if (memcmp(Screen, NewScreen, 33 * 108) == 0) // No Change + return 0; + + memcpy(Screen, NewScreen, 33 * 108); + InvalidateRect(StatusWnd,NULL,FALSE); + + return(0); +} + +LRESULT CALLBACK StatusWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + PAINTSTRUCT ps; + HDC hdc; + HFONT hOldFont ; + HGLOBAL hMem; + MINMAXINFO * mmi; + int i; + + switch (message) + { + case WM_TIMER: + + if (Semaphore.Flag == 0) + DoStatus(); + break; + + case WM_MDIACTIVATE: + + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)hConsMenu, "Actions"); + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); + } + else + { + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + } + + DrawMenuBar(FrameWnd); + + return TRUE; //DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_GETMINMAXINFO: + + mmi = (MINMAXINFO *)lParam; + mmi->ptMaxSize.x = 850; + mmi->ptMaxSize.y = 500; + mmi->ptMaxTrackSize.x = 850; + mmi->ptMaxTrackSize.y = 500; + + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + //Parse the menu selections: + + switch (wmId) + { + +/* + case BPQSTREAMS: + + CheckMenuItem(hMenu,BPQSTREAMS,MF_CHECKED); + CheckMenuItem(hMenu,BPQIPSTATUS,MF_UNCHECKED); + + StreamDisplay = TRUE; + + break; + + case BPQIPSTATUS: + + CheckMenuItem(hMenu,BPQSTREAMS,MF_UNCHECKED); + CheckMenuItem(hMenu,BPQIPSTATUS,MF_CHECKED); + + StreamDisplay = FALSE; + memset(Screen, ' ', 4000); + + + break; + +*/ + + case BPQCOPY: + + // + // Copy buffer to clipboard + // + hMem=GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, 33*110); + + if (hMem != 0) + { + if (OpenClipboard(hWnd)) + { +// CopyScreentoBuffer(GlobalLock(hMem)); + GlobalUnlock(hMem); + EmptyClipboard(); + SetClipboardData(CF_TEXT,hMem); + CloseClipboard(); + } + else + { + GlobalFree(hMem); + } + + } + + break; + + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case SC_MAXIMIZE: + + break; + + case SC_MINIMIZE: + + StatusMinimized = TRUE; + break; + + case SC_RESTORE: + + StatusMinimized = FALSE; + SendMessage(ClientWnd, WM_MDIRESTORE, (WPARAM)hWnd, 0); + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_PAINT: + + hdc = BeginPaint (hWnd, &ps); + + hOldFont = SelectObject( hdc, hFont) ; + + for (i=0; i<33; i++) + { + TextOut(hdc,0,i*14,&Screen[i*108],108); + } + + SelectObject( hdc, hOldFont ) ; + EndPaint (hWnd, &ps); + + break; + + case WM_DESTROY: + +// PostQuitMessage(0); + + break; + + + default: + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + } + return (0); +} + +VOID SaveMDIWindowPos(HWND hWnd, char * RegKey, char * Value, BOOL Minimized) +{ + HKEY hKey=0; + char Size[80]; + char Key[80]; + int retCode, disp; + RECT Rect; + + if (IsWindow(hWnd) == FALSE) + return; + + ShowWindow(hWnd, SW_RESTORE); + + if (GetWindowRect(hWnd, &Rect) == FALSE) + return; + + // Make relative to Frame + + Rect.top -= FRect.top ; + Rect.left -= FRect.left; + Rect.bottom -= FRect.top; + Rect.right -= FRect.left; + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\%s", RegKey); + + retCode = RegCreateKeyEx(REGTREE, Key, 0, 0, 0, + KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + sprintf(Size,"%d,%d,%d,%d,%d", Rect.left, Rect.right, Rect.top ,Rect.bottom, Minimized); + retCode = RegSetValueEx(hKey, Value, 0, REG_SZ,(BYTE *)&Size, strlen(Size)); + RegCloseKey(hKey); + } +} + +extern int GPSPort; +extern char LAT[]; // in standard APRS Format +extern char LON[]; // in standard APRS Format + +VOID SaveBPQ32Windows() +{ + HKEY hKey=0; + char Size[80]; + int retCode, disp; + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + int i; + + retCode = RegCreateKeyEx(REGTREE, "SOFTWARE\\G8BPQ\\BPQ32", 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + sprintf(Size,"%d,%d,%d,%d", FRect.left, FRect.right, FRect.top, FRect.bottom); + retCode = RegSetValueEx(hKey, "FrameWindowSize", 0, REG_SZ, (BYTE *)&Size, strlen(Size)); + + // Save GPS Position + + if (GPSPort) + { + sprintf(Size, "%s, %s", LAT, LON); + retCode = RegSetValueEx(hKey, "GPS", 0, REG_SZ,(BYTE *)&Size, strlen(Size)); + } + + RegCloseKey(hKey); + } + + SaveMDIWindowPos(StatusWnd, "", "StatusWindowSize", StatusMinimized); + SaveMDIWindowPos(hConsWnd, "", "WindowSize", ConsoleMinimized); + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External + { + if (PORTVEC->PORT_EXT_ADDR) + { + SaveWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + SaveAXIPWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + } + } + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + + SaveWindowPos(40); // Rigcontrol + + + if (hIPResWnd) + SaveMDIWindowPos(hIPResWnd, "", "IPResSize", IPMinimized); + + SaveHostSessions(); +} + +DllExport BOOL APIENTRY CheckIfOwner() +{ + // + // Returns TRUE if current process is root process + // that loaded the DLL + // + + if (TimerInst == GetCurrentProcessId()) + + return (TRUE); + else + return (FALSE); +} + +VOID GetParam(char * input, char * key, char * value) +{ + char * ptr = strstr(input, key); + char Param[2048]; + char * ptr1, * ptr2; + char c; + + if (ptr) + { + ptr2 = strchr(ptr, '&'); + if (ptr2) *ptr2 = 0; + strcpy(Param, ptr + strlen(key)); + if (ptr2) *ptr2 = '&'; // Restore string + + // Undo any % transparency + + ptr1 = Param; + ptr2 = Param; + + c = *(ptr1++); + + while (c) + { + if (c == '%') + { + int n; + int m = *(ptr1++) - '0'; + if (m > 9) m = m - 7; + n = *(ptr1++) - '0'; + if (n > 9) n = n - 7; + + *(ptr2++) = m * 16 + n; + } + else if (c == '+') + *(ptr2++) = ' '; + else + *(ptr2++) = c; + + c = *(ptr1++); + } + + *(ptr2++) = 0; + + strcpy(value, Param); + } +} + +int GetListeningPortsPID(int Port) +{ + MIB_TCPTABLE_OWNER_PID * TcpTable = NULL; + PMIB_TCPROW_OWNER_PID Row; + int dwSize = 0; + DWORD n; + + // Get PID of process for this TCP Port + + // Get Length of table + + GetExtendedTcpTable(TcpTable, &dwSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_LISTENER, 0); + + TcpTable = malloc(dwSize); + + if (TcpTable == NULL) + return 0; + + GetExtendedTcpTable(TcpTable, &dwSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_LISTENER, 0); + + for (n = 0; n < TcpTable->dwNumEntries; n++) + { + Row = &TcpTable->table[n]; + + if (Row->dwLocalPort == Port && Row->dwState == MIB_TCP_STATE_LISTEN) + { + return Row->dwOwningPid; + break; + } + } + return 0; // Not found +} + +DllExport char * APIENTRY GetLOC() +{ + return LOC; +} + +// UZ7HO Dll PTT interface + +// 1 ext_PTT_info +// 2 ext_PTT_settings +// 3 ext_PTT_OFF +// 4 ext_PTT_ON +// 5 ext_PTT_close +// 6 ext_PTT_open + +extern struct RIGINFO * DLLRIG; // Rig record for dll PTT interface (currently only for UZ7HO); + +VOID Rig_PTT(struct TNCINFO * TNC, BOOL PTTState); +VOID Rig_PTTEx(struct RIGINFO * RIG, BOOL PTTState, struct TNCINFO * TNC); + +int WINAPI ext_PTT_info() +{ + return 0; +} + +int WINAPI ext_PTT_settings() +{ + return 0; +} + +int WINAPI ext_PTT_OFF(int Port) +{ + if (DLLRIG) + Rig_PTTEx(DLLRIG, 0, 0); + + return 0; +} + +int WINAPI ext_PTT_ON(int Port) +{ + if (DLLRIG) + Rig_PTTEx(DLLRIG, 1, 0); + + return 0; +} +int WINAPI ext_PTT_close() +{ + if (DLLRIG) + Rig_PTTEx(DLLRIG, 0, 0); + + return 0; +} + +DllExport INT WINAPI ext_PTT_open() +{ + return 1; +} + +char * stristr (char *ch1, char *ch2) +{ + char *chN1, *chN2; + char *chNdx; + char *chRet = NULL; + + chN1 = _strdup(ch1); + chN2 = _strdup(ch2); + + if (chN1 && chN2) + { + chNdx = chN1; + while (*chNdx) + { + *chNdx = (char) tolower(*chNdx); + chNdx ++; + } + chNdx = chN2; + + while (*chNdx) + { + *chNdx = (char) tolower(*chNdx); + chNdx ++; + } + + chNdx = strstr(chN1, chN2); + + if (chNdx) + chRet = ch1 + (chNdx - chN1); + } + + free (chN1); + free (chN2); + return chRet; +} + + + diff --git a/Bpq32.c b/Bpq32.c index 2fdcdd1..a322966 100644 --- a/Bpq32.c +++ b/Bpq32.c @@ -1151,6 +1151,7 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses // Fix using ; in UNPROTO Mode messages (52) // Use sha1 code from https://www.packetizer.com/security/sha1/ instead of openssl (53) // Fix TNC Emulator Monitoring (53) +// Fix attach and connect on Telnet port bug introduced in .55 (56) #define CKernel diff --git a/Cmd-HPLaptop-2.c b/Cmd-HPLaptop-2.c new file mode 100644 index 0000000..f6c3fc1 --- /dev/null +++ b/Cmd-HPLaptop-2.c @@ -0,0 +1,5599 @@ +/* +Copyright 2001-2022 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 +*/ + +// +// C replacement for cmd.asm +// +#define Kernel + +#define _CRT_SECURE_NO_DEPRECATE +#pragma data_seg("_BPQDATA") + +//#include "windows.h" +//#include "winerror.h" + + +#include "time.h" +#include "stdio.h" +#include +//#include "vmm.h" +//#include "SHELLAPI.H" + +#include "CHeaders.h" +#include "bpqaprs.h" + +#pragma pack() + +#include "tncinfo.h" +#include "telnetserver.h" + +//#include "GetVersion.h" + +//#define DllImport __declspec( dllimport ) +//#define DllExport __declspec( dllexport ) + +BOOL DecodeCallString(char * Calls, BOOL * Stay, BOOL * Spy, UCHAR *AXCalls); +VOID Send_AX_Datagram(PDIGIMESSAGE Block, DWORD Len, UCHAR Port); +int APIENTRY ClearNodes(); +VOID GetJSONValue(char * _REPLYBUFFER, char * Name, char * Value); +VOID SendHTTPRequest(SOCKET sock, char * Host, int Port, char * Request, char * Params, int Len, char * Return); +SOCKET OpenWL2KHTTPSock(); +VOID FormatTime3(char * Time, time_t cTime); +VOID Format_Addr(unsigned char * Addr, char * Output, BOOL IPV6); +VOID Tel_Format_Addr(struct ConnectionInfo * sockptr, char * dst); +VOID FindLostBuffers(); +BOOL CheckCMS(struct TNCINFO * TNC); +VOID L2SENDXID(struct _LINKTABLE * LINK); +int CountBits(unsigned long in); +VOID SaveMH(); +BOOL RestartTNC(struct TNCINFO * TNC); +void GetPortCTEXT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID WriteMiniDump(); + +char COMMANDBUFFER[81] = ""; // Command Hander input buffer +char OrigCmdBuffer[81] = ""; // Command Hander input buffer + +struct DATAMESSAGE * REPLYBUFFER = NULL; +UINT APPLMASK = 0; +UCHAR SAVEDAPPLFLAGS = 0; + +UCHAR ALIASINVOKED = 0; + +extern struct TNCINFO * TNCInfo[41]; + +VOID * CMDPTR = 0; + +short CMDPACLEN = 0; + +char OKMSG[] = "Ok\r"; + +char CMDERRMSG[] = "Invalid command - Enter ? for command list\r"; +#define CMDERRLEN sizeof(CMDERRMSG) - 1 + +char PASSWORDMSG[] = "Command requires SYSOP status - enter password\r"; +#define LPASSMSG sizeof(PASSWORDMSG) - 1 + +char CMDLIST[] = "CONNECT BYE INFO NODES PORTS ROUTES USERS MHEARD"; + +#define CMDLISTLEN sizeof(CMDLIST) - 1 + +char BADMSG[] = "Bad Parameter\r"; +char BADPORT[] = "Invalid Port Number\r"; +char NOTEXTPORT[] = "Only valid on EXT ports\r"; +char NOVALCALLS[] = "No Valid Calls defined on this port\r"; + +char BADVALUEMSG[] = "Invalid parameter\r"; + +char BADCONFIGMSG[] = "Configuration File check falled - will continue with old config\r"; +#ifdef LINBPQ +char REBOOTOK[] = "Rebooting\r"; +#else +char REBOOTOK[] = "Rebooting in 20 secs\r"; +#endif +char REBOOTFAILED[] = "Shutdown failed\r"; + +char RESTARTOK[] = "Restarting\r"; +char RESTARTFAILED[] = "Restart failed\r"; + +UCHAR ARDOP[7] = {'A'+'A','R'+'R','D'+'D','O'+'O','P'+'P',' '+' '}; // ARDOP IN AX25 +UCHAR VARA[7] = {'V'+'V','A'+'A','R'+'R','A'+'A',' '+' ',' '+' '}; // VARA IN AX25 + +int STATSTIME = 0; +int MAXBUFFS = 0; +int QCOUNT = 0; +int MINBUFFCOUNT = 65535; +int NOBUFFCOUNT = 0; +int BUFFERWAITS = 0; +int MAXDESTS = 0; +int NUMBEROFNODES = 0; +int L4CONNECTSOUT = 0; +int L4CONNECTSIN = 0; +int L4FRAMESTX = 0; +int L4FRAMESRX = 0; +int L4FRAMESRETRIED = 0; +int OLDFRAMES = 0; +int L3FRAMES = 0; + +VOID SENDSABM(); +VOID RESET2(); + +int APPL1 = 0; +int PASSCMD = 0; + +#pragma pack(1) + +struct _EXTPORTDATA DP; // Only way I can think of to get offets to port data into cmd table + +char CMDALIAS[ALIASLEN][NumberofAppls] = {0}; +char * ALIASPTR = &CMDALIAS[0][0]; + +extern int RigReconfigFlag; + +CMDX COMMANDS[]; + +int CMDXLEN = sizeof (CMDX); + +VOID SENDNODESMSG(); +VOID KISSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID STOPCMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID STARTCMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID STOPPORT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID STARTPORT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID FINDBUFFS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID WL2KSYSOP(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID AXRESOLVER(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID AXMHEARD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID SHOWTELNET(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID SHOWAGW(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID SHOWARP(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID SHOWNAT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID PING(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID SHOWIPROUTE(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID FLMSG(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD); +void ListExcludedCalls(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID APRSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID RECONFIGTELNET (TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID HELPCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); + + + +char * __cdecl Cmdprintf(TRANSPORTENTRY * Session, char * Bufferptr, const char * format, ...) +{ + // Send Command response checking PACLEN + + char Mess[4096]; + va_list(arglist); + int OldLen; + int MsgLen; + struct DATAMESSAGE * Buffer; + char * Messptr = Mess; + int Paclen = Session->SESSPACLEN; + + if (Paclen == 0) + Paclen = 255; + + va_start(arglist, format); + + MsgLen = vsprintf(Mess, format, arglist); + + OldLen = (int)(Bufferptr - (char *)REPLYBUFFER->L2DATA); + + while ((OldLen + MsgLen) > Paclen) + { + // Have to send Paclen then get a new buffer + + int ThisBit = Paclen - OldLen; // What we can send this time + + if (ThisBit < 0) + ThisBit = 0; // How can this happen?? + + memcpy(Bufferptr, Messptr, ThisBit); + Messptr += ThisBit; + MsgLen -= ThisBit; + + // QUEUE IT AND GET ANOTHER BUFFER + + Buffer = (struct DATAMESSAGE *)GetBuff(); + + if (Buffer == NULL) + + // No buffers, so just reuse the old one (better than crashing !!) + + Buffer = REPLYBUFFER; + else + SendCommandReply(Session, REPLYBUFFER, Paclen + (4 + sizeof(void *))); + + + REPLYBUFFER = Buffer; + Buffer->PID = 0xf0; + + Bufferptr = &Buffer->L2DATA[0]; + OldLen = 0; + } + + // Add last bit to buffer + + memcpy(Bufferptr, Messptr, MsgLen); + + return Bufferptr + MsgLen; +} + + +VOID SENDNODES(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + SENDNODESMSG(); + + strcpy(Bufferptr, OKMSG); + Bufferptr += (int)strlen(OKMSG); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID SAVEMHCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + SaveMH(); + + strcpy(Bufferptr, OKMSG); + Bufferptr += (int)strlen(OKMSG); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID SAVENODES(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + SaveNodes(); + + strcpy(Bufferptr, OKMSG); + Bufferptr += (int)strlen(OKMSG); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID DUMPCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + WriteMiniDump(); + + strcpy(Bufferptr, OKMSG); + Bufferptr += (int)strlen(OKMSG); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID RIGRECONFIG(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + if (!ProcessConfig()) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Configuration File check falled - will continue with old config"); + } + else + { + RigReconfigFlag = TRUE; + Bufferptr = Cmdprintf(Session, Bufferptr, "Rigcontrol Reconfig requested"); + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID REBOOT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + if (Reboot()) + { + strcpy(Bufferptr, REBOOTOK); + Bufferptr += (int)strlen(REBOOTOK); + } + else + { + strcpy(Bufferptr, REBOOTFAILED); + Bufferptr += (int)strlen(REBOOTFAILED); + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID RESTART(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + if (Restart()) + { + strcpy(Bufferptr, RESTARTOK); + Bufferptr += (int)strlen(RESTARTOK); + } + else + { + strcpy(Bufferptr, RESTARTFAILED); + Bufferptr += (int)strlen(RESTARTFAILED); + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID RESTARTTNC(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + char * ptr, *Context; + int portno; + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr) + { + portno = atoi (ptr); + + if (portno && portno < 33) + { + struct TNCINFO * TNC = TNCInfo[portno]; + + if (TNC == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); + } + else + { + if (TNC->ProgramPath) + { + if (RestartTNC(TNC)) + Bufferptr = Cmdprintf(Session, Bufferptr, "Restart %s Ok\r", TNC->ProgramPath); + else + Bufferptr = Cmdprintf(Session, Bufferptr, "Restart %s Failed\r", TNC->ProgramPath); + } + else + { + Bufferptr = Cmdprintf(Session, Bufferptr, "PATH not defined so can't restart TNC\r"); + } + } + } + else + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); + + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +UCHAR VALNODESFLAG = 0, EXTONLY = 0; + +VOID PORTVAL (TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); + +VOID VALNODES(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + VALNODESFLAG = 1; + PORTVAL(Session, Bufferptr, CmdTail, CMD); +} + +VOID EXTPORTVAL(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + EXTONLY = 1; + PORTVAL(Session, Bufferptr, CmdTail, CMD); +} +VOID PORTVAL(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // PROCESS PORT VALUE COMMANDS + + char * ptr, *Context, * ptr1; + int portno; + UCHAR oldvalue, newvalue; + struct PORTCONTROL * PORT = PORTTABLE; + int n = NUMBEROFPORTS; + UCHAR * valueptr; + + // Get port number + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr) + { + portno = atoi (ptr); + + if (portno) + { + while (n--) + { + if (PORT->PORTNUMBER == portno) + { + if (VALNODESFLAG) + { + char * VNPtr = PORT->PERMITTEDCALLS; + char Normcall[10]; + + VALNODESFLAG = 0; + + if (VNPtr) + { + while (VNPtr[0]) + { + Normcall[ConvFromAX25(VNPtr, Normcall)] = 0; + Bufferptr = Cmdprintf(Session, Bufferptr, "%s ", Normcall); + VNPtr += 7; + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + } + else + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%s", NOVALCALLS); + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + + return; + + } + + if (EXTONLY) + { + // Make sure an Extenal Port + + EXTONLY = 0; + + if (PORT->PORTTYPE != 0x10) + { + strcpy(Bufferptr, NOTEXTPORT); + Bufferptr += (int)strlen(NOTEXTPORT); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + } + + valueptr = (UCHAR *)PORT + CMD->CMDFLAG; + oldvalue = *valueptr; + + // Display Param Namee + + ptr1 = &CMD->String[0]; + n = 12; + + while (*(ptr1) != ' ' && n--) + *(Bufferptr++) = *(ptr1++); + + // See if another param - if not, just display current value + + ptr = strtok_s(NULL, " ", &Context); + + if (ptr && ptr[0]) + { + // Get new value + + newvalue = atoi(ptr); + *valueptr = newvalue; + + Bufferptr = Cmdprintf(Session, Bufferptr, " was %d now %d\r", oldvalue, newvalue); + } + + else + Bufferptr = Cmdprintf(Session, Bufferptr, " %d\r", oldvalue); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + + } + PORT = PORT->PORTPOINTER; + } + } + } + + // Bad port + + strcpy(Bufferptr, BADPORT); + Bufferptr += (int)strlen(BADPORT); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + +} + +VOID SWITCHVAL (TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // Update switch 8 bit value + + char * ptr, *Context, * ptr1; + UCHAR oldvalue, newvalue; + int n; + UCHAR * valueptr; + + valueptr = (UCHAR *)CMD->CMDFLAG; + + oldvalue = *valueptr; + + // Display Param Name + + ptr1 = &CMD->String[0]; + n = 12; + + while (*(ptr1) != ' ' && n--) + *(Bufferptr++) = *(ptr1++); + + // See if a param - if not, just display current value + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr && ptr[0]) + { + // Get new value + + newvalue = atoi(ptr); + *valueptr = newvalue; + + Bufferptr = Cmdprintf(Session, Bufferptr, " was %d now %d\r", oldvalue, newvalue); + + if (memcmp(CMD->String, "NODESINT ", 8) == 0) + L3TIMER = L3INTERVAL; + } + else + Bufferptr = Cmdprintf(Session, Bufferptr, " %d\r", oldvalue); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + +} + +VOID SWITCHVALW (TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // Update switch 16 bit value + + char * ptr, *Context, * ptr1; + USHORT oldvalue, newvalue; + int n; + USHORT * valueptr; + + valueptr = (USHORT *)CMD->CMDFLAG; + + oldvalue = (USHORT)*valueptr; + + // Display Param Name + + ptr1 = &CMD->String[0]; + n = 12; + + while (*(ptr1) != ' ' && n--) + *(Bufferptr++) = *(ptr1++); + + // See if a param - if not, just display current value + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr && ptr[0]) + { + // Get new value + + newvalue = atoi(ptr); + *valueptr = newvalue; + + Bufferptr = Cmdprintf(Session, Bufferptr, " was %d now %d\r", oldvalue, newvalue); + } + else + Bufferptr = Cmdprintf(Session, Bufferptr, " %d\r", oldvalue); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + +} + +TRANSPORTENTRY * SetupSessionFromSession(TRANSPORTENTRY * Session, PBPQVECSTRUC HOSTSESS, UINT APPLMASK) +{ + // Create a Transport (L4) session linked to an incoming Session + + TRANSPORTENTRY * NewSess = L4TABLE; + int Index = 0; + + while (Index < MAXCIRCUITS) + { + if (NewSess->L4USER[0] == 0) + { + // Got One + + UCHAR * ourcall = &MYCALL[0]; + + Session->L4CROSSLINK = NewSess; + NewSess->L4CROSSLINK = Session; + + if (APPLMASK) + { + // Circuit for APPL - look for an APPLCALL + + APPLCALLS * APPL = APPLCALLTABLE; + + while ((APPLMASK & 1) == 0) + { + APPLMASK >>= 1; + APPL++; + } + if (APPL->APPLCALL[0] > 0x40) // We have an applcall + ourcall = &APPL->APPLCALL[0]; + } + + memcpy(NewSess->L4USER, ourcall, 7); + memcpy(NewSess->L4MYCALL, Session->L4MYCALL, 7); + + NewSess->CIRCUITINDEX = Index; //OUR INDEX + NewSess->CIRCUITID = NEXTID; + + NEXTID++; + if (NEXTID == 0) + NEXTID++; // kEEP nON-ZERO + + NewSess->SESSIONT1 = Session->SESSIONT1; + NewSess->L4WINDOW = (UCHAR)L4DEFAULTWINDOW; + NewSess->SESSPACLEN = PACLEN; // Default; + + NewSess->L4TARGET.HOST = HOSTSESS; + NewSess->L4STATE = 5; + return NewSess; + } + Index++; + NewSess++; + } + return NULL; +} + +extern int GETCONNECTIONINFO(); + + +BOOL cATTACHTOBBS(TRANSPORTENTRY * Session, UINT Mask, int Paclen, int * AnySessions) +{ + PBPQVECSTRUC HOSTSESS = BPQHOSTVECTOR; + TRANSPORTENTRY * NewSess; + int ApplNum; + int n = BPQHOSTSTREAMS; + int ConfigedPorts = 0; + + // LOOK FOR A FREE HOST SESSION + + while (n--) + { + if (HOSTSESS->HOSTAPPLMASK & Mask) + { + // Right appl + + ConfigedPorts++; + + if (HOSTSESS->HOSTSESSION == NULL && (HOSTSESS->HOSTFLAGS & 3) == 0) // Not attached and no report outstanding + { + // WEVE GOT A FREE BPQ HOST PORT - USE IT + + NewSess = SetupSessionFromSession(Session, HOSTSESS, Mask); + + if (NewSess == NULL) + return FALSE; // Appl not available + + HOSTSESS->HOSTSESSION = NewSess; + + // Convert APPLMASK to APPLNUM + + ApplNum = 1; + + while (APPLMASK && (APPLMASK & 1) == 0) + { + ApplNum++; + APPLMASK >>= 1; + } + + HOSTSESS->HOSTAPPLNUM = ApplNum; + + HOSTSESS->HOSTFLAGS |= 2; // Indicate State Change + + NewSess->L4CIRCUITTYPE = BPQHOST | DOWNLINK; + + PostStateChange(NewSess); + + NewSess->SESS_APPLFLAGS = HOSTSESS->HOSTAPPLFLAGS; + + NewSess->SESSPACLEN = Paclen; + + return TRUE; + } + } + HOSTSESS++; + } + + *AnySessions = ConfigedPorts; // to distinguish between none and all in use + return FALSE; +} + +VOID APPLCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + BOOL CONFAILED = 0; + UINT CONERROR ; + char APPName[13]; + char * ptr1, *ptr2; + int n = 12; + BOOL Stay = FALSE; + + // Copy Appl and Null Terminate + + ptr1 = &CMD->String[0]; + ptr2 = APPName; + + while (*(ptr1) != ' ' && n--) + *(ptr2++) = *(ptr1++); + + *(ptr2) = 0; + + if (Session->LISTEN) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Can't use %s while listening\r", APPName); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + + if (CmdTail[0] == 'S') + Stay = TRUE; + + Session->STAYFLAG = Stay; + + memcpy(Session->APPL, CMD->String, 12); + + // SEE IF THERE IS AN ALIAS DEFINDED FOR THIS COMMAND + + if (ALIASPTR[0] > ' ') + { + // COPY ALIAS TO COMMAND BUFFER, THEN REENTER COMMAND HANDLER + + int SaveSecure = Session->Secure_Session; + + memcpy(COMMANDBUFFER, ALIASPTR, ALIASLEN); + _strupr(COMMANDBUFFER); + memcpy(OrigCmdBuffer, ALIASPTR, ALIASLEN); // In case original case version needed + + ALIASINVOKED = 1; // To prevent Alias Loops + + // Set secure session for application alias in case telnet outward connect + + Session->Secure_Session = 1; + DoTheCommand(Session); + Session->Secure_Session = SaveSecure; + + return; + } + + if (cATTACHTOBBS(Session, APPLMASK, CMDPACLEN, &CONERROR) == 0) + { + // No Streams + + if (CONERROR) + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, All %s Ports are in use - Please try later\r", APPName); + else + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, Application %s is not running - Please try later\r", APPName); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // IF CMD_TO_APPL SET IN APPLFLAGS, SEND INPUT MSG TO APPL + + if (Session->L4CROSSLINK->SESS_APPLFLAGS & CMD_TO_APPL) + { + struct DATAMESSAGE * Msg = (struct DATAMESSAGE *)GetBuff(); + TRANSPORTENTRY * XSession = Session->L4CROSSLINK; + + if (Msg) + { + COMMANDBUFFER[72] = 13; + memcpy(Msg->L2DATA, COMMANDBUFFER, 73); + Msg->LENGTH = 73 + 4 + sizeof(void *); + Msg->PID = 0xf0; + + C_Q_ADD(&XSession->L4TX_Q, (UINT *)Msg); + PostDataAvailable(XSession); + } + } + + if (Stay) + Session->L4CROSSLINK->L4TARGET.HOST->HOSTFLAGS |= 0x20; + + // IF MSG_TO_USER SET, SEND 'CONNECTED' MESSAGE TO USER + + Session->SESS_APPLFLAGS = Session->L4CROSSLINK->SESS_APPLFLAGS; + + if (Session->L4CROSSLINK->SESS_APPLFLAGS & MSG_TO_USER) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Connected to %s\r", APPName); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + + // DONT NEED BUFFER ANY MORE + + ReleaseBuffer((UINT *)REPLYBUFFER); + return; +} + + +VOID CMDI00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + Bufferptr = Cmdprintf(Session, Bufferptr, "%s", INFOMSG); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID CMDV00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + if (sizeof(void *) == 4) + Bufferptr = Cmdprintf(Session, Bufferptr, "Version %s\r", VersionString); + else + Bufferptr = Cmdprintf(Session, Bufferptr, "Version %s (64 bit)\r", VersionString); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID BYECMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + CLOSECURRENTSESSION(Session); // Kills any crosslink, plus local link + ReleaseBuffer((UINT *)REPLYBUFFER); + return; +} + +VOID CMDPAC(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // SET PACLEN FOR THIS SESSION + + char * ptr, *Context; + int newvalue; + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr && ptr[0]) + { + // Get new value + + newvalue = atoi(ptr); + if (newvalue > 29 && newvalue < 256) + Session->SESSPACLEN = newvalue & 0xff; + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "PACLEN - %d\r", Session->SESSPACLEN); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID CMDIDLE(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // SET IDLETIME FOR THIS SESSION + + char * ptr, *Context; + int newvalue; + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr && ptr[0]) + { + // Get new value + + newvalue = atoi(ptr); + if (newvalue > 59 && newvalue < 901) + Session->L4LIMIT = newvalue; + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "IDLETIME - %d\r", Session->L4LIMIT); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + +} +VOID CMDT00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // SET L4 TIMEOUT FOR CONNECTS ON THIS SESSION + + char * ptr, *Context; + int newvalue; + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr && ptr[0]) + { + // Get new value + + newvalue = atoi(ptr); + if (newvalue > 20) + Session->SESSIONT1 = newvalue; + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "L4TIMEOUT - %d\r", Session->SESSIONT1); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +UCHAR PWLen; +char PWTEXT[80]; + +VOID PWDCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + char * ptr, *Context; + USHORT pwsum = 0; + int n = 5, p1, p2, p3, p4, p5; + + if (Session->Secure_Session) // HOST - SET AUTHORISED REGARDLESS + { + Session->PASSWORD = 0xFFFF; // SET AUTHORISED + Session->Secure_Session = 1; + strcpy(Bufferptr, OKMSG); + Bufferptr += (int)strlen(OKMSG); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr && ptr[0]) + { + // Check Password + + n = 5; + + while (n--) + pwsum += *(ptr++); + + if (Session->PASSWORD == pwsum) + { + Session->PASSWORD = 0xFFFF; // SET AUTHORISED + Session->Secure_Session = 1; + strcpy(Bufferptr, OKMSG); + Bufferptr += (int)strlen(OKMSG); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + ReleaseBuffer((UINT *)REPLYBUFFER); + return; + } + + // SEND PASSWORD PROMPT + + if (PWLen == 0) + PWLen = 1; + + p1 = rand() % PWLen; + pwsum += PWTEXT[p1++]; + + p2 = rand() % PWLen; + pwsum += PWTEXT[p2++]; + + p3 = rand() % PWLen; + pwsum += PWTEXT[p3++]; + + p4 = rand() % PWLen; + pwsum += PWTEXT[p4++]; + + p5 = rand() % PWLen; + pwsum += PWTEXT[p5++]; + + Session->PASSWORD = pwsum; + + Bufferptr = Cmdprintf(Session, Bufferptr, "%d %d %d %d %d\r", p1, p2, p3, p4, p5); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; +} + +VOID CMDSTATS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + char * ptr, *Context; + int Port = 0, cols = NUMBEROFPORTS, i; + char * uptime; + struct PORTCONTROL * PORT = PORTTABLE; + struct PORTCONTROL * STARTPORT; + + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + // SEE IF ANY PARAM + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr && ptr[0]) + Port = atoi(ptr); + + // IF ASKING FOR PORT STATS, DONT DO SYSTEM ONES + + if (Port == 0) + { + uptime = FormatUptime(STATSTIME); + Bufferptr = Cmdprintf(Session, Bufferptr, "%s", uptime); + + Bufferptr = Cmdprintf(Session, Bufferptr, "Semaphore Get-Rel/Clashes %9d%9d\r", + Semaphore.Gets - Semaphore.Rels, Semaphore.Clashes); + + Bufferptr = Cmdprintf(Session, Bufferptr, "Buffers:Max/Cur/Min/Out/Wait%9d%9d%9d%9d%9d\r", + MAXBUFFS, QCOUNT, MINBUFFCOUNT, NOBUFFCOUNT, BUFFERWAITS); + + Bufferptr = Cmdprintf(Session, Bufferptr, "Known Nodes/Max Nodes %9d%9d\r", + NUMBEROFNODES, MAXDESTS); + + Bufferptr = Cmdprintf(Session, Bufferptr, "L4 Connects Sent/Rxed %9d%9d\r", + L4CONNECTSOUT, L4CONNECTSIN); + + Bufferptr = Cmdprintf(Session, Bufferptr, "L4 Frames TX/RX/Resent/Reseq%9d%9d%9d%9d\r", + L4FRAMESTX, L4FRAMESRX, L4FRAMESRETRIED, OLDFRAMES); + + Bufferptr = Cmdprintf(Session, Bufferptr, "L3 Frames Relayed %9d\r", L3FRAMES); + + if (ptr && ptr[0] == 'S') + { + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + } + + // POSITION TO REQUESTED PORT + + if (Port) + { + while (PORT && PORT->PORTNUMBER != Port) + { + PORT = PORT->PORTPOINTER; + cols--; + } + } + + if (PORT == NULL) // REQUESTED PORT NOT FOUND + { + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + STARTPORT = PORT; + + if (cols > 7) + cols = 7; + + Bufferptr = Cmdprintf(Session, Bufferptr, " "); + + for (i = 0; i < cols; i++) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Port %02d ", PORT->PORTNUMBER); + PORT = PORT->PORTPOINTER; + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + PORT = STARTPORT; + Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Frames Digied"); + + for (i = 0; i < cols; i++) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2DIGIED); + PORT = PORT->PORTPOINTER; + } + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + PORT = STARTPORT; + Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Frames Heard "); + + for (i = 0; i < cols; i++) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2FRAMES); + PORT = PORT->PORTPOINTER; + } + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + PORT = STARTPORT; + Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Frames Rxed "); + + for (i = 0; i < cols; i++) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2FRAMESFORUS); + PORT = PORT->PORTPOINTER; + } + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + PORT = STARTPORT; + Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Frames Sent "); + + for (i = 0; i < cols; i++) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2FRAMESSENT); + PORT = PORT->PORTPOINTER; + } + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + PORT = STARTPORT; + Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Timeouts "); + + for (i = 0; i < cols; i++) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2TIMEOUTS); + PORT = PORT->PORTPOINTER; + } + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + PORT = STARTPORT; + Bufferptr = Cmdprintf(Session, Bufferptr, "REJ Frames Rxed "); + + for (i = 0; i < cols; i++) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2REJCOUNT); + PORT = PORT->PORTPOINTER; + } + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + PORT = STARTPORT; + Bufferptr = Cmdprintf(Session, Bufferptr, "RX out of Seq "); + + for (i = 0; i < cols; i++) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2OUTOFSEQ); + PORT = PORT->PORTPOINTER; + } + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + PORT = STARTPORT; + Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Resequenced "); + + for (i = 0; i < cols; i++) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2RESEQ); + PORT = PORT->PORTPOINTER; + } + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + PORT = STARTPORT; + Bufferptr = Cmdprintf(Session, Bufferptr, "Undrun/Poll T/o "); + + for (i = 0; i < cols; i++) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2URUNC); + PORT = PORT->PORTPOINTER; + } + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + PORT = STARTPORT; + Bufferptr = Cmdprintf(Session, Bufferptr, "RX Overruns "); + + for (i = 0; i < cols; i++) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2ORUNC); + PORT = PORT->PORTPOINTER; + } + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + PORT = STARTPORT; + Bufferptr = Cmdprintf(Session, Bufferptr, "RX CRC Errors "); + + for (i = 0; i < cols; i++) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->RXERRORS); + PORT = PORT->PORTPOINTER; + } + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + PORT = STARTPORT; + Bufferptr = Cmdprintf(Session, Bufferptr, "FRMRs Sent "); + + for (i = 0; i < cols; i++) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2FRMRTX); + PORT = PORT->PORTPOINTER; + } + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + PORT = STARTPORT; + Bufferptr = Cmdprintf(Session, Bufferptr, "FRMRs Received "); + + for (i = 0; i < cols; i++) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2FRMRRX); + PORT = PORT->PORTPOINTER; + } + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + PORT = STARTPORT; + Bufferptr = Cmdprintf(Session, Bufferptr, "Frames abandoned"); + + for (i = 0; i < cols; i++) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L1DISCARD); + PORT = PORT->PORTPOINTER; + } + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + PORT = STARTPORT; + Bufferptr = Cmdprintf(Session, Bufferptr, "Link Active %% "); + + for (i = 0; i < cols; i++) + { + Bufferptr = Cmdprintf(Session, Bufferptr, " %2d %3d", PORT->AVSENDING, PORT->AVACTIVE); + PORT = PORT->PORTPOINTER; + } + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID CMDL00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // PROCESS 'LINKS' MESSAGE + + struct _LINKTABLE * LINK = LINKS; + int n = MAXLINKS; + int len; + char Normcall[11] = ""; + + Bufferptr = Cmdprintf(Session, Bufferptr, "Links\r"); + + while (n--) + { + if (LINK->LINKCALL[0]) + { + len = ConvFromAX25(LINK->LINKCALL, Normcall); + + Bufferptr = Cmdprintf(Session, Bufferptr, "%s", Normcall); + + len = ConvFromAX25(LINK->OURCALL, Normcall); + Bufferptr = Cmdprintf(Session, Bufferptr, "%s", Normcall); + + if (LINK->Ver2point2) + Bufferptr = Cmdprintf(Session, Bufferptr, " S=%d P=%d T=%d V=2.2\r", + LINK->L2STATE, LINK->LINKPORT->PORTNUMBER, LINK->LINKTYPE); + else + Bufferptr = Cmdprintf(Session, Bufferptr, " S=%d P=%d T=%d V=%d\r", + LINK->L2STATE, LINK->LINKPORT->PORTNUMBER, LINK->LINKTYPE, 2 - LINK->VER1FLAG); + } + LINK++; + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + + +VOID CMDS00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // PROCESS 'USERS' + + int n = MAXCIRCUITS; + TRANSPORTENTRY * L4 = L4TABLE; + TRANSPORTENTRY * Partner; + int MaxLinks = MAXLINKS; + char State[12] = "", Type[12] = "Uplink"; + char LHS[50] = "", MID[10] = "", RHS[50] = ""; + char Line[100]; + + Bufferptr = Cmdprintf(Session, Bufferptr, "%s%d)\r", SESSIONHDDR, QCOUNT); + + while (n--) + { + if (L4->L4USER[0]) + { + RHS[0] = MID[0] = 0; + + if ((L4->L4CIRCUITTYPE & UPLINK) == 0) //SHORT CMDS10A ; YES + { + // IF DOWNLINK, ONLY DISPLAY IF NO CROSSLINK + + if (L4->L4CROSSLINK == 0) //jne CMDS60 ; WILL PROCESS FROM OTHER END + { + // ITS A DOWNLINK WITH NO PARTNER - MUST BE A CLOSING SESSION + // DISPLAY TO THE RIGHT FOR NOW + + strcpy(LHS, "(Closing) "); + DISPLAYCIRCUIT(L4, RHS); + goto CMDS50; + } + else + goto CMDS60; // WILL PROCESS FROM OTHER END + } + + if (L4->L4CROSSLINK == 0) + { + // Single Entry + + DISPLAYCIRCUIT(L4, LHS); + } + else + { + DISPLAYCIRCUIT(L4, LHS); + + Partner = L4->L4CROSSLINK; + + if (Partner->L4STATE == 5) + strcpy(MID, "<-->"); + else + strcpy(MID, "<~~>"); + + DISPLAYCIRCUIT(Partner, RHS); + } +CMDS50: + memset(Line, 32, 100); + memcpy(Line, LHS, (int)strlen(LHS)); + memcpy(&Line[35], MID, (int)strlen(MID)); + strcpy(&Line[40], RHS); + strcat(&Line[40], "\r"); + Bufferptr = Cmdprintf(Session, Bufferptr, "%s", Line); + } +CMDS60: + L4++; + } + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID CMDP00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // Process PORTS Message + + struct PORTCONTROL * PORT = PORTTABLE; + + Bufferptr = Cmdprintf(Session, Bufferptr, "Ports\r"); + + while (PORT) + { + if (PORT->Hide == 0) + Bufferptr = Cmdprintf(Session, Bufferptr, " %2d %s\r", PORT->PORTNUMBER, PORT->PORTDESCRIPTION); + + PORT = PORT->PORTPOINTER; + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +char * DisplayRoute(TRANSPORTENTRY * Session, char * Bufferptr, struct ROUTE * Routes, char Verbose) +{ + char Normcall[10]; + char locked[] = " ! "; + int NodeCount; + int Percent = 0; + char PercentString[20]; + int Iframes, Retries; + char Active[10]; + int Queued; + + int Port = 0; + + int len = ConvFromAX25(Routes->NEIGHBOUR_CALL, Normcall); + + Normcall[9]=0; + + if ((Routes->NEIGHBOUR_FLAG & 1) == 1) + strcpy(locked, "!"); + else + strcpy(locked, " "); + + NodeCount = COUNTNODES(Routes); + + if (Routes->NEIGHBOUR_LINK && Routes->NEIGHBOUR_LINK->L2STATE >= 5) + strcpy(Active, ">"); + else + strcpy(Active, " "); + + if (Verbose) + { + if (Routes->NEIGHBOUR_LINK) + Queued = COUNT_AT_L2(Routes->NEIGHBOUR_LINK); // SEE HOW MANY QUEUED + else + Queued = 0; + + Iframes = Routes->NBOUR_IFRAMES; + Retries = Routes->NBOUR_RETRIES; + + if (Iframes) + { + Percent = (Retries * 100) / Iframes; + sprintf(PercentString, "%3d%%", Percent); + } + else + strcpy(PercentString, " "); + + + Bufferptr = Cmdprintf(Session, Bufferptr, "%s%2d %s %3d %3d%s%4d %4d %s %d %d %02d:%02d %d %d", + Active, Routes->NEIGHBOUR_PORT, Normcall, + Routes->NEIGHBOUR_QUAL, NodeCount, locked, Iframes, Retries, PercentString, Routes->NBOUR_MAXFRAME, Routes->NBOUR_FRACK, + Routes->NEIGHBOUR_TIME >> 8, (Routes->NEIGHBOUR_TIME) & 0xff, Queued, Routes->OtherendsRouteQual); + + // IF INP3 DISPLAY SRTT + + if (Routes->INP3Node) // INP3 Enabled? + { + double srtt = Routes->SRTT/1000.0; + double nsrtt = Routes->NeighbourSRTT/1000.0; + + Bufferptr = Cmdprintf(Session, Bufferptr, " %4.2fs %4.2fs", srtt, nsrtt); + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + } + else + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%s %d %s %d %d%s\r", + Active, Routes->NEIGHBOUR_PORT, Normcall, Routes->NEIGHBOUR_QUAL, NodeCount, locked); + } + + return Bufferptr; +} + + +VOID CMDR00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + struct ROUTE * Routes = NEIGHBOURS; + int MaxRoutes = MAXNEIGHBOURS; + char locked[] = " ! "; + int Percent = 0; + char * ptr, * Context; + char Verbose = 0; + int Port = 0; + char AXCALL[7]; + BOOL Found; + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr && (int)strlen(ptr) > 1) + { + // Route Update + + goto ROUTEUPDATE; + } + + if (ptr) + { + Verbose = ptr[0]; + ptr = strtok_s(NULL, " ", &Context); + if (ptr) + Port = atoi(ptr); + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "Routes\r"); + + while (MaxRoutes--) + { + if (Routes->NEIGHBOUR_CALL[0] != 0) + if (Port == 0 || Port == Routes->NEIGHBOUR_PORT) + Bufferptr = DisplayRoute(Session, Bufferptr, Routes, Verbose); + + Routes++; + } + goto SendReply; + +ROUTEUPDATE: + + if (Session->PASSWORD != 0xFFFF) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%s", PASSWORDMSG); + goto SendReply; + } + + // Line is + + // ROUTES G8BPQ-2 2 100 - Set quality to 100 + // ROUTES G8BPQ-2 2 ! - Toggle 'Locked Route' flag + // ROUTES G8BPQ-2 2 100 ! - Set quality and toggle 'locked' flag + + + ConvToAX25(ptr, AXCALL); + + ptr = strtok_s(NULL, " ", &Context); + + if (ptr) + Port = atoi(ptr); + + if (Port == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Port Number Missing \r"); + goto SendReply; + } + + Found = FindNeighbour(AXCALL, Port, &Routes); + + if (Context && Context[0] > 32) + { + // More Params + + ptr = strtok_s(NULL, " ", &Context); + + if (ptr) + { + // Adding + + memcpy(Routes->NEIGHBOUR_CALL, AXCALL, 7); // In case Add + Routes->NEIGHBOUR_PORT = Port; + Found = TRUE; + } + + if (strcmp(ptr, "!") == 0) + { + // Toggle Lock + + Routes->NEIGHBOUR_FLAG ^= 1; // FLIP LOCKED BIT + goto Displayit; + } + + if (strcmp(ptr, "Z") == 0) + { + // Clear Counts + + Routes->NBOUR_IFRAMES = 0; + Routes->NBOUR_RETRIES = 0; + goto Displayit; + } + + Routes->NEIGHBOUR_QUAL = atoi(ptr); + + if (Context && Context[0] == '!') + { + // Toggle Lock + + Routes->NEIGHBOUR_FLAG ^= 1; // FLIP LOCKED BIT + goto Displayit; + } + } + +Displayit: + + // Just display + + if (Found) + Bufferptr = DisplayRoute(Session, Bufferptr, Routes, 1); + else + Bufferptr = Cmdprintf(Session, Bufferptr, "Not Found\r"); + + + +/* MOV ROUTEDISP,1 + + CMP BYTE PTR [ESI],20H + JE SHORT JUSTDISPLAY + + MOV ZAPFLAG,0 + + CMP BYTE PTR [ESI],'Z' + JNE SHORT NOTZAP + + MOV ZAPFLAG,1 + JMP SHORT JUSTDISPLAY + + PUBLIC NOTZAP +NOTZAP: + + MOV ROUTEDISP,2 ; LOCK UPDATE + + CMP BYTE PTR [ESI],'!' + JE SHORT JUSTDISPLAY +; +; LOOK FOR V FOR ADDING A DIGI +; + CMP WORD PTR [ESI],' V' ; V [SPACE] + JE ADDDIGI + + CALL GETVALUE ; GET NUMBER, UP TO SPACE , CR OR OFFH + JC SHORT BADROUTECMD ; INVALID DIGITS + + MOV NEWROUTEVAL,AL + + MOV ROUTEDISP,0 + + CALL SCAN ; SEE IF ! + MOV AH,[ESI] + + + PUBLIC JUSTDISPLAY +JUSTDISPLAY: + + + MOV ESI,OFFSET32 AX25CALL + CALL _FINDNEIGHBOUR + JZ SHORT FOUNDROUTE ; IN LIST - OK + + CMP EBX,0 + JE SHORT BADROUTECMD ; TABLE FULL?? + + MOV ECX,7 + MOV EDI,EBX + REP MOVSB ; PUT IN CALL + + MOV AL,SAVEPORT + MOV NEIGHBOUR_PORT[EBX],AL + + JMP SHORT FOUNDROUTE + + + PUBLIC BADROUTECMD +BADROUTECMD: + + POP EDI + + JMP PBADVALUE + + PUBLIC FOUNDROUTE +FOUNDROUTE: + + CMP ZAPFLAG,1 + JNE SHORT NOTCLEARCOUNTS + + XOR AX,AX + MOV ES:WORD PTR NBOUR_IFRAMES[EDI],AX + MOV ES:WORD PTR NBOUR_IFRAMES+2[EDI],AX + MOV ES:WORD PTR NBOUR_RETRIES[EDI],AX + MOV ES:WORD PTR NBOUR_RETRIES+2[EDI],AX + + JMP SHORT NOUPDATE + + PUBLIC NOTCLEARCOUNTS +NOTCLEARCOUNTS: + + CMP ROUTEDISP,1 + JE SHORT NOUPDATE + + CMP ROUTEDISP,2 + JE SHORT LOCKUPDATE + + MOV AL,NEWROUTEVAL + MOV NEIGHBOUR_QUAL[EBX],AL + + CMP AH,'!' + JNE SHORT NOUPDATE + + PUBLIC LOCKUPDATE +LOCKUPDATE: + + XOR NEIGHBOUR_FLAG[EBX],1 ; FLIP LOCKED BIT + + PUBLIC NOUPDATE +NOUPDATE: + + MOV ESI,EBX + POP EDI + + POP EBX + CALL DISPLAYROUTE + + JMP SENDCOMMANDREPLY + + PUBLIC ADDDIGI +ADDDIGI: + + ADD ESI,2 + PUSH ESI ; SAVE INPUT BUFFER + + MOV ESI,OFFSET32 AX25CALL + CALL _FINDNEIGHBOUR + + POP ESI + + JZ SHORT ADD_FOUND ; IN LIST - OK + + JMP BADROUTECMD + + PUBLIC ADD_FOUND +ADD_FOUND: + + CALL CONVTOAX25 ; GET DIGI CALLSIGN + + PUSH ESI + + MOV ESI,OFFSET32 AX25CALL + LEA EDI,NEIGHBOUR_DIGI[EBX] + MOV ECX,7 + REP MOVSB + + POP ESI ; MSG BUFFER +; +; SEE IF ANOTHER DIGI +; + CMP BYTE PTR [ESI],20H + JE SHORT NOMORE + + CALL CONVTOAX25 ; GET DIGI CALLSIGN + MOV ESI,OFFSET32 AX25CALL + LEA EDI,NEIGHBOUR_DIGI+7[EBX] + MOV ECX,7 + REP MOVSB + + PUBLIC NOMORE +NOMORE: + + JMP NOUPDATE + + + +*/ + +SendReply: + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + + +VOID LISTENCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // PROCESS LISTEN COMMAND + + // for monitoring a remote ax.25 port + + int Port = 0, index =0; + unsigned int ListenMask = 0; + char * ptr, *Context; + struct PORTCONTROL * PORT = NULL; + char ListenPortList[128] = ""; + + ptr = strtok_s(CmdTail, " ,", &Context); + + // Now accepts a list of ports + + if (ptr == 0 || memcmp(ptr, "OFF", 3) == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Listening disabled\r"); + Session->LISTEN = 0; + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + while (ptr) + { + Port = atoi(ptr); + + if (Port == 0 && NUMBEROFPORTS == 1) + Port = 1; + + ptr = strtok_s(NULL, ", ", &Context); // Get Unproto String + + if (Port) + PORT = GetPortTableEntryFromPortNum(Port); + + if (PORT == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port %d\r", Port); + continue; + } + + if (PORT->PROTOCOL == 10 && PORT->UICAPABLE == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port %d is not an ax.25 port\r", Port); + continue; + } + + if (PORT->PORTL3FLAG) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port %d is for internode traffic only\r", Port); + continue; + } + + if (Session->L4CIRCUITTYPE == L2LINK + UPLINK) + { + if (Session->L4TARGET.LINK->LINKPORT->PORTNUMBER == Port) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "You can't Listen to the port you are connected on\r"); + continue; + } + } + + sprintf(ListenPortList, "%s %d", ListenPortList, Port); + + + ListenMask |= (1 << (Port - 1)); + } + + Session->LISTEN = ListenMask; + + if (ListenMask) + { + if (CountBits(ListenMask) == 1) + Bufferptr = Cmdprintf(Session, Bufferptr, "Listening on port%s. Use CQ to send a beacon, LIS to disable\r", ListenPortList); + else + Bufferptr = Cmdprintf(Session, Bufferptr, "Listening on ports%s. Use LIS to disable\r", ListenPortList); + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; +} + + +VOID UNPROTOCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // PROCESS UNPROTO COMMAND + + int Port = 0, index =0; + char * ptr, *Context; + struct PORTCONTROL * PORT = NULL; + UCHAR axcalls[64]; + BOOL Stay, Spy; + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr) + Port = atoi(ptr); + + if (Port == 0 && NUMBEROFPORTS == 1) + Port = 1; + else + ptr = strtok_s(NULL, " ", &Context); // Get Unproto String + + if (Port) + PORT = GetPortTableEntryFromPortNum(Port); + + if (PORT == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (ptr == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Destination missing\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + ptr[strlen(ptr)] = ' '; // Put param back together + + if (DecodeCallString(ptr, &Stay, &Spy, &axcalls[0]) == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Call\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (PORT->PROTOCOL == 10 && PORT->UICAPABLE == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is not an ax.25 port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (PORT->PORTL3FLAG) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is for internode traffic only\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // Copy Address Info to Session Record + + Session->UNPROTO = Port; + Session->UAddrLen = (int)strlen(axcalls); + memcpy(Session->UADDRESS, axcalls, 63); + + Bufferptr = Cmdprintf(Session, Bufferptr, "Unproto Mode - enter ctrl/z or /ex to exit\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; +} + +VOID CALCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // PROCESS CAL COMMAND + + int Port = 0, index = 0, Count = 0; + char * ptr, *Context; + struct PORTCONTROL * PORT = NULL; + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr) + Port = atoi(ptr); + + if (Port == 0 && NUMBEROFPORTS == 1) + Port = 1; + else + ptr = strtok_s(NULL, " ", &Context); // Get Unproto String + + if (Port) + PORT = GetPortTableEntryFromPortNum(Port); + + if (PORT == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (PORT->PROTOCOL == 10 && PORT->UICAPABLE == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is not an ax.25 port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (ptr == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Count Missing\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + Count = atoi(ptr); + + ptr = strtok_s(NULL, " ", &Context); // Get Unproto String + + Bufferptr = Cmdprintf(Session, Bufferptr, "Ok\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; +} + + + +VOID CQCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // Send a CQ Beacon on a radio port. Must be in LISTEN state + + DIGIMESSAGE Msg; + int Port = 0; + int OneBits = 0; + unsigned int MaskCopy = Session->LISTEN; + int Len; + UCHAR CQCALL[7]; + char Empty[] = ""; + char * ptr1 = &OrigCmdBuffer[3]; + UCHAR * axptr = &Msg.DIGIS[0][0]; + char * ptr2, *Context; + + while (MaskCopy) + { + if (MaskCopy & 1) + OneBits++; + + Port++; + MaskCopy = MaskCopy >> 1; + } + + if (OneBits == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "You must enter LISTEN before calling CQ\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (OneBits > 1) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "You can't call CQ if LISTENing on more than one port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + + Len = (int)strlen(OrigCmdBuffer) - 3; + + if (Len < 0) + Len = 0; + + memset(&Msg, 0, sizeof(Msg)); + + Msg.PORT = Port; + Msg.CTL = 3; // UI + + // see if a Via specified + + if (_memicmp(ptr1, "via ", 4) == 0) + { + ptr2 = strtok_s(ptr1 + 4, ",", &Context); + + while (ptr2) + { + if (ConvToAX25(ptr2, axptr) == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid via string\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + axptr += 7; + + if (axptr == &Msg.DIGIS[7][0]) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Too many digis\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + ptr1 = ptr2; + ptr2 = strtok_s(NULL, ",", &Context); + } + + // ptr1 is start of last digi call. We need to position to data + + ptr1 = strchr(ptr1, ' '); + + if (ptr1 == NULL) + ptr1 = Empty; + else + ptr1++ ; // to message + + Len = (int)strlen(ptr1); + + } + + ConvToAX25("CQ", CQCALL); + memcpy(Msg.DEST, CQCALL, 7); + memcpy(Msg.ORIGIN, Session->L4USER, 7); + Msg.ORIGIN[6] ^= 0x1e; // Flip SSID + Msg.PID = 0xf0; // Data PID + memcpy(&Msg.L2DATA, ptr1, Len); + + Send_AX_Datagram(&Msg, Len + 2, Port); // Len is Payload ie CTL, PID and Data + + Bufferptr = Cmdprintf(Session, Bufferptr, "CQ sent\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + +} + + +TRANSPORTENTRY * SetupNewSession(TRANSPORTENTRY * Session, char * Bufferptr) +{ + TRANSPORTENTRY * NewSess = L4TABLE; + int Index = 0; + + while (Index < MAXCIRCUITS) + { + if (NewSess->L4USER[0] == 0) + { + // Got One + + Session->L4CROSSLINK = NewSess; + NewSess->L4CROSSLINK = Session; + + memcpy(NewSess->L4USER, Session->L4USER, 7); + memcpy(NewSess->L4MYCALL, Session->L4MYCALL, 7); + + + NewSess->CIRCUITINDEX = Index; //OUR INDEX + NewSess->CIRCUITID = NEXTID; + + NEXTID++; + if (NEXTID == 0) + NEXTID++; // kEEP nON-ZERO + + NewSess->SESSIONT1 = Session->SESSIONT1; + NewSess->L4WINDOW = (UCHAR)L4DEFAULTWINDOW; + + return NewSess; + } + Index++; + NewSess++; + } + + if (Bufferptr) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry - System Tables Full\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + } + + return NULL; +} + + +VOID DoNetromConnect(TRANSPORTENTRY * Session, char * Bufferptr, struct DEST_LIST * Dest, BOOL Spy) +{ + TRANSPORTENTRY * NewSess; + + NewSess = SetupNewSession(Session, Bufferptr); + + if (NewSess == NULL) + return; // Tables Full + + NewSess->L4CIRCUITTYPE = SESSION + DOWNLINK; + + NewSess->L4TARGET.DEST = Dest; + NewSess->L4STATE = 2; // CONNECTING + + NewSess->SPYFLAG = Spy; + + ReleaseBuffer((UINT *)REPLYBUFFER); + + SENDL4CONNECT(NewSess); + + L4CONNECTSOUT++; + + return; +} + +BOOL FindLink(UCHAR * LinkCall, UCHAR * OurCall, int Port, struct _LINKTABLE ** REQLINK) +{ + struct _LINKTABLE * LINK = LINKS; + struct _LINKTABLE * FIRSTSPARE = NULL; + int n = MAXLINKS; + + while (n--) + { + if (LINK->LINKCALL[0] == 0) // Spare + { + if (FIRSTSPARE == NULL) + FIRSTSPARE = LINK; + + LINK++; + continue; + } + + if ((LINK->LINKPORT->PORTNUMBER == Port) && CompareCalls(LINK->LINKCALL, LinkCall) && CompareCalls(LINK->OURCALL, OurCall)) + { + *REQLINK = LINK; + return TRUE; + } + + LINK++; + } + // ENTRY NOT FOUND - FIRSTSPARE HAS FIRST FREE ENTRY, OR ZERO IF TABLE FULL + + *REQLINK = FIRSTSPARE; + return FALSE; +} + +VOID ATTACHCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD); + +VOID CMDC00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // PROCESS CONNECT COMMAND + + TRANSPORTENTRY * NewSess; + + int CONNECTPORT, Port; + BOOL CallEvenIfInNodes = FALSE; + char * ptr, *Context; + UCHAR axcalls[64]; + UCHAR ourcall[7]; // Call we are using (may have SSID bits inverted + int ret; + struct PORTCONTROL * PORT = PORTTABLE; + struct _LINKTABLE * LINK; + int CQFLAG = 0; // NOT CQ CALL + BOOL Stay, Spy; + int n; + char TextCall[10]; + int TextCallLen; + char PortString[10]; + char cmdCopy[256]; + struct _EXTPORTDATA * EXTPORT = (struct _EXTPORTDATA *)PORT;; + + +#ifdef EXCLUDEBITS + + if (CheckExcludeList(Session->L4USER) == FALSE) + { + // CONNECTS FROM THIS STATION ARE NOT ALLOWED + + ReleaseBuffer((UINT *)REPLYBUFFER); + return; + } + +#endif + + if (Session->LISTEN) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Can't connect while listening\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + CONNECTPORT = 0; // NO PORT SPECIFIED + + ptr = strtok_s(CmdTail, " ", &Context); + + strcpy(cmdCopy, Context); // Save in case Telnet Connect + + if (ptr == 0) + { + // No param + + if (CFLAG) // C Command Disabled ? + { + // Convert to HOST (appl 32) command + + //MOV _CMDPTR,OFFSET32 _HOSTCMD + //MOV _ALIASPTR,OFFSET32 _HOSTCMD + 32 * 31 + + //MOV _APPLMASK, 80000000H ; Internal Term + + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Call\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + Port = atoi(ptr); + + if (Port) + { + // IF THERE IS NOTHING FOLLOWING THE NUMBER, ASSUME IT IS A + // NUMERIC ALIAS INSTEAD OF A PORT + + sprintf(PortString, "%d", Port); + + if (strlen(PortString) < (int)strlen(ptr)) + goto NoPort; + + PORT = GetPortTableEntryFromPortNum(Port); + + if (PORT == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + EXTPORT = (struct _EXTPORTDATA *)PORT; + + ptr = strtok_s(NULL, " ", &Context); + + if (ptr == 0) + { + // No param + + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Call\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + CONNECTPORT = Port; + + if (strcmp(ptr, "CMS") == 0 || strcmp(ptr, "HOST") == 0) // In case someeone has CMS or HOST as an alias + goto Downlink; + + } + +NoPort: + + ptr[strlen(ptr)] = ' '; // Put param back together + + if (ptr[0] == '!') + { + CallEvenIfInNodes = TRUE; + ptr++; + } + + if (memcmp(ptr, "RELAY ", 5) == 0 || memcmp(ptr, "SYNC ", 5) == 0) + { + // c p relay with extra parms + + goto Downlink; + } + + // Skip call validation if using a ptc to allow 1:call, 2:call format + + if (Port && PORT->PROTOCOL == 10 && memcmp(EXTPORT->PORT_DLL_NAME, "SCSPACTOR", 9) == 0) + { + char * p; + + if (p = strstr(cmdCopy, " S ")) + { + Stay = TRUE; + p++; + *p = ' '; + } + + if (p = strstr(cmdCopy, " Z ")) + { + Spy = TRUE; + p++; + *p = ' '; + } + + goto Downlink; + } + else + { + if (DecodeCallString(ptr, &Stay, &Spy, &axcalls[0]) == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Call\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + } + + Session->STAYFLAG = Stay; + + TextCallLen = ConvFromAX25(axcalls, TextCall); + + if (CallEvenIfInNodes) + goto Downlink; + + // SEE IF CALL TO ANY OF OUR HOST SESSIONS - UNLESS DIGIS SPECIFIED + + if (axcalls[7] == 0) + { + // If this connect is as a result of a command alias, don't check appls or we will loop + + if (ALIASINVOKED == 0) + { + APPLCALLS * APPL = APPLCALLTABLE; + int n = NumberofAppls; + APPLMASK = 1; + + while (n--) + { + if (memcmp(axcalls, APPL->APPLALIAS, 6) == 0 || CompareCalls(axcalls, APPL->APPLCALL)) + { + // Call to an appl + + // Convert to an APPL command, so any alias is actioned + + // SEE IF THERE IS AN ALIAS DEFINDED FOR THIS COMMAND + + if (APPL->APPLHASALIAS && APPL->APPLALIASVAL[0] != 0x20) + { + // COPY ALIAS TO COMMAND _BUFFER, THEN REENTER COMMAND HANDLER + + memcpy(COMMANDBUFFER, APPL->APPLALIASVAL, ALIASLEN); + COMMANDBUFFER[80] = 0; + _strupr(COMMANDBUFFER); + memcpy(OrigCmdBuffer, APPL->APPLALIASVAL, ALIASLEN); // In case original case version needed + + ALIASINVOKED = TRUE; // To prevent Alias Loops + } + else + { + + // Copy Appl Command to Command Buffer. Ensure doesn't contain old command + + memset(COMMANDBUFFER, ' ', 72); + memcpy(COMMANDBUFFER, APPL->APPLCMD, 12); + } + DoTheCommand(Session); + return; + } + APPL++; + APPLMASK <<= 1; + } + } + } + + if (axcalls[7] == 0) + { + // SEE IF CALL TO ANOTHER NODE + + struct DEST_LIST * Dest = DESTS; + int n = MAXDESTS; + + if (axcalls[6] == 0x60) // if SSID, dont check aliases + { + while (n--) + { + if (memcmp(Dest->DEST_ALIAS, TextCall, 6) == 0) + { + DoNetromConnect(Session, Bufferptr, Dest, Spy); + return; + } + Dest++; + } + } + + Dest = DESTS; + n = MAXDESTS; + + while (n--) + { + if (CompareCalls(Dest->DEST_CALL, axcalls)) + { + DoNetromConnect(Session, Bufferptr, Dest, Spy); + return; + } + Dest++; + } + } + + // Must be Downlink Connect + +Downlink: + + if (CONNECTPORT == 0 && NUMBEROFPORTS > 1) + { + // L2 NEEDS PORT NUMBER + + Bufferptr = Cmdprintf(Session, Bufferptr, "Downlink connect needs port number - C P CALLSIGN\r"); + + // Send Port List + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // ENSURE PORT IS AVAILABLE FOR L2 USE + + if (PORT->PROTOCOL >= 10) // Pactor=-style port? + { + int count; + + // if Via PACTOR ARDOP WINMOR or VARA, convert to attach and call = Digi's are in AX25STRING (+7) + + if (memcmp(&axcalls[7], &WINMOR[0], 6) == 0 || + memcmp(&axcalls[7], &ARDOP[0], 6) == 0 || + memcmp(&axcalls[7], &VARA[0], 6) == 0 || + memcmp(&axcalls[7], &PACTORCALL[0], 6) == 0) + { + char newcmd[80]; + + TextCall[TextCallLen] = 0; + sprintf(newcmd, "%s %s", CmdTail, TextCall); + + ATTACHCMD(Session, Bufferptr, newcmd, NULL); + return; + } + + // If on a KAM or SCS with ax.25 on port 2, do an Attach command, then pass on connect + + if (EXTPORT->MAXHOSTMODESESSIONS <= 1) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is not an ax.25 port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // Only Allow Attach VHF from Secure Applications or if PERMITGATEWAY is set + + if (EXTPORT->PERMITGATEWAY == 0 && Session->Secure_Session == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, you are not allowed to use this port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + count = EXTPORT->MAXHOSTMODESESSIONS; + count--; // First is Pactor Stream, count is now last ax.25 session + + while (count) + { + if (EXTPORT->ATTACHEDSESSIONS[count] == 0) + { + int Paclen, PortPaclen; + struct DATAMESSAGE * Buffer; + struct DATAMESSAGE Message = {0}; + char Callstring[80]; + int len; + + // Found a free one - use it + + // See if TNC is OK + + Message.PORT = count; + + ret = PORT->PORTTXCHECKCODE(PORT, Message.PORT); + + if ((ret & 0xff00) == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Error - TNC Not Ready\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // GET CIRCUIT TABLE ENTRY FOR OTHER END OF LINK + + NewSess = SetupNewSession(Session, Bufferptr); + if (NewSess == NULL) + return; + + // if a UZ7HO port, and the uplink is L2 or Uz7HO invert SSID bits + + // We only get here if multisession + + if (memcmp(EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0) + goto noFlip; + + if ((Session->L4CIRCUITTYPE & BPQHOST))// host + goto noFlip; + + if ((Session->L4CIRCUITTYPE & PACTOR)) + { + // incoming is Pactorlike - see if UZ7HO + + if (memcmp(Session->L4TARGET.EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0) + goto noFlip; + else + NewSess->L4USER[6] ^= 0x1e; // UZ7HO Uplink - flip + } + else + + // Must be L2 uplink - flip + + NewSess->L4USER[6] ^= 0x1e; // Flip SSID +noFlip: + EXTPORT->ATTACHEDSESSIONS[count] = NewSess; + + NewSess->KAMSESSION = count; + + // Set paclen to lower of incoming and outgoing + + Paclen = Session->SESSPACLEN; // Incoming PACLEN + + if (Paclen == 0) + Paclen = 256; // 0 = 256 + + PortPaclen = PORT->PORTPACLEN; + + if (PortPaclen == 0) + PortPaclen = 256; // 0 = 256 + + if (PortPaclen < Paclen) + Paclen = PortPaclen; + + NewSess->SESSPACLEN = Paclen; + Session->SESSPACLEN = Paclen; + + NewSess->L4STATE = 5; + NewSess->L4CIRCUITTYPE = DOWNLINK + PACTOR; + NewSess->L4TARGET.PORT = PORT; + + // Send the connect command to the TNC + + Buffer = REPLYBUFFER; + + Buffer->PORT = count; + Buffer->PID = 0xf0; + + // if on Telnet Port convert use original cmd tail + + // Why just on telnet - what not all ports?? + + if (memcmp(EXTPORT->PORT_DLL_NAME, "TELNET", 6) == 0 || memcmp(EXTPORT->PORT_DLL_NAME, "SCSPACTOR", 9) == 0) + { + NewSess->Secure_Session = Session->Secure_Session; + len = sprintf(Callstring,"C %s", cmdCopy); + } + else + { + TextCall[TextCallLen] = 0; + + len = sprintf(Callstring,"C %s", TextCall); + + if (axcalls[7]) + { + int digi = 7; + + // we have digis + + len += sprintf(&Callstring[len], " via"); + + while (axcalls[digi]) + { + TextCall[ConvFromAX25(&axcalls[digi], TextCall)] = 0; + len += sprintf(&Callstring[len], " %s", TextCall); + digi += 7; + } + } + } + Callstring[len++] = 13; + Callstring[len] = 0; + + Buffer->LENGTH = len + MSGHDDRLEN + 1; + memcpy(Buffer->L2DATA, Callstring, len); + C_Q_ADD(&PORT->PORTTX_Q, (UINT *)Buffer); + + return; + } + count--; + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "Error - No free streams on this port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if ((Session->L4CIRCUITTYPE & BPQHOST) == 0 && PORT->PORTL3FLAG) + { + //Port only for L3 + + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is for internode traffic only\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (PORT->PortUIONLY) + { + //Port only for UI + + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is for UI traffic only\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (Session->L4USER[6] == 0x42 || Session->L4USER[6] == 0x44) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry - Can't make ax.25 calls with SSID of T or R\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // Get Session Entry for Downlink + + NewSess = SetupNewSession(Session, Bufferptr); + if (NewSess == NULL) + return; + + NewSess->L4CIRCUITTYPE = L2LINK + DOWNLINK; + + // FORMAT LINK TABLE ENTRY FOR THIS CONNECTION + + memcpy(ourcall, NewSess->L4USER, 7); + + // SSID SWAP TEST - LEAVE ALONE FOR HOST or Pactor like (unless UZ7HO) + + if ((Session->L4CIRCUITTYPE & BPQHOST))// host + goto noFlip3; + + if ((Session->L4CIRCUITTYPE & PACTOR)) + { + // incoming is Pactorlike - see if UZ7HO + + if (memcmp(Session->L4TARGET.EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0) + goto noFlip3; + + if (Session->L4TARGET.EXTPORT->MAXHOSTMODESESSIONS < 2) // Not multisession + goto noFlip3; + + ourcall[6] ^= 0x1e; // UZ7HO Uplink - flip + } + else + + // Must be L2 uplink - flip + + ourcall[6] ^= 0x1e; // Flip SSID + +noFlip3: + + // SET UP NEW SESSION (OR RESET EXISTING ONE) + + FindLink(axcalls, ourcall, Port, &LINK); + + if (LINK == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry - System Tables Full\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + + // Should release NewSess + + return; + } + + memcpy(LINK->LINKCALL, axcalls, 7); + memcpy(LINK->OURCALL, ourcall, 7); + + LINK->LINKPORT = PORT; + + LINK->L2TIME = PORT->PORTT1; + + // Copy Digis + + n = 7; + ptr = &LINK->DIGIS[0]; + + while (axcalls[n]) + { + memcpy(ptr, &axcalls[n], 7); + n += 7; + ptr += 7; + + LINK->L2TIME += 2 * PORT->PORTT1; // ADJUST TIMER VALUE FOR 1 DIGI + } + + LINK->LINKTYPE = 2; // DOWNLINK + LINK->LINKWINDOW = PORT->PORTWINDOW; + + RESET2(LINK); // RESET ALL FLAGS + + if (CMD->String[0] == 'N' && SUPPORT2point2) + LINK->L2STATE = 1; // New (2.2) send XID + else + LINK->L2STATE = 2; // Send SABM + + LINK->CIRCUITPOINTER = NewSess; + + NewSess->L4TARGET.LINK = LINK; + + if (PORT->PORTPACLEN) + NewSess->SESSPACLEN = Session->SESSPACLEN = PORT->PORTPACLEN; + + if (CQFLAG == 0) // if a CQ CALL DONT SEND SABM + { + if (LINK->L2STATE == 1) + L2SENDXID(LINK); + else + SENDSABM(LINK); + } + ReleaseBuffer((UINT *)REPLYBUFFER); + return; +} + +BOOL DecodeCallString(char * Calls, BOOL * Stay, BOOL * Spy, UCHAR * AXCalls) +{ + // CONVERT CALL + OPTIONAL DIGI STRING TO AX25, RETURN + // CONVERTED STRING IN AXCALLS. Return FALSE if invalied + + char * axptr = AXCalls; + char * ptr, *Context; + int CQFLAG = 0; // NOT CQ CALL + int n = 8; // Max digis + + *Stay = 0; + *Spy = 0; + + memset(AXCalls, 0, 64); + + ptr = strtok_s(Calls, " ,", &Context); + + if (ptr == NULL) + return FALSE; + + // First field is Call + + if (ConvToAX25(ptr, axptr) == 0) + return FALSE; + + axptr += 7; + + ptr = strtok_s(NULL, " ,", &Context); + + while (ptr && n--) + { + // NEXT FIELD = COULD BE CALLSIGN, VIA, OR S (FOR STAY) + + if (strcmp(ptr, "S") == 0) + *Stay = TRUE; + else if (strcmp(ptr, "Z") == 0) + *Spy = TRUE; + else if (memcmp(ptr, "VIA", (int)strlen(ptr)) == 0) + { + } //skip via + else + { + // Convert next digi + + if (ConvToAX25(ptr, axptr) == 0) + return FALSE; + + axptr += 7; + } + + ptr = strtok_s(NULL, " ,", &Context); + } + + return TRUE; +} + + +VOID LINKCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // PROCESS *** LINKED to CALLSIGN + + char * ptr, *Context; + UCHAR axcall[7]; + int ret; + + if (LINKEDFLAG == 'Y' || // UNCONDITIONAL? + (LINKEDFLAG == 'A' && + ((Session->L4CIRCUITTYPE & BPQHOST) || Session->Secure_Session || Session->PASSWORD == 0xffff))) + { + ptr = strtok_s(CmdTail, " ", &Context); + if (ptr) + ptr = strtok_s(NULL, " ", &Context); + + if (ptr) + { + ret = ConvToAX25Ex(ptr, axcall); + + if (ret) + { + memcpy(Session->L4USER, axcall, 7); + strcpy(Bufferptr, OKMSG); + Bufferptr += (int)strlen(OKMSG); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + } + + strcpy(Bufferptr, BADMSG); + Bufferptr += (int)strlen(BADMSG); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + memcpy(Bufferptr, PASSWORDMSG, LPASSMSG); + Bufferptr += LPASSMSG; + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +int CompareNode(const void *a, const void *b); +int CompareAlias(const void *a, const void *b); + +char * DoOneNode(TRANSPORTENTRY * Session, char * Bufferptr, struct DEST_LIST * Dest) +{ + char Normcall[10]; + char Alias[10]; + struct NR_DEST_ROUTE_ENTRY * NRRoute; + struct DEST_ROUTE_ENTRY * Route; + struct ROUTE * Neighbour; + int i, Active, len; + + Alias[6] = 0; + + memcpy(Alias, Dest->DEST_ALIAS, 6); + strlop(Alias, ' '); + + Normcall[ConvFromAX25(Dest->DEST_CALL, Normcall)] = 0; + + Bufferptr = Cmdprintf(Session, Bufferptr, "Routes to: %s:%s", Alias, Normcall); + + if (Dest->DEST_COUNT) + Bufferptr = Cmdprintf(Session, Bufferptr, " RTT=%4.2f FR=%d %c %.1d\r", + Dest->DEST_RTT /1000.0, Dest->DEST_COUNT, + (Dest->DEST_STATE & 0x40)? 'B':' ', (Dest->DEST_STATE & 63)); + else + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + NRRoute = &Dest->NRROUTE[0]; + + Active = Dest->DEST_ROUTE; + + for (i = 1; i < 4; i++) + { + Neighbour = NRRoute->ROUT_NEIGHBOUR; + + if (Neighbour) + { + len = ConvFromAX25(Neighbour->NEIGHBOUR_CALL, Normcall); + Normcall[len] = 0; + + Bufferptr = Cmdprintf(Session, Bufferptr, "%c %d %d %d %s\r", + (Active == i)?'>':' ',NRRoute->ROUT_QUALITY, NRRoute->ROUT_OBSCOUNT, Neighbour->NEIGHBOUR_PORT, Normcall); + } + NRRoute++; + } + + // DISPLAY INP3 ROUTES + + Route = &Dest->ROUTE[0]; + + Active = Dest->DEST_ROUTE; + + for (i = 1; i < 4; i++) + { + Neighbour = Route->ROUT_NEIGHBOUR; + + if (Neighbour) + { + double srtt = Route->SRTT/1000.0; + + len = ConvFromAX25(Neighbour->NEIGHBOUR_CALL, Normcall); + Normcall[len] = 0; + + Bufferptr = Cmdprintf(Session, Bufferptr, "%c %d %4.2fs %d %s\r", + (Active == i + 3)?'>':' ',Route->Hops, srtt, Neighbour->NEIGHBOUR_PORT, Normcall); + } + Route++; + } + + return Bufferptr; +} + + +int DoViaEntry(struct DEST_LIST * Dest, int n, char * line, int cursor) +{ + char Portcall[10]; + int len; + + if (Dest->NRROUTE[n].ROUT_NEIGHBOUR != 0 && Dest->NRROUTE[n].ROUT_NEIGHBOUR->INP3Node == 0) + { + len=ConvFromAX25(Dest->NRROUTE[n].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, Portcall); + Portcall[len]=0; + + len=sprintf(&line[cursor],"%s %d %d ", + Portcall, + Dest->NRROUTE[n].ROUT_NEIGHBOUR->NEIGHBOUR_PORT, + Dest->NRROUTE[n].ROUT_QUALITY); + + cursor+=len; + + if (Dest->NRROUTE[n].ROUT_OBSCOUNT > 127) + { + len=sprintf(&line[cursor],"! "); + cursor+=len; + } + } + return cursor; +} + +int DoINP3ViaEntry(struct DEST_LIST * Dest, int n, char * line, int cursor) +{ + char Portcall[10]; + int len; + double srtt; + + if (Dest->ROUTE[n].ROUT_NEIGHBOUR != 0) + { + srtt = Dest->ROUTE[n].SRTT/1000.0; + + len=ConvFromAX25(Dest->ROUTE[n].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, Portcall); + Portcall[len]=0; + + len=sprintf(&line[cursor],"%s %d %d %4.2fs ", + Portcall, + Dest->ROUTE[n].ROUT_NEIGHBOUR->NEIGHBOUR_PORT, + Dest->ROUTE[n].Hops, srtt); + + cursor+=len; + + if (Dest->NRROUTE[n].ROUT_OBSCOUNT > 127) + { + len=sprintf(&line[cursor],"! "); + cursor+=len; + } + } + return cursor; +} + +int WildCmp(char * pattern, char * string) +{ + // Check if string is at end or not. + + if (*pattern == '\0') + return *string == '\0'; + + // Check for single character missing or match + + if (*pattern == '?' || *pattern == *string) + return *string != '\0' && WildCmp(pattern + 1, string + 1); + + if (*pattern == '*') + { + // Check for multiple character missing + + return WildCmp(pattern + 1, string) || (*string != '\0' && WildCmp(pattern, string + 1)); + } + + return 0; +} + +VOID CMDN00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + struct DEST_LIST * Dest = DESTS; + int count = MAXDESTS, i; + char Normcall[10]; + char Alias[10]; + int Width = 4; + int x = 0, n = 0; + struct DEST_LIST * List[1000]; + char Param = 0; + char * ptr, * param2,* Context; + char Nodeline[21]; + char AXCALL[7]; + char * Call; + char * Qualptr; + int Qual; + char line[160]; + int cursor, len; + UCHAR axcall[7]; + int SavedOBSINIT = OBSINIT; + struct ROUTE * ROUTE = NULL; + char Pattern[80] = ""; + char * firststar; + int minqual = 0; + + ptr = strtok_s(CmdTail, " ", &Context); + param2 = strtok_s(NULL, " ", &Context); + + if (ptr) + { + if (strcmp(ptr, "ADD") == 0) + goto NODE_ADD; + + if (strcmp(ptr, "DEL") == 0) + goto NODE_DEL; + + if (strcmp(ptr, "VIA") == 0) + goto NODE_VIA; + } + + if (ptr) + { + // Could be C or a pattern. Accept C pattern or pattern C + + if ((int)strlen(ptr) > 1) + { + strcpy(Pattern, ptr); + if (param2 && param2[0] == 'C') + Param = 'C'; + } + else + { + Param = ptr[0]; + if (param2) + strcpy(Pattern, param2); + } + } + + // Pattern >nnn selects nodes with at least that quality + + if (Pattern[0] == '>') + { + minqual = atoi(&Pattern[1]); + Pattern[0] = 0; + } + + // We need to pick out CALL or CALL* from other patterns (as call use detail display) + + firststar = strchr(Pattern, '*'); + + if ((firststar && *(firststar + 1) != 0)|| strchr(Pattern, '?')) //(* not on end) + + // definitely pattern + + goto DoNodePattern; + + // If it works as CALL*, process, else drop through + + if (Pattern[0]) + { + UCHAR AXCall[8]; + int count; + int paramlen = (int)strlen(ptr); + char parampadded[20]; + int n = 0; + + Alias[8] = 0; + strcpy(parampadded, Pattern); + strcat(parampadded, " "); + + ConvToAX25(Pattern, AXCall); + + // if * on end, list all ssids + + if (firststar) + { + AXCall[6] = 0; + + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + while (AXCall[6] < 32) + { + Dest = DESTS; + + for (count = 0; count < MAXDESTS; count++) + { + if (memcmp(Dest->DEST_ALIAS, parampadded, 6) == 0 || CompareCalls(Dest->DEST_CALL, AXCall)) + { + break; + } + Dest++; + } + + if (count < MAXDESTS) + { + Bufferptr = DoOneNode(Session, Bufferptr, Dest); + n++; + } + + AXCall[6] += 2; + } + + if (n) // Found Some + { + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + Dest = DESTS; // Reset + + // Drop through to try as pattern + } + else + { + // process as just call + + for (count = 0; count < MAXDESTS; count++) + { + if (memcmp(Dest->DEST_ALIAS, parampadded, 6) == 0 || CompareCalls(Dest->DEST_CALL, AXCall)) + { + break; + } + Dest++; + } + + if (count == MAXDESTS) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not found\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + Bufferptr = DoOneNode(Session, Bufferptr, Dest); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + } + +DoNodePattern: + + Bufferptr = Cmdprintf(Session, Bufferptr, "Nodes\r"); + + while (count--) + { + if (Dest->DEST_CALL[0] != 0) + { + if (Dest->NRROUTE->ROUT_QUALITY >= minqual) + if (Param != 'T' || Dest->DEST_COUNT) + List[n++] = Dest; + + if (n > 999) + break; + } + Dest++; + } + + if (Param == 'C') + qsort(List, n, sizeof(void *), CompareNode); + else + qsort(List, n, sizeof(void *), CompareAlias); + + + for (i = 0; i < n; i++) + { + int len = ConvFromAX25(List[i]->DEST_CALL, Normcall); + Normcall[len]=0; + + memcpy(Alias, List[i]->DEST_ALIAS, 6); + Alias[6] = 0; + strlop(Alias, ' '); + + if (strlen(Alias)) + strcat(Alias, ":"); + + if (Alias[0] == '#' && HIDENODES == 1 && Param != '*') // Hidden Node and not N * command + continue; + + if (Pattern[0]) + if (!WildCmp(Pattern, Normcall) && !WildCmp(Pattern, Alias)) + continue; + + if (Param == 'T') + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%s%s RTT=%4.2f Frames = %d %c %.1d\r", + Alias, Normcall, List[i]->DEST_RTT /1000.0, List[i]->DEST_COUNT, + (List[i]->DEST_STATE & 0x40)? 'B':' ', (List[i]->DEST_STATE & 63)); + } + else + { + len = sprintf(Nodeline, "%s%s", Alias, Normcall); + memset(&Nodeline[len], ' ', 20 - len); + Nodeline[20] = 0; + Bufferptr = Cmdprintf(Session, Bufferptr, "%s", Nodeline); + + if (++x == Width) + { + x = 0; + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + } + } + } + + if (x) + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + goto SendReply; + + +NODE_VIA: + + // List Nodes reachable via a neighbour + + ptr = param2; + + if (ptr == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Missing Call\r"); + goto SendReply; + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + ConvToAX25(ptr, AXCALL); + + Dest = DESTS; + + Dest-=1; + + for (count=0; countNRROUTE[0].ROUT_NEIGHBOUR == 0 && Dest->ROUTE[0].ROUT_NEIGHBOUR == 0) + continue; + + + if ((Dest->NRROUTE[0].ROUT_NEIGHBOUR && CompareCalls(Dest->NRROUTE[0].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL)) + || (Dest->NRROUTE[1].ROUT_NEIGHBOUR && CompareCalls(Dest->NRROUTE[1].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL)) + || (Dest->NRROUTE[2].ROUT_NEIGHBOUR && CompareCalls(Dest->NRROUTE[2].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL)) + + || (Dest->ROUTE[0].ROUT_NEIGHBOUR && CompareCalls(Dest->ROUTE[0].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL)) + || (Dest->ROUTE[1].ROUT_NEIGHBOUR && CompareCalls(Dest->ROUTE[1].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL)) + || (Dest->ROUTE[2].ROUT_NEIGHBOUR && CompareCalls(Dest->ROUTE[2].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL))) + { + len=ConvFromAX25(Dest->DEST_CALL,Normcall); + + Normcall[len]=0; + + memcpy(Alias,Dest->DEST_ALIAS,6); + + Alias[6]=0; + + for (i=0;i<6;i++) + { + if (Alias[i] == ' ') + Alias[i] = 0; + } + + cursor=sprintf(line,"%s:%s ", Alias,Normcall); + + cursor = DoViaEntry(Dest, 0, line, cursor); + cursor = DoViaEntry(Dest, 1, line, cursor); + cursor = DoViaEntry(Dest, 2, line, cursor); + cursor = DoINP3ViaEntry(Dest, 0, line, cursor); + cursor = DoINP3ViaEntry(Dest, 1, line, cursor); + cursor = DoINP3ViaEntry(Dest, 2, line, cursor); + + line[cursor++]='\r'; + line[cursor++]=0; + + Bufferptr = Cmdprintf(Session, Bufferptr, "%s", line); + } + } + + + goto SendReply; + +NODE_ADD: + + // FORMAT IS NODE ADD ALIAS:CALL QUAL ROUTE PORT + + + if (Session->PASSWORD != 0xFFFF) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%s", PASSWORDMSG); + goto SendReply; + } + + ptr = param2; + + if (ptr == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Missing Alias:Call\r"); + goto SendReply; + } + + Call = strlop(ptr, ':'); + + if (Call == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Missing Alias:Call\r"); + goto SendReply; + } + + + ConvToAX25(Call, AXCALL); + + Qualptr = strtok_s(NULL, " ", &Context); + + if (Qualptr == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Quality missing\r"); + goto SendReply; + } + + Qual = atoi(Qualptr); + + if (Qual < MINQUAL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Quality is below MINQUAL\r"); + goto SendReply; + } + + if (FindDestination(AXCALL, &Dest)) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Node already in Table\r"); + goto SendReply; + } + + if (Dest == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Node Table Full\r"); + goto SendReply; + } + + memcpy(Dest->DEST_CALL, AXCALL, 7); + memcpy(Dest->DEST_ALIAS, ptr, 6); + + NUMBEROFNODES++; + + ptr = strtok_s(NULL, " ", &Context); + + if (ptr == NULL || ptr[0] == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Neighbour missing\r"); + goto SendReply; + } + + if (ConvToAX25(ptr, axcall) == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Neighbour\r"); + goto SendReply; + } + else + { + int Port; + + ptr = strtok_s(NULL, " ", &Context); + if (ptr == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Port missing\r"); + goto SendReply; + } + + Port = atoi(ptr); + + if (Context[0] == '!') + { + OBSINIT = 255; //; SPECIAL FOR LOCKED + } + + if (FindNeighbour(axcall, Port, &ROUTE)) + { + PROCROUTES(Dest, ROUTE, Qual); + } + + OBSINIT = SavedOBSINIT; + + Bufferptr = Cmdprintf(Session, Bufferptr, "Node Added\r"); + goto SendReply; + } + + + + +/* +PNODE48: + + +; GET NEIGHBOURS FOR THIS DESTINATION +; + CALL CONVTOAX25 + JNZ SHORT BADROUTE +; + CALL GETVALUE + MOV SAVEPORT,AL ; SET PORT FOR _FINDNEIGHBOUR + + CALL GETVALUE + MOV ROUTEQUAL,AL +; + MOV ESI,OFFSET32 AX25CALL + + PUSH EBX ; SAVE DEST + CALL _FINDNEIGHBOUR + MOV EAX,EBX ; ROUTE TO AX + POP EBX + + JZ SHORT NOTBADROUTE + + JMP SHORT BADROUTE + +NOTBADROUTE: +; +; UPDATE ROUTE LIST FOR THIS DEST +; + MOV ROUT1_NEIGHBOUR[EBX],EAX + MOV AL,ROUTEQUAL + MOV ROUT1_QUALITY[EBX],AL + MOV ROUT1_OBSCOUNT[EBX],255 ; LOCKED +; + POP EDI + POP EBX + + INC _NUMBEROFNODES + + JMP SENDOK + +BADROUTE: +; +; KILL IT +; + MOV ECX,TYPE DEST_LIST + MOV EDI,EBX + MOV AL,0 + REP STOSB + + JMP BADROUTECMD + +*/ + + goto SendReply; + + +NODE_DEL: + + if (Session->PASSWORD != 0xFFFF) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%s", PASSWORDMSG); + goto SendReply; + } + + ptr = param2; + + if (ptr == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Missing Call\r"); + goto SendReply; + } + + if (strcmp(ptr, "ALL") == 0) + { + struct DEST_LIST * DEST = DESTS; + int n = MAXDESTS; + + while (n--) + { + if (DEST->DEST_CALL[0] && ((DEST->DEST_STATE & 0x80) == 0)) // Don't delete appl node + REMOVENODE(DEST); + + DEST++; + } + + ClearNodes(); + + Bufferptr = Cmdprintf(Session, Bufferptr, "All Nodes Deleted\r"); + goto SendReply; + } + + ConvToAX25(ptr, AXCALL); + + if (FindDestination(AXCALL, &Dest) == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not Found\r"); + goto SendReply; + } + + if (Dest->DEST_STATE & 0x80) + Bufferptr = Cmdprintf(Session, Bufferptr, "APPL Node - Can't delete\r"); + else + { + REMOVENODE(Dest); + Bufferptr = Cmdprintf(Session, Bufferptr, "Node Deleted\r"); + } + Bufferptr = Cmdprintf(Session, Bufferptr, "Node Deleted\r"); + +SendReply: + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID CMDQUERY(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD) +{ + // DISPLAY AVAILABLE COMMANDS + + int n; + char * ptr; + char ApplList[2048]; + char * out = ApplList; + + CMDX * CMD = &COMMANDS[APPL1]; + + for (n = 0; n < NumberofAppls; n++) + { + ptr = &CMD->String[0]; + if (*(ptr) != '*') + { + while (*ptr != ' ') + { + *(out++) = *(ptr++); + } + *(out++) = ' '; + } + CMD++; + } + + *(out) = 0; + + n = CMDLISTLEN; + + if (NEEDMH == 0) + n -= 7; // Dont show MH + + Bufferptr = Cmdprintf(Session, Bufferptr, "%s%s\r", ApplList, CMDLIST); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +char * FormatMH(MHSTRUC * MH, char Format); + +VOID MHCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // DISPLAY HEARD LIST + + int Port = 0, sess = 0; + char * ptr, *Context, *pattern; + struct PORTCONTROL * PORT = NULL; + MHSTRUC * MH; + int count = MHENTRIES; + int n; + char Normcall[20]; + char From[10]; + char DigiList[100]; + char * Output; + int len; + char Digi = 0; + + + // Note that the MHDIGIS field may contain rubbish. You have to check End of Address bit to find + // how many digis there are + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr) + Port = atoi(ptr); + + if (Port) + PORT = GetPortTableEntryFromPortNum(Port); + + if (PORT == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + pattern = strtok_s(NULL, " ", &Context); + + if (pattern) + _strupr(pattern); // Optional filter + + MH = PORT->PORTMHEARD; + + if (MH == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "MHEARD not enabled on that port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (pattern && strstr(pattern, "CLEAR")) + { + if (Session->Secure_Session) + { + memset(MH, 0, MHENTRIES * sizeof(MHSTRUC)); + SaveMH(); + Bufferptr = Cmdprintf(Session, Bufferptr, "Heard List for Port %d Cleared\r", Port); + } + else + { + Bufferptr = Cmdprintf(Session, Bufferptr, "MH Clear needs SYSOP status\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + } + else + { + if (CMD->String[2] == 'V') // MHV + { + Bufferptr = Cmdprintf(Session, Bufferptr, "MHeard List %s for Port %d\r", MYNODECALL, Port); + Bufferptr = Cmdprintf(Session, Bufferptr, "Callsign Last heard Pkts RX via Digi ;) \r"); + Bufferptr = Cmdprintf(Session, Bufferptr, "--------- ----------- ------- ------------------------------------------\r"); + } + else + if (pattern) + Bufferptr = Cmdprintf(Session, Bufferptr, "Heard List for Port %d filtered by %s\r", Port, pattern); + else + Bufferptr = Cmdprintf(Session, Bufferptr, "Heard List for Port %d\r", Port); + } + while (count--) + { + if (MH->MHCALL[0] == 0) + break; + + Digi = 0; + + len = ConvFromAX25(MH->MHCALL, Normcall); + + Normcall[len++] = MH->MHDIGI; + Normcall[len++] = 0; + + if (pattern && strstr(Normcall, pattern) == 0) + { + MH++; + continue; + } + + n = 8; // Max number of digi-peaters + + ptr = &MH->MHCALL[6]; // End of Address bit + + Output = &DigiList[0]; + + if ((*ptr & 1) == 0) + { + // at least one digi + + strcpy(Output, "via "); + Output += 4; + + while ((*ptr & 1) == 0) + { + // MORE TO COME + + From[ConvFromAX25(ptr + 1, From)] = 0; + Output += sprintf((char *)Output, "%s", From); + + ptr += 7; + n--; + + if (n == 0) + break; + + // See if digi actioned - put a * on last actioned + + if (*ptr & 0x80) + { + if (*ptr & 1) // if last address, must need * + { + *(Output++) = '*'; + Digi = '*'; + } + + else + if ((ptr[7] & 0x80) == 0) // Repeased by next? + { + *(Output++) = '*'; // No, so need * + Digi = '*'; + } + +} + *(Output++) = ','; + } + *(--Output) = 0; // remove last comma + } + else + *(Output) = 0; + + // if we used a digi set * on call and display via string + + + if (Digi) + Normcall[len++] = Digi; + else + DigiList[0] = 0; // Dont show list if not used + + Normcall[len++] = 0; + + + ptr = FormatMH(MH, CMD->String[2]); + + if (CMD->String[2] == 'V') // MHV + Bufferptr = Cmdprintf(Session, Bufferptr, "%-10s %-10s %-10d %-30s\r", + Normcall, ptr, MH->MHCOUNT, DigiList); + else + Bufferptr = Cmdprintf(Session, Bufferptr, "%-10s %s %s\r", Normcall, ptr, DigiList); + + MH++; + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +int Rig_Command(int Session, char * Command); + +VOID RADIOCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD) +{ + char * ptr; + + if (Rig_Command(Session->CIRCUITINDEX, CmdTail)) + { + ReleaseBuffer((UINT *)REPLYBUFFER); + return; + } + + // Error Message is in buffer + + ptr = strchr(CmdTail, 13); + + if (ptr) + { + int len = (int)(++ptr - CmdTail); + + memcpy(Bufferptr, CmdTail, len); + Bufferptr += len; + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + + +VOID SendNRRecordRoute(struct DEST_LIST * DEST, TRANSPORTENTRY * Session); + + +VOID NRRCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD) +{ + // PROCESS 'NRR - Netrom Record Route' COMMAND + + char * ptr, *Context; + struct DEST_LIST * Dest = DESTS; + int count = MAXDESTS; + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr) + { + UCHAR AXCall[8]; + int count; + + ConvToAX25(ptr, AXCall); + strcat(ptr, " "); + + for (count = 0; count < MAXDESTS; count++) + { + if (memcmp(Dest->DEST_ALIAS, ptr, 6) == 0 || CompareCalls(Dest->DEST_CALL, AXCall)) + { + SendNRRecordRoute(Dest, Session); + memcpy(Bufferptr, OKMSG, 3); + Bufferptr += 3; + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + + return; + } + Dest++; + } + } + Bufferptr = Cmdprintf(Session, Bufferptr, "Not found\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; +} + +int CHECKINTERLOCK(struct PORTCONTROL * OURPORT) +{ + // See if any Interlocked ports are Busy + + struct PORTCONTROL * PORT = PORTTABLE; + struct _EXTPORTDATA * EXTPORT; + + int n = NUMBEROFPORTS; + int ourgroup = OURPORT->PORTINTERLOCK; + + while (PORT) + { + if (PORT != OURPORT) + { + if (PORT->PORTINTERLOCK == ourgroup) + { + // Same Group - is it busy + + int i = 0; + + EXTPORT = (struct _EXTPORTDATA *)PORT; + + while (i < 27) + if (EXTPORT->ATTACHEDSESSIONS[i++]) + return PORT->PORTNUMBER; + } + } + PORT = PORT->PORTPOINTER; + } + + return 0; +} + +VOID ATTACHCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD) +{ + // ATTACH to a PACTOR or similar port + + TRANSPORTENTRY * NewSess; + struct _EXTPORTDATA * EXTPORT; + struct TNCINFO * TNC; + + int Port = 0, sess = 0; + char * ptr, *Context; + int ret; + struct PORTCONTROL * PORT = NULL; + struct DATAMESSAGE Message = {0}; + int Paclen, PortPaclen; + struct DATAMESSAGE * Buffer; + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr) + Port = atoi(ptr); + + if (Port) + PORT = GetPortTableEntryFromPortNum(Port); + + if (PORT == NULL || PORT->PROTOCOL < 10) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // If attach on telnet port, find a free stream + + EXTPORT = (struct _EXTPORTDATA *)PORT; + + if (strstr(EXTPORT->PORT_DLL_NAME, "TELNET")) + { + int count = EXTPORT->MAXHOSTMODESESSIONS; + count--; // First is Pactor Stream, count is now last ax.25 session + + while (count) + { + if (EXTPORT->ATTACHEDSESSIONS[count] == 0) + { + int Paclen, PortPaclen; + struct DATAMESSAGE Message = {0}; + + // Found a free one - use it + + // See if TNC is OK + + Message.PORT = count; + + ret = PORT->PORTTXCHECKCODE(PORT, Message.PORT); + + if ((ret & 0xff00) == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Error - TNC Not Ready\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // GET CIRCUIT TABLE ENTRY FOR OTHER END OF LINK + + NewSess = SetupNewSession(Session, Bufferptr); + + if (NewSess == NULL) + return; + + EXTPORT->ATTACHEDSESSIONS[count] = NewSess; + + NewSess->Secure_Session = Session->Secure_Session; + + NewSess->KAMSESSION = count; + + // Set paclen to lower of incoming and outgoing + + Paclen = Session->SESSPACLEN; // Incoming PACLEN + + if (Paclen == 0) + Paclen = 256; // 0 = 256 + + PortPaclen = PORT->PORTPACLEN; + + if (PortPaclen == 0) + PortPaclen = 256; // 0 = 256 + + if (PortPaclen < Paclen) + Paclen = PortPaclen; + + NewSess->SESSPACLEN = Paclen; + Session->SESSPACLEN = Paclen; + + NewSess->L4STATE = 5; + NewSess->L4CIRCUITTYPE = DOWNLINK + PACTOR; + NewSess->L4TARGET.PORT = PORT; + + ptr = strtok_s(NULL, " ", &Context); + sess = count; + + // Replace command tail with original (before conversion to upper case + + Context = Context + (OrigCmdBuffer - COMMANDBUFFER); + + goto checkattachandcall; + + + memcpy(Bufferptr, OKMSG, 3); + Bufferptr += 3; + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + + return; + } + count--; + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "Error - No free streams on this port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + Message.PORT = 0; + + ret = PORT->PORTTXCHECKCODE(PORT, Message.PORT); + + if ((ret & 0xff00) == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Error - TNC Not Ready\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // See if "Attach and Call" (for VHF ports) + + ptr = strtok_s(NULL, " ", &Context); + + if (ptr && strcmp(ptr, "S") == 0) + { + Session->STAYFLAG = TRUE; + ptr = strtok_s(NULL, " ", &Context); + } + + if (ptr) + { + // we have another param + + // if it is a single char it is a channel number for vhf attach + + if (strlen(ptr) == 1) + { + // Only Allow Attach VHF from Secure Applications or if PERMITGATEWAY is set + + if (EXTPORT->PERMITGATEWAY == 0 && Session->Secure_Session == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, you are not allowed to use this port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + sess = ptr[0] - '@'; + + if (sess < 1 || sess > EXTPORT->MAXHOSTMODESESSIONS) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Error - Invalid Channel\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + ptr = strtok_s(NULL, " ", &Context); + + if (ptr && strcmp(ptr, "S") == 0) + { + Session->STAYFLAG = TRUE; + ptr = strtok_s(NULL, " ", &Context); + } + } + } + + if (ret & 0x8000) // Disconnecting + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Error - Port in use\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // Check Interlock. Only ports with a TNC record can be interlocked + + TNC = PORT->TNC; + + if (TNC) + { + // See if any interlocked ports are in use + + struct TNCINFO * OtherTNC; + int i; + int rxInterlock = TNC->RXRadio; + int txInterlock = TNC->TXRadio; + + if (rxInterlock || txInterlock) + { + for (i=1; i<33; i++) + { + OtherTNC = TNCInfo[i]; + + if (OtherTNC == NULL) + continue; + + if (OtherTNC == TNC) + continue; + + if (rxInterlock == OtherTNC->RXRadio || txInterlock == OtherTNC->TXRadio) // Same Group + { + int n; + + for (n = 0; n <= 26; n++) + { + if (OtherTNC->PortRecord->ATTACHEDSESSIONS[n]) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, interlocked port %d is in use\r", i); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + } + } + } + } + } + + + + + if (EXTPORT->ATTACHEDSESSIONS[sess]) + { + // In use + + Bufferptr = Cmdprintf(Session, Bufferptr, "Error - Port in use\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + // GET CIRCUIT TABLE ENTRY FOR OTHER END OF LINK + + NewSess = SetupNewSession(Session, Bufferptr); + + if (NewSess == NULL) + return; + + // if a UZ7HO port, and the uplink is L2 or Uz7HO and multisession, + // invert SSID bits + + if (memcmp(EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0) + goto noFlip1; + + if (EXTPORT->MAXHOSTMODESESSIONS < 2) // Not multisession + goto noFlip1; + + if ((Session->L4CIRCUITTYPE & BPQHOST)) // host + goto noFlip1; + + if ((Session->L4CIRCUITTYPE & PACTOR)) + { + // incoming is Pactorlike - see if UZ7HO + + if (memcmp(Session->L4TARGET.EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0) + goto noFlip1; + else + NewSess->L4USER[6] ^= 0x1e; // UZ7HO Uplink - flip + } + else + + // Must be L2 uplink - flip + + NewSess->L4USER[6] ^= 0x1e; // Flip SSID +noFlip1: + + EXTPORT->ATTACHEDSESSIONS[sess] = NewSess; + + NewSess->KAMSESSION = sess; + + // Set paclen to lower of incoming and outgoing + + Paclen = Session->SESSPACLEN; // Incoming PACLEN + + if (Paclen == 0) + Paclen = 256; // 0 = 256 + + PortPaclen = PORT->PORTPACLEN; + + if (PortPaclen == 0) + PortPaclen = 256; // 0 = 256 + + if (PortPaclen < Paclen) + Paclen = PortPaclen; + + NewSess->SESSPACLEN = Paclen; + Session->SESSPACLEN = Paclen; + NewSess->L4STATE = 5; + NewSess->L4CIRCUITTYPE = DOWNLINK + PACTOR; + NewSess->L4TARGET.PORT = PORT; + +checkattachandcall: + + if (ptr) + { + // we have a call to connect to + + char Callstring[80]; + int len; + + Buffer = REPLYBUFFER; + Buffer->PORT = sess; + Buffer->PID = 0xf0; + + len = sprintf(Callstring,"C %s", ptr); + + ptr = strtok_s(NULL, " ", &Context); + + while (ptr) // if any other params (such as digis) copy them + { + if (strcmp(ptr, "S") == 0) + { + Session->STAYFLAG = TRUE; + } + else + len += sprintf(&Callstring[len], " %s", ptr); + + ptr = strtok_s(NULL, " ", &Context); + } + + Callstring[len++] = 13; + Callstring[len] = 0; + + Buffer->LENGTH = len + MSGHDDRLEN + 1; + memcpy(Buffer->L2DATA, Callstring, len); + C_Q_ADD(&PORT->PORTTX_Q, (UINT *)Buffer); + + return; + } + + memcpy(Bufferptr, OKMSG, 3); + Bufferptr += 3; + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + + return; +} + +// SYSOP COMMANDS + +CMDX COMMANDS[] = +{ + "SAVENODES ",8, SAVENODES, 0, + "TELRECONFIG ",4, RECONFIGTELNET, 0, + "SAVEMH ",6, SAVEMHCMD, 0, + "REBOOT ",6, REBOOT, 0, + "RIGRECONFIG ",8 , RIGRECONFIG, 0, + "RESTART ",7,RESTART,0, + "RESTARTTNC ",10,RESTARTTNC,0, + "SENDNODES ",8,SENDNODES,0, + "EXTRESTART ",10, EXTPORTVAL, offsetof(EXTPORTDATA, EXTRESTART), + "TXDELAY ",3, PORTVAL, offsetof(PORTCONTROLX, PORTTXDELAY), + "MAXFRAME ",3, PORTVAL, offsetof(PORTCONTROLX, PORTWINDOW), + "RETRIES ",3, PORTVAL, offsetof(PORTCONTROLX, PORTN2), + "FRACK ",3,PORTVAL, offsetof(PORTCONTROLX, PORTT1), + "RESPTIME ",3,PORTVAL, offsetof(PORTCONTROLX, PORTT2), + "PPACLEN ",3,PORTVAL, offsetof(PORTCONTROLX, PORTPACLEN), + "QUALITY ",3,PORTVAL, offsetof(PORTCONTROLX, PORTQUALITY), + "PERSIST ",2,PORTVAL, offsetof(PORTCONTROLX, PORTPERSISTANCE), + "TXTAIL ",3,PORTVAL, offsetof(PORTCONTROLX, PORTTAILTIME), + "XMITOFF ",7,PORTVAL, offsetof(PORTCONTROLX, PORTDISABLED), + "DIGIFLAG ",5,PORTVAL, offsetof(PORTCONTROLX, DIGIFLAG), + "DIGIPORT ",5,PORTVAL, offsetof(PORTCONTROLX, DIGIPORT), + "MAXUSERS ",4,PORTVAL, offsetof(PORTCONTROLX, USERS), + "L3ONLY ",6,PORTVAL, offsetof(PORTCONTROLX, PORTL3FLAG), + "BBSALIAS ",4,PORTVAL, offsetof(PORTCONTROLX, PORTBBSFLAG), + "VALIDCALLS ",5,VALNODES,0, + "WL2KSYSOP ",5,WL2KSYSOP,0, + "STOPPORT ",4,STOPPORT,0, + "STARTPORT ",5,STARTPORT,0, + "STOPCMS ",7,STOPCMS,0, + "STARTCMS ",8,STARTCMS,0, + + "FINDBUFFS ",4,FINDBUFFS,0, + "KISS ",4,KISSCMD,0, + "GETPORTCTEXT",9,GetPortCTEXT, 0, + +#ifdef EXCLUDEBITS + + "EXCLUDE ",4,ListExcludedCalls,0, + +#endif + + "FULLDUP ",4,PORTVAL, offsetof(PORTCONTROLX, FULLDUPLEX), + "SOFTDCD ",4,PORTVAL, offsetof(PORTCONTROLX, SOFTDCDFLAG), + "OBSINIT ",7,SWITCHVAL,(size_t)&OBSINIT, + "OBSMIN ",6,SWITCHVAL,(size_t)&OBSMIN, + "NODESINT ",8,SWITCHVAL,(size_t)&L3INTERVAL, + "L3TTL ",5,SWITCHVAL,(size_t)&L3LIVES, + "L4RETRIES ",5,SWITCHVAL,(size_t)&L4N2, + "L4TIMEOUT ",5,SWITCHVALW,(size_t)&L4T1, + "T3 ",2,SWITCHVALW,(size_t)&T3, + "NODEIDLETIME",8,SWITCHVALW,(size_t)&L4LIMIT, + "LINKEDFLAG ",10,SWITCHVAL,(size_t)&LINKEDFLAG, + "IDINTERVAL ",5,SWITCHVAL,(size_t)&IDINTERVAL, + "MINQUAL ",7,SWITCHVAL,(size_t)&MINQUAL, + "FULLCTEXT ",6,SWITCHVAL,(size_t)&FULL_CTEXT, + "HIDENODES ",8,SWITCHVAL,(size_t)&HIDENODES, + "L4DELAY ",7,SWITCHVAL,(size_t)&L4DELAY, + "L4WINDOW ",6,SWITCHVAL,(size_t)&L4DEFAULTWINDOW, + "BTINTERVAL ",5,SWITCHVAL,(size_t)&BTINTERVAL, + "PASSWORD ", 8, PWDCMD, 0, + + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, // Apppl 32 is internal Terminal + "*** LINKED ",10,LINKCMD,0, + "CQ ",2,CQCMD,0, + "CONNECT ",1,CMDC00,0, + "NC ",2,CMDC00,0, + "BYE ",1,BYECMD,0, + "QUIT ",1,BYECMD,0, + "INFO ",1,CMDI00,0, + "HELP ",1,HELPCMD,0, + "VERSION ",1,CMDV00,0, + "NODES ",1,CMDN00,0, + "LINKS ",1,CMDL00,0, + "LISTEN ",3,LISTENCMD,0, + "L4T1 ",2,CMDT00,0, + "PORTS ",1,CMDP00,0, + "PACLEN ",3,CMDPAC,0, + "IDLETIME ",4,CMDIDLE,0, + "ROUTES ",1,CMDR00,0, + "STATS ",1,CMDSTATS,0, + "USERS ",1,CMDS00,0, + "UNPROTO ",2,UNPROTOCMD,0, + "? ",1,CMDQUERY,0, + "DUMP ",4,DUMPCMD,0, + "MHU ",3,MHCMD,0, // UTC Times + "MHL ",3,MHCMD,0, // Local Times + "MHV ",3,MHCMD,0, + "MHEARD ",1,MHCMD,0, + "APRS ",2,APRSCMD,0, + "ATTACH ",1,ATTACHCMD,0, + "RADIO ",3,RADIOCMD,0, + "AXRESOLVER ",3,AXRESOLVER,0, + "AXMHEARD ",3,AXMHEARD,0, + "TELSTATUS ",3,SHOWTELNET,0, + "NRR ",1,NRRCMD,0, + "PING ",2,PING,0, + "AGWSTATUS ",3,SHOWAGW,0, + "ARP ",3,SHOWARP,0, + "NAT ",3,SHOWNAT,0, + "IPROUTE ",3,SHOWIPROUTE,0, + "..FLMSG ",7,FLMSG,0 +}; + +CMDX * CMD = NULL; + +int NUMBEROFCOMMANDS = sizeof(COMMANDS)/sizeof(CMDX); + +char * ReplyPointer; // Pointer into reply buffer + +int DecodeNodeName(char * NodeName, char * ptr) +{ + // NodeName is TABLE ENTRY WITH AX25 CALL AND ALIAS + + // Copyies 20 byte 20 DECODED NAME IN FORM ALIAS:CALL to ptr + // Returns significant length of string + + int len; + char Normcall[10]; + char * alias = &NodeName[7]; + int n = 6; + char * start = ptr; + + memset(ptr, ' ', 20); + + len = ConvFromAX25(NodeName, Normcall); + + if (*(alias) > ' ') // Does alias start with a null or a space ? + { + while (*(alias) > ' ' && n--) + { + *ptr++ = *alias++; + } + *ptr++ = ':'; + } + + memcpy(ptr, Normcall, len); + ptr += len; + + return (int)(ptr - start); +} + +char * SetupNodeHeader(struct DATAMESSAGE * Buffer) +{ + char Header[20]; + int len; + + char * ptr = &Buffer->L2DATA[0]; + + len = DecodeNodeName(MYCALLWITHALIAS, Header); + + memcpy (ptr, Header, len); + ptr += len; + + (*ptr++) = HEADERCHAR; + (*ptr++) = ' '; + + return ptr; +} + +VOID SendCommandReply(TRANSPORTENTRY * Session, struct DATAMESSAGE * Buffer, int Len) +{ + if (Len == (4 + sizeof(void *))) // Null Packet + { + ReleaseBuffer((UINT *)Buffer); + return; + } + + Buffer->LENGTH = Len; + + C_Q_ADD(&Session->L4TX_Q, (UINT *)Buffer); + + PostDataAvailable(Session); +} + + +VOID CommandHandler(TRANSPORTENTRY * Session, struct DATAMESSAGE * Buffer) +{ + // ignore frames with single NULL (Keepalive) + + if (Buffer->LENGTH == sizeof(void *) + 5 && Buffer->L2DATA[0] == 0) + { + ReleaseBuffer(Buffer); + return; + } + + if (Buffer->LENGTH > 100) + { +// Debugprintf("BPQ32 command too long %s", Buffer->L2DATA); + ReleaseBuffer(Buffer); + return; + } + +InnerLoop: + + InnerCommandHandler(Session, Buffer); + +// See if any more commands in buffer + + if (Session->PARTCMDBUFFER) + { + char * ptr1, * ptr2; + int len; + + Buffer = Session->PARTCMDBUFFER; + + // Check that message has a CR, if not save buffer and exit + + len = Buffer->LENGTH - (4 + sizeof(void *)); + ptr1 = &Buffer->L2DATA[0]; + + ptr2 = memchr(ptr1, 13, len); + + if (ptr2 == NULL) + return; + + Session->PARTCMDBUFFER = NULL; + + goto InnerLoop; + } +} + + +VOID InnerCommandHandler(TRANSPORTENTRY * Session, struct DATAMESSAGE * Buffer) +{ + char * ptr1, * ptr2, *ptr3; + int len, oldlen, newlen, rest, n; + struct DATAMESSAGE * OldBuffer; + struct DATAMESSAGE * SaveBuffer; + char c; + + // If a partial command is stored, append this data to it. + + if (Session->PARTCMDBUFFER) + { + len = Buffer->LENGTH - (sizeof(void *) + 4); + ptr1 = &Buffer->L2DATA[0]; + + OldBuffer = Session->PARTCMDBUFFER; // Old Data + + if (OldBuffer == Buffer) + { + // something has gone horribly wrong + + Session->PARTCMDBUFFER = NULL; + return; + } + + oldlen = OldBuffer->LENGTH; + + newlen = len + oldlen; + + if (newlen > 200) + { + // Command far too long - ignore previous + + OldBuffer->LENGTH = oldlen = sizeof(void *) + 4; + } + + OldBuffer->LENGTH += len; + memcpy(&OldBuffer->L2DATA[oldlen - (sizeof(void *) + 4)], Buffer->L2DATA, len); + + ReleaseBuffer((UINT *)Buffer); + + Buffer = OldBuffer; + + Session->PARTCMDBUFFER = NULL; + } + + // Check that message has a CR, if not save buffer and exit + + len = Buffer->LENGTH - (sizeof(void *) + 4); + ptr1 = &Buffer->L2DATA[0]; + + // Check for sending YAPP to Node + + if (len == 2 && ptr1[0] == 5 && ptr1[1] == 1) + { + ptr1[0] = 0x15; // NAK + + ptr1[1] = sprintf(&ptr1[2], "Node doesn't support YAPP Transfers"); + + Buffer->LENGTH += ptr1[1]; + + C_Q_ADD(&Session->L4TX_Q, (UINT *)Buffer); + PostDataAvailable(Session); + return; + } + + + ptr2 = memchr(ptr1, ';', len); + + if (ptr2 == 0) + { + ptr2 = memchr(ptr1, 13, len); + + if (ptr2 == 0) + { + // No newline + + Session->PARTCMDBUFFER = Buffer; + return; + } + } + + ptr2++; + + rest = len - (int)(ptr2 - ptr1); + + if (rest) + { + // there are chars beyond the cr in the buffer + + // see if LF after CR + + if ((*ptr2) == 10) // LF + { + ptr2++; + rest--; + } + + if (rest) // May only have had LF + { + // Get a new buffer, and copy extra data to it. + + SaveBuffer = (struct DATAMESSAGE *)GetBuff(); + + if (SaveBuffer) //`Just ignore if no buffers + { + SaveBuffer->LENGTH = rest + MSGHDDRLEN + 1; + SaveBuffer->PID = 0xf0; + memcpy(&SaveBuffer->L2DATA[0], ptr2, rest); + Session->PARTCMDBUFFER = SaveBuffer; + } + } + } + + // GET PACLEN FOR THIS CONNECTION + + CMDPACLEN = Session->SESSPACLEN; + + if (CMDPACLEN == 0) + CMDPACLEN = PACLEN; // Use default if no Session PACLEN + + // If sesion is in UNPROTO Mode, send message as a UI message + + if (Session->UNPROTO) + { + DIGIMESSAGE Msg; + int Port = Session->UNPROTO; + int Len = Buffer->LENGTH - (MSGHDDRLEN -1); // Need PID + + // First check for UNPROTO exit - ctrl/z or /ex + + if (Buffer->L2DATA[0] == 26 || (Len == 6 && _memicmp(&Buffer->L2DATA[0], "/ex", 3) == 0)) // CTRL/Z or /ex + { + REPLYBUFFER = Buffer; + + Session->UNPROTO = 0; + memset(Session->UADDRESS, 0, 64); + + // SET UP HEADER + + Buffer->PID = 0xf0; + ptr1 = SetupNodeHeader(Buffer); + memcpy(ptr1, OKMSG, 3); + ptr1 += 3; + SendCommandReply(Session, Buffer, (int)(ptr1 - (char *)Buffer)); + + return; + } + + memset(&Msg, 0, sizeof(Msg)); + + Msg.PORT = Port; + Msg.CTL = 3; // UI + memcpy(Msg.DEST, Session->UADDRESS, 7); + memcpy(Msg.ORIGIN, Session->L4USER, 7); + memcpy(Msg.DIGIS, &Session->UADDRESS[7], Session->UAddrLen - 7); + memcpy(&Msg.PID, &Buffer->PID, Len); + + Send_AX_Datagram(&Msg, Len, Port); // Len is Payload - CTL, PID and Data + +// SendUIModeFrame(Session, (PMESSAGE)Buffer, Session->UNPROTO); + + ReleaseBuffer((UINT *)Buffer); // Not using buffer for reply + + return; + } + + memset(COMMANDBUFFER, 32, 80); // Clear to spaces + + ptr1 = &Buffer->L2DATA[0]; + ptr2 = &COMMANDBUFFER[0]; + ptr3 = &OrigCmdBuffer[0]; + + memset(OrigCmdBuffer, 0, 80); + n = 80; + + while (n--) + { + c = *(ptr1++) & 0x7f; // Mask paritu + + if (c == 13 || c == ';') + break; // CR + + *(ptr3++) = c; // Original Case + + c = toupper(c); + *(ptr2++) = c; + } + + + // USE INPUT MESSAGE _BUFFER FOR REPLY + + REPLYBUFFER = Buffer; + + // SET UP HEADER + + Buffer->PID = 0xf0; + ptr1 = SetupNodeHeader(Buffer); + + ReplyPointer = ptr1; + + ALIASINVOKED = 0; // Clear "Invoked by APPL ALIAS flag" + + DoTheCommand(Session); // We also call DotheCommand when we need to reprocess - eg for alias handling +} + +VOID DoTheCommand(TRANSPORTENTRY * Session) +{ + struct DATAMESSAGE * Buffer = REPLYBUFFER; + char * ptr1, * ptr2; + int n; + + ptr1 = &COMMANDBUFFER[0]; // + + n = 10; + + while ((*ptr1 == ' ' || *ptr1 == 0) && n--) + ptr1++; // STRIP LEADING SPACES and nulls (from keepalive) + + if (n == -1) + { + // Null command + + ReleaseBuffer((UINT *)Buffer); + return; + } + + ptr2 = ptr1; // Save + + + CMD = &COMMANDS[0]; + n = 0; + + for (n = 0; n < NUMBEROFCOMMANDS; n++) + { + int CL = CMD->CMDLEN; + + ptr1 = ptr2; + + CMDPTR = CMD; + + if (n == APPL1) // First APPL command + { + APPLMASK = 1; // FOR APPLICATION ATTACH REQUESTS + ALIASPTR = &CMDALIAS[0][0]; + } + + // ptr1 is input command + + if (memcmp(CMD->String, ptr1, CL) == 0) + { + // Found match so far - check rest + + char * ptr2 = &CMD->String[CL]; + + ptr1 += CL; + + if (*(ptr1) != ' ') + { + while(*(ptr1) == *ptr2 && *(ptr1) != ' ') + { + ptr1++; + ptr2++; + } + } + + if (*(ptr1) == ' ') + { + Session->BADCOMMANDS = 0; // RESET ERROR COUNT + + // SEE IF SYSOP COMMAND, AND IF SO IF PASSWORD HAS BEEN ENTERED + + if (n < PASSCMD) + { + //NEEDS PASSWORD FOR SYSOP COMMANDS + + if (Session->PASSWORD != 0xFFFF) + { + ptr1 = ReplyPointer; + + memcpy(ptr1, PASSWORDMSG, LPASSMSG); + ptr1 += LPASSMSG; + + SendCommandReply(Session, Buffer, (int)(ptr1 - (char *)Buffer)); + return; + } + } +// VALNODESFLAG = 0; // NOT VALID NODES COMMAND + + ptr1++; // Skip space + + CMD->CMDPROC(Session, ReplyPointer, ptr1, CMD); + return; + } + } + + APPLMASK <<= 1; + ALIASPTR += ALIASLEN; + + CMD++; + + } + Session->BADCOMMANDS++; + + if (Session->BADCOMMANDS > 6) // TOO MANY ERRORS + { + ReleaseBuffer((UINT *)Buffer); + Session->STAYFLAG = 0; + CLOSECURRENTSESSION(Session); + return; + } + + ptr1 = ReplyPointer; + + memcpy(ptr1, CMDERRMSG, CMDERRLEN); + ptr1 += CMDERRLEN; + + SendCommandReply(Session, Buffer, (int)(ptr1 - (char *)Buffer)); +} + + +VOID StatsTimer() +{ + struct PORTCONTROL * PORT = PORTTABLE; + int sum; + + while(PORT) + { + sum = PORT->SENDING / 11; + PORT->AVSENDING = sum; + + sum = (PORT->SENDING + PORT->ACTIVE) /11; + PORT->AVACTIVE = sum; + + PORT->SENDING = 0; + PORT->ACTIVE = 0; + + PORT = PORT->PORTPOINTER; + } +} + + + +extern struct AXIPPORTINFO * Portlist[]; + +#define TCPConnected 4 + + +VOID AXRESOLVER(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // DISPLAY AXIP Resolver info + + int Port = 0, index =0; + char * ptr, *Context; + struct PORTCONTROL * PORT = NULL; + struct AXIPPORTINFO * AXPORT; + char Normcall[11]; + char Flags[10]; + struct arp_table_entry * arp; + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr) + Port = atoi(ptr); + + if (Port) + PORT = GetPortTableEntryFromPortNum(Port); + + if (PORT == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + AXPORT = Portlist[Port]; + + if (AXPORT == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not an AXIP port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "AXIP Resolver info for Port %d\r", Port); + + while (index < AXPORT->arp_table_len) + { + arp = &AXPORT->arp_table[index]; + + if (arp->ResolveFlag && arp->error != 0) + { + // resolver error - Display Error Code + sprintf(AXPORT->hostaddr, "Error %d", arp->error); + } + else + { + if (arp->IPv6) + Format_Addr((unsigned char *)&arp->destaddr6.sin6_addr, AXPORT->hostaddr, TRUE); + else + Format_Addr((unsigned char *)&arp->destaddr.sin_addr, AXPORT->hostaddr, FALSE); + } + + ConvFromAX25(arp->callsign, Normcall); + + Flags[0] = 0; + + if (arp->BCFlag) + strcat(Flags, "B "); + + if (arp->TCPState == TCPConnected) + strcat(Flags, "C "); + + if (arp->AutoAdded) + strcat(Flags, "A"); + + if (arp->port == arp->SourcePort) + Bufferptr = Cmdprintf(Session, Bufferptr,"%.10s = %.64s %d = %-.42s %s\r", + Normcall, + arp->hostname, + arp->port, + AXPORT->hostaddr, + Flags); + + else + Bufferptr = Cmdprintf(Session, Bufferptr,"%.10s = %.64s %d<%d = %-.42s %s\r", + Normcall, + arp->hostname, + arp->port, + arp->SourcePort, + AXPORT->hostaddr, + Flags); + + index++; + } + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID AXMHEARD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // DISPLAY AXIP Mheard info + + int Port = 0, index = 0; + char * ptr, *Context; + struct PORTCONTROL * PORT = NULL; + struct AXIPPORTINFO * AXPORT; + int n = MHENTRIES; + char Normcall[11]; + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr) + Port = atoi(ptr); + + if (Port) + PORT = GetPortTableEntryFromPortNum(Port); + + if (PORT == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + AXPORT = Portlist[Port]; + + if (AXPORT == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not an AXIP port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "AXIP Mheard for Port %d\r", Port); + + while (index < MaxMHEntries) + { + if (AXPORT->MHTable[index].proto != 0) + { + char Addr[80]; + + Format_Addr((unsigned char *)&AXPORT->MHTable[index].ipaddr6, Addr, AXPORT->MHTable[index].IPv6); + + Normcall[ConvFromAX25(AXPORT->MHTable[index].callsign, Normcall)] = 0; + + Bufferptr = Cmdprintf(Session, Bufferptr, "%-10s%-15s %c %-6d %-25s%c\r", Normcall, + Addr, + AXPORT->MHTable[index].proto, + AXPORT->MHTable[index].port, + asctime(gmtime( &AXPORT->MHTable[index].LastHeard )), + (AXPORT->MHTable[index].Keepalive == 0) ? ' ' : 'K'); + + Bufferptr[-3] = ' '; // Clear CR returned by asctime + } + + index++; + } + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +#pragma pack() + +extern struct TNCINFO * TNCInfo[41]; + +extern char WL2KCall[10]; +extern char WL2KLoc[7]; + +BOOL GetWL2KSYSOPInfo(char * Call, char * _REPLYBUFFER); +BOOL UpdateWL2KSYSOPInfo(char * Call, char * SQL); + +VOID WL2KSYSOP(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + char _REPLYBUFFER[1000] = ""; + + char LastUpdated[100]; + char Name[100] = ""; + char Addr1[100] = ""; + char Addr2[100] = ""; + char City[100] = ""; + char State[100] = ""; + char Country[100] = ""; + char PostCode[100] = ""; + char Email[100] = ""; + char Website[100] = ""; + char Phone[100] = ""; + char Data[100] = ""; + char LOC[100] = ""; + BOOL Exists = TRUE; + time_t LastUpdateSecs = 0; + char * ptr1, * ptr2; + + SOCKET sock; + + int Len; + char Message[2048]; + + if (WL2KCall[0] < 33) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Winlink reporting is not configured\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + + if (GetWL2KSYSOPInfo(WL2KCall, _REPLYBUFFER) == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Failed to connect to WL2K Database\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (strstr(_REPLYBUFFER, "\"ErrorMessage\":")) + Exists = FALSE; + + GetJSONValue(_REPLYBUFFER, "\"SysopName\":", Name); + GetJSONValue(_REPLYBUFFER, "\"StreetAddress1\":", Addr1); + GetJSONValue(_REPLYBUFFER, "\"StreetAddress2\":", Addr2); + GetJSONValue(_REPLYBUFFER, "\"City\":", City); + GetJSONValue(_REPLYBUFFER, "\"State\":", State); + GetJSONValue(_REPLYBUFFER, "\"Country\":", Country); + GetJSONValue(_REPLYBUFFER, "\"PostalCode\":", PostCode); + GetJSONValue(_REPLYBUFFER, "\"Email\":", Email); + GetJSONValue(_REPLYBUFFER, "\"Website\":", Website); + GetJSONValue(_REPLYBUFFER, "\"Phones\":", Phone); + GetJSONValue(_REPLYBUFFER, "\"Comments\":", Data); + GetJSONValue(_REPLYBUFFER, "\"GridSquare\":", LOC); + GetJSONValue(_REPLYBUFFER, "\"Timestamp\":", LastUpdated); + + ptr1 = strchr(LastUpdated, '('); + + if (ptr1) + { + ptr2 = strchr(++ptr1, ')'); + + if (ptr2) + { + *(ptr2 - 3) = 0; // remove millisecs + LastUpdateSecs = atoi(ptr1); + + FormatTime3(LastUpdated, LastUpdateSecs); + } + } + + if (_memicmp(CmdTail, "SET ", 4) == 0) + { + if (Exists) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Record already exists in WL2K Database\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // Set New Values. Any other params are values to set, separated by | + +// ptr1 = strtok_s(&CmdTail[4], ",", &Context); + +// if (ptr1 == NULL) +// goto DoReplace; + +// strcpy(Name, ptr1); + +//DoReplace: + + Len = sprintf(Message, + "\"Callsign\":\"%s\"," + "\"GridSquare\":\"%s\"," + "\"SysopName\":\"%s\"," + "\"StreetAddress1\":\"%s\"," + "\"StreetAddress2\":\"%s\"," + "\"City\":\"%s\"," + "\"State\":\"%s\"," + "\"Country\":\"%s\"," + "\"PostalCode\":\"%s\"," + "\"Email\":\"%s\"," + "\"Phones\":\"%s\"," + "\"Website\":\"%s\"," + "\"Comments\":\"%s\",", + + WL2KCall, WL2KLoc, Name, Addr1, Addr2, City, State, Country, PostCode, Email, Phone, Website, Data); + + Debugprintf("Sending %s", Message); + + sock = OpenWL2KHTTPSock(); + + if (sock) + SendHTTPRequest(sock, "api.winlink.org", 80, + "/sysop/add", Message, Len, NULL); + + closesocket(sock); + + Bufferptr = Cmdprintf(Session, Bufferptr, "Database Updated\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (Exists) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "\rWL2K SYSOP Info for %s\r", WL2KCall); + Bufferptr = Cmdprintf(Session, Bufferptr, "Grid Square: %s\r", LOC); + Bufferptr = Cmdprintf(Session, Bufferptr, "Name: %s\r", Name); + Bufferptr = Cmdprintf(Session, Bufferptr, "Addr Line 1: %s\r", Addr1); + Bufferptr = Cmdprintf(Session, Bufferptr, "Addr Line 2: %s\r", Addr2); + Bufferptr = Cmdprintf(Session, Bufferptr, "City: %s\r", City); + Bufferptr = Cmdprintf(Session, Bufferptr, "State: %s\r", State); + Bufferptr = Cmdprintf(Session, Bufferptr, "Country: %s\r", Country); + Bufferptr = Cmdprintf(Session, Bufferptr, "PostCode: %s\r", PostCode); + Bufferptr = Cmdprintf(Session, Bufferptr, "Email Address: %s\r", Email); + Bufferptr = Cmdprintf(Session, Bufferptr, "Website: %s\r", Website); + Bufferptr = Cmdprintf(Session, Bufferptr, "Phone: %s\r", Phone); + Bufferptr = Cmdprintf(Session, Bufferptr, "Additional Data: %s\r", Data); + Bufferptr = Cmdprintf(Session, Bufferptr, "Last Updated: %s\r", LastUpdated); + } + else + Bufferptr = Cmdprintf(Session, Bufferptr, "No SYSOP record for %s\r", WL2KCall); + + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; +} + +VOID CloseKISSPort(struct PORTCONTROL * PortVector); +int OpenConnection(struct PORTCONTROL * PortVector); + +VOID STOPCMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + char _REPLYBUFFER[1000] = ""; + char * ptr, * Context; + + int portno; + + struct TNCINFO * TNC; + struct TCPINFO * TCP; + struct PORTCONTROL * PORT = PORTTABLE; + int n = NUMBEROFPORTS; + + // Get port number + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr) + { + portno = atoi (ptr); + + if (portno) + { + while (n--) + { + if (PORT->PORTNUMBER == portno) + { + TNC = TNCInfo[portno]; + + if (!TNC || !TNC->TCPInfo) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not a Telnet Port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + TCP = TNC->TCPInfo; + + TCP->CMS = 0; + TCP->CMSOK = FALSE; +#ifndef LINBPQ + CheckMenuItem(TCP->hActionMenu, 3, MF_BYPOSITION | TCP->CMS<<3); + SetWindowText(TCP->hCMSWnd, "CMS Off"); +#endif + Bufferptr = Cmdprintf(Session, Bufferptr, "CMS Server Disabled\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + PORT = PORT->PORTPOINTER; + } + } + } + + // Bad port + + strcpy(Bufferptr, BADPORT); + Bufferptr += (int)strlen(BADPORT); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; +} + + +VOID STARTCMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + char _REPLYBUFFER[1000] = ""; + char * ptr, * Context; + + int portno; + + struct TNCINFO * TNC; + struct TCPINFO * TCP; + struct PORTCONTROL * PORT = PORTTABLE; + int n = NUMBEROFPORTS; + + // Get port number + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr) + { + portno = atoi (ptr); + + if (portno) + { + while (n--) + { + if (PORT->PORTNUMBER == portno) + { + TNC = TNCInfo[portno]; + + if (!TNC || !TNC->TCPInfo) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not a Telnet Port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + TCP = TNC->TCPInfo; + TCP->CMS = 1; +#ifndef LINBPQ + CheckMenuItem(TCP->hActionMenu, 3, MF_BYPOSITION | TCP->CMS<<3); +#endif + CheckCMS(TNC); + + Bufferptr = Cmdprintf(Session, Bufferptr, "CMS Server Enabled\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + PORT = PORT->PORTPOINTER; + } + } + } + + // Bad port + + strcpy(Bufferptr, BADPORT); + Bufferptr += (int)strlen(BADPORT); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; +} + + +VOID STOPPORT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + char _REPLYBUFFER[1000] = ""; + char * ptr, * Context; + + int portno; + struct PORTCONTROL * PORT = PORTTABLE; + int n = NUMBEROFPORTS; + + // Get port number + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr) + { + portno = atoi (ptr); + + if (portno) + { + while (n--) + { + if (PORT->PORTNUMBER == portno) + { + struct KISSINFO * KISS; + + if (PORT->PORTSTOPCODE) + { + // Port has Close Routine + + PORT->PortStopped = TRUE; + + if (PORT->PORTSTOPCODE(PORT)) + Bufferptr = Cmdprintf(Session, Bufferptr, "Port Closed\r"); + else + Bufferptr = Cmdprintf(Session, Bufferptr, "Port Close Failed\r"); + + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + if (PORT->PORTTYPE != 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not a KISS Port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (PORT->PORTIPADDR.s_addr || PORT->KISSSLAVE) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not a serial port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + KISS = (struct KISSINFO *) PORT; + + if (KISS->FIRSTPORT != KISS) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not first port of a Multidrop Set\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + CloseKISSPort(PORT); + PORT->PortStopped = TRUE; + Bufferptr = Cmdprintf(Session, Bufferptr, "Port Closed\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + + return; + } + PORT = PORT->PORTPOINTER; + } + } + } + + // Bad port + + strcpy(Bufferptr, BADPORT); + Bufferptr += (int)strlen(BADPORT); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; +} + + +VOID STARTPORT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + char _REPLYBUFFER[1000] = ""; + char * ptr, * Context; + + int portno; + struct PORTCONTROL * PORT = PORTTABLE; + int n = NUMBEROFPORTS; + + // Get port number + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr) + { + portno = atoi (ptr); + + if (portno) + { + while (n--) + { + if (PORT->PORTNUMBER == portno) + { + struct KISSINFO * KISS; + + if (PORT->PORTSTARTCODE) + { + // Port has Open Routine + + PORT->PortStopped = FALSE; + + if (PORT->PORTSTARTCODE(PORT)) + Bufferptr = Cmdprintf(Session, Bufferptr, "Port Opened\r"); + else + Bufferptr = Cmdprintf(Session, Bufferptr, "Port Open Failed\r"); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + + if (PORT->PORTTYPE != 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not a KISS Port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (PORT->PORTIPADDR.s_addr || PORT->KISSSLAVE) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not a serial port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + KISS = (struct KISSINFO *) PORT; + + if (KISS->FIRSTPORT != KISS) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not first port of a Multidrop Set\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (OpenConnection(PORT), TRUE) + Bufferptr = Cmdprintf(Session, Bufferptr, "Port Opened\r"); + else + Bufferptr = Cmdprintf(Session, Bufferptr, "Port Open Failed\r"); + + PORT->PortStopped = FALSE; + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + PORT = PORT->PORTPOINTER; + } + } + } + + // Bad port + + strcpy(Bufferptr, BADPORT); + Bufferptr += (int)strlen(BADPORT); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; +} + + + +#define FEND 0xC0 +int ASYSEND(struct PORTCONTROL * PortVector, char * buffer, int count); + + +VOID KISSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + char _REPLYBUFFER[1000] = ""; + char * ptr, * Context; + + int portno = 0; + int cmd = 0, val = 0; + struct PORTCONTROL * PORT = PORTTABLE; + int n = NUMBEROFPORTS; + + // Send KISS Command to TNC + + // Get port number + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr) + { + portno = atoi (ptr); + ptr = strtok_s(NULL, " ", &Context); + + if (ptr) + { + cmd = atoi (ptr); + ptr = strtok_s(NULL, " ", &Context); + + if (ptr) + val = atoi (ptr); + } + } + + if (portno == 0 || cmd == 0) + { + strcpy(Bufferptr, BADMSG); + Bufferptr += (int)strlen(BADMSG); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + while (n--) + { + if (PORT->PORTNUMBER == portno) + { + struct KISSINFO * KISS; + UCHAR ENCBUFF[16]; + unsigned char * ptr = ENCBUFF; + + if (PORT->PORTTYPE != 0 && PORT->PORTTYPE != 22) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not a KISS Port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + KISS = (struct KISSINFO *) PORT; + + if (KISS->FIRSTPORT != KISS) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not first port of a Multidrop Set\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // Send Command + + *(ptr++) = FEND; + *(ptr++) = KISS->OURCTRL | cmd; + *(ptr++) = (UCHAR)val; + *(ptr++) = FEND; + + PORT = (struct PORTCONTROL *)KISS->FIRSTPORT; // ALL FRAMES GO ON SAME Q + + PORT->Session = Session; + PORT->LastKISSCmdTime = time(NULL); + + ASYSEND(PORT, ENCBUFF, 4); + + Bufferptr = Cmdprintf(Session, Bufferptr, "Command Sent\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + PORT = PORT->PORTPOINTER; + } + + + // Bad port + + strcpy(Bufferptr, BADPORT); + Bufferptr += (int)strlen(BADPORT); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; +} + + +VOID FINDBUFFS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + FindLostBuffers(); + +#ifdef WIN32 + Bufferptr = Cmdprintf(Session, Bufferptr, "Lost buffer info dumped to Debugview\r"); +#else + Bufferptr = Cmdprintf(Session, Bufferptr, "Lost buffer info dumped to syslog\r"); +#endif + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID FLMSG(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD) +{ + // Telnet Connection from FLMSG + CLOSECURRENTSESSION(Session); // Kills any crosslink, plus local link + ReleaseBuffer((UINT *)REPLYBUFFER); +} + +BOOL CheckExcludeList(UCHAR * Call) +{ + UCHAR * ptr1 = ExcludeList; + + while (*ptr1) + { + if (memcmp(Call, ptr1, 6) == 0) + return FALSE; + + ptr1 += 7; + } + + return TRUE; +} + + +void ListExcludedCalls(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + + UCHAR * ptr = ExcludeList; + char Normcall[10] = ""; + UCHAR AXCall[8] = ""; + + if (*CmdTail == ' ') + goto DISPLIST; + + if (*CmdTail == 'Z') + { + // CLEAR LIST + + memset(ExcludeList, 0, 70); + goto DISPLIST; + } + + ConvToAX25(CmdTail, AXCall); + + if (strlen(ExcludeList) < 70) + strcat(ExcludeList, AXCall); + +DISPLIST: + + while (*ptr) + { + Normcall[ConvFromAX25(ptr, Normcall)] = 0; + Bufferptr = Cmdprintf(Session, Bufferptr, "%s ", Normcall); + ptr += 7; + } + + *(Bufferptr++) = '\r'; + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +BOOL isSYSOP(TRANSPORTENTRY * Session, char * Bufferptr) +{ + if (Session->PASSWORD != 0xFFFF) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%s", PASSWORDMSG); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + + return FALSE; + } + + return TRUE; +} + +VOID HELPCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + char * MsgBytes; + struct stat STAT; + char * ptr1, * ptr, * ptr2; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/%s", BPQDirectory, "NodeHelp.txt"); + + if (stat(MsgFile, &STAT) == -1) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Help file not found\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Help file not found\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + MsgBytes = malloc(FileSize+1); + + fread(MsgBytes, 1, FileSize, hFile); + + fclose(hFile); + + MsgBytes[FileSize] = 0; + + ptr1 = MsgBytes; + + // Replace LF or CRLF with CR + + // First remove cr from crlf + + while(ptr2 = strstr(ptr1, "\r\n")) + { + memmove(ptr2, ptr2 + 1, strlen(ptr2)); + } + + // Now replace lf with cr + + ptr1 = MsgBytes; + + while (*ptr1) + { + if (*ptr1 == '\n') + *(ptr1) = '\r'; + + ptr1++; + } + + ptr = ptr1 = MsgBytes; + + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + // Read and send a line at a time, converting any line endings into CR + + while (*ptr1) + { + if (*ptr1 == '\r') + { + *(ptr1++) = 0; + + Bufferptr = Cmdprintf(Session, Bufferptr, "%s\r", ptr); + + ptr = ptr1; + } + else + ptr1++; + } + + free(MsgBytes); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + + + + + + + + + + diff --git a/Cmd.c b/Cmd.c index 2157939..6a8c581 100644 --- a/Cmd.c +++ b/Cmd.c @@ -3769,7 +3769,7 @@ VOID ATTACHCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX TRANSPORTENTRY * NewSess; struct _EXTPORTDATA * EXTPORT; - struct TNCINFO * TNC; + struct TNCINFO * TNC = 0; int Port = 0, sess = 0; char * ptr, *Context; diff --git a/CommonCode-HPLaptop.c b/CommonCode-HPLaptop.c new file mode 100644 index 0000000..2abf722 --- /dev/null +++ b/CommonCode-HPLaptop.c @@ -0,0 +1,4742 @@ +/* +Copyright 2001-2022 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 +*/ + + + +// General C Routines common to bpq32 and linbpq. Mainly moved from BPQ32.c + +#pragma data_seg("_BPQDATA") + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#include + +#pragma data_seg("_BPQDATA") + +#include "CHeaders.h" +#include "tncinfo.h" +#include "configstructs.h" + +extern struct CONFIGTABLE xxcfg; + +#define LIBCONFIG_STATIC +#include "libconfig.h" + +#ifndef LINBPQ + +//#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. + +#include "commctrl.h" +#include "Commdlg.h" + +#endif + +struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + +extern int ReportTimer; + +Dll VOID APIENTRY Send_AX(UCHAR * Block, DWORD Len, UCHAR Port); +TRANSPORTENTRY * SetupSessionFromHost(PBPQVECSTRUC HOST, UINT ApplMask); +int Check_Timer(); +VOID SENDUIMESSAGE(struct DATAMESSAGE * Msg); +DllExport struct PORTCONTROL * APIENTRY GetPortTableEntryFromSlot(int portslot); +VOID APIENTRY md5 (char *arg, unsigned char * checksum); +VOID COMSetDTR(HANDLE fd); +VOID COMClearDTR(HANDLE fd); +VOID COMSetRTS(HANDLE fd); +VOID COMClearRTS(HANDLE fd); + +VOID WriteMiniDump(); +void printStack(void); +char * FormatMH(PMHSTRUC MH, char Format); +void WriteConnectLog(char * fromCall, char * toCall, UCHAR * Mode); +extern BOOL LogAllConnects; + +extern VOID * ENDBUFFERPOOL; + +// Read/Write length field in a buffer header + +// Needed for Big/LittleEndian and ARM5 (unaligned operation problem) portability + + +VOID PutLengthinBuffer(PDATAMESSAGE buff, USHORT datalen) +{ + if (datalen <= sizeof(void *) + 4) + datalen = sizeof(void *) + 4; // Protect + + memcpy(&buff->LENGTH, &datalen, 2); +} + +int GetLengthfromBuffer(PDATAMESSAGE buff) +{ + USHORT Length; + + memcpy(&Length, &buff->LENGTH, 2); + return Length; +} + +BOOL CheckQHeadder(UINT * Q) +{ +#ifdef WIN32 + UINT Test; + + __try + { + Test = *Q; + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + Debugprintf("Invalid Q Header %p", Q); + printStack(); + return FALSE; + } +#endif + return TRUE; +} + +// Get buffer from Queue + + +VOID * _Q_REM(VOID **PQ, char * File, int Line) +{ + void ** Q; + void ** first; + VOID * next; + PMESSAGE Test; + + // PQ may not be word aligned, so copy as bytes (for ARM5) + + Q = PQ; + + if (Semaphore.Flag == 0) + Debugprintf("Q_REM called without semaphore from %s Line %d", File, Line); + + if (CheckQHeadder((UINT *) Q) == 0) + return(0); + + first = Q[0]; + + if (first == 0) + return (0); // Empty + + next = first[0]; // Address of next buffer + + Q[0] = next; + + // Make sure guard zone is zeros + + Test = (PMESSAGE)first; + + if (Test->GuardZone != 0) + { + Debugprintf("Q_REM %p GUARD ZONE CORRUPT %x Called from %s Line %d", first, Test->GuardZone, File, Line); + printStack(); + } + + return first; +} + +// Non=pool version (for IPGateway) + +VOID * _Q_REM_NP(VOID *PQ, char * File, int Line) +{ + void ** Q; + void ** first; + void * next; + + // PQ may not be word aligned, so copy as bytes (for ARM5) + + Q = PQ; + + if (CheckQHeadder((UINT *)Q) == 0) + return(0); + + first = Q[0]; + + if (first == 0) return (0); // Empty + + next = first[0]; // Address of next buffer + + Q[0] = next; + + return first; +} + +// Return Buffer to Free Queue + +extern VOID * BUFFERPOOL; +extern void ** Bufferlist[1000]; +void printStack(void); + +void _CheckGuardZone(char * File, int Line) +{ + int n = 0, i, offset = 0; + PMESSAGE Test; + UINT CodeDump[8]; + unsigned char * ptr; + + n = NUMBEROFBUFFERS; + + while (n--) + { + Test = (PMESSAGE)Bufferlist[n]; + + if (Test && Test->GuardZone) + { + Debugprintf("CheckGuardZone %p GUARD ZONE CORRUPT %d Called from %s Line %d", Test, Test->Process, File, Line); + + offset = 0; + ptr = (unsigned char *)Test; + + while (offset < 400) + { + memcpy(CodeDump, &ptr[offset], 32); + + for (i = 0; i < 8; i++) + CodeDump[i] = htonl(CodeDump[i]); + + Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ", + &ptr[offset], CodeDump[0], CodeDump[1], CodeDump[2], CodeDump[3], CodeDump[4], CodeDump[5], CodeDump[6], CodeDump[7]); + + offset += 32; + } + WriteMiniDump(); +#ifdef MDIKERNEL + CloseAllNeeded = 1; +#endif + } + + } +} + +UINT _ReleaseBuffer(VOID *pBUFF, char * File, int Line) +{ + void ** pointer, ** BUFF = pBUFF; + int n = 0; + void ** debug; + PMESSAGE Test; + UINT CodeDump[16]; + int i; + unsigned int rev; + + if (Semaphore.Flag == 0) + Debugprintf("ReleaseBuffer called without semaphore from %s Line %d", File, Line); + + // Make sure address is within pool + + if ((uintptr_t)BUFF < (uintptr_t)BUFFERPOOL || (uintptr_t)BUFF > (uintptr_t)ENDBUFFERPOOL) + { + // Not pointing to a buffer . debug points to the buffer that this is chained from + + // Dump first chunk and source tag + + memcpy(CodeDump, BUFF, 64); + + Debugprintf("Releasebuffer Buffer not in pool from %s Line %d, ptr %p prev %d", File, Line, BUFF, 0); + + for (i = 0; i < 16; i++) + { + rev = (CodeDump[i] & 0xff) << 24; + rev |= (CodeDump[i] & 0xff00) << 8; + rev |= (CodeDump[i] & 0xff0000) >> 8; + rev |= (CodeDump[i] & 0xff000000) >> 24; + + CodeDump[i] = rev; + } + + Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ", + Bufferlist[n], CodeDump[0], CodeDump[1], CodeDump[2], CodeDump[3], CodeDump[4], CodeDump[5], CodeDump[6], CodeDump[7]); + + Debugprintf(" %08x %08x %08x %08x %08x %08x %08x %08x", + CodeDump[8], CodeDump[9], CodeDump[10], CodeDump[11], CodeDump[12], CodeDump[13], CodeDump[14], CodeDump[15]); + + + return 0; + } + + Test = (PMESSAGE)pBUFF; + + if (Test->GuardZone != 0) + { + Debugprintf("_ReleaseBuffer %p GUARD ZONE CORRUPT %x Called from %s Line %d", pBUFF, Test->GuardZone, File, Line); + } + + while (n <= NUMBEROFBUFFERS) + { + if (BUFF == Bufferlist[n++]) + goto BOK1; + } + + Debugprintf("ReleaseBuffer %X not in Pool called from %s Line %d", BUFF, File, Line); + printStack(); + + return 0; + +BOK1: + + n = 0; + + // validate free Queue + + pointer = FREE_Q; + debug = &FREE_Q; + + while (pointer) + { + // Validate pointer to make sure it is in pool - it may be a duff address if Q is corrupt + + Test = (PMESSAGE)pointer; + + if (Test->GuardZone || (uintptr_t)pointer < (uintptr_t)BUFFERPOOL || (uintptr_t)pointer > (uintptr_t)ENDBUFFERPOOL) + { + // Not pointing to a buffer . debug points to the buffer that this is chained from + + // Dump first chunk and source tag + + memcpy(CodeDump, debug, 64); + + Debugprintf("Releasebuffer Pool Corruption n = %d, ptr %p prev %p", n, pointer, debug); + + for (i = 0; i < 16; i++) + { + rev = (CodeDump[i] & 0xff) << 24; + rev |= (CodeDump[i] & 0xff00) << 8; + rev |= (CodeDump[i] & 0xff0000) >> 8; + rev |= (CodeDump[i] & 0xff000000) >> 24; + + CodeDump[i] = rev; + } + + Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ", + Bufferlist[n], CodeDump[0], CodeDump[1], CodeDump[2], CodeDump[3], CodeDump[4], CodeDump[5], CodeDump[6], CodeDump[7]); + + Debugprintf(" %08x %08x %08x %08x %08x %08x %08x %08x", + CodeDump[8], CodeDump[9], CodeDump[10], CodeDump[11], CodeDump[12], CodeDump[13], CodeDump[14], CodeDump[15]); + + if (debug[400]) + Debugprintf(" %s", &debug[400]); + + } + + // See if already on free Queue + + if (pointer == BUFF) + { + Debugprintf("Trying to free buffer %p when already on FREE_Q called from %s Line %d", BUFF, File, Line); +// WriteMiniDump(); + return 0; + } + +// if (pointer[0] && pointer == pointer[0]) +// { +// Debugprintf("Buffer chained to itself"); +// return 0; +// } + + debug = pointer; + pointer = pointer[0]; + n++; + + if (n > 1000) + { + Debugprintf("Loop searching free chain - pointer = %p %p", debug, pointer); + return 0; + } + } + + pointer = FREE_Q; + + *BUFF = pointer; + + FREE_Q = BUFF; + + QCOUNT++; + + return 0; +} + +int _C_Q_ADD(VOID *PQ, VOID *PBUFF, char * File, int Line) +{ + void ** Q; + void ** BUFF = PBUFF; + void ** next; + PMESSAGE Test; + + + int n = 0; + +// PQ may not be word aligned, so copy as bytes (for ARM5) + + Q = PQ; + + if (Semaphore.Flag == 0) + Debugprintf("C_Q_ADD called without semaphore from %s Line %d", File, Line); + + if (CheckQHeadder((UINT *)Q) == 0) // Make sure Q header is readable + return(0); + + // Make sure guard zone is zeros + + Test = (PMESSAGE)PBUFF; + + if (Test->GuardZone != 0) + { + Debugprintf("C_Q_ADD %p GUARD ZONE CORRUPT %x Called from %s Line %d", PBUFF, Test->GuardZone, File, Line); + } + + Test = (PMESSAGE)Q; + + + + // Make sure address is within pool + + while (n <= NUMBEROFBUFFERS) + { + if (BUFF == Bufferlist[n++]) + goto BOK2; + } + + Debugprintf("C_Q_ADD %X not in Pool called from %s Line %d", BUFF, File, Line); + printStack(); + + return 0; + +BOK2: + + BUFF[0] = 0; // Clear chain in new buffer + + if (Q[0] == 0) // Empty + { + Q[0]=BUFF; // New one on front + return(0); + } + + next = Q[0]; + + while (next[0] != 0) + { + next = next[0]; // Chain to end of queue + } + next[0] = BUFF; // New one on end + + return(0); +} + +// Non-pool version + +int C_Q_ADD_NP(VOID *PQ, VOID *PBUFF) +{ + void ** Q; + void ** BUFF = PBUFF; + void ** next; + int n = 0; + +// PQ may not be word aligned, so copy as bytes (for ARM5) + + Q = PQ; + + if (CheckQHeadder((UINT *)Q) == 0) // Make sure Q header is readable + return(0); + + BUFF[0]=0; // Clear chain in new buffer + + if (Q[0] == 0) // Empty + { + Q[0]=BUFF; // New one on front +// memcpy(PQ, &BUFF, 4); + return 0; + } + next = Q[0]; + + while (next[0] != 0) + next=next[0]; // Chain to end of queue + + next[0] = BUFF; // New one on end + + return(0); +} + + +int C_Q_COUNT(VOID *PQ) +{ + void ** Q; + int count = 0; + +// PQ may not be word aligned, so copy as bytes (for ARM5) + + Q = PQ; + + if (CheckQHeadder((UINT *)Q) == 0) // Make sure Q header is readable + return(0); + + // SEE HOW MANY BUFFERS ATTACHED TO Q HEADER + + while (*Q) + { + count++; + if ((count + QCOUNT) > MAXBUFFS) + { + Debugprintf("C_Q_COUNT Detected corrupt Q %p len %d", PQ, count); + return count; + } + Q = *Q; + } + + return count; +} + +VOID * _GetBuff(char * File, int Line) +{ + UINT * Temp; + MESSAGE * Msg; + char * fptr = 0; + unsigned char * byteaddr; + + Temp = Q_REM(&FREE_Q); + +// FindLostBuffers(); + + if (Semaphore.Flag == 0) + Debugprintf("GetBuff called without semaphore from %s Line %d", File, Line); + + if (Temp) + { + QCOUNT--; + + if (QCOUNT < MINBUFFCOUNT) + MINBUFFCOUNT = QCOUNT; + + Msg = (MESSAGE *)Temp; + fptr = File + (int)strlen(File); + while (*fptr != '\\' && *fptr != '/') + fptr--; + fptr++; + + // Buffer Length is BUFFLEN, but buffers are allocated 512 + // So add file info in gap between + + byteaddr = (unsigned char *)Msg; + + + memset(&byteaddr[0], 0, 64); // simplify debugging lost buffers + memset(&byteaddr[400], 0, 64); // simplify debugging lost buffers + sprintf(&byteaddr[400], "%s %d", fptr, Line); + + Msg->Process = (short)GetCurrentProcessId(); + Msg->Linkptr = NULL; + } + else + Debugprintf("Warning - Getbuff returned NULL"); + + return Temp; +} + +void * zalloc(int len) +{ + // malloc and clear + + void * ptr; + + ptr=malloc(len); + + if (ptr) + memset(ptr, 0, len); + + return ptr; +} + +char * strlop(char * buf, char delim) +{ + // Terminate buf at delim, and return rest of string + + char * ptr; + + if (buf == NULL) return NULL; // Protect + + ptr = strchr(buf, delim); + + if (ptr == NULL) return NULL; + + *(ptr)++=0; + + return ptr; +} + +VOID DISPLAYCIRCUIT(TRANSPORTENTRY * L4, char * Buffer) +{ + UCHAR Type = L4->L4CIRCUITTYPE; + struct PORTCONTROL * PORT; + struct _LINKTABLE * LINK; + BPQVECSTRUC * VEC; + struct DEST_LIST * DEST; + + char Normcall[20] = ""; // Could be alias:call + char Normcall2[11] = ""; + char Alias[11] = ""; + + Buffer[0] = 0; + + switch (Type) + { + case PACTOR+UPLINK: + + PORT = L4->L4TARGET.PORT; + + ConvFromAX25(L4->L4USER, Normcall); + strlop(Normcall, ' '); + + if (PORT) + sprintf(Buffer, "%s %d/%d(%s)", "TNC Uplink Port", PORT->PORTNUMBER, L4->KAMSESSION, Normcall); + + return; + + + case PACTOR+DOWNLINK: + + PORT = L4->L4TARGET.PORT; + + if (PORT) + sprintf(Buffer, "%s %d/%d", "Attached to Port", PORT->PORTNUMBER, L4->KAMSESSION); + return; + + + case L2LINK+UPLINK: + + LINK = L4->L4TARGET.LINK; + + ConvFromAX25(L4->L4USER, Normcall); + strlop(Normcall, ' '); + + if (LINK &&LINK->LINKPORT) + sprintf(Buffer, "%s %d(%s)", "Uplink", LINK->LINKPORT->PORTNUMBER, Normcall); + + return; + + case L2LINK+DOWNLINK: + + LINK = L4->L4TARGET.LINK; + + if (LINK == NULL) + return; + + ConvFromAX25(LINK->OURCALL, Normcall); + strlop(Normcall, ' '); + + ConvFromAX25(LINK->LINKCALL, Normcall2); + strlop(Normcall2, ' '); + + sprintf(Buffer, "%s %d(%s %s)", "Downlink", LINK->LINKPORT->PORTNUMBER, Normcall, Normcall2); + return; + + case BPQHOST + UPLINK: + case BPQHOST + DOWNLINK: + + // if the call has a Level 4 address display ALIAS:CALL, else just Call + + if (FindDestination(L4->L4USER, &DEST)) + Normcall[DecodeNodeName(DEST->DEST_CALL, Normcall)] = 0; // null terminate + else + Normcall[ConvFromAX25(L4->L4USER, Normcall)] = 0; + + VEC = L4->L4TARGET.HOST; + sprintf(Buffer, "%s%02d(%s)", "Host", (int)(VEC - BPQHOSTVECTOR) + 1, Normcall); + return; + + case SESSION + DOWNLINK: + case SESSION + UPLINK: + + ConvFromAX25(L4->L4USER, Normcall); + strlop(Normcall, ' '); + + DEST = L4->L4TARGET.DEST; + + if (DEST == NULL) + return; + + ConvFromAX25(DEST->DEST_CALL, Normcall2); + strlop(Normcall2, ' '); + + memcpy(Alias, DEST->DEST_ALIAS, 6); + strlop(Alias, ' '); + + sprintf(Buffer, "Circuit(%s:%s %s)", Alias, Normcall2, Normcall); + + return; + } +} + +VOID CheckForDetach(struct TNCINFO * TNC, int Stream, struct STREAMINFO * STREAM, + VOID TidyCloseProc(), VOID ForcedCloseProc(), VOID CloseComplete()) +{ + void ** buffptr; + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] == 0) + { + // Node has disconnected - clear any connection + + if (STREAM->Disconnecting) + { + // Already detected the detach, and have started to close + + STREAM->DisconnectingTimeout--; + + if (STREAM->DisconnectingTimeout) + return; // Give it a bit longer + + // Close has timed out - force a disc, and clear + + ForcedCloseProc(TNC, Stream); // Send Tidy Disconnect + + goto NotConnected; + } + + // New Disconnect + + Debugprintf("New Disconnect Port %d Q %x", TNC->Port, STREAM->BPQtoPACTOR_Q); + + if (STREAM->Connected || STREAM->Connecting) + { + char logmsg[120]; + time_t Duration; + + // Need to do a tidy close + + STREAM->Connecting = FALSE; + STREAM->Disconnecting = TRUE; + STREAM->DisconnectingTimeout = 300; // 30 Secs + + if (Stream == 0) + SetWindowText(TNC->xIDC_TNCSTATE, "Disconnecting"); + + // Create a traffic record + + if (STREAM->Connected && STREAM->ConnectTime) + { + Duration = time(NULL) - STREAM->ConnectTime; + + if (Duration == 0) + Duration = 1; // Or will get divide by zero error + + sprintf(logmsg,"Port %2d %9s Bytes Sent %d BPS %d Bytes Received %d BPS %d Time %d Seconds", + TNC->Port, STREAM->RemoteCall, + STREAM->BytesTXed, (int)(STREAM->BytesTXed/Duration), + STREAM->BytesRXed, (int)(STREAM->BytesRXed/Duration), (int)Duration); + + Debugprintf(logmsg); + + STREAM->ConnectTime = 0; + } + + if (STREAM->BPQtoPACTOR_Q) // Still data to send? + return; // Will close when all acked + +// if (STREAM->FramesOutstanding && TNC->Hardware == H_UZ7HO) +// return; // Will close when all acked + + TidyCloseProc(TNC, Stream); // Send Tidy Disconnect + + return; + } + + // Not connected +NotConnected: + + STREAM->Disconnecting = FALSE; + STREAM->Attached = FALSE; + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; + + if (Stream == 0) + SetWindowText(TNC->xIDC_TNCSTATE, "Free"); + + STREAM->FramesQueued = 0; + STREAM->FramesOutstanding = 0; + + CloseComplete(TNC, Stream); + + while(STREAM->BPQtoPACTOR_Q) + { + buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q); + ReleaseBuffer(buffptr); + } + + while(STREAM->PACTORtoBPQ_Q) + { + buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q); + ReleaseBuffer(buffptr); + } + } +} + +char * CheckAppl(struct TNCINFO * TNC, char * Appl) +{ + APPLCALLS * APPL; + BPQVECSTRUC * PORTVEC; + int Allocated = 0, Available = 0; + int App, Stream; + struct TNCINFO * APPLTNC; + +// Debugprintf("Checking if %s is running", Appl); + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + + if (_memicmp(APPL->APPLCMD, Appl, 12) == 0) + { + int _APPLMASK = 1 << App; + + // If App has an alias, assume it is running , unless a CMS alias - then check CMS + + if (APPL->APPLHASALIAS) + { + if (_memicmp(APPL->APPLCMD, "RELAY ", 6) == 0) + return APPL->APPLCALL_TEXT; // Assume people using RELAY know what they are doing + + if (APPL->APPLPORT && (_memicmp(APPL->APPLCMD, "RMS ", 4) == 0)) + { + APPLTNC = TNCInfo[APPL->APPLPORT]; + { + if (APPLTNC) + { + if (APPLTNC->TCPInfo && !APPLTNC->TCPInfo->CMSOK && !APPLTNC->TCPInfo->FallbacktoRelay) + return NULL; + } + } + } + return APPL->APPLCALL_TEXT; + } + + // See if App is running + + PORTVEC = &BPQHOSTVECTOR[0]; + + for (Stream = 0; Stream < 64; Stream++) + { + if (PORTVEC->HOSTAPPLMASK & _APPLMASK) + { + Allocated++; + + if (PORTVEC->HOSTSESSION == 0 && (PORTVEC->HOSTFLAGS & 3) == 0) + { + // Free and no outstanding report + + return APPL->APPLCALL_TEXT; // Running + } + } + PORTVEC++; + } + } + } + + return NULL; // Not Running +} + +VOID SetApplPorts() +{ + // If any appl has an alias, get port number + + struct APPLCONFIG * App; + APPLCALLS * APPL; + + char C[80]; + char Port[80]; + char Call[80]; + + int i, n; + + App = &xxcfg.C_APPL[0]; + + for (i=0; i < NumberofAppls; i++) + { + APPL=&APPLCALLTABLE[i]; + + if (APPL->APPLHASALIAS) + { + n = sscanf(App->CommandAlias, "%s %s %s", &C[0], &Port[0], &Call[0]); + if (n == 3) + APPL->APPLPORT = atoi(Port); + } + App++; + } +} + + +extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + + +char Modenames[19][10] = {"WINMOR", "SCS", "KAM", "AEA", "HAL", "TELNET", "TRK", + "V4", "UZ7HO", "MPSK", "FLDIGI", "UIARQ", "ARDOP", "VARA", + "SERIAL", "KISSHF", "WINRPR", "HSMODEM", "FREEDATA"}; + +BOOL ProcessIncommingConnect(struct TNCINFO * TNC, char * Call, int Stream, BOOL SENDCTEXT) +{ + return ProcessIncommingConnectEx(TNC, Call, Stream, SENDCTEXT, FALSE); +} + +BOOL ProcessIncommingConnectEx(struct TNCINFO * TNC, char * Call, int Stream, BOOL SENDCTEXT, BOOL AllowTR) +{ + TRANSPORTENTRY * Session; + int Index = 0; + PMSGWITHLEN buffptr; + int Totallen = 0; + UCHAR * ptr; + struct PORTCONTROL * PORT; + + PORT = &TNC->PortRecord->PORTCONTROL; + + // Stop Scanner + + if (Stream == 0 || TNC->Hardware == H_UZ7HO) + { + char Msg[80]; + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command(-1, Msg); + + UpdateMH(TNC, Call, '+', 'I'); + } + + Session = L4TABLE; + + // Find a free Circuit Entry + + while (Index < MAXCIRCUITS) + { + if (Session->L4USER[0] == 0) + break; + + Session++; + Index++; + } + + if (Index == MAXCIRCUITS) + return FALSE; // Tables Full + + memset(Session, 0, sizeof(TRANSPORTENTRY)); + + memcpy(TNC->Streams[Stream].RemoteCall, Call, 9); // Save Text Callsign + + if (AllowTR) + ConvToAX25Ex(Call, Session->L4USER); // Allow -T and -R SSID's for MPS + else + ConvToAX25(Call, Session->L4USER); + ConvToAX25(MYNODECALL, Session->L4MYCALL); + Session->CIRCUITINDEX = Index; + Session->CIRCUITID = NEXTID; + NEXTID++; + if (NEXTID == 0) NEXTID++; // Keep non-zero + + TNC->PortRecord->ATTACHEDSESSIONS[Stream] = Session; + TNC->Streams[Stream].Attached = TRUE; + + Session->L4TARGET.EXTPORT = TNC->PortRecord; + + Session->L4CIRCUITTYPE = UPLINK+PACTOR; + Session->L4WINDOW = L4DEFAULTWINDOW; + Session->L4STATE = 5; + Session->SESSIONT1 = L4T1; + Session->SESSPACLEN = TNC->PortRecord->PORTCONTROL.PORTPACLEN; + Session->KAMSESSION = Stream; + + TNC->Streams[Stream].Connected = TRUE; // Subsequent data to data channel + + if (LogAllConnects) + { + if (TNC->TargetCall[0]) + WriteConnectLog(Call, TNC->TargetCall, Modenames[TNC->Hardware - 1]); + else + WriteConnectLog(Call, MYNODECALL, Modenames[TNC->Hardware - 1]); + } + + if (SENDCTEXT == 0) + return TRUE; + + // if Port CTEXT defined, use it + + if (PORT->CTEXT) + { + Totallen = strlen(PORT->CTEXT); + ptr = PORT->CTEXT; + } + else if (HFCTEXTLEN > 0) + { + Totallen = HFCTEXTLEN; + ptr = HFCTEXT; + } + else + return TRUE; + + while (Totallen > 0) + { + int sendLen = TNC->PortRecord->ATTACHEDSESSIONS[Stream]->SESSPACLEN; + + if (sendLen == 0) + sendLen = 80; + + if (Totallen < sendLen) + sendLen = Totallen; + + buffptr = (PMSGWITHLEN)GetBuff(); + if (buffptr == 0) return TRUE; // No buffers + + buffptr->Len = sendLen; + memcpy(&buffptr->Data[0], ptr, sendLen); + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + Totallen -= sendLen; + ptr += sendLen; + } + return TRUE; +} + +char * Config; +static char * ptr1, * ptr2; + +BOOL ReadConfigFile(int Port, int ProcLine()) +{ + char buf[256],errbuf[256]; + + if (TNCInfo[Port]) // If restarting, free old config + free(TNCInfo[Port]); + + TNCInfo[Port] = NULL; + + Config = PortConfig[Port]; + + if (Config) + { + // Using config from bpq32.cfg + + if (strlen(Config) == 0) + { + // Empty Config File - OK for most types + + struct TNCINFO * TNC = TNCInfo[Port] = zalloc(sizeof(struct TNCINFO)); + + TNC->InitScript = malloc(2); + TNC->InitScript[0] = 0; + + return TRUE; + } + + ptr1 = Config; + + ptr2 = strchr(ptr1, 13); + while(ptr2) + { + memcpy(buf, ptr1, ptr2 - ptr1 + 1); + buf[ptr2 - ptr1 + 1] = 0; + ptr1 = ptr2 + 2; + ptr2 = strchr(ptr1, 13); + + strcpy(errbuf,buf); // save in case of error + + if (!ProcLine(buf, Port)) + { + WritetoConsoleLocal("\n"); + WritetoConsoleLocal("Bad config record "); + WritetoConsoleLocal(errbuf); + } + } + } + else + { + sprintf(buf," ** Error - No Configuration info in bpq32.cfg"); + WritetoConsoleLocal(buf); + } + + return (TRUE); +} +int GetLine(char * buf) +{ +loop: + + if (ptr2 == NULL) + return 0; + + memcpy(buf, ptr1, ptr2 - ptr1 + 2); + buf[ptr2 - ptr1 + 2] = 0; + ptr1 = ptr2 + 2; + ptr2 = strchr(ptr1, 13); + + if (buf[0] < 0x20) goto loop; + if (buf[0] == '#') goto loop; + if (buf[0] == ';') goto loop; + + if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0; + if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0; + buf[strlen(buf)] = 13; + + return 1; +} +VOID DigiToMultiplePorts(struct PORTCONTROL * PORTVEC, PMESSAGE Msg) +{ + USHORT Mask=PORTVEC->DIGIMASK; + int i; + + for (i=1; i<=NUMBEROFPORTS; i++) + { + if (Mask & 1) + { + // Block includes the Msg Header (7/11 bytes), Len Does not! + + Msg->PORT = i; + Send_AX((UCHAR *)&Msg, Msg->LENGTH - MSGHDDRLEN, i); + Mask>>=1; + } + } +} + +int CompareAlias(struct DEST_LIST ** a, struct DEST_LIST ** b) +{ + return memcmp(a[0]->DEST_ALIAS, b[0]->DEST_ALIAS, 6); + /* strcmp functions works exactly as expected from comparison function */ +} + + +int CompareNode(struct DEST_LIST ** a, struct DEST_LIST ** b) +{ + return memcmp(a[0]->DEST_CALL, b[0]->DEST_CALL, 7); +} + +DllExport int APIENTRY CountFramesQueuedOnStream(int Stream) +{ + BPQVECSTRUC * PORTVEC = &BPQHOSTVECTOR[Stream-1]; // API counts from 1 + TRANSPORTENTRY * L4 = PORTVEC->HOSTSESSION; + + int Count = 0; + + if (L4) + { + if (L4->L4CROSSLINK) // CONNECTED? + Count = CountFramesQueuedOnSession(L4->L4CROSSLINK); + else + Count = CountFramesQueuedOnSession(L4); + } + return Count; +} + +DllExport int APIENTRY ChangeSessionCallsign(int Stream, unsigned char * AXCall) +{ + // Equivalent to "*** linked to" command + + memcpy(BPQHOSTVECTOR[Stream-1].HOSTSESSION->L4USER, AXCall, 7); + return (0); +} + +DllExport int APIENTRY ChangeSessionPaclen(int Stream, int Paclen) +{ + BPQHOSTVECTOR[Stream-1].HOSTSESSION->SESSPACLEN = Paclen; + return (0); +} + +DllExport int APIENTRY ChangeSessionIdletime(int Stream, int idletime) +{ + if (BPQHOSTVECTOR[Stream-1].HOSTSESSION) + BPQHOSTVECTOR[Stream-1].HOSTSESSION->L4LIMIT = idletime; + return (0); +} + +DllExport int APIENTRY Get_APPLMASK(int Stream) +{ + return BPQHOSTVECTOR[Stream-1].HOSTAPPLMASK; +} +DllExport int APIENTRY GetStreamPID(int Stream) +{ + return BPQHOSTVECTOR[Stream-1].STREAMOWNER; +} + +DllExport int APIENTRY GetApplFlags(int Stream) +{ + return BPQHOSTVECTOR[Stream-1].HOSTAPPLFLAGS; +} + +DllExport int APIENTRY GetApplNum(int Stream) +{ + return BPQHOSTVECTOR[Stream-1].HOSTAPPLNUM; +} + +DllExport int APIENTRY GetApplMask(int Stream) +{ + return BPQHOSTVECTOR[Stream-1].HOSTAPPLMASK; +} + +DllExport BOOL APIENTRY GetAllocationState(int Stream) +{ + return BPQHOSTVECTOR[Stream-1].HOSTFLAGS & 0x80; +} + +VOID Send_AX_Datagram(PDIGIMESSAGE Block, DWORD Len, UCHAR Port); + +extern int InitDone; +extern int SemHeldByAPI; +extern char pgm[256]; // Uninitialised so per process +extern int BPQHOSTAPI(); + + +VOID POSTSTATECHANGE(BPQVECSTRUC * SESS) +{ + // Post a message if requested +#ifndef LINBPQ + if (SESS->HOSTHANDLE) + PostMessage(SESS->HOSTHANDLE, BPQMsg, SESS->HOSTSTREAM, 4); +#endif + return; +} + + +DllExport int APIENTRY SessionControl(int stream, int command, int Mask) +{ + BPQVECSTRUC * SESS; + TRANSPORTENTRY * L4; + + stream--; // API uses 1 - 64 + + if (stream < 0 || stream > 63) + return (0); + + SESS = &BPQHOSTVECTOR[stream]; + + // Send Session Control command (BPQHOST function 6) + //; CL=0 CONNECT USING APPL MASK IN DL + //; CL=1, CONNECT. CL=2 - DISCONNECT. CL=3 RETURN TO NODE + + if (command > 1) + { + // Disconnect + + if (SESS->HOSTSESSION == 0) + { + SESS->HOSTFLAGS |= 1; // State Change + POSTSTATECHANGE(SESS); + return 0; // NOT CONNECTED + } + + if (command == 3) + SESS->HOSTFLAGS |= 0x20; // Set Stay + + SESS->HOSTFLAGS |= 0x40; // SET 'DISC REQ' FLAG + + return 0; + } + + // 0 or 1 - connect + + if (SESS->HOSTSESSION) // ALREADY CONNECTED + { + SESS->HOSTFLAGS |= 1; // State Change + POSTSTATECHANGE(SESS); + return 0; + } + + // SET UP A SESSION FOR THE CONSOLE + + SESS->HOSTFLAGS |= 0x80; // SET ALLOCATED BIT + + if (command == 1) // Zero is mask supplied by caller + Mask = SESS->HOSTAPPLMASK; // SO WE GET CORRECT CALLSIGN + + L4 = SetupSessionFromHost(SESS, Mask); + + if (L4 == 0) // tables Full + { + SESS->HOSTFLAGS |= 3; // State Change + POSTSTATECHANGE(SESS); + return 0; + } + + SESS->HOSTSESSION = L4; + L4->L4CIRCUITTYPE = BPQHOST | UPLINK; + L4->Secure_Session = AuthorisedProgram; // Secure Host Session + + SESS->HOSTFLAGS |= 1; // State Change + POSTSTATECHANGE(SESS); + return 0; // ALREADY CONNECTED +} + +int FindFreeStreamEx(int GetSem); + +int FindFreeStreamNoSem() +{ + return FindFreeStreamEx(0); +} + +DllExport int APIENTRY FindFreeStream() +{ + return FindFreeStreamEx(1); +} + +int FindFreeStreamEx(int GetSem) +{ + int stream, n; + BPQVECSTRUC * PORTVEC; + +// Returns number of first unused BPQHOST stream. If none available, +// returns 255. See API function 13. + + // if init has not yet been run, wait. + + while (InitDone == 0) + { + Debugprintf("Waiting for init to complete"); + Sleep(1000); + } + + if (InitDone == -1) // Init failed + exit(0); + + if (GetSem) + GetSemaphore(&Semaphore, 9); + + stream = 0; + n = 64; + + while (n--) + { + PORTVEC = &BPQHOSTVECTOR[stream++]; + if ((PORTVEC->HOSTFLAGS & 0x80) == 0) + { + PORTVEC->STREAMOWNER=GetCurrentProcessId(); + PORTVEC->HOSTFLAGS = 128; // SET ALLOCATED BIT, clear others + memcpy(&PORTVEC->PgmName[0], pgm, 31); + if (GetSem) + FreeSemaphore(&Semaphore); + return stream; + } + } + + if (GetSem) + FreeSemaphore(&Semaphore); + + return 255; +} + +DllExport int APIENTRY AllocateStream(int stream) +{ +// Allocate stream. If stream is already allocated, return nonzero. +// Otherwise allocate stream, and return zero. + + BPQVECSTRUC * PORTVEC = &BPQHOSTVECTOR[stream -1]; // API counts from 1 + + if ((PORTVEC->HOSTFLAGS & 0x80) == 0) + { + PORTVEC->STREAMOWNER=GetCurrentProcessId(); + PORTVEC->HOSTFLAGS = 128; // SET ALLOCATED BIT, clear others + memcpy(&PORTVEC->PgmName[0], pgm, 31); + FreeSemaphore(&Semaphore); + return 0; + } + + return 1; // Already allocated +} + + +DllExport int APIENTRY DeallocateStream(int stream) +{ + BPQVECSTRUC * PORTVEC; + UINT * monbuff; + BOOL GotSem = Semaphore.Flag; + +// Release stream. + + stream--; + + if (stream < 0 || stream > 63) + return (0); + + PORTVEC=&BPQHOSTVECTOR[stream]; + + PORTVEC->STREAMOWNER=0; + PORTVEC->PgmName[0] = 0; + PORTVEC->HOSTAPPLFLAGS=0; + PORTVEC->HOSTAPPLMASK=0; + PORTVEC->HOSTHANDLE=0; + + // Clear Trace Queue + + if (PORTVEC->HOSTSESSION) + SessionControl(stream + 1, 2, 0); + + if (GotSem == 0) + GetSemaphore(&Semaphore, 0); + + while (PORTVEC->HOSTTRACEQ) + { + monbuff = Q_REM((void *)&PORTVEC->HOSTTRACEQ); + ReleaseBuffer(monbuff); + } + + if (GotSem == 0) + FreeSemaphore(&Semaphore); + + PORTVEC->HOSTFLAGS &= 0x60; // Clear Allocated. Must leave any DISC Pending bits + + return(0); +} +DllExport int APIENTRY SessionState(int stream, int * state, int * change) +{ + // Get current Session State. Any state changed is ACK'ed + // automatically. See BPQHOST functions 4 and 5. + + BPQVECSTRUC * HOST = &BPQHOSTVECTOR[stream -1]; // API counts from 1 + + Check_Timer(); // In case Appl doesnt call it often ehough + + GetSemaphore(&Semaphore, 20); + + // CX = 0 if stream disconnected or CX = 1 if stream connected + // DX = 0 if no change of state since last read, or DX = 1 if + // the connected/disconnected state has changed since + // last read (ie. delta-stream status). + + // HOSTFLAGS = Bit 80 = Allocated + // Bit 40 = Disc Request + // Bit 20 = Stay Flag + // Bit 02 and 01 State Change Bits + + if ((HOST->HOSTFLAGS & 3) == 0) + // No Chaange + *change = 0; + else + *change = 1; + + if (HOST->HOSTSESSION) // LOCAL SESSION + // Connected + *state = 1; + else + *state = 0; + + HOST->HOSTFLAGS &= 0xFC; // Clear Change Bitd + + FreeSemaphore(&Semaphore); + return 0; +} + +DllExport int APIENTRY SessionStateNoAck(int stream, int * state) +{ + // Get current Session State. Dont ACK any change + // See BPQHOST function 4 + + BPQVECSTRUC * HOST = &BPQHOSTVECTOR[stream -1]; // API counts from 1 + + Check_Timer(); // In case Appl doesnt call it often ehough + + if (HOST->HOSTSESSION) // LOCAL SESSION + // Connected + *state = 1; + else + *state = 0; + + return 0; +} + +DllExport int APIENTRY SendMsg(int stream, char * msg, int len) +{ + // Send message to stream (BPQHOST Function 2) + + BPQVECSTRUC * SESS; + TRANSPORTENTRY * L4; + TRANSPORTENTRY * Partner; + PDATAMESSAGE MSG; + + Check_Timer(); + + if (len > 256) + return 0; // IGNORE + + if (stream == 0) + { + // Send UNPROTO - SEND FRAME TO ALL RADIO PORTS + + // COPY DATA TO A BUFFER IN OUR SEGMENTS - SIMPLFIES THINGS LATER + + if (QCOUNT < 50) + return 0; // Dont want to run out + + GetSemaphore(&Semaphore, 10); + + if ((MSG = GetBuff()) == 0) + { + FreeSemaphore(&Semaphore); + return 0; + } + + MSG->PID = 0xF0; // Normal Data PID + + memcpy(&MSG->L2DATA[0], msg, len); + MSG->LENGTH = (len + MSGHDDRLEN + 1); + + SENDUIMESSAGE(MSG); + ReleaseBuffer(MSG); + FreeSemaphore(&Semaphore); + return 0; + } + + stream--; // API uses 1 - 64 + + if (stream < 0 || stream > 63) + return 0; + + SESS = &BPQHOSTVECTOR[stream]; + L4 = SESS->HOSTSESSION; + + if (L4 == 0) + return 0; + + GetSemaphore(&Semaphore, 22); + + SESS->HOSTFLAGS |= 0x80; // SET ALLOCATED BIT + + if (QCOUNT < 40) // PLENTY FREE? + { + FreeSemaphore(&Semaphore); + return 1; + } + + // Dont allow massive queues to form + + if (QCOUNT < 100) + { + int n = CountFramesQueuedOnStream(stream + 1); + + if (n > 100) + { + Debugprintf("Stream %d QCOUNT %d Q Len %d - discarding", stream, QCOUNT, n); + FreeSemaphore(&Semaphore); + return 1; + } + } + + if ((MSG = GetBuff()) == 0) + { + FreeSemaphore(&Semaphore); + return 1; + } + + MSG->PID = 0xF0; // Normal Data PID + + memcpy(&MSG->L2DATA[0], msg, len); + MSG->LENGTH = len + MSGHDDRLEN + 1; + + // IF CONNECTED, PASS MESSAGE TO TARGET CIRCUIT - FLOW CONTROL AND + // DELAYED DISC ONLY WORK ON ONE SIDE + + Partner = L4->L4CROSSLINK; + + L4->L4KILLTIMER = 0; // RESET SESSION TIMEOUT + + if (Partner && Partner->L4STATE > 4) // Partner and link up + { + // Connected + + Partner->L4KILLTIMER = 0; // RESET SESSION TIMEOUT + C_Q_ADD(&Partner->L4TX_Q, MSG); + PostDataAvailable(Partner); + } + else + C_Q_ADD(&L4->L4RX_Q, MSG); + + FreeSemaphore(&Semaphore); + return 0; +} +DllExport int APIENTRY SendRaw(int port, char * msg, int len) +{ + struct PORTCONTROL * PORT; + MESSAGE * MSG; + + Check_Timer(); + + // Send Raw (KISS mode) frame to port (BPQHOST function 10) + + if (len > (MAXDATA - (MSGHDDRLEN + 8))) + return 0; + + if (QCOUNT < 50) + return 1; + + // GET A BUFFER + + PORT = GetPortTableEntryFromSlot(port); + + if (PORT == 0) + return 0; + + GetSemaphore(&Semaphore, 24); + + MSG = GetBuff(); + + if (MSG == 0) + { + FreeSemaphore(&Semaphore); + return 1; + } + + memcpy(MSG->DEST, msg, len); + + MSG->LENGTH = len + MSGHDDRLEN; + + if (PORT->PROTOCOL == 10) // PACTOR/WINMOR Style + { + // Pactor Style. Probably will only be used for Tracker uneless we do APRS over V4 or WINMOR + + EXTPORTDATA * EXTPORT = (EXTPORTDATA *) PORT; + + C_Q_ADD(&EXTPORT->UI_Q, MSG); + + FreeSemaphore(&Semaphore); + return 0; + } + + MSG->PORT = PORT->PORTNUMBER; + + PUT_ON_PORT_Q(PORT, MSG); + + FreeSemaphore(&Semaphore); + return 0; +} + +DllExport time_t APIENTRY GetRaw(int stream, char * msg, int * len, int * count) +{ + time_t Stamp; + BPQVECSTRUC * SESS; + PMESSAGE MSG; + int Msglen; + + Check_Timer(); + + *len = 0; + *count = 0; + + stream--; // API uses 1 - 64 + + if (stream < 0 || stream > 63) + return 0; + + SESS = &BPQHOSTVECTOR[stream]; + + GetSemaphore(&Semaphore, 26); + + if (SESS->HOSTTRACEQ == 0) + { + FreeSemaphore(&Semaphore); + return 0; + } + + MSG = Q_REM((void *)&SESS->HOSTTRACEQ); + + Msglen = MSG->LENGTH; + + if (Msglen < 0 || Msglen > 350) + { + FreeSemaphore(&Semaphore); + return 0; + } + + Stamp = MSG->Timestamp; + + memcpy(msg, MSG, Msglen); + + *len = Msglen; + + ReleaseBuffer(MSG); + + *count = C_Q_COUNT(&SESS->HOSTTRACEQ); + FreeSemaphore(&Semaphore); + + return Stamp; +} + +DllExport int APIENTRY GetMsg(int stream, char * msg, int * len, int * count ) +{ +// Get message from stream. Returns length, and count of frames +// still waiting to be collected. (BPQHOST function 3) +// AH = 3 Receive frame into buffer at ES:DI, length of frame returned +// in CX. BX returns the number of outstanding frames still to +// be received (ie. after this one) or zero if no more frames +// (ie. this is last one). +// + + BPQVECSTRUC * SESS; + TRANSPORTENTRY * L4; + PDATAMESSAGE MSG; + int Msglen; + + Check_Timer(); + + *len = 0; + *count = 0; + + stream--; // API uses 1 - 64 + + if (stream < 0 || stream > 63) + return 0; + + + SESS = &BPQHOSTVECTOR[stream]; + L4 = SESS->HOSTSESSION; + + GetSemaphore(&Semaphore, 25); + + if (L4 == 0 || L4->L4TX_Q == 0) + { + FreeSemaphore(&Semaphore); + return 0; + } + + L4->L4KILLTIMER = 0; // RESET SESSION TIMEOUT + + if(L4->L4CROSSLINK) + L4->L4CROSSLINK->L4KILLTIMER = 0; + + MSG = Q_REM((void *)&L4->L4TX_Q); + + Msglen = MSG->LENGTH - (MSGHDDRLEN + 1); // Dont want PID + + if (Msglen < 0) + { + FreeSemaphore(&Semaphore); + return 0; + } + + if (Msglen > 256) + Msglen = 256; + + memcpy(msg, &MSG->L2DATA[0], Msglen); + + *len = Msglen; + + ReleaseBuffer(MSG); + + *count = C_Q_COUNT(&L4->L4TX_Q); + FreeSemaphore(&Semaphore); + + return 0; +} + + +DllExport int APIENTRY RXCount(int stream) +{ +// Returns count of packets waiting on stream +// (BPQHOST function 7 (part)). + + BPQVECSTRUC * SESS; + TRANSPORTENTRY * L4; + + Check_Timer(); + + stream--; // API uses 1 - 64 + + if (stream < 0 || stream > 63) + return 0; + + SESS = &BPQHOSTVECTOR[stream]; + L4 = SESS->HOSTSESSION; + + if (L4 == 0) + return 0; // NOT CONNECTED + + return C_Q_COUNT(&L4->L4TX_Q); +} + +DllExport int APIENTRY TXCount(int stream) +{ +// Returns number of packets on TX queue for stream +// (BPQHOST function 7 (part)). + + BPQVECSTRUC * SESS; + TRANSPORTENTRY * L4; + + Check_Timer(); + + stream--; // API uses 1 - 64 + + if (stream < 0 || stream > 63) + return 0; + + SESS = &BPQHOSTVECTOR[stream]; + L4 = SESS->HOSTSESSION; + + if (L4 == 0) + return 0; // NOT CONNECTED + + L4 = L4->L4CROSSLINK; + + if (L4 == 0) + return 0; // NOTHING ro Q on + + return (CountFramesQueuedOnSession(L4)); +} + +DllExport int APIENTRY MONCount(int stream) +{ +// Returns number of monitor frames available +// (BPQHOST function 7 (part)). + + BPQVECSTRUC * SESS; + + Check_Timer(); + + stream--; // API uses 1 - 64 + + if (stream < 0 || stream > 63) + return 0; + + SESS = &BPQHOSTVECTOR[stream]; + + return C_Q_COUNT(&SESS->HOSTTRACEQ); +} + + +DllExport int APIENTRY GetCallsign(int stream, char * callsign) +{ + // Returns call connected on stream (BPQHOST function 8 (part)). + + BPQVECSTRUC * SESS; + TRANSPORTENTRY * L4; + TRANSPORTENTRY * Partner; + UCHAR Call[11] = "SWITCH "; + UCHAR * AXCall = NULL; + Check_Timer(); + + stream--; // API uses 1 - 64 + + if (stream < 0 || stream > 63) + return 0; + + SESS = &BPQHOSTVECTOR[stream]; + L4 = SESS->HOSTSESSION; + + GetSemaphore(&Semaphore, 26); + + if (L4 == 0) + { + FreeSemaphore(&Semaphore); + return 0; + } + + Partner = L4->L4CROSSLINK; + + if (Partner) + { + // CONNECTED OUT - GET TARGET SESSION + + if (Partner->L4CIRCUITTYPE & BPQHOST) + { + AXCall = &Partner->L4USER[0]; + } + else if (Partner->L4CIRCUITTYPE & L2LINK) + { + struct _LINKTABLE * LINK = Partner->L4TARGET.LINK; + + if (LINK) + AXCall = LINK->LINKCALL; + + if (Partner->L4CIRCUITTYPE & UPLINK) + { + // IF UPLINK, SHOULD USE SESSION CALL, IN CASE *** LINKED HAS BEEN USED + + AXCall = &Partner->L4USER[0]; + } + } + else if (Partner->L4CIRCUITTYPE & PACTOR) + { + // PACTOR Type - Frames are queued on the Port Entry + + EXTPORTDATA * EXTPORT = Partner->L4TARGET.EXTPORT; + + if (EXTPORT) + AXCall = &EXTPORT->ATTACHEDSESSIONS[Partner->KAMSESSION]->L4USER[0]; + + } + else + { + // MUST BE NODE SESSION + + // ANOTHER NODE + + // IF THE HOST IS THE UPLINKING STATION, WE NEED THE TARGET CALL + + if (L4->L4CIRCUITTYPE & UPLINK) + { + struct DEST_LIST *DEST = Partner->L4TARGET.DEST; + + if (DEST) + AXCall = &DEST->DEST_CALL[0]; + } + else + AXCall = Partner->L4USER; + } + if (AXCall) + ConvFromAX25(AXCall, Call); + } + + memcpy(callsign, Call, 10); + + FreeSemaphore(&Semaphore); + return 0; +} + +DllExport int APIENTRY GetConnectionInfo(int stream, char * callsign, + int * port, int * sesstype, int * paclen, + int * maxframe, int * l4window) +{ + // Return the Secure Session Flag rather than not connected + + BPQVECSTRUC * SESS; + TRANSPORTENTRY * L4; + TRANSPORTENTRY * Partner; + UCHAR Call[11] = "SWITCH "; + UCHAR * AXCall; + Check_Timer(); + + stream--; // API uses 1 - 64 + + if (stream < 0 || stream > 63) + return 0; + + SESS = &BPQHOSTVECTOR[stream]; + L4 = SESS->HOSTSESSION; + + GetSemaphore(&Semaphore, 27); + + if (L4 == 0) + { + FreeSemaphore(&Semaphore); + return 0; + } + + Partner = L4->L4CROSSLINK; + + // Return the Secure Session Flag rather than not connected + + // AL = Radio port on which channel is connected (or zero) + // AH = SESSION TYPE BITS + // EBX = L2 paclen for the radio port + // ECX = L2 maxframe for the radio port + // EDX = L4 window size (if L4 circuit, or zero) or -1 if not connected + // ES:DI = CALLSIGN + + *port = 0; + *sesstype = 0; + *paclen = 0; + *maxframe = 0; + *l4window = 0; + if (L4->SESSPACLEN) + *paclen = L4->SESSPACLEN; + else + *paclen = 256; + + if (Partner) + { + // CONNECTED OUT - GET TARGET SESSION + + *l4window = Partner->L4WINDOW; + *sesstype = Partner->L4CIRCUITTYPE; + + if (Partner->L4CIRCUITTYPE & BPQHOST) + { + AXCall = &Partner->L4USER[0]; + } + else if (Partner->L4CIRCUITTYPE & L2LINK) + { + struct _LINKTABLE * LINK = Partner->L4TARGET.LINK; + + // EXTRACT PORT AND MAXFRAME + + *port = LINK->LINKPORT->PORTNUMBER; + *maxframe = LINK->LINKWINDOW; + *l4window = 0; + + AXCall = LINK->LINKCALL; + + if (Partner->L4CIRCUITTYPE & UPLINK) + { + // IF UPLINK, SHOULD USE SESSION CALL, IN CASE *** LINKED HAS BEEN USED + + AXCall = &Partner->L4USER[0]; + } + } + else if (Partner->L4CIRCUITTYPE & PACTOR) + { + // PACTOR Type - Frames are queued on the Port Entry + + EXTPORTDATA * EXTPORT = Partner->L4TARGET.EXTPORT; + + *port = EXTPORT->PORTCONTROL.PORTNUMBER; + AXCall = &EXTPORT->ATTACHEDSESSIONS[Partner->KAMSESSION]->L4USER[0]; + + } + else + { + // MUST BE NODE SESSION + + // ANOTHER NODE + + // IF THE HOST IS THE UPLINKING STATION, WE NEED THE TARGET CALL + + if (L4->L4CIRCUITTYPE & UPLINK) + { + struct DEST_LIST *DEST = Partner->L4TARGET.DEST; + + AXCall = &DEST->DEST_CALL[0]; + } + else + AXCall = Partner->L4USER; + } + ConvFromAX25(AXCall, Call); + } + + memcpy(callsign, Call, 10); + + FreeSemaphore(&Semaphore); + + if (Partner) + return Partner->Secure_Session; + + return 0; +} + + +DllExport int APIENTRY SetAppl(int stream, int flags, int mask) +{ +// Sets Application Flags and Mask for stream. (BPQHOST function 1) +// AH = 1 Set application mask to value in EDX (or even DX if 16 +// applications are ever to be supported). +// +// Set application flag(s) to value in CL (or CX). +// whether user gets connected/disconnected messages issued +// by the node etc. + + + BPQVECSTRUC * PORTVEC; + stream--; + + if (stream < 0 || stream > 63) + return (0); + + PORTVEC=&BPQHOSTVECTOR[stream]; + + PORTVEC->HOSTAPPLFLAGS = flags; + PORTVEC->HOSTAPPLMASK = mask; + + // If either is non-zero, set allocated and Process. This gets round problem with + // stations that don't call allocate stream + + if (flags || mask) + { + if ((PORTVEC->HOSTFLAGS & 128) == 0) // Not allocated + { + PORTVEC->STREAMOWNER=GetCurrentProcessId(); + memcpy(&PORTVEC->PgmName[0], pgm, 31); + PORTVEC->HOSTFLAGS = 128; // SET ALLOCATED BIT, clear others + } + } + + return (0); +} + +DllExport struct PORTCONTROL * APIENTRY GetPortTableEntry(int portslot) // Kept for Legacy apps +{ + struct PORTCONTROL * PORTVEC=PORTTABLE; + + if (portslot>NUMBEROFPORTS) + portslot=NUMBEROFPORTS; + + while (--portslot > 0) + PORTVEC=PORTVEC->PORTPOINTER; + + return PORTVEC; +} + +// Proc below renamed to avoid confusion with GetPortTableEntryFromPortNum + +DllExport struct PORTCONTROL * APIENTRY GetPortTableEntryFromSlot(int portslot) +{ + struct PORTCONTROL * PORTVEC=PORTTABLE; + + if (portslot>NUMBEROFPORTS) + portslot=NUMBEROFPORTS; + + while (--portslot > 0) + PORTVEC=PORTVEC->PORTPOINTER; + + return PORTVEC; +} + +struct PORTCONTROL * APIENTRY GetPortTableEntryFromPortNum(int portnum) +{ + struct PORTCONTROL * PORTVEC=PORTTABLE; + + do + { + if (PORTVEC->PORTNUMBER == portnum) + return PORTVEC; + + PORTVEC=PORTVEC->PORTPOINTER; + } + while (PORTVEC); + + return NULL; +} + +DllExport UCHAR * APIENTRY GetPortDescription(int portslot, char * Desc) +{ + struct PORTCONTROL * PORTVEC=PORTTABLE; + + if (portslot>NUMBEROFPORTS) + portslot=NUMBEROFPORTS; + + while (--portslot > 0) + PORTVEC=PORTVEC->PORTPOINTER; + + memcpy(Desc, PORTVEC->PORTDESCRIPTION, 30); + Desc[30]=0; + + return 0; +} + +// Standard serial port handling routines, used by lots of modules. + +int OpenCOMMPort(struct TNCINFO * conn, char * Port, int Speed, BOOL Quiet) +{ + if (conn->WEB_COMMSSTATE == NULL) + conn->WEB_COMMSSTATE = zalloc(100); + + if (Port == NULL) + return (FALSE); + + conn->hDevice = OpenCOMPort(Port, Speed, TRUE, TRUE, Quiet, 0); + + if (conn->hDevice == 0) + { + sprintf(conn->WEB_COMMSSTATE,"%s Open failed - Error %d", Port, GetLastError()); + if (conn->xIDC_COMMSSTATE) + SetWindowText(conn->xIDC_COMMSSTATE, conn->WEB_COMMSSTATE); + + return (FALSE); + } + + sprintf(conn->WEB_COMMSSTATE,"%s Open", Port); + + if (conn->xIDC_COMMSSTATE) + SetWindowText(conn->xIDC_COMMSSTATE, conn->WEB_COMMSSTATE); + + return TRUE; +} + + + +#ifdef WIN32 + +HANDLE OpenCOMPort(char * pPort, int speed, BOOL SetDTR, BOOL SetRTS, BOOL Quiet, int Stopbits) +{ + char szPort[80]; + BOOL fRetVal ; + COMMTIMEOUTS CommTimeOuts ; + int Err; + char buf[100]; + HANDLE fd; + DCB dcb; + + // if Port Name starts COM, convert to \\.\COM or ports above 10 wont work + + if (_memicmp(pPort, "COM", 3) == 0) + { + char * pp = (char *)pPort; + int p = atoi(&pp[3]); + sprintf( szPort, "\\\\.\\COM%d", p); + } + else + strcpy(szPort, pPort); + + // open COMM device + + fd = CreateFile( szPort, GENERIC_READ | GENERIC_WRITE, + 0, // exclusive access + NULL, // no security attrs + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + if (fd == (HANDLE) -1) + { + if (Quiet == 0) + { + Debugprintf("%s could not be opened %d", pPort, GetLastError()); + } + return (FALSE); + } + + Err = GetFileType(fd); + + // setup device buffers + + SetupComm(fd, 4096, 4096 ) ; + + // purge any information in the buffer + + PurgeComm(fd, PURGE_TXABORT | PURGE_RXABORT | + PURGE_TXCLEAR | PURGE_RXCLEAR ) ; + + // set up for overlapped I/O + + CommTimeOuts.ReadIntervalTimeout = 0xFFFFFFFF ; + CommTimeOuts.ReadTotalTimeoutMultiplier = 0 ; + CommTimeOuts.ReadTotalTimeoutConstant = 0 ; + CommTimeOuts.WriteTotalTimeoutMultiplier = 0 ; +// CommTimeOuts.WriteTotalTimeoutConstant = 0 ; + CommTimeOuts.WriteTotalTimeoutConstant = 500 ; + SetCommTimeouts(fd, &CommTimeOuts ) ; + + dcb.DCBlength = sizeof( DCB ) ; + + GetCommState(fd, &dcb ) ; + + dcb.BaudRate = speed; + dcb.ByteSize = 8; + dcb.Parity = 0; + dcb.StopBits = TWOSTOPBITS; + dcb.StopBits = Stopbits; + + // setup hardware flow control + + dcb.fOutxDsrFlow = 0; + dcb.fDtrControl = DTR_CONTROL_DISABLE ; + + dcb.fOutxCtsFlow = 0; + dcb.fRtsControl = RTS_CONTROL_DISABLE ; + + // setup software flow control + + dcb.fInX = dcb.fOutX = 0; + dcb.XonChar = 0; + dcb.XoffChar = 0; + dcb.XonLim = 100 ; + dcb.XoffLim = 100 ; + + // other various settings + + dcb.fBinary = TRUE ; + dcb.fParity = FALSE; + + fRetVal = SetCommState(fd, &dcb); + + if (fRetVal) + { + if (SetDTR) + EscapeCommFunction(fd, SETDTR); + else + EscapeCommFunction(fd, CLRDTR); + + if (SetRTS) + EscapeCommFunction(fd, SETRTS); + else + EscapeCommFunction(fd, CLRRTS); + } + else + { + sprintf(buf,"%s Setup Failed %d ", pPort, GetLastError()); + + WritetoConsoleLocal(buf); + OutputDebugString(buf); + CloseHandle(fd); + return 0; + } + + return fd; + +} + +int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error); + +int ReadCOMBlock(HANDLE fd, char * Block, int MaxLength) +{ + BOOL Error; + return ReadCOMBlockEx(fd, Block, MaxLength, &Error); +} + +// version to pass read error back to caller + +int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error) +{ + BOOL fReadStat ; + COMSTAT ComStat ; + DWORD dwErrorFlags; + DWORD dwLength; + BOOL ret; + + if (fd == NULL) + return 0; + + // only try to read number of bytes in queue + + ret = ClearCommError(fd, &dwErrorFlags, &ComStat); + + if (ret == 0) + { + int Err = GetLastError(); + *Error = TRUE; + return 0; + } + + + dwLength = min((DWORD) MaxLength, ComStat.cbInQue); + + if (dwLength > 0) + { + fReadStat = ReadFile(fd, Block, dwLength, &dwLength, NULL) ; + + if (!fReadStat) + { + dwLength = 0 ; + ClearCommError(fd, &dwErrorFlags, &ComStat ) ; + } + } + + *Error = FALSE; + + return dwLength; +} + + +BOOL WriteCOMBlock(HANDLE fd, char * Block, int BytesToWrite) +{ + BOOL fWriteStat; + DWORD BytesWritten; + DWORD ErrorFlags; + COMSTAT ComStat; + + fWriteStat = WriteFile(fd, Block, BytesToWrite, + &BytesWritten, NULL ); + + if ((!fWriteStat) || (BytesToWrite != BytesWritten)) + { + int Err = GetLastError(); + ClearCommError(fd, &ErrorFlags, &ComStat); + return FALSE; + } + return TRUE; +} + +VOID CloseCOMPort(HANDLE fd) +{ + if (fd == NULL) + return; + + SetCommMask(fd, 0); + + // drop DTR + + COMClearDTR(fd); + + // purge any outstanding reads/writes and close device handle + + PurgeComm(fd, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR ) ; + + CloseHandle(fd); + fd = NULL; +} + + +VOID COMSetDTR(HANDLE fd) +{ + EscapeCommFunction(fd, SETDTR); +} + +VOID COMClearDTR(HANDLE fd) +{ + EscapeCommFunction(fd, CLRDTR); +} + +VOID COMSetRTS(HANDLE fd) +{ + EscapeCommFunction(fd, SETRTS); +} + +VOID COMClearRTS(HANDLE fd) +{ + EscapeCommFunction(fd, CLRRTS); +} + + +#else + +static struct speed_struct +{ + int user_speed; + speed_t termios_speed; +} speed_table[] = { + {300, B300}, + {600, B600}, + {1200, B1200}, + {2400, B2400}, + {4800, B4800}, + {9600, B9600}, + {19200, B19200}, + {38400, B38400}, + {57600, B57600}, + {115200, B115200}, + {-1, B0} +}; + + +HANDLE OpenCOMPort(VOID * pPort, int speed, BOOL SetDTR, BOOL SetRTS, BOOL Quiet, int Stopbits) +{ + char Port[256]; + char buf[100]; + + // Linux Version. + + int fd; + int hwflag = 0; + u_long param=1; + struct termios term; + struct speed_struct *s; + + if ((UINT)pPort < 256) + sprintf(Port, "%s/com%d", BPQDirectory, (int)pPort); + else + strcpy(Port, pPort); + + if ((fd = open(Port, O_RDWR | O_NDELAY)) == -1) + { + if (Quiet == 0) + { + perror("Com Open Failed"); + sprintf(buf," %s could not be opened \n", Port); + WritetoConsoleLocal(buf); + Debugprintf(buf); + } + return 0; + } + + // Validate Speed Param + + for (s = speed_table; s->user_speed != -1; s++) + if (s->user_speed == speed) + break; + + if (s->user_speed == -1) + { + fprintf(stderr, "tty_speed: invalid speed %d\n", speed); + return FALSE; + } + + if (tcgetattr(fd, &term) == -1) + { + perror("tty_speed: tcgetattr"); + return FALSE; + } + + cfmakeraw(&term); + cfsetispeed(&term, s->termios_speed); + cfsetospeed(&term, s->termios_speed); + + if (tcsetattr(fd, TCSANOW, &term) == -1) + { + perror("tty_speed: tcsetattr"); + return FALSE; + } + + ioctl(fd, FIONBIO, ¶m); + + Debugprintf("LinBPQ Port %s fd %d", Port, fd); + + if (SetDTR) + { + COMSetDTR(fd); + } + else + { + COMClearDTR(fd); + } + + if (SetRTS) + { + COMSetRTS(fd); + } + else + { + COMClearRTS(fd); + } + return fd; +} + +int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error); + +int ReadCOMBlock(HANDLE fd, char * Block, int MaxLength) +{ + BOOL Error; + return ReadCOMBlockEx(fd, Block, MaxLength, &Error); +} + +// version to pass read error back to caller + +int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error) +{ + int Length; + + if (fd == 0) + { + *Error = 1; + return 0; + } + + errno = 22222; // to catch zero read (?? file closed ??) + + Length = read(fd, Block, MaxLength); + + *Error = 0; + + if (Length == 0 && errno == 22222) // seems to be result of unpluging USB + { +// printf("KISS read returned zero len and no errno\n"); + *Error = 1; + return 0; + } + + if (Length < 0) + { + if (errno != 11 && errno != 35) // Would Block + { + perror("read"); + printf("Handle %d Errno %d Len %d\n", fd, errno, Length); + *Error = errno; + } + return 0; + } + + return Length; +} + +BOOL WriteCOMBlock(HANDLE fd, char * Block, int BytesToWrite) +{ + // Some systems seem to have a very small max write size + + int ToSend = BytesToWrite; + int Sent = 0, ret; + + while (ToSend) + { + ret = write(fd, &Block[Sent], ToSend); + + if (ret >= ToSend) + return TRUE; + + if (ret == -1) + { + if (errno != 11 && errno != 35) // Would Block + return FALSE; + + usleep(10000); + ret = 0; + } + + Sent += ret; + ToSend -= ret; + } + return TRUE; +} + +VOID CloseCOMPort(HANDLE fd) +{ + if (fd == 0) + return; + + close(fd); + fd = 0; +} + +VOID COMSetDTR(HANDLE fd) +{ + int status; + + ioctl(fd, TIOCMGET, &status); + status |= TIOCM_DTR; + ioctl(fd, TIOCMSET, &status); +} + +VOID COMClearDTR(HANDLE fd) +{ + int status; + + ioctl(fd, TIOCMGET, &status); + status &= ~TIOCM_DTR; + ioctl(fd, TIOCMSET, &status); +} + +VOID COMSetRTS(HANDLE fd) +{ + int status; + + ioctl(fd, TIOCMGET, &status); + status |= TIOCM_RTS; + ioctl(fd, TIOCMSET, &status); +} + +VOID COMClearRTS(HANDLE fd) +{ + int status; + + ioctl(fd, TIOCMGET, &status); + status &= ~TIOCM_RTS; + ioctl(fd, TIOCMSET, &status); +} + +#endif + + +int MaxNodes; +int MaxRoutes; +int NodeLen; +int RouteLen; +struct DEST_LIST * Dests; +struct ROUTE * Routes; + +FILE *file; + +int DoRoutes() +{ + char digis[30] = ""; + int count, len; + char Normcall[10], Portcall[10]; + char line[80]; + + for (count=0; countNEIGHBOUR_CALL[0] != 0) + { + len=ConvFromAX25(Routes->NEIGHBOUR_CALL,Normcall); + Normcall[len]=0; + + if (Routes->NEIGHBOUR_DIGI1[0] != 0) + { + memcpy(digis," VIA ",5); + + len=ConvFromAX25(Routes->NEIGHBOUR_DIGI1,Portcall); + Portcall[len]=0; + strcpy(&digis[5],Portcall); + + if (Routes->NEIGHBOUR_DIGI2[0] != 0) + { + len=ConvFromAX25(Routes->NEIGHBOUR_DIGI2,Portcall); + Portcall[len]=0; + strcat(digis," "); + strcat(digis,Portcall); + } + } + else + digis[0] = 0; + + len=sprintf(line, + "ROUTE ADD %s %d %d %s %d %d %d %d %d\n", + Normcall, + Routes->NEIGHBOUR_PORT, + Routes->NEIGHBOUR_QUAL, digis, + Routes->NBOUR_MAXFRAME, + Routes->NBOUR_FRACK, + Routes->NBOUR_PACLEN, + Routes->INP3Node | (Routes->NoKeepAlive << 2), + Routes->OtherendsRouteQual); + + fputs(line, file); + } + + Routes+=1; + } + + return (0); +} + +int DoNodes() +{ + int count, len, cursor, i; + char Normcall[10], Portcall[10]; + char line[80]; + char Alias[7]; + + Dests-=1; + + for (count=0; countNRROUTE[0].ROUT_NEIGHBOUR == 0) + continue; + + { + len=ConvFromAX25(Dests->DEST_CALL,Normcall); + Normcall[len]=0; + + memcpy(Alias,Dests->DEST_ALIAS,6); + + Alias[6]=0; + + for (i=0;i<6;i++) + { + if (Alias[i] == ' ') + Alias[i] = 0; + } + + cursor=sprintf(line,"NODE ADD %s:%s ", Alias,Normcall); + + if (Dests->NRROUTE[0].ROUT_NEIGHBOUR != 0 && Dests->NRROUTE[0].ROUT_NEIGHBOUR->INP3Node == 0) + { + len=ConvFromAX25( + Dests->NRROUTE[0].ROUT_NEIGHBOUR->NEIGHBOUR_CALL,Portcall); + Portcall[len]=0; + + len=sprintf(&line[cursor],"%s %d %d ", + Portcall, + Dests->NRROUTE[0].ROUT_NEIGHBOUR->NEIGHBOUR_PORT, + Dests->NRROUTE[0].ROUT_QUALITY); + + cursor+=len; + + if (Dests->NRROUTE[0].ROUT_OBSCOUNT > 127) + { + len=sprintf(&line[cursor],"! "); + cursor+=len; + } + } + + if (Dests->NRROUTE[1].ROUT_NEIGHBOUR != 0 && Dests->NRROUTE[1].ROUT_NEIGHBOUR->INP3Node == 0) + { + len=ConvFromAX25( + Dests->NRROUTE[1].ROUT_NEIGHBOUR->NEIGHBOUR_CALL,Portcall); + Portcall[len]=0; + + len=sprintf(&line[cursor],"%s %d %d ", + Portcall, + Dests->NRROUTE[1].ROUT_NEIGHBOUR->NEIGHBOUR_PORT, + Dests->NRROUTE[1].ROUT_QUALITY); + + cursor+=len; + + if (Dests->NRROUTE[1].ROUT_OBSCOUNT > 127) + { + len=sprintf(&line[cursor],"! "); + cursor+=len; + } + } + + if (Dests->NRROUTE[2].ROUT_NEIGHBOUR != 0 && Dests->NRROUTE[2].ROUT_NEIGHBOUR->INP3Node == 0) + { + len=ConvFromAX25( + Dests->NRROUTE[2].ROUT_NEIGHBOUR->NEIGHBOUR_CALL,Portcall); + Portcall[len]=0; + + len=sprintf(&line[cursor],"%s %d %d ", + Portcall, + Dests->NRROUTE[2].ROUT_NEIGHBOUR->NEIGHBOUR_PORT, + Dests->NRROUTE[2].ROUT_QUALITY); + + cursor+=len; + + if (Dests->NRROUTE[2].ROUT_OBSCOUNT > 127) + { + len=sprintf(&line[cursor],"! "); + cursor+=len; + } + } + + if (cursor > 30) + { + line[cursor++]='\n'; + line[cursor++]=0; + fputs(line, file); + } + } + } + return (0); +} + +void SaveMH() +{ + char FN[250]; + struct PORTCONTROL * PORT = PORTTABLE; + FILE *file; + + if (BPQDirectory[0] == 0) + { + strcpy(FN, "MHSave.txt"); + } + else + { + strcpy(FN,BPQDirectory); + strcat(FN,"/"); + strcat(FN,"MHSave.txt"); + } + + if ((file = fopen(FN, "w")) == NULL) + return; + + while (PORT) + { + int Port = 0; + char * ptr; + + MHSTRUC * MH = PORT->PORTMHEARD; + + int count = MHENTRIES; + int n; + char Normcall[20]; + char From[10]; + char DigiList[100]; + char * Output; + int len; + char Digi = 0; + + + // Note that the MHDIGIS field may contain rubbish. You have to check End of Address bit to find + // how many digis there are + + if (MH == NULL) + continue; + + fprintf(file, "Port:%d\n", PORT->PORTNUMBER); + + while (count--) + { + if (MH->MHCALL[0] == 0) + break; + + Digi = 0; + + len = ConvFromAX25(MH->MHCALL, Normcall); + Normcall[len] = 0; + + n = 8; // Max number of digi-peaters + + ptr = &MH->MHCALL[6]; // End of Address bit + + Output = &DigiList[0]; + + if ((*ptr & 1) == 0) + { + // at least one digi + + strcpy(Output, "via "); + Output += 4; + + while ((*ptr & 1) == 0) + { + // MORE TO COME + + From[ConvFromAX25(ptr + 1, From)] = 0; + Output += sprintf((char *)Output, "%s", From); + + ptr += 7; + n--; + + if (n == 0) + break; + + // See if digi actioned - put a * on last actioned + + if (*ptr & 0x80) + { + if (*ptr & 1) // if last address, must need * + { + *(Output++) = '*'; + Digi = '*'; + } + + else + if ((ptr[7] & 0x80) == 0) // Repeased by next? + { + *(Output++) = '*'; // No, so need * + Digi = '*'; + } + + + } + *(Output++) = ','; + } + *(--Output) = 0; // remove last comma + } + else + *(Output) = 0; + + // if we used a digi set * on call and display via string + + + if (Digi) + Normcall[len++] = Digi; + else + DigiList[0] = 0; // Dont show list if not used + + Normcall[len++] = 0; + + ptr = FormatMH(MH, 'U'); + + ptr[15] = 0; + + if (MH->MHDIGI) + fprintf(file, "%d %6d %-10s%c %s %s|%s|%s\n", (int)MH->MHTIME, MH->MHCOUNT, Normcall, MH->MHDIGI, ptr, DigiList, MH->MHLocator, MH->MHFreq); + else + fprintf(file, "%d %6d %-10s%c %s %s|%s|%s\n", (int)MH->MHTIME, MH->MHCOUNT, Normcall, ' ', ptr, DigiList, MH->MHLocator, MH->MHFreq); + + MH++; + } + PORT = PORT->PORTPOINTER; + } + + fclose(file); + + return; +} + + +int APIENTRY SaveNodes () +{ + char FN[250]; + + Routes = NEIGHBOURS; + RouteLen = ROUTE_LEN; + MaxRoutes = MAXNEIGHBOURS; + + Dests = DESTS; + NodeLen = DEST_LIST_LEN; + MaxNodes = MAXDESTS; + + // Set up pointer to BPQNODES file + + if (BPQDirectory[0] == 0) + { + strcpy(FN,"BPQNODES.dat"); + } + else + { + strcpy(FN,BPQDirectory); + strcat(FN,"/"); + strcat(FN,"BPQNODES.dat"); + } + + if ((file = fopen(FN, "w")) == NULL) + return FALSE; + + DoRoutes(); + DoNodes(); + + fclose(file); + + return (0); +} + +DllExport int APIENTRY ClearNodes () +{ + char FN[250]; + + // Set up pointer to BPQNODES file + + if (BPQDirectory[0] == 0) + { + strcpy(FN,"BPQNODES.dat"); + } + else + { + strcpy(FN,BPQDirectory); + strcat(FN,"/"); + strcat(FN,"BPQNODES.dat"); + } + + if ((file = fopen(FN, "w")) == NULL) + return FALSE; + + fclose(file); + + return (0); +} +char * FormatUptime(int Uptime) + { + struct tm * TM; + static char UPTime[50]; + time_t szClock = Uptime * 60; + + TM = gmtime(&szClock); + + sprintf(UPTime, "Uptime (Days Hours Mins) %.2d:%.2d:%.2d\r", + TM->tm_yday, TM->tm_hour, TM->tm_min); + + return UPTime; + } + +static char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + + +char * FormatMH(PMHSTRUC MH, char Format) +{ + struct tm * TM; + static char MHTime[50]; + time_t szClock; + char LOC[7]; + + memcpy(LOC, MH->MHLocator, 6); + LOC[6] = 0; + + if (Format == 'U' || Format =='L') + szClock = MH->MHTIME; + else + szClock = time(NULL) - MH->MHTIME; + + if (Format == 'L') + TM = localtime(&szClock); + else + TM = gmtime(&szClock); + + if (Format == 'U' || Format =='L') + sprintf(MHTime, "%s %02d %.2d:%.2d:%.2d %s %s", + month[TM->tm_mon], TM->tm_mday, TM->tm_hour, TM->tm_min, TM->tm_sec, MH->MHFreq, LOC); + else + sprintf(MHTime, "%.2d:%.2d:%.2d:%.2d %s %s", + TM->tm_yday, TM->tm_hour, TM->tm_min, TM->tm_sec, MH->MHFreq, LOC); + + return MHTime; + +} + + +Dll VOID APIENTRY CreateOneTimePassword(char * Password, char * KeyPhrase, int TimeOffset) +{ + // Create a time dependent One Time Password from the KeyPhrase + // TimeOffset is used when checking to allow for slight variation in clocks + + time_t NOW = time(NULL); + UCHAR Hash[16]; + char Key[1000]; + int i, chr; + + NOW = NOW/30 + TimeOffset; // Only Change every 30 secs + + sprintf(Key, "%s%x", KeyPhrase, (int)NOW); + + md5(Key, Hash); + + for (i=0; i<16; i++) + { + chr = (Hash[i] & 31); + if (chr > 9) chr += 7; + + Password[i] = chr + 48; + } + + Password[16] = 0; + return; +} + +Dll BOOL APIENTRY CheckOneTimePassword(char * Password, char * KeyPhrase) +{ + char CheckPassword[17]; + int Offsets[10] = {0, -1, 1, -2, 2, -3, 3, -4, 4}; + int i, Pass; + + if (strlen(Password) < 16) + Pass = atoi(Password); + + for (i = 0; i < 9; i++) + { + CreateOneTimePassword(CheckPassword, KeyPhrase, Offsets[i]); + + if (strlen(Password) < 16) + { + // Using a numeric extract + + long long Val; + + memcpy(&Val, CheckPassword, 8); + Val = Val %= 1000000; + + if (Pass == Val) + return TRUE; + } + else + if (memcmp(Password, CheckPassword, 16) == 0) + return TRUE; + } + + return FALSE; +} + + +DllExport BOOL ConvToAX25Ex(unsigned char * callsign, unsigned char * ax25call) +{ + // Allows SSID's of 'T and 'R' + + int i; + + memset(ax25call,0x40,6); // in case short + ax25call[6]=0x60; // default SSID + + for (i=0;i<7;i++) + { + if (callsign[i] == '-') + { + // + // process ssid and return + // + + if (callsign[i+1] == 'T') + { + ax25call[6]=0x42; + return TRUE; + } + + if (callsign[i+1] == 'R') + { + ax25call[6]=0x44; + return TRUE; + } + i = atoi(&callsign[i+1]); + + if (i < 16) + { + ax25call[6] |= i<<1; + return (TRUE); + } + return (FALSE); + } + + if (callsign[i] == 0 || callsign[i] == 13 || callsign[i] == ' ' || callsign[i] == ',') + { + // + // End of call - no ssid + // + return (TRUE); + } + + ax25call[i] = callsign[i] << 1; + } + + // + // Too many chars + // + + return (FALSE); +} + + +DllExport BOOL ConvToAX25(unsigned char * callsign, unsigned char * ax25call) +{ + int i; + + memset(ax25call,0x40,6); // in case short + ax25call[6]=0x60; // default SSID + + for (i=0;i<7;i++) + { + if (callsign[i] == '-') + { + // + // process ssid and return + // + i = atoi(&callsign[i+1]); + + if (i < 16) + { + ax25call[6] |= i<<1; + return (TRUE); + } + return (FALSE); + } + + if (callsign[i] == 0 || callsign[i] == 13 || callsign[i] == ' ' || callsign[i] == ',') + { + // + // End of call - no ssid + // + return (TRUE); + } + + ax25call[i] = callsign[i] << 1; + } + + // + // Too many chars + // + + return (FALSE); +} + + +DllExport int ConvFromAX25(unsigned char * incall,unsigned char * outcall) +{ + int in,out=0; + unsigned char chr; + + memset(outcall,0x20,10); + + for (in=0;in<6;in++) + { + chr=incall[in]; + if (chr == 0x40) + break; + chr >>= 1; + outcall[out++]=chr; + } + + chr=incall[6]; // ssid + + if (chr == 0x42) + { + outcall[out++]='-'; + outcall[out++]='T'; + return out; + } + + if (chr == 0x44) + { + outcall[out++]='-'; + outcall[out++]='R'; + return out; + } + + chr >>= 1; + chr &= 15; + + if (chr > 0) + { + outcall[out++]='-'; + if (chr > 9) + { + chr-=10; + outcall[out++]='1'; + } + chr+=48; + outcall[out++]=chr; + } + return (out); +} + +unsigned short int compute_crc(unsigned char *buf, int txlen); + +SOCKADDR_IN reportdest = {0}; + +SOCKET ReportSocket = 0; + +SOCKADDR_IN Chatreportdest = {0}; + +extern char LOCATOR[]; // Locator for Reporting - may be Maidenhead or LAT:LON +extern char MAPCOMMENT[]; // Locator for Reporting - may be Maidenhead or LAT:LON +extern char LOC[7]; // Maidenhead Locator for Reporting +extern char ReportDest[7]; + + +VOID SendReportMsg(char * buff, int txlen) +{ + unsigned short int crc = compute_crc(buff, txlen); + + crc ^= 0xffff; + + buff[txlen++] = (crc&0xff); + buff[txlen++] = (crc>>8); + + sendto(ReportSocket, buff, txlen, 0, (LPSOCKADDR)&reportdest, sizeof(reportdest)); + +} +VOID SendLocation() +{ + MESSAGE AXMSG = {0}; + PMESSAGE AXPTR = &AXMSG; + char Msg[512]; + int Len; + + Len = sprintf(Msg, "%s %s
%s", LOCATOR, VersionString, MAPCOMMENT); + +#ifdef LINBPQ + Len = sprintf(Msg, "%s L%s
%s", LOCATOR, VersionString, MAPCOMMENT); +#endif +#ifdef MACBPQ + Len = sprintf(Msg, "%s M%s
%s", LOCATOR, VersionString, MAPCOMMENT); +#endif +#ifdef FREEBSD + Len = sprintf(Msg, "%s F%s
%s", LOCATOR, VersionString, MAPCOMMENT); +#endif + + if (Len > 256) + Len = 256; + + // Block includes the Msg Header (7 bytes), Len Does not! + + memcpy(AXPTR->DEST, ReportDest, 7); + memcpy(AXPTR->ORIGIN, MYCALL, 7); + AXPTR->DEST[6] &= 0x7e; // Clear End of Call + AXPTR->DEST[6] |= 0x80; // set Command Bit + + AXPTR->ORIGIN[6] |= 1; // Set End of Call + AXPTR->CTL = 3; //UI + AXPTR->PID = 0xf0; + memcpy(AXPTR->L2DATA, Msg, Len); + + SendReportMsg((char *)&AXMSG.DEST, Len + 16); + + return; + +} + + + + +VOID SendMH(struct TNCINFO * TNC, char * call, char * freq, char * LOC, char * Mode) +{ + MESSAGE AXMSG; + PMESSAGE AXPTR = &AXMSG; + char Msg[100]; + int Len; + + if (ReportSocket == 0 || LOCATOR[0] == 0) + return; + + Len = sprintf(Msg, "MH %s,%s,%s,%s", call, freq, LOC, Mode); + + // Block includes the Msg Header (7 bytes), Len Does not! + + memcpy(AXPTR->DEST, ReportDest, 7); + if (TNC->PortRecord->PORTCONTROL.PORTCALL[0]) + memcpy(AXPTR->ORIGIN, TNC->PortRecord->PORTCONTROL.PORTCALL, 7); + else + memcpy(AXPTR->ORIGIN, MYCALL, 7); + AXPTR->DEST[6] &= 0x7e; // Clear End of Call + AXPTR->DEST[6] |= 0x80; // set Command Bit + + AXPTR->ORIGIN[6] |= 1; // Set End of Call + AXPTR->CTL = 3; //UI + AXPTR->PID = 0xf0; + memcpy(AXPTR->L2DATA, Msg, Len); + + SendReportMsg((char *)&AXMSG.DEST, Len + 16) ; + + return; + +} + +time_t TimeLastNRRouteSent = 0; + +char NRRouteMessage[256]; +int NRRouteLen = 0; + + +VOID SendNETROMRoute(struct PORTCONTROL * PORT, unsigned char * axcall) +{ + // Called to update Link Map when a NODES Broadcast is received + // Batch to reduce Load + + MESSAGE AXMSG; + PMESSAGE AXPTR = &AXMSG; + char Msg[300]; + int Len; + char Call[10]; + char Report[16]; + time_t Now = time(NULL); + int NeedSend = FALSE; + + + if (ReportSocket == 0 || LOCATOR[0] == 0) + return; + + Call[ConvFromAX25(axcall, Call)] = 0; + + sprintf(Report, "%s,%d,", Call, PORT->PORTTYPE); + + if (Now - TimeLastNRRouteSent > 60) + NeedSend = TRUE; + + if (strstr(NRRouteMessage, Report) == 0) // reported recently + strcat(NRRouteMessage, Report); + + if (strlen(NRRouteMessage) > 230 || NeedSend) + { + Len = sprintf(Msg, "LINK %s", NRRouteMessage); + + // Block includes the Msg Header (7 bytes), Len Does not! + + memcpy(AXPTR->DEST, ReportDest, 7); + memcpy(AXPTR->ORIGIN, MYCALL, 7); + AXPTR->DEST[6] &= 0x7e; // Clear End of Call + AXPTR->DEST[6] |= 0x80; // set Command Bit + + AXPTR->ORIGIN[6] |= 1; // Set End of Call + AXPTR->CTL = 3; //UI + AXPTR->PID = 0xf0; + memcpy(AXPTR->L2DATA, Msg, Len); + + SendReportMsg((char *)&AXMSG.DEST, Len + 16) ; + + TimeLastNRRouteSent = Now; + NRRouteMessage[0] = 0; + } + + return; + +} + +DllExport char * APIENTRY GetApplCall(int Appl) +{ + if (Appl < 1 || Appl > NumberofAppls ) return NULL; + + return (UCHAR *)(&APPLCALLTABLE[Appl-1].APPLCALL_TEXT); +} +DllExport char * APIENTRY GetApplAlias(int Appl) +{ + if (Appl < 1 || Appl > NumberofAppls ) return NULL; + + return (UCHAR *)(&APPLCALLTABLE[Appl-1].APPLALIAS_TEXT); +} + +DllExport int32_t APIENTRY GetApplQual(int Appl) +{ + if (Appl < 1 || Appl > NumberofAppls ) return 0; + + return (APPLCALLTABLE[Appl-1].APPLQUAL); +} + +char * GetApplCallFromName(char * App) +{ + int i; + char PaddedAppl[13] = " "; + + memcpy(PaddedAppl, App, (int)strlen(App)); + + for (i = 0; i < NumberofAppls; i++) + { + if (memcmp(&APPLCALLTABLE[i].APPLCMD, PaddedAppl, 12) == 0) + return &APPLCALLTABLE[i].APPLCALL_TEXT[0]; + } + return NULL; +} + + +DllExport char * APIENTRY GetApplName(int Appl) +{ + if (Appl < 1 || Appl > NumberofAppls ) return NULL; + + return (UCHAR *)(&APPLCALLTABLE[Appl-1].APPLCMD); +} + +DllExport int APIENTRY GetNumberofPorts() +{ + return (NUMBEROFPORTS); +} + +DllExport int APIENTRY GetPortNumber(int portslot) +{ + struct PORTCONTROL * PORTVEC=PORTTABLE; + + if (portslot>NUMBEROFPORTS) + portslot=NUMBEROFPORTS; + + while (--portslot > 0) + PORTVEC=PORTVEC->PORTPOINTER; + + return PORTVEC->PORTNUMBER; + +} + +DllExport char * APIENTRY GetVersionString() +{ +// return ((char *)&VersionStringWithBuild); + return ((char *)&VersionString); +} + +#ifdef MACBPQ + +//Fiddle till I find a better solution + +#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1060 +int __sync_lock_test_and_set(int * ptr, int val) +{ + *ptr = val; + return 0; +} +#endif // __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ +#endif // MACBPQ + + + +void GetSemaphore(struct SEM * Semaphore, int ID) +{ + // + // Wait for it to be free + // + + if (Semaphore->Flag != 0) + { + Semaphore->Clashes++; + } + +loop1: + + while (Semaphore->Flag != 0) + { + Sleep(10); + } + + // + // try to get semaphore + // + +#ifdef WIN32 + + { + if (InterlockedExchange(&Semaphore->Flag, 1) != 0) // Failed to get it + goto loop1; // try again;; + } + +#else + + if (__sync_lock_test_and_set(&Semaphore->Flag, 1) != 0) + + // Failed to get it + goto loop1; // try again; + +#endif + + //Ok. got it + + Semaphore->Gets++; + Semaphore->SemProcessID = GetCurrentProcessId(); + Semaphore->SemThreadID = GetCurrentThreadId(); + SemHeldByAPI = ID; + + return; +} + +void FreeSemaphore(struct SEM * Semaphore) +{ + if (Semaphore->Flag == 0) + Debugprintf("Free Semaphore Called when Sem not held"); + + Semaphore->Rels++; + Semaphore->Flag = 0; + + return; +} + +#ifdef WIN32 + +#include "DbgHelp.h" +/* +USHORT WINAPI RtlCaptureStackBackTrace( + __in ULONG FramesToSkip, + __in ULONG FramesToCapture, + __out PVOID *BackTrace, + __out_opt PULONG BackTraceHash +); +*/ +#endif + +void printStack(void) +{ +#ifdef WIN32 +#ifdef _DEBUG // So we can use on 98/2K + + unsigned int i; + void * stack[ 100 ]; + unsigned short frames; + SYMBOL_INFO * symbol; + HANDLE process; + + Debugprintf("Stack Backtrace"); + + process = GetCurrentProcess(); + + SymInitialize( process, NULL, TRUE ); + + frames = RtlCaptureStackBackTrace( 0, 60, stack, NULL ); + symbol = ( SYMBOL_INFO * )calloc( sizeof( SYMBOL_INFO ) + 256 * sizeof( char ), 1 ); + symbol->MaxNameLen = 255; + symbol->SizeOfStruct = sizeof( SYMBOL_INFO ); + + for( i = 0; i < frames; i++ ) + { + SymFromAddr( process, ( DWORD64 )( stack[ i ] ), 0, symbol ); + + Debugprintf( "%i: %s - %p", frames - i - 1, symbol->Name, symbol->Address ); + } + + free(symbol); + +#endif +#endif +} + +pthread_t ResolveUpdateThreadId = 0; + +char NodeMapServer[80] = "update.g8bpq.net"; +char ChatMapServer[80] = "chatupdate.g8bpq.net"; + +VOID ResolveUpdateThread(void * Unused) +{ + struct hostent * HostEnt1; + struct hostent * HostEnt2; + + ResolveUpdateThreadId = GetCurrentThreadId(); + + while (TRUE) + { + if (pthread_equal(ResolveUpdateThreadId, GetCurrentThreadId()) == FALSE) + { + Debugprintf("Resolve Update thread %x redundant - closing", GetCurrentThreadId()); + return; + } + + // Resolve name to address + + Debugprintf("Resolving %s", NodeMapServer); + HostEnt1 = gethostbyname (NodeMapServer); +// HostEnt1 = gethostbyname ("192.168.1.64"); + + if (HostEnt1) + memcpy(&reportdest.sin_addr.s_addr,HostEnt1->h_addr,4); + + Debugprintf("Resolving %s", ChatMapServer); + HostEnt2 = gethostbyname (ChatMapServer); +// HostEnt2 = gethostbyname ("192.168.1.64"); + + if (HostEnt2) + memcpy(&Chatreportdest.sin_addr.s_addr,HostEnt2->h_addr,4); + + if (HostEnt1 && HostEnt2) + { + Sleep(1000 * 60 * 30); + continue; + } + + Debugprintf("Resolve Failed for update.g8bpq.net or chatmap.g8bpq.net"); + Sleep(1000 * 60 * 5); + } +} + + +VOID OpenReportingSockets() +{ + u_long param=1; + BOOL bcopt=TRUE; + + if (LOCATOR[0]) + { + // Enable Node Map Reports + + ReportTimer = 600; + + ReportSocket = socket(AF_INET,SOCK_DGRAM,0); + + if (ReportSocket == INVALID_SOCKET) + { + Debugprintf("Failed to create Reporting socket"); + ReportSocket = 0; + return; + } + + ioctlsocket (ReportSocket, FIONBIO, ¶m); + setsockopt (ReportSocket, SOL_SOCKET, SO_BROADCAST, (const char FAR *)&bcopt,4); + + reportdest.sin_family = AF_INET; + reportdest.sin_port = htons(81); + ConvToAX25("DUMMY-1", ReportDest); + } + + // Set up Chat Report even if no LOCATOR reportdest.sin_family = AF_INET; + // Socket must be opened in MailChat Process + + Chatreportdest.sin_family = AF_INET; + Chatreportdest.sin_port = htons(81); + + _beginthread(ResolveUpdateThread, 0, NULL); +} + +VOID WriteMiniDumpThread(); + +time_t lastMiniDump = 0; + +void WriteMiniDump() +{ +#ifdef WIN32 + + _beginthread(WriteMiniDumpThread, 0, 0); + Sleep(3000); +} + +VOID WriteMiniDumpThread() +{ + HANDLE hFile; + BOOL ret; + char FN[256]; + struct tm * TM; + time_t Now = time(NULL); + + if (lastMiniDump == Now) // Not more than one per second + { + Debugprintf("minidump suppressed"); + return; + } + + lastMiniDump = Now; + + TM = gmtime(&Now); + + sprintf(FN, "%s/Logs/MiniDump%d%02d%02d%02d%02d%02d.dmp", BPQDirectory, + TM->tm_year + 1900, TM->tm_mon +1, TM->tm_mday, TM->tm_hour, TM->tm_min, TM->tm_sec); + + hFile = CreateFile(FN, GENERIC_READ | GENERIC_WRITE, + 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE)) + { + // Create the minidump + + ret = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), + hFile, MiniDumpNormal, 0, 0, 0 ); + + if(!ret) + Debugprintf("MiniDumpWriteDump failed. Error: %u", GetLastError()); + else + Debugprintf("Minidump %s created.", FN); + CloseHandle(hFile); + } +#endif +} + +// UI Util Code + +#pragma pack(1) + +typedef struct _MESSAGEX +{ +// BASIC LINK LEVEL MESSAGE BUFFER LAYOUT + + struct _MESSAGEX * CHAIN; + + UCHAR PORT; + USHORT LENGTH; + + UCHAR DEST[7]; + UCHAR ORIGIN[7]; + +// MAY BE UP TO 56 BYTES OF DIGIS + + UCHAR CTL; + UCHAR PID; + UCHAR DATA[256]; + UCHAR PADDING[56]; // In case he have Digis + +}MESSAGEX, *PMESSAGEX; + +#pragma pack() + + +int PortNum[33] = {0}; // Tab nunber to port + +char * UIUIDigi[33]= {0}; +char * UIUIDigiAX[33] = {0}; // ax.25 version of digistring +int UIUIDigiLen[33] = {0}; // Length of AX string + +char UIUIDEST[33][11] = {0}; // Dest for Beacons + +char UIAXDEST[33][7] = {0}; + + +UCHAR FN[33][256]; // Filename +int Interval[33]; // Beacon Interval (Mins) +int MinCounter[33]; // Interval Countdown + +BOOL SendFromFile[33]; +char Message[33][1000]; // Beacon Text + +VOID SendUIBeacon(int Port); + +BOOL RunUI = TRUE; + +VOID UIThread(void * Unused) +{ + int Port, MaxPorts = GetNumberofPorts(); + + Sleep(60000); + + while (RunUI) + { + int sleepInterval = 60000; + + for (Port = 1; Port <= MaxPorts; Port++) + { + if (MinCounter[Port]) + { + MinCounter[Port]--; + + if (MinCounter[Port] == 0) + { + MinCounter[Port] = Interval[Port]; + SendUIBeacon(Port); + + // pause beteen beacons but adjust sleep interval to suit + + Sleep(10000); + sleepInterval -= 10000; + } + } + } + + while (sleepInterval <= 0) // just in case we have a crazy config + sleepInterval += 60000; + + Sleep(sleepInterval); + } +} + +int UIRemoveLF(char * Message, int len) +{ + // Remove lf chars + + char * ptr1, * ptr2; + + ptr1 = ptr2 = Message; + + while (len-- > 0) + { + *ptr2 = *ptr1; + + if (*ptr1 == '\r') + if (*(ptr1+1) == '\n') + { + ptr1++; + len--; + } + ptr1++; + ptr2++; + } + + return (int)(ptr2 - Message); +} + + + + +VOID UISend_AX_Datagram(UCHAR * Msg, DWORD Len, UCHAR Port, UCHAR * HWADDR, BOOL Queue) +{ + MESSAGEX AXMSG; + PMESSAGEX AXPTR = &AXMSG; + int DataLen = Len; + struct PORTCONTROL * PORT = GetPortTableEntryFromSlot(Port); + + // Block includes the Msg Header (7 or 11 bytes), Len Does not! + + memcpy(AXPTR->DEST, HWADDR, 7); + + // Get BCALL or PORTCALL if set + + if (PORT && PORT->PORTBCALL[0]) + memcpy(AXPTR->ORIGIN, PORT->PORTBCALL, 7); + else if (PORT && PORT->PORTCALL[0]) + memcpy(AXPTR->ORIGIN, PORT->PORTCALL, 7); + else + memcpy(AXPTR->ORIGIN, MYCALL, 7); + + AXPTR->DEST[6] &= 0x7e; // Clear End of Call + AXPTR->DEST[6] |= 0x80; // set Command Bit + + if (UIUIDigi[Port]) + { + // This port has a digi string + + int DigiLen = UIUIDigiLen[Port]; + UCHAR * ptr; + + memcpy(&AXPTR->CTL, UIUIDigiAX[Port], DigiLen); + + ptr = (UCHAR *)AXPTR; + ptr += DigiLen; + AXPTR = (PMESSAGEX)ptr; + + Len += DigiLen; + } + + AXPTR->ORIGIN[6] |= 1; // Set End of Call + AXPTR->CTL = 3; //UI + AXPTR->PID = 0xf0; + memcpy(AXPTR->DATA, Msg, DataLen); + +// if (Queue) +// QueueRaw(Port, &AXMSG, Len + 16); +// else + SendRaw(Port, (char *)&AXMSG.DEST, Len + 16); + + return; + +} + + + +VOID SendUIBeacon(int Port) +{ + char UIMessage[1024]; + int Len = (int)strlen(Message[Port]); + int Index = 0; + + if (SendFromFile[Port]) + { + FILE * hFile; + + hFile = fopen(FN[Port], "rb"); + + if (hFile == 0) + return; + + Len = (int)fread(UIMessage, 1, 1024, hFile); + + fclose(hFile); + + } + else + strcpy(UIMessage, Message[Port]); + + Len = UIRemoveLF(UIMessage, Len); + + while (Len > 256) + { + UISend_AX_Datagram(&UIMessage[Index], 256, Port, UIAXDEST[Port], TRUE); + Index += 256; + Len -= 256; + Sleep(2000); + } + UISend_AX_Datagram(&UIMessage[Index], Len, Port, UIAXDEST[Port], TRUE); +} + +#ifndef LINBPQ + +typedef struct tag_dlghdr +{ + HWND hwndTab; // tab control + HWND hwndDisplay; // current child dialog box + RECT rcDisplay; // display rectangle for the tab control + + DLGTEMPLATE *apRes[33]; + +} DLGHDR; + +DLGTEMPLATE * WINAPI DoLockDlgRes(LPCSTR lpszResName); + +#endif + +HWND hwndDlg; +int PageCount; +int CurrentPage=0; // Page currently on show in tabbed Dialog + + +VOID WINAPI OnSelChanged(HWND hwndDlg); +VOID WINAPI OnChildDialogInit(HWND hwndDlg); + +#define ICC_STANDARD_CLASSES 0x00004000 + +HWND hwndDisplay; + +#define ID_TEST 102 +#define IDD_DIAGLOG1 103 +#define IDC_FROMFILE 1022 +#define IDC_EDIT1 1054 +#define IDC_FILENAME 1054 +#define IDC_EDIT2 1055 +#define IDC_MESSAGE 1055 +#define IDC_EDIT3 1056 +#define IDC_INTERVAL 1056 +#define IDC_EDIT4 1057 +#define IDC_UIDEST 1057 +#define IDC_FILE 1058 +#define IDC_TAB1 1059 +#define IDC_UIDIGIS 1059 +#define IDC_PORTNAME 1060 + +extern HKEY REGTREE; +HBRUSH bgBrush; + +VOID SetupUI(int Port) +{ + char DigiString[100], * DigiLeft; + + ConvToAX25(UIUIDEST[Port], &UIAXDEST[Port][0]); + + UIUIDigiLen[Port] = 0; + + if (UIUIDigi[Port]) + { + UIUIDigiAX[Port] = zalloc(100); + strcpy(DigiString, UIUIDigi[Port]); + DigiLeft = strlop(DigiString,','); + + while(DigiString[0]) + { + ConvToAX25(DigiString, &UIUIDigiAX[Port][UIUIDigiLen[Port]]); + UIUIDigiLen[Port] += 7; + + if (DigiLeft) + { + memmove(DigiString, DigiLeft, (int)strlen(DigiLeft) + 1); + DigiLeft = strlop(DigiString,','); + } + else + DigiString[0] = 0; + } + } +} + +#ifndef LINBPQ + +VOID SaveIntValue(config_setting_t * group, char * name, int value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_INT); + if(setting) + config_setting_set_int(setting, value); +} + +VOID SaveStringValue(config_setting_t * group, char * name, char * value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_STRING); + if (setting) + config_setting_set_string(setting, value); + +} + +#endif + +config_t cfg; + +VOID SaveUIConfig() +{ + config_setting_t *root, *group, *UIGroup; + int Port, MaxPort = GetNumberofPorts(); + char ConfigName[256]; + + if (BPQDirectory[0] == 0) + { + strcpy(ConfigName,"UIUtil.cfg"); + } + else + { + strcpy(ConfigName,BPQDirectory); + strcat(ConfigName,"/"); + strcat(ConfigName,"UIUtil.cfg"); + } + + // Get rid of old config before saving + + config_init(&cfg); + + root = config_root_setting(&cfg); + + group = config_setting_add(root, "main", CONFIG_TYPE_GROUP); + + UIGroup = config_setting_add(group, "UIUtil", CONFIG_TYPE_GROUP); + + for (Port = 1; Port <= MaxPort; Port++) + { + char Key[20]; + + sprintf(Key, "Port%d", Port); + group = config_setting_add(UIGroup, Key, CONFIG_TYPE_GROUP); + + SaveStringValue(group, "UIDEST", &UIUIDEST[Port][0]); + SaveStringValue(group, "FileName", &FN[Port][0]); + SaveStringValue(group, "Message", &Message[Port][0]); + SaveStringValue(group, "Digis", UIUIDigi[Port]); + + SaveIntValue(group, "Interval", Interval[Port]); + SaveIntValue(group, "SendFromFile", SendFromFile[Port]); + } + + if(!config_write_file(&cfg, ConfigName)) + { + fprintf(stderr, "Error while writing file.\n"); + config_destroy(&cfg); + return; + } + + config_destroy(&cfg); +} + +VOID GetUIConfig() +{ +#ifdef LINBPQ + + char Key[100]; + char CfgFN[256]; + char Digis[100]; + struct stat STAT; + + config_t cfg; + config_setting_t *group; + int Port, MaxPort = GetNumberofPorts(); + + memset((void *)&cfg, 0, sizeof(config_t)); + + config_init(&cfg); + + if (BPQDirectory[0] == 0) + { + strcpy(CfgFN,"UIUtil.cfg"); + } + else + { + strcpy(CfgFN,BPQDirectory); + strcat(CfgFN,"/"); + strcat(CfgFN,"UIUtil.cfg"); + } + + if (stat(CfgFN, &STAT) == -1) + { + Debugprintf("UIUtil Config File not found\n"); + return; + } + + if(!config_read_file(&cfg, CfgFN)) + { + fprintf(stderr, "UI Util Config Error Line %d - %s\n", config_error_line(&cfg), config_error_text(&cfg)); + + config_destroy(&cfg); + return; + } + + group = config_lookup(&cfg, "main"); + + if (group) + { + for (Port = 1; Port <= MaxPort; Port++) + { + sprintf(Key, "main.UIUtil.Port%d", Port); + + group = config_lookup (&cfg, Key); + + if (group) + { + GetStringValue(group, "UIDEST", &UIUIDEST[Port][0]); + GetStringValue(group, "FileName", &FN[Port][0]); + GetStringValue(group, "Message", &Message[Port][0]); + GetStringValue(group, "Digis", Digis); + UIUIDigi[Port] = _strdup(Digis); + + Interval[Port] = GetIntValue(group, "Interval"); + MinCounter[Port] = Interval[Port]; + + SendFromFile[Port] = GetIntValue(group, "SendFromFile"); + + SetupUI(Port); + } + } + } + +#else + + int retCode, Vallen, Type, i; + char Key[80]; + char Size[80]; + HKEY hKey; + RECT Rect; + + wsprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\UIUtil"); + + retCode = RegOpenKeyEx (REGTREE, Key, 0, KEY_QUERY_VALUE, &hKey); + + if (retCode == ERROR_SUCCESS) + { + Vallen=80; + + retCode = RegQueryValueEx(hKey,"Size",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + if (retCode == ERROR_SUCCESS) + sscanf(Size,"%d,%d,%d,%d",&Rect.left,&Rect.right,&Rect.top,&Rect.bottom); + + RegCloseKey(hKey); + } + + for (i=1; i<=32; i++) + { + wsprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\UIUtil\\UIPort%d", i); + + retCode = RegOpenKeyEx (REGTREE, + Key, + 0, + KEY_QUERY_VALUE, + &hKey); + + if (retCode == ERROR_SUCCESS) + { + Vallen=0; + RegQueryValueEx(hKey,"Digis",0, + (ULONG *)&Type, NULL, (ULONG *)&Vallen); + + if (Vallen) + { + UIUIDigi[i] = malloc(Vallen); + RegQueryValueEx(hKey,"Digis",0, + (ULONG *)&Type, UIUIDigi[i], (ULONG *)&Vallen); + } + + Vallen=4; + retCode = RegQueryValueEx(hKey, "Interval", 0, + (ULONG *)&Type, (UCHAR *)&Interval[i], (ULONG *)&Vallen); + + MinCounter[i] = Interval[i]; + + Vallen=4; + retCode = RegQueryValueEx(hKey, "SendFromFile", 0, + (ULONG *)&Type, (UCHAR *)&SendFromFile[i], (ULONG *)&Vallen); + + + Vallen=10; + retCode = RegQueryValueEx(hKey, "UIDEST", 0, &Type, &UIUIDEST[i][0], &Vallen); + + Vallen=255; + retCode = RegQueryValueEx(hKey, "FileName", 0, &Type, &FN[i][0], &Vallen); + + Vallen=999; + retCode = RegQueryValueEx(hKey, "Message", 0, &Type, &Message[i][0], &Vallen); + + SetupUI(i); + + RegCloseKey(hKey); + } + } + + SaveUIConfig(); + +#endif + + _beginthread(UIThread, 0, NULL); + +} + +#ifndef LINBPQ + +INT_PTR CALLBACK ChildDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ +// This processes messages from controls on the tab subpages + int Command; + + int retCode, disp; + char Key[80]; + HKEY hKey; + BOOL OK; + OPENFILENAME ofn; + char Digis[100]; + + int Port = PortNum[CurrentPage]; + + + switch (message) + { + case WM_NOTIFY: + + switch (((LPNMHDR)lParam)->code) + { + case TCN_SELCHANGE: + OnSelChanged(hDlg); + return TRUE; + // More cases on WM_NOTIFY switch. + case NM_CHAR: + return TRUE; + } + + break; + case WM_INITDIALOG: + OnChildDialogInit( hDlg); + return (INT_PTR)TRUE; + + case WM_CTLCOLORDLG: + + return (LONG)bgBrush; + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + return (LONG)bgBrush; + } + + + case WM_COMMAND: + + Command = LOWORD(wParam); + + if (Command == 2002) + return TRUE; + + switch (Command) + { + case IDC_FILE: + + memset(&ofn, 0, sizeof (OPENFILENAME)); + ofn.lStructSize = sizeof (OPENFILENAME); + ofn.hwndOwner = hDlg; + ofn.lpstrFile = &FN[Port][0]; + ofn.nMaxFile = 250; + ofn.lpstrTitle = "File to send as beacon"; + ofn.lpstrInitialDir = BPQDirectory; + + if (GetOpenFileName(&ofn)) + SetDlgItemText(hDlg, IDC_FILENAME, &FN[Port][0]); + + break; + + + case IDOK: + + GetDlgItemText(hDlg, IDC_UIDEST, &UIUIDEST[Port][0], 10); + + if (UIUIDigi[Port]) + { + free(UIUIDigi[Port]); + UIUIDigi[Port] = NULL; + } + + if (UIUIDigiAX[Port]) + { + free(UIUIDigiAX[Port]); + UIUIDigiAX[Port] = NULL; + } + + GetDlgItemText(hDlg, IDC_UIDIGIS, Digis, 99); + + UIUIDigi[Port] = _strdup(Digis); + + GetDlgItemText(hDlg, IDC_FILENAME, &FN[Port][0], 255); + GetDlgItemText(hDlg, IDC_MESSAGE, &Message[Port][0], 1000); + + Interval[Port] = GetDlgItemInt(hDlg, IDC_INTERVAL, &OK, FALSE); + + MinCounter[Port] = Interval[Port]; + + SendFromFile[Port] = IsDlgButtonChecked(hDlg, IDC_FROMFILE); + + wsprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\UIUtil\\UIPort%d", PortNum[CurrentPage]); + + retCode = RegCreateKeyEx(REGTREE, + Key, 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + retCode = RegSetValueEx(hKey, "UIDEST", 0, REG_SZ,(BYTE *)&UIUIDEST[Port][0], (int)strlen(&UIUIDEST[Port][0])); + retCode = RegSetValueEx(hKey, "FileName", 0, REG_SZ,(BYTE *)&FN[Port][0], (int)strlen(&FN[Port][0])); + retCode = RegSetValueEx(hKey, "Message", 0, REG_SZ,(BYTE *)&Message[Port][0], (int)strlen(&Message[Port][0])); + retCode = RegSetValueEx(hKey, "Interval", 0, REG_DWORD,(BYTE *)&Interval[Port], 4); + retCode = RegSetValueEx(hKey, "SendFromFile", 0, REG_DWORD,(BYTE *)&SendFromFile[Port], 4); + retCode = RegSetValueEx(hKey, "Digis",0, REG_SZ, Digis, (int)strlen(Digis)); + + RegCloseKey(hKey); + } + + SetupUI(Port); + + SaveUIConfig(); + + return (INT_PTR)TRUE; + + + case IDCANCEL: + + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + + case ID_TEST: + + SendUIBeacon(Port); + return TRUE; + + } + break; + + } + return (INT_PTR)FALSE; +} + + + +VOID WINAPI OnTabbedDialogInit(HWND hDlg) +{ + DLGHDR *pHdr = (DLGHDR *) LocalAlloc(LPTR, sizeof(DLGHDR)); + DWORD dwDlgBase = GetDialogBaseUnits(); + int cxMargin = LOWORD(dwDlgBase) / 4; + int cyMargin = HIWORD(dwDlgBase) / 8; + + TC_ITEM tie; + RECT rcTab; + + int i, pos, tab = 0; + INITCOMMONCONTROLSEX init; + + char PortNo[60]; + struct _EXTPORTDATA * PORTVEC; + + hwndDlg = hDlg; // Save Window Handle + + // Save a pointer to the DLGHDR structure. + +#define GWL_USERDATA (-21) + + SetWindowLong(hwndDlg, GWL_USERDATA, (LONG) pHdr); + + // Create the tab control. + + + init.dwICC = ICC_STANDARD_CLASSES; + init.dwSize=sizeof(init); + i=InitCommonControlsEx(&init); + + pHdr->hwndTab = CreateWindow(WC_TABCONTROL, "", WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, + 0, 0, 100, 100, hwndDlg, NULL, hInstance, NULL); + + if (pHdr->hwndTab == NULL) { + + // handle error + + } + + // Add a tab for each of the child dialog boxes. + + tie.mask = TCIF_TEXT | TCIF_IMAGE; + + tie.iImage = -1; + + for (i = 1; i <= NUMBEROFPORTS; i++) + { + // Only allow UI on ax.25 ports + + PORTVEC = (struct _EXTPORTDATA * )GetPortTableEntryFromSlot(i); + + if (PORTVEC->PORTCONTROL.PORTTYPE == 16) // EXTERNAL + if (PORTVEC->PORTCONTROL.PROTOCOL == 10) // Pactor/WINMOR + if (PORTVEC->PORTCONTROL.UICAPABLE == 0) + continue; + + wsprintf(PortNo, "Port %2d", GetPortNumber(i)); + PortNum[tab] = i; + + tie.pszText = PortNo; + TabCtrl_InsertItem(pHdr->hwndTab, tab, &tie); + + pHdr->apRes[tab++] = DoLockDlgRes("PORTPAGE"); + } + + PageCount = tab; + + // Determine the bounding rectangle for all child dialog boxes. + + SetRectEmpty(&rcTab); + + for (i = 0; i < PageCount; i++) + { + if (pHdr->apRes[i]->cx > rcTab.right) + rcTab.right = pHdr->apRes[i]->cx; + + if (pHdr->apRes[i]->cy > rcTab.bottom) + rcTab.bottom = pHdr->apRes[i]->cy; + + } + + MapDialogRect(hwndDlg, &rcTab); + +// rcTab.right = rcTab.right * LOWORD(dwDlgBase) / 4; + +// rcTab.bottom = rcTab.bottom * HIWORD(dwDlgBase) / 8; + + // Calculate how large to make the tab control, so + + // the display area can accomodate all the child dialog boxes. + + TabCtrl_AdjustRect(pHdr->hwndTab, TRUE, &rcTab); + + OffsetRect(&rcTab, cxMargin - rcTab.left, cyMargin - rcTab.top); + + // Calculate the display rectangle. + + CopyRect(&pHdr->rcDisplay, &rcTab); + + TabCtrl_AdjustRect(pHdr->hwndTab, FALSE, &pHdr->rcDisplay); + + // Set the size and position of the tab control, buttons, + + // and dialog box. + + SetWindowPos(pHdr->hwndTab, NULL, rcTab.left, rcTab.top, rcTab.right - rcTab.left, rcTab.bottom - rcTab.top, SWP_NOZORDER); + + // Move the Buttons to bottom of page + + pos=rcTab.left+cxMargin; + + + // Size the dialog box. + + SetWindowPos(hwndDlg, NULL, 0, 0, rcTab.right + cyMargin + 2 * GetSystemMetrics(SM_CXDLGFRAME), + rcTab.bottom + 2 * cyMargin + 2 * GetSystemMetrics(SM_CYDLGFRAME) + GetSystemMetrics(SM_CYCAPTION), + SWP_NOMOVE | SWP_NOZORDER); + + // Simulate selection of the first item. + + OnSelChanged(hwndDlg); + +} + +// DoLockDlgRes - loads and locks a dialog template resource. + +// Returns a pointer to the locked resource. + +// lpszResName - name of the resource + +DLGTEMPLATE * WINAPI DoLockDlgRes(LPCSTR lpszResName) +{ + HRSRC hrsrc = FindResource(hInstance, lpszResName, RT_DIALOG); + HGLOBAL hglb = LoadResource(hInstance, hrsrc); + + return (DLGTEMPLATE *) LockResource(hglb); +} + +//The following function processes the TCN_SELCHANGE notification message for the main dialog box. The function destroys the dialog box for the outgoing page, if any. Then it uses the CreateDialogIndirect function to create a modeless dialog box for the incoming page. + +// OnSelChanged - processes the TCN_SELCHANGE notification. + +// hwndDlg - handle of the parent dialog box + +VOID WINAPI OnSelChanged(HWND hwndDlg) +{ + char PortDesc[40]; + int Port; + + DLGHDR *pHdr = (DLGHDR *) GetWindowLong(hwndDlg, GWL_USERDATA); + + CurrentPage = TabCtrl_GetCurSel(pHdr->hwndTab); + + // Destroy the current child dialog box, if any. + + if (pHdr->hwndDisplay != NULL) + + DestroyWindow(pHdr->hwndDisplay); + + // Create the new child dialog box. + + pHdr->hwndDisplay = CreateDialogIndirect(hInstance, pHdr->apRes[CurrentPage], hwndDlg, ChildDialogProc); + + hwndDisplay = pHdr->hwndDisplay; // Save + + Port = PortNum[CurrentPage]; + // Fill in the controls + + GetPortDescription(PortNum[CurrentPage], PortDesc); + + SetDlgItemText(hwndDisplay, IDC_PORTNAME, PortDesc); + + CheckDlgButton(hwndDisplay, IDC_FROMFILE, SendFromFile[Port]); + + SetDlgItemInt(hwndDisplay, IDC_INTERVAL, Interval[Port], FALSE); + + SetDlgItemText(hwndDisplay, IDC_UIDEST, &UIUIDEST[Port][0]); + SetDlgItemText(hwndDisplay, IDC_UIDIGIS, UIUIDigi[Port]); + + + + SetDlgItemText(hwndDisplay, IDC_FILENAME, &FN[Port][0]); + SetDlgItemText(hwndDisplay, IDC_MESSAGE, &Message[Port][0]); + + ShowWindow(pHdr->hwndDisplay, SW_SHOWNORMAL); + +} + + +//The following function processes the WM_INITDIALOG message for each of the child dialog boxes. You cannot specify the position of a dialog box created using the CreateDialogIndirect function. This function uses the SetWindowPos function to position the child dialog within the tab control's display area. + +// OnChildDialogInit - Positions the child dialog box to fall + +// within the display area of the tab control. + +VOID WINAPI OnChildDialogInit(HWND hwndDlg) +{ + HWND hwndParent = GetParent(hwndDlg); + DLGHDR *pHdr = (DLGHDR *) GetWindowLong(hwndParent, GWL_USERDATA); + + SetWindowPos(hwndDlg, HWND_TOP, pHdr->rcDisplay.left, pHdr->rcDisplay.top, 0, 0, SWP_NOSIZE); +} + + + +LRESULT CALLBACK UIWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + HKEY hKey=0; + + switch (message) { + + case WM_INITDIALOG: + OnTabbedDialogInit(hWnd); + return (INT_PTR)TRUE; + + case WM_NOTIFY: + + switch (((LPNMHDR)lParam)->code) + { + case TCN_SELCHANGE: + OnSelChanged(hWnd); + return TRUE; + // More cases on WM_NOTIFY switch. + case NM_CHAR: + return TRUE; + } + + break; + + + case WM_CTLCOLORDLG: + return (LONG)bgBrush; + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + + return (LONG)bgBrush; + } + + case WM_COMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) { + + case IDOK: + + return TRUE; + + default: + + return 0; + } + + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case SC_RESTORE: + + return (DefWindowProc(hWnd, message, wParam, lParam)); + + case SC_MINIMIZE: + + if (MinimizetoTray) + return ShowWindow(hWnd, SW_HIDE); + else + return (DefWindowProc(hWnd, message, wParam, lParam)); + + break; + + default: + return (DefWindowProc(hWnd, message, wParam, lParam)); + } + + case WM_CLOSE: + return(DestroyWindow(hWnd)); + + default: + return (DefWindowProc(hWnd, message, wParam, lParam)); + + } + + return (0); +} + +#endif + +extern struct DATAMESSAGE * REPLYBUFFER; +char * __cdecl Cmdprintf(TRANSPORTENTRY * Session, char * Bufferptr, const char * format, ...); +void GetPortCTEXT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + char FN[250]; + FILE *hFile; + struct stat STAT; + struct PORTCONTROL * PORT = PORTTABLE; + char PortList[256] = ""; + + while (PORT) + { + if (PORT->CTEXT) + { + free(PORT->CTEXT); + PORT->CTEXT = 0; + } + + if (BPQDirectory[0] == 0) + sprintf(FN, "Port%dCTEXT.txt", PORT->PORTNUMBER); + else + sprintf(FN, "%s/Port%dCTEXT.txt", BPQDirectory, PORT->PORTNUMBER); + + if (stat(FN, &STAT) == -1) + { + PORT = PORT->PORTPOINTER; + continue; + } + + hFile = fopen(FN, "rb"); + + if (hFile) + { + char * ptr; + + PORT->CTEXT = zalloc(STAT.st_size + 1); + fread(PORT->CTEXT , 1, STAT.st_size, hFile); + fclose(hFile); + + // convert CRLF or LF to CR + + while (ptr = strstr(PORT->CTEXT, "\r\n")) + memmove(ptr, ptr + 1, strlen(ptr)); + + // Now has LF + + while (ptr = strchr(PORT->CTEXT, '\n')) + *ptr = '\r'; + + + sprintf(PortList, "%s,%d", PortList, PORT->PORTNUMBER); + } + + PORT = PORT->PORTPOINTER; + } + + if (Session) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "CTEXT Read for ports %s\r", &PortList[1]); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + } + else + Debugprintf("CTEXT Read for ports %s\r", &PortList[1]); +} + + + + + diff --git a/HFCommon-HPLaptop.c b/HFCommon-HPLaptop.c new file mode 100644 index 0000000..21b7376 --- /dev/null +++ b/HFCommon-HPLaptop.c @@ -0,0 +1,2154 @@ +/* +Copyright 2001-2022 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 +*/ + + +#pragma data_seg("_BPQDATA") + +#define _CRT_SECURE_NO_DEPRECATE + +#include + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + + +#include "kernelresource.h" +#include "CHeaders.h" +#include "tncinfo.h" +#ifndef LINBPQ +#include +#endif +//#include +#include "bpq32.h" +#include "adif.h" + +extern char * PortConfig[33]; + +HANDLE hInstance; +extern HBRUSH bgBrush; +extern HWND ClientWnd, FrameWnd; +extern int OffsetH, OffsetW; + +extern HMENU hMainFrameMenu; +extern HMENU hBaseMenu; +extern HANDLE hInstance; + +extern HKEY REGTREE; + +extern int Ver[]; + + +int KillTNC(struct TNCINFO * TNC); +int RestartTNC(struct TNCINFO * TNC); + +char * GetChallengeResponse(char * Call, char * ChallengeString); + +VOID __cdecl Debugprintf(const char * format, ...); +int FromLOC(char * Locator, double * pLat, double * pLon); +BOOL ToLOC(double Lat, double Lon , char * Locator); + +int GetPosnFromAPRS(char * Call, double * Lat, double * Lon); +char * stristr (char *ch1, char *ch2); + +static RECT Rect; + +extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + +#define WSA_ACCEPT WM_USER + 1 +#define WSA_DATA WM_USER + 2 +#define WSA_CONNECT WM_USER + 3 + +int Winmor_Socket_Data(int sock, int error, int eventcode); + +struct WL2KInfo * WL2KReports; + +int WL2KTimer = 0; + +int ModetoBaud[31] = {0,0,0,0,0,0,0,0,0,0,0, // 0 = 10 + 200,600,3200,600,3200,3200, // 11 - 16 + 0,0,0,0,0,0,0,0,0,0,0,0,0,600}; // 17 - 30 + +extern char HFCTEXT[]; +extern int HFCTEXTLEN; + + +extern char WL2KCall[10]; +extern char WL2KLoc[7]; + + +VOID MoveWindows(struct TNCINFO * TNC) +{ +#ifndef LINBPQ + RECT rcClient; + int ClientHeight, ClientWidth; + + GetClientRect(TNC->hDlg, &rcClient); + + ClientHeight = rcClient.bottom; + ClientWidth = rcClient.right; + + if (TNC->hMonitor) + MoveWindow(TNC->hMonitor,2 , TNC->RigControlRow + 3, ClientWidth-4, ClientHeight - (TNC->RigControlRow + 3), TRUE); +#endif +} + +char * Config; +static char * ptr1, * ptr2; + +#ifndef LINBPQ + +LRESULT CALLBACK PacWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + MINMAXINFO * mmi; + + int i; + struct TNCINFO * TNC; + + HKEY hKey; + char Key[80]; + int retCode, disp; + + for (i=0; i<41; i++) + { + TNC = TNCInfo[i]; + if (TNC == NULL) + continue; + + if (TNC->hDlg == hWnd) + break; + } + + if (TNC == NULL) + return DefMDIChildProc(hWnd, message, wParam, lParam); + + switch (message) { + + case WM_CREATE: + + break; + + case WM_PAINT: + +// hdc = BeginPaint (hWnd, &ps); + +// SelectObject( hdc, hFont) ; + +// EndPaint (hWnd, &ps); +// +// wParam = hdc; + + break; + + + case WM_GETMINMAXINFO: + + if (TNC->ClientHeight) + { + mmi = (MINMAXINFO *)lParam; + mmi->ptMaxSize.x = TNC->ClientWidth; + mmi->ptMaxSize.y = TNC->ClientHeight; + mmi->ptMaxTrackSize.x = TNC->ClientWidth; + mmi->ptMaxTrackSize.y = TNC->ClientHeight; + } + + break; + + + case WM_MDIACTIVATE: + { + + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + + if (TNC->hMenu) + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)TNC->hMenu, "Actions"); + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); + +// SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) TNC->hMenu, (LPARAM) TNC->hWndMenu); + } + else + { + // Deactivate + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + } + + // call DrawMenuBar after the menu items are set + DrawMenuBar(FrameWnd); + + return DefMDIChildProc(hWnd, message, wParam, lParam); + } + + + + case WM_INITMENUPOPUP: + + if (wParam == (WPARAM)TNC->hMenu) + { + if (TNC->ProgramPath) + { + if (strstr(TNC->ProgramPath, " TNC") || strstr(TNC->ProgramPath, "ARDOP") + || strstr(TNC->ProgramPath, "VARA") || stristr(TNC->ProgramPath, "FREEDATA")) + { + EnableMenuItem(TNC->hMenu, WINMOR_RESTART, MF_BYCOMMAND | MF_ENABLED); + EnableMenuItem(TNC->hMenu, WINMOR_KILL, MF_BYCOMMAND | MF_ENABLED); + + break; + } + } + EnableMenuItem(TNC->hMenu, WINMOR_RESTART, MF_BYCOMMAND | MF_GRAYED); + EnableMenuItem(TNC->hMenu, WINMOR_KILL, MF_BYCOMMAND | MF_GRAYED); + } + + break; + + case WM_COMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) + { + case WINMOR_KILL: + + TNC->DontRestart = TRUE; + KillTNC(TNC); + break; + + case WINMOR_RESTART: + + TNC->DontRestart = FALSE; + KillTNC(TNC); + RestartTNC(TNC); + break; + + case WINMOR_RESTARTAFTERFAILURE: + + TNC->RestartAfterFailure = !TNC->RestartAfterFailure; + CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\PACTOR\\PORT%d", TNC->Port); + + retCode = RegCreateKeyEx(REGTREE, Key, 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + RegSetValueEx(hKey,"TNC->RestartAfterFailure",0,REG_DWORD,(BYTE *)&TNC->RestartAfterFailure, 4); + RegCloseKey(hKey); + } + break; + + case ARDOP_ABORT: + + if (TNC->ForcedCloseProc) + TNC->ForcedCloseProc(TNC, 0); + + break; + } + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_SIZING: + case WM_SIZE: + + MoveWindows(TNC); + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) + { + + case SC_RESTORE: + + TNC->Minimized = FALSE; + break; + + case SC_MINIMIZE: + + TNC->Minimized = TRUE; + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_CTLCOLORDLG: + return (LONG)bgBrush; + + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + return (LONG)bgBrush; + } + + case WM_HSCROLL: + { + char value[16]; + + switch (LOWORD(wParam)) + { + case TB_ENDTRACK: + case TB_THUMBTRACK: + + TNC->TXOffset = SendMessage(TNC->xIDC_TXTUNE, TBM_GETPOS, 0, 0); + sprintf(value, "%d", TNC->TXOffset); + MySetWindowText(TNC->xIDC_TXTUNEVAL, value); + + break; + } + + default: + break; + } + case WM_DESTROY: + + break; + } + return DefMDIChildProc(hWnd, message, wParam, lParam); +} +#endif + +BOOL CreatePactorWindow(struct TNCINFO * TNC, char * ClassName, char * WindowTitle, int RigControlRow, WNDPROC WndProc, int Width, int Height, VOID ForcedCloseProc()) +{ +#ifdef LINBPQ + return FALSE; +#else + WNDCLASS wc; + char Title[80]; + int retCode, Type, Vallen; + HKEY hKey=0; + char Key[80]; + char Size[80]; + int Top, Left; + HANDLE hDlg = 0; + static int LP = 1235; + + if (TNC->hDlg) + { + ShowWindow(TNC->hDlg, SW_SHOWNORMAL); + SetForegroundWindow(TNC->hDlg); + return FALSE; // Already open + } + + wc.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + wc.lpfnWndProc = WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON) ); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + wc.lpszMenuName = NULL; + wc.lpszClassName = ClassName; + + RegisterClass(&wc); + +// if (TNC->Hardware == H_WINMOR || TNC->Hardware == H_TELNET ||TNC->Hardware == H_ARDOP || +// TNC->Hardware == H_V4 || TNC->Hardware == H_FLDIGI || TNC->Hardware == H_UIARQ || TNC->Hardware == H_VARA) + if (TNC->PortRecord) + sprintf(Title, "%s Status - Port %d %s", WindowTitle, TNC->Port, TNC->PortRecord->PORTCONTROL.PORTDESCRIPTION); + else + sprintf(Title, "Rigcontrol"); + + if (TNC->Hardware == H_MPSK) + sprintf(Title, "Rigcontrol for MultiPSK Port %d", TNC->Port); + + TNC->hDlg = hDlg = CreateMDIWindow(ClassName, Title, 0, + 0, 0, Width, Height, ClientWnd, hInstance, ++LP); + + // CreateDialog(hInstance,ClassName,0,NULL); + + Rect.top = 100; + Rect.left = 20; + Rect.right = Width + 20; + Rect.bottom = Height + 100; + + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\PACTOR\\PORT%d", TNC->Port); + + retCode = RegOpenKeyEx (REGTREE, Key, 0, KEY_QUERY_VALUE, &hKey); + + if (retCode == ERROR_SUCCESS) + { + Vallen=80; + + retCode = RegQueryValueEx(hKey,"Size",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + if (retCode == ERROR_SUCCESS) + { + sscanf(Size,"%d,%d,%d,%d,%d",&Rect.left,&Rect.right,&Rect.top,&Rect.bottom, &TNC->Minimized); + + if (Rect.top < - 500 || Rect.left < - 500) + { + Rect.left = 0; + Rect.top = 0; + Rect.right = 600; + Rect.bottom = 400; + } + + if (Rect.top < OffsetH) + { + int Error = OffsetH - Rect.top; + Rect.top += Error; + Rect.bottom += Error; + } + } + + if (TNC->Hardware == H_WINMOR || TNC->Hardware == H_ARDOP|| TNC->Hardware == H_VARA) + retCode = RegQueryValueEx(hKey,"TNC->RestartAfterFailure",0, + (ULONG *)&Type,(UCHAR *)&TNC->RestartAfterFailure,(ULONG *)&Vallen); + + RegCloseKey(hKey); + } + + Top = Rect.top; + Left = Rect.left; + +// GetWindowRect(hDlg, &Rect); // Get the real size + + MoveWindow(hDlg, Left - (OffsetW /2), Top - OffsetH, Rect.right - Rect.left, Rect.bottom - Rect.top, TRUE); + + if (TNC->Minimized) + ShowWindow(hDlg, SW_SHOWMINIMIZED); + else + ShowWindow(hDlg, SW_RESTORE); + + TNC->RigControlRow = RigControlRow; + + SetWindowText(TNC->xIDC_TNCSTATE, "Free"); + + TNC->ForcedCloseProc = ForcedCloseProc; + + return TRUE; +#endif +} + + +// WL2K Reporting Code. + +static SOCKADDR_IN sinx; + + +VOID SendReporttoWL2KThread(void * unused); +VOID SendHTTPReporttoWL2KThread(void * unused); + +VOID CheckWL2KReportTimer() +{ + if (WL2KReports == NULL) + return; // Shouldn't happen! + + WL2KTimer--; + + if (WL2KTimer != 0) + return; + +#ifdef WIN32 + WL2KTimer = 2 * 32910; // Every 2 Hours - PC Tick is a bit slow +#else + WL2KTimer = 2 * 36000; // Every 2 Hours +#endif + + if (CheckAppl(NULL, "RMS ") == NULL) + if (CheckAppl(NULL, "RELAY ") == NULL) + return; + + _beginthread(SendHTTPReporttoWL2KThread, 0, 0); + + return; +} + +static char HeaderTemplate[] = "POST %s HTTP/1.1\r\n" + "Accept: application/json\r\n" +// "Accept-Encoding: gzip,deflate,gzip, deflate\r\n" + "Content-Type: application/json\r\n" + "Host: %s:%d\r\n" + "Content-Length: %d\r\n" + //r\nUser-Agent: BPQ32(G8BPQ)\r\n" +// "Expect: 100-continue\r\n" + "\r\n{%s}"; + +char Missing[] = "** Missing **"; + +VOID GetJSONValue(char * _REPLYBUFFER, char * Name, char * Value) +{ + char * ptr1, * ptr2; + + strcpy(Value, Missing); + + ptr1 = strstr(_REPLYBUFFER, Name); + + if (ptr1 == 0) + return; + + ptr1 += (strlen(Name) + 1); + + ptr2 = strchr(ptr1, '"'); + + if (ptr2) + { + size_t ValLen = ptr2 - ptr1; + memcpy(Value, ptr1, ValLen); + Value[ValLen] = 0; + } + + return; +} + + +// Send Winlink Session Record + +extern char LOC[7]; +extern char TextVerstring[50]; + +double Distance(double laa, double loa, double lah, double loh, BOOL KM); +double Bearing(double lat2, double lon2, double lat1, double lon1); +VOID SendHTTPRequest(SOCKET sock, char * Request, char * Params, int Len, char * Return); +SOCKET OpenWL2KHTTPSock(); + + + +struct WL2KMode +{ + int Mode; + char * WL2KString; + char * ADIFString; + char * BPQString; +}; + +struct WL2KMode WL2KModeList[] = +{ + {0,"Packet 1200"}, + {1,"Packet 2400"}, + {2, "Packet 4800"}, + {3, "Packet 9600"}, + {4, "Packet 19200"}, + {5, "Packet 38400"}, + {11, "Pactor 1"}, + {12, "Pactor 1,2"}, + {13, "Pactor 1,2,3"}, + {14, "Pactor 2"}, + {15, "Pactor 2,3"}, + {16, "Pactor 3"}, + {17, "Pactor 1,2,3,4"}, + {18, "Pactor 2,3,4"}, + {19, "Pactor 3,4"}, + {20, "Pactor 4"}, + {21, "WINMOR 500"}, + {22, "WINMOR 1600"}, + {30, "Robust Packet"}, + {40, "ARDOP 200"}, + {41, "ARDOP 500"}, + {42, "ARDOP 1000"}, + {43, "ARDOP 2000"}, + {44, "ARDOP 2000 FM"}, + {50, "VARA"}, + {51, "VARA FM"}, + {52, "VARA FM WIDE"}, + {53, "VARA 500"} +}; + +char WL2KModes [55][18] = { + "Packet 1200", "Packet 2400", "Packet 4800", "Packet 9600", "Packet 19200", "Packet 38400", "High Speed Packet", "", "", "", "", + "Pactor 1", "Pactor", "Pactor", "Pactor 2", "Pactor", "Pactor 3", "Pactor", "Pactor", "Pactor", "Pactor 4", // 11 - 20 + "Winmor 500", "Winmor 1600", "", "", "", "", "", "", "", // 21 - 29 + "Robust Packet", "", "", "", "", "", "", "", "", "", // 30 - 39 + "ARDOP 200", "ARDOP 500", "ARDOP 1000", "ARDOP 2000", "ARDOP 2000 FM", "", "", "", "", "", // 40 - 49 + "VARA", "VARA FM", "VARA FM WIDE", "VARA 500", "VARA 2750"}; + + +VOID SendWL2KSessionRecordThread(void * param) +{ + SOCKET sock; + char Message[512]; + + strcpy(Message, param); + free(param); + + Debugprintf("Sending %s", Message); + + sock = OpenWL2KHTTPSock(); + + if (sock) + { + SendHTTPRequest(sock, "/session/add", (char *)Message, (int)strlen(Message), NULL); + closesocket(sock); + } + + return; +} + +VOID SendWL2KRegisterHybridThread(void * param) +{ + SOCKET sock; + char Message[512]; + + strcpy(Message, param); + free(param); + + Debugprintf("Sending %s", Message); + + sock = OpenWL2KHTTPSock(); + + if (sock) + { + SendHTTPRequest(sock, "/radioNetwork/params/add", (char *)Message, (int)strlen(Message), NULL); + closesocket(sock); + } + + return; +} + +VOID SendWL2KRegisterHybrid(struct TNCINFO * TNC) +{ + char Message[512]; + char Date[80] ; + int Len; + struct TCPINFO * TCP = TNC->TCPInfo; + time_t T; + struct tm * tm; + char Call[10]; + + if (TCP == NULL || TCP->GatewayLoc[0] == 0) + return; + + strcpy(Call, TCP->GatewayCall); + strlop(Call, '-'); + + T = time(NULL); + tm = gmtime(&T); + + //2021-10-31-14=35=29 + + sprintf(Date, "%04d-%02d-%02d-%02d:%02d:%02d", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + +// "Callsign":"String","Password":"String","Param":"String","Value":"String","Key":"String" + + Len = sprintf(Message, "\"Callsign\":\"%s\",\"Password\":\"%s\",\"Param\":\"RMSRelayVersion\",\"Value\":\"%s|%s|*HARMNNNN|%s|%s|\"", + Call, TCP->SecureCMSPassword, Date, "3.1.11.2", + TCP->HybridServiceCode, TCP->GatewayLoc); + + SendWL2KRegisterHybridThread(_strdup(Message)); + + Len = sprintf(Message, "\"Callsign\":\"%s\",\"Password\":\"%s\",\"Param\":\"CoLocatedRMS\",\"Value\":\"%s\"", + Call, TCP->SecureCMSPassword, TCP->HybridCoLocatedRMS); + + SendWL2KRegisterHybridThread(_strdup(Message)); + + Len = sprintf(Message, "\"Callsign\":\"%s\",\"Password\":\"%s\",\"Param\":\"AllowFreq\",\"Value\":\"%s\"", + Call, TCP->SecureCMSPassword, TCP->HybridFrequencies); + + SendWL2KRegisterHybridThread(_strdup(Message)); + + return; +} + +BOOL NoSessionAccount = FALSE; +BOOL SessionAccountChecked = FALSE; + +BOOL SendWL2KSessionRecord(ADIF * ADIF, int BytesSent, int BytesReceived) +{ +/* +The API is /session/add https://api.winlink.org/json/metadata?op=SessionAdd + +The important parameters are (others can be omitted): + +Application (gateway program name) +Server (gateway callsign) +ServerGrid +Client (client callsign) +ClientGrid +Mode (Pactor, winmor, vara, etc) +Frequency +MessagesSent +MessagesReceived +BytesSent +BytesReceived +HoldingSeconds (duration of connection) +IdTag (random alphanumeric, 12 chars) + +"Application":"RMS Trimode", +"Version":"1.3.25.0", +"Cms":"CMS-A", +"Server":"AB4NX", +"ServerGrid":"EM73WT", +"Client":"VE2SCA","ClientGrid":"", +"Sid":"","Mode":"WINMOR16", +"Frequency":10145000, +"Kilometers":0, +"Degrees":0, +"LastCommand":">", +"MessagesSent":0, +"MessagesReceived":0, +"BytesSent":179, +"BytesReceived":0, +"HoldingSeconds":126, +"IdTag":"ATK9S3QGL2E1"} +*/ + time_t T; + + char Message[4096] = ""; + char * MessagePtr; + int MessageLen; + int Dist = 0; + int intBearing = 0; + + double Lat, Lon; + double myLat, myLon; + + char Tag[32]; + + SOCKET sock; + char Response[1024]; + int Len; + + // Only report if the CMSCall has a WL2KAccount + + if (NoSessionAccount) + return TRUE; + + if (!SessionAccountChecked) + { + // only check once + + sock = OpenWL2KHTTPSock(); + + if (sock) + { + SessionAccountChecked = TRUE; + + Len = sprintf(Message, "\"Callsign\":\"%s\"", ADIF->CMSCall); + + SendHTTPRequest(sock, "/account/exists", Message, Len, Response); + closesocket(sock); + + if (strstr(Response, "false")) + { + WritetoConsole("WL2K Traffic Reporting disabled - Gateway "); + WritetoConsole(ADIF->CMSCall); + WritetoConsole(" does not have a Winlink Account\r\n"); + Debugprintf("WL2K Traffic Reporting disabled - Gateway %s does not have a Winlink Account", ADIF->CMSCall); + NoSessionAccount = TRUE; + return TRUE; + } + } + } + + if (ADIF == NULL || ADIF->LOC[0] == 0 || ADIF->Call[0] == 0) + return TRUE; + + if (ADIF->StartTime == 0 || ADIF->ServerSID[0] == 0 || ADIF->CMSCall[0] == 0) + return TRUE; + + T = time(NULL); + + // Extract Info we need + + // Distance and Bearing + + if (LOC[0] && ADIF->LOC[0]) + { + if (FromLOC(LOC, &myLat, &myLon) == 0) // Basic checks on LOCs + return TRUE; + if (FromLOC(ADIF->LOC, &Lat, &Lon) == 0) + return TRUE; + + Dist = (int)Distance(myLat, myLon, Lat, Lon, TRUE); + intBearing = (int)Bearing(Lat, Lon, myLat, myLon); + } + + MessageLen = sprintf(Message, "\"Application\":\"%s\",", "BPQ32"); + MessageLen += sprintf(&Message[MessageLen], "\"Version\":\"%s\",", TextVerstring); + MessageLen += sprintf(&Message[MessageLen], "\"Cms\":\"%s\",", "CMS"); + MessageLen += sprintf(&Message[MessageLen], "\"Server\":\"%s\",", ADIF->CMSCall); + MessageLen += sprintf(&Message[MessageLen], "\"ServerGrid\":\"%s\",", LOC); + MessageLen += sprintf(&Message[MessageLen], "\"Client\":\"%s\",", ADIF->Call); + MessageLen += sprintf(&Message[MessageLen], "\"ClientGrid\":\"%s\",", ADIF->LOC); + MessageLen += sprintf(&Message[MessageLen], "\"Sid\":\"%s\",", ADIF->UserSID); + MessageLen += sprintf(&Message[MessageLen], "\"Mode\":\"%s\",", WL2KModes[ADIF->Mode]); + MessageLen += sprintf(&Message[MessageLen], "\"Frequency\":%lld,", ADIF->Freq); + MessageLen += sprintf(&Message[MessageLen], "\"Kilometers\":%d,", Dist); + MessageLen += sprintf(&Message[MessageLen], "\"Degrees\":%d,", intBearing); + MessageLen += sprintf(&Message[MessageLen], "\"LastCommand\":\"%s\",", ADIF->Termination); + MessageLen += sprintf(&Message[MessageLen], "\"MessagesSent\":%d,", ADIF->Sent); + MessageLen += sprintf(&Message[MessageLen], "\"MessagesReceived\":%d,", ADIF->Received); + MessageLen += sprintf(&Message[MessageLen], "\"BytesSent\":%d,", BytesSent); + MessageLen += sprintf(&Message[MessageLen], "\"BytesReceived\":%d,", BytesReceived); + MessageLen += sprintf(&Message[MessageLen], "\"HoldingSeconds\":%d,", (int)(T - ADIF->StartTime)); + sprintf(Tag, "%012X", (int)T * (rand() + 1)); + MessageLen += sprintf(&Message[MessageLen], "\"IdTag\":\"%s\"", Tag); + + MessagePtr = _strdup(Message); + _beginthread(SendWL2KSessionRecordThread, 0, (void *)MessagePtr); + + return TRUE; +} + +char APIKey[] = ",\"Key\":\"0D0C7AD6B38C45A7A9534E67111C38A7\""; + + +VOID SendHTTPRequest(SOCKET sock, char * Request, char * Params, int Len, char * Return) +{ + int InputLen = 0; + int inptr = 0; + char Buffer[2048]; + char Header[2048]; + char * ptr, * ptr1; + int Sent; + + strcat(Params, APIKey); + Len += (int)strlen(APIKey); + + sprintf(Header, HeaderTemplate, Request, "api.winlink.org", 80, Len + 2, Params); + Sent = send(sock, Header, (int)strlen(Header), 0); + + if (Sent == -1) + { + int Err = WSAGetLastError(); + Debugprintf("Error %d from WL2K Update send()", Err); + return; + } + + while (InputLen != -1) + { + InputLen = recv(sock, &Buffer[inptr], 2048 - inptr, 0); + + if (InputLen == -1 || InputLen == 0) + { + int Err = WSAGetLastError(); + Debugprintf("Error %d from WL2K Update recv()", Err); + return; + } + + // As we are using a persistant connection, can't look for close. Check + // for complete message + + inptr += InputLen; + + Buffer[inptr] = 0; + + ptr = strstr(Buffer, "\r\n\r\n"); + + if (ptr) + { + // got header + + int Hddrlen = (int)(ptr - Buffer); + + ptr1 = strstr(Buffer, "Content-Length:"); + + if (ptr1) + { + // Have content length + + int ContentLen = atoi(ptr1 + 16); + + if (ContentLen + Hddrlen + 4 == inptr) + { + // got whole response + + if (strstr(Buffer, " 200 OK")) + { + if (Return) + { + memcpy(Return, ptr + 4, ContentLen); + Return[ContentLen] = 0; + } + else + Debugprintf("WL2K Database update ok"); + + } + else + { + strlop(Buffer, 13); + Debugprintf("WL2K Update Params - %s", Params); + Debugprintf("WL2K Update failed - %s", Buffer); + } + return; + } + } + else + { + ptr1 = strstr(_strlwr(Buffer), "transfer-encoding:"); + + if (ptr1) + { + // Just accept anything until I've sorted things with Lee + Debugprintf("%s", ptr1); + Debugprintf("WL2K Database update ok"); + return; + } + } + } + } +} + +BOOL WL2KAccountChecked = FALSE; +BOOL NoWL2KAccount = FALSE; + +VOID SendHTTPReporttoWL2KThread(void * unused) +{ + // Uses HTTP/JSON Interface + + struct WL2KInfo * WL2KReport = WL2KReports; + char * LastHost = NULL; + char * LastRMSCall = NULL; + char Message[512]; + int LastSocket = 0; + SOCKET sock = 0; + struct sockaddr_in destaddr; + int addrlen=sizeof(sinx); + struct hostent * HostEnt; + int err; + u_long param=1; + BOOL bcopt=TRUE; + int Len; + + // Send all reports in list + + char Response[1024]; + + // Only report if the CMSCall has a WL2KAccount + + if (NoWL2KAccount) + return; + + if (!WL2KAccountChecked) + { + // only check once + + sock = OpenWL2KHTTPSock(); + + if (sock) + { + WL2KAccountChecked = TRUE; + + Len = sprintf(Message, "\"Callsign\":\"%s\"", + WL2KReport->BaseCall); + + SendHTTPRequest(sock, "/account/exists", Message, Len, Response); + closesocket(sock); + + if (strstr(Response, "false")) + { + WritetoConsole("WL2K Reporting disabled - Gateway "); + WritetoConsole(WL2KReport->BaseCall); + WritetoConsole(" does not have a Winlink Account\r\n"); + NoWL2KAccount = TRUE; + return; + } + } + } + + while (WL2KReport) + { + // Resolve Name if needed + + if (LastHost && strcmp(LastHost, WL2KReport->Host) == 0) // Same host? + goto SameHost; + + // New Host - Connect to it + + LastHost = WL2KReport->Host; + + destaddr.sin_family = AF_INET; + destaddr.sin_addr.s_addr = inet_addr(WL2KReport->Host); + destaddr.sin_port = htons(WL2KReport->WL2KPort); + + if (destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + Debugprintf("Resolving %s", WL2KReport->Host); + HostEnt = gethostbyname (WL2KReport->Host); + + if (!HostEnt) + { + err = WSAGetLastError(); + + Debugprintf("Resolve Failed for %s %d %x", WL2KReport->Host, err, err); + return; // Resolve failed + } + + memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + + // Allocate a Socket entry + + if (sock) + closesocket(sock); + + sock = socket(AF_INET, SOCK_STREAM, 0); + + if (sock == INVALID_SOCKET) + return; + +// ioctlsocket(sock, FIONBIO, ¶m); + + setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char FAR *)&bcopt, 4); + + destaddr.sin_family = AF_INET; + + if (sock == INVALID_SOCKET) + { + sock = 0; + return; + } + + setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); + + // Connect to Host + + if (connect(sock,(LPSOCKADDR) &destaddr, sizeof(destaddr)) != 0) + { + err=WSAGetLastError(); + + // + // Connect failed + // + + Debugprintf("Connect Failed"); + closesocket(sock); + sock = 0; + break; + } + + SameHost: + + Len = sprintf(Message, + "\"Callsign\":\"%s\"," + "\"BaseCallsign\":\"%s\"," + "\"GridSquare\":\"%s\"," + "\"Frequency\":%lld," + "\"Mode\":%d," + "\"Baud\":%d," + "\"Power\":%d," + "\"Height\":%d," + "\"Gain\":%d," + "\"Direction\":%d," + "\"Hours\":\"%s\"," + "\"ServiceCode\":\"%s\"", + + WL2KReport->RMSCall, WL2KReport->BaseCall, WL2KReport->GridSquare, + WL2KReport->Freq, WL2KReport->mode, WL2KReport->baud, WL2KReport->power, + WL2KReport->height, WL2KReport->gain, WL2KReport->direction, + WL2KReport->Times, WL2KReport->ServiceCode); + + Debugprintf("Sending %s", Message); + + SendHTTPRequest(sock, "/channel/add", Message, Len, NULL); + + + // Send Version Message + + + if (LastRMSCall == NULL || strcmp(WL2KReport->RMSCall, LastRMSCall) != 0) + { + int Len; + + LastRMSCall = WL2KReport->RMSCall; + + // "Callsign":"String","Program":"String","Version":"String","Comments":"String" + + Len = sprintf(Message, "\"Callsign\":\"%s\",\"Program\":\"BPQ32\"," + "\"Version\":\"%d.%d.%d.%d\",\"Comments\":\"Test Comment\"", + WL2KReport->RMSCall, Ver[0], Ver[1], Ver[2], Ver[3]); + + Debugprintf("Sending %s", Message); + + SendHTTPRequest(sock, "/version/add", Message, Len, NULL); + } + + WL2KReport = WL2KReport->Next; + } + + Sleep(100); + closesocket(sock); + sock = 0; + +} + +struct WL2KInfo * DecodeWL2KReportLine(char * buf) +{ + //06'', '', '', , , , , + // , , , '', , '' + + // WL2KREPORT service, api.winlink.org, 80, GM8BPQ, IO68VL, 00-23, 144800000, PKT1200, 10, 20, 5, 0, BPQTEST + + char * Context; + char * p_cmd; + char * param; + char errbuf[256]; + struct WL2KInfo * WL2KReport = zalloc(sizeof(struct WL2KInfo)); + char * ptr; + char Param[8][256]; + char * ptr1, * ptr2; + int n = 0; + + memset(Param, 0, 2048); + + strcpy(errbuf, buf); + + p_cmd = strtok_s(&buf[10], ", \t\n\r", &Context); + if (p_cmd == NULL) goto BadLine; + + strcpy(WL2KReport->ServiceCode, p_cmd); + + // Can default Host and Port, so cant use strtok for them + + ptr1 = Context; + + while (ptr1 && *ptr1 && n < 2) + { + while(ptr1 && *ptr1 && *ptr1 == ' ') + ptr1++; + + ptr2 = strchr(ptr1, ','); + if (ptr2) *ptr2++ = 0; + + strcpy(&Param[n][0], ptr1); + strlop(Param[n++], ' '); + ptr1 = ptr2; + + } + + if (n < 2) + goto BadLine; + + if (Param[1][0] == 0) + WL2KReport->WL2KPort = 80; // HTTP Interface + else + WL2KReport->WL2KPort = atoi(&Param[1][0]); + + if (Param[0][0] == 0) + WL2KReport->Host = _strdup("api.winlink.org"); + else + { + _strlwr(&Param[0][0]); + + if (strstr(&Param[0][0], "winlink.org")) + { + WL2KReport->WL2KPort = 80; // HTTP Interface + WL2KReport->Host = _strdup("api.winlink.org"); + } + else + WL2KReport->Host = _strdup(&Param[0][0]); + } + + Context = ptr1; + + p_cmd = strtok_s(NULL, ", \t\n\r", &Context); + if (p_cmd == NULL) goto BadLine; + + if (WL2KReport->WL2KPort == 0) goto BadLine; + + strcpy(WL2KReport->RMSCall, p_cmd); + strcpy(WL2KReport->BaseCall, p_cmd); + strlop(WL2KReport->BaseCall, '-'); // Remove any SSID + + strcpy(WL2KCall, WL2KReport->BaseCall); // For SYSOP Update + + p_cmd = strtok_s(NULL, " ,\t\n\r", &Context); + if (p_cmd == NULL) goto BadLine; + if (strlen(p_cmd) != 6) goto BadLine; + + strcpy(WL2KReport->GridSquare, p_cmd); + strcpy(WL2KLoc, p_cmd); + + p_cmd = strtok_s(NULL, " ,\t\n\r", &Context); + if (p_cmd == NULL) goto BadLine; + if (strlen(p_cmd) > 79) goto BadLine; + + // Convert any : in times to comma + + ptr = strchr(p_cmd, ':'); + + while (ptr) + { + *ptr = ','; + ptr = strchr(p_cmd, ':'); + } + + strcpy(WL2KReport->Times, p_cmd); + + p_cmd = strtok_s(NULL, " ,\t\n\r", &Context); + if (p_cmd == NULL) goto BadLine; + + WL2KReport->Freq = strtoll(p_cmd, NULL, 10); + + if (WL2KReport->Freq == 0) // Invalid + goto BadLine; + + param = strtok_s(NULL, " ,\t\n\r", &Context); + + // Mode Designator - one of + + // PKTnnnnnn + // WINMOR500 + // WINMOR1600 + // ROBUST + // P1 P12 P123 P1234 etc + + if (memcmp(param, "PKT", 3) == 0) + { + int Speed, Mode; + + Speed = atoi(¶m[3]); + + WL2KReport->baud = Speed; + + if (Speed <= 1200) + Mode = 0; // 1200 + else if (Speed <= 2400) + Mode = 1; // 2400 + else if (Speed <= 4800) + Mode = 2; // 4800 + else if (Speed <= 9600) + Mode = 3; // 9600 + else if (Speed <= 19200) + Mode = 4; // 19200 + else if (Speed <= 38400) + Mode = 5; // 38400 + else + Mode = 6; // >38400 + + WL2KReport->mode = Mode; + } + else if (_stricmp(param, "WINMOR500") == 0) + WL2KReport->mode = 21; + else if (_stricmp(param, "WINMOR1600") == 0) + WL2KReport->mode = 22; + else if (_stricmp(param, "ROBUST") == 0) + { + WL2KReport->mode = 30; + WL2KReport->baud = 600; + } + else if (_stricmp(param, "ARDOP200") == 0) + WL2KReport->mode = 40; + else if (_stricmp(param, "ARDOP500") == 0) + WL2KReport->mode = 41; + else if (_stricmp(param, "ARDOP1000") == 0) + WL2KReport->mode = 42; + else if (_stricmp(param, "ARDOP2000") == 0) + WL2KReport->mode = 43; + else if (_stricmp(param, "ARDOP2000FM") == 0) + WL2KReport->mode = 44; + else if (_stricmp(param, "P1") == 0) + WL2KReport->mode = 11; + else if (_stricmp(param, "P12") == 0) + WL2KReport->mode = 12; + else if (_stricmp(param, "P123") == 0) + WL2KReport->mode = 13; + else if (_stricmp(param, "P2") == 0) + WL2KReport->mode = 14; + else if (_stricmp(param, "P23") == 0) + WL2KReport->mode = 15; + else if (_stricmp(param, "P3") == 0) + WL2KReport->mode = 16; + else if (_stricmp(param, "P1234") == 0) + WL2KReport->mode = 17; + else if (_stricmp(param, "P234") == 0) + WL2KReport->mode = 18; + else if (_stricmp(param, "P34") == 0) + WL2KReport->mode = 19; + else if (_stricmp(param, "P4") == 0) + WL2KReport->mode = 20; + else if (_stricmp(param, "VARA") == 0) + WL2KReport->mode = 50; + else if (_stricmp(param, "VARA2300") == 0) + WL2KReport->mode = 50; + else if (_stricmp(param, "VARAFM") == 0) + WL2KReport->mode = 51; + else if (_stricmp(param, "VARAFM12") == 0) + WL2KReport->mode = 51; + else if (_stricmp(param, "VARAFM96") == 0) + WL2KReport->mode = 52; + else if (_stricmp(param, "VARA500") == 0) + WL2KReport->mode = 53; + else if (_stricmp(param, "VARA2750") == 0) + WL2KReport->mode = 54; + else + goto BadLine; + + param = strtok_s(NULL, " ,\t\n\r", &Context); + + // Optional Params + + WL2KReport->power = (param)? atoi(param) : 0; + param = strtok_s(NULL, " ,\t\n\r", &Context); + WL2KReport->height = (param)? atoi(param) : 0; + param = strtok_s(NULL, " ,\t\n\r", &Context); + WL2KReport->gain = (param)? atoi(param) : 0; + param = strtok_s(NULL, " ,\t\n\r", &Context); + WL2KReport->direction = (param)? atoi(param) : 0; + + WL2KTimer = 60; + + WL2KReport->Next = WL2KReports; + WL2KReports = WL2KReport; + + return WL2KReport; + +BadLine: + + WritetoConsole(" Bad config record "); + WritetoConsole(errbuf); + WritetoConsole("\r\n"); + + return 0; +} + +VOID UpdateMHSupport(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction, char * Loc, BOOL Report, BOOL Digis); + +VOID UpdateMHwithDigis(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction) +{ + UpdateMHSupport(TNC, Call, Mode, Direction, NULL, TRUE, TRUE); +} +VOID UpdateMHEx(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction, char * LOC, BOOL Report) +{ + UpdateMHSupport(TNC, Call, Mode, Direction, LOC, Report, FALSE); +} + +VOID UpdateMH(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction) +{ + UpdateMHSupport(TNC, Call, Mode, Direction, NULL, TRUE, FALSE); +} + +VOID UpdateMHSupport(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction, char * Loc, BOOL Report, BOOL Digis) +{ + PMHSTRUC MH = TNC->PortRecord->PORTCONTROL.PORTMHEARD; + PMHSTRUC MHBASE = MH; + UCHAR AXCall[72] = ""; + int i; + char * LOC, * LOCEND; + char ReportMode[20]; + char NoLOC[7] = ""; + double Freq; + char ReportFreq[350] = ""; + int OldCount = 0; + char ReportCall[16]; + + if (MH == 0) return; + + if (Digis) + { + // Call is an ax.25 digi string not a text call + + memcpy(AXCall, Call, 7 * 9); + ReportCall[ConvFromAX25(Call, ReportCall)] = 0; + + // if this is a UI frame with a locator or APRS position + // we could derive a position from it + + } + else + { + strcpy(ReportCall, Call); + ConvToAX25(Call, AXCall); + AXCall[6] |= 1; // Set End of address + } + + // Adjust freq to centre + +// if (Mode != ' ' && TNC->RIG->Valchar[0]) + if (TNC->RIG->Valchar[0]) + { + if (TNC->Hardware == H_UZ7HO) + { + // See if we have Center Freq Info + if (TNC->AGWInfo->CenterFreq) + { + Freq = atof(TNC->RIG->Valchar) + ((TNC->AGWInfo->CenterFreq * 1.0) / 1000000.0); + } +#ifdef WIN32 + else if (TNC->AGWInfo->hFreq) + { + char Centre[16]; + double ModemFreq; + + SendMessage(TNC->AGWInfo->hFreq, WM_GETTEXT, 15, (LPARAM)Centre); + + ModemFreq = atof(Centre); + + Freq = atof(TNC->RIG->Valchar) + (ModemFreq / 1000000); + } +#endif + else + Freq = atof(TNC->RIG->Valchar) + 0.0015; // Assume 1500 + } + else + + // Not UZ7HO or Linux + + Freq = atof(TNC->RIG->Valchar) + 0.0015; + + _gcvt(Freq, 9, ReportFreq); + } + + if (TNC->Hardware == H_ARDOP) + { + LOC = memchr(Call, '[', 20); + + if (LOC) + { + LOCEND = memchr(Call, ']', 30); + if (LOCEND) + { + LOC--; + *(LOC++) = 0; + *(LOCEND) = 0; + LOC++; + if (strlen(LOC) != 6 && strlen(LOC) != 0) + { + Debugprintf("Corrupt LOC %s %s", Call, LOC); + LOC = NoLOC; + } + goto NOLOC; + } + } + } + + else if (TNC->Hardware != H_WINMOR) // Only WINMOR has a locator + { + LOC = NoLOC; + goto NOLOC; + } + + LOC = memchr(Call, '(', 20); + + if (LOC) + { + LOCEND = memchr(Call, ')', 30); + if (LOCEND) + { + LOC--; + *(LOC++) = 0; + *(LOCEND) = 0; + LOC++; + if (strlen(LOC) != 6 && strlen(LOC) != 0) + { + Debugprintf("Corrupt LOC %s %s", Call, LOC); + LOC = NoLOC; + } + } + } + else + LOC = NoLOC; + +NOLOC: + + if (Loc) + LOC = Loc; // Supplied Locator overrides + + for (i = 0; i < MHENTRIES; i++) + { + if (Mode == ' ' || Mode == '*') // Packet + { + if ((MH->MHCALL[0] == 0) || ((memcmp(AXCall, MH->MHCALL, 7) == 0) && MH->MHDIGI == Mode)) // Spare or our entry + { + OldCount = MH->MHCOUNT; + goto DoMove; + } + } + else + { + if ((MH->MHCALL[0] == 0) || ((memcmp(AXCall, MH->MHCALL, 7) == 0) && + MH->MHDIGI == Mode && strcmp(MH->MHFreq, ReportFreq) == 0)) // Spare or our entry + { + OldCount = MH->MHCOUNT; + goto DoMove; + } + } + MH++; + } + + // TABLE FULL AND ENTRY NOT FOUND - MOVE DOWN ONE, AND ADD TO TOP + + i = MHENTRIES - 1; + + // Move others down and add at front +DoMove: + + if (i != 0) // First + memmove(MHBASE + 1, MHBASE, i * sizeof(MHSTRUC)); + +// memcpy (MHBASE->MHCALL, Buffer->ORIGIN, 7 * 9); + memcpy (MHBASE->MHCALL, AXCall, 7 * 9); // Save Digis + MHBASE->MHDIGI = Mode; + MHBASE->MHTIME = time(NULL); + MHBASE->MHCOUNT = ++OldCount; + + memcpy(MHBASE->MHLocator, LOC, 6); + strcpy(MHBASE->MHFreq, ReportFreq); + + // Report to NodeMap + + if (Report == FALSE) + return; + + if (Mode == '*') + return; // Digi'ed Packet + + if (Mode == ' ') // Packet Data + { + if (TNC->PktUpdateMap == 1) + Mode = '!'; + else + return; + } + + ReportMode[0] = TNC->Hardware + '@'; + ReportMode[1] = Mode; + if (TNC->Hardware == H_HAL) + ReportMode[2] = TNC->CurrentMode; + else + ReportMode[2] = (TNC->RIG->CurrentBandWidth) ? TNC->RIG->CurrentBandWidth : '?'; + ReportMode[3] = Direction; + ReportMode[4] = 0; + + // If no position see if we have an APRS posn + + if (LOC[0] == 0) + { + double Lat, Lon; + + if (GetPosnFromAPRS(ReportCall, &Lat, &Lon) && Lat != 0.0) + { + ToLOC(Lat, Lon, LOC); + } + } + + SendMH(TNC, ReportCall, ReportFreq, LOC, ReportMode); + + return; +} + +VOID CloseDriverWindow(int port) +{ +#ifndef LINBPQ + + struct TNCINFO * TNC; + + TNC = TNCInfo[port]; + if (TNC == NULL) + return; + + if (TNC->hDlg == NULL) + return; + + PostMessage(TNC->hDlg, WM_CLOSE,0,0); +// DestroyWindow(TNC->hDlg); + + TNC->hDlg = NULL; +#endif + return; +} + +VOID SaveWindowPos(int port) +{ +#ifndef LINBPQ + + struct TNCINFO * TNC; + char Key[80]; + + TNC = TNCInfo[port]; + + if (TNC == NULL) + return; + + if (TNC->hDlg == NULL) + return; + + sprintf(Key, "PACTOR\\PORT%d", port); + + SaveMDIWindowPos(TNC->hDlg, Key, "Size", TNC->Minimized); + +#endif + return; +} + +VOID ShowTraffic(struct TNCINFO * TNC) +{ + char Status[80]; + + sprintf(Status, "RX %d TX %d ACKED %d ", + TNC->Streams[0].BytesRXed, TNC->Streams[0].BytesTXed, TNC->Streams[0].BytesAcked); +#ifndef LINBPQ + SetDlgItemText(TNC->hDlg, IDC_TRAFFIC, Status); +#endif +} + +BOOL InterlockedCheckBusy(struct TNCINFO * ThisTNC) +{ + // See if this port, or any interlocked ports are reporting channel busy + + struct TNCINFO * TNC; + int i; + int rxInterlock = ThisTNC->RXRadio; + int txInterlock = ThisTNC->TXRadio; + + if (ThisTNC->Busy) + return TRUE; // Our port is busy + + if (rxInterlock == 0 && txInterlock == 0) + return ThisTNC->Busy; // No Interlock + + for (i=1; i<33; i++) + { + TNC = TNCInfo[i]; + + if (TNC == NULL) + continue; + + if (TNC == ThisTNC) + continue; + + if (rxInterlock == TNC->RXRadio || txInterlock == TNC->TXRadio) // Same Group + if (TNC->Busy) + return TRUE; // Interlocked port is busy + + } + return FALSE; // None Busy +} + +char ChallengeResponse[13]; + +char * GetChallengeResponse(char * Call, char * ChallengeString) +{ + // Generates a response to the CMS challenge string... + + long long Challenge = _atoi64(ChallengeString); + long long CallSum = 0; + long long Mask; + long long Response; + long long XX = 1065484730; + + char CallCopy[10]; + UINT i; + + + if (Challenge == 0) + return "000000000000"; + +// Calculate Mask from Callsign + + memcpy(CallCopy, Call, 10); + strlop(CallCopy, '-'); + strlop(CallCopy, ' '); + + for (i = 0; i < strlen(CallCopy); i++) + { + CallSum += CallCopy[i]; + } + + Mask = CallSum + CallSum * 4963 + CallSum * 782386; + + Response = (Challenge % 930249781); + Response ^= Mask; + + sprintf(ChallengeResponse, "%012lld", Response); + + return ChallengeResponse; +} + +SOCKET OpenWL2KHTTPSock() +{ + SOCKET sock = 0; + struct sockaddr_in destaddr; + struct sockaddr_in sinx; + int addrlen=sizeof(sinx); + struct hostent * HostEnt; + int err; + u_long param=1; + BOOL bcopt=TRUE; + + destaddr.sin_family = AF_INET; + destaddr.sin_port = htons(80); + + // Resolve name to address + + HostEnt = gethostbyname ("api.winlink.org"); + + if (!HostEnt) + { + err = WSAGetLastError(); + + Debugprintf("Resolve Failed for %s %d %x", "api.winlink.org", err, err); + return 0 ; // Resolve failed + } + + memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + + // Allocate a Socket entry + + sock = socket(AF_INET,SOCK_STREAM,0); + + if (sock == INVALID_SOCKET) + return 0; + + setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + if (bind(sock, (struct sockaddr *) &sinx, addrlen) != 0 ) + return FALSE; + + if (connect(sock,(struct sockaddr *) &destaddr, sizeof(destaddr)) != 0) + { + err=WSAGetLastError(); + closesocket(sock); + return 0; + } + + return sock; +} + +BOOL GetWL2KSYSOPInfo(char * Call, char * _REPLYBUFFER) +{ + SOCKET sock = 0; + int Len; + char Message[1000]; + + sock = OpenWL2KHTTPSock(); + + if (sock == 0) + return 0; + + // {"Callsign":"String"} + + Len = sprintf(Message, "\"Callsign\":\"%s\"", Call); + + SendHTTPRequest(sock, "/sysop/get", Message, Len, _REPLYBUFFER); + + closesocket(sock); + + return _REPLYBUFFER[0]; +} + +BOOL UpdateWL2KSYSOPInfo(char * Call, char * SQL) +{ + SOCKET sock = 0; + struct sockaddr_in destaddr; + struct sockaddr_in sinx; + int len = 100; + int addrlen=sizeof(sinx); + struct hostent * HostEnt; + int err; + u_long param=1; + BOOL bcopt=TRUE; + char Buffer[1000]; + char SendBuffer[1000]; + + destaddr.sin_family = AF_INET; + destaddr.sin_addr.s_addr = inet_addr("api.winlink.org"); + destaddr.sin_port = htons(80); + + HostEnt = gethostbyname ("api.winlink.org"); + + if (!HostEnt) + { + err = WSAGetLastError(); + + Debugprintf("Resolve Failed for %s %d %x", "api.winlink.org", err, err); + return 0 ; // Resolve failed + } + + memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + + // Allocate a Socket entry + + sock = socket(AF_INET,SOCK_STREAM,0); + + if (sock == INVALID_SOCKET) + return 0; + + setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + if (bind(sock, (struct sockaddr *) &sinx, addrlen) != 0 ) + return FALSE; + + if (connect(sock,(struct sockaddr *) &destaddr, sizeof(destaddr)) != 0) + { + err=WSAGetLastError(); + closesocket(sock); + return 0; + } + + len = recv(sock, &Buffer[0], len, 0); + + len = sprintf(SendBuffer, "02%07d%-12s%s%s", (int)strlen(SQL), Call, GetChallengeResponse(Call, Buffer), SQL); + + send(sock, SendBuffer, len, 0); + + len = 1000; + + len = recv(sock, &Buffer[0], len, 0); + + Buffer[len] = 0; + Debugprintf(Buffer); + + closesocket(sock); + + return TRUE; + +} +// http://server.winlink.org:8085/csv/reply/ChannelList?Modes=40,41,42,43,44&ServiceCodes=BPQTEST,PUBLIC + +// Process config lines that are common to a number of HF modes + +extern int nextDummyInterlock; + +int standardParams(struct TNCINFO * TNC, char * buf) +{ + if (_memicmp(buf, "WL2KREPORT", 10) == 0) + TNC->WL2K = DecodeWL2KReportLine(buf); + else if (_memicmp(buf, "SESSIONTIMELIMIT", 16) == 0) + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit = atoi(&buf[17]) * 60; + else if (_memicmp(buf, "BUSYHOLD", 8) == 0) // Hold Time for Busy Detect + TNC->BusyHold = atoi(&buf[8]); + else if (_memicmp(buf, "BUSYWAIT", 8) == 0) // Wait time before failing connect if busy + TNC->BusyWait = atoi(&buf[8]); + else if (_memicmp(buf, "AUTOSTARTDELAY", 14) == 0) // Time to wait for TNC to start + TNC->AutoStartDelay = atoi(&buf[15]); + else if (_memicmp(buf, "DEFAULTRADIOCOMMAND", 19) == 0) + TNC->DefaultRadioCmd = _strdup(&buf[20]); + else if (_memicmp(buf, "MYCALLS", 7) == 0) + { + TNC->LISTENCALLS = _strdup(&buf[8]); + strlop(TNC->LISTENCALLS, '\r'); + } + else if (_memicmp(buf, "MAXCONREQ", 9) == 0) // Hold Time for Busy Detect + TNC->MaxConReq = atoi(&buf[9]); + + else if (_memicmp(buf, "FREQUENCY", 9) == 0) + TNC->Frequency = _strdup(&buf[10]); + else if (_memicmp(buf, "SendTandRtoRelay", 16) == 0) + TNC->SendTandRtoRelay = atoi(&buf[17]); + else if (_memicmp(buf, "Radio", 5) == 0) // Rig Control RADIO for TX amd RX (Equiv to INTERLOCK) + TNC->RXRadio = TNC->TXRadio = atoi(&buf[6]); + else if (_memicmp(buf, "TXRadio", 7) == 0) // Rig Control RADIO for TX + TNC->TXRadio = atoi(&buf[8]); + else if (_memicmp(buf, "RXRadio", 7) == 0) // Rig Control RADIO for RXFRETRIES + TNC->RXRadio = atoi(&buf[8]); + else if (_memicmp(buf, "TXFreq", 6) == 0) // For PTT Sets Freq mode + TNC->TXFreq = strtoll(&buf[7], NULL, 10); + else if (_memicmp(buf, "DefaultFreq", 11) == 0) // For PTT Sets Freq mode + TNC->DefaultFreq = strtoll(&buf[12], NULL, 10); + else if (_memicmp(buf, "PTTONHEX", 8) == 0) + { + // Hex String to use for PTT on for this port + + char * ptr1 = &buf[9]; + char * ptr2 = TNC->PTTOn; + int i, j, len; + + _strupr(ptr1); + + TNC->PTTOnLen = len = strlen(ptr1) / 2; + + if (len < 240) + { + while ((len--) > 0) + { + i = *(ptr1++); + i -= '0'; + if (i > 9) + i -= 7; + + j = i << 4; + + i = *(ptr1++); + i -= '0'; + if (i > 9) + i -= 7; + + *(ptr2++) = j | i; + } + } + } + else if (_memicmp(buf, "PTTOFFHEX", 9) == 0) + { + // Hex String to use for PTT off + + char * ptr = &buf[10]; + char * ptr2 = TNC->PTTOff; + int i, j, len; + + _strupr(ptr); + + TNC->PTTOffLen = len = strlen(ptr) / 2; + + if (len < 240) + { + while ((len--) > 0) + { + i = *(ptr++); + i -= '0'; + if (i > 9) + i -= 7; + + j = i << 4; + + i = *(ptr++); + i -= '0'; + if (i > 9) + i -= 7; + + *(ptr2++) = j | i; + } + } + } + else + return FALSE; + + return TRUE; +} + +void DecodePTTString(struct TNCINFO * TNC, char * ptr) +{ + if (_stricmp(ptr, "CI-V") == 0) + TNC->PTTMode = PTTCI_V; + else if (_stricmp(ptr, "CAT") == 0) + TNC->PTTMode = PTTCI_V; + else if (_stricmp(ptr, "RTS") == 0) + TNC->PTTMode = PTTRTS; + else if (_stricmp(ptr, "DTR") == 0) + TNC->PTTMode = PTTDTR; + else if (_stricmp(ptr, "DTRRTS") == 0) + TNC->PTTMode = PTTDTR | PTTRTS; + else if (_stricmp(ptr, "CM108") == 0) + TNC->PTTMode = PTTCM108; + else if (_stricmp(ptr, "HAMLIB") == 0) + TNC->PTTMode = PTTHAMLIB; + else if (_stricmp(ptr, "FLRIG") == 0) + TNC->PTTMode = PTTFLRIG; +} + +extern SOCKET ReportSocket; +extern char LOCATOR[80]; +extern char ReportDest[7]; +extern int NumberofPorts; +extern struct RIGPORTINFO * PORTInfo[34]; // Records are Malloc'd + +time_t LastModeReportTime; +time_t LastFreqReportTime; + +VOID SendReportMsg(char * buff, int txlen); + +void sendModeReport() +{ + // if TNC is connected send mode and frequencies to Node Map as a MODE record + // Are we better sending scan info as a separate record ?? + + // MODE Port, HWType, Interlock + + struct PORTCONTROL * PORT = PORTTABLE; + + struct TNCINFO * TNC; + MESSAGE AXMSG; + PMESSAGE AXPTR = &AXMSG; + char Msg[300] = "MODE "; + int i, Len = 5; + + if ((CurrentSecs - LastModeReportTime) < 900) // Every 15 Mins + return; + + LastModeReportTime = CurrentSecs; + + for (i = 0; i < NUMBEROFPORTS; i++) + { + if (PORT->PROTOCOL == 10) + { + PEXTPORTDATA PORTVEC = (PEXTPORTDATA)PORT; + TNC = TNCInfo[PORT->PORTNUMBER]; + PORT = PORT->PORTPOINTER; + + if (TNC == NULL) + continue; + + if (TNC->CONNECTED == 0 && TNC->TNCOK == 0) + continue; + + if (ReportSocket == 0 || LOCATOR[0] == 0) + continue; + + if (TNC->Frequency) + Len += sprintf(&Msg[Len], "%d,%d,%d,%.6f/", TNC->Port, TNC->Hardware, TNC->RXRadio, atof(TNC->Frequency)); + else + Len += sprintf(&Msg[Len], "%d,%d,%d/", TNC->Port, TNC->Hardware, TNC->RXRadio); + + if (Len > 240) + break; + } + else + PORT = PORT->PORTPOINTER; + } + + if (Len == 5) + return; // Nothing to send + + // Block includes the Msg Header (7 bytes), Len Does not! + + memcpy(AXPTR->DEST, ReportDest, 7); + memcpy(AXPTR->ORIGIN, MYCALL, 7); + AXPTR->DEST[6] &= 0x7e; // Clear End of Call + AXPTR->DEST[6] |= 0x80; // set Command Bit + + AXPTR->ORIGIN[6] |= 1; // Set End of Call + AXPTR->CTL = 3; //UI + AXPTR->PID = 0xf0; + memcpy(AXPTR->L2DATA, Msg, Len); + + SendReportMsg((char *)&AXMSG.DEST, Len + 16) ; +} + +void sendFreqReport(char * From) +{ + // Send info from rig control or Port Frequency info to Node Map for Mode page. + + MESSAGE AXMSG; + PMESSAGE AXPTR = &AXMSG; + char Msg[300] = "FREQ "; + int i, Len = 5, p; + + struct RIGPORTINFO * RIGPORT; + struct RIGINFO * RIG; + struct TimeScan * Band; + struct PORTCONTROL * PORT = PORTTABLE; + struct TNCINFO * TNC; + + if ((CurrentSecs - LastFreqReportTime) < 7200) // Every 2 Hours + return; + + LastFreqReportTime = CurrentSecs; + + for (p = 0; p < NumberofPorts; p++) + { + RIGPORT = PORTInfo[p]; + + for (i = 0; i < RIGPORT->ConfiguredRigs; i++) + { + int j = 1, k = 0; + + RIG = &RIGPORT->Rigs[i]; + + if (RIG->reportFreqs) + { + Len += sprintf(&Msg[Len], "%d/00:00/%s,\\|",RIG->Interlock,RIG->reportFreqs); + } + else + { + if (RIG->TimeBands) + { + Len += sprintf(&Msg[Len], "%d/",RIG->Interlock); + while (RIG->TimeBands[j]) + { + Band = RIG->TimeBands[j]; + k = 0; + + if (Band->Scanlist[0]) + { + Len += sprintf(&Msg[Len], "%02d:%02d/", Band->Start / 3600, Band->Start % 3600); + + while (Band->Scanlist[k]) + { + Len += sprintf(&Msg[Len],"%.0f,", Band->Scanlist[k]->Freq + RIG->rxOffset); + k++; + } + Len += sprintf(&Msg[Len], "\\"); + } + j++; + } + Len += sprintf(&Msg[Len], "|"); + } + } + } + } + + // Look for Port freq info + + for (i = 0; i < NUMBEROFPORTS; i++) + { + if (PORT->PROTOCOL == 10) + { + PEXTPORTDATA PORTVEC = (PEXTPORTDATA)PORT; + TNC = TNCInfo[PORT->PORTNUMBER]; + PORT = PORT->PORTPOINTER; + + if (TNC == NULL) + continue; + + if (TNC->Frequency == NULL) + continue; + + if (TNC->RIG->TimeBands && TNC->RIG->TimeBands[1]->Scanlist) + continue; // Have freq info from Rigcontrol + + if (TNC->RXRadio == 0) // Replace with dummy + TNC->RXRadio = nextDummyInterlock++; + + // Use negative port no instead of interlock group + + Len += sprintf(&Msg[Len], "%d/00:00/%.0f|", TNC->RXRadio, atof(TNC->Frequency) * 1000000.0); + } + else + PORT = PORT->PORTPOINTER; + } + + if (Len == 5) + return; // Nothing to send + + // Block includes the Msg Header (7 bytes), Len Does not! + + memcpy(AXPTR->DEST, ReportDest, 7); + memcpy(AXPTR->ORIGIN, MYCALL, 7); + AXPTR->DEST[6] &= 0x7e; // Clear End of Call + AXPTR->DEST[6] |= 0x80; // set Command Bit + + AXPTR->ORIGIN[6] |= 1; // Set End of Call + AXPTR->CTL = 3; //UI + AXPTR->PID = 0xf0; + memcpy(AXPTR->L2DATA, Msg, Len); + + SendReportMsg((char *)&AXMSG.DEST, Len + 16) ; +} + + diff --git a/HTTPcode-HPLaptop.c b/HTTPcode-HPLaptop.c new file mode 100644 index 0000000..eaf894b --- /dev/null +++ b/HTTPcode-HPLaptop.c @@ -0,0 +1,4428 @@ +/* +Copyright 2001-2022 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 +*/ + + +//#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + +#define _CRT_SECURE_NO_DEPRECATE + +#define DllImport + +#include "CHeaders.h" +#include + +#include "tncinfo.h" +#include "time.h" +#include "bpq32.h" +#include "telnetserver.h" + +// This is needed to link with a lib built from source + +#ifdef WIN32 +#define ZEXPORT __stdcall +#endif + +#include "zlib.h" + +#define CKernel +#include "httpconnectioninfo.h" + +extern int MAXBUFFS, QCOUNT, MINBUFFCOUNT, NOBUFFCOUNT, BUFFERWAITS, L3FRAMES; +extern int NUMBEROFNODES, MAXDESTS, L4CONNECTSOUT, L4CONNECTSIN, L4FRAMESTX, L4FRAMESRX, L4FRAMESRETRIED, OLDFRAMES; +extern int STATSTIME; +extern TRANSPORTENTRY * L4TABLE; +extern BPQVECSTRUC BPQHOSTVECTOR[]; +extern BOOL APRSApplConnected; +extern char VersionString[]; +VOID FormatTime3(char * Time, time_t cTime); +DllExport int APIENTRY Get_APPLMASK(int Stream); +VOID SaveUIConfig(); +int ProcessNodeSignon(SOCKET sock, struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, int LOCAL); +VOID SetupUI(int Port); +VOID SendUIBeacon(int Port); +VOID GetParam(char * input, char * key, char * value); +VOID ARDOPAbort(struct TNCINFO * TNC); +VOID WriteMiniDump(); +BOOL KillTNC(struct TNCINFO * TNC); +BOOL RestartTNC(struct TNCINFO * TNC); +int GetAISPageInfo(char * Buffer, int ais, int adsb); +int GetAPRSPageInfo(char * Buffer, double N, double S, double W, double E, int aprs, int ais, int adsb); +unsigned char * Compressit(unsigned char * In, int Len, int * OutLen); +char * stristr (char *ch1, char *ch2); +int GetAPRSIcon(unsigned char * _REPLYBUFFER, char * NodeURL); +char * GetStandardPage(char * FN, int * Len); +BOOL SHA1PasswordHash(char * String, char * Hash); +char * byte_base64_encode(char *str, int len); + +extern struct ROUTE * NEIGHBOURS; +extern int ROUTE_LEN; +extern int MAXNEIGHBOURS; + +extern struct DEST_LIST * DESTS; // NODE LIST +extern int DEST_LIST_LEN; +extern int MAXDESTS; // MAX NODES IN SYSTEM + +extern struct _LINKTABLE * LINKS; +extern int LINK_TABLE_LEN; +extern int MAXLINKS; +extern char * RigWebPage; +extern COLORREF Colours[256]; + +extern BOOL IncludesMail; +extern BOOL IncludesChat; + +extern BOOL APRSWeb; +extern BOOL RigActive; + +extern char * UIUIDigi[33]; +extern char UIUIDEST[33][11]; // Dest for Beacons +extern UCHAR FN[33][256]; // Filename +extern int Interval[33]; // Beacon Interval (Mins) +extern char Message[33][1000]; // Beacon Text + +extern int MinCounter[33]; // Interval Countdown +extern BOOL SendFromFile[33]; + +extern HKEY REGTREE; + +extern BOOL APRSActive; + +extern UCHAR LogDirectory[]; + +extern struct RIGPORTINFO * PORTInfo[34]; +extern int NumberofPorts; + +char * strlop(char * buf, char delim); +VOID sendandcheck(SOCKET sock, const char * Buffer, int Len); +int CompareNode(const void *a, const void *b); +int CompareAlias(const void *a, const void *b); +void ProcessMailHTTPMessage(struct HTTPConnectionInfo * Session, char * Method, char * URL, char * input, char * Reply, int * RLen, int InputLen); +void ProcessChatHTTPMessage(struct HTTPConnectionInfo * Session, char * Method, char * URL, char * input, char * Reply, int * RLen); +struct PORTCONTROL * APIENTRY GetPortTableEntryFromSlot(int portslot); +int SetupNodeMenu(char * Buff, int SYSOP); +int StatusProc(char * Buff); +int ProcessMailSignon(struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, BOOL WebMail, int LOCAL); +int ProcessChatSignon(struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, int LOCAL); +VOID APRSProcessHTTPMessage(SOCKET sock, char * MsgPtr, BOOL LOCAL, BOOL COOKIE); + + +static struct HTTPConnectionInfo * SessionList; // active term mode sessions + +char Mycall[10]; + +char MAILPipeFileName[] = "\\\\.\\pipe\\BPQMAILWebPipe"; +char CHATPipeFileName[] = "\\\\.\\pipe\\BPQCHATWebPipe"; + +char Index[] = "%s's BPQ32 Web Server

" +"" +"" +"
Node PagesAPRS Pages
"; + +char IndexNoAPRS[] = "" +""; + +//char APRSBit[] = "APRS Pages"; + +//char MailBit[] = "Mail Mgmt" +// "WebMail"; +//char ChatBit[] = "Chat Mgmt"; + +char Tail[] = ""; + +char RouteHddr[] = "

Routes

" +""; + +char RouteLine[] = ""; +char xNodeHddr[] = "
" +"
PortCallQualityNode CountFrame CountRetriesPercentMaxframeFrackLast HeardQueuedRem Qual
%s%d%s%c%d%d%d%d%d%%d%d%02d:%02d%d%d
" +"
" +"" +"
" +"

Nodes %s

"; + +char NodeHddr[] = "
" +"" +"" +"
" +"

Nodes %s

"; + +char NodeLine[] = ""; + + +char StatsHddr[] = "

Node Stats

%s:%s
" +""; + +char PortStatsHddr[] = "

Stats for Port %d

"; + +char PortStatsLine[] = ""; + + +char Beacons[] = "

Beacon Configuration for Port %d

You need to be signed in to save changes

%s %d
" +"" +"
" +"" +"" +"" +"" +"" +"
Send Interval (Minutes)
To
Path
Send From File
Text
" +"" + +"

" +""; + + +char LinkHddr[] = "

Links

" +""; + +char LinkLine[] = ""; + +char UserHddr[] = "

Sessions

Far CallOur CallPortax.25 stateLink Typeax.25 Version
%s%s%d%s%s%d
"; + +char UserLine[] = ""; + +char TermSignon[] = "BPQ32 Node %s Terminal Access" +"

BPQ32 Node %s Terminal Access

" +"

Please enter username and password to access the node

" +"" +"
%s%s%s
" +"" +"
User
Password
" +"

" +""; + + +char PassError[] = "

Sorry, User or Password is invalid - please try again

"; + +char BusyError[] = "

Sorry, No sessions available - please try later

"; + +char LostSession[] = "Sorry, Session had been lost - refresh page to sign in again"; +char NoSessions[] = "Sorry, No Sessions available - refresh page to try again"; + +char TermPage[] = "" +"BPQ32 Node %s" +"" +"" +"

BPQ32 Node %s

" +"
" +"

" +"" +"" +""; + +char TermOutput[] = "" +"" +"" +"" +"" +"" +"" +"
\""; + + +// font-family:monospace;background-color:black;color:lawngreen;font-size:12px + +char TermOutputTail[] = "
"; + +/* +char InputLine[] = "" +"
" +"" +"
"; +*/ +char InputLine[] = "" +"
" +"\" id=inp type=text text width=100%% name=input />" +"
"; + +static char NodeSignon[] = "BPQ32 Node SYSOP Access" +"

BPQ32 Node %s SYSOP Access

" +"

This page sets Cookies. Don't continue if you object to this

" +"

Please enter Callsign and Password to access the Node

" +"
" +"" +"" +"
User
Password
" +"

"; + + +static char MailSignon[] = "BPQ32 Mail Server Access" +"

BPQ32 Mail Server %s Access

" +"

Please enter Callsign and Password to access the BBS

" +"
" +"" +"" +"
User
Password
" +"

"; + +static char ChatSignon[] = "BPQ32 Chat Server Access" +"

BPQ32 Chat Server %s Access

" +"

Please enter Callsign and Password to access the Chat Server

" +"
" +"" +"" +"
User
Password
" +"

"; + + +static char MailLostSession[] = "" +"
" +"Sorry, Session had been lost

    " +"
"; + + +static char ConfigEditPage[] = "" +"Edit Config" +"
" +"

" +"
"; + +static char EXCEPTMSG[80] = ""; + + +void UndoTransparency(char * input) +{ + char * ptr1, * ptr2; + char c; + int hex; + + if (input == NULL) + return; + + ptr1 = ptr2 = input; + + // Convert any %xx constructs + + while (1) + { + c = *(ptr1++); + + if (c == 0) + break; + + if (c == '%') + { + c = *(ptr1++); + if(isdigit(c)) + hex = (c - '0') << 4; + else + hex = (tolower(c) - 'a' + 10) << 4; + + c = *(ptr1++); + if(isdigit(c)) + hex += (c - '0'); + else + hex += (tolower(c) - 'a' + 10); + + *(ptr2++) = hex; + } + else if (c == '+') + *(ptr2++) = 32; + else + *(ptr2++) = c; + } + *ptr2 = 0; +} + + + + +VOID PollSession(struct HTTPConnectionInfo * Session) +{ + int state, change; + int count, len; + char Msg[400] = ""; + char Formatted[8192]; + char * ptr1, * ptr2; + char c; + int Line; + + // Poll Node + + SessionState(Session->Stream, &state, &change); + + if (change == 1) + { + int Line = Session->LastLine++; + free(Session->ScreenLines[Line]); + + if (state == 1)// Connected + Session->ScreenLines[Line] = _strdup("*** Connected
\r\n"); + else + Session->ScreenLines[Line] = _strdup("*** Disconnected
\r\n"); + + if (Line == 99) + Session->LastLine = 0; + + Session->Changed = TRUE; + } + + if (RXCount(Session->Stream) > 0) + { + int realLen = 0; + + do + { + GetMsg(Session->Stream, &Msg[0], &len, &count); + + // replace cr with
and space with   + + + ptr1 = Msg; + ptr2 = &Formatted[0]; + + if (Session->PartLine) + { + // Last line was incomplete - append to it + + realLen = Session->PartLine; + + Line = Session->LastLine - 1; + + if (Line < 0) + Line = 99; + + strcpy(Formatted, Session->ScreenLines[Line]); + ptr2 += strlen(Formatted); + + Session->LastLine = Line; + Session->PartLine = FALSE; + } + + while (len--) + { + c = *(ptr1++); + realLen++; + + if (c == 13) + { + int LineLen; + + strcpy(ptr2, "
\r\n"); + + // Write to screen + + Line = Session->LastLine++; + free(Session->ScreenLines[Line]); + + LineLen = (int)strlen(Formatted); + + // if line starts with a colour code, process it + + if (Formatted[0] == 0x1b && LineLen > 1) + { + int ColourCode = Formatted[1] - 10; + COLORREF Colour = Colours[ColourCode]; + char ColString[30]; + + memmove(&Formatted[20], &Formatted[2], LineLen); + sprintf(ColString, "", GetRValue(Colour), GetGValue(Colour), GetBValue(Colour)); + memcpy(Formatted, ColString, 20); + strcat(Formatted, ""); + LineLen =+ 28; + } + + Session->ScreenLineLen[Line] = LineLen; + Session->ScreenLines[Line] = _strdup(Formatted); + + if (Line == 99) + Session->LastLine = 0; + + ptr2 = &Formatted[0]; + realLen = 0; + + } + else if (c == 32) + { + memcpy(ptr2, " ", 6); + ptr2 += 6; + + // Make sure line isn't too long + // but beware of spaces expanded to   - count chars in line + + if ((realLen) > 100) + { + strcpy(ptr2, "
\r\n"); + + Line = Session->LastLine++; + free(Session->ScreenLines[Line]); + + Session->ScreenLines[Line] = _strdup(Formatted); + + if (Line == 99) + Session->LastLine = 0; + + ptr2 = &Formatted[0]; + realLen = 0; + } + } + else if (c == '>') + { + memcpy(ptr2, ">", 4); + ptr2 += 4; + } + else if (c == '<') + { + memcpy(ptr2, "<", 4); + ptr2 += 4; + } + else + *(ptr2++) = c; + + } + + *ptr2 = 0; + + if (ptr2 != &Formatted[0]) + { + // Incomplete line + + // Save to screen + + Line = Session->LastLine++; + free(Session->ScreenLines[Line]); + + Session->ScreenLines[Line] = _strdup(Formatted); + + if (Line == 99) + Session->LastLine = 0; + + Session->PartLine = realLen; + } + + // strcat(Session->ScreenBuffer, Formatted); + Session->Changed = TRUE; + + } while (count > 0); + } +} + + +VOID HTTPTimer() +{ + // Run every tick. Check for status change and data available + + struct HTTPConnectionInfo * Session = SessionList; // active term mode sessions + struct HTTPConnectionInfo * PreviousSession = NULL; + +// inf(); + + while (Session) + { + Session->KillTimer++; + + if (Session->Key[0] != 'T') + { + PreviousSession = Session; + Session = Session->Next; + continue; + } + + if (Session->KillTimer > 3000) // Around 5 mins + { + int i; + int Stream = Session->Stream; + + for (i = 0; i < 100; i++) + { + free(Session->ScreenLines[i]); + } + + SessionControl(Stream, 2, 0); + SessionState(Stream, &i, &i); + DeallocateStream(Stream); + + if (PreviousSession) + PreviousSession->Next = Session->Next; // Remove from chain + else + SessionList = Session->Next; + + free(Session); + + break; + } + + PollSession(Session); + + // if (Session->ResponseTimer == 0 && Session->Changed) + // Debugprintf("Data to send but no outstanding GET"); + + if (Session->ResponseTimer) + { + Session->ResponseTimer--; + + if (Session->ResponseTimer == 0 || Session->Changed) + { + SOCKET sock = Session->sock; + char _REPLYBUFFER[100000]; + int ReplyLen; + char Header[256]; + int HeaderLen; + int Last = Session->LastLine; + int n; + struct TNCINFO * TNC = Session->TNC; + struct TCPINFO * TCP = 0; + + if (TNC) + TCP = TNC->TCPInfo; + + if (TCP && TCP->WebTermCSS) + sprintf(_REPLYBUFFER, TermOutput, TCP->WebTermCSS); + else + sprintf(_REPLYBUFFER, TermOutput, ""); + + for (n = Last;;) + { + strcat(_REPLYBUFFER, Session->ScreenLines[n]); + + if (n == 99) + n = -1; + + if (++n == Last) + break; + } + + ReplyLen = (int)strlen(_REPLYBUFFER); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", TermOutputTail); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, _REPLYBUFFER, ReplyLen); + + Session->ResponseTimer = Session->Changed = 0; + } + } + PreviousSession = Session; + Session = Session->Next; + } +} + +struct HTTPConnectionInfo * AllocateSession(SOCKET sock, char Mode) +{ + time_t KeyVal; + struct HTTPConnectionInfo * Session = zalloc(sizeof(struct HTTPConnectionInfo)); + int i; + + if (Session == NULL) + return NULL; + + if (Mode == 'T') + { + // Terminal + + for (i = 0; i < 20; i++) + Session->ScreenLines[i] = _strdup("Scroll to end
"); + + for (i = 20; i < 100; i++) + Session->ScreenLines[i] = _strdup("
\r\n"); + + Session->Stream = FindFreeStream(); + + if (Session->Stream == 0) + return NULL; + + SessionControl(Session->Stream, 1, 0); + } + + KeyVal = (int)sock * time(NULL); + + sprintf(Session->Key, "%c%012X", Mode, (int)KeyVal); + + if (SessionList) + Session->Next = SessionList; + + SessionList = Session; + + return Session; +} + +struct HTTPConnectionInfo * FindSession(char * Key) +{ + struct HTTPConnectionInfo * Session = SessionList; + + while (Session) + { + if (strcmp(Session->Key, Key) == 0) + return Session; + + Session = Session->Next; + } + + return NULL; +} + +void ProcessTermInput(SOCKET sock, char * MsgPtr, int MsgLen, char * Key) +{ + char _REPLYBUFFER[1024]; + int ReplyLen; + char Header[256]; + int HeaderLen; + int State; + struct HTTPConnectionInfo * Session = FindSession(Key); + int Stream; + + if (Session == NULL) + { + ReplyLen = sprintf(_REPLYBUFFER, "%s", LostSession); + } + else + { + char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + char * end = &MsgPtr[MsgLen]; + int Line = Session->LastLine++; + char * ptr1, * ptr2; + char c; + UCHAR hex; + + struct TNCINFO * TNC = Session->TNC; + struct TCPINFO * TCP = 0; + + if (TNC) + TCP = TNC->TCPInfo; + + if (TCP && TCP->WebTermCSS) + ReplyLen = sprintf(_REPLYBUFFER, InputLine, Key, TCP->WebTermCSS); + else + ReplyLen = sprintf(_REPLYBUFFER, InputLine, Key, ""); + + + Stream = Session->Stream; + + input += 10; + ptr1 = ptr2 = input; + + // Convert any %xx constructs + + while (ptr1 != end) + { + c = *(ptr1++); + if (c == '%') + { + c = *(ptr1++); + if(isdigit(c)) + hex = (c - '0') << 4; + else + hex = (tolower(c) - 'a' + 10) << 4; + + c = *(ptr1++); + if(isdigit(c)) + hex += (c - '0'); + else + hex += (tolower(c) - 'a' + 10); + + *(ptr2++) = hex; + } + else if (c == '+') + *(ptr2++) = 32; + else + *(ptr2++) = c; + } + + end = ptr2; + + *ptr2 = 0; + + strcat(input, "
\r\n"); + + free(Session->ScreenLines[Line]); + + Session->ScreenLines[Line] = _strdup(input); + + if (Line == 99) + Session->LastLine = 0; + + *end++ = 13; + *end = 0; + + SessionStateNoAck(Stream, &State); + + if (State == 0) + { + char AXCall[10]; + SessionControl(Stream, 1, 0); + if (BPQHOSTVECTOR[Session->Stream -1].HOSTSESSION == NULL) + { + //No L4 sessions free + + ReplyLen = sprintf(_REPLYBUFFER, "%s", NoSessions); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + return; + } + + ConvToAX25(Session->HTTPCall, AXCall); + ChangeSessionCallsign(Stream, AXCall); + if (Session->USER) + BPQHOSTVECTOR[Session->Stream -1].HOSTSESSION->Secure_Session = Session->USER->Secure; + else + Debugprintf("HTTP Term Session->USER is NULL"); + } + + SendMsg(Stream, input, (int)(end - input)); + Session->Changed = TRUE; + Session->KillTimer = 0; + } + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); +} + + +void ProcessTermClose(SOCKET sock, char * MsgPtr, int MsgLen, char * Key, int LOCAL) +{ + char _REPLYBUFFER[8192]; + int ReplyLen = sprintf(_REPLYBUFFER, InputLine, Key, ""); + char Header[256]; + int HeaderLen; + struct HTTPConnectionInfo * Session = FindSession(Key); + + if (Session) + { + Session->KillTimer = 99999; + } + + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n" + "\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); +} + +int ProcessTermSignon(struct TNCINFO * TNC, SOCKET sock, char * MsgPtr, int MsgLen, int LOCAL) +{ + char _REPLYBUFFER[8192]; + int ReplyLen; + char Header[256]; + int HeaderLen; + char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + char * user, * password, * Context, * Appl; + char NoApp[] = ""; + struct TCPINFO * TCP = TNC->TCPInfo; + + if (input) + { + int i; + struct UserRec * USER; + + UndoTransparency(input); + + if (strstr(input, "Cancel=Cancel")) + { + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + goto Sendit; + } + user = strtok_s(&input[9], "&", &Context); + password = strtok_s(NULL, "=", &Context); + password = strtok_s(NULL, "&", &Context); + + Appl = strtok_s(NULL, "=", &Context); + Appl = strtok_s(NULL, "&", &Context); + + if (Appl == 0) + Appl = NoApp; + + if (password == NULL) + { + ReplyLen = sprintf(_REPLYBUFFER, TermSignon, Mycall, Mycall, Appl); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", PassError); + goto Sendit; + } + + for (i = 0; i < TCP->NumberofUsers; i++) + { + USER = TCP->UserRecPtr[i]; + + if ((strcmp(password, USER->Password) == 0) && + ((_stricmp(user, USER->UserName) == 0 ) || (_stricmp(USER->UserName, "ANON") == 0))) + { + // ok + + struct HTTPConnectionInfo * Session = AllocateSession(sock, 'T'); + + if (Session) + { + char AXCall[10]; + ReplyLen = sprintf(_REPLYBUFFER, TermPage, Mycall, Mycall, Session->Key, Session->Key, Session->Key); + if (_stricmp(USER->UserName, "ANON") == 0) + strcpy(Session->HTTPCall, _strupr(user)); + else + strcpy(Session->HTTPCall, USER->Callsign); + ConvToAX25(Session->HTTPCall, AXCall); + ChangeSessionCallsign(Session->Stream, AXCall); + BPQHOSTVECTOR[Session->Stream -1].HOSTSESSION->Secure_Session = USER->Secure; + Session->USER = USER; + + if (USER->Appl[0]) + SendMsg(Session->Stream, USER->Appl, (int)strlen(USER->Appl)); + } + else + { + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", BusyError); + } + break; + } + } + + if (i == TCP->NumberofUsers) + { + // Not found + + ReplyLen = sprintf(_REPLYBUFFER, TermSignon, Mycall, Mycall, Appl); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", PassError); + } + + } + +Sendit: + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + + return 1; +} + +char * LookupKey(char * Key) +{ + if (strcmp(Key, "##MY_CALLSIGN##") == 0) + { + char Mycall[10]; + memcpy(Mycall, &MYNODECALL, 10); + strlop(Mycall, ' '); + + return _strdup(Mycall); + } + return NULL; +} + + +int ProcessSpecialPage(char * Buffer, int FileSize) +{ + // replaces ##xxx### constructs with the requested data + + char * NewMessage = malloc(100000); + char * ptr1 = Buffer, * ptr2, * ptr3, * ptr4, * NewPtr = NewMessage; + int PrevLen; + int BytesLeft = FileSize; + int NewFileSize = FileSize; + char * StripPtr = ptr1; + + // strip comments blocks + + while (ptr4 = strstr(ptr1, ""); + if (ptr2) + { + PrevLen = (int)(ptr4 - ptr1); + memcpy(StripPtr, ptr1, PrevLen); + StripPtr += PrevLen; + ptr1 = ptr2 + 3; + BytesLeft = (int)(FileSize - (ptr1 - Buffer)); + } + } + + memcpy(StripPtr, ptr1, BytesLeft); + StripPtr += BytesLeft; + + BytesLeft = (int)(StripPtr - Buffer); + + FileSize = BytesLeft; + NewFileSize = FileSize; + ptr1 = Buffer; + ptr1[FileSize] = 0; + +loop: + ptr2 = strstr(ptr1, "##"); + + if (ptr2) + { + PrevLen = (int)(ptr2 - ptr1); // Bytes before special text + + ptr3 = strstr(ptr2+2, "##"); + + if (ptr3) + { + char Key[80] = ""; + int KeyLen; + char * NewText; + int NewTextLen; + + ptr3 += 2; + KeyLen = (int)(ptr3 - ptr2); + + if (KeyLen < 80) + memcpy(Key, ptr2, KeyLen); + + NewText = LookupKey(Key); + + if (NewText) + { + NewTextLen = (int)strlen(NewText); + NewFileSize = NewFileSize + NewTextLen - KeyLen; + // NewMessage = realloc(NewMessage, NewFileSize); + + memcpy(NewPtr, ptr1, PrevLen); + NewPtr += PrevLen; + memcpy(NewPtr, NewText, NewTextLen); + NewPtr += NewTextLen; + + free(NewText); + NewText = NULL; + } + else + { + // Key not found, so just leave + + memcpy(NewPtr, ptr1, PrevLen + KeyLen); + NewPtr += (PrevLen + KeyLen); + } + + ptr1 = ptr3; // Continue scan from here + BytesLeft = (int)(Buffer + FileSize - ptr3); + } + else // Unmatched ## + { + memcpy(NewPtr, ptr1, PrevLen + 2); + NewPtr += (PrevLen + 2); + ptr1 = ptr2 + 2; + } + goto loop; + } + + // Copy Rest + + memcpy(NewPtr, ptr1, BytesLeft); + NewMessage[NewFileSize] = 0; + + strcpy(Buffer, NewMessage); + free(NewMessage); + + return NewFileSize; +} + +int SendMessageFile(SOCKET sock, char * FN, BOOL OnlyifExists, int allowDeflate) +{ + int FileSize = 0, Sent, Loops = 0; + char * MsgBytes; + char MsgFile[512]; + FILE * hFile; + int ReadLen; + BOOL Special = FALSE; + int Len; + int HeaderLen; + char Header[256]; + char TimeString[64]; + char FileTimeString[64]; + struct stat STAT; + char * ptr; + char * Compressed = 0; + char Encoding[] = "Content-Encoding: deflate\r\n"; + char Type[64] = "Content-Type: text/html\r\n"; + +#ifdef WIN32 + + struct _EXCEPTION_POINTERS exinfo; + strcpy(EXCEPTMSG, "SendMessageFile"); + + __try { +#endif + + UndoTransparency(FN); + + if (strstr(FN, "..")) + { + FN[0] = '/'; + FN[1] = 0; + } + + if (strlen(FN) > 256) + { + FN[256] = 0; + Debugprintf("HTTP File Name too long %s", FN); + } + + if (strcmp(FN, "/") == 0) + if (APRSActive) + sprintf(MsgFile, "%s/HTML/index.html", BPQDirectory); + else + sprintf(MsgFile, "%s/HTML/indexnoaprs.html", BPQDirectory); + else + sprintf(MsgFile, "%s/HTML%s", BPQDirectory, FN); + + + // First see if file exists so we can override standard ones in code + + if (stat(MsgFile, &STAT) == 0 && (hFile = fopen(MsgFile, "rb"))) + { + FileSize = STAT.st_size; + + MsgBytes = zalloc(FileSize + 1); + + ReadLen = (int)fread(MsgBytes, 1, FileSize, hFile); + + fclose(hFile); + + // ft.QuadPart -= 116444736000000000; + // ft.QuadPart /= 10000000; + + // ctime = ft.LowPart; + + FormatTime3(FileTimeString, STAT.st_ctime); + } + else + { + // See if it is a hard coded file + + MsgBytes = GetStandardPage(&FN[1], &FileSize); + + if (MsgBytes) + { + if (FileSize == 0) + FileSize = strlen(MsgBytes); + + FormatTime3(FileTimeString, 0); + } + else + { + if (OnlyifExists) // Set if we dont want an error response if missing + return -1; + + Len = sprintf(Header, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n"); + send(sock, Header, Len, 0); + return 0; + } + } + + // if HTML file, look for ##...## substitutions + + if ((strcmp(FN, "/") == 0 || strstr(FN, "htm" ) || strstr(FN, "HTM")) && strstr(MsgBytes, "##" )) + { + FileSize = ProcessSpecialPage(MsgBytes, FileSize); + FormatTime3(FileTimeString, time(NULL)); + + } + + FormatTime3(TimeString, time(NULL)); + + ptr = FN; + + while (strchr(ptr, '.')) + { + ptr = strchr(ptr, '.'); + ++ptr; + } + + if (_stricmp(ptr, "js") == 0) + strcpy(Type, "Content-Type: text/javascript\r\n"); + + if (_stricmp(ptr, "pdf") == 0) + strcpy(Type, "Content-Type: application/pdf\r\n"); + + if (allowDeflate) + { + Compressed = Compressit(MsgBytes, FileSize, &FileSize); + } + else + { + Encoding[0] = 0; + Compressed = MsgBytes; + } + + if (_stricmp(ptr, "jpg") == 0 || _stricmp(ptr, "jpeg") == 0 || _stricmp(ptr, "png") == 0 || _stricmp(ptr, "gif") == 0 || _stricmp(ptr, "ico") == 0) + strcpy(Type, "Content-Type: image\r\n"); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n" + "Date: %s\r\n" + "Last-Modified: %s\r\n" + "%s%s" + "\r\n", FileSize, TimeString, FileTimeString, Type, Encoding); + + send(sock, Header, HeaderLen, 0); + + Sent = send(sock, Compressed, FileSize, 0); + + while (Sent != FileSize && Loops++ < 3000) // 100 secs max + { + if (Sent > 0) // something sent + { +// Debugprintf("%d out of %d sent", Sent, FileSize); + FileSize -= Sent; + memmove(Compressed, &Compressed[Sent], FileSize); + } + + Sleep(30); + Sent = send(sock, Compressed, FileSize, 0); + } + +// Debugprintf("%d out of %d sent %d loops", Sent, FileSize, Loops); + + + free (MsgBytes); + if (allowDeflate) + free (Compressed); + +#ifdef WIN32 + } +#include "StdExcept.c" + Debugprintf("Sending FIle %s", FN); +} +#endif + +return 0; +} + +VOID sendandcheck(SOCKET sock, const char * Buffer, int Len) +{ + int Loops = 0; + int Sent = send(sock, Buffer, Len, 0); + char * Copy = NULL; + + while (Sent != Len && Loops++ < 300) // 10 secs max + { + // Debugprintf("%d out of %d sent %d Loops", Sent, Len, Loops); + + if (Copy == NULL) + { + Copy = malloc(Len); + memcpy(Copy, Buffer, Len); + } + + if (Sent > 0) // something sent + { + Len -= Sent; + memmove(Copy, &Copy[Sent], Len); + } + + Sleep(30); + Sent = send(sock, Copy, Len, 0); + } + + if (Copy) + free(Copy); + + return; +} + +int RefreshTermWindow(struct TCPINFO * TCP, struct HTTPConnectionInfo * Session, char * _REPLYBUFFER) +{ + char Msg[400] = ""; + int HeaderLen, ReplyLen; + char Header[256]; + + PollSession(Session); // See if anything received + + if (Session->Changed) + { + int Last = Session->LastLine; + int n; + + if (TCP && TCP->WebTermCSS) + sprintf(_REPLYBUFFER, TermOutput, TCP->WebTermCSS); + else + sprintf(_REPLYBUFFER, TermOutput, ""); + + for (n = Last;;) + { + strcat(_REPLYBUFFER, Session->ScreenLines[n]); + + if (n == 99) + n = -1; + + if (++n == Last) + break; + } + + Session->Changed = 0; + + ReplyLen = (int)strlen(_REPLYBUFFER); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", TermOutputTail); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen); + sendandcheck(Session->sock, Header, HeaderLen); + sendandcheck(Session->sock, _REPLYBUFFER, ReplyLen); + + return 1; + } + else + return 0; +} + + +extern struct TNCINFO * TNCInfo[41]; + +int SetupNodeMenu(char * Buff, int LOCAL) +{ + int Len = 0, i; + struct TNCINFO * TNC; + int top = 0, left = 0; + + char NodeMenuHeader[] = "%s's BPQ32 Web Server" + "" + + "" + "

BPQ32 Node %s

" + "

" + "" + "" + "" + "" + "" + "" + "%s%s%s%s%s%s"; + + char DriverBit[] = "" + ""; + + char APRSBit[] = ""; + + char MailBit[] = "" + ""; + + char ChatBit[] = ""; + char SigninBit[] = ""; + + char NodeTail[] = + "" + "
RoutesNodesPortsLinksUsersStatsTerminalDriver WindowsStream StatusAPRS PagesMail MgmtWebMailChat MgmtSYSOP SigninEdit Config" + "
"; + + + Len = sprintf(Buff, NodeMenuHeader, Mycall); + + for (i=1; i<33; i++) + { + TNC = TNCInfo[i]; + if (TNC == NULL) + continue; + + if (TNC->WebWindowProc) + { + Len += sprintf(&Buff[Len], NodeMenuLine, i, TNC->WebWinX, TNC->WebWinY, top, left); + top += 22; + left += 22; + } + } + + Len += sprintf(&Buff[Len], NodeMenuRest, Mycall, + DriverBit, + (APRSWeb)?APRSBit:"", + (IncludesMail)?MailBit:"", (IncludesChat)?ChatBit:"", (LOCAL)?"":SigninBit, NodeTail); + + return Len; +} + +VOID SaveConfigFile(SOCKET sock , char * MsgPtr, char * Rest, int LOCAL) +{ + int ReplyLen = 0; + char * ptr, * ptr1, * ptr2, *input; + char c; + int MsgLen, WriteLen = 0; + char inputname[250]="bpq32.cfg"; + FILE *fp1; + char Header[256]; + int HeaderLen; + char Reply[4096]; + char Mess[256]; + char Backup1[MAX_PATH]; + char Backup2[MAX_PATH]; + struct stat STAT; + + input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + + if (input) + { + if (strstr(input, "Cancel=Cancel")) + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + // ReplyLen = sprintf(Reply, "%s", ""); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, Reply, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + return; + } + + ptr = strstr(input, "&Save="); + + if (ptr) + { + *ptr = 0; + + // Undo any % transparency + + ptr1 = ptr2 = input + 8; + + c = *(ptr1++); + + while (c) + { + if (c == '%') + { + int n; + int m = *(ptr1++) - '0'; + if (m > 9) m = m - 7; + n = *(ptr1++) - '0'; + if (n > 9) n = n - 7; + + c = m * 16 + n; + } + else if (c == '+') + c = ' '; + +#ifndef WIN32 + if (c != 13) // Strip CR if Linux +#endif + *(ptr2++) = c; + + c = *(ptr1++); + + } + + *(ptr2++) = 0; + + MsgLen = (int)strlen(input + 8); + + if (BPQDirectory[0] == 0) + { + strcpy(inputname, "bpq32.cfg"); + } + else + { + strcpy(inputname,BPQDirectory); + strcat(inputname,"/"); + strcat(inputname, "bpq32.cfg"); + } + + // Make a backup of the config + + // Keep 4 Generations + + strcpy(Backup2, inputname); + strcat(Backup2, ".bak.3"); + + strcpy(Backup1, inputname); + strcat(Backup1, ".bak.2"); + + DeleteFile(Backup2); // Remove old .bak.3 + MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 + + strcpy(Backup2, inputname); + strcat(Backup2, ".bak.1"); + + MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 + + strcpy(Backup1, inputname); + strcat(Backup1, ".bak"); + + MoveFile(Backup1, Backup2); // Move .bak to .bak.1 + + CopyFile(inputname, Backup1, FALSE); // Copy to .bak + + // Get length to compare with new length + + stat(inputname, &STAT); + + fp1 = fopen(inputname, "wb"); + + if (fp1) + { + WriteLen = (int)fwrite(input + 8, 1, MsgLen, fp1); + fclose(fp1); + } + + if (WriteLen != MsgLen) + sprintf_s(Mess, sizeof(Mess), "Failed to write Config File"); + else + sprintf_s(Mess, sizeof(Mess), "Configuration Saved, Orig Length %d New Length %d", + STAT.st_size, MsgLen); + } + + ReplyLen = sprintf(Reply, "", Mess); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, Reply, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + } + return; +} + +// Compress using deflate. Caller must free output buffer after use + +unsigned char * Compressit(unsigned char * In, int Len, int * OutLen) +{ + z_stream defstream; + int maxSize; + unsigned char * Out; + + defstream.zalloc = Z_NULL; + defstream.zfree = Z_NULL; + defstream.opaque = Z_NULL; + + defstream.avail_in = Len; // size of input + defstream.next_in = (Bytef *)In; // input char array + + deflateInit(&defstream, Z_BEST_COMPRESSION); + maxSize = deflateBound(&defstream, Len); + + Out = malloc(maxSize); + + defstream.avail_out = maxSize; // size of output + defstream.next_out = (Bytef *)Out; // output char array + + deflate(&defstream, Z_FINISH); + deflateEnd(&defstream); + + *OutLen = defstream.total_out; + + return Out; +} + + +int InnerProcessHTTPMessage(struct ConnectionInfo * conn) +{ + struct TCPINFO * TCP = conn->TNC->TCPInfo; + SOCKET sock = conn->socket; + char * MsgPtr = conn->InputBuffer; + int MsgLen = conn->InputLen; + int InputLen = 0; + int OutputLen = 0; + int Bufferlen; + struct HTTPConnectionInfo CI; + struct HTTPConnectionInfo * sockptr = &CI; + struct HTTPConnectionInfo * Session = NULL; + + char URL[100000]; + char * ptr; + char * encPtr = 0; + int allowDeflate = 0; + char * Compressed = 0; + char * HostPtr = 0; + + char * Context, * Method, * NodeURL, * Key; + char _REPLYBUFFER[250000]; + char Reply[250000]; + + int ReplyLen = 0; + char Header[256]; + int HeaderLen; + char TimeString[64]; + BOOL LOCAL = FALSE; + BOOL COOKIE = FALSE; + int Len; + char * WebSock = 0; + + char PortsHddr[] = "

Ports

" + ""; + + char PortLine[] = ""; + + char PortLineWithBeacon[] = "" + "\r\n"; + + char SessionPortLine[] = "" + "\r\n"; + + char PortLineWithDriver[] = "" + "\r\n"; + + + char PortLineWithBeaconAndDriver[] = "" + "" + "\r\n"; + + char RigControlLine[] = "" + "\r\n"; + + + char Encoding[] = "Content-Encoding: deflate\r\n"; + +#ifdef WIN32 + + struct _EXCEPTION_POINTERS exinfo; + strcpy(EXCEPTMSG, "ProcessHTTPMessage"); + + __try { +#endif + + Len = (int)strlen(MsgPtr); + if (Len > 100000) + return 0; + + strcpy(URL, MsgPtr); + + HostPtr = strstr(MsgPtr, "Host: "); + + WebSock = strstr(MsgPtr, "Upgrade"); + + if (HostPtr) + { + uint32_t Host; + char Hostname[32]= ""; + struct LOCALNET * LocalNet = conn->TNC->TCPInfo->LocalNets; + + HostPtr += 6; + memcpy(Hostname, HostPtr, 31); + strlop(Hostname, ':'); + Host = inet_addr(Hostname); + + if (strcmp(Hostname, "127.0.0.1") == 0) + LOCAL = TRUE; + else + { + if (conn->sin.sin_family != AF_INET6) + { + while(LocalNet) + { + uint32_t MaskedHost = conn->sin.sin_addr.s_addr & LocalNet->Mask; + if (MaskedHost == LocalNet->Network) + { + LOCAL = 1; + break; + } + LocalNet = LocalNet->Next; + } + } + } + } + + encPtr = stristr(MsgPtr, "Accept-Encoding:"); + + if (encPtr && stristr(encPtr, "deflate")) + allowDeflate = 1; + else + Encoding[0] = 0; + + ptr = strstr(MsgPtr, "BPQSessionCookie=N"); + + if (ptr) + { + COOKIE = TRUE; + Key = ptr + 17; + ptr = strchr(Key, ','); + if (ptr) + { + *ptr = 0; + Session = FindSession(Key); + *ptr = ','; + } + else + { + ptr = strchr(Key, 13); + if (ptr) + { + *ptr = 0; + Session = FindSession(Key); + *ptr = 13; + } + } + } + + if (WebSock) + { + // Websock connection request - Reply and remember state. + + char KeyMsg[128]; + char Webx[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; // Fixed UID + char Hash[64] = ""; + char * Hash64; // base 64 version + char * ptr; + + //Sec-WebSocket-Key: l622yZS3n+zI+hR6SVWkPw== + + char ReplyMsg[] = + "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: %s\r\n" +// "Sec-WebSocket-Protocol: chat\r\n" + "\r\n"; + + ptr = strstr(MsgPtr, "Sec-WebSocket-Key:"); + + if (ptr) + { + ptr += 18; + while (*ptr == ' ') + ptr++; + + memcpy(KeyMsg, ptr, 40); + strlop(KeyMsg, 13); + strlop(KeyMsg, ' '); + strcat(KeyMsg, Webx); + + SHA1PasswordHash(KeyMsg, Hash); + Hash64 = byte_base64_encode(Hash, 20); + + conn->WebSocks = 1; + strlop(&URL[4], ' '); + strcpy(conn->WebURL, &URL[4]); + + ReplyLen = sprintf(Reply, ReplyMsg, Hash64); + + free(Hash64); + goto Returnit; + + } + } + + + ptr = strstr(URL, " HTTP"); + + if (ptr) + *ptr = 0; + + Method = strtok_s(URL, " ", &Context); + + memcpy(Mycall, &MYNODECALL, 10); + strlop(Mycall, ' '); + + + // APRS process internally + + if (_memicmp(Context, "/APRS/", 6) == 0 || _stricmp(Context, "/APRS") == 0) + { + APRSProcessHTTPMessage(sock, MsgPtr, LOCAL, COOKIE); + return 0; + } + + + if (_stricmp(Context, "/Node/Signon?Node") == 0) + { + char * IContext; + + NodeURL = strtok_s(Context, "?", &IContext); + Key = strtok_s(NULL, "?", &IContext); + + ProcessNodeSignon(sock, TCP, MsgPtr, Key, Reply, &Session, LOCAL); + return 0; + + } + + // If for Mail or Chat, check for a session, and send login screen if necessary + + // Moved here to simplify operation with both internal and external clients + + if (_memicmp(Context, "/Mail/", 6) == 0) + { + int RLen = 0; + char Appl; + char * input; + char * IContext; + + NodeURL = strtok_s(Context, "?", &IContext); + Key = strtok_s(NULL, "?", &IContext); + + if (_stricmp(NodeURL, "/Mail/Signon") == 0) + { + ReplyLen = ProcessMailSignon(TCP, MsgPtr, Key, Reply, &Session, FALSE, LOCAL); + + if (ReplyLen) + { + goto Returnit; + } + +#ifdef LINBPQ + strcpy(Context, "/Mail/Header"); +#else + strcpy(MsgPtr, "POST /Mail/Header"); +#endif + goto doHeader; + } + + if (_stricmp(NodeURL, "/Mail/Lost") == 0) + { + input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + + if (input && strstr(input, "Cancel=Exit")) + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + RLen = ReplyLen; + goto Returnit; + } + if (Key) + Appl = Key[0]; + + Key = 0; + } + + if (Key == 0 || Key[0] == 0) + { + // No Session + + // if not local send a signon screen, else create a user session + + if (LOCAL || COOKIE) + { + Session = AllocateSession(sock, 'M'); + + if (Session) + { + strcpy(Context, "/Mail/Header"); + goto doHeader; + } + else + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + ReplyLen += sprintf(&Reply[ReplyLen], "%s", BusyError); + } + RLen = ReplyLen; + + goto Returnit; + + } + + ReplyLen = sprintf(Reply, MailSignon, Mycall, Mycall); + + RLen = ReplyLen; + goto Returnit; + } + + Session = FindSession(Key); + + if (Session == NULL) + { + ReplyLen = sprintf(Reply, MailLostSession, Key); + RLen = ReplyLen; + goto Returnit; + } + } + + if (_memicmp(Context, "/Chat/", 6) == 0) + { + int RLen = 0; + char Appl; + char * input; + char * IContext; + + + HostPtr = strstr(MsgPtr, "Host: "); + + if (HostPtr) + { + uint32_t Host; + char Hostname[32]= ""; + struct LOCALNET * LocalNet = conn->TNC->TCPInfo->LocalNets; + + HostPtr += 6; + memcpy(Hostname, HostPtr, 31); + strlop(Hostname, ':'); + Host = inet_addr(Hostname); + + if (strcmp(Hostname, "127.0.0.1") == 0) + LOCAL = TRUE; + else while(LocalNet) + { + uint32_t MaskedHost = Host & LocalNet->Mask; + if (MaskedHost == LocalNet->Network) + { + char * rest; + LOCAL = 1; + rest = strchr(HostPtr, 13); + if (rest) + { + memmove(HostPtr + 9, rest, strlen(rest) + 1); + memcpy(HostPtr, "127.0.0.1", 9); + break; + } + } + LocalNet = LocalNet->Next; + } + } + + NodeURL = strtok_s(Context, "?", &IContext); + Key = strtok_s(NULL, "?", &IContext); + + if (_stricmp(NodeURL, "/Chat/Signon") == 0) + { + ReplyLen = ProcessChatSignon(TCP, MsgPtr, Key, Reply, &Session, LOCAL); + + if (ReplyLen) + { + goto Returnit; + } + +#ifdef LINBPQ + strcpy(Context, "/Chat/Header"); +#else + strcpy(MsgPtr, "POST /Chat/Header"); +#endif + goto doHeader; + } + + if (_stricmp(NodeURL, "/Chat/Lost") == 0) + { + input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + + if (input && strstr(input, "Cancel=Exit")) + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + RLen = ReplyLen; + goto Returnit; + } + if (Key) + Appl = Key[0]; + + Key = 0; + } + + if (Key == 0 || Key[0] == 0) + { + // No Session + + // if not local send a signon screen, else create a user session + + if (LOCAL || COOKIE) + { + Session = AllocateSession(sock, 'C'); + + if (Session) + { + strcpy(Context, "/Chat/Header"); + goto doHeader; + } + else + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + ReplyLen += sprintf(&Reply[ReplyLen], "%s", BusyError); + } + RLen = ReplyLen; + + goto Returnit; + + } + + ReplyLen = sprintf(Reply, ChatSignon, Mycall, Mycall); + + RLen = ReplyLen; + goto Returnit; + } + + Session = FindSession(Key); + + if (Session == NULL) + { + int Sent, Loops = 0; + ReplyLen = sprintf(Reply, MailLostSession, Key); + RLen = ReplyLen; +Returnit: + if (memcmp(Reply, "HTTP", 4) == 0) + { + // Full Header provided by appl - just send it + + // Send may block + + Sent = send(sock, Reply, ReplyLen, 0); + + while (Sent != ReplyLen && Loops++ < 3000) // 100 secs max + { + // Debugprintf("%d out of %d sent %d Loops", Sent, InputLen, Loops); + + if (Sent > 0) // something sent + { + InputLen -= Sent; + memmove(Reply, &Reply[Sent], ReplyLen); + } + + Sleep(30); + Sent = send(sock, Reply, ReplyLen, 0); + } + + return 0; + } + + // compress if allowed + + if (allowDeflate) + Compressed = Compressit(Reply, ReplyLen, &ReplyLen); + else + Compressed = Reply; + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n%s\r\n", ReplyLen, Encoding); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, Compressed, ReplyLen); + + if (allowDeflate) + free (Compressed); + + return 0; + } + } + +doHeader: + +#ifdef LINBPQ + + if ((_memicmp(Context, "/MAIL/", 6) == 0) || (_memicmp(Context, "/WebMail", 8) == 0)) + { + char _REPLYBUFFER[250000]; + struct HTTPConnectionInfo Dummy = {0}; + int Sent, Loops = 0; + + ReplyLen = 0; + + if (Session == 0) + Session = &Dummy; + + Session->TNC = (void *)LOCAL; // TNC only used for Web Terminal Sessions + + ProcessMailHTTPMessage(Session, Method, Context, MsgPtr, _REPLYBUFFER, &ReplyLen, MsgLen); + + if (memcmp(_REPLYBUFFER, "HTTP", 4) == 0) + { + // Full Header provided by appl - just send it + + // Send may block + + Sent = send(sock, _REPLYBUFFER, ReplyLen, 0); + + while (Sent != ReplyLen && Loops++ < 3000) // 100 secs max + { + // Debugprintf("%d out of %d sent %d Loops", Sent, InputLen, Loops); + + if (Sent > 0) // something sent + { + InputLen -= Sent; + memmove(_REPLYBUFFER, &_REPLYBUFFER[Sent], ReplyLen); + } + + Sleep(30); + Sent = send(sock, _REPLYBUFFER, ReplyLen, 0); + } + return 0; + } + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + + // Send may block + + Sent = send(sock, _REPLYBUFFER, ReplyLen, 0); + + if (Sent == -1) + return 0; + + while (Sent != ReplyLen && Loops++ < 3000) // 100 secs max + { + // Debugprintf("%d out of %d sent %d Loops", Sent, InputLen, Loops); + + if (Sent > 0) // something sent + { + InputLen -= Sent; + memmove(_REPLYBUFFER, &_REPLYBUFFER[Sent], ReplyLen); + } + + Sleep(30); + Sent = send(sock, _REPLYBUFFER, ReplyLen, 0); + } + + send(sock, Tail, (int)strlen(Tail), 0); + return 0; + + } + + if (_memicmp(Context, "/CHAT/", 6) == 0) + { + char _REPLYBUFFER[100000]; + + ReplyLen = 0; + + ProcessChatHTTPMessage(Session, Method, Context, MsgPtr, _REPLYBUFFER, &ReplyLen); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + + return 0; + + } + + + /* + Sent = send(sock, _REPLYBUFFER, InputLen, 0); + + while (Sent != InputLen && Loops++ < 3000) // 100 secs max + { + // Debugprintf("%d out of %d sent %d Loops", Sent, InputLen, Loops); + + if (Sent > 0) // something sent + { + InputLen -= Sent; + memmove(_REPLYBUFFER, &_REPLYBUFFER[Sent], InputLen); + } + + Sleep(30); + Sent = send(sock, _REPLYBUFFER, InputLen, 0); + } + return 0; + } + */ +#else + + // Pass to MailChat if active + + if ((_memicmp(Context, "/MAIL/", 6) == 0) || (_memicmp(Context, "/WebMail", 8) == 0)) + { + // If for Mail, Pass to Mail Server via Named Pipe + + HANDLE hPipe; + + hPipe = CreateFile(MAILPipeFileName, GENERIC_READ | GENERIC_WRITE, + 0, // exclusive access + NULL, // no security attrs + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + if (hPipe == (HANDLE)-1) + { + InputLen = sprintf(Reply, "HTTP/1.1 404 Not Found\r\nContent-Length: 28\r\n\r\nMail Data is not available\r\n"); + send(sock, Reply, InputLen, 0); + } + else + { + // int Sent; + int Loops = 0; + struct HTTPConnectionInfo Dummy = {0}; + + if (Session == 0) + Session = &Dummy; + + Session->TNC = LOCAL; // TNC is only used on Web Terminal Sessions so can reuse as LOCAL flag + + WriteFile(hPipe, Session, sizeof (struct HTTPConnectionInfo), &InputLen, NULL); + WriteFile(hPipe, MsgPtr, MsgLen, &InputLen, NULL); + + + ReadFile(hPipe, Session, sizeof (struct HTTPConnectionInfo), &InputLen, NULL); + ReadFile(hPipe, Reply, 250000, &ReplyLen, NULL); + if (ReplyLen <= 0) + { + InputLen = GetLastError(); + } + + CloseHandle(hPipe); + goto Returnit; + } + return 0; + } + + if (_memicmp(Context, "/CHAT/", 6) == 0) + { + HANDLE hPipe; + + hPipe = CreateFile(CHATPipeFileName, GENERIC_READ | GENERIC_WRITE, + 0, // exclusive access + NULL, // no security attrs + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + if (hPipe == (HANDLE)-1) + { + InputLen = sprintf(Reply, "HTTP/1.1 404 Not Found\r\nContent-Length: 28\r\n\r\nChat Data is not available\r\n"); + send(sock, Reply, InputLen, 0); + } + else + { + // int Sent; + int Loops = 0; + + WriteFile(hPipe, Session, sizeof (struct HTTPConnectionInfo), &InputLen, NULL); + WriteFile(hPipe, MsgPtr, MsgLen, &InputLen, NULL); + + + ReadFile(hPipe, Session, sizeof (struct HTTPConnectionInfo), &InputLen, NULL); + ReadFile(hPipe, Reply, 100000, &ReplyLen, NULL); + if (ReplyLen <= 0) + { + InputLen = GetLastError(); + } + + CloseHandle(hPipe); + goto Returnit; + } + return 0; + } + +#endif + + NodeURL = strtok_s(NULL, "?", &Context); + + if (NodeURL == NULL) + return 0; + + if (strcmp(Method, "POST") == 0) + { + if (_stricmp(NodeURL, "/Node/freqOffset") == 0) + { + char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + int port = atoi(Context); + + if (input == 0) + return 1; + + input += 4; + + if (port > 0 && port < 33) + { + struct TNCINFO * TNC = TNCInfo[port]; + char value[6]; + + if (TNC == 0) + return 1; + + TNC->TXOffset = atoi(input); +#ifdef WIN32 + sprintf(value, "%d", TNC->TXOffset); + MySetWindowText(TNC->xIDC_TXTUNEVAL, value); + SendMessage(TNC->xIDC_TXTUNE, TBM_SETPOS, (WPARAM) TRUE, (LPARAM) TNC->TXOffset); // min. & max. positions + +#endif + } + return 1; + } + + if (_stricmp(NodeURL, "/Node/PortAction") == 0) + { + char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + int port = atoi(Context); + + if (input == 0) + return 1; + + input += 4; + + if (port > 0 && port < 33) + { + struct TNCINFO * TNC = TNCInfo[port]; + + if (TNC == 0) + return 1; + + if (LOCAL == FALSE && COOKIE == FALSE) + return 1; + + if (strcmp(input, "Abort") == 0) + { + if (TNC->ForcedCloseProc) + TNC->ForcedCloseProc(TNC, 0); + } + else if (strcmp(input, "Kill") == 0) + { + TNC->DontRestart = TRUE; + KillTNC(TNC); + } + else if (strcmp(input, "KillRestart") == 0) + { + TNC->DontRestart = FALSE; + KillTNC(TNC); + RestartTNC(TNC); + + } + } + return 1; + } + + if (_stricmp(NodeURL, "/TermInput") == 0) + { + ProcessTermInput(sock, MsgPtr, MsgLen, Context); + return 0; + } + + if (_stricmp(NodeURL, "/Node/TermSignon") == 0) + { + ProcessTermSignon(conn->TNC, sock, MsgPtr, MsgLen, LOCAL); + } + + if (_stricmp(NodeURL, "/Node/Signon") == 0) + { + ProcessNodeSignon(sock, TCP, MsgPtr, Key, Reply, &Session, LOCAL); + return 0; + } + + if (_stricmp(NodeURL, "/Node/TermClose") == 0) + { + ProcessTermClose(sock, MsgPtr, MsgLen, Context, LOCAL); + return 0; + } + + if (_stricmp(NodeURL, "/Node/BeaconAction") == 0) + { + char Header[256]; + int HeaderLen; + char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + int Port; + char Param[100]; +#ifndef LINBPQ + int retCode, disp; + char Key[80]; + HKEY hKey; +#endif + struct PORTCONTROL * PORT; + int Slot = 0; + + + if (LOCAL == FALSE && COOKIE == FALSE) + { + // Send Not Authorized + + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "
Not authorized - please sign in"); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + + return 1; + } + + if (strstr(input, "Cancel=Cancel")) + { + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + + return 1; + } + + GetParam(input, "Port", &Param[0]); + Port = atoi(&Param[1]); + PORT = GetPortTableEntryFromPortNum(Port); // Need slot not number + if (PORT) + Slot = PORT->PortSlot; + + GetParam(input, "Every", &Param[0]); + Interval[Slot] = atoi(&Param[1]); + + + //extern char * UIUIDigi[33]; + //extern char UIUIDEST[33][11]; // Dest for Beacons + //extern UCHAR FN[33][256]; // Filename + //extern int [33]; // Beacon Interval (Mins) + //extern char Message[33][1000]; // Beacon Text + + + GetParam(input, "Dest", &Param[0]); + _strupr(Param); + strcpy(UIUIDEST[Slot], &Param[1]); + + GetParam(input, "Path", &Param[0]); + _strupr(Param); + if (UIUIDigi[Slot]) + free(UIUIDigi[Slot]); + UIUIDigi[Slot] = _strdup(&Param[1]); + + GetParam(input, "File", &Param[0]); + strcpy(FN[Slot], &Param[1]); + GetParam(input, "Text", &Param[0]); + strcpy(Message[Slot], &Param[1]); + + MinCounter[Slot] = Interval[Slot]; + + SendFromFile[Slot] = 0; + + if (FN[Slot][0]) + SendFromFile[Slot] = 1; + + SetupUI(Slot); + +#ifdef LINBPQ + SaveUIConfig(); +#else + SaveUIConfig(); + + wsprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\UIUtil\\UIPort%d", Port); + + retCode = RegCreateKeyEx(REGTREE, + Key, 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + retCode = RegSetValueEx(hKey, "UIDEST", 0, REG_SZ,(BYTE *)&UIUIDEST[Port][0], strlen(&UIUIDEST[Port][0])); + retCode = RegSetValueEx(hKey, "FileName", 0, REG_SZ,(BYTE *)&FN[Port][0], strlen(&FN[Port][0])); + retCode = RegSetValueEx(hKey, "Message", 0, REG_SZ,(BYTE *)&Message[Port][0], strlen(&Message[Port][0])); + retCode = RegSetValueEx(hKey, "Interval", 0, REG_DWORD,(BYTE *)&Interval[Port], 4); + retCode = RegSetValueEx(hKey, "SendFromFile", 0, REG_DWORD,(BYTE *)&SendFromFile[Port], 4); + retCode = RegSetValueEx(hKey, "Digis",0, REG_SZ, UIUIDigi[Port], strlen(UIUIDigi[Port])); + + RegCloseKey(hKey); + } +#endif + if (strstr(input, "Test=Test")) + SendUIBeacon(Slot); + + + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], Beacons, Port, + Interval[Slot], &UIUIDEST[Slot][0], &UIUIDigi[Slot][0], &FN[Slot][0], &Message[Slot][0], Port); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + + return 1; + } + + if (_stricmp(NodeURL, "/Node/CfgSave") == 0) + { + // Save Config File + + SaveConfigFile(sock, MsgPtr, Key, LOCAL); + return 0; + } + + if (_stricmp(NodeURL, "/Node/LogAction") == 0) + { + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + + return 1; + } + + + if (_stricmp(NodeURL, "/Node/ARDOPAbort") == 0) + { + int port = atoi(Context); + + if (port > 0 && port < 33) + { + struct TNCINFO * TNC = TNCInfo[port]; + + if (TNC && TNC->ForcedCloseProc) + TNC->ForcedCloseProc(TNC, 0); + + + if (TNC && TNC->WebWindowProc) + ReplyLen = TNC->WebWindowProc(TNC, _REPLYBUFFER, LOCAL); + + + ReplyLen = sprintf(Reply, "", "Ok"); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, Reply, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + + // goto SendResp; + + // HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + strlen(Tail)); + // send(sock, Header, HeaderLen, 0); + // send(sock, _REPLYBUFFER, ReplyLen, 0); + // send(sock, Tail, strlen(Tail), 0); + + return 1; + } + + } + + send(sock, _REPLYBUFFER, InputLen, 0); + return 0; + } + + if (_stricmp(NodeURL, "/") == 0 || _stricmp(NodeURL, "/Index.html") == 0) + { + // Send if present, else use default + + Bufferlen = SendMessageFile(sock, NodeURL, TRUE, allowDeflate); // return -1 if not found + + if (Bufferlen != -1) + return 0; // We've sent it + else + { + if (APRSApplConnected) + ReplyLen = sprintf(_REPLYBUFFER, Index, Mycall, Mycall); + else + ReplyLen = sprintf(_REPLYBUFFER, IndexNoAPRS, Mycall, Mycall); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + + return 0; + } + } + + else if (_stricmp(NodeURL, "/NodeMenu.html") == 0 || _stricmp(NodeURL, "/Node/NodeMenu.html") == 0) + { + // Send if present, else use default + + char Menu[] = "/NodeMenu.html"; + + Bufferlen = SendMessageFile(sock, Menu, TRUE, allowDeflate); // return -1 if not found + + if (Bufferlen != -1) + return 0; // We've sent it + } + + else if (_memicmp(NodeURL, "/aisdata.txt", 12) == 0) + { + char * Compressed; + ReplyLen = GetAISPageInfo(_REPLYBUFFER, 1, 1); + + if (allowDeflate) + Compressed = Compressit(_REPLYBUFFER, ReplyLen, &ReplyLen); + else + Compressed = _REPLYBUFFER; + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: Text\r\n%s\r\n", ReplyLen, Encoding); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, Compressed, ReplyLen); + + if (allowDeflate) + free (Compressed); + + return 0; + } + + else if (_memicmp(NodeURL, "/aprsdata.txt", 13) == 0) + { + char * Compressed; + char * ptr; + double N, S, W, E; + int aprs = 1, ais = 1, adsb = 1; + + ptr = &NodeURL[14]; + + N = atof(ptr); + ptr = strlop(ptr, '|'); + S = atof(ptr); + ptr = strlop(ptr, '|'); + W = atof(ptr); + ptr = strlop(ptr, '|'); + E = atof(ptr); + ptr = strlop(ptr, '|'); + if (ptr) + { + aprs = atoi(ptr); + ptr = strlop(ptr, '|'); + ais = atoi(ptr); + ptr = strlop(ptr, '|'); + adsb = atoi(ptr); + } + ReplyLen = GetAPRSPageInfo(_REPLYBUFFER, N, S, W, E, aprs, ais, adsb); + + if (ReplyLen < 240000) + ReplyLen += GetAISPageInfo(&_REPLYBUFFER[ReplyLen], ais, adsb); + + if (allowDeflate) + Compressed = Compressit(_REPLYBUFFER, ReplyLen, &ReplyLen); + else + Compressed = _REPLYBUFFER; + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nAccess-Control-Allow-Origin: *\r\nContent-Length: %d\r\nContent-Type: Text\r\n%s\r\n", ReplyLen, Encoding); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, Compressed, ReplyLen); + + if (allowDeflate) + free (Compressed); + + return 0; + } + + else if (_memicmp(NodeURL, "/Icon", 5) == 0 && _memicmp(&NodeURL[10], ".png", 4) == 0) + { + // APRS internal Icon + + char * Compressed; + + ReplyLen = GetAPRSIcon(_REPLYBUFFER, NodeURL); + + if (allowDeflate) + Compressed = Compressit(_REPLYBUFFER, ReplyLen, &ReplyLen); + else + Compressed = _REPLYBUFFER; + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: Text\r\n%s\r\n", ReplyLen, Encoding); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, Compressed, ReplyLen); + + if (allowDeflate) + free (Compressed); + + return 0; + + } + + else if (_memicmp(NodeURL, "/NODE/", 6)) + { + // Not Node, See if a local file + + Bufferlen = SendMessageFile(sock, NodeURL, FALSE, allowDeflate); // Send error if not found + return 0; + } + + // Node URL + + { + + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + + if (_stricmp(NodeURL, "/Node/webproc.css") == 0) + { + char WebprocCSS[] = + ".dropbtn {position: relative; border: 1px solid black;padding:1px;}\r\n" + ".dropdown {position: relative; display: inline-block;}\r\n" + ".dropdown-content {display: none; position: absolute;background-color: #ccc; " + "min-width: 160px; box-shadow: 0px 8px 16px 0px rgba(0,0,00.2); z-index: 1;}\r\n" + ".dropdown-content a {color: black; padding: 1px 1px;text-decoration:none;display:block;}" + ".dropdown-content a:hover {background-color: #dddfff;}\r\n" + ".dropdown:hover .dropdown-content {display: block;}\r\n" + ".dropdown:hover .dropbtn {background-color: #ddd;}\r\n" + "input.btn:active {background:black;color:white;}\r\n" + "submit.btn:active {background:black;color:white;}\r\n"; + ReplyLen = sprintf(_REPLYBUFFER, "%s", WebprocCSS); + } + + else if (_stricmp(NodeURL, "/Node/Killandrestart") == 0) + { + int port = atoi(Context); + + if (port > 0 && port < 33) + { + struct TNCINFO * TNC = TNCInfo[port]; + + KillTNC(TNC); + TNC->DontRestart = FALSE; + RestartTNC(TNC); + + if (TNC && TNC->WebWindowProc) + ReplyLen = TNC->WebWindowProc(TNC, _REPLYBUFFER, LOCAL); + + } + } + + else if (_stricmp(NodeURL, "/Node/Port") == 0 || _stricmp(NodeURL, "/Node/ARDOPAbort") == 0) + { + int port = atoi(Context); + + if (port > 0 && port < 33) + { + struct TNCINFO * TNC = TNCInfo[port]; + + if (TNC && TNC->WebWindowProc) + ReplyLen = TNC->WebWindowProc(TNC, _REPLYBUFFER, LOCAL); + } + + } + + else if (_stricmp(NodeURL, "/Node/Streams") == 0) + { + ReplyLen = StatusProc(_REPLYBUFFER); + } + + else if (_stricmp(NodeURL, "/Node/Stats.html") == 0) + { + struct tm * TM; + char UPTime[50]; + time_t szClock = STATSTIME * 60; + + TM = gmtime(&szClock); + + sprintf(UPTime, "%.2d:%.2d:%.2d", TM->tm_yday, TM->tm_hour, TM->tm_min); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", StatsHddr); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "", + "Version", VersionString); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "", + "Uptime (Days Hours Mins)", UPTime); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "", + "Semaphore: Get-Rel/Clashes", Semaphore.Gets - Semaphore.Rels, Semaphore.Clashes); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "", + "Buffers: Max/Cur/Min/Out/Wait", MAXBUFFS, QCOUNT, MINBUFFCOUNT, NOBUFFCOUNT, BUFFERWAITS); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "", + "Known Nodes/Max Nodes", NUMBEROFNODES, MAXDESTS); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "", + "L4 Connects Sent/Rxed ", L4CONNECTSOUT, L4CONNECTSIN); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "", + "L4 Frames TX/RX/Resent/Reseq", L4FRAMESTX, L4FRAMESRX, L4FRAMESRETRIED, OLDFRAMES); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "", + "L3 Frames Relayed", L3FRAMES); + + } + + else if (_stricmp(NodeURL, "/Node/RigControl.html") == 0) + { + char Test[] = + "\r\n" + "Rigcontrol\r\n" + "\r\n" + "\r\n" + "\r\n" + "
Waiting for data...
\r\n" + "\r\n"; + + + char NoRigCtl[] = + "\r\n" + "Rigcontrol\r\n" + "\r\n" + "\r\n" + "
RigControl Not Configured...
\r\n" + "\r\n"; + + if (RigWebPage) + ReplyLen = sprintf(_REPLYBUFFER, "%s", Test); + else + ReplyLen = sprintf(_REPLYBUFFER, "%s", NoRigCtl); + } + + else if (_stricmp(NodeURL, "/Node/ShowLog.html") == 0) + { + char ShowLogPage[] = + "" + "" + "Log Display" + "" + "
" + "
" +// "" + "" + "" + "
"; + + char * _REPLYBUFFER; + int ReplyLen; + char Header[256]; + int HeaderLen; + char * CfgBytes; + int CfgLen; + char inputname[250]; + FILE *fp1; + struct stat STAT; + char DummyKey[] = "DummyKey"; + time_t T; + struct tm * tm; + char Name[64] = ""; + + T = time(NULL); + tm = gmtime(&T); + + if (LOCAL == FALSE && COOKIE == FALSE) + { + // Send Not Authorized + + char _REPLYBUFFER[4096]; + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "
Not authorized - please sign in"); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + return 1; + } + + if (COOKIE == FALSE) + Key = DummyKey; + + if (memcmp(Context, "date=", 5) == 0) + { + memset(tm, 0, sizeof(struct tm)); + tm->tm_year = atoi(&Context[5]) - 1900; + tm->tm_mon = atoi(&Context[10]) - 1; + tm->tm_mday = atoi(&Context[13]); + } + + + + if (strcmp(Context, "input=Back") == 0) + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, Reply, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + return 1; + } + + if (LogDirectory[0] == 0) + { + strcpy(inputname, "logs/"); + } + else + { + strcpy(inputname,LogDirectory); + strcat(inputname,"/"); + strcat(inputname, "/logs/"); + } + + if (strstr(Context, "CMS")) + { + sprintf(Name, "CMSAccess_%04d%02d%02d.log", + tm->tm_year +1900, tm->tm_mon+1, tm->tm_mday); + } + else if (strstr(Context, "Debug")) + { + sprintf(Name, "log_%02d%02d%02d_DEBUG.txt", + tm->tm_year - 100, tm->tm_mon+1, tm->tm_mday); + } + else if (strstr(Context, "BBS")) + { + sprintf(Name, "log_%02d%02d%02d_BBS.txt", + tm->tm_year - 100, tm->tm_mon+1, tm->tm_mday); + } + else if (strstr(Context, "Chat")) + { + sprintf(Name, "log_%02d%02d%02d_CHAT.txt", + tm->tm_year - 100, tm->tm_mon+1, tm->tm_mday); + } + else if (strstr(Context, "Telnet")) + { + sprintf(Name, "Telnet_%02d%02d%02d.log", + tm->tm_year - 100, tm->tm_mon+1, tm->tm_mday); + } + + strcat(inputname, Name); + + if (stat(inputname, &STAT) == -1) + { + CfgBytes = malloc(256); + sprintf(CfgBytes, "Log %s not found", inputname); + CfgLen = strlen(CfgBytes); + } + else + { + fp1 = fopen(inputname, "rb"); + + if (fp1 == 0) + { + CfgBytes = malloc(256); + sprintf(CfgBytes, "Log %s not found", inputname); + CfgLen = strlen(CfgBytes); + } + else + { + CfgLen = STAT.st_size; + + CfgBytes = malloc(CfgLen + 1); + + CfgLen = (int)fread(CfgBytes, 1, CfgLen, fp1); + CfgBytes[CfgLen] = 0; + } + } + + _REPLYBUFFER = malloc(CfgLen + 1000); + + ReplyLen = sprintf(_REPLYBUFFER, ShowLogPage, CfgBytes); + free (CfgBytes); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, _REPLYBUFFER, ReplyLen); + sendandcheck(sock, Tail, (int)strlen(Tail)); + free (_REPLYBUFFER); + + return 1; + } + + else if (_stricmp(NodeURL, "/Node/EditCfg.html") == 0) + { + char * _REPLYBUFFER; + int ReplyLen; + char Header[256]; + int HeaderLen; + char * CfgBytes; + int CfgLen; + char inputname[250]="bpq32.cfg"; + FILE *fp1; + struct stat STAT; + char DummyKey[] = "DummyKey"; + + if (LOCAL == FALSE && COOKIE == FALSE) + { + // Send Not Authorized + + char _REPLYBUFFER[4096]; + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "
Not authorized - please sign in"); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + return 1; + } + + if (COOKIE ==FALSE) + Key = DummyKey; + + if (BPQDirectory[0] == 0) + { + strcpy(inputname, "bpq32.cfg"); + } + else + { + strcpy(inputname,BPQDirectory); + strcat(inputname,"/"); + strcat(inputname, "bpq32.cfg"); + } + + + if (stat(inputname, &STAT) == -1) + { + CfgBytes = _strdup("Config File not found"); + } + else + { + fp1 = fopen(inputname, "rb"); + + if (fp1 == 0) + { + CfgBytes = _strdup("Config File not found"); + } + else + { + CfgLen = STAT.st_size; + + CfgBytes = malloc(CfgLen + 1); + + CfgLen = (int)fread(CfgBytes, 1, CfgLen, fp1); + CfgBytes[CfgLen] = 0; + } + } + + _REPLYBUFFER = malloc(CfgLen + 1000); + + ReplyLen = sprintf(_REPLYBUFFER, ConfigEditPage, Key, CfgBytes); + free (CfgBytes); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, _REPLYBUFFER, ReplyLen); + sendandcheck(sock, Tail, (int)strlen(Tail)); + free (_REPLYBUFFER); + + return 1; + } + + + + if (_stricmp(NodeURL, "/Node/PortBeacons") == 0) + { + char * PortChar = strtok_s(NULL, "&", &Context); + int PortNo = atoi(PortChar); + struct PORTCONTROL * PORT; + int PortSlot = 0; + + PORT = GetPortTableEntryFromPortNum(PortNo); // Need slot not number + if (PORT) + PortSlot = PORT->PortSlot; + + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], Beacons, PortNo, + Interval[PortSlot], &UIUIDEST[PortSlot][0], &UIUIDigi[PortSlot][0], &FN[PortSlot][0], &Message[PortSlot][0], PortNo); + } + + + + if (_stricmp(NodeURL, "/Node/PortStats") == 0) + { + struct _EXTPORTDATA * Port; + + char * PortChar = strtok_s(NULL, "&", &Context); + int PortNo = atoi(PortChar); + int Protocol; + int PortType; + + // char PORTTYPE; // H/W TYPE + // 0 = ASYNC, 2 = PC120, 4 = DRSI + // 6 = TOSH, 8 = QUAD, 10 = RLC100 + // 12 = RLC400 14 = INTERNAL 16 = EXTERNAL + +#define KISS 0 +#define NETROM 2 +#define HDLC 6 +#define L2 8 +#define WINMOR 10 + + + // char PROTOCOL; // PORT PROTOCOL + // 0 = KISS, 2 = NETROM, 4 = BPQKISS + //; 6 = HDLC, 8 = L2 + + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsHddr, PortNo); + + Port = (struct _EXTPORTDATA *)GetPortTableEntryFromPortNum(PortNo); + + if (Port == NULL) + { + ReplyLen = sprintf(_REPLYBUFFER, "Invalid Port"); + goto SendResp; + } + + Protocol = Port->PORTCONTROL.PROTOCOL; + PortType = Port->PORTCONTROL.PROTOCOL; + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "L2 Frames Digied", Port->PORTCONTROL.L2DIGIED); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "L2 Frames Heard", Port->PORTCONTROL.L2FRAMES); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "L2 Frames Received", Port->PORTCONTROL.L2FRAMESFORUS); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "L2 Frames Sent", Port->PORTCONTROL.L2FRAMESSENT); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "L2 Timeouts", Port->PORTCONTROL.L2TIMEOUTS); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "REJ Frames Received", Port->PORTCONTROL.L2REJCOUNT); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "RX out of Seq", Port->PORTCONTROL.L2OUTOFSEQ); + // ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "L2 Resequenced", Port->PORTCONTROL.L2RESEQ); + if (Protocol == HDLC) + { + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "Underrun", Port->PORTCONTROL.L2URUNC); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "RX Overruns", Port->PORTCONTROL.L2ORUNC); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "RX CRC Errors", Port->PORTCONTROL.RXERRORS); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "Frames abandoned", Port->PORTCONTROL.L1DISCARD); + } + else if ((Protocol == KISS && Port->PORTCONTROL.KISSFLAGS) || Protocol == NETROM) + { + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "Poll Timeout", Port->PORTCONTROL.L2URUNC); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "RX CRC Errors", Port->PORTCONTROL.RXERRORS); + } + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "FRMRs Sent", Port->PORTCONTROL.L2FRMRTX); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "FRMRs Received", Port->PORTCONTROL.L2FRMRRX); + + // DB 'Link Active % ' + // DD AVSENDING + + } + + if (_stricmp(NodeURL, "/Node/Ports.html") == 0) + { + struct _EXTPORTDATA * ExtPort; + struct PORTCONTROL * Port; + + int count; + char DLL[20]; + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", PortsHddr); + + for (count = 1; count <= NUMBEROFPORTS; count++) + { + Port = GetPortTableEntryFromSlot(count); + ExtPort = (struct _EXTPORTDATA *)Port; + + if (Port->PORTTYPE == 0x10) + { + strcpy(DLL, ExtPort->PORT_DLL_NAME); + strlop(DLL, '.'); + } + else if (Port->PORTTYPE == 0) + strcpy(DLL, "ASYNC"); + + else if (Port->PORTTYPE == 22) + strcpy(DLL, "I2C"); + + else if (Port->PORTTYPE == 14) + strcpy(DLL, "INTERNAL"); + + else if (Port->PORTTYPE > 0 && Port->PORTTYPE < 14) + strcpy(DLL, "HDLC"); + + + if (Port->TNC && Port->TNC->WebWindowProc) // Has a Window + { + if (Port->UICAPABLE) + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortLineWithBeaconAndDriver, Port->PORTNUMBER, DLL, + Port->PORTDESCRIPTION, Port->PORTNUMBER, Port->PORTNUMBER, Port->TNC->WebWinX, Port->TNC->WebWinY, 200, 200); + else + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortLineWithDriver, Port->PORTNUMBER, DLL, + Port->PORTDESCRIPTION, Port->PORTNUMBER, Port->TNC->WebWinX, Port->TNC->WebWinY, 200, 200); + + continue; + } + + if (Port->PORTTYPE == 16 && Port->PROTOCOL == 10 && Port->UICAPABLE == 0) // EXTERNAL, Pactor/WINMO + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], SessionPortLine, Port->PORTNUMBER, DLL, + Port->PORTDESCRIPTION, Port->PORTNUMBER); + else + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortLineWithBeacon, Port->PORTNUMBER, Port->PORTNUMBER, + DLL, DLL, Port->PORTDESCRIPTION, Port->PORTNUMBER); + } + + if (RigActive) + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], RigControlLine, 33, "Rig Control", "Rig Control", 600, 350, 200, 200); + + } + + if (_stricmp(NodeURL, "/Node/Nodes.html") == 0) + { + struct DEST_LIST * Dests = DESTS; + int count, i; + char Normcall[10]; + char Alias[10]; + int Width = 5; + int x = 0, n = 0; + struct DEST_LIST * List[1000]; + char Param = 0; + + if (Context) + { + _strupr(Context); + Param = Context[0]; + } + + for (count = 0; count < MAXDESTS; count++) + { + if (Dests->DEST_CALL[0] != 0) + { + if (Param != 'T' || Dests->DEST_COUNT) + List[n++] = Dests; + + if (n > 999) + break; + } + + Dests++; + } + + if (n > 1) + { + if (Param == 'C') + qsort(List, n, sizeof(void *), CompareNode); + else + qsort(List, n, sizeof(void *), CompareAlias); + } + + Alias[6] = 0; + + if (Param == 'T') + { + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], NodeHddr, "with traffic"); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], ""); + } + else if (Param == 'C') + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], NodeHddr, "sorted by Call"); + else + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], NodeHddr, "sorted by Alias"); + + for (i = 0; i < n; i++) + { + int len = ConvFromAX25(List[i]->DEST_CALL, Normcall); + Normcall[len]=0; + + memcpy(Alias, List[i]->DEST_ALIAS, 6); + strlop(Alias, ' '); + + if (Param == 'T') + { + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "", + Normcall, Alias, List[i]->DEST_COUNT, List[i]->DEST_RTT /16, + (List[i]->DEST_STATE & 0x40)? 'B':' ', (List[i]->DEST_STATE & 63)); + + } + else + { + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], NodeLine, Normcall, Alias, Normcall); + + if (++x == Width) + { + x = 0; + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], ""); + } + } + } + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], ""); + } + + if (_stricmp(NodeURL, "/Node/NodeDetail") == 0) + { + UCHAR AXCall[8]; + struct DEST_LIST * Dest = DESTS; + struct NR_DEST_ROUTE_ENTRY * NRRoute; + struct ROUTE * Neighbour; + char Normcall[10]; + int i, len, count, Active; + char Alias[7]; + + Alias[6] = 0; + + _strupr(Context); + + ConvToAX25(Context, AXCall); + + for (count = 0; count < MAXDESTS; count++) + { + if (CompareCalls(Dest->DEST_CALL, AXCall)) + { + break; + } + Dest++; + } + + if (count == MAXDESTS) + { + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "

Call %s not found

", Context); + goto SendResp; + } + + memcpy(Alias, Dest->DEST_ALIAS, 6); + strlop(Alias, ' '); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], + "

Info for Node %s:%s

", Alias, Context); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "

PortDriverIDBeaconsDriver Window
%d %s%s
%d %s%s Beacons
%d%s%s
%d%s%s Driver Window
%d%s%s BeaconsDriver Window
%d%s%s Rig Control
%s%s
%s%s
%s%d%d
%s%d%d%d%d%d
%s%d%d
%s%d%d
%s%d%d%d%d
%s%d
CallFramesRTTBPQ?Hops
%s:%s%d%d%c%.0d
"); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "
FramesRTTBPQ?Hops
%d%d%c%.0d
", + Dest->DEST_COUNT, Dest->DEST_RTT /16, + (Dest->DEST_STATE & 0x40)? 'B':' ', (Dest->DEST_STATE & 63)); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "

Neighbours

"); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], + "" + ""); + + NRRoute = &Dest->NRROUTE[0]; + + Active = Dest->DEST_ROUTE; + + for (i = 1; i < 4; i++) + { + Neighbour = NRRoute->ROUT_NEIGHBOUR; + + if (Neighbour) + { + len = ConvFromAX25(Neighbour->NEIGHBOUR_CALL, Normcall); + Normcall[len] = 0; + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "", + (Active == i)?'>':' ',NRRoute->ROUT_QUALITY, NRRoute->ROUT_OBSCOUNT, Neighbour->NEIGHBOUR_PORT, Normcall); + } + NRRoute++; + } + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "
Qual Obs Port Call
%c %d%d%d%s
"); + + goto SendResp; + + } + /* + + MOV ESI,OFFSET32 NODEROUTEHDDR + MOV ECX,11 + REP MOVSB + + LEA ESI,DEST_CALL[EBX] + CALL DECODENODENAME ; CONVERT TO ALIAS:CALL + REP MOVSB + + CMP DEST_RTT[EBX],0 + JE SHORT @f ; TIMER NOT SET - DEST PROBABLY NOT USED + + MOVSB ; ADD SPACE + CALL DORTT + + @@: + MOV AL,CR + STOSB + + MOV ECX,3 + MOV DH,DEST_ROUTE[EBX] ; CURRENT ACTIVE ROUTE + MOV DL,1 + + push ebx + + PUBLIC CMDN110 + CMDN110: + + MOV ESI,ROUT1_NEIGHBOUR[EBX] + CMP ESI,0 + JE CMDN199 + + + MOV AX,' ' + CMP DH,DL + JNE SHORT CMDN112 ; NOT CURRENT DEST + MOV AX,' >' + + CMDN112: + + STOSW + + PUSH ECX + + MOV AL,ROUT1_QUALITY[EBX] + CALL CONV_DIGITS ; CONVERT AL TO DECIMAL DIGITS + + mov AL,' ' + stosb + + MOV AL,ROUT1_OBSCOUNT[EBX] + CALL CONV_DIGITS ; CONVERT AL TO DECIMAL DIGITS + + mov AL,' ' + stosb + + MOV AL,NEIGHBOUR_PORT[ESI] ; GET PORT + CALL CONV_DIGITS ; CONVERT AL TO DECIMAL DIGITS + + mov AL,' ' + stosb + + + PUSH EDI + CALL CONVFROMAX25 ; CONVERT TO CALL + POP EDI + + MOV ESI,OFFSET32 NORMCALL + REP MOVSB + + MOV AL,CR + STOSB + + ADD EBX,ROUTEVECLEN + INC DL ; ROUTE NUMBER + + POP ECX + DEC ECX + JNZ CMDN110 + + PUBLIC CMDN199 + CMDN199: + + POP EBX + + ; DISPLAY INP3 ROUTES + + MOV ECX,3 + MOV DL,4 + + PUBLIC CMDNINP3 + CMDNINP3: + + MOV ESI,INPROUT1_NEIGHBOUR[EBX] + CMP ESI,0 + JE CMDNINPEND + + MOV AX,' ' + CMP DH,DL + JNE SHORT @F ; NOT CURRENT DEST + MOV AX,' >' + + @@: + + STOSW + + PUSH ECX + + MOV AL, Hops1[EBX] + CALL CONV_DIGITS ; CONVERT AL TO DECIMAL DIGITS + + mov AL,' ' + stosb + + MOVZX EAX, SRTT1[EBX] + + MOV EDX,0 + MOV ECX, 100 + DIV ECX + CALL CONV_5DIGITS + MOV AL,'.' + STOSB + MOV EAX, EDX + CALL PRINTNUM + MOV AL,'s' + STOSB + MOV AL,' ' + STOSB + + MOV AL,NEIGHBOUR_PORT[ESI] ; GET PORT + CALL CONV_DIGITS ; CONVERT AL TO DECIMAL DIGITS + + mov AL,' ' + stosb + + PUSH EDI + CALL CONVFROMAX25 ; CONVERT TO CALL + POP EDI + + MOV ESI,OFFSET32 NORMCALL + REP MOVSB + + + MOV AL,CR + STOSB + + ADD EBX,INPROUTEVECLEN + INC DL ; ROUTE NUMBER + + POP ECX + LOOP CMDNINP3 + + CMDNINPEND: + + ret + + */ + + + if (_stricmp(NodeURL, "/Node/Routes.html") == 0) + { + struct ROUTE * Routes = NEIGHBOURS; + int MaxRoutes = MAXNEIGHBOURS; + int count; + char Normcall[10]; + char locked; + int NodeCount; + int Percent = 0; + int Iframes, Retries; + char Active[10]; + int Queued; + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", RouteHddr); + + for (count=0; countNEIGHBOUR_CALL[0] != 0) + { + int len = ConvFromAX25(Routes->NEIGHBOUR_CALL, Normcall); + Normcall[len]=0; + + if ((Routes->NEIGHBOUR_FLAG & 1) == 1) + locked = '!'; + else + locked = ' '; + + NodeCount = COUNTNODES(Routes); + + if (Routes->NEIGHBOUR_LINK) + Queued = COUNT_AT_L2(Routes->NEIGHBOUR_LINK); + else + Queued = 0; + + Iframes = Routes->NBOUR_IFRAMES; + Retries = Routes->NBOUR_RETRIES; + + if (Routes->NEIGHBOUR_LINK && Routes->NEIGHBOUR_LINK->L2STATE >= 5) + strcpy(Active, ">"); + else + strcpy(Active, " "); + + if (Iframes) + Percent = (Retries * 100) / Iframes; + else + Percent = 0; + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], RouteLine, Active, Routes->NEIGHBOUR_PORT, Normcall, locked, + Routes->NEIGHBOUR_QUAL, NodeCount, Iframes, Retries, Percent, Routes->NBOUR_MAXFRAME, Routes->NBOUR_FRACK, + Routes->NEIGHBOUR_TIME >> 8, Routes->NEIGHBOUR_TIME & 0xff, Queued, Routes->OtherendsRouteQual); + } + Routes+=1; + } + } + + if (_stricmp(NodeURL, "/Node/Links.html") == 0) + { + struct _LINKTABLE * Links = LINKS; + int MaxLinks = MAXLINKS; + int count; + char Normcall1[10]; + char Normcall2[10]; + char State[12] = "", Type[12] = "Uplink"; + int axState; + int cctType; + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", LinkHddr); + + for (count=0; countLINKCALL[0] != 0) + { + int len = ConvFromAX25(Links->LINKCALL, Normcall1); + Normcall1[len] = 0; + + len = ConvFromAX25(Links->OURCALL, Normcall2); + Normcall2[len] = 0; + + axState = Links->L2STATE; + + if (axState == 2) + strcpy(State, "Connecting"); + else if (axState == 3) + strcpy(State, "FRMR"); + else if (axState == 4) + strcpy(State, "Closing"); + else if (axState == 5) + strcpy(State, "Active"); + else if (axState == 6) + strcpy(State, "REJ Sent"); + + cctType = Links->LINKTYPE; + + if (cctType == 1) + strcpy(Type, "Uplink"); + else if (cctType == 2) + strcpy(Type, "Downlink"); + else if (cctType == 3) + strcpy(Type, "Node-Node"); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], LinkLine, Normcall1, Normcall2, Links->LINKPORT->PORTNUMBER, + State, Type, 2 - Links->VER1FLAG ); + + Links+=1; + } + } + } + + if (_stricmp(NodeURL, "/Node/Users.html") == 0) + { + TRANSPORTENTRY * L4 = L4TABLE; + TRANSPORTENTRY * Partner; + int MaxLinks = MAXLINKS; + int count; + char State[12] = "", Type[12] = "Uplink"; + char LHS[50] = "", MID[10] = "", RHS[50] = ""; + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", UserHddr); + + for (count=0; count < MAXCIRCUITS; count++) + { + if (L4->L4USER[0]) + { + RHS[0] = MID[0] = 0; + + if ((L4->L4CIRCUITTYPE & UPLINK) == 0) //SHORT CMDS10A ; YES + { + // IF DOWNLINK, ONLY DISPLAY IF NO CROSSLINK + + if (L4->L4CROSSLINK == 0) //jne CMDS60 ; WILL PROCESS FROM OTHER END + { + // ITS A DOWNLINK WITH NO PARTNER - MUST BE A CLOSING SESSION + // DISPLAY TO THE RIGHT FOR NOW + + strcpy(LHS, "(Closing) "); + DISPLAYCIRCUIT(L4, RHS); + goto CMDS50; + } + else + goto CMDS60; // WILL PROCESS FROM OTHER END + } + + if (L4->L4CROSSLINK == 0) + { + // Single Entry + + DISPLAYCIRCUIT(L4, LHS); + } + else + { + DISPLAYCIRCUIT(L4, LHS); + + Partner = L4->L4CROSSLINK; + + if (Partner->L4STATE == 5) + strcpy(MID, "<-->"); + else + strcpy(MID, "<~~>"); + + DISPLAYCIRCUIT(Partner, RHS); + } +CMDS50: + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], UserLine, LHS, MID, RHS); + } +CMDS60: + L4++; + } + } + /* + PUBLIC CMDUXX_1 + CMDUXX_1: + push EBX + push ESI + PUSH ECX + push EDI + + call _FINDDESTINATION + pop EDI + + jz SHORT NODE_FOUND + + push EDI ; NET/ROM not found + call CONVFROMAX25 ; CONVERT TO CALL + pop EDI + mov ESI,OFFSET32 NORMCALL + rep movsb + + jmp SHORT END_CMDUXX + + PUBLIC NODE_FOUND + NODE_FOUND: + + lea ESI,DEST_CALL[EBX] + call DECODENODENAME + + REP MOVSB + + PUBLIC END_CMDUXX + END_CMDUXX: + + POP ECX + pop ESI + pop EBX + ret + + }}} + */ + + else if (_stricmp(NodeURL, "/Node/Terminal.html") == 0) + { + if (COOKIE && Session) + { + // Already signed in as sysop + + struct UserRec * USER = Session->USER; + + struct HTTPConnectionInfo * NewSession = AllocateSession(sock, 'T'); + + if (NewSession) + { + char AXCall[10]; + ReplyLen = sprintf(_REPLYBUFFER, TermPage, Mycall, Mycall, NewSession->Key, NewSession->Key, NewSession->Key); + strcpy(NewSession->HTTPCall, USER->Callsign); + ConvToAX25(NewSession->HTTPCall, AXCall); + ChangeSessionCallsign(NewSession->Stream, AXCall); + BPQHOSTVECTOR[NewSession->Stream -1].HOSTSESSION->Secure_Session = USER->Secure; + Session->USER = USER; + NewSession->TNC = conn->TNC; + + + // if (Appl[0]) + // { + // strcat(Appl, "\r"); + // SendMsg(Session->Stream, Appl, strlen(Appl)); + // } + + } + else + { + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", BusyError); + } + } + else if (LOCAL) + { + // connected to 127.0.0.1 so sign in using node call + + struct HTTPConnectionInfo * NewSession = AllocateSession(sock, 'T'); + + if (NewSession) + { + ReplyLen = sprintf(_REPLYBUFFER, TermPage, Mycall, Mycall, NewSession->Key, NewSession->Key, NewSession->Key); + strcpy(NewSession->HTTPCall, MYNODECALL); + ChangeSessionCallsign(NewSession->Stream, MYCALL); + BPQHOSTVECTOR[NewSession->Stream -1].HOSTSESSION->Secure_Session = TRUE; + NewSession->TNC = conn->TNC; + } + } + else + ReplyLen = sprintf(_REPLYBUFFER, TermSignon, Mycall, Mycall, Context); + } + + else if (_stricmp(NodeURL, "/Node/Signon.html") == 0) + { + ReplyLen = sprintf(_REPLYBUFFER, NodeSignon, Mycall, Mycall, Context); + } + + else if (_stricmp(NodeURL, "/Node/Drivers") == 0) + { + int Bufferlen = SendMessageFile(sock, "/Drivers.htm", TRUE, allowDeflate); // return -1 if not found + + if (Bufferlen != -1) + return 0; // We've sent it + } + + else if (_stricmp(NodeURL, "/Node/OutputScreen.html") == 0) + { + struct HTTPConnectionInfo * Session = FindSession(Context); + + if (Session == NULL) + { + ReplyLen = sprintf(_REPLYBUFFER, "%s", LostSession); + } + else + { + Session->sock = sock; // socket to reply on + ReplyLen = RefreshTermWindow(TCP, Session, _REPLYBUFFER); + + if (ReplyLen == 0) // Nothing new + { + // Debugprintf("GET with no data avail - response held"); + Session->ResponseTimer = 1200; // Delay response for up to a minute + } + else + { + // Debugprintf("GET - outpur sent, timer was %d, set to zero", Session->ResponseTimer); + Session->ResponseTimer = 0; + } + + Session->KillTimer = 0; + return 0; // Refresh has sent any available output + } + } + + else if (_stricmp(NodeURL, "/Node/InputLine.html") == 0) + { + struct TNCINFO * TNC = conn->TNC; + struct TCPINFO * TCP = 0; + + if (TNC) + TCP = TNC->TCPInfo; + + if (TCP && TCP->WebTermCSS) + ReplyLen = sprintf(_REPLYBUFFER, InputLine, Context, TCP->WebTermCSS); + else + ReplyLen = sprintf(_REPLYBUFFER, InputLine, Context, ""); + + } + + else if (_stricmp(NodeURL, "/Node/PTT") == 0) + { + struct TNCINFO * TNC = conn->TNC; + int x = atoi(Context); + } + + +SendResp: + + FormatTime3(TimeString, time(NULL)); + + strcpy(&_REPLYBUFFER[ReplyLen], Tail); + ReplyLen += (int)strlen(Tail); + + + if (allowDeflate) + { + Compressed = Compressit(_REPLYBUFFER, ReplyLen, &ReplyLen); + } + else + { + Encoding[0] = 0; + Compressed = _REPLYBUFFER; + } + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n" + "Date: %s\r\n%s\r\n", ReplyLen, TimeString, Encoding); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, Compressed, ReplyLen); + + if (allowDeflate) + free (Compressed); + } + return 0; + +#ifdef WIN32 + } +#include "StdExcept.c" +} +return 0; +#endif +} + +void ProcessHTTPMessage(void * conn) +{ + // conn is a malloc'ed copy to handle reused connections, so need to free it + + InnerProcessHTTPMessage((struct ConnectionInfo *)conn); + free(conn); + return; +} + +static char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; +static char *dat[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + + +VOID FormatTime3(char * Time, time_t cTime) +{ + struct tm * TM; + TM = gmtime(&cTime); + + sprintf(Time, "%s, %02d %s %3d %02d:%02d:%02d GMT", dat[TM->tm_wday], TM->tm_mday, month[TM->tm_mon], + TM->tm_year + 1900, TM->tm_hour, TM->tm_min, TM->tm_sec); + +} + +// Sun, 06 Nov 1994 08:49:37 GMT + +int StatusProc(char * Buff) +{ + int i; + char callsign[12] = ""; + char flag[3]; + UINT Mask, MaskCopy; + int Flags; + int AppNumber; + int OneBits; + int Len = sprintf(Buff, "" + "Stream Status"); + + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], ""); + + for (i=1;i<65; i++) + { + callsign[0]=0; + + if (GetAllocationState(i)) + + strcpy(flag,"*"); + else + strcpy(flag," "); + + GetCallsign(i,callsign); + + Mask = MaskCopy = Get_APPLMASK(i); + + // if only one bit set, convert to number + + AppNumber = 0; + OneBits = 0; + + while (MaskCopy) + { + if (MaskCopy & 1) + OneBits++; + + AppNumber++; + MaskCopy = MaskCopy >> 1; + } + + Flags=GetApplFlags(i); + + if (OneBits > 1) + Len += sprintf(&Buff[Len], "" + "", + i, flag, RXCount(i), TXCount(i), MONCount(i), Mask, Flags, callsign, BPQHOSTVECTOR[i-1].PgmName); + + else + Len += sprintf(&Buff[Len], "" + "", + i, flag, RXCount(i), TXCount(i), MONCount(i), AppNumber, Flags, callsign, BPQHOSTVECTOR[i-1].PgmName); + + if ((i & 1) == 0) + Len += sprintf(&Buff[Len], ""); + + } + + Len += sprintf(&Buff[Len], "
    RX   TX   MON  App  Flg Callsign  Program    RX   TX   MON  App  Flg Callsign  Program
%d%s%d%d%d%x%x%s%s%d%s%d%d%d%d%x%s%s
"); + return Len; +} + +int ProcessNodeSignon(SOCKET sock, struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, int LOCAL) +{ + int ReplyLen = 0; + char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + char * user, * password, * Key; + char Header[256]; + int HeaderLen; + struct HTTPConnectionInfo *Sess; + + + if (input) + { + int i; + struct UserRec * USER; + + UndoTransparency(input); + + if (strstr(input, "Cancel=Cancel")) + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n" + "\r\n", (int)(ReplyLen + strlen(Tail))); + send(sock, Header, HeaderLen, 0); + send(sock, Reply, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + } + user = strtok_s(&input[9], "&", &Key); + password = strtok_s(NULL, "=", &Key); + password = Key; + + for (i = 0; i < TCP->NumberofUsers; i++) + { + USER = TCP->UserRecPtr[i]; + + if (user && _stricmp(user, USER->UserName) == 0) + { + if (strcmp(password, USER->Password) == 0 && USER->Secure) + { + // ok + + Sess = *Session = AllocateSession(sock, 'N'); + Sess->USER = USER; + + ReplyLen = SetupNodeMenu(Reply, LOCAL); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n" + "Set-Cookie: BPQSessionCookie=%s; Path = /\r\n\r\n", (int)(ReplyLen + strlen(Tail)), Sess->Key); + send(sock, Header, HeaderLen, 0); + send(sock, Reply, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + + return ReplyLen; + } + } + } + } + + ReplyLen = sprintf(Reply, NodeSignon, Mycall, Mycall); + ReplyLen += sprintf(&Reply[ReplyLen], "%s", PassError); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", (int)(ReplyLen + strlen(Tail))); + send(sock, Header, HeaderLen, 0); + send(sock, Reply, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + + return 0; + + + return ReplyLen; +} + + + + +int ProcessMailSignon(struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, BOOL WebMail, int LOCAL) +{ + int ReplyLen = 0; + char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + char * user, * password, * Key; + struct HTTPConnectionInfo * NewSession; + + if (input) + { + int i; + struct UserRec * USER; + + UndoTransparency(input); + + if (strstr(input, "Cancel=Cancel")) + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + return ReplyLen; + } + user = strtok_s(&input[9], "&", &Key); + password = strtok_s(NULL, "=", &Key); + password = Key; + + for (i = 0; i < TCP->NumberofUsers; i++) + { + USER = TCP->UserRecPtr[i]; + + if (user && _stricmp(user, USER->UserName) == 0) + { + if (strcmp(password, USER->Password) == 0 && (USER->Secure || WebMail)) + { + // ok + + NewSession = AllocateSession(Appl[0], 'M'); + + *Session = NewSession; + + if (NewSession) + { + + ReplyLen = 0; + strcpy(NewSession->Callsign, USER->Callsign); + } + else + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + ReplyLen += sprintf(&Reply[ReplyLen], "%s", BusyError); + } + return ReplyLen; + } + } + } + } + + ReplyLen = sprintf(Reply, MailSignon, Mycall, Mycall); + ReplyLen += sprintf(&Reply[ReplyLen], "%s", PassError); + + return ReplyLen; +} + + +int ProcessChatSignon(struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, int LOCAL) +{ + int ReplyLen = 0; + char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + char * user, * password, * Key; + + if (input) + { + int i; + struct UserRec * USER; + + UndoTransparency(input); + + if (strstr(input, "Cancel=Cancel")) + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + return ReplyLen; + } + + user = strtok_s(&input[9], "&", &Key); + password = strtok_s(NULL, "=", &Key); + password = Key; + + + for (i = 0; i < TCP->NumberofUsers; i++) + { + USER = TCP->UserRecPtr[i]; + + if (user && _stricmp(user, USER->UserName) == 0) + { + if (strcmp(password, USER->Password) == 0 && USER->Secure) + { + // ok + + *Session = AllocateSession(Appl[0], 'C'); + + if (Session) + { + ReplyLen = 0; + } + else + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + ReplyLen += sprintf(&Reply[ReplyLen], "%s", BusyError); + } + return ReplyLen; + } + } + } + } + + ReplyLen = sprintf(Reply, ChatSignon, Mycall, Mycall); + ReplyLen += sprintf(&Reply[ReplyLen], "%s", PassError); + + return ReplyLen; + +} + +#ifdef WIN32 + +#include +#include + +#define SHA1_HASH_LEN 20 + +BOOL SHA1PasswordHash(char * lpszPassword, char * Hash) +{ + HCRYPTPROV hCryptProv; // Handle to our context + HCRYPTHASH hCryptHash; // Handle to our hash + BYTE bHashValue[SHA1_HASH_LEN]; // This will hold our SHA-1 hash + DWORD dwSize = SHA1_HASH_LEN; // Size of output + BOOL bSuccess = FALSE; // We change this to TRUE if we complete the operations + // Declare all the variables at the start of our code for C89 compatability + + if(CryptAcquireContext(&hCryptProv, NULL, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) + { // Initiate usage of the functions + if(CryptCreateHash(hCryptProv, CALG_SHA1, 0, 0, &hCryptHash)) + { // Create a SHA1 hash + if(CryptHashData(hCryptHash, (PBYTE)lpszPassword, lstrlen(lpszPassword) * sizeof(TCHAR), 0)) + { // Update the hash, (process our password) + if(CryptGetHashParam(hCryptHash, HP_HASHVAL, bHashValue, &dwSize, 0)) + { // Extract the hash + + memcpy(Hash, bHashValue, 20); + bSuccess = TRUE; + } + } + CryptDestroyHash(hCryptHash); + } + CryptReleaseContext(hCryptProv, 0); + } + + return bSuccess; +} + +#else + +#include + +BOOL SHA1PasswordHash(char * data, char * Hash) +{ + SHA1(data, strlen(data), Hash); + return 1; +} +#endif + +int BuildRigCtlPage(char * _REPLYBUFFER) +{ + int ReplyLen; + + struct RIGPORTINFO * PORT; + struct RIGINFO * RIG; + int p, i; + + char Page[] = + "\r\n" + // "\r\n" + "Rigcontrol\r\n" + "" + "

Rigcontrol

\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + ""; + char RigLine[] = + "\r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n"; + char Tail[] = + "
RadioFreqModeSTPorts
%s%s%s/1%c%c%s
\r\n" + "\r\n"; + + ReplyLen = sprintf(_REPLYBUFFER, "%s", Page); + + for (p = 0; p < NumberofPorts; p++) + { + PORT = PORTInfo[p]; + + for (i=0; i< PORT->ConfiguredRigs; i++) + { + RIG = &PORT->Rigs[i]; + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], RigLine, RIG->WEB_Label, RIG->WEB_FREQ, RIG->WEB_MODE, RIG->WEB_SCAN, RIG->WEB_PTT, RIG->WEB_PORTS, RIG->Interlock); + } + } + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", Tail); + return ReplyLen; +} + + +void SendRigWebPage() +{ + int i, n; + struct ConnectionInfo * sockptr; + struct TNCINFO * TNC; + struct TCPINFO * TCP; + + for (i = 0; i < 33; i++) + { + TNC = TNCInfo[i]; + + if (TNC && TNC->Hardware == H_TELNET) + { + TCP = TNC->TCPInfo; + + if (TCP) + { + for (n = 0; n <= TCP->MaxSessions; n++) + { + sockptr = TNC->Streams[n].ConnectionInfo; + + if (sockptr->SocketActive) + { + if (sockptr->HTTPMode && sockptr->WebSocks && strcmp(sockptr->WebURL, "RIGCTL") == 0) + { + char RigMsg[8192]; + int RigMsgLen = strlen(RigWebPage); + char* ptr; + + RigMsg[0] = 0x81; // Fin, Data + RigMsg[1] = 126; // Unmasked, Extended Len + RigMsg[2] = RigMsgLen >> 8; + RigMsg[3] = RigMsgLen & 0xff; + strcpy(&RigMsg[4], RigWebPage); + + // If secure session enable PTT button + + if (sockptr->WebSecure) + { + while (ptr = strstr(RigMsg, "hidden")) + memcpy(ptr, " ", 6); + } + + send(sockptr->socket, RigMsg, RigMsgLen + 4, 0); + } + } + } + } + } + } +} + +// Webmail web socket code + +int ProcessWebmailWebSock(char * MsgPtr, char * OutBuffer); + +void ProcessWebmailWebSockThread(void * conn) +{ + // conn is a malloc'ed copy to handle reused connections, so need to free it + + struct ConnectionInfo * sockptr = (struct ConnectionInfo *)conn; + char * URL = sockptr->WebURL; + int Loops = 0; + int Sent; + int InputLen; + struct HTTPConnectionInfo Dummy = {0}; + int ReplyLen = 0; + +#ifdef LINBPQ + + char _REPLYBUFFER[250000]; + + ReplyLen = ProcessWebmailWebSock(URL, _REPLYBUFFER); + + // Send may block + + Sent = send(sockptr->socket, _REPLYBUFFER, ReplyLen, 0); + + while (Sent != ReplyLen && Loops++ < 3000) // 100 secs max + { + if (Sent > 0) // something sent + { + ReplyLen -= Sent; + memmove(_REPLYBUFFER, &_REPLYBUFFER[Sent], ReplyLen); + } + + Sleep(30); + Sent = send(sockptr->socket, _REPLYBUFFER, ReplyLen, 0); + } + +#else + // Send URL to BPQMail via Pipe. Just need a dummy session, as URL contains session key + + HANDLE hPipe; + char Reply[250000]; + + + + hPipe = CreateFile(MAILPipeFileName, GENERIC_READ | GENERIC_WRITE, + 0, // exclusive access + NULL, // no security attrs + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + if (hPipe == (HANDLE)-1) + { + free(conn); + return; + } + + WriteFile(hPipe, &Dummy, sizeof (struct HTTPConnectionInfo), &InputLen, NULL); + WriteFile(hPipe, URL, strlen(URL), &InputLen, NULL); + + ReadFile(hPipe, &Dummy, sizeof (struct HTTPConnectionInfo), &InputLen, NULL); + ReadFile(hPipe, Reply, 250000, &ReplyLen, NULL); + + if (ReplyLen <= 0) + { + InputLen = GetLastError(); + } + + CloseHandle(hPipe); + + // ?? do we need a thread to handle write which may block + + Sent = send(sockptr->socket, Reply, ReplyLen, 0); + + while (Sent != ReplyLen && Loops++ < 3000) // 100 secs max + { + // Debugprintf("%d out of %d sent %d Loops", Sent, InputLen, Loops); + + if (Sent > 0) // something sent + { + InputLen -= Sent; + memmove(Reply, &Reply[Sent], ReplyLen); + } + + Sleep(30); + Sent = send(sockptr->socket, Reply, ReplyLen, 0); + } +#endif + free(conn); + return; +} + + + + diff --git a/RigControl-HPLaptop.c b/RigControl-HPLaptop.c new file mode 100644 index 0000000..e0d38be --- /dev/null +++ b/RigControl-HPLaptop.c @@ -0,0 +1,9179 @@ +/* +Copyright 2001-2022 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 +*/ + +// +// Rig Control Module +// + +// Dec 29 2009 + +// Add Scan Control for SCS + +// August 2010 + +// Fix logic error in Port Initialisation (wasn't always raising RTS and DTR +// Clear RTS and DTR on close + +// Fix Kenwood processing of multiple messages in one packet. + +// Fix reporting of set errors in scan to the wrong session + + +// Yaesu List + +// FT990 define as FT100 + + +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_DEPRECATE + + +#include +#include +#include "time.h" + +#include "CHeaders.h" +#include "tncinfo.h" +#ifdef WIN32 +#include +#else +char *fcvt(double number, int ndigits, int *decpt, int *sign); +#include +#include +#include +#endif +#include "bpq32.h" + +#include "hidapi.h" + +extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + +int Row = -20; + +extern struct PORTCONTROL * PORTTABLE; + +VOID __cdecl Debugprintf(const char * format, ...); + +struct RIGINFO * RigConfig(struct TNCINFO * TNC, char * buf, int Port); +struct RIGPORTINFO * CreateTTYInfo(int port, int speed); +BOOL OpenConnection(int); +BOOL SetupConnection(int); +BOOL RigCloseConnection(struct RIGPORTINFO * PORT); +BOOL RigWriteCommBlock(struct RIGPORTINFO * PORT); +BOOL DestroyTTYInfo(int port); +void CheckRX(struct RIGPORTINFO * PORT); +static int OpenRigCOMMPort(struct RIGPORTINFO * PORT, VOID * Port, int Speed); +VOID ICOMPoll(struct RIGPORTINFO * PORT); +VOID ProcessFrame(struct RIGPORTINFO * PORT, UCHAR * rxbuff, int len); +VOID ProcessICOMFrame(struct RIGPORTINFO * PORT, UCHAR * rxbuffer, int Len); +int SendResponse(int Stream, char * Msg); +VOID ProcessYaesuFrame(struct RIGPORTINFO * PORT); +VOID YaesuPoll(struct RIGPORTINFO * PORT); +VOID ProcessYaesuCmdAck(struct RIGPORTINFO * PORT); +VOID ProcessKenwoodFrame(struct RIGPORTINFO * PORT, int Length); +VOID KenwoodPoll(struct RIGPORTINFO * PORT); +VOID DummyPoll(struct RIGPORTINFO * PORT); +VOID SwitchAntenna(struct RIGINFO * RIG, char Antenna); +VOID DoBandwidthandAntenna(struct RIGINFO *RIG, struct ScanEntry * ptr); +VOID SetupScanInterLockGroups(struct RIGINFO *RIG); +VOID ProcessFT100Frame(struct RIGPORTINFO * PORT); +VOID ProcessFT990Frame(struct RIGPORTINFO * PORT); +VOID ProcessFT1000Frame(struct RIGPORTINFO * PORT); +VOID AddNMEAChecksum(char * msg); +VOID ProcessNMEA(struct RIGPORTINFO * PORT, char * NMEAMsg, int len); +VOID COMSetDTR(HANDLE fd); +VOID COMClearDTR(HANDLE fd); +VOID COMSetRTS(HANDLE fd); +VOID COMClearRTS(HANDLE fd); +void CM108_set_ptt(struct RIGINFO *RIG, int PTTState); +BOOL OpenHIDPort(struct RIGPORTINFO * PORT, VOID * Port, int Speed); +int HID_Read_Block(struct RIGPORTINFO * PORT); +int HID_Write_Block(struct RIGPORTINFO * PORT); +HANDLE rawhid_open(char * Device); +int rawhid_recv(int num, void *buf, int len, int timeout); +int rawhid_send(int num, void *buf, int len, int timeout); +void rawhid_close(int num); +VOID ConnecttoHAMLIB(struct RIGPORTINFO * PORT); +VOID ConnecttoFLRIG(struct RIGPORTINFO * PORT); +int DecodeHAMLIBAddr(struct RIGPORTINFO * PORT, char * ptr); +void ProcessHAMLIBFrame(struct RIGPORTINFO * PORT, int Length); +VOID HAMLIBPoll(struct RIGPORTINFO * PORT); +void HAMLIBSlaveThread(struct RIGINFO * RIG); +void CheckAndProcessRTLUDP(struct RIGPORTINFO * PORT); +VOID RTLUDPPoll(struct RIGPORTINFO * PORT); +VOID ConnecttoRTLUDP(struct RIGPORTINFO * PORT); +VOID FLRIGPoll(struct RIGPORTINFO * PORT); +void ProcessFLRIGFrame(struct RIGPORTINFO * PORT); +VOID FLRIGSendCommand(struct RIGPORTINFO * PORT, char * Command, char * Value); + +VOID SetupPortRIGPointers(); +VOID PTTCATThread(struct RIGINFO *RIG); +VOID ConnecttoHAMLIB(struct RIGPORTINFO * PORT); + +int SendPTCRadioCommand(struct TNCINFO * TNC, char * Block, int Length); +int GetPTCRadioCommand(struct TNCINFO * TNC, char * Block); +int BuildRigCtlPage(char * _REPLYBUFFER); +void SendRigWebPage(); + +extern TRANSPORTENTRY * L4TABLE; +HANDLE hInstance; + +VOID APIENTRY CreateOneTimePassword(char * Password, char * KeyPhrase, int TimeOffset); +BOOL APIENTRY CheckOneTimePassword(char * Password, char * KeyPhrase); + +char * GetApplCallFromName(char * App); + +char Modes[25][6] = {"LSB", "USB", "AM", "CW", "RTTY", "FM", "WFM", "CW-R", "RTTY-R", + "????","????","????","????","????","????","????","????","DV", "LSBD1", + "USBD1", "LSBD2","USBD2", "LSBD3","USBD3", "????"}; + +/* +DV = 17 +F8101 +(0000=LSB, 0001=USB, 0002=AM, +0003=CW, 0004=RTTY, +0018=LSB D1, 0019=USB D1, +0020=LSB D2, 0021=USB D2, +0022=LSB D3, 0023=USB D3 +*/ +// 0 1 2 3 4 5 6 7 8 9 0A 0B 0C 88 + +char YaesuModes[16][6] = {"LSB", "USB", "CW", "CWR", "AM", "", "", "", "FM", "", "DIG", "", "PKT", "FMN", "????"}; + +char FT100Modes[9][6] = {"LSB", "USB", "CW", "CWR", "AM", "DIG", "FM", "WFM", "????"}; + +char FT990Modes[13][6] = {"LSB", "USB", "CW2k4", "CW500", "AM6k", "AM2k4", "FM", "FM", "RTTYL", "RTTYU", "PKTL", "PKTFM", "????"}; + +char FT1000Modes[13][6] = {"LSB", "USB", "CW", "CWR", "AM", "AMS", "FM", "WFM", "RTTYL", "RTTYU", "PKTL", "PKTF", "????"}; + +char FTRXModes[8][6] = {"LSB", "USB", "CW", "AM", "FM", "RTTY", "PKT", ""}; + +char KenwoodModes[16][6] = {"????", "LSB", "USB", "CW", "FM", "AM", "FSK", "????"}; + +//char FT2000Modes[16][6] = {"????", "LSB", "USB", "CW", "FM", "AM", "FSK", "PKT-L", "FSK-R", "PKT-FM", "FM-N", "PKT-U", "????"}; +char FT2000Modes[16][6] = {"????", "LSB", "USB", "CW", "FM", "AM", "FSK", "CW-R", "PKT-L", "FSK-R", "PKT-FM", "FM-N", "PKT-U", "????"}; + +char FT991AModes[16][9] = {"????", "LSB", "USB", "CW-U", "FM", "AM", "RTTY-LSB", "CW-L", "DATA-LSB", "RTTY-USB", "DATA-FM", "FM-N", "DATA-USB", "AM-N", "C4FM", "????"}; + +char FLEXModes[16][6] = {"LSB", "USB", "DSB", "CWL", "CWU", "FM", "AM", "DIGU", "SPEC", "DIGL", "SAM", "DRM"}; + +char AuthPassword[100] = ""; + +char LastPassword[17]; + +int NumberofPorts = 0; + +BOOL EndPTTCATThread = FALSE; + +int HAMLIBMasterRunning = 0; +int HAMLIBSlaveRunning = 0; +int FLRIGRunning = 0; + +char * RigWebPage = 0; +int RigWebPageLen = 0; + + +struct RIGPORTINFO * PORTInfo[34] = {NULL}; // Records are Malloc'd + +struct RIGINFO * DLLRIG = NULL; // Rig record for dll PTT interface (currently only for UZ7HO); + + +struct TimeScan * AllocateTimeRec(struct RIGINFO * RIG) +{ + struct TimeScan * Band = zalloc(sizeof (struct TimeScan)); + + RIG->TimeBands = realloc(RIG->TimeBands, (++RIG->NumberofBands+2) * sizeof(void *)); + RIG->TimeBands[RIG->NumberofBands] = Band; + RIG->TimeBands[RIG->NumberofBands+1] = NULL; + + return Band; +} + +struct ScanEntry ** CheckTimeBands(struct RIGINFO * RIG) +{ + int i = 0; + time_t NOW = time(NULL) % 86400; + + // Find TimeBand + + while (i < RIG->NumberofBands) + { + if (RIG->TimeBands[i + 1]->Start > NOW) + { + break; + } + i++; + } + + RIG->FreqPtr = RIG->TimeBands[i]->Scanlist; + + return RIG->FreqPtr; +} + +VOID Rig_PTTEx(struct RIGINFO * RIG, BOOL PTTState, struct TNCINFO * TNC); + +VOID Rig_PTT(struct TNCINFO * TNC, BOOL PTTState) +{ + if (TNC == NULL) return; + + if (TNC->TXRIG) + Rig_PTTEx(TNC->TXRIG, PTTState, TNC); + else + Rig_PTTEx(TNC->RIG, PTTState, TNC); +} + +VOID Rig_PTTEx(struct RIGINFO * RIG, BOOL PTTState, struct TNCINFO * TNC) +{ + struct RIGPORTINFO * PORT; + int i, Len; + char cmd[32]; + char onString[128]; // Actual CAT strings to send. May be modified for QSY on PTT + char offString[128]; + int onLen = 0, offLen = 0; + + if (RIG == NULL) return; + + PORT = RIG->PORT; + + if (PORT == NULL) + return; + + // CAT string defaults to that set up by RIGConfig in RIG->PTTOn and RIG->PTTOff, + // but can be overidden by Port specify strings from TNC->PTTOn and TNC->PTTOff. + // If PTTSetsFreq is set on a Rig, that overrides the RIG->PTTOn but not TNC->PTTOn + + + if (PTTState) + { + MySetWindowText(RIG->hPTT, "T"); + RIG->WEB_PTT = 'T'; + RIG->PTTTimer = PTTLimit; + RIG->repeatPTTOFFTimer = 0; // Cancel repeated off command + + if (TNC && TNC->PTTOn[0]) + { + memcpy(onString, TNC->PTTOn, TNC->PTTOnLen); + onLen = TNC->PTTOnLen; + } + else + { + memcpy(onString, RIG->PTTOn, RIG->PTTOnLen); + onLen = RIG->PTTOnLen; + + // If PTT_SETS_FREQ set calculate TX Freq and see if changed + + // Freq can be set on the TNC, the RIG, or calculated from current rx freq + pttOffset + + if (TNC && RIG->PTTSetsFreq) + { + long long txfreq = 0; + + if (TNC->TXFreq) + txfreq = TNC->TXFreq + TNC->TXOffset + RIG->txError; + else if (TNC->RIG && TNC->RIG->txFreq) + txfreq = RIG->txFreq; // Used if not associated with a TNC port - eg HAMLIB + WSJT + else if (TNC->RIG && TNC->RIG->RigFreq != 0.0) + { + // Use RigFreq + pttOffset, so TX Tracks RX + + long long rxfreq = (long long)(TNC->RIG->RigFreq * 1000000.0) - TNC->RIG->rxOffset; + txfreq = rxfreq + RIG->pttOffset + RIG->txError; + txfreq += RIG->rxError; + } + + if (txfreq) + { + if (RIG->lastSetFreq != txfreq) + { + char FreqString[80]; + char * CmdPtr = onString; + + RIG->lastSetFreq = txfreq; + + // Convert to CAT string + + sprintf(FreqString, "%012d", txfreq); + + switch (PORT->PortType) + { + case ICOM: + + // CI-V must send all commands as one string, or Radio will start to ack them and + // collide with rest of command + + // Set Freq is sent before set PTT, so set up QSY string then copy PTT string to buffer + // Need to convert two chars to bcd digit + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x5; // Set frequency command + + *(CmdPtr++) = (FreqString[11] - 48) | ((FreqString[10] - 48) << 4); + *(CmdPtr++) = (FreqString[9] - 48) | ((FreqString[8] - 48) << 4); + *(CmdPtr++) = (FreqString[7] - 48) | ((FreqString[6] - 48) << 4); + *(CmdPtr++) = (FreqString[5] - 48) | ((FreqString[4] - 48) << 4); + if (RIG->IC735) + { + *(CmdPtr++) = 0xFD; + onLen = 10; + } + else + { + *(CmdPtr++) = (FreqString[3] - 48); + *(CmdPtr++) = 0xFD; + onLen = 11; + } + + // Now add PTT String + + memcpy(&onString[onLen], RIG->PTTOn, RIG->PTTOnLen); + onLen += RIG->PTTOnLen; + + break; + + case HAMLIB: + + // Dont need to save, as we can send strings separately + + Len = sprintf(cmd, "F %lld\n", txfreq); + i = send(PORT->remoteSock, cmd, Len, 0); + RIG->PollCounter = 100; // Don't read for 10 secs to avoid clash with PTT OFF + } + } + } + } + } + } + else + { + // Drop PTT + + MySetWindowText(RIG->hPTT, " "); + RIG->WEB_PTT = ' '; + RIG->PTTTimer = 0; + if (PORT->PortType == ICOM) + RIG->repeatPTTOFFTimer = 300; // set 30 second repeated off command + + if (TNC && TNC->PTTOff[0]) + { + memcpy(offString, TNC->PTTOff, TNC->PTTOffLen); + offLen = TNC->PTTOffLen; + RIG->lastSetFreq = 0; + } + else + { + memcpy(offString, RIG->PTTOff, RIG->PTTOffLen); + offLen = RIG->PTTOffLen; + + // If PTT_SETS_FREQ set calculate TX Freq and see if changed + + if (PTTState == 0 && RIG->PTTSetsFreq && RIG->defaultFreq) + { + // Dropped PTT. See if need to set freq back to default + + long long txfreq = RIG->defaultFreq + RIG->txError; + + if (RIG->lastSetFreq != txfreq) + { + char FreqString[80]; + char * CmdPtr = offString; + + RIG->lastSetFreq = txfreq; + + // Convert to CAT string + + sprintf(FreqString, "%012d", txfreq); + + switch (PORT->PortType) + { + case ICOM: + + // CI-V must send all commands as one string, or Radio will start to ack them and + // collide with rest of command + + // Set Freq is sent after drop PTT, so copy PTT string to buffer then set up QSY string + // Need to convert two chars to bcd digit + + // We copied off string earlier, so just append QSY string + + CmdPtr += offLen; + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x5; // Set frequency command + + *(CmdPtr++) = (FreqString[11] - 48) | ((FreqString[10] - 48) << 4); + *(CmdPtr++) = (FreqString[9] - 48) | ((FreqString[8] - 48) << 4); + *(CmdPtr++) = (FreqString[7] - 48) | ((FreqString[6] - 48) << 4); + *(CmdPtr++) = (FreqString[5] - 48) | ((FreqString[4] - 48) << 4); + if (RIG->IC735) + { + *(CmdPtr++) = 0xFD; + offLen += 10; + } + else + { + *(CmdPtr++) = (FreqString[3] - 48); + *(CmdPtr++) = 0xFD; + offLen += 11; + } + + case FLRIG: + + sprintf(cmd, "%lld", txfreq); + FLRIGSendCommand(PORT, "rig.set_vfo", cmd); + RIG->PollCounter = 100; // Don't read for 10 secs to avoid clash with PTT OFF + + case HAMLIB: + + // Dont need to save, as we can send strings separately + + Len = sprintf(cmd, "F %lld\n", txfreq); + send(PORT->remoteSock, cmd, Len, 0); + RIG->PollCounter = 100; // Don't read for 10 secs to avoid clash with PTT OFF + } + } + } + } + } + + // Now send the command + + if (RIG->PTTMode & PTTCI_V) + { + UCHAR * Poll = PORT->TXBuffer; + + // Don't read for 10 secs to avoid clash with PTT OFF + // Should do this for all rigs on port + + for (i = 0; i< PORT->ConfiguredRigs; i++) + PORT->Rigs[i].PollCounter = 100; + + PORT->AutoPoll = TRUE; + + switch (PORT->PortType) + { + case ICOM: + case KENWOOD: + case FT2000: + case FT991A: + case FLEX: + case NMEA: + + if (PTTState) + { + memcpy(Poll, onString, onLen); + PORT->TXLen = onLen; + } + else + { + memcpy(Poll, offString, offLen); + PORT->TXLen = offLen; + } + + RigWriteCommBlock(PORT); + + if (PORT->PortType == ICOM && !PTTState) + RigWriteCommBlock(PORT); // Send ICOM PTT OFF Twice + + PORT->Retries = 1; + + if (PORT->PortType != ICOM) + PORT->Timeout = 0; + + return; + + case FT100: + case FT990: + case FT1000: + + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = PTTState; // OFF/ON + *(Poll++) = 15; + + PORT->TXLen = 5; + RigWriteCommBlock(PORT); + + PORT->Retries = 1; + PORT->Timeout = 0; + + return; + + case YAESU: // 897 - maybe others + + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = PTTState ? 0x08 : 0x88; // CMD = 08 : PTT ON CMD = 88 : PTT OFF + + PORT->TXLen = 5; + RigWriteCommBlock(PORT); + + PORT->Retries = 1; + PORT->Timeout = 0; + + return; + + case FLRIG: + + sprintf(cmd, "%d", PTTState); + FLRIGSendCommand(PORT, "rig.set_ptt", cmd); + RIG->PollCounter = 100; // Don't read for 10 secs to avoid clash with PTT OFF + + return; + + case HAMLIB: + + Len = sprintf(cmd, "T %d\n", PTTState); + send(PORT->remoteSock, cmd, Len, 0); + RIG->PollCounter = 100; // Don't read for 10 secs to avoid clash with PTT OFF + + return; + } + } + + if (RIG->PTTMode & PTTRTS) + if (PTTState) + COMSetRTS(PORT->hPTTDevice); + else + COMClearRTS(PORT->hPTTDevice); + + if (RIG->PTTMode & PTTDTR) + if (PTTState) + COMSetDTR(PORT->hPTTDevice); + else + COMClearDTR(PORT->hPTTDevice); + + if (RIG->PTTMode & PTTCM108) + CM108_set_ptt(RIG, PTTState); + + if (RIG->PTTMode & PTTHAMLIB) + { + char Msg[16]; + int Len = sprintf(Msg, "T %d\n", PTTState); + + Len = send(PORT->remoteSock, Msg, Len, 0); + RIG->PollCounter = 100; // Don't read for 10 secs to avoid clash with PTT OFF + } + if (RIG->PTTMode & PTTFLRIG) + { + char cmd[32]; + + sprintf(cmd, "%d", PTTState); + FLRIGSendCommand(PORT, "rig.set_ptt", cmd); + RIG->PollCounter = 100; // Don't read for 10 secs to avoid clash with PTT OFF + } +} + +void saveNewFreq(struct RIGINFO * RIG, double Freq, char * Mode) +{ + if (Freq > 0.0) + { + _gcvt((Freq + RIG->rxOffset) / 1000000.0, 9, RIG->Valchar); + strcpy(RIG->WEB_FREQ, RIG->Valchar); + MySetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + } + + if (Mode[0]) + { + strcpy(RIG->ModeString, Mode); + MySetWindowText(RIG->hMODE, Mode); + } + +} + +// Need version that doesn't need Port Number + +int Rig_CommandEx(struct RIGPORTINFO * PORT, struct RIGINFO * RIG, int Session, char * Command); + +int Rig_Command(int Session, char * Command) +{ + char * ptr; + int i, n, p, Port; + TRANSPORTENTRY * L4 = L4TABLE; + struct RIGPORTINFO * PORT; + struct RIGINFO * RIG; + + // Only Allow RADIO from Secure Applications + + _strupr(Command); + + ptr = strchr(Command, 13); + if (ptr) *(ptr) = 0; // Null Terminate + + if (memcmp(Command, "AUTH ", 5) == 0) + { + if (AuthPassword[0] && (memcmp(LastPassword, &Command[5], 16) != 0)) + { + if (CheckOneTimePassword(&Command[5], AuthPassword)) + { + L4 += Session; + L4->Secure_Session = 1; + + sprintf(Command, "Ok\r"); + + memcpy(LastPassword, &Command[5], 16); // Save + + return FALSE; + } + } + + sprintf(Command, "Sorry AUTH failed\r"); + return FALSE; + } + + if (Session != -1) // Used for internal Stop/Start + { + L4 += Session; + + if (L4->Secure_Session == 0) + { + sprintf(Command, "Sorry - you are not allowed to use this command\r"); + return FALSE; + } + } + if (NumberofPorts == 0) + { + sprintf(Command, "Sorry - Rig Control not configured\r"); + return FALSE; + } + + // if Port starts with 'R' then select Radio (was Interlock) number, not BPQ Port + + if (Command[0] == 'R') + { + n = sscanf(&Command[1],"%d ", &Port); + + for (p = 0; p < NumberofPorts; p++) + { + PORT = PORTInfo[p]; + + for (i=0; i< PORT->ConfiguredRigs; i++) + { + RIG = &PORT->Rigs[i]; + + if (RIG->Interlock == Port) + goto portok; + } + } + + sprintf(Command, "Sorry - Port not found\r"); + return FALSE; + } + + n = sscanf(Command,"%d ", &Port); + + // Look for the port + + + for (p = 0; p < NumberofPorts; p++) + { + PORT = PORTInfo[p]; + + for (i=0; i< PORT->ConfiguredRigs; i++) + { + RIG = &PORT->Rigs[i]; + + if (RIG->BPQPort & (1 << Port)) + goto portok; + } + } + + sprintf(Command, "Sorry - Port not found\r"); + return FALSE; + +portok: + + return Rig_CommandEx(PORT, RIG, Session, Command); +} + + + +static char MsgHddr[] = "POST /RPC2 HTTP/1.1\r\n" + "User-Agent: XMLRPC++ 0.8\r\n" + "Host: 127.0.0.1:7362\r\n" + "Content-Type: text/xml\r\n" + "Content-length: %d\r\n" + "\r\n%s"; + +static char Req[] = "\r\n" + "%s\r\n" + "%s" + "\r\n"; + + + + +int Rig_CommandEx(struct RIGPORTINFO * PORT, struct RIGINFO * RIG, int Session, char * Command) +{ + int n, ModeNo, Filter, Port = 0; + double Freq = 0.0; + char FreqString[80]="", FilterString[80]="", Mode[80]="", Data[80] = "", Dummy[80] = ""; + struct MSGWITHOUTLEN * buffptr; + UCHAR * Poll; + + int i; + TRANSPORTENTRY * L4 = L4TABLE; + char * ptr; + int Split, DataFlag, Bandwidth, Antenna; + struct ScanEntry * FreqPtr; + char * CmdPtr; + int Len; + char MemoryBank = 0; // For Memory Scanning + int MemoryNumber = 0; + + // Only Allow RADIO from Secure Applications + + _strupr(Command); + + ptr = strchr(Command, 13); + if (ptr) *(ptr) = 0; // Null Terminate + + if (memcmp(Command, "AUTH ", 5) == 0) + { + if (AuthPassword[0] && (memcmp(LastPassword, &Command[5], 16) != 0)) + { + if (CheckOneTimePassword(&Command[5], AuthPassword)) + { + L4 += Session; + L4->Secure_Session = 1; + + sprintf(Command, "Ok\r"); + + memcpy(LastPassword, &Command[5], 16); // Save + + return FALSE; + } + } + + sprintf(Command, "Sorry AUTH failed\r"); + return FALSE; + } + + if (Session != -1) // Used for internal Stop/Start + { + L4 += Session; + + if (L4->Secure_Session == 0) + { + sprintf(Command, "Sorry - you are not allowed to use this command\r"); + return FALSE; + } + } + if (NumberofPorts == 0) + { + sprintf(Command, "Sorry - Rig Control not configured\r"); + return FALSE; + } + + // if Port starts with 'R' then select Radio (was Interlock) number, not BPQ Port + + if (Command[0] == 'R') + n = sscanf(Command,"%s %s %s %s %s", &Dummy, &FreqString[0], &Mode[0], &FilterString[0], &Data[0]); + else + n = sscanf(Command,"%d %s %s %s %s", &Port, &FreqString[0], &Mode[0], &FilterString[0], &Data[0]); + + if (_stricmp(FreqString, "CLOSE") == 0) + { + PORT->Closed = 1; + RigCloseConnection(PORT); + + MySetWindowText(RIG->hSCAN, "C"); + RIG->WEB_SCAN = 'C'; + + sprintf(Command, "Ok\r"); + return FALSE; + } + + if (_stricmp(FreqString, "OPEN") == 0) + { + PORT->ReopenDelay = 300; + PORT->Closed = 0; + + MySetWindowText(RIG->hSCAN, ""); + RIG->WEB_SCAN = ' '; + + sprintf(Command, "Ok\r"); + return FALSE; + } + + if (n > 1) + { + if (_stricmp(FreqString, "SCANSTART") == 0) + { + if (RIG->NumberofBands) + { + RIG->ScanStopped &= (0xffffffff ^ (1 << Port)); + + if (Session != -1) // Used for internal Stop/Start + RIG->ScanStopped &= 0xfffffffe; // Clear Manual Stopped Bit + + if (n > 2) + RIG->ScanCounter = atoi(Mode) * 10; //Start Delay + else + RIG->ScanCounter = 10; + + RIG->WaitingForPermission = FALSE; // In case stuck + + if (RIG->ScanStopped == 0) + { + SetWindowText(RIG->hSCAN, "S"); + RIG->WEB_SCAN = 'S'; + } + sprintf(Command, "Ok\r"); + + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 SCANSTART Port %d", Port); + } + else + sprintf(Command, "Sorry no Scan List defined for this port\r"); + + return FALSE; + } + + if (_stricmp(FreqString, "SCANSTOP") == 0) + { + RIG->ScanStopped |= (1 << Port); + + if (Session != -1) // Used for internal Stop/Start + RIG->ScanStopped |= 1; // Set Manual Stopped Bit + + MySetWindowText(RIG->hSCAN, ""); + RIG->WEB_SCAN = ' '; + + sprintf(Command, "Ok\r"); + + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 SCANSTOP Port %d", Port); + + RIG->PollCounter = 50 / RIG->PORT->ConfiguredRigs; // Dont read freq for 5 secs + + return FALSE; + } + } + + + + if (RIG->RIGOK == 0) + { + if (Session != -1) + { + if (PORT->Closed) + sprintf(Command, "Sorry - Radio port closed\r"); + else + sprintf(Command, "Sorry - Radio not responding\r"); + } + return FALSE; + } + + if (n == 2 && _stricmp(FreqString, "FREQ") == 0) + { + if (RIG->Valchar[0]) + sprintf(Command, "Frequency is %s MHz\r", RIG->Valchar); + else + sprintf(Command, "Frequency not known\r"); + + return FALSE; + } + + if (n == 2 && _stricmp(FreqString, "PTT") == 0) + { + Rig_PTTEx(RIG, TRUE, NULL); + RIG->PTTTimer = 10; // 1 sec + sprintf(Command, "Ok\r"); + return FALSE; + } + + RIG->Session = Session; // BPQ Stream + RIG->PollCounter = 50; // Dont read freq for 5 secs in case clash with Poll + + if (_stricmp(FreqString, "TUNE") == 0) + { + char ReqBuf[256]; + char SendBuff[256]; + char FLPoll[80]; + + switch (PORT->PortType) + { + case ICOM: + + buffptr = GetBuff(); + + if (buffptr == 0) + { + sprintf(Command, "Sorry - No Buffers available\r"); + return FALSE; + } + + + // Build a ScanEntry in the buffer + + FreqPtr = (struct ScanEntry *)buffptr->Data; + memset(FreqPtr, 0, sizeof(struct ScanEntry)); + + CmdPtr = FreqPtr->Cmd1 = FreqPtr->Cmd1Msg; + FreqPtr->Cmd2 = NULL; + FreqPtr->Cmd3 = NULL; + + // IC7100 Tune Fe fe 88 e0 1c 01 02 fd + + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x1C; + *(CmdPtr++) = 0x01; + *(CmdPtr++) = 0x02; + *(CmdPtr++) = 0xFD; + FreqPtr[0].Cmd1Len = 8; + + C_Q_ADD(&RIG->BPQtoRADIO_Q, buffptr); + + return TRUE; + + case KENWOOD: + + buffptr = GetBuff(); + + if (buffptr == 0) + { + sprintf(Command, "Sorry - No Buffers available\r"); + return FALSE; + } + + // Build a ScanEntry in the buffer + + FreqPtr = (struct ScanEntry *)buffptr->Data; + memset(FreqPtr, 0, sizeof(struct ScanEntry)); + + Poll = FreqPtr->Cmd1 = FreqPtr->Cmd1Msg; + + FreqPtr->Cmd1Len = sprintf(Poll, "AC111;AC;"); + + C_Q_ADD(&RIG->BPQtoRADIO_Q, buffptr); + return TRUE; + + case FLRIG: + + strcpy(FLPoll, "rig.tune"); + + Len = sprintf(ReqBuf, Req, FLPoll, ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + + if (PORT->CONNECTED) + { + if (send(PORT->remoteSock, SendBuff, Len, 0) != Len) + { + if (PORT->remoteSock) + closesocket(PORT->remoteSock); + + PORT->remoteSock = 0; + PORT->CONNECTED = FALSE; + PORT->hDevice = 0; + } + } + sprintf(Command, "Ok\r"); + return FALSE; + } + + sprintf(Command, "Sorry - TUNE not supported on your radio\r"); + return FALSE; + } + + if (_stricmp(FreqString, "POWER") == 0) + { + char PowerString[8] = ""; + int Power = atoi(Mode); + int len; + char cmd[80]; + + switch (PORT->PortType) + { + case ICOM: + + if (n != 3 || Power > 255) + { + strcpy(Command, "Sorry - Invalid Format - should be POWER Level (0 - 255)\r"); + return FALSE; + } + + sprintf(PowerString, "%04d", Power); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + sprintf(Command, "Sorry - No Buffers available\r"); + return FALSE; + } + + + // Build a ScanEntry in the buffer + + FreqPtr = (struct ScanEntry *)buffptr->Data; + memset(FreqPtr, 0, sizeof(struct ScanEntry)); + + CmdPtr = FreqPtr->Cmd1 = FreqPtr->Cmd1Msg; + FreqPtr->Cmd2 = NULL; + FreqPtr->Cmd3 = NULL; + + // IC7100 Set Power Fe fe 88 e0 14 0a xx xx fd + + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x14; + *(CmdPtr++) = 0x0A; + + // Need to convert param to decimal digits + + *(CmdPtr++) = (PowerString[1] - 48) | ((PowerString[0] - 48) << 4); + *(CmdPtr++) = (PowerString[3] - 48) | ((PowerString[2] - 48) << 4); + + *(CmdPtr++) = 0xFD; + FreqPtr[0].Cmd1Len = 9; + + C_Q_ADD(&RIG->BPQtoRADIO_Q, buffptr); + + return TRUE; + + case KENWOOD: + + if (n != 3 || Power > 200 || Power < 5) + { + strcpy(Command, "Sorry - Invalid Format - should be POWER Level (5 - 200)\r"); + return FALSE; + } + + buffptr = GetBuff(); + + if (buffptr == 0) + { + sprintf(Command, "Sorry - No Buffers available\r"); + return FALSE; + } + + // Build a ScanEntry in the buffer + + FreqPtr = (struct ScanEntry *)buffptr->Data; + memset(FreqPtr, 0, sizeof(struct ScanEntry)); + + Poll = FreqPtr->Cmd1 = FreqPtr->Cmd1Msg; + + FreqPtr->Cmd1Len = sprintf(Poll, "PC%03d;PC;", Power); + + C_Q_ADD(&RIG->BPQtoRADIO_Q, buffptr); + return TRUE; + + case FLRIG: + + len = sprintf(cmd, "%d", Power); + + FLRIGSendCommand(PORT, "rig.set_power", cmd); + + sprintf(Command, "Ok\r"); + return FALSE; + } + + sprintf(Command, "Sorry - POWER not supported on your Radio\r"); + return FALSE; + } + + if (_stricmp(FreqString, "CMD") == 0) + { + // Send arbitrary command to radio + + char c; + int val; + char * ptr1; + int Len; + + if (n < 3) + { + strcpy(Command, "Sorry - Invalid Format - should be HEX Hexstring\r"); + return FALSE; + } + + buffptr = GetBuff(); + + if (buffptr == NULL) + return FALSE; + + ptr1 = strstr(Command, "CMD"); + + if (ptr1 == NULL) + return FALSE; + + ptr1 += 4; + + + FreqPtr = (struct ScanEntry *)buffptr->Data; + memset(FreqPtr, 0, sizeof(struct ScanEntry)); + + CmdPtr = FreqPtr->Cmd1 = FreqPtr->Cmd1Msg; + FreqPtr->Cmd2 = NULL; + FreqPtr->Cmd3 = NULL; + + + switch (PORT->PortType) + { + case ICOM: + + // String is in Hex + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + + while (c = *(ptr1++)) + { + if (c == ' ') continue; // Allow space between pairs + + val = c - 0x30; + if (val > 15) val -= 7; + val <<= 4; + c = *(ptr1++) - 0x30; + if (c > 15) c -= 7; + val |= c; + *(CmdPtr++) = val; + } + + *(CmdPtr++) = 0xFD; + + + *(CmdPtr) = 0; + + Len = (int)(CmdPtr - (char *)&buffptr[30]); + break; + + case KENWOOD: + case FT991A: + case FT2000: + case FLEX: + case NMEA: + + // use text command + + Len = sprintf(CmdPtr, ptr1); + break; + + case YAESU: + + // String is in Hex (5 values) + + while (c = *(ptr1++)) + { + if (c == ' ') continue; // Allow space between pairs + + val = c - 0x30; + if (val > 15) val -= 7; + val <<= 4; + c = *(ptr1++) - 0x30; + if (c > 15) c -= 7; + val |= c; + *(CmdPtr++) = val; + } + + *(CmdPtr) = 0; + + Len = 5; + break; + + default: + sprintf(Command, "Sorry - CMD not supported on your Radio\r"); + return FALSE; + } + + FreqPtr[0].Cmd1Len = Len; // for ICOM + C_Q_ADD(&RIG->BPQtoRADIO_Q, buffptr); + return TRUE; + } + + if (_memicmp(FreqString, "Chan", 4) == 0) + { + if (strchr(FreqString, '/') ) // Bank/Chan + { + MemoryBank = FreqString[4]; + MemoryNumber = atoi(&FreqString[6]); + } + else + MemoryNumber = atoi(&FreqString[4]); // Just Chan + + Freq = 0.0; + } + else + { + Freq = atof(FreqString); + + if (Freq < 0.1 && PORT->PortType != FLRIG) + { + strcpy(Command, "Sorry - Invalid Frequency\r"); + return FALSE; + } + } + + Freq = Freq * 1000000.0; + + sprintf(FreqString, "%09.0f", Freq); + + if (PORT->PortType != ICOM) + strcpy(Data, FilterString); // Others don't have a filter. + + Split = DataFlag = Bandwidth = Antenna = 0; + + _strupr(Data); + + if (strchr(Data, '+')) + Split = '+'; + else if (strchr(Data, '-')) + Split = '-'; + else if (strchr(Data, 'S')) + Split = 'S'; + else if (strchr(Data, 'D')) + DataFlag = 1; + + if (strchr(Data, 'W')) + Bandwidth = 'W'; + else if (strchr(Data, 'N')) + Bandwidth = 'N'; + + if (strstr(Data, "A1")) + Antenna = '1'; + else if (strstr(Data, "A2")) + Antenna = '2'; + else if (strstr(Data, "A3")) + Antenna = '3'; + else if (strstr(Data, "A4")) + Antenna = '4'; + else if (strstr(Data, "A5")) + Antenna = '5'; + else if (strstr(Data, "A6")) + Antenna = '6'; + + switch (PORT->PortType) + { + case ICOM: + + if (n == 2) + // Set Freq Only + + ModeNo = -1; + else + { + if (n < 4 && RIG->ICF8101 == 0) + { + strcpy(Command, "Sorry - Invalid Format - should be Port Freq Mode Filter Width\r"); + return FALSE; + } + + Filter = atoi(FilterString); + + for (ModeNo = 0; ModeNo < 24; ModeNo++) + { + if (_stricmp(Modes[ModeNo], Mode) == 0) + break; + } + + if (ModeNo == 24) + { + sprintf(Command, "Sorry - Invalid Mode\r"); + return FALSE; + } + } + + buffptr = GetBuff(); + + if (buffptr == 0) + { + sprintf(Command, "Sorry - No Buffers available\r"); + return FALSE; + } + + // Build a ScanEntry in the buffer + + FreqPtr = (struct ScanEntry *)buffptr->Data; + memset(FreqPtr, 0, sizeof(struct ScanEntry)); + + FreqPtr->Freq = Freq; + FreqPtr->Bandwidth = Bandwidth; + FreqPtr->Antenna = Antenna; + FreqPtr->Dwell = 51; + + CmdPtr = FreqPtr->Cmd1 = FreqPtr->Cmd1Msg; + FreqPtr->Cmd2 = NULL; + FreqPtr->Cmd3 = NULL; + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + + + if (MemoryNumber) + { + // Set Memory Channel instead of Freq, Mode, etc + + char ChanString[5]; + + // Send Set Memory, then Channel + + *(CmdPtr++) = 0x08; + *(CmdPtr++) = 0xFD; + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + + sprintf(ChanString, "%04d", MemoryNumber); + + *(CmdPtr++) = 0x08; + *(CmdPtr++) = (ChanString[1] - 48) | ((ChanString[0] - 48) << 4); + *(CmdPtr++) = (ChanString[3] - 48) | ((ChanString[2] - 48) << 4); + *(CmdPtr++) = 0xFD; + + FreqPtr[0].Cmd1Len = 14; + + if (MemoryBank) + { + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x08; + *(CmdPtr++) = 0xA0; + *(CmdPtr++) = MemoryBank - 0x40; + *(CmdPtr++) = 0xFD; + + FreqPtr[0].Cmd1Len += 8; + } + } + else + { + if (RIG->ICF8101) + { + // Set Freq is 1A 35 and set Mode 1A 36 + + *(CmdPtr++) = 0x1A; + *(CmdPtr++) = 0x35; // Set frequency command + + // Need to convert two chars to bcd digit + + *(CmdPtr++) = (FreqString[8] - 48) | ((FreqString[7] - 48) << 4); + *(CmdPtr++) = (FreqString[6] - 48) | ((FreqString[5] - 48) << 4); + *(CmdPtr++) = (FreqString[4] - 48) | ((FreqString[3] - 48) << 4); + *(CmdPtr++) = (FreqString[2] - 48) | ((FreqString[1] - 48) << 4); + *(CmdPtr++) = (FreqString[0] - 48); + *(CmdPtr++) = 0xFD; + FreqPtr[0].Cmd1Len = 12; + + if (ModeNo != -1) // Dont Set + { + CmdPtr = FreqPtr->Cmd2 = FreqPtr->Cmd2Msg; + FreqPtr[0].Cmd2Len = 9; + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x1A; + *(CmdPtr++) = 0x36; // Set mode command + *(CmdPtr++) = 0; + if (ModeNo > 10) + *(CmdPtr++) = ModeNo + 6; + else + *(CmdPtr++) = ModeNo; + *(CmdPtr++) = 0xFD; + } + } + else + { + + *(CmdPtr++) = 0x5; // Set frequency command + + // Need to convert two chars to bcd digit + + *(CmdPtr++) = (FreqString[8] - 48) | ((FreqString[7] - 48) << 4); + *(CmdPtr++) = (FreqString[6] - 48) | ((FreqString[5] - 48) << 4); + *(CmdPtr++) = (FreqString[4] - 48) | ((FreqString[3] - 48) << 4); + *(CmdPtr++) = (FreqString[2] - 48) | ((FreqString[1] - 48) << 4); + if (RIG->IC735) + { + *(CmdPtr++) = 0xFD; + FreqPtr[0].Cmd1Len = 10; + } + else + { + *(CmdPtr++) = (FreqString[0] - 48); + *(CmdPtr++) = 0xFD; + FreqPtr[0].Cmd1Len = 11; + } + + // Send Set VFO in case last chan was memory + + // *(CmdPtr++) = 0xFE; + // *(CmdPtr++) = 0xFE; + // *(CmdPtr++) = RIG->RigAddr; + // *(CmdPtr++) = 0xE0; + + // *(CmdPtr++) = 0x07; + // *(CmdPtr++) = 0xFD; + + // FreqPtr[0].Cmd1Len = 17; + + if (ModeNo != -1) // Dont Set + { + CmdPtr = FreqPtr->Cmd2 = FreqPtr->Cmd2Msg; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x6; // Set Mode + *(CmdPtr++) = ModeNo; + *(CmdPtr++) = Filter; + *(CmdPtr++) = 0xFD; + + FreqPtr->Cmd2Len = 8; + + if (Split) + { + CmdPtr = FreqPtr->Cmd3 = FreqPtr->Cmd3Msg; + FreqPtr->Cmd3Len = 7; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0xF; // Set Mode + if (Split == 'S') + *(CmdPtr++) = 0x10; + else + if (Split == '+') + *(CmdPtr++) = 0x12; + else + if (Split == '-') + *(CmdPtr++) = 0x11; + + *(CmdPtr++) = 0xFD; + } + else if (DataFlag) + { + CmdPtr = FreqPtr->Cmd3 = FreqPtr->Cmd3Msg; + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x1a; + + if ((strcmp(RIG->RigName, "IC7100") == 0) || + (strcmp(RIG->RigName, "IC7410") == 0) || + (strcmp(RIG->RigName, "IC7600") == 0) || + (strcmp(RIG->RigName, "IC7610") == 0) || + (strcmp(RIG->RigName, "IC7300") == 0)) + { + FreqPtr[0].Cmd3Len = 9; + *(CmdPtr++) = 0x6; // Send/read DATA mode with filter set + *(CmdPtr++) = 0x1; // Data On + *(CmdPtr++) = Filter; //Filter + } + else if (strcmp(RIG->RigName, "IC7200") == 0) + { + FreqPtr[0].Cmd3Len = 9; + *(CmdPtr++) = 0x4; // Send/read DATA mode with filter set + *(CmdPtr++) = 0x1; // Data On + *(CmdPtr++) = Filter; // Filter + } + else + { + FreqPtr[0].Cmd3Len = 8; + *(CmdPtr++) = 0x6; // Set Data + *(CmdPtr++) = 0x1; //On + } + + *(CmdPtr++) = 0xFD; + } + } + + if (Antenna == '5' || Antenna == '6') + { + // Antenna select for 746 and maybe others + + // Could be going in cmd2 3 or 4 + + if (FreqPtr[0].Cmd2 == NULL) + { + CmdPtr = FreqPtr->Cmd2 = FreqPtr->Cmd2Msg; + FreqPtr[0].Cmd2Len = 7; + } + else if (FreqPtr[0].Cmd3 == NULL) + { + CmdPtr = FreqPtr->Cmd3 = FreqPtr->Cmd3Msg; + FreqPtr[0].Cmd3Len = 7; + } + else + { + CmdPtr = FreqPtr->Cmd4 = FreqPtr->Cmd4Msg; + FreqPtr[0].Cmd4Len = 7; + } + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x12; // Set Antenna + *(CmdPtr++) = Antenna - '5'; // 0 for A5 1 for A6 + *(CmdPtr++) = 0xFD; + } + } + } + + C_Q_ADD(&RIG->BPQtoRADIO_Q, buffptr); + + return TRUE; + + case YAESU: + + if (n == 2) // Just set freq + { + ModeNo = -1; + } + else + { + for (ModeNo = 0; ModeNo < 15; ModeNo++) + { + if (_stricmp(YaesuModes[ModeNo], Mode) == 0) + break; + } + + if (ModeNo == 15) + { + sprintf(Command, "Sorry -Invalid Mode\r"); + return FALSE; + } + } + + buffptr = GetBuff(); + + if (buffptr == 0) + { + sprintf(Command, "Sorry - No Buffers available\r"); + return FALSE; + } + + // Build a ScanEntry in the buffer + + FreqPtr = (struct ScanEntry *)buffptr->Data; + memset(FreqPtr, 0, sizeof(struct ScanEntry)); + + FreqPtr->Freq = Freq; + FreqPtr->Bandwidth = Bandwidth; + FreqPtr->Antenna = Antenna; + + Poll = FreqPtr->Cmd1 = FreqPtr->Cmd1Msg; + + // Send Mode then Freq - setting Mode seems to change frequency + + FreqPtr->Cmd1Len = 0; + + if (ModeNo != -1) // Set freq only + { + *(Poll++) = ModeNo; + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 7; // Set Mode + FreqPtr->Cmd1Len = 5; + } + + *(Poll++) = (FreqString[1] - 48) | ((FreqString[0] - 48) << 4); + *(Poll++) = (FreqString[3] - 48) | ((FreqString[2] - 48) << 4); + *(Poll++) = (FreqString[5] - 48) | ((FreqString[4] - 48) << 4); + *(Poll++) = (FreqString[7] - 48) | ((FreqString[6] - 48) << 4); + *(Poll++) = 1; // Set Freq + + FreqPtr->Cmd1Len += 5; + + if (strcmp(PORT->Rigs[0].RigName, "FT847") == 0) + { + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 3; // Status Poll + + FreqPtr->Cmd1Len = 15; + } + + C_Q_ADD(&RIG->BPQtoRADIO_Q, buffptr); + + return TRUE; + + + case FT100: + case FT990: + case FT1000: + + if (n == 2) // Set Freq Only + ModeNo = -1; + else + { + if (n < 3) + { + strcpy(Command, "Sorry - Invalid Format - should be Port Freq Mode\r"); + return FALSE; + } + + if (PORT->PortType == FT100) + { + for (ModeNo = 0; ModeNo < 8; ModeNo++) + { + if (_stricmp(FT100Modes[ModeNo], Mode) == 0) + break; + } + + if (ModeNo == 8) + { + sprintf(Command, "Sorry -Invalid Mode\r"); + return FALSE; + } + } + else if (PORT->PortType == FT990) + { + for (ModeNo = 0; ModeNo < 12; ModeNo++) + { + if (_stricmp(FT990Modes[ModeNo], Mode) == 0) + break; + } + + if (ModeNo == 12) + { + sprintf(Command, "Sorry -Invalid Mode\r"); + return FALSE; + } + } + else + { + for (ModeNo = 0; ModeNo < 12; ModeNo++) + { + if (_stricmp(FT1000Modes[ModeNo], Mode) == 0) + break; + } + + if (ModeNo == 12) + { + sprintf(Command, "Sorry -Invalid Mode\r"); + return FALSE; + } + } + } + + buffptr = GetBuff(); + + if (buffptr == 0) + { + sprintf(Command, "Sorry - No Buffers available\r"); + return FALSE; + } + + // Build a ScanEntry in the buffer + + FreqPtr = (struct ScanEntry *)buffptr->Data; + memset(FreqPtr, 0, sizeof(struct ScanEntry)); + + FreqPtr->Freq = Freq; + FreqPtr->Bandwidth = Bandwidth; + FreqPtr->Antenna = Antenna; + + Poll = FreqPtr->Cmd1 = FreqPtr->Cmd1Msg; + + // Send Mode then Freq - setting Mode seems to change frequency + + if (ModeNo == -1) // Don't set Mode + { + // Changing the length messes up a lot of code, + // so set freq twice instead of omitting entry + + *(Poll++) = (FreqString[7] - 48) | ((FreqString[6] - 48) << 4); + *(Poll++) = (FreqString[5] - 48) | ((FreqString[4] - 48) << 4); + *(Poll++) = (FreqString[3] - 48) | ((FreqString[2] - 48) << 4); + *(Poll++) = (FreqString[1] - 48) | ((FreqString[0] - 48) << 4); + *(Poll++) = 10; // Set Freq + } + else + { + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = ModeNo; + *(Poll++) = 12; // Set Mode + } + + *(Poll++) = (FreqString[7] - 48) | ((FreqString[6] - 48) << 4); + *(Poll++) = (FreqString[5] - 48) | ((FreqString[4] - 48) << 4); + *(Poll++) = (FreqString[3] - 48) | ((FreqString[2] - 48) << 4); + *(Poll++) = (FreqString[1] - 48) | ((FreqString[0] - 48) << 4); + *(Poll++) = 10; // Set Freq + + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 0; + if (PORT->PortType == FT990 || PORT->YaesuVariant == FT1000D) + *(Poll++) = 3; + else + *(Poll++) = 2; // 100 or 1000MP + + *(Poll++) = 16; // Status Poll + + FreqPtr->Cmd1Len = 15; + + C_Q_ADD(&RIG->BPQtoRADIO_Q, buffptr); + + return TRUE; + + case KENWOOD: + case FT2000: + case FT991A: + case FLEX: + + if (n < 3) + { + strcpy(Command, "Sorry - Invalid Format - should be Port Freq Mode\r"); + return FALSE; + } + + for (ModeNo = 0; ModeNo < 16; ModeNo++) + { + if (PORT->PortType == FT2000) + if (_stricmp(FT2000Modes[ModeNo], Mode) == 0) + break; + + if (PORT->PortType == FT991A) + if (_stricmp(FT991AModes[ModeNo], Mode) == 0) + break; + + if (PORT->PortType == KENWOOD) + if (_stricmp(KenwoodModes[ModeNo], Mode) == 0) + break; + if (PORT->PortType == FLEX) + if (_stricmp(FLEXModes[ModeNo], Mode) == 0) + break; + } + + if (ModeNo > 15) + { + sprintf(Command, "Sorry -Invalid Mode\r"); + return FALSE; + } + + buffptr = GetBuff(); + + if (buffptr == 0) + { + sprintf(Command, "Sorry - No Buffers available\r"); + return FALSE; + } + + // Build a ScanEntry in the buffer + + FreqPtr = (struct ScanEntry *)buffptr->Data; + memset(FreqPtr, 0, sizeof(struct ScanEntry)); + + FreqPtr->Freq = Freq; + FreqPtr->Bandwidth = Bandwidth; + FreqPtr->Antenna = Antenna; + + Poll = FreqPtr->Cmd1 = FreqPtr->Cmd1Msg; + + if (PORT->PortType == FT2000) + FreqPtr->Cmd1Len = sprintf(Poll, "FA%s;MD0%X;FA;MD;", &FreqString[1], ModeNo); + else + if (PORT->PortType == FT991A) + FreqPtr->Cmd1Len = sprintf(Poll, "FA%s;MD0%X;FA;MD;", &FreqString, ModeNo); + else + if (PORT->PortType == FLEX) + FreqPtr->Cmd1Len = sprintf(Poll, "ZZFA00%s;ZZMD%02d;ZZFA;ZZMD;", &FreqString[1], ModeNo); + else + { + if (Antenna == '5' || Antenna == '6') + FreqPtr->Cmd1Len = sprintf(Poll, "FA00%s;MD%d;AN%c;FA;MD;", FreqString, ModeNo, Antenna - 4); + else + FreqPtr->Cmd1Len = sprintf(Poll, "FA00%s;MD%d;FA;MD;", FreqString, ModeNo); + } + C_Q_ADD(&RIG->BPQtoRADIO_Q, buffptr); + + return TRUE; + + case NMEA: + + if (n < 3) + { + strcpy(Command, "Sorry - Invalid Format - should be Port Freq Mode\r"); + return FALSE; + } + buffptr = GetBuff(); + + if (buffptr == 0) + { + sprintf(Command, "Sorry - No Buffers available\r"); + return FALSE; + } + + // Build a ScanEntry in the buffer + + FreqPtr = (struct ScanEntry *)buffptr->Data; + memset(FreqPtr, 0, sizeof(struct ScanEntry)); + + FreqPtr->Freq = Freq; + FreqPtr->Bandwidth = Bandwidth; + FreqPtr->Antenna = Antenna; + + Poll = FreqPtr->Cmd1 = FreqPtr->Cmd1Msg; + + i = sprintf(Poll, "$PICOA,90,%02x,RXF,%.6f*xx\r\n", RIG->RigAddr, Freq/1000000.); + AddNMEAChecksum(Poll); + Len = i; + i = sprintf(Poll + Len, "$PICOA,90,%02x,TXF,%.6f*xx\r\n", RIG->RigAddr, Freq/1000000.); + AddNMEAChecksum(Poll + Len); + Len += i; + i = sprintf(Poll + Len, "$PICOA,90,%02x,MODE,%s*xx\r\n", RIG->RigAddr, Mode); + AddNMEAChecksum(Poll + Len); + + FreqPtr->Cmd1Len = i + Len; + + C_Q_ADD(&RIG->BPQtoRADIO_Q, buffptr); + + return TRUE; + + case HAMLIB: + { + char cmd[80]; + + int len = sprintf(cmd, "F %s\n+f\nM %s %d\n+m\n", + FreqString, Mode, atoi(Data)); + + send(PORT->remoteSock, cmd, len, 0); + sprintf(Command, "Ok\r"); + + saveNewFreq(RIG, Freq, Mode); + + return FALSE; + } + + case FLRIG: + { + char cmd[80]; + + int len = sprintf(cmd, "%.0f", Freq); + + strcpy(PORT->ScanEntry.Cmd2Msg, Mode); + strcpy(PORT->ScanEntry.Cmd3Msg, FilterString); + + if (Freq > 0.0) + FLRIGSendCommand(PORT, "rig.set_vfo", cmd); + + else if (PORT->ScanEntry.Cmd2Msg[0] && Mode[0] != '*') + { + sprintf(cmd, "%s", PORT->ScanEntry.Cmd2Msg); + FLRIGSendCommand(PORT, "rig.set_mode", cmd); + } + + else if (PORT->ScanEntry.Cmd3Msg[0] && strcmp(PORT->ScanEntry.Cmd3Msg, "0") != 0) + { + sprintf(cmd, "%s", PORT->ScanEntry.Cmd3Msg); + FLRIGSendCommand(PORT, "rig.set_bandwidth", cmd); + } + else + { + sprintf(Command, "Sorry - Nothing to do\r"); + return FALSE; + } + + PORT->AutoPoll = 0; + + saveNewFreq(RIG, Freq, Mode); + return TRUE; + } + + + case RTLUDP: + { + char cmd[80]; + int len = 5; + int FreqInt = (int)Freq; + int FM = 0; + int AM = 1; + int USB = 2; + int LSB = 3; + + cmd[0] = 0; + cmd[1] = FreqInt & 0xff; + cmd[2] = (FreqInt >> 8) & 0xff; + cmd[3] = (FreqInt >> 16) & 0xff; + cmd[4] = (FreqInt >> 24) & 0xff; + + len = sendto(PORT->remoteSock, cmd, 5, 0, &PORT->remoteDest, sizeof(struct sockaddr)); + + if (Mode[0]) + { + if (strcmp(Mode, "FM") == 0) + memcpy(&cmd[1], &FM, 4); + else if (strcmp(Mode, "AM") == 0) + memcpy(&cmd[1], &AM, 4); + else if (strcmp(Mode, "USB") == 0) + memcpy(&cmd[1], &USB, 4); + else if (strcmp(Mode, "LSB") == 0) + memcpy(&cmd[1], &LSB, 4); + + cmd[0] = 1; + len = sendto(PORT->remoteSock, cmd, 5, 0, &PORT->remoteDest, sizeof(struct sockaddr)); + } + + saveNewFreq(RIG, Freq, Mode); + + sprintf(Command, "Ok\r"); + return FALSE; + } + + + + + + } + return TRUE; +} + +int BittoInt(UINT BitMask) +{ + // Returns bit position of first 1 bit in BitMask + + int i = 0; + while ((BitMask & 1) == 0) + { + BitMask >>= 1; + i ++; + } + return i; +} + +extern char * RadioConfigMsg[36]; + +struct TNCINFO RIGTNC; // Dummy TNC Record for Rigcontrol without a corresponding TNC + +DllExport BOOL APIENTRY Rig_Init() +{ + struct RIGPORTINFO * PORT; + int i, p, port; + struct RIGINFO * RIG; + struct TNCINFO * TNC = &RIGTNC; + HWND hDlg; +#ifndef LINBPQ + int RigRow = 0; +#endif + int NeedRig = 0; + int Interlock = 0; + + memset(&RIGTNC, 0, sizeof(struct TNCINFO)); + + TNCInfo[40] = TNC; + + // Get config info + + NumberofPorts = 0; + + for (port = 0; port < 32; port++) + PORTInfo[port] = NULL; + + // See if any rigcontrol defined (either RADIO or RIGCONTROL lines) + + for (port = 0; port < 32; port++) + { + if (RadioConfigMsg[port]) + NeedRig++; + } + + if (NeedRig == 0) + { + SetupPortRIGPointers(); // Sets up Dummy Rig Pointer + return FALSE; + } + + +#ifndef LINBPQ + + TNC->Port = 40; + CreatePactorWindow(TNC, "RIGCONTROL", "RigControl", 10, PacWndProc, 550, NeedRig * 20 + 60, NULL); + hDlg = TNC->hDlg; + +#endif + + TNC->ClientHeight = NeedRig * 20 + 60; + TNC->ClientWidth = 550; + + for (port = 0; port < 33; port++) + { + if (RadioConfigMsg[port]) + { + char msg[1000]; + + char * SaveRigConfig = _strdup(RadioConfigMsg[port]); + char * RigConfigMsg1 = _strdup(RadioConfigMsg[port]); + + Interlock = atoi(&RigConfigMsg1[5]); + + RIG = RigConfig(TNC, RigConfigMsg1, port); + + if (RIG == NULL) + { + // Report Error + + sprintf(msg,"Port %d Invalid Rig Config %s", port, SaveRigConfig); + WritetoConsole(msg); + free(SaveRigConfig); + free(RigConfigMsg1); + continue; + } + + RIG->Interlock = Interlock; + + if (RIG->PORT->IOBASE[0] == 0) + { + // We have to find the SCS TNC in this scan group as TNC is now a dummy + + struct TNCINFO * PTCTNC; + int n; + + for (n = 1; n < 33; n++) + { + PTCTNC = TNCInfo[n]; + + if (PTCTNC == NULL || (PTCTNC->Hardware != H_SCS && PTCTNC->Hardware != H_ARDOP)) + continue; + + if (PTCTNC->RXRadio != Interlock) + continue; + + RIG->PORT->PTC = PTCTNC; + } + } + +#ifndef LINBPQ + + // Create line for this rig + + RIG->hLabel = CreateWindow(WC_STATIC , "", WS_CHILD | WS_VISIBLE, + 10, RigRow, 80,20, hDlg, NULL, hInstance, NULL); + + // RIG->hCAT = CreateWindow(WC_STATIC , "", WS_CHILD | WS_VISIBLE, + // 90, RigRow, 40,20, hDlg, NULL, hInstance, NULL); + + RIG->hFREQ = CreateWindow(WC_STATIC , "", WS_CHILD | WS_VISIBLE, + 95, RigRow, 100,20, hDlg, NULL, hInstance, NULL); + + RIG->hMODE = CreateWindow(WC_STATIC , "", WS_CHILD | WS_VISIBLE, + 200, RigRow, 100,20, hDlg, NULL, hInstance, NULL); + + RIG->hSCAN = CreateWindow(WC_STATIC , "", WS_CHILD | WS_VISIBLE, + 300, RigRow, 20,20, hDlg, NULL, hInstance, NULL); + + RIG->hPTT = CreateWindow(WC_STATIC , "", WS_CHILD | WS_VISIBLE, + 320, RigRow, 20,20, hDlg, NULL, hInstance, NULL); + + RIG->hPORTS = CreateWindow(WC_STATIC , "", WS_CHILD | WS_VISIBLE, + 340, RigRow, 210, 20, hDlg, NULL, hInstance, NULL); + + RigRow += 20; + + //if (PORT->PortType == ICOM) + //{ + // sprintf(msg,"%02X", PORT->Rigs[i].RigAddr); + // SetWindowText(RIG->hCAT, msg); + //} + SetWindowText(RIG->hLabel, RIG->RigName); +#endif + + RIG->WEB_Label = _strdup(RIG->RigName); + // RIG->WEB_CAT; + RIG->WEB_FREQ = zalloc(80); + RIG->WEB_MODE = zalloc(80); + RIG->WEB_PORTS = zalloc(80); + strcpy(RIG->WEB_FREQ, "-----------"); + strcpy(RIG->WEB_MODE, "------"); + + RIG->WEB_PTT = ' '; + RIG->WEB_SCAN = ' '; + } + } + + for (p = 0; p < NumberofPorts; p++) + { + PORT = PORTInfo[p]; + + if (PORT->PortType == HAMLIB) + { + HAMLIBMasterRunning = 1; + ConnecttoHAMLIB(PORT); + } + else if (PORT->PortType == FLRIG) + { + FLRIGRunning = 1; + ConnecttoFLRIG(PORT); + } + else if (PORT->PortType == RTLUDP) + ConnecttoRTLUDP(PORT); + else if (PORT->HIDDevice) // This is RAWHID, Not CM108 + OpenHIDPort(PORT, PORT->IOBASE, PORT->SPEED); + else if (PORT->PTC == 0 && _stricmp(PORT->IOBASE, "CM108") != 0) + OpenRigCOMMPort(PORT, PORT->IOBASE, PORT->SPEED); + + if (PORT->PTTIOBASE[0]) // Using separate port for PTT? + { + if (PORT->PTTIOBASE[3] == '=') + PORT->hPTTDevice = OpenCOMPort(&PORT->PTTIOBASE[4], PORT->SPEED, FALSE, FALSE, FALSE, 0); + else + PORT->hPTTDevice = OpenCOMPort(&PORT->PTTIOBASE[3], PORT->SPEED, FALSE, FALSE, FALSE, 0); + } + else + PORT->hPTTDevice = PORT->hDevice; // Use same port for PTT + } + + for (p = 0; p < NumberofPorts; p++) + { + PORT = PORTInfo[p]; + + for (i=0; i < PORT->ConfiguredRigs; i++) + { + int j; + int k = 0; + int BitMask; + struct _EXTPORTDATA * PortEntry; + + RIG = &PORT->Rigs[i]; + + SetupScanInterLockGroups(RIG); + + // Get record for each port in Port Bitmap + + // The Scan "Request Permission to Change" code needs the Port Records in order - + // Those with active connect lock (eg SCS) first, then those with just a connect pending lock (eg WINMOR) + // then those with neither + + BitMask = RIG->BPQPort; + for (j = 0; j < 32; j++) + { + if (BitMask & 1) + { + PortEntry = (struct _EXTPORTDATA *)GetPortTableEntryFromPortNum(j); // BPQ32 port record for this port + if (PortEntry) + if (PortEntry->SCANCAPABILITIES == CONLOCK) + RIG->PortRecord[k++] = PortEntry; + } + BitMask >>= 1; + } + + BitMask = RIG->BPQPort; + for (j = 0; j < 32; j++) + { + if (BitMask & 1) + { + PortEntry = (struct _EXTPORTDATA *)GetPortTableEntryFromPortNum(j); // BPQ32 port record for this port + if (PortEntry) + if (PortEntry->SCANCAPABILITIES == SIMPLE) + RIG->PortRecord[k++] = PortEntry; + } + BitMask >>= 1; + } + + BitMask = RIG->BPQPort; + for (j = 0; j < 32; j++) + { + if (BitMask & 1) + { + PortEntry = (struct _EXTPORTDATA *)GetPortTableEntryFromPortNum(j); // BPQ32 port record for this port + if (PortEntry) + if (PortEntry->SCANCAPABILITIES == NONE) + RIG->PortRecord[k++] = PortEntry; + } + BitMask >>= 1; + } + + RIG->PORT = PORT; // For PTT + + if (RIG->NumberofBands) + CheckTimeBands(RIG); // Set initial timeband + +#ifdef WIN32 + if (RIG->PTTCATPort[0]) // Serial port RTS to CAT + _beginthread(PTTCATThread,0,RIG); +#endif + if (RIG->HAMLIBPORT) + { + // Open listening socket + + struct sockaddr_in local_sin; /* Local socket - internet style */ + struct sockaddr_in * psin; + SOCKET sock = 0; + u_long param=1; + + char szBuff[80]; + + psin=&local_sin; + psin->sin_family = AF_INET; + psin->sin_addr.s_addr = INADDR_ANY; + + sock = socket(AF_INET, SOCK_STREAM, 0); + + setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (char *)¶m,4); + + psin->sin_port = htons(RIG->HAMLIBPORT); // Convert to network ordering + + if (bind( sock, (struct sockaddr FAR *) &local_sin, sizeof(local_sin)) == SOCKET_ERROR) + { + sprintf(szBuff, "bind(sock) failed port %d Error %d\n", port, WSAGetLastError()); + WritetoConsoleLocal(szBuff); + closesocket(sock); + } + else + { + if (listen(sock, 5) < 0) + { + sprintf(szBuff, "listen(sock) failed port %d Error %d\n", port, WSAGetLastError()); + WritetoConsoleLocal(szBuff); + } + else + { + ioctl(sock, FIONBIO, ¶m); + RIG->ListenSocket = sock; + _beginthread(HAMLIBSlaveThread, 0, RIG); + } + } + } + Rig_PTTEx(RIG, 0, NULL); // Send initial PTT Off (Mainly to set input mux on soundcard rigs) + } + } + + // MoveWindow(hDlg, Rect.left, Rect.top, Rect.right - Rect.left, Row + 100, TRUE); + + SetupPortRIGPointers(); + + RigWebPage = zalloc(10); + + WritetoConsole("\nRig Control Enabled\n"); + + return TRUE; +} + +DllExport BOOL APIENTRY Rig_Close() +{ + struct RIGPORTINFO * PORT; + struct TNCINFO * TNC; + int n, p; + + HAMLIBMasterRunning = 0; // Close HAMLIB thread(s) + HAMLIBSlaveRunning = 0; // Close HAMLIB thread(s) + FLRIGRunning = 0; // Close FLRIG thread(s) + + for (p = 0; p < NumberofPorts; p++) + { + PORT = PORTInfo[p]; + + if (PORT->PortType == NMEA) + { + // Send Remote OFF + + int i; + char REMOFF[80]; + + i = sprintf(REMOFF, "$PICOA,90,%02x,REMOTE,OFF*xx\r\n", PORT->Rigs[0].RigAddr); + AddNMEAChecksum(REMOFF); + + WriteCOMBlock(PORT->hDevice, REMOFF, i); + Sleep(200); + } + + if (PORT->PortType != HAMLIB && PORT->PortType != RTLUDP && PORT->PortType != FLRIG && strcmp(PORT->IOBASE, "RAWHID") != 0) + { + if (PORT->hPTTDevice != PORT->hDevice) + CloseCOMPort(PORT->hPTTDevice); + + CloseCOMPort(PORT->hDevice); + } + + PORT->hDevice = 0; + PORT->hPTTDevice = 0; + + // Free the RIG and Port Records + + for (n = 0; n < PORT->ConfiguredRigs; n++) + { + struct RIGINFO * RIG = &PORT->Rigs[n]; + struct HAMLIBSOCK * Entry = RIG->Sockets; + struct HAMLIBSOCK * Save; + + // if RIG has any HAMLIB slave connections, close them + + Entry = RIG->Sockets; + + while (Entry) + { + closesocket(Entry->Sock); + Save = Entry; + Entry = Entry->Next; + free(Save); + } + + RIG->Sockets = 0; + + if (RIG->TimeBands) + free (RIG->TimeBands[1]->Scanlist); + + if (RIG->PTTCATPort[0]) + { + Rig_PTTEx(RIG, FALSE, NULL); // Make sure PTT is down + EndPTTCATThread = TRUE; + } + } + + free (PORT); + PORTInfo[p] = NULL; + } + + NumberofPorts = 0; // For possible restart + + // And free the TNC config info + + for (p = 1; p < 33; p++) + { + TNC = TNCInfo[p]; + + if (TNC == NULL) + continue; + + TNC->RIG = NULL; + + // memset(TNC->WL2KInfoList, 0, sizeof(TNC->WL2KInfoList)); + + } + + return TRUE; +} + + + +BOOL Rig_Poll() +{ + int p, i, Len; + char RigWeb[16384]; + + struct RIGPORTINFO * PORT; + struct RIGINFO * RIG; + + for (p = 0; p < NumberofPorts; p++) + { + PORT = PORTInfo[p]; + + if (PORT->PortType == DUMMY) + { + DummyPoll(PORT); + return TRUE; + } + + if (PORT->hDevice == 0) + { + // Try to reopen every 15 secs + + PORT->ReopenDelay++; + + if (PORT->ReopenDelay > 150) + { + PORT->ReopenDelay = 0; + + if (PORT->PortType == HAMLIB) + ConnecttoHAMLIB(PORT); + else if (PORT->PortType == FLRIG) + ConnecttoFLRIG(PORT); + else if (PORT->PortType == RTLUDP) + ConnecttoRTLUDP(PORT); + else if (PORT->HIDDevice) + OpenHIDPort(PORT, PORT->IOBASE, PORT->SPEED); + else if (PORT->PTC == 0 + && _stricmp(PORT->IOBASE, "REMOTE") != 0 + && _stricmp(PORT->IOBASE, "CM108") != 0) + { + if (PORT->Closed == 0) + { + OpenRigCOMMPort(PORT, PORT->IOBASE, PORT->SPEED); + if (PORT->IOBASE && PORT->PTTIOBASE[0] == 0) // Using separate port for PTT? + PORT->hPTTDevice = PORT->hDevice; + } + } + } + } + + if (PORT == NULL || (PORT->hDevice == 0 && PORT->PTC == 0 && PORT->remoteSock == 0)) + continue; + + // Check PTT Timers + + for (i=0; i< PORT->ConfiguredRigs; i++) + { + RIG = &PORT->Rigs[i]; + + // Active PTT command + + if (RIG->PTTTimer) + { + RIG->PTTTimer--; + if (RIG->PTTTimer == 0) + Rig_PTTEx(RIG, FALSE, NULL); + } + + // repeated off timer + + if (RIG->repeatPTTOFFTimer) + { + RIG->repeatPTTOFFTimer--; + if (RIG->repeatPTTOFFTimer == 0) + { + Rig_PTTEx(RIG, FALSE, NULL); + RIG->repeatPTTOFFTimer = 0; // Don't repeat repeat! + } + } + } + + CheckRX(PORT); + + switch (PORT->PortType) + { + case ICOM: + + ICOMPoll(PORT); + break; + + case YAESU: + case FT100: + case FT990: + case FT1000: + + YaesuPoll(PORT); + break; + + case KENWOOD: + case FT2000: + case FT991A: + case FLEX: + case NMEA: + + KenwoodPoll(PORT); + break; + + case HAMLIB: + HAMLIBPoll(PORT); + break; + + case RTLUDP: + RTLUDPPoll(PORT); + break; + + case FLRIG: + FLRIGPoll(PORT); + break; + + + } + } + + // Build page for Web Display + + Len = BuildRigCtlPage(RigWeb); + + if (strcmp(RigWebPage, RigWeb) == 0) + return TRUE; // No change + + free(RigWebPage); + + RigWebPage = _strdup(RigWeb); + + SendRigWebPage(); + + return TRUE; +} + + +BOOL RigCloseConnection(struct RIGPORTINFO * PORT) +{ + // disable event notification and wait for thread + // to halt + + CloseCOMPort(PORT->hDevice); + PORT->hDevice = 0; + return TRUE; + +} // end of CloseConnection() + +#ifndef WIN32 +#define ONESTOPBIT 0 +#define ONE5STOPBITS 1 +#define TWOSTOPBITS 2 +#endif + +int OpenRigCOMMPort(struct RIGPORTINFO * PORT, VOID * Port, int Speed) +{ + if (PORT->remoteSock) // Using WINMORCONTROL + return TRUE; + + if (PORT->PortType == FT2000 || PORT->PortType == FT991A || strcmp(PORT->Rigs[0].RigName, "FT847") == 0) // FT2000 and similar seem to need two stop bits + PORT->hDevice = OpenCOMPort((VOID *)Port, Speed, FALSE, FALSE, PORT->Alerted, TWOSTOPBITS); + else if (PORT->PortType == NMEA) + PORT->hDevice = OpenCOMPort((VOID *)Port, Speed, FALSE, FALSE, PORT->Alerted, ONESTOPBIT); + else + PORT->hDevice = OpenCOMPort((VOID *)Port, Speed, FALSE, FALSE, PORT->Alerted, TWOSTOPBITS); + + if (PORT->hDevice == 0) + { + PORT->Alerted = TRUE; + return FALSE; + } + + PORT->Alerted = FALSE; + + if (PORT->PortType == PTT || (PORT->Rigs[0].PTTMode & PTTRTS)) + COMClearRTS(PORT->hDevice); + else + COMSetRTS(PORT->hDevice); + + if (PORT->PortType == PTT || (PORT->Rigs[0].PTTMode & PTTDTR)) + COMClearDTR(PORT->hDevice); + else + COMSetDTR(PORT->hDevice); + + if (strcmp(PORT->Rigs[0].RigName, "FT847") == 0) + { + // Looks like FT847 Needa a "Cat On" Command + + UCHAR CATON[6] = {0,0,0,0,0}; + + WriteCOMBlock(PORT->hDevice, CATON, 5); + } + + if (PORT->PortType == NMEA) + { + // Looks like NMEA Needs Remote ON + + int i; + char REMON[80]; + + i = sprintf(REMON, "$PICOA,90,%02x,REMOTE,ON*xx\r\n", PORT->Rigs[0].RigAddr); + AddNMEAChecksum(REMON); + + WriteCOMBlock(PORT->hDevice, REMON, i); + } + + + return TRUE; +} + +void CheckRX(struct RIGPORTINFO * PORT) +{ + int Length; + char NMEAMsg[100]; + unsigned char * ptr; + int len; + + if (PORT->PortType == FLRIG) + { + // Data received in thread + + return; + } + + if (PORT->PortType == HAMLIB) + { + // Data received in thread (do we need thread?? + + Length = PORT->RXLen; + PORT->RXLen = 0; + } + + else if (PORT->PortType == RTLUDP) + { + CheckAndProcessRTLUDP(PORT); + Length = 0; + PORT->RXLen = 0; + return; + } + + else if (PORT->HIDDevice) + Length = HID_Read_Block(PORT); + else if (PORT->PTC) + Length = GetPTCRadioCommand(PORT->PTC, &PORT->RXBuffer[PORT->RXLen]); + else if (PORT->remoteSock) + { + struct sockaddr_in rxaddr; + int addrlen = sizeof(struct sockaddr_in); + + Length = recvfrom(PORT->remoteSock, PORT->RXBuffer, 500, 0, (struct sockaddr *)&rxaddr, &addrlen); + if (Length == -1) + Length = 0; + } + else + { + if (PORT->hDevice == 0) + return; + + // only try to read number of bytes in queue + + if (PORT->RXLen == 500) + PORT->RXLen = 0; + + Length = 500 - (DWORD)PORT->RXLen; + + Length = ReadCOMBlock(PORT->hDevice, &PORT->RXBuffer[PORT->RXLen], Length); + } + + if (Length == 0) + return; // Nothing doing + + PORT->RXLen += Length; + + Length = PORT->RXLen; + + switch (PORT->PortType) + { + case ICOM: + + if (Length < 6) // Minimum Frame Sise + return; + + if (PORT->RXBuffer[Length-1] != 0xfd) + return; + + ProcessICOMFrame(PORT, PORT->RXBuffer, Length); // Could have multiple packets in buffer + + PORT->RXLen = 0; // Ready for next frame + return; + + case YAESU: + + // Possible responses are a single byte ACK/NAK or a 5 byte info frame + + if (Length == 1 && PORT->CmdSent > 0) + { + ProcessYaesuCmdAck(PORT); + return; + } + + if (Length < 5) // Frame Sise + return; + + if (Length > 5) // Frame Sise + { + PORT->RXLen = 0; // Corruption - reset and wait for retry + return; + } + + ProcessYaesuFrame(PORT); + + PORT->RXLen = 0; // Ready for next frame + return; + + case FT100: + + // Only response should be a 16 byte info frame + + if (Length < 32) // Frame Sise why??????? + return; + + if (Length > 32) // Frame Sise + { + PORT->RXLen = 0; // Corruption - reset and wait for retry + return; + } + + ProcessFT100Frame(PORT); + + PORT->RXLen = 0; // Ready for next frame + return; + + case FT990: + + // Only response should be a 32 byte info frame + + if (Length < 32) // Frame Sise + return; + + if (Length > 32) // Frame Sise + { + PORT->RXLen = 0; // Corruption - reset and wait for retry + return; + } + + ProcessFT990Frame(PORT); + PORT->RXLen = 0; // Ready for next frame + return; + + + case FT1000: + + // Only response should be a 16 byte info frame + + ptr = PORT->RXBuffer; + + if (Length < 16) // Frame Sise + return; + + if (Length > 16) // Frame Sise + { + PORT->RXLen = 0; // Corruption - reset and wait for retry + return; + } + + ProcessFT1000Frame(PORT); + + PORT->RXLen = 0; // Ready for next frame + return; + + case KENWOOD: + case FT2000: + case FT991A: + case FLEX: + + if (Length < 2) // Minimum Frame Sise + return; + + if (Length > 50) // Garbage + { + PORT->RXLen = 0; // Ready for next frame + return; + } + + if (PORT->RXBuffer[Length-1] != ';') + return; + + ProcessKenwoodFrame(PORT, Length); + + PORT->RXLen = 0; // Ready for next frame + return; + + case NMEA: + + ptr = memchr(PORT->RXBuffer, 0x0a, Length); + + while (ptr != NULL) + { + ptr++; // include lf + len = (int)(ptr - &PORT->RXBuffer[0]); + + memcpy(NMEAMsg, PORT->RXBuffer, len); + + NMEAMsg[len] = 0; + +// if (Check0183CheckSum(NMEAMsg, len)) + ProcessNMEA(PORT, NMEAMsg, len); + + Length -= len; // bytes left + + if (Length > 0) + { + memmove(PORT->RXBuffer, ptr, Length); + ptr = memchr(PORT->RXBuffer, 0x0a, Length); + } + else + ptr=0; + } + + PORT->RXLen = Length; + return; + + case HAMLIB: + + ProcessHAMLIBFrame(PORT, Length); + PORT->RXLen = 0; + return; + } +} + +VOID ProcessICOMFrame(struct RIGPORTINFO * PORT, UCHAR * rxbuffer, int Len) +{ + UCHAR * FendPtr; + int NewLen; + + // Split into Packets. By far the most likely is a single frame + // so treat as special case + + FendPtr = memchr(rxbuffer, 0xfd, Len); + + if (FendPtr == &rxbuffer[Len-1]) + { + ProcessFrame(PORT, rxbuffer, Len); + return; + } + + // Process the first Packet in the buffer + + NewLen = (int)(FendPtr - rxbuffer + 1); + + ProcessFrame(PORT, rxbuffer, NewLen); + + // Loop Back + + ProcessICOMFrame(PORT, FendPtr+1, Len - NewLen); + return; +} + + +BOOL RigWriteCommBlock(struct RIGPORTINFO * PORT) +{ + // if using a PTC radio interface send to the SCSPactor Driver, else send to COM port + + int ret; + + if (PORT->HIDDevice) + HID_Write_Block(PORT); + else + if (PORT->PTC) + SendPTCRadioCommand(PORT->PTC, PORT->TXBuffer, PORT->TXLen); + else if (PORT->remoteSock) + ret = sendto(PORT->remoteSock, PORT->TXBuffer, PORT->TXLen, 0, &PORT->remoteDest, sizeof(struct sockaddr)); + else + { + BOOL fWriteStat; + DWORD BytesWritten; + +#ifndef WIN32 + BytesWritten = write(PORT->hDevice, PORT->TXBuffer, PORT->TXLen); +#else + fWriteStat = WriteFile(PORT->hDevice, PORT->TXBuffer, PORT->TXLen, &BytesWritten, NULL ); +#endif + if (PORT->TXLen != BytesWritten) + { + struct RIGINFO * RIG = &PORT->Rigs[PORT->CurrentRig]; // Only one on Yaseu + + if (RIG->RIGOK) + { + SetWindowText(RIG->hFREQ, "Port Closed"); + SetWindowText(RIG->hMODE, "----------"); + strcpy(RIG->WEB_FREQ, "-----------");; + strcpy(RIG->WEB_MODE, "------"); + } + + RIG->RIGOK = FALSE; + + if (PORT->hDevice) + CloseCOMPort(PORT->hDevice); + + OpenRigCOMMPort(PORT, PORT->IOBASE, PORT->SPEED); + + if (PORT->IOBASE && PORT->PTTIOBASE[0] == 0) // Using separate port for PTT? + PORT->hPTTDevice = PORT->hDevice; + + if (PORT->hDevice) + { + // Try Again + +#ifndef WIN32 + BytesWritten = write(PORT->hDevice, PORT->TXBuffer, PORT->TXLen); +#else + fWriteStat = WriteFile(PORT->hDevice, PORT->TXBuffer, PORT->TXLen, &BytesWritten, NULL ); +#endif + } + } + } + + ret = GetLastError(); + + PORT->Timeout = 100; // 2 secs + return TRUE; +} + +VOID ReleasePermission(struct RIGINFO *RIG) +{ + int i = 0; + struct _EXTPORTDATA * PortRecord; + + while (RIG->PortRecord[i]) + { + PortRecord = RIG->PortRecord[i]; + PortRecord->PORT_EXT_ADDR(6, PortRecord->PORTCONTROL.PORTNUMBER, 3); // Release Perrmission + i++; + } +} + +int GetPermissionToChange(struct RIGPORTINFO * PORT, struct RIGINFO *RIG) +{ + struct ScanEntry ** ptr; + struct _EXTPORTDATA * PortRecord; + int i = 0; + + // Get Permission to change + + if (RIG->RIG_DEBUG) + Debugprintf("GetPermissionToChange - WaitingForPermission = %d", RIG->WaitingForPermission); + + // See if any two stage (CONLOCK) ports + + if (RIG->PortRecord[0] && RIG->PortRecord[0]->SCANCAPABILITIES != CONLOCK) + goto CheckOtherPorts; + + + if (RIG->WaitingForPermission) + { + // This code should only be called if port needs two stage + // eg Request then confirm. only SCS Pactor at the moment. + + // TNC has been asked for permission, and we are waiting respoonse + // Only SCS pactor returns WaitingForPrmission, so check shouldn't be called on others + + RIG->OKtoChange = (int)(intptr_t)RIG->PortRecord[0]->PORT_EXT_ADDR(6, RIG->PortRecord[0]->PORTCONTROL.PORTNUMBER, 2); // Get Ok Flag + + if (RIG->OKtoChange == 1) + { + if (RIG->RIG_DEBUG) + Debugprintf("Check Permission returned OK to change"); + + i = 1; + goto CheckOtherPorts; + } + + if (RIG->OKtoChange == -1) + { + // Permission Refused. Wait Scan Interval and try again + + if (RIG->RIG_DEBUG) + Debugprintf("Scan Debug %s Refused permission - waiting ScanInterval %d", + RIG->PortRecord[0]->PORT_DLL_NAME, PORT->FreqPtr->Dwell ); + + RIG->WaitingForPermission = FALSE; + SetWindowText(RIG->hSCAN, "-"); + RIG->WEB_SCAN = '='; + + RIG->ScanCounter = PORT->FreqPtr->Dwell; + + if (RIG->ScanCounter == 0) // ? After manual change + RIG->ScanCounter = 50; + + return FALSE; + } + + if (RIG->RIG_DEBUG) + Debugprintf("Scan Debug %s Still Waiting", RIG->PortRecord[0]->PORT_DLL_NAME); + + + return FALSE; // Haven't got reply yet. Will re-enter next tick + } + else + { + // not waiting for permission, so must be first call of a cycle + + if (RIG->PortRecord[0] && RIG->PortRecord[0]->PORT_EXT_ADDR) + RIG->WaitingForPermission = (int)(intptr_t)RIG->PortRecord[0]->PORT_EXT_ADDR(6, RIG->PortRecord[0]->PORTCONTROL.PORTNUMBER, 1); // Request Perrmission + + // If it returns zero there is no need to wait. + // Normally SCS Returns True for first call, but returns 0 if Link not running + + if (RIG->WaitingForPermission) + return FALSE; // Wait till driver has status + + if (RIG->RIG_DEBUG) + Debugprintf("First call to %s returned OK to change", RIG->PortRecord[0]->PORT_DLL_NAME); + + i = 1; + } + +CheckOtherPorts: + + // Either first TNC gave permission or there are no SCS like ports. + // Ask any others (these are assumed to give immediate yes/no + + while (RIG->PortRecord[i]) + { + PortRecord = RIG->PortRecord[i]; + + if (PortRecord->PORT_EXT_ADDR(6, PortRecord->PORTCONTROL.PORTNUMBER, 1)) + { + // 1 means can't change - release all + + if (RIG->RIG_DEBUG && PORT->FreqPtr) + Debugprintf("Scan Debug %s Refused permission - waiting ScanInterval %d", + PortRecord->PORT_DLL_NAME, PORT->FreqPtr->Dwell); + + RIG->WaitingForPermission = FALSE; + MySetWindowText(RIG->hSCAN, "-"); + RIG->WEB_SCAN = '-'; + + if (PORT->FreqPtr) + RIG->ScanCounter = PORT->FreqPtr->Dwell; + + if (RIG->ScanCounter == 0) // ? After manual change + RIG->ScanCounter = 50; + + ReleasePermission(RIG); + return FALSE; + } + else + if (RIG->RIG_DEBUG) + Debugprintf("Scan Debug %s gave permission", PortRecord->PORT_DLL_NAME); + + i++; + } + + + RIG->WaitingForPermission = FALSE; + + // Update pointer to next frequency + + RIG->FreqPtr++; + + ptr = RIG->FreqPtr; + + if (ptr == NULL) + { + Debugprintf("Scan Debug - No freqs - quitting"); + return FALSE; // No Freqs + } + + if (ptr[0] == (struct ScanEntry *)0) // End of list - reset to start + { + ptr = CheckTimeBands(RIG); + + if (ptr[0] == (struct ScanEntry *)0) + { + // Empty Timeband - delay 60 secs (we need to check for timeband change sometimes) + + RIG->ScanCounter = 600; + ReleasePermission(RIG); + return FALSE; + } + } + + PORT->FreqPtr = ptr[0]; // Save Scan Command Block + + RIG->ScanCounter = PORT->FreqPtr->Dwell; + + if (RIG->ScanCounter == 0) // ? After manual change + RIG->ScanCounter = 150; + + MySetWindowText(RIG->hSCAN, "S"); + RIG->WEB_SCAN = 'S'; + + // Do Bandwidth and antenna switches (if needed) + + DoBandwidthandAntenna(RIG, ptr[0]); + + return TRUE; +} + +VOID DoBandwidthandAntenna(struct RIGINFO *RIG, struct ScanEntry * ptr) +{ + // If Bandwidth Change needed, do it + + int i; + struct _EXTPORTDATA * PortRecord; + + if (ptr->Bandwidth || ptr->RPacketMode || ptr->HFPacketMode + || ptr->PMaxLevel || ptr->ARDOPMode[0] || ptr->VARAMode) + { + i = 0; + + while (RIG->PortRecord[i]) + { + PortRecord = RIG->PortRecord[i]; + + RIG->CurrentBandWidth = ptr->Bandwidth; + + PortRecord->PORT_EXT_ADDR(6, PortRecord->PORTCONTROL.PORTNUMBER, ptr); + +/* if (ptr->Bandwidth == 'R') // Robust Packet + PortRecord->PORT_EXT_ADDR(6, PortRecord->PORTCONTROL.PORTNUMBER, 6); // Set Robust Packet + else + + if (ptr->Bandwidth == 'W') + PortRecord->PORT_EXT_ADDR(6, PortRecord->PORTCONTROL.PORTNUMBER, 4); // Set Wide Mode + else + PortRecord->PORT_EXT_ADDR(6, PortRecord->PORTCONTROL.PORTNUMBER, 5); // Set Narrow Mode +*/ + i++; + } + } + + // If Antenna Change needed, do it + + // 5 and 6 are used for CI-V switching so ignore here + + if (ptr->Antenna && ptr->Antenna < '5') + { + SwitchAntenna(RIG, ptr->Antenna); + } + + return; +} + +VOID ICOMPoll(struct RIGPORTINFO * PORT) +{ + UCHAR * Poll = PORT->TXBuffer; + int i; + + struct RIGINFO * RIG; + + for (i=0; i< PORT->ConfiguredRigs; i++) + { + RIG = &PORT->Rigs[i]; + + if (RIG->ScanStopped == 0) + if (RIG->ScanCounter) + RIG->ScanCounter--; + } + + if (PORT->Timeout) + { + PORT->Timeout--; + + if (PORT->Timeout) // Still waiting + return; + + PORT->Retries--; + + if(PORT->Retries) + { + RigWriteCommBlock(PORT); // Retransmit Block + return; + } + + RIG = &PORT->Rigs[PORT->CurrentRig]; + + + SetWindowText(RIG->hFREQ, "-----------"); + strcpy(RIG->WEB_FREQ, "-----------"); + SetWindowText(RIG->hMODE, "------"); + strcpy(RIG->WEB_MODE, "------"); + +// SetWindowText(RIG->hFREQ, "145.810000"); +// SetWindowText(RIG->hMODE, "RTTY/1"); + + PORT->Rigs[PORT->CurrentRig].RIGOK = FALSE; + + return; + + } + + // Send Data if avail, else send poll + + PORT->CurrentRig++; + + if (PORT->CurrentRig >= PORT->ConfiguredRigs) + PORT->CurrentRig = 0; + + RIG = &PORT->Rigs[PORT->CurrentRig]; + +/* + RIG->DebugDelay ++; + + if (RIG->DebugDelay > 600) + { + RIG->DebugDelay = 0; + Debugprintf("Scan Debug %d %d %d %d %d %d", PORT->CurrentRig, + RIG->NumberofBands, RIG->RIGOK, RIG->ScanStopped, RIG->ScanCounter, + RIG->WaitingForPermission); + } +*/ + if (RIG->NumberofBands && RIG->RIGOK && (RIG->ScanStopped == 0)) + { + if (RIG->ScanCounter <= 0) + { + // Send Next Freq + + if (GetPermissionToChange(PORT, RIG)) + { + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Change Freq to %9.4f", PORT->FreqPtr->Freq/1000000.0); + + memcpy(PORT->TXBuffer, PORT->FreqPtr->Cmd1, PORT->FreqPtr->Cmd1Len); + + if (PORT->FreqPtr->Freq > 0.0) + { + _gcvt((PORT->FreqPtr->Freq + RIG->rxOffset) / 1000000.0, 9, RIG->Valchar); // For MH + strcpy(RIG->WEB_FREQ, RIG->Valchar); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + } + + + PORT->TXLen = PORT->FreqPtr->Cmd1Len; + RigWriteCommBlock(PORT); + PORT->Retries = 2; + PORT->AutoPoll = TRUE; + + return; + } + } + } + + if (RIG->RIGOK && RIG->BPQtoRADIO_Q) + { + struct MSGWITHOUTLEN * buffptr; + + buffptr = Q_REM(&RIG->BPQtoRADIO_Q); + + // Copy the ScanEntry struct from the Buffer to the PORT Scanentry + + memcpy(&PORT->ScanEntry, buffptr->Data, sizeof(struct ScanEntry)); + + PORT->FreqPtr = &PORT->ScanEntry; // Block we are currently sending. + + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Manual Change Freq to %9.4f", PORT->FreqPtr->Freq/1000000.0); + + + memcpy(Poll, PORT->FreqPtr->Cmd1, PORT->FreqPtr->Cmd1Len); + + DoBandwidthandAntenna(RIG, &PORT->ScanEntry); + + if (PORT->FreqPtr->Freq > 0.0) + { + _gcvt((PORT->FreqPtr->Freq + RIG->rxOffset) / 1000000.0, 9, RIG->Valchar); // For MH + strcpy(RIG->WEB_FREQ, RIG->Valchar); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + } + + PORT->TXLen = PORT->FreqPtr->Cmd1Len; // First send the set Freq + RigWriteCommBlock(PORT); + PORT->Retries = 2; + + ReleaseBuffer(buffptr); + + PORT->AutoPoll = FALSE; + + return; + } + + + if (RIG->RIGOK && RIG->ScanStopped == 0 && RIG->NumberofBands && + RIG->ScanCounter && RIG->ScanCounter < 30) // no point in reading freq if we are about to change + return; + + if (RIG->PollCounter) + { + RIG->PollCounter--; + if (RIG->PollCounter) + return; + } + + // Need to make sure we don't poll multiple rigs on port at the same time + + if (RIG->RIGOK) + { + PORT->Retries = 2; + RIG->PollCounter = 10 / PORT->ConfiguredRigs; // Once Per Sec + } + else + { + PORT->Retries = 1; + RIG->PollCounter = 100 / PORT->ConfiguredRigs; // Slow Poll if down + } + + RIG->PollCounter += PORT->CurrentRig * 3; + + PORT->AutoPoll = TRUE; + + // Read Frequency + + Poll[0] = 0xFE; + Poll[1] = 0xFE; + Poll[2] = RIG->RigAddr; + Poll[3] = 0xE0; + Poll[4] = 0x3; // Get frequency command + Poll[5] = 0xFD; + + PORT->TXLen = 6; + + RigWriteCommBlock(PORT); + + PORT->Retries = 1; + PORT->Timeout = 10; + + return; +} + + +VOID ProcessFrame(struct RIGPORTINFO * PORT, UCHAR * Msg, int framelen) +{ + UCHAR * Poll = PORT->TXBuffer; + struct RIGINFO * RIG; + int i; + int cmdSent = PORT->TXBuffer[4]; + + if (Msg[0] != 0xfe || Msg[1] != 0xfe) + + // Duff Packet - return + + return; + + if (Msg[2] != 0xe0 && Msg[2] != 0) + { + // Echo - Proves a CI-V interface is attached + + if (PORT->PORTOK == FALSE) + { + // Just come up + PORT->PORTOK = TRUE; + Debugprintf("Cat Port OK"); + } + return; + } + + for (i=0; i< PORT->ConfiguredRigs; i++) + { + RIG = &PORT->Rigs[i]; + if (Msg[3] == RIG->RigAddr) + goto ok; + } + + return; + +ok: + + if (Msg[4] == 0xFB) + { + // Accept + + if (cmdSent == 0x1C && PORT->TXBuffer[5] == 1) // Tune + { + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Tune OK"); + + PORT->Timeout = 0; + return; + } + + if (cmdSent == 0x14 && PORT->TXBuffer[5] == 0x0A) // Power + { + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Set Power OK"); + + PORT->Timeout = 0; + return; + } + + // if it was the set freq, send the set mode + + if (RIG->ICF8101) + { + if (cmdSent == 0x1A && PORT->TXBuffer[5] == 0x35) + { + if (PORT->FreqPtr->Cmd2) + { + memcpy(PORT->TXBuffer, PORT->FreqPtr->Cmd2, PORT->FreqPtr->Cmd2Len); + PORT->TXLen = PORT->FreqPtr->Cmd2Len; + RigWriteCommBlock(PORT); + PORT->Retries = 2; + } + else + { + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Frequency Set OK"); + + PORT->Timeout = 0; + } + return; + } + + if (cmdSent == 0x1a && PORT->TXBuffer[5] == 0x36) + { + // Set Mode Response - if scanning read freq, else return OK to user + + if (RIG->ScanStopped == 0) + { + ReleasePermission(RIG); // Release Perrmission + + Poll[0] = 0xFE; + Poll[1] = 0xFE; + Poll[2] = RIG->RigAddr; + Poll[3] = 0xE0; + Poll[4] = 0x3; // Get frequency command + Poll[5] = 0xFD; + + PORT->TXLen = 6; + RigWriteCommBlock(PORT); + PORT->Retries = 2; + return; + } + else + { + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Frequency and Mode Set OK"); + + RIG->PollCounter = 30 / RIG->PORT->ConfiguredRigs; // Dont read freq for 3 secs + } + } + + PORT->Timeout = 0; + return; + } + + if (cmdSent == 5 || cmdSent == 7) // Freq or VFO + { + if (PORT->FreqPtr && PORT->FreqPtr->Cmd2) + { + memcpy(PORT->TXBuffer, PORT->FreqPtr->Cmd2, PORT->FreqPtr->Cmd2Len); + PORT->TXLen = PORT->FreqPtr->Cmd2Len; + RigWriteCommBlock(PORT); + PORT->Retries = 2; + } + else + { + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Frequency Set OK"); + + PORT->Timeout = 0; + } + return; + } + + if (cmdSent == 6) // Set Mode + { + if (PORT->FreqPtr && PORT->FreqPtr->Cmd3) + { + memcpy(PORT->TXBuffer, PORT->FreqPtr->Cmd3, PORT->FreqPtr->Cmd3Len); + PORT->TXLen = PORT->FreqPtr->Cmd3Len; + RigWriteCommBlock(PORT); + PORT->Retries = 2; + return; + } + + goto SetFinished; + } + + if (cmdSent == 0x08) + { + // Memory Chan + + cmdSent = 0; // So we only do it once + + goto SetFinished; + } + + if (cmdSent == 0x12) + goto SetFinished; // Antenna always sent last + + + if (cmdSent == 0x0f || cmdSent == 0x01a) // Set DUP or Data + { + // These are send from cmd3. There may be a cmd4 + // for antenna switching + + if (PORT->FreqPtr && PORT->FreqPtr->Cmd4) + { + memcpy(PORT->TXBuffer, PORT->FreqPtr->Cmd4, PORT->FreqPtr->Cmd4Len); + PORT->TXLen = PORT->FreqPtr->Cmd4Len; + RigWriteCommBlock(PORT); + PORT->Retries = 2; + return; + } + +SetFinished: + + // Set Mode Response - if scanning read freq, else return OK to user + + if (RIG->ScanStopped == 0) + { + ReleasePermission(RIG); // Release Perrmission + + Poll[0] = 0xFE; + Poll[1] = 0xFE; + Poll[2] = RIG->RigAddr; + Poll[3] = 0xE0; + Poll[4] = 0x3; // Get frequency command + Poll[5] = 0xFD; + + PORT->TXLen = 6; + RigWriteCommBlock(PORT); + PORT->Retries = 2; + return; + } + + else + { + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Frequency and Mode Set OK"); + + RIG->PollCounter = 30 / RIG->PORT->ConfiguredRigs; // Dont read freq for 3 secs + } + } + + PORT->Timeout = 0; + return; + } + + if (Msg[4] == 0xFA) + { + // Reject + + PORT->Timeout = 0; + + if (!PORT->AutoPoll) + { + if (cmdSent == 5) + SendResponse(RIG->Session, "Sorry - Set Frequency Command Rejected"); + else + if (cmdSent == 6) + SendResponse(RIG->Session, "Sorry - Set Mode Command Rejected"); + else + if (cmdSent == 0x0f) + SendResponse(RIG->Session, "Sorry - Set Shift Command Rejected"); + else + if (cmdSent == 0x12) + SendResponse(RIG->Session, "Sorry - Set Antenna Command Rejected"); + else + if (cmdSent == 0x1C && PORT->TXBuffer[5] == 1) // Tune + SendResponse(RIG->Session, "Sorry - Tune Command Rejected"); + else + if (cmdSent == 0x14 && PORT->TXBuffer[5] == 0x0a) // Power + SendResponse(RIG->Session, "Sorry - Power Command Rejected"); + else + SendResponse(RIG->Session, "Sorry - Command Rejected"); + + } + return; + } + + if (Msg[4] == PORT->TXBuffer[4]) + { + // Response to our command + + // Any valid frame is an ACK + + RIG->RIGOK = TRUE; + PORT->Timeout = 0; + + if (!PORT->AutoPoll) + { + // Manual response probably to CMD Query. Return response + + char reply[256] = "CMD Response "; + char * p1 = &reply[13]; + UCHAR * p2 = &Msg[4]; + int n = framelen - 4; + + while (n > 0) + { + sprintf(p1, "%02X ", *(p2)); + p1 += 3; + p2++; + n--; + } + + SendResponse(RIG->Session, reply); + + } + + } + else + return; // What does this mean?? + + + if (PORT->PORTOK == FALSE) + { + // Just come up +// char Status[80]; + + PORT->PORTOK = TRUE; +// sprintf(Status,"COM%d PORT link OK", PORT->IOBASE); +// SetWindowText(PORT->hStatus, Status); + } + + if (Msg[4] == 3) + { + // Rig Frequency + int n, j, decdigit; + long long Freq = 0; + int start = 9; + long long MHz, Hz; + char CharMHz[16] = ""; + char CharHz[16] = ""; + + int i; + + + if (RIG->IC735) + start = 8; // shorted msg + + for (j = start; j > 4; j--) + { + n = Msg[j]; + decdigit = (n >> 4); + decdigit *= 10; + decdigit += n & 0xf; + Freq = (Freq *100 ) + decdigit; + } + + Freq += RIG->rxOffset; + + RIG->RigFreq = Freq / 1000000.0; + + // If we convert to float to display we get rounding errors, so convert to MHz and Hz to display + + MHz = Freq / 1000000; + + Hz = Freq - MHz * 1000000; + + sprintf(CharMHz, "%lld", MHz); + sprintf(CharHz, "%06lld", Hz); + + for (i = 5; i > 2; i--) + { + if (CharHz[i] == '0') + CharHz[i] = 0; + else + break; + } + + sprintf(RIG->WEB_FREQ,"%lld.%s", MHz, CharHz); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + strcpy(RIG->Valchar, RIG->WEB_FREQ); + + + // Now get Mode + + Poll[0] = 0xFE; + Poll[1] = 0xFE; + Poll[2] = RIG->RigAddr; + Poll[3] = 0xE0; + + if (RIG->ICF8101) + { + Poll[4] = 0x1A; // Get Mode + Poll[5] = 0x34; + Poll[6] = 0xFD; + PORT->TXLen = 7; + } + else + { + Poll[4] = 0x4; // Get Mode + Poll[5] = 0xFD; + + PORT->TXLen = 6; + } + + RigWriteCommBlock(PORT); + PORT->Retries = 2; + return; + } + + if (RIG->ICF8101) + { + if (cmdSent == 0x1A && PORT->TXBuffer[5] == 0x34) + { + // Get Mode + + unsigned int Mode; + + Mode = (Msg[7] >> 4); + Mode *= 10; + Mode += Msg[7] & 0xf; + + if (Mode > 24) Mode = 24; + + sprintf(RIG->WEB_MODE,"%s", Modes[Mode]); + + strcpy(RIG->ModeString, Modes[Mode]); + SetWindowText(RIG->hMODE, RIG->WEB_MODE); + + return; + } + } + + + if (Msg[4] == 4) + { + // Mode + + unsigned int Mode; + + Mode = (Msg[5] >> 4); + Mode *= 10; + Mode += Msg[5] & 0xf; + + if (Mode > 24) Mode = 24; + + if (RIG->IC735) + sprintf(RIG->WEB_MODE,"%s", Modes[Mode]); + else + sprintf(RIG->WEB_MODE,"%s/%d", Modes[Mode], Msg[6]); + + strcpy(RIG->ModeString, Modes[Mode]); + SetWindowText(RIG->hMODE, RIG->WEB_MODE); + } +} + +int SendResponse(int Session, char * Msg) +{ + PDATAMESSAGE Buffer; + BPQVECSTRUC * VEC; + TRANSPORTENTRY * L4 = L4TABLE; + + if (Session == -1) + return 0; + + Buffer = GetBuff(); + + L4 += Session; + + Buffer->PID = 0xf0; + Buffer->LENGTH = MSGHDDRLEN + 1; // Includes PID + Buffer->LENGTH += sprintf(Buffer->L2DATA, "%s\r", Msg); + + VEC = L4->L4TARGET.HOST; + + C_Q_ADD(&L4->L4TX_Q, (UINT *)Buffer); + +#ifndef LINBPQ + + if (VEC) + PostMessage(VEC->HOSTHANDLE, BPQMsg, VEC->HOSTSTREAM, 2); +#endif + return 0; +} + +VOID ProcessFT100Frame(struct RIGPORTINFO * PORT) +{ + // Only one we should see is a Status Message + + UCHAR * Poll = PORT->TXBuffer; + UCHAR * Msg = PORT->RXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on Yaseu + int Freq; + double FreqF; + unsigned int Mode; + + RIG->RIGOK = TRUE; + PORT->Timeout = 0; + + // Bytes 0 is Band + // 1 - 4 is Freq in binary in units of 1.25 HZ (!) + // Byte 5 is Mode + + Freq = (Msg[1] << 24) + (Msg[2] << 16) + (Msg[3] << 8) + Msg[4]; + + FreqF = (Freq * 1.25) / 1000000; + + if (PORT->YaesuVariant == FT1000MP) + FreqF = FreqF / 2; // No idea why! + + RIG->RigFreq = FreqF; + + _gcvt(FreqF, 9, RIG->Valchar); + + sprintf(RIG->WEB_FREQ,"%s", RIG->Valchar); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + + if (PORT->PortType == FT100) + { + Mode = Msg[5] & 15; + if (Mode > 8) Mode = 8; + sprintf(RIG->WEB_MODE,"%s", FT100Modes[Mode]); + } + else // FT1000 + { + Mode = Msg[7] & 7; + sprintf(RIG->WEB_MODE,"%s", FTRXModes[Mode]); + } + + strcpy(RIG->ModeString, RIG->WEB_MODE); + SetWindowText(RIG->hMODE, RIG->WEB_MODE); + + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Mode and Frequency Set OK"); + else + if (PORT->TXLen > 5) // Poll is 5 Change is more + ReleasePermission(RIG); // Release Perrmission to change +} + + + +VOID ProcessFT990Frame(struct RIGPORTINFO * PORT) +{ + // Only one we should see is a Status Message + + UCHAR * Poll = PORT->TXBuffer; + UCHAR * Msg = PORT->RXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on Yaseu + int Freq; + double FreqF; + unsigned int Mode; + + RIG->RIGOK = TRUE; + PORT->Timeout = 0; + + // Bytes 0 is Band + // 1 - 4 is Freq in units of 10Hz (I think!) + // Byte 5 is Mode + + Freq = (Msg[1] << 16) + (Msg[2] << 8) + Msg[3]; + + FreqF = (Freq * 10.0) / 1000000; + + RIG->RigFreq = FreqF; + + _gcvt(FreqF, 9, RIG->Valchar); + + sprintf(RIG->WEB_FREQ,"%s", RIG->Valchar); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + + Mode = Msg[7] & 7; + sprintf(RIG->WEB_MODE,"%s", FTRXModes[Mode]); + + strcpy(RIG->ModeString, RIG->WEB_MODE); + SetWindowText(RIG->hMODE, RIG->WEB_MODE); + + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Mode and Frequency Set OK"); + else + if (PORT->TXLen > 5) // Poll is 5 change is more + ReleasePermission(RIG); // Release Perrmission to change +} + +VOID ProcessFT1000Frame(struct RIGPORTINFO * PORT) +{ + // Only one we should see is a Status Message + + UCHAR * Poll = PORT->TXBuffer; + UCHAR * Msg = PORT->RXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on Yaseu + int Freq; + double FreqF; + unsigned int Mode; + + RIG->RIGOK = TRUE; + PORT->Timeout = 0; + + // I think the FT1000/1000D is same as 990 + // FT1000MP is similar to FT100, but steps on .625 Hz (despite manual) + // Bytes 0 is Band + // 1 - 4 is Freq in binary in units of 1.25 HZ (!) + // Byte 5 is Mode + + if (PORT->YaesuVariant == FT1000MP) + { + Freq = (Msg[1] << 24) + (Msg[2] << 16) + (Msg[3] << 8) + Msg[4]; + FreqF = (Freq * 1.25) / 1000000; + FreqF = FreqF / 2; // No idea why! + } + else + { + Freq = (Msg[1] << 16) + (Msg[2] << 8) + Msg[3]; + FreqF = (Freq * 10.0) / 1000000; + } + + RIG->RigFreq = FreqF; + + _gcvt(FreqF, 9, RIG->Valchar); + + sprintf(RIG->WEB_FREQ,"%s", RIG->Valchar); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + + if (PORT->PortType == FT100) + { + Mode = Msg[5] & 15; + if (Mode > 8) Mode = 8; + sprintf(RIG->WEB_MODE,"%s", FT100Modes[Mode]); + } + else // FT1000 + { + Mode = Msg[7] & 7; + sprintf(RIG->WEB_MODE,"%s", FTRXModes[Mode]); + } + + strcpy(RIG->ModeString, RIG->WEB_MODE); + SetWindowText(RIG->hMODE, RIG->WEB_MODE); + + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Mode and Frequency Set OK"); + else + if (PORT->TXLen > 5) // Poll is 5 change is more + ReleasePermission(RIG); // Release Perrmission to change +} + + + + + +VOID ProcessYaesuCmdAck(struct RIGPORTINFO * PORT) +{ + UCHAR * Poll = PORT->TXBuffer; + UCHAR * Msg = PORT->RXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on Yaseu + + PORT->Timeout = 0; + PORT->RXLen = 0; // Ready for next frame + + if (PORT->CmdSent == 1) // Set Freq + { + ReleasePermission(RIG); // Release Perrmission + + if (Msg[0]) + { + // I think nonzero is a Reject + + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Sorry - Set Frequency Rejected"); + + return; + } + else + { + if (RIG->ScanStopped == 0) + { + // Send a Get Freq - We Don't Poll when scanning + + Poll[0] = Poll[1] = Poll[2] = Poll[3] = 0; + Poll[4] = 0x3; // Get frequency amd mode command + + RigWriteCommBlock(PORT); + PORT->Retries = 2; + PORT->CmdSent = 0; + } + else + + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Mode and Frequency Set OK"); + + return; + } + } + + if (PORT->CmdSent == 7) // Set Mode + { + if (Msg[0]) + { + // I think nonzero is a Reject + + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Sorry - Set Mode Rejected"); + + return; + } + else + { + // Send the Frequency + + memcpy(Poll, &Poll[5], 5); + RigWriteCommBlock(PORT); + PORT->CmdSent = Poll[4]; + PORT->Retries = 2; + + return; + } + } + +} +VOID ProcessYaesuFrame(struct RIGPORTINFO * PORT) +{ + UCHAR * Poll = PORT->TXBuffer; + UCHAR * Msg = PORT->RXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on Yaseu + int n, j, Freq = 0, decdigit; + double FreqF; + unsigned int Mode; + + // I'm not sure we get anything but a Command Response, + // and the only command we send is Get Rig Frequency and Mode + + + RIG->RIGOK = TRUE; + PORT->Timeout = 0; + + for (j = 0; j < 4; j++) + { + n = Msg[j]; + decdigit = (n >> 4); + decdigit *= 10; + decdigit += n & 0xf; + Freq = (Freq *100 ) + decdigit; + } + + FreqF = Freq / 100000.0; + + RIG->RigFreq = FreqF; + +// Valchar = _fcvt(FreqF, 6, &dec, &sign); + _gcvt(FreqF, 9, RIG->Valchar); + + sprintf(RIG->WEB_FREQ,"%s", RIG->Valchar); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + + Mode = Msg[4]; + + if (Mode > 15) Mode = 15; + + sprintf(RIG->WEB_MODE,"%s", YaesuModes[Mode]); + strcpy(RIG->ModeString, RIG->WEB_MODE); + SetWindowText(RIG->hMODE, RIG->WEB_MODE); + + // FT847 Manual Freq Change response ends up here + + if (strcmp(RIG->RigName, "FT847") == 0) + { + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Mode and Frequency Set OK"); + + if (PORT->CmdSent == -1) + ReleasePermission(RIG); // Release Perrmission to change + } +} + +VOID YaesuPoll(struct RIGPORTINFO * PORT) +{ + UCHAR * Poll = PORT->TXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on Yaseu + + if (RIG->ScanStopped == 0) + if (RIG->ScanCounter) + RIG->ScanCounter--; + + if (PORT->Timeout) + { + PORT->Timeout--; + + if (PORT->Timeout) // Still waiting + return; + + PORT->Retries--; + + if(PORT->Retries) + { + RigWriteCommBlock(PORT); // Retransmit Block + return; + } + + SetWindowText(RIG->hFREQ, "------------------"); + SetWindowText(RIG->hMODE, "----------"); + strcpy(RIG->WEB_FREQ, "-----------");; + strcpy(RIG->WEB_MODE, "------"); + + + PORT->Rigs[PORT->CurrentRig].RIGOK = FALSE; + + return; + + } + + // Send Data if avail, else send poll + + if (RIG->NumberofBands && (RIG->ScanStopped == 0)) + { + if (RIG->ScanCounter <= 0) + { + // Send Next Freq + + if (GetPermissionToChange(PORT, RIG)) + { + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Change Freq to %9.4f", PORT->FreqPtr->Freq); + + _gcvt(PORT->FreqPtr->Freq / 1000000.0, 9, RIG->Valchar); // For MH + + memcpy(PORT->TXBuffer, PORT->FreqPtr->Cmd1, 24); + + if (PORT->PortType == YAESU) + { + if (strcmp(PORT->Rigs[0].RigName, "FT847") == 0) + { + PORT->TXLen = 15; // No Cmd ACK, so send Mode, Freq and Poll + PORT->CmdSent = -1; + } + else + { + PORT->TXLen = 5; + PORT->CmdSent = Poll[4]; + } + RigWriteCommBlock(PORT); + PORT->Retries = 2; + PORT->AutoPoll = TRUE; + return; + } + + // FT100 + + PORT->TXLen = 15; // Set Mode, Set Freq, Poll + RigWriteCommBlock(PORT); + PORT->Retries = 2; + PORT->AutoPoll = TRUE; + } + } + } + + if (RIG->RIGOK && RIG->BPQtoRADIO_Q) + { + struct MSGWITHOUTLEN * buffptr; + + buffptr = Q_REM(&RIG->BPQtoRADIO_Q); + + // Copy the ScanEntry struct from the Buffer to the PORT Scanentry + + memcpy(&PORT->ScanEntry, buffptr->Data, sizeof(struct ScanEntry)); + + PORT->FreqPtr = &PORT->ScanEntry; // Block we are currently sending. + + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Change Freq to %9.4f", PORT->FreqPtr->Freq); + + _gcvt(PORT->FreqPtr->Freq / 1000000.0, 9, RIG->Valchar); // For MH + + DoBandwidthandAntenna(RIG, &PORT->ScanEntry); + + memcpy(Poll, PORT->FreqPtr->Cmd1, PORT->FreqPtr->Cmd1Len); + + if (PORT->PortType == YAESU) + { + if (strcmp(PORT->Rigs[0].RigName, "FT847") == 0) + { + PORT->TXLen = 15; // Send all + PORT->CmdSent = -1; + } + else + { + PORT->TXLen = 5; // First send the set Freq + PORT->CmdSent = Poll[4]; + } + } + else + PORT->TXLen = 15; // Send all + + RigWriteCommBlock(PORT); + PORT->Retries = 2; + + ReleaseBuffer(buffptr); + PORT->AutoPoll = FALSE; + + return; + } + + if (RIG->RIGOK && RIG->ScanStopped == 0 && RIG->NumberofBands && + RIG->ScanCounter && RIG->ScanCounter < 30) // no point in reading freq if we are about to change + return; + + if (RIG->PollCounter) + { + RIG->PollCounter--; + if (RIG->PollCounter) + return; + } + + RIG->PollCounter = 10; // Once Per Sec + + // Read Frequency + + Poll[0] = 0; + Poll[1] = 0; + Poll[2] = 0; + + if (PORT->PortType == FT990 || PORT->PortType == YAESU || PORT->YaesuVariant == FT1000D) + Poll[3] = 3; + else + Poll[3] = 2; + + if (PORT->PortType == YAESU) + Poll[4] = 0x3; // Get frequency amd mode command + else + Poll[4] = 16; // FT100/990/1000 Get frequency amd mode command + + PORT->TXLen = 5; + RigWriteCommBlock(PORT); + PORT->Retries = 2; + PORT->CmdSent = 0; + + PORT->AutoPoll = TRUE; + + return; +} + +VOID ProcessNMEA(struct RIGPORTINFO * PORT, char * Msg, int Length) +{ + UCHAR * Poll = PORT->TXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on Yaseu + + Msg[Length] = 0; + + if (PORT->PORTOK == FALSE) + { + // Just come up + PORT->PORTOK = TRUE; + } + + RIG->RIGOK = TRUE; + PORT->Timeout = 0; + + if (!PORT->AutoPoll) + { + // Response to a RADIO Command + + if (Msg[0] == '?') + SendResponse(RIG->Session, "Sorry - Command Rejected"); + else + SendResponse(RIG->Session, "Mode and Frequency Set OK"); + + PORT->AutoPoll = TRUE; + } + + if (memcmp(&Msg[13], "RXF", 3) == 0) + { + if (Length < 24) + return; + + RIG->RigFreq = atof(&Msg[17]); + + sprintf(RIG->WEB_FREQ,"%f", RIG->RigFreq); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + strcpy(RIG->Valchar, RIG->WEB_FREQ); + + return; + } + + if (memcmp(&Msg[13], "MODE", 3) == 0) + { + char * ptr; + + if (Length < 24) + return; + + ptr = strchr(&Msg[18], '*'); + if (ptr) *(ptr) = 0; + + SetWindowText(RIG->hMODE, &Msg[18]); + strcpy(RIG->WEB_MODE, &Msg[18]); + strcpy(RIG->ModeString, RIG->WEB_MODE); + return; + } + + +} + + +//FA00014103000;MD2; + +VOID ProcessKenwoodFrame(struct RIGPORTINFO * PORT, int Length) +{ + UCHAR * Poll = PORT->TXBuffer; + UCHAR * Msg = PORT->RXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on Yaseu + UCHAR * ptr; + int CmdLen; + + Msg[Length] = 0; + + if (PORT->PORTOK == FALSE) + { + // Just come up + PORT->PORTOK = TRUE; + } + + RIG->RIGOK = TRUE; + + if (!PORT->AutoPoll) + { + // Response to a RADIO Command + + if (Msg[0] == '?') + SendResponse(RIG->Session, "Sorry - Command Rejected"); + else if (Msg[0] == 'A' && Msg[1] == 'C') + SendResponse(RIG->Session, "TUNE OK"); + else if (Msg[0] == 'P' && Msg[1] == 'C') + SendResponse(RIG->Session, "Power Set OK"); + else + SendResponse(RIG->Session, "Mode and Frequency Set OK"); + + PORT->AutoPoll = TRUE; + return; + } + + +Loop: + + if (PORT->PortType == FLEX) + { + Msg += 2; // Skip ZZ + Length -= 2; + } + + ptr = strchr(Msg, ';'); + CmdLen = (int)(ptr - Msg + 1); + + if (Msg[0] == 'F' && Msg[1] == 'A' && CmdLen > 9) + { + char CharMHz[16] = ""; + char CharHz[16] = ""; + + int i; + long long Freq; + long long MHz, Hz; + + Freq = strtoll(&Msg[2], NULL, 10) + RIG->rxOffset; + + RIG->RigFreq = Freq / 1000000.0; + + // If we convert to float to display we get rounding errors, so convert to MHz and Hz to display + + MHz = Freq / 1000000; + + Hz = Freq - MHz * 1000000; + + sprintf(CharMHz, "%lld", MHz); + sprintf(CharHz, "%06lld", Hz); + +/* + if (PORT->PortType == FT2000) + { + memcpy(FreqDecimal,&Msg[4], 6); + Msg[4] = 0; + } + else if (PORT->PortType == FT991A) + { + memcpy(FreqDecimal,&Msg[5], 6); + Msg[5] = 0; + } + + else + { + memcpy(FreqDecimal,&Msg[7], 6); + Msg[7] = 0; + } + FreqDecimal[6] = 0; +*/ + + for (i = 5; i > 2; i--) + { + if (CharHz[i] == '0') + CharHz[i] = 0; + else + break; + } + + + sprintf(RIG->WEB_FREQ,"%lld.%s", MHz, CharHz); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + strcpy(RIG->Valchar, RIG->WEB_FREQ); + + PORT->Timeout = 0; + } + else if (Msg[0] == 'M' && Msg[1] == 'D') + { + int Mode; + + if (PORT->PortType == FT2000) + { + Mode = Msg[3] - 48; + if (Mode > 13) Mode = 13; + SetWindowText(RIG->hMODE, FT2000Modes[Mode]); + strcpy(RIG->WEB_MODE, FT2000Modes[Mode]); + strcpy(RIG->ModeString, RIG->WEB_MODE); + } + else if (PORT->PortType == FT991A) + { + Mode = Msg[3] - 48; + if (Mode > 16) + Mode -= 7; + + if (Mode > 15) Mode = 13; + SetWindowText(RIG->hMODE, FT991AModes[Mode]); + strcpy(RIG->WEB_MODE, FT991AModes[Mode]); + strcpy(RIG->ModeString, RIG->WEB_MODE); + } + else if (PORT->PortType == FLEX) + { + Mode = atoi(&Msg[3]); + if (Mode > 12) Mode = 12; + SetWindowText(RIG->hMODE, FLEXModes[Mode]); + strcpy(RIG->WEB_MODE, FLEXModes[Mode]); + strcpy(RIG->ModeString, RIG->WEB_MODE); + } + else + { + Mode = Msg[2] - 48; + if (Mode > 7) Mode = 7; + SetWindowText(RIG->hMODE, KenwoodModes[Mode]); + strcpy(RIG->WEB_MODE, KenwoodModes[Mode]); + strcpy(RIG->ModeString, RIG->WEB_MODE); + } + } + + if (CmdLen < Length) + { + // Another Message in Buffer + + ptr++; + Length -= (int)(ptr - Msg); + + if (Length <= 0) + return; + + memmove(Msg, ptr, Length +1); + + goto Loop; + } +} + + +VOID KenwoodPoll(struct RIGPORTINFO * PORT) +{ + UCHAR * Poll = PORT->TXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on Kenwood + + if (RIG->ScanStopped == 0) + if (RIG->ScanCounter) + RIG->ScanCounter--; + + if (PORT->Timeout) + { + PORT->Timeout--; + + if (PORT->Timeout) // Still waiting + return; + + PORT->Retries--; + + if(PORT->Retries) + { + RigWriteCommBlock(PORT); // Retransmit Block + return; + } + + SetWindowText(RIG->hFREQ, "------------------"); + SetWindowText(RIG->hMODE, "----------"); + strcpy(RIG->WEB_FREQ, "-----------");; + strcpy(RIG->WEB_MODE, "------"); + + RIG->RIGOK = FALSE; + + return; + } + + // Send Data if avail, else send poll + + if (RIG->NumberofBands && RIG->RIGOK && (RIG->ScanStopped == 0)) + { + if (RIG->ScanCounter <= 0) + { + // Send Next Freq + + if (GetPermissionToChange(PORT, RIG)) + { + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Change Freq to %9.4f", PORT->FreqPtr->Freq); + + memcpy(PORT->TXBuffer, PORT->FreqPtr->Cmd1, PORT->FreqPtr->Cmd1Len); + PORT->TXLen = PORT->FreqPtr->Cmd1Len; + + _gcvt(PORT->FreqPtr->Freq / 1000000.0, 9, RIG->Valchar); // For MH + + RigWriteCommBlock(PORT); + PORT->CmdSent = 1; + PORT->Retries = 0; + PORT->Timeout = 0; + PORT->AutoPoll = TRUE; + + // There isn't a response to a set command, so clear Scan Lock here + + ReleasePermission(RIG); // Release Perrmission + + return; + } + } + } + + if (RIG->RIGOK && RIG->BPQtoRADIO_Q) + { + struct MSGWITHOUTLEN * buffptr; + + buffptr = Q_REM(&RIG->BPQtoRADIO_Q); + + // Copy the ScanEntry struct from the Buffer to the PORT Scanentry + + memcpy(&PORT->ScanEntry, buffptr->Data, sizeof(struct ScanEntry)); + + PORT->FreqPtr = &PORT->ScanEntry; // Block we are currently sending. + + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Change Freq to %9.4f", PORT->FreqPtr->Freq); + + DoBandwidthandAntenna(RIG, &PORT->ScanEntry); + + _gcvt(PORT->FreqPtr->Freq / 1000000.0, 9, RIG->Valchar); // For MH + + memcpy(Poll, PORT->FreqPtr->Cmd1, PORT->FreqPtr->Cmd1Len); + + PORT->TXLen = PORT->FreqPtr->Cmd1Len; + RigWriteCommBlock(PORT); + PORT->CmdSent = Poll[4]; + PORT->Timeout = 0; + RIG->PollCounter = 10; + + ReleaseBuffer(buffptr); + PORT->AutoPoll = FALSE; + + return; + } + + if (RIG->PollCounter) + { + RIG->PollCounter--; + if (RIG->PollCounter > 1) + return; + } + + if (RIG->RIGOK && RIG->ScanStopped == 0 && RIG->NumberofBands && + RIG->ScanCounter && RIG->ScanCounter < 30) + return; // no point in reading freq if we are about to change it + + RIG->PollCounter = 10; // Once Per Sec + + // Read Frequency + + PORT->TXLen = RIG->PollLen; + strcpy(Poll, RIG->Poll); + + RigWriteCommBlock(PORT); + PORT->Retries = 1; + PORT->Timeout = 10; + PORT->CmdSent = 0; + + PORT->AutoPoll = TRUE; + + return; +} + +VOID DummyPoll(struct RIGPORTINFO * PORT) +{ + UCHAR * Poll = PORT->TXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; + + if (RIG->ScanStopped == 0) + if (RIG->ScanCounter) + RIG->ScanCounter--; + + if (RIG->NumberofBands && RIG->RIGOK && (RIG->ScanStopped == 0)) + { + if (RIG->ScanCounter <= 0) + { + // Send Next Freq + + if (GetPermissionToChange(PORT, RIG)) + { + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Change Freq to %9.4f", PORT->FreqPtr->Freq); + + _gcvt(PORT->FreqPtr->Freq / 1000000.0, 9, RIG->Valchar); // For MH + + memcpy(PORT->TXBuffer, PORT->FreqPtr->Cmd1, PORT->FreqPtr->Cmd1Len); + PORT->TXLen = PORT->FreqPtr->Cmd1Len; +/* + RigWriteCommBlock(PORT); + PORT->CmdSent = 1; + PORT->Retries = 0; + PORT->Timeout = 0; + PORT->AutoPoll = TRUE; +*/ + // There isn't a response to a set command, so clear Scan Lock here + + ReleasePermission(RIG); // Release Perrmission + + return; + } + } + } + + return; +} + +VOID SwitchAntenna(struct RIGINFO * RIG, char Antenna) +{ + struct RIGPORTINFO * PORT; + char Ant[3]=" "; + + if (RIG == NULL) return; + + PORT = RIG->PORT; + + Ant[1] = Antenna; + + SetWindowText(RIG->hPTT, Ant); + + switch (Antenna) + { + case '1': + COMClearDTR(PORT->hDevice); + COMClearRTS(PORT->hDevice); + break; + case '2': + COMSetDTR(PORT->hDevice); + COMClearRTS(PORT->hDevice); + break; + case '3': + COMClearDTR(PORT->hDevice); + COMSetRTS(PORT->hDevice); + break; + case '4': + COMSetDTR(PORT->hDevice); + COMSetRTS(PORT->hDevice); + break; + } +} + +BOOL DecodeModePtr(char * Param, double * Dwell, double * Freq, char * Mode, + char * PMinLevel, char * PMaxLevel, char * PacketMode, + char * RPacketMode, char * Split, char * Data, char * WinmorMode, + char * Antenna, BOOL * Supress, char * Filter, char * Appl, + char * MemoryBank, int * MemoryNumber, char * ARDOPMode, char * VARAMode, int * BandWidth, int * Power) +{ + char * Context; + char * ptr; + + *Filter = '1'; // Defaults + *PMinLevel = 1; + *MemoryBank = 0; + *MemoryNumber = 0; + *Mode = 0; + *ARDOPMode = 0; + *VARAMode = 0; + *Power = 0; + + + ptr = strtok_s(Param, ",", &Context); + + if (ptr == NULL) + return FALSE; + + // "New" format - Dwell, Freq, Params. + + // Each param is a 2 char pair, separated by commas + + // An - Antenna + // Pn - Pactor + // Wn - Winmor + // Pn - Packet + // Fn - Filter + // Sx - Split + + // 7.0770/LSB,F1,A3,WN,P1,R1 + + *Dwell = atof(ptr); + + ptr = strtok_s(NULL, ",", &Context); + + if (ptr == NULL) + return FALSE; + + + // May be a frequency or a Memory Bank/Channel + + if (_memicmp(ptr, "Chan", 4) == 0) + { + if (strchr(ptr, '/')) // Bank/Chan + { + memcpy(MemoryBank, &ptr[4], 1); + *MemoryNumber = atoi(&ptr[6]); + } + else + *MemoryNumber = atoi(&ptr[4]); // Just Chan + + *Freq = 0.0; + } + else + *Freq = atof(ptr); + + ptr = strtok_s(NULL, ",", &Context); + + if (ptr == NULL || strlen(ptr) > 8) + return FALSE; + + // If channel, dont need mode + + if (*MemoryNumber == 0) + { + strcpy(Mode, ptr); + ptr = strtok_s(NULL, ",", &Context); + } + + while (ptr) + { + if (isdigit(ptr[0])) + *BandWidth = atoi(ptr); + + else if (memcmp(ptr, "APPL=", 5) == 0) + strcpy(Appl, ptr + 5); + + else if (memcmp(ptr, "POWER=", 6) == 0) + *Power = atoi(ptr + 6); + + else if (ptr[0] == 'A' && (ptr[1] == 'S' || ptr[1] == '0') && strlen(ptr) < 7) + strcpy(ARDOPMode, "S"); + + else if (ptr[0] == 'A' && strlen(ptr) > 2 && strlen(ptr) < 7) + strcpy(ARDOPMode, &ptr[1]); + + else if (ptr[0] == 'A' && strlen(ptr) == 2) + *Antenna = ptr[1]; + + else if (ptr[0] == 'F') + *Filter = ptr[1]; + + else if (ptr[0] == 'R') + *RPacketMode = ptr[1]; + + else if (ptr[0] == 'H') + *PacketMode = ptr[1]; + + else if (ptr[0] == 'N') + *PacketMode = ptr[1]; + + else if (ptr[0] == 'P') + { + *PMinLevel = ptr[1]; + *PMaxLevel = ptr[strlen(ptr) - 1]; + } + else if (ptr[0] == 'W') + { + *WinmorMode = ptr[1]; + if (*WinmorMode == '0') + *WinmorMode = 'X'; + else if (*WinmorMode == '1') + *WinmorMode = 'N'; + else if (*WinmorMode == '2') + *WinmorMode = 'W'; + } + + else if (ptr[0] == 'V') + { + *VARAMode = ptr[1]; + // W N S T (skip) or 0 (also Skip) + if (ptr[1] == '0') + *VARAMode = 'S'; + } + else if (ptr[0] == '+') + *Split = '+'; + else if (ptr[0] == '-') + *Split = '-'; + else if (ptr[0] == 'S') + *Split = 'S'; + else if (ptr[0] == 'D') + *Data = 1; + else if (ptr[0] == 'X') + *Supress = TRUE; + + ptr = strtok_s(NULL, ",", &Context); + } + return TRUE; +} + +VOID AddNMEAChecksum(char * msg) +{ + UCHAR CRC = 0; + + msg++; // Skip $ + + while (*(msg) != '*') + { + CRC ^= *(msg++); + } + + sprintf(++msg, "%02X\r\n", CRC); +} + +void DecodeRemote(struct RIGPORTINFO * PORT, char * ptr) +{ + // Param is IPHOST:PORT for use with WINMORCONTROL + + struct sockaddr_in * destaddr = (SOCKADDR_IN * )&PORT->remoteDest; + UCHAR param = 1; + u_long ioparam = 1; + + char * port = strlop(ptr, ':'); + + PORT->remoteSock = socket(AF_INET,SOCK_DGRAM,0); + + if (PORT->remoteSock == INVALID_SOCKET) + return; + + setsockopt (PORT->remoteSock, SOL_SOCKET, SO_BROADCAST, ¶m, 1); + + if (port == NULL) + return; + + ioctl (PORT->remoteSock, FIONBIO, &ioparam); + + destaddr->sin_family = AF_INET; + destaddr->sin_addr.s_addr = inet_addr(ptr); + destaddr->sin_port = htons(atoi(port)); + + if (destaddr->sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + struct hostent * HostEnt = gethostbyname(ptr); + + if (!HostEnt) + return; // Resolve failed + + memcpy(&destaddr->sin_addr.s_addr,HostEnt->h_addr,4); + } + + return; +} + + +char * CM108Device = NULL; + +void DecodeCM108(int Port, char * ptr) +{ + // Called if Device Name or PTT = Param is CM108 + +#ifdef WIN32 + + // Next Param is VID and PID - 0xd8c:0x8 or Full device name + // On Windows device name is very long and difficult to find, so + // easier to use VID/PID, but allow device in case more than one needed + + char * next; + int32_t VID = 0, PID = 0; + char product[256]; + + struct hid_device_info *devs, *cur_dev; + const char *path_to_open = NULL; + hid_device *handle = NULL; + + if (strlen(ptr) > 16) + CM108Device = _strdup(ptr); + else + { + VID = strtol(ptr, &next, 0); + if (next) + PID = strtol(++next, &next, 0); + + // Look for Device + + devs = hid_enumerate(0,0); // so we list devices(USHORT)VID, (USHORT)PID); + cur_dev = devs; + while (cur_dev) + { + wcstombs(product, cur_dev->product_string, 255); + + if (product) + Debugprintf("HID Device %s VID %X PID %X %s", product, cur_dev->vendor_id, cur_dev->product_id, cur_dev->path); + else + Debugprintf("HID Device %s VID %X PID %X %s", "Missing Product", cur_dev->vendor_id, cur_dev->product_id, cur_dev->path); + + if (cur_dev->vendor_id == VID && cur_dev->product_id == PID) + path_to_open = cur_dev->path; + + cur_dev = cur_dev->next; + } + + if (path_to_open) + { + handle = hid_open_path(path_to_open); + + if (handle) + { + hid_close(handle); + CM108Device = _strdup(path_to_open); + } + else + { + char msg[128]; + sprintf(msg,"Port %d Unable to open CM108 device %x %x", Port, VID, PID); + WritetoConsole(msg); + } + } + hid_free_enumeration(devs); + } +#else + + // Linux - Next Param HID Device, eg /dev/hidraw0 + + CM108Device = _strdup(ptr); +#endif +} + + + +// Called by Port Driver .dll to add/update rig info + +// RIGCONTROL COM60 19200 ICOM IC706 5e 4 14.103/U1w 14.112/u1 18.1/U1n 10.12/l1 + + +struct RIGINFO * RigConfig(struct TNCINFO * TNC, char * buf, int Port) +{ + int i; + char * ptr; + char * COMPort = NULL; + char * RigName; + int RigAddr; + struct RIGPORTINFO * PORT; + struct RIGINFO * RIG; + struct ScanEntry ** FreqPtr; + char * CmdPtr; + char * Context; + struct TimeScan * SaveBand; + char PTTRigName[] = "PTT"; + double ScanFreq; + double Dwell; + char MemoryBank; // For Memory Scanning + int MemoryNumber; + BOOL RIG_DEBUG = FALSE; + + BOOL PTTControlsInputMUX = FALSE; + BOOL DataPTT = FALSE; + int DataPTTOffMode = 1; // ACC + int ICF8101Mode = 8; // USB (as in upper sideband) + + char onString[256] = ""; + char offString[256] = ""; + int onLen = 0; + int offLen = 0; + + CM108Device = NULL; + Debugprintf("Processing RIG line %s", buf); + + // Starts RADIO Interlockgroup + + ptr = strtok_s(&buf[5], " \t\n\r", &Context); // Skip Interlock + if (ptr == NULL) return FALSE; + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr == NULL) return FALSE; + + if (_memicmp(ptr, "DEBUG", 5) == 0) + { + ptr = strtok_s(NULL, " \t\n\r", &Context); + RIG_DEBUG = TRUE; + } + + if (_memicmp(ptr, "AUTH", 4) == 0) + { + ptr = strtok_s(NULL, " \t\n\r", &Context); + if (ptr == NULL) return FALSE; + if (strlen(ptr) > 100) return FALSE; + + strcpy(AuthPassword, ptr); + ptr = strtok_s(NULL, " \t\n\r", &Context); + } + + if (ptr == NULL || ptr[0] == 0) + return FALSE; + + if (_memicmp(ptr, "DUMMY", 5) == 0) + { + // Dummy to allow PTC application scanning + + PORT = PORTInfo[NumberofPorts++] = zalloc(sizeof(struct RIGPORTINFO)); + PORT->PortType = DUMMY; + PORT->ConfiguredRigs = 1; + RIG = &PORT->Rigs[0]; + RIG->RIGOK = TRUE; + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + goto CheckScan; + } + + if (_memicmp(ptr, "FLRIG", 5) == 0) + { + // Use FLRIG + + // Need parameter - Host:Port + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr == NULL || strlen(ptr) > 79) return FALSE; + + // Have a parameter to define port. Will decode it later + + PORT = PORTInfo[NumberofPorts++] = zalloc(sizeof(struct RIGPORTINFO)); + PORT->PortType = FLRIG; + PORT->ConfiguredRigs = 1; + RIG = &PORT->Rigs[0]; + RIG->RIGOK = TRUE; + RIG->PORT = PORT; + + strcpy(PORT->IOBASE, ptr); + strcpy(RIG->RigName, "FLRIG"); + + // Decode host + + DecodeHAMLIBAddr(PORT, ptr); + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + // look for other config statementes and scan params + + goto CheckOtherParams; + } + + if (_memicmp(ptr, "HAMLIB", 5) == 0) + { + // Use rigctld + + // Need parameter - Host:Port + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr == NULL || strlen(ptr) > 79) return FALSE; + + // Have a parameter to define port. Will decode it later + + PORT = PORTInfo[NumberofPorts++] = zalloc(sizeof(struct RIGPORTINFO)); + PORT->PortType = HAMLIB; + PORT->ConfiguredRigs = 1; + RIG = &PORT->Rigs[0]; + RIG->RIGOK = TRUE; + RIG->PORT = PORT; + + strcpy(PORT->IOBASE, ptr); + strcpy(RIG->RigName, "HAMLIB"); + + // Decode host + + DecodeHAMLIBAddr(PORT, ptr); + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + // look for scan params + + goto CheckOtherParams; + } + + if (_memicmp(ptr, "rtludp", 5) == 0) + { + // rtl_fm with udp freq control + + // Need parameter - Host:Port + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr == NULL || strlen(ptr) > 79) return FALSE; + + // Have a parameter to define port. Will decode it later + + PORT = PORTInfo[NumberofPorts++] = zalloc(sizeof(struct RIGPORTINFO)); + PORT->PortType = RTLUDP; + PORT->ConfiguredRigs = 1; + RIG = &PORT->Rigs[0]; + RIG->RIGOK = TRUE; + RIG->PORT = PORT; + + strcpy(PORT->IOBASE, ptr); + strcpy(RIG->RigName, "RTLUDP"); + + // Decode host + + DecodeHAMLIBAddr(PORT, ptr); + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + // look for scan params + + goto CheckOtherParams; + } + + + if ((_memicmp(ptr, "VCOM", 4) == 0) && TNC->Hardware == H_SCS) // Using Radio Port on PTC + COMPort = 0; + else if (_memicmp(ptr, "PTCPORT", 7) == 0) + COMPort = 0; + else + COMPort = ptr; + + // See if port is already defined. We may be adding another radio (ICOM only) or updating an existing one + + // Unless CM108 - they must be on separate Ports + + if (COMPort && _stricmp("CM108", COMPort) != 0) + { + for (i = 0; i < NumberofPorts; i++) + { + PORT = PORTInfo[i]; + + if (COMPort) + if (strcmp(PORT->IOBASE, COMPort) == 0) + goto PortFound; + + if (COMPort == 0) + if (PORT->IOBASE == COMPort) + goto PortFound; + } + } + + // Allocate a new one + + PORT = PORTInfo[NumberofPorts++] = zalloc(sizeof(struct RIGPORTINFO)); + + if (COMPort) + strcpy(PORT->IOBASE, COMPort); + +PortFound: + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr == NULL) return (FALSE); + + if (_stricmp(PORT->IOBASE, "RAWHID") == 0) // HID Addr instead of Speed + { + DecodeCM108(Port, ptr); + if (CM108Device) + PORT->HIDDevice = CM108Device; + else + PORT->HIDDevice = _strdup ("MissingHID"); + + CM108Device = 0; + } + + if ( _stricmp(PORT->IOBASE, "CM108") == 0) // HID Addr instead of Speed + { + DecodeCM108(Port, ptr); + } + + if (_stricmp(PORT->IOBASE, "REMOTE") == 0) // IP Addr/Port + DecodeRemote(PORT, ptr); + else + PORT->SPEED = atoi(ptr); + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr == NULL) return (FALSE); + + if (_memicmp(ptr, "PTTCOM", 6) == 0 || _memicmp(ptr, "PTT=", 4) == 0) + { + if (_stricmp(ptr, "PTT=CM108") == 0) + { + // PTT on CM108 GPIO + + ptr = strtok_s(NULL, " \t\n\r", &Context); + if (ptr == NULL) return (FALSE); + + DecodeCM108(Port, ptr); + } + else + { + strcpy(PORT->PTTIOBASE, ptr); + } + ptr = strtok_s(NULL, " \t\n\r", &Context); + if (ptr == NULL) return (FALSE); + + } + + // if (strcmp(ptr, "ICOM") == 0 || strcmp(ptr, "YAESU") == 0 + // || strcmp(ptr, "KENWOOD") == 0 || strcmp(ptr, "PTTONLY") == 0 || strcmp(ptr, "ANTENNA") == 0) + + // RADIO IC706 4E 5 14.103/U1 14.112/u1 18.1/U1 10.12/l1 + // Read RADIO Lines + + _strupr(ptr); + + + if (strcmp(ptr, "ICOM") == 0) + PORT->PortType = ICOM; + else if (strcmp(ptr, "YAESU") == 0) + PORT->PortType = YAESU; + else if (strcmp(ptr, "KENWOOD") == 0) + PORT->PortType = KENWOOD; + else if (strcmp(ptr, "FLEX") == 0) + PORT->PortType = FLEX; + else if (strcmp(ptr, "NMEA") == 0) + PORT->PortType = NMEA; + else if (strcmp(ptr, "PTTONLY") == 0) + PORT->PortType = PTT; + else if (strcmp(ptr, "ANTENNA") == 0) + PORT->PortType = ANT; + else + return FALSE; + + Debugprintf("Port type = %d", PORT->PortType); + + _strupr(Context); + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr && memcmp(ptr, "HAMLIB=", 7) == 0) + { + // HAMLIB Emulator - param is port to listen on + + if (PORT->PortType == PTT) + { + RIG = &PORT->Rigs[PORT->ConfiguredRigs++]; + strcpy(RIG->RigName, PTTRigName); + + RIG->HAMLIBPORT = atoi(&ptr[7]); + ptr = strtok_s(NULL, " \t\n\r", &Context); + + } + } + + if (ptr && strcmp(ptr, "PTTMUX") == 0) + { + if (PORT->PortType == PTT) + { + RIG = &PORT->Rigs[PORT->ConfiguredRigs++]; + strcpy(RIG->RigName, PTTRigName); + goto domux; // PTTONLY with PTTMUX + } + } + + + if (ptr == NULL) + { + if (PORT->PortType == PTT) + ptr = PTTRigName; + else + return FALSE; + } + + if (strlen(ptr) > 9) return FALSE; + + RigName = ptr; + + Debugprintf("Rigname = *%s*", RigName); + + // FT100 seems to be different to most other YAESU types + + if (strcmp(RigName, "FT100") == 0 && PORT->PortType == YAESU) + { + PORT->PortType = FT100; + } + + // FT990 seems to be different to most other YAESU types + + if (strcmp(RigName, "FT990") == 0 && PORT->PortType == YAESU) + { + PORT->PortType = FT990; + } + + // FT1000 seems to be different to most other YAESU types + + if (strstr(RigName, "FT1000") && PORT->PortType == YAESU) + { + PORT->PortType = FT1000; + + // Subtypes need different code. D and no suffix are same + + if (strstr(RigName, "FT1000MP")) + PORT->YaesuVariant = FT1000MP; + else + PORT->YaesuVariant = FT1000D; + } + + // FT2000 seems to be different to most other YAESU types + + if (strcmp(RigName, "FT2000") == 0 && PORT->PortType == YAESU) + { + PORT->PortType = FT2000; + } + + // FT991A seems to be different to most other YAESU types + + if (strcmp(RigName, "FT991A") == 0 && PORT->PortType == YAESU) + { + PORT->PortType = FT991A; + } + + + // If ICOM, we may be adding a new Rig + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (PORT->PortType == ICOM || PORT->PortType == NMEA) + { + if (ptr == NULL) return (FALSE); + sscanf(ptr, "%x", &RigAddr); + + // See if already defined + + for (i = 0; i < PORT->ConfiguredRigs; i++) + { + RIG = &PORT->Rigs[i]; + + if (RIG->RigAddr == RigAddr) + goto RigFound; + } + + // Allocate a new one + + RIG = &PORT->Rigs[PORT->ConfiguredRigs++]; + RIG->RigAddr = RigAddr; + +RigFound: + + ptr = strtok_s(NULL, " \t\n\r", &Context); + // if (ptr == NULL) return (FALSE); + } + else + { + // Only allows one RIG + + PORT->ConfiguredRigs = 1; + RIG = &PORT->Rigs[0]; + } + + RIG->RIG_DEBUG = RIG_DEBUG; + RIG->PORT = PORT; + + strcpy(RIG->RigName, RigName); + + RIG->TSMenu = 63; // Default to TS590S + + // IC735 uses shorter freq message + + if (strcmp(RigName, "IC735") == 0 && PORT->PortType == ICOM) + RIG->IC735 = TRUE; + + // IC-F8101 uses a different set of commands + + if (strstr(RigName, "F8101") && PORT->PortType == ICOM) + RIG->ICF8101 = TRUE; + + if (PORT->PortType == KENWOOD) + { + RIG->TSMenu = 63; // Default to TS590S + + if (strcmp(RigName, "TS590SG") == 0) + RIG->TSMenu = 69; + } + + if (PORT->PortType == FT991A) + RIG->TSMenu = 72; //Menu for Data/USB siwtching + +domux: + + RIG->CM108Device = CM108Device; + +CheckOtherParams: + + while (ptr) + { + if (strcmp(ptr, "PTTMUX") == 0) + { + // Ports whose RTS/DTR will be converted to CAT commands (for IC7100/IC7200 etc) + + int PIndex = 0; + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + while (memcmp(ptr, "COM", 3) == 0) + { + char * tncport = strlop(ptr, '/'); + + strcpy(RIG->PTTCATPort[PIndex], &ptr[3]); + + if (tncport) + RIG->PTTCATTNC[PIndex] = TNCInfo[atoi(tncport)]; + + if (PIndex < 3) + PIndex++; + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr == NULL) + break; + } + if (ptr == NULL) + break; + else + continue; + } + else if (strcmp(ptr, "PTT_SETS_INPUT") == 0) + { + // Send Select Soundcard as mod source with PTT commands + + PTTControlsInputMUX = TRUE; + + // See if following param is an PTT Off Mode + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr == NULL) + break; + + if (strcmp(ptr, "MIC") == 0) + DataPTTOffMode = 0; + else if (strcmp(ptr, "AUX") == 0) + DataPTTOffMode = 1; + else if (strcmp(ptr, "MICAUX") == 0) + DataPTTOffMode = 2; + else if (RIG->ICF8101 && strcmp(ptr, "LSB") == 0) + ICF8101Mode = 0x7; + else if (RIG->ICF8101 && strcmp(ptr, "USB") == 0) + ICF8101Mode = 0x8; + else if (RIG->ICF8101 && strcmp(ptr, "LSBD1") == 0) + ICF8101Mode = 0x18; + else if (RIG->ICF8101 && strcmp(ptr, "USBD1") == 0) + ICF8101Mode = 0x19; + else if (RIG->ICF8101 && strcmp(ptr, "LSBD2") == 0) + ICF8101Mode = 0x20; + else if (RIG->ICF8101 && strcmp(ptr, "USBD2") == 0) + ICF8101Mode = 0x21; + else if (RIG->ICF8101 && strcmp(ptr, "LSBD3") == 0) + ICF8101Mode = 0x22; + else if (RIG->ICF8101 && strcmp(ptr, "USBD3") == 0) + ICF8101Mode = 0x23; + else + continue; + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + continue; + + } + else if (strcmp(ptr, "PTT_SETS_FREQ") == 0) + { + // Send Select Soundcard as mod source with PTT commands + + RIG->PTTSetsFreq = TRUE; + ptr = strtok_s(NULL, " \t\n\r", &Context); + + continue; + + } + + else if (strcmp(ptr, "DATAPTT") == 0) + { + // Send Select Soundcard as mod source with PTT commands + + DataPTT = TRUE; + } + + else if (memcmp(ptr, "PTTONHEX=", 9) == 0) + { + // Hex String to use for PTT on + + char * ptr1 = ptr; + char * ptr2 = onString ; + int i, j, len; + + ptr1 += 9; + onLen = len = strlen(ptr1) / 2; + + if (len < 240) + { + while ((len--) > 0) + { + i = *(ptr1++); + i -= '0'; + if (i > 9) + i -= 7; + + j = i << 4; + + i = *(ptr1++); + i -= '0'; + if (i > 9) + i -= 7; + + *(ptr2++) = j | i; + } + } + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr == NULL) + break; + } + + else if (memcmp(ptr, "PTTOFFHEX=", 10) == 0) + { + // Hex String to use for PTT off + + char * ptr2 = offString ; + int i, j, len; + + ptr += 10; + offLen = len = strlen(ptr) / 2; + + if (len < 240) + { + while ((len--) > 0) + { + i = *(ptr++); + i -= '0'; + if (i > 9) + i -= 7; + + j = i << 4; + + i = *(ptr++); + i -= '0'; + if (i > 9) + i -= 7; + + *(ptr2++) = j | i; + } + } + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr == NULL) + break; + + } + + else if (memcmp(ptr, "HAMLIB=", 7) == 0) + { + // HAMLIB Emulator - param is port to listen on + + RIG->HAMLIBPORT = atoi(&ptr[7]); + } + + else if (memcmp(ptr, "TXOFFSET", 8) == 0) + { + RIG->txOffset = strtoll(&ptr[9], NULL, 10); + } + + else if (memcmp(ptr, "RXOFFSET", 8) == 0) + { + RIG->rxOffset = strtoll(&ptr[9], NULL, 10); + } + + else if (memcmp(ptr, "PTTOFFSET", 9) == 0) + { + RIG->pttOffset = strtoll(&ptr[10], NULL, 10); + } + + else if (memcmp(ptr, "RXERROR", 7) == 0) + { + RIG->rxError = atoi(&ptr[8]); + } + + else if (memcmp(ptr, "TXERROR", 7) == 0) + { + RIG->txError = atoi(&ptr[8]); + } + + else if (memcmp(ptr, "REPORTFREQS", 11) == 0) + { + RIG->reportFreqs = _strdup(&ptr[12]); + } + + else if (memcmp(ptr, "DEFAULTFREQ", 11) == 0) + { + RIG->defaultFreq = atoi(&ptr[12]); + } + + else if (atoi(ptr)) + break; // Not scan freq oe timeband, so see if another param + + ptr = strtok_s(NULL, " \t\n\r", &Context); + } + + if (PORT->PortType == PTT || PORT->PortType == ANT) + return RIG; + + // Set up PTT and Poll Strings + + if (PORT->PortType == ICOM) + { + char * Poll; + Poll = &RIG->PTTOn[0]; + + if (onLen && offLen) + { + memcpy(RIG->PTTOn, onString, onLen); + RIG->PTTOnLen = onLen; + memcpy(RIG->PTTOff, offString, offLen); + RIG->PTTOffLen = offLen; + } + + else if (RIG->ICF8101) + { + // Need to send ACC PTT command (1A 37 0002), not normal ICOM IC 00 + + if (PTTControlsInputMUX) + { + *(Poll++) = 0xFE; + *(Poll++) = 0xFE; + *(Poll++) = RIG->RigAddr; + *(Poll++) = 0xE0; + *(Poll++) = 0x1a; + *(Poll++) = 0x05; + *(Poll++) = ICF8101Mode; + *(Poll++) = 0x03; // USB Data Mode Source + *(Poll++) = 0x00; + *(Poll++) = 0x01; // Soundcard + *(Poll++) = 0xFD; + } + *(Poll++) = 0xFE; + *(Poll++) = 0xFE; + *(Poll++) = RIG->RigAddr; + *(Poll++) = 0xE0; + *(Poll++) = 0x1A; + *(Poll++) = 0x37; // Send/read the TX status + *(Poll++) = 0x00; + *(Poll++) = 0x02; // ACC PTT + *(Poll++) = 0xFD; + + RIG->PTTOnLen = (int)(Poll - &RIG->PTTOn[0]); + + Poll = &RIG->PTTOff[0]; + + *(Poll++) = 0xFE; + *(Poll++) = 0xFE; + *(Poll++) = RIG->RigAddr; + *(Poll++) = 0xE0; + *(Poll++) = 0x1A; + *(Poll++) = 0x37; // Send/read the TX status + *(Poll++) = 0x00; + *(Poll++) = 0x00; // RX + *(Poll++) = 0xFD; + + if (PTTControlsInputMUX) + { + *(Poll++) = 0xFE; + *(Poll++) = 0xFE; + *(Poll++) = RIG->RigAddr; + *(Poll++) = 0xE0; + *(Poll++) = 0x1a; + + *(Poll++) = 0x05; + *(Poll++) = ICF8101Mode; + *(Poll++) = 0x03; // USB Data Mode Source + *(Poll++) = 0x00; + *(Poll++) = 0x02; // ACC + *(Poll++) = 0xFD; + } + RIG->PTTOffLen = (int)(Poll - &RIG->PTTOff[0]); + } + else + { + if (PTTControlsInputMUX) + { + *(Poll++) = 0xFE; + *(Poll++) = 0xFE; + *(Poll++) = RIG->RigAddr; + *(Poll++) = 0xE0; + *(Poll++) = 0x1a; + + if (strcmp(RIG->RigName, "IC7100") == 0) + { + *(Poll++) = 0x05; + *(Poll++) = 0x00; + *(Poll++) = 0x91; // Data Mode Source + } + else if (strcmp(RIG->RigName, "IC7200") == 0) + { + *(Poll++) = 0x03; + *(Poll++) = 0x24; // Data Mode Source + } + else if (strcmp(RIG->RigName, "IC7300") == 0) + { + *(Poll++) = 0x05; + *(Poll++) = 0x00; + *(Poll++) = 0x67; // Data Mode Source + } + else if (strcmp(RIG->RigName, "IC7600") == 0) + { + *(Poll++) = 0x05; + *(Poll++) = 0x00; + *(Poll++) = 0x31; // Data1 Mode Source + } + else if (strcmp(RIG->RigName, "IC7610") == 0) + { + *(Poll++) = 0x05; + *(Poll++) = 0x00; + *(Poll++) = 0x92; // Data1 Mode Source + } + else if (strcmp(RIG->RigName, "IC7410") == 0) + { + *(Poll++) = 0x03; + *(Poll++) = 0x39; // Data Mode Source + } + + *(Poll++) = 0x03; // USB Soundcard + *(Poll++) = 0xFD; + } + + *(Poll++) = 0xFE; + *(Poll++) = 0xFE; + *(Poll++) = RIG->RigAddr; + *(Poll++) = 0xE0; + *(Poll++) = 0x1C; // RIG STATE + *(Poll++) = 0x00; // PTT + *(Poll++) = 1; // ON + *(Poll++) = 0xFD; + + RIG->PTTOnLen = (int)(Poll - &RIG->PTTOn[0]); + + Poll = &RIG->PTTOff[0]; + + *(Poll++) = 0xFE; + *(Poll++) = 0xFE; + *(Poll++) = RIG->RigAddr; + *(Poll++) = 0xE0; + *(Poll++) = 0x1C; // RIG STATE + *(Poll++) = 0x00; // PTT + *(Poll++) = 0; // OFF + *(Poll++) = 0xFD; + + if (PTTControlsInputMUX) + { + *(Poll++) = 0xFE; + *(Poll++) = 0xFE; + *(Poll++) = RIG->RigAddr; + *(Poll++) = 0xE0; + *(Poll++) = 0x1a; + + if (strcmp(RIG->RigName, "IC7100") == 0) + { + *(Poll++) = 0x05; + *(Poll++) = 0x00; + *(Poll++) = 0x91; // Data Mode Source + } + else if (strcmp(RIG->RigName, "IC7200") == 0) + { + *(Poll++) = 0x03; + *(Poll++) = 0x24; // Data Mode Source + } + else if (strcmp(RIG->RigName, "IC7300") == 0) + { + *(Poll++) = 0x05; + *(Poll++) = 0x00; + *(Poll++) = 0x67; // Data Mode Source + } + else if (strcmp(RIG->RigName, "IC7410") == 0) + { + *(Poll++) = 0x03; + *(Poll++) = 0x39; // Data Mode Source + } + else if (strcmp(RIG->RigName, "IC7600") == 0) + { + *(Poll++) = 0x05; + *(Poll++) = 0x00; + *(Poll++) = 0x31; // Data1 Mode Source + } + else if (strcmp(RIG->RigName, "IC7610") == 0) + { + *(Poll++) = 0x05; + *(Poll++) = 0x00; + *(Poll++) = 0x92; // Data1 Mode Source + } + + *(Poll++) = DataPTTOffMode; + *(Poll++) = 0xFD; + } + RIG->PTTOffLen = (int)(Poll - &RIG->PTTOff[0]); + } + } + else if (PORT->PortType == KENWOOD) + { + RIG->PollLen = 6; + strcpy(RIG->Poll, "FA;MD;"); + + if (PTTControlsInputMUX) + { + sprintf(RIG->PTTOn, "EX%03d00001;TX1;", RIG->TSMenu); // Select USB before PTT + sprintf(RIG->PTTOff, "RX;EX%03d00000;", RIG->TSMenu); // Select ACC after dropping PTT + } + else + { + strcpy(RIG->PTTOff, "RX;"); + + if (DataPTT) + strcpy(RIG->PTTOn, "TX1;"); + else + strcpy(RIG->PTTOn, "TX;"); + } + + RIG->PTTOnLen = (int)strlen(RIG->PTTOn); + RIG->PTTOffLen = (int)strlen(RIG->PTTOff); + + } + else if (PORT->PortType == FLEX) + { + RIG->PollLen = 10; + strcpy(RIG->Poll, "ZZFA;ZZMD;"); + + strcpy(RIG->PTTOn, "ZZTX1;"); + RIG->PTTOnLen = 6; + strcpy(RIG->PTTOff, "ZZTX0;"); + RIG->PTTOffLen = 6; + } + else if (PORT->PortType == FT2000) + { + RIG->PollLen = 6; + strcpy(RIG->Poll, "FA;MD;"); + + strcpy(RIG->PTTOn, "TX1;"); + RIG->PTTOnLen = 4; + strcpy(RIG->PTTOff, "TX0;"); + RIG->PTTOffLen = 4; + } + else if (PORT->PortType == FT991A) + { + RIG->PollLen = 7; + strcpy(RIG->Poll, "FA;MD0;"); + + if (PTTControlsInputMUX) + { + RIG->PTTOnLen = sprintf(RIG->PTTOn, "EX0721;TX1;"); // Select USB before PTT + RIG->PTTOffLen = sprintf(RIG->PTTOff, "TX0;EX0720;"); // Select DATA after dropping PTT + } + else + { + strcpy(RIG->PTTOn, "TX1;"); + RIG->PTTOnLen = 4; + strcpy(RIG->PTTOff, "TX0;"); + RIG->PTTOffLen = 4; + } + } + else if (PORT->PortType == NMEA) + { + int Len; + + i = sprintf(RIG->Poll, "$PICOA,90,%02x,RXF*xx\r\n", RIG->RigAddr); + AddNMEAChecksum(RIG->Poll); + Len = i; + i = sprintf(RIG->Poll + Len, "$PICOA,90,%02x,MODE*xx\r\n", RIG->RigAddr); + AddNMEAChecksum(RIG->Poll + Len); + RIG->PollLen = Len + i; + + i = sprintf(RIG->PTTOn, "$PICOA,90,%02x,TRX,TX*xx\r\n", RIG->RigAddr); + AddNMEAChecksum(RIG->PTTOn); + RIG->PTTOnLen = i; + + i = sprintf(RIG->PTTOff, "$PICOA,90,%02x,TRX,RX*xx\r\n", RIG->RigAddr); + AddNMEAChecksum(RIG->PTTOff); + RIG->PTTOffLen = i; + } + + if (ptr == NULL) return RIG; // No Scanning, just Interactive control + + if (strchr(ptr, ',') == 0 && strchr(ptr, ':') == 0) // Old Format + { + ScanFreq = atof(ptr); + +#pragma warning(push) +#pragma warning(disable : 4244) + + RIG->ScanFreq = ScanFreq * 10; + +#pragma warning(push) + + ptr = strtok_s(NULL, " \t\n\r", &Context); + } + + // Frequency List + +CheckScan: + + if (ptr) + if (ptr[0] == ';' || ptr[0] == '#') + ptr = NULL; + + if (ptr != NULL) + { + // Create Default Timeband + + struct TimeScan * Band; + + RIG->TimeBands = zalloc(sizeof(void *)); + + Band = AllocateTimeRec(RIG); + SaveBand = Band; + + Band->Start = 0; + Band->End = 84540; //23:59 + FreqPtr = Band->Scanlist = RIG->FreqPtr = malloc(1000); + memset(FreqPtr, 0, 1000); + } + + while(ptr) + { + int ModeNo; + BOOL Supress; + double Freq = 0.0; + int FreqInt = 0; + char FreqString[80]=""; + char * Modeptr = NULL; + char Split, Data, PacketMode, RPacketMode, PMinLevel, PMaxLevel, Filter; + char Mode[10] = ""; + char WinmorMode, Antenna; + char ARDOPMode[6] = ""; + char VARAMode[6] = ""; + char Appl[13]; + char * ApplCall; + int BandWidth; + int Power; + + if (ptr[0] == ';' || ptr[0] == '#') + break; + + Filter = PMinLevel = PMaxLevel = PacketMode = RPacketMode = Split = + Data = WinmorMode = Antenna = ModeNo = Supress = + MemoryBank = MemoryNumber = BandWidth = 0; + + Appl[0] = 0; + ARDOPMode[0] = 0; + VARAMode[0] = 0; + Dwell = 0.0; + + while (strchr(ptr, ':')) + { + // New TimeBand + + struct TimeScan * Band; + + Band = AllocateTimeRec(RIG); + + *FreqPtr = (struct ScanEntry *)0; // Terminate Last Band + + Band->Start = (atoi(ptr) * 3600) + (atoi(&ptr[3]) * 60); + Band->End = 84540; //23:59 + SaveBand->End = Band->Start - 60; + + SaveBand = Band; + + FreqPtr = Band->Scanlist = RIG->FreqPtr = malloc(1000); + memset(FreqPtr, 0, 1000); + + ptr = strtok_s(NULL, " \t\n\r", &Context); + } + + if (strchr(ptr, ',')) // New Format + { + DecodeModePtr(ptr, &Dwell, &Freq, Mode, &PMinLevel, &PMaxLevel, &PacketMode, + &RPacketMode, &Split, &Data, &WinmorMode, &Antenna, &Supress, &Filter, &Appl[0], + &MemoryBank, &MemoryNumber, ARDOPMode, VARAMode, &BandWidth, &Power); + } + else + { + Modeptr = strchr(ptr, '/'); + + if (Modeptr) + *Modeptr++ = 0; + + Freq = atof(ptr); + + if (Modeptr) + { + // Mode can include 1/2/3 for Icom Filers. W/N for Winmor/Pactor Bandwidth, and +/-/S for Repeater Shift (S = Simplex) + // First is always Mode + // First char is Mode (USB, LSB etc) + + Mode[0] = Modeptr[0]; + Filter = Modeptr[1]; + + if (strchr(&Modeptr[1], '+')) + Split = '+'; + else if (strchr(&Modeptr[1], '-')) + Split = '-'; + else if (strchr(&Modeptr[1], 'S')) + Split = 'S'; + else if (strchr(&Modeptr[1], 'D')) + Data = 1; + + if (strchr(&Modeptr[1], 'W')) + { + WinmorMode = 'W'; + PMaxLevel = '3'; + PMinLevel = '1'; + } + else if (strchr(&Modeptr[1], 'N')) + { + WinmorMode = 'N'; + PMaxLevel = '2'; + PMinLevel = '1'; + } + + if (strchr(&Modeptr[1], 'R')) // Robust Packet + RPacketMode = '2'; // R600 + else if (strchr(&Modeptr[1], 'H')) // HF Packet on Tracker + PacketMode = '1'; // 300 + + if (strchr(&Modeptr[1], 'X')) // Dont Report to WL2K + Supress = 1; + + if (strstr(&Modeptr[1], "A1")) + Antenna = '1'; + else if (strstr(&Modeptr[1], "A2")) + Antenna = '2'; + else if (strstr(&Modeptr[1], "A3")) + Antenna = '3'; + else if (strstr(&Modeptr[1], "A4")) + Antenna = '4'; + else if (strstr(&Modeptr[1], "A5")) + Antenna = '5'; + else if (strstr(&Modeptr[1], "A6")) + Antenna = '6'; + } + } + + switch(PORT->PortType) + { + case ICOM: + + for (ModeNo = 0; ModeNo < 24; ModeNo++) + { + if (strlen(Mode) == 1) + { + if (Modes[ModeNo][0] == Mode[0]) + break; + } + else + { + if (_stricmp(Modes[ModeNo], Mode) == 0) + break; + } + } + break; + + case YAESU: + + for (ModeNo = 0; ModeNo < 16; ModeNo++) + { + if (strlen(Mode) == 1) + { + if (YaesuModes[ModeNo][0] == Mode[0]) + break; + } + else + { + if (_stricmp(YaesuModes[ModeNo], Mode) == 0) + break; + } + } + break; + + case KENWOOD: + + for (ModeNo = 0; ModeNo < 8; ModeNo++) + { + if (strlen(Mode) == 1) + { + if (KenwoodModes[ModeNo][0] == Mode[0]) + break; + } + else + { + if (_stricmp(KenwoodModes[ModeNo], Mode) == 0) + break; + } + } + break; + + case FLEX: + + for (ModeNo = 0; ModeNo < 12; ModeNo++) + { + if (strlen(Mode) == 1) + { + if (FLEXModes[ModeNo][0] == Mode[0]) + break; + } + else + { + if (_stricmp(FLEXModes[ModeNo], Mode) == 0) + break; + } + } + break; + + case FT2000: + + if (Modeptr) + { + if (strstr(Modeptr, "PL")) + { + ModeNo = 8; + break; + } + if (strstr(Modeptr, "PU")) + { + ModeNo = 12; + break; + } + } + for (ModeNo = 0; ModeNo < 14; ModeNo++) + { + if (strlen(Mode) == 1) + { + if (FT2000Modes[ModeNo][0] == Mode[0]) + break; + } + else + { + if (_stricmp(FT2000Modes[ModeNo], Mode) == 0) + break; + } + } + break; + + case FT991A: + +/* if (Modeptr) + { + if (strstr(Modeptr, "PL")) + { + ModeNo = 8; + break; + } + if (strstr(Modeptr, "PU")) + { + ModeNo = 12; + break; + } + } +*/ + for (ModeNo = 0; ModeNo < 15; ModeNo++) + { + if (strlen(Mode) == 1) + { + if (FT991AModes[ModeNo][0] == Mode[0]) + break; + } + else + { + if (_stricmp(FT991AModes[ModeNo], Mode) == 0) + break; + } + } + break; + + + case FT100: + + for (ModeNo = 0; ModeNo < 8; ModeNo++) + { + if (strlen(Mode) == 1) + { + if (FT100Modes[ModeNo][0] == Mode[0]) + break; + } + else + { + if (_stricmp(FT100Modes[ModeNo], Mode) == 0) + break; + } + } + break; + + case FT990: + + for (ModeNo = 0; ModeNo < 12; ModeNo++) + { + if (strlen(Mode) == 1) + { + if (FT990Modes[ModeNo][0] == Mode[0]) + break; + } + else + { + if (_stricmp(FT990Modes[ModeNo], Mode) == 0) + break; + } + } + break; + + case FT1000: + + for (ModeNo = 0; ModeNo < 12; ModeNo++) + { + if (strlen(Mode) == 1) + { + if (FT1000Modes[ModeNo][0] == Mode[0]) + break; + } + else + { + if (_stricmp(FT1000Modes[ModeNo], Mode) == 0) + break; + } + } + break; + } + + Freq = Freq * 1000000.0; + + sprintf(FreqString, "%09.0f", Freq); + + FreqInt = Freq; + + FreqPtr[0] = malloc(sizeof(struct ScanEntry)); + memset(FreqPtr[0], 0, sizeof(struct ScanEntry)); + +#pragma warning(push) +#pragma warning(disable : 4244) + + if (Dwell == 0.0) + FreqPtr[0]->Dwell = ScanFreq * 10; + else + FreqPtr[0]->Dwell = Dwell * 10; + +#pragma warning(pop) + + FreqPtr[0]->Freq = Freq; + FreqPtr[0]->Bandwidth = WinmorMode; + FreqPtr[0]->RPacketMode = RPacketMode; + FreqPtr[0]->HFPacketMode = PacketMode; + FreqPtr[0]->PMaxLevel = PMaxLevel; + FreqPtr[0]->PMinLevel = PMinLevel; + FreqPtr[0]->Antenna = Antenna; + strcpy(FreqPtr[0]->ARDOPMode, ARDOPMode); + FreqPtr[0]->VARAMode = VARAMode[0]; + + strcpy(FreqPtr[0]->APPL, Appl); + + ApplCall = GetApplCallFromName(Appl); + + if (strcmp(Appl, "NODE") == 0) + { + memcpy(FreqPtr[0]->APPLCALL, TNC->NodeCall, 9); + strlop(FreqPtr[0]->APPLCALL, ' '); + } + else + { + if (ApplCall && ApplCall[0] > 32) + { + memcpy(FreqPtr[0]->APPLCALL, ApplCall, 9); + strlop(FreqPtr[0]->APPLCALL, ' '); + } + } + + CmdPtr = FreqPtr[0]->Cmd1 = malloc(100); + + if (PORT->PortType == ICOM) + { + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + + if (RIG->ICF8101) + { + // Set Freq is 1A 35 and set Mode 1A 36 + + + *(CmdPtr++) = 0x1A; + *(CmdPtr++) = 0x35; // Set frequency command + + // Need to convert two chars to bcd digit + + *(CmdPtr++) = (FreqString[8] - 48) | ((FreqString[7] - 48) << 4); + *(CmdPtr++) = (FreqString[6] - 48) | ((FreqString[5] - 48) << 4); + *(CmdPtr++) = (FreqString[4] - 48) | ((FreqString[3] - 48) << 4); + *(CmdPtr++) = (FreqString[2] - 48) | ((FreqString[1] - 48) << 4); + *(CmdPtr++) = (FreqString[0] - 48); + *(CmdPtr++) = 0xFD; + FreqPtr[0]->Cmd1Len = 12; + + CmdPtr = FreqPtr[0]->Cmd2 = malloc(10); + FreqPtr[0]->Cmd2Len = 9; + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x1A; + *(CmdPtr++) = 0x36; // Set mode command + *(CmdPtr++) = 0; + if (ModeNo > 10) + *(CmdPtr++) = ModeNo + 6; + else + *(CmdPtr++) = ModeNo; + + *(CmdPtr++) = 0xFD; + } + else + { + if (MemoryNumber) + { + // Set Memory Channel instead of Freq, Mode, etc + + char ChanString[5]; + + // Send Set Memory, then Channel + + *(CmdPtr++) = 0x08; + *(CmdPtr++) = 0xFD; + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + + sprintf(ChanString, "%04d", MemoryNumber); + + *(CmdPtr++) = 0x08; + *(CmdPtr++) = (ChanString[1] - 48) | ((ChanString[0] - 48) << 4); + *(CmdPtr++) = (ChanString[3] - 48) | ((ChanString[2] - 48) << 4); + *(CmdPtr++) = 0xFD; + + FreqPtr[0]->Cmd1Len = 14; + + if (MemoryBank) + { + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x08; + *(CmdPtr++) = 0xA0; + *(CmdPtr++) = MemoryBank - 0x40; + *(CmdPtr++) = 0xFD; + + FreqPtr[0]->Cmd1Len += 8; + } + } + else + { + *(CmdPtr++) = 0x5; // Set frequency command + + // Need to convert two chars to bcd digit + + *(CmdPtr++) = (FreqString[8] - 48) | ((FreqString[7] - 48) << 4); + *(CmdPtr++) = (FreqString[6] - 48) | ((FreqString[5] - 48) << 4); + *(CmdPtr++) = (FreqString[4] - 48) | ((FreqString[3] - 48) << 4); + *(CmdPtr++) = (FreqString[2] - 48) | ((FreqString[1] - 48) << 4); + if (RIG->IC735) + { + *(CmdPtr++) = 0xFD; + FreqPtr[0]->Cmd1Len = 10; + } + else + { + *(CmdPtr++) = (FreqString[0] - 48); + *(CmdPtr++) = 0xFD; + FreqPtr[0]->Cmd1Len = 11; + } + + // Send Set VFO in case last chan was memory + + // *(CmdPtr++) = 0xFE; + // *(CmdPtr++) = 0xFE; + // *(CmdPtr++) = RIG->RigAddr; + // *(CmdPtr++) = 0xE0; + + // *(CmdPtr++) = 0x07; + // *(CmdPtr++) = 0xFD; + + // FreqPtr[0]->Cmd1Len = 17; + + if (Filter) + { + CmdPtr = FreqPtr[0]->Cmd2 = malloc(10); + FreqPtr[0]->Cmd2Len = 8; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x6; // Set Mode + *(CmdPtr++) = ModeNo; + *(CmdPtr++) = Filter - '0'; //Filter + *(CmdPtr++) = 0xFD; + + if (Split) + { + CmdPtr = FreqPtr[0]->Cmd3 = malloc(10); + FreqPtr[0]->Cmd3Len = 7; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0xF; // Set Mode + if (Split == 'S') + *(CmdPtr++) = 0x10; + else + if (Split == '+') + *(CmdPtr++) = 0x12; + else + if (Split == '-') + *(CmdPtr++) = 0x11; + + *(CmdPtr++) = 0xFD; + } + else + { + if (Data) + { + CmdPtr = FreqPtr[0]->Cmd3 = malloc(10); + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x1a; + + + if ((strcmp(RIG->RigName, "IC7100") == 0) || + (strcmp(RIG->RigName, "IC7410") == 0) || + (strcmp(RIG->RigName, "IC7600") == 0) || + (strcmp(RIG->RigName, "IC7610") == 0) || + (strcmp(RIG->RigName, "IC7300") == 0)) + { + FreqPtr[0]->Cmd3Len = 9; + *(CmdPtr++) = 0x6; // Send/read DATA mode with filter set + *(CmdPtr++) = 0x1; // Data On + *(CmdPtr++) = Filter - '0'; //Filter + } + else if (strcmp(RIG->RigName, "IC7200") == 0) + { + FreqPtr[0]->Cmd3Len = 9; + *(CmdPtr++) = 0x4; // Send/read DATA mode with filter set + *(CmdPtr++) = 0x1; // Data On + *(CmdPtr++) = Filter - '0'; //Filter + } + else + { + FreqPtr[0]->Cmd3Len = 8; + *(CmdPtr++) = 0x6; // Set Data + *(CmdPtr++) = 0x1; //On + } + + *(CmdPtr++) = 0xFD; + } + } + } + + if (Antenna == '5' || Antenna == '6') + { + // Antenna select for 746 and maybe others + + // Could be going in cmd2 3 or 4 + + if (FreqPtr[0]->Cmd2 == NULL) + { + CmdPtr = FreqPtr[0]->Cmd2 = malloc(10); + FreqPtr[0]->Cmd2Len = 7; + } + else if (FreqPtr[0]->Cmd3 == NULL) + { + CmdPtr = FreqPtr[0]->Cmd3 = malloc(10); + FreqPtr[0]->Cmd3Len = 7; + } + else + { + CmdPtr = FreqPtr[0]->Cmd4 = malloc(10); + FreqPtr[0]->Cmd4Len = 7; + } + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x12; // Set Antenna + *(CmdPtr++) = Antenna - '5'; // 0 for A5 1 for A6 + *(CmdPtr++) = 0xFD; + } + } + } + } + else if (PORT->PortType == YAESU) + { + //Send Mode first - changing mode can change freq + + *(CmdPtr++) = ModeNo; + *(CmdPtr++) = 0; + *(CmdPtr++) = 0; + *(CmdPtr++) = 0; + *(CmdPtr++) = 7; + + *(CmdPtr++) = (FreqString[1] - 48) | ((FreqString[0] - 48) << 4); + *(CmdPtr++) = (FreqString[3] - 48) | ((FreqString[2] - 48) << 4); + *(CmdPtr++) = (FreqString[5] - 48) | ((FreqString[4] - 48) << 4); + *(CmdPtr++) = (FreqString[7] - 48) | ((FreqString[6] - 48) << 4); + *(CmdPtr++) = 1; + + // FT847 Needs a Poll Here. Set up anyway, but only send if 847 + + *(CmdPtr++) = 0; + *(CmdPtr++) = 0; + *(CmdPtr++) = 0; + *(CmdPtr++) = 0; + *(CmdPtr++) = 3; + + + } + else if (PORT->PortType == KENWOOD) + { + if (Power == 0) + { + if (Antenna == '5' || Antenna == '6') + FreqPtr[0]->Cmd1Len = sprintf(CmdPtr, "FA00%s;MD%d;AN%c;FA;MD;", FreqString, ModeNo, Antenna - 4); + else + FreqPtr[0]->Cmd1Len = sprintf(CmdPtr, "FA00%s;MD%d;FA;MD;", FreqString, ModeNo); + } + else + { + if (Antenna == '5' || Antenna == '6') + FreqPtr[0]->Cmd1Len = sprintf(CmdPtr, "FA00%s;MD%d;AN%c;PC%03d;FA;MD;PC;", FreqString, ModeNo, Antenna - 4, Power); + else + FreqPtr[0]->Cmd1Len = sprintf(CmdPtr, "FA00%s;MD%d;PC%03d;FA;MD;PC;", FreqString, ModeNo, Power); + } + } + else if (PORT->PortType == FLEX) + { + FreqPtr[0]->Cmd1Len = sprintf(CmdPtr, "ZZFA00%s;ZZMD%02d;ZZFA;ZZMD;", FreqString, ModeNo); + } + else if (PORT->PortType == FT2000) + { + FreqPtr[0]->Cmd1Len = sprintf(CmdPtr, "FA%s;MD0%X;FA;MD;", &FreqString[1], ModeNo); + } + else if (PORT->PortType == FT991A) + { + FreqPtr[0]->Cmd1Len = sprintf(CmdPtr, "FA%s;MD0%X;FA;MD0;", &FreqString, ModeNo); + } + else if (PORT->PortType == FT100 || PORT->PortType == FT990 + || PORT->PortType == FT1000) + { + // Allow Mode = "LEAVE" to suppress mode change + + //Send Mode first - changing mode can change freq + + if (strcmp(Mode, "LEAVE") == 0) + { + // we can't easily change the string length, + // so just set freq twice + + *(CmdPtr++) = (FreqString[7] - 48) | ((FreqString[6] - 48) << 4); + *(CmdPtr++) = (FreqString[5] - 48) | ((FreqString[4] - 48) << 4); + *(CmdPtr++) = (FreqString[3] - 48) | ((FreqString[2] - 48) << 4); + *(CmdPtr++) = (FreqString[1] - 48) | ((FreqString[0] - 48) << 4); + *(CmdPtr++) = 10; + } + else + { + *(CmdPtr++) = 0; + *(CmdPtr++) = 0; + *(CmdPtr++) = 0; + *(CmdPtr++) = ModeNo; + *(CmdPtr++) = 12; + } + + *(CmdPtr++) = (FreqString[7] - 48) | ((FreqString[6] - 48) << 4); + *(CmdPtr++) = (FreqString[5] - 48) | ((FreqString[4] - 48) << 4); + *(CmdPtr++) = (FreqString[3] - 48) | ((FreqString[2] - 48) << 4); + *(CmdPtr++) = (FreqString[1] - 48) | ((FreqString[0] - 48) << 4); + *(CmdPtr++) = 10; + + // Send Get Status, as these types doesn't ack commands + + *(CmdPtr++) = 0; + *(CmdPtr++) = 0; + *(CmdPtr++) = 0; + + if (PORT->PortType == FT990 || PORT->YaesuVariant == FT1000D) + *(CmdPtr++) = 3; + else + *(CmdPtr++) = 2; // F100 or FT1000MP + + *(CmdPtr++) = 16; // Get Status + } + else if (PORT->PortType == NMEA) + { + int Len; + + i = sprintf(CmdPtr, "$PICOA,90,%02x,RXF,%.6f*xx\r\n", RIG->RigAddr, Freq/1000000.); + AddNMEAChecksum(CmdPtr); + Len = i; + i = sprintf(CmdPtr + Len, "$PICOA,90,%02x,TXF,%.6f*xx\r\n", RIG->RigAddr, Freq/1000000.); + AddNMEAChecksum(CmdPtr + Len); + Len += i; + i = sprintf(CmdPtr + Len, "$PICOA,90,%02x,MODE,%s*xx\r\n", RIG->RigAddr, Mode); + AddNMEAChecksum(CmdPtr + Len); + FreqPtr[0]->Cmd1Len = Len + i; + + i = sprintf(RIG->Poll, "$PICOA,90,%02x,RXF*xx\r\n", RIG->RigAddr); + AddNMEAChecksum(RIG->Poll); + Len = i; + i = sprintf(RIG->Poll + Len, "$PICOA,90,%02x,MODE*xx\r\n", RIG->RigAddr); + AddNMEAChecksum(RIG->Poll + Len); + RIG->PollLen = Len + i; + } + else if (PORT->PortType == HAMLIB) + { + FreqPtr[0]->Cmd1Len = sprintf(CmdPtr, "F %s\n+f\nM %s %d\n+m\n", FreqString, Mode, BandWidth); + } + + else if (PORT->PortType == FLRIG) + { + sprintf(FreqPtr[0]->Cmd1Msg, "%.0f", Freq); + sprintf(FreqPtr[0]->Cmd2Msg, "%s", Mode); + sprintf(FreqPtr[0]->Cmd3Msg, "%d", BandWidth); + } + + else if (PORT->PortType == RTLUDP) + { + int FM = 0; + int AM = 1; + int USB = 2; + int LSB = 3; + + CmdPtr[0] = 0; + memcpy(&CmdPtr[1], &FreqInt, 4); + + CmdPtr[1] = FreqInt & 0xff; + CmdPtr[2] = (FreqInt >> 8) & 0xff; + CmdPtr[3] = (FreqInt >> 16) & 0xff; + CmdPtr[4] = (FreqInt >> 24) & 0xff; + + FreqPtr[0]->Cmd1Len = 5; + + if (Mode[0]) + { + CmdPtr[5] = 1; + FreqPtr[0]->Cmd1Len = 10; + + if (strcmp(Mode, "FM") == 0) + memcpy(&CmdPtr[6], &FM, 4); + else if (strcmp(Mode, "AM") == 0) + memcpy(&CmdPtr[6], &AM, 4); + else if (strcmp(Mode, "USB") == 0) + memcpy(&CmdPtr[6], &USB, 4); + else if (strcmp(Mode, "LSB") == 0) + memcpy(&CmdPtr[6], &LSB, 4); + } + + } + + FreqPtr++; + + RIG->ScanCounter = 20; + + ptr = strtok_s(NULL, " \t\n\r", &Context); // Next Freq + } + + + + if (RIG->NumberofBands) + { + CheckTimeBands(RIG); // Set initial FreqPtr; + PORT->FreqPtr = RIG->FreqPtr[0]; + } + + return RIG; +} + +VOID SetupScanInterLockGroups(struct RIGINFO *RIG) +{ + struct PORTCONTROL * PortRecord; + struct TNCINFO * TNC; + int port; + int Interlock = RIG->Interlock; + char PortString[128] = ""; + char TxPortString[128] = ""; + + // Find TNC ports in this Rig's scan group + + for (port = 1; port < 33; port++) + { + TNC = TNCInfo[port]; + + if (TNC == NULL) + continue; + + PortRecord = &TNC->PortRecord->PORTCONTROL; + + if (TNC->RXRadio == Interlock) + { + int p = PortRecord->PORTNUMBER; + RIG->BPQPort |= (1 << p); + sprintf(PortString, "%s,%d", PortString, p); + TNC->RIG = RIG; + + if (RIG->PTTMode == 0 && TNC->PTTMode) + RIG->PTTMode = TNC->PTTMode; + } + if (TNC->TXRadio == Interlock && TNC->TXRadio != TNC->RXRadio) + { + int p = PortRecord->PORTNUMBER; + RIG->BPQPort |= (1 << p); + sprintf(TxPortString, "%s,%d", TxPortString, p); + TNC->TXRIG = RIG; + + if (RIG->PTTMode == 0 && TNC->PTTMode) + RIG->PTTMode = TNC->PTTMode; + } + } + + if (RIG->PTTMode == 0 && (RIG->PTTCATPort[0] || RIG->HAMLIBPORT)) // PTT Mux Implies CAT + RIG->PTTMode = PTTCI_V; + + if (PortString[0] && TxPortString[0]) // Have both + sprintf(RIG->WEB_PORTS, "Rx: %s Tx: %s", &PortString[1], &TxPortString[1]); + else if (PortString[0]) + strcpy(RIG->WEB_PORTS, &PortString[1]); + else if (TxPortString[0]) + sprintf(RIG->WEB_PORTS, "Tx: %s", &TxPortString[1]); + + SetWindowText(RIG->hPORTS, RIG->WEB_PORTS); +} + +VOID SetupPortRIGPointers() +{ + struct TNCINFO * TNC; + int port; + + for (port = 1; port < 33; port++) + { + TNC = TNCInfo[port]; + + if (TNC == NULL) + continue; + + if (TNC->RIG == NULL) + TNC->RIG = &TNC->DummyRig; // Not using Rig control, so use Dummy + } +} + +#ifdef WIN32 + +VOID PTTCATThread(struct RIGINFO *RIG) +{ + DWORD dwLength = 0; + int Length, ret, i; + UCHAR * ptr1; + UCHAR * ptr2; + UCHAR c; + UCHAR Block[4][80]; + UCHAR CurrentState[4] = {0}; +#define RTS 2 +#define DTR 4 + HANDLE Event; + HANDLE Handle[4]; + DWORD EvtMask[4]; + OVERLAPPED Overlapped[4]; + char Port[32]; + int PIndex = 0; + int HIndex = 0; + int rc; + + EndPTTCATThread = FALSE; + + while (PIndex < 4 && RIG->PTTCATPort[PIndex][0]) + { + RIG->RealMux[HIndex] = 0; + + sprintf(Port, "\\\\.\\pipe\\BPQCOM%s", RIG->PTTCATPort[PIndex]); + + Handle[HIndex] = CreateFile(Port, GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); + + if (Handle[HIndex] == (HANDLE) -1) + { + int Err = GetLastError(); + Consoleprintf("PTTMUX port BPQCOM%s Open failed code %d - trying real com port", RIG->PTTCATPort[PIndex], Err); + + // See if real com port + + sprintf(Port, "\\\\.\\\\COM%s", RIG->PTTCATPort[PIndex]); + + Handle[HIndex] = CreateFile(Port, GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + + RIG->RealMux[HIndex] = 1; + + if (Handle[HIndex] == (HANDLE) -1) + { + int Err = GetLastError(); + Consoleprintf("PTTMUX port COM%s Open failed code %d", RIG->PTTCATPort[PIndex], Err); + } + else + { + rc = SetCommMask(Handle[HIndex], EV_CTS | EV_DSR); // Request notifications + HIndex++; + } + } + else + HIndex++; + + PIndex++; + + } + + if (PIndex == 0) + return; // No ports + + Event = CreateEvent(NULL, TRUE, FALSE, NULL); + + for (i = 0; i < HIndex; i ++) + { + memset(&Overlapped[i], 0, sizeof(OVERLAPPED)); + Overlapped[i].hEvent = Event; + + if (RIG->RealMux[i]) + { + // Request Interface change notifications + + rc = WaitCommEvent(Handle[i], &EvtMask[i], &Overlapped[i]); + rc = GetLastError(); + + } + else + { + + // Prime a read on each handle + + ReadFile(Handle[i], Block[i], 80, &Length, &Overlapped[i]); + } + } + + while (EndPTTCATThread == FALSE) + { + +WaitAgain: + + ret = WaitForSingleObject(Event, 1000); + + if (ret == WAIT_TIMEOUT) + { + if (EndPTTCATThread) + { + for (i = 0; i < HIndex; i ++) + { + CancelIo(Handle[i]); + CloseHandle(Handle[i]); + Handle[i] = INVALID_HANDLE_VALUE; + } + CloseHandle(Event); + return; + } + goto WaitAgain; + } + + ResetEvent(Event); + + // See which request(s) have completed + + for (i = 0; i < HIndex; i ++) + { + ret = GetOverlappedResult(Handle[i], &Overlapped[i], &Length, FALSE); + + if (ret) + { + if (RIG->RealMux[i]) + { + // Request Interface change notifications + + DWORD Mask; + + GetCommModemStatus(Handle[i], &Mask); + + if (Mask & MS_CTS_ON) + Rig_PTTEx(RIG, TRUE, RIG->PTTCATTNC[i]); + else + Rig_PTTEx(RIG, FALSE, RIG->PTTCATTNC[i]); + + memset(&Overlapped[i], 0, sizeof(OVERLAPPED)); + Overlapped[i].hEvent = Event; + WaitCommEvent(Handle[i], &EvtMask[i], &Overlapped[i]); + + } + else + { + + ptr1 = Block[i]; + ptr2 = Block[i]; + + while (Length > 0) + { + c = *(ptr1++); + + Length--; + + if (c == 0xff) + { + c = *(ptr1++); + Length--; + + if (c == 0xff) // ff ff means ff + { + Length--; + } + else + { + // This is connection / RTS/DTR statua from other end + // Convert to CAT Command + + if (c == CurrentState[i]) + continue; + + if (c & RTS) + Rig_PTTEx(RIG, TRUE, RIG->PTTCATTNC[i]); + else + Rig_PTTEx(RIG, FALSE, RIG->PTTCATTNC[i]); + + CurrentState[i] = c; + continue; + } + } + } + + memset(&Overlapped[i], 0, sizeof(OVERLAPPED)); + Overlapped[i].hEvent = Event; + + ReadFile(Handle[i], Block[i], 80, &Length, &Overlapped[i]); + } + } + } + } + EndPTTCATThread = FALSE; +} + +/* + memset(&Overlapped, 0, sizeof(Overlapped)); + Overlapped.hEvent = Event; + ResetEvent(Event); + + ret = ReadFile(Handle, Block, 80, &Length, &Overlapped); + + if (ret == 0) + { + ret = GetLastError(); + + if (ret != ERROR_IO_PENDING) + { + if (ret == ERROR_BROKEN_PIPE || ret == ERROR_INVALID_HANDLE) + { + CloseHandle(Handle); + RIG->PTTCATHandles[0] = INVALID_HANDLE_VALUE; + return; + } + } + } +*/ +#endif + +// HAMLIB Support Code + +VOID HAMLIBPoll(struct RIGPORTINFO * PORT) +{ + UCHAR * Poll = PORT->TXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on HAMLIB + char cmd[80]; + int len; + + if (RIG->ScanStopped == 0) + if (RIG->ScanCounter) + RIG->ScanCounter--; + + if (PORT->Timeout) + { + PORT->Timeout--; + + if (PORT->Timeout) // Still waiting + return; + + SetWindowText(RIG->hFREQ, "------------------"); + SetWindowText(RIG->hMODE, "----------"); + strcpy(RIG->WEB_FREQ, "-----------");; + strcpy(RIG->WEB_MODE, "------"); + + RIG->RIGOK = FALSE; + return; + } + + // Send Data if avail, else send poll + + if (RIG->NumberofBands && RIG->RIGOK && (RIG->ScanStopped == 0)) + { + if (RIG->ScanCounter <= 0) + { + // Send Next Freq + + if (GetPermissionToChange(PORT, RIG)) + { + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Change Freq to %9.4f", PORT->FreqPtr->Freq); + + memcpy(PORT->TXBuffer, PORT->FreqPtr->Cmd1, PORT->FreqPtr->Cmd1Len); + PORT->TXLen = PORT->FreqPtr->Cmd1Len; + + _gcvt(PORT->FreqPtr->Freq / 1000000.0, 9, RIG->Valchar); // For MH + + send(PORT->remoteSock, PORT->TXBuffer, PORT->TXLen, 0); + PORT->CmdSent = 1; + PORT->Retries = 0; + PORT->Timeout = 0; + PORT->AutoPoll = TRUE; + + // There isn't a response to a set command, so clear Scan Lock here + + ReleasePermission(RIG); // Release Perrmission + return; + } + } + } + + if (RIG->PollCounter) + { + RIG->PollCounter--; + if (RIG->PollCounter > 1) + return; + } + + if (RIG->RIGOK && (RIG->ScanStopped == 0) && RIG->NumberofBands) + return; // no point in reading freq if we are about to change it + + RIG->PollCounter = 10; // Once Per Sec + + // Read Frequency + + len = sprintf(cmd, "+f\n+m\n"); + + send(PORT->remoteSock, cmd, len, 0); + + PORT->Timeout = 10; + PORT->CmdSent = 0; + + PORT->AutoPoll = TRUE; + + return; +} + + +void HAMLIBProcessMessage(struct RIGPORTINFO * PORT) +{ + // Called from Background thread + + int InputLen = recv(PORT->remoteSock, &PORT->RXBuffer[PORT->RXLen], 500 - PORT->RXLen, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + if (PORT->remoteSock) + closesocket(PORT->remoteSock); + + PORT->remoteSock = 0; + + PORT->CONNECTED = FALSE; + PORT->hDevice = 0; + return; + } + + PORT->RXLen += InputLen; +} + +void FLRIGProcessMessage(struct RIGPORTINFO * PORT) +{ + // Called from Background thread + + int InputLen = recv(PORT->remoteSock, &PORT->RXBuffer[PORT->RXLen], 500 - PORT->RXLen, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + if (PORT->remoteSock) + closesocket(PORT->remoteSock); + + PORT->remoteSock = 0; + + PORT->CONNECTED = FALSE; + PORT->hDevice = 0; + return; + } + + PORT->RXLen += InputLen; + ProcessFLRIGFrame(PORT); +} + +void ProcessHAMLIBFrame(struct RIGPORTINFO * PORT, int Length) +{ + char * msg = PORT->RXBuffer; + char * rest; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on Yaseu + msg[Length] = 0; + + PORT->Timeout = 0; + RIG->RIGOK = 1; + + // extract lines from input + + while (msg && msg[0]) + { + rest = strlop(msg, 10); + + if (memcmp(msg, "Frequency:", 10) == 0) + { + RIG->RigFreq = atof(&msg[11]) / 1000000.0; + + _gcvt(RIG->RigFreq, 9, RIG->Valchar); + + sprintf(RIG->WEB_FREQ,"%s", RIG->Valchar); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + } + + else if (memcmp(msg, "Mode:", 5) == 0) + { + if (strlen(&msg[6]) < 15) + strcpy(RIG->ModeString, &msg[6]); + } + + else if (memcmp(msg, "Passband:", 9) == 0) + { + RIG->Passband = atoi(&msg[10]); + sprintf(RIG->WEB_MODE, "%s/%d", RIG->ModeString, RIG->Passband); + SetWindowText(RIG->hMODE, RIG->WEB_MODE); + } + + msg = rest; + } +} + + +void ProcessFLRIGFrame(struct RIGPORTINFO * PORT) +{ + char * msg; + int Length; + + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one + char * ptr1, * ptr2, * val; + int Len, TotalLen; + char cmd[80]; + char ReqBuf[256]; + char SendBuff[256]; + + while (PORT->RXLen > 0) + { + int b1 = 0, b2 = 0; + + msg = PORT->RXBuffer; + Length = PORT->RXLen; + + msg[Length] = 0; + ptr1 = strstr(msg, "Content-length:"); + + if (ptr1 == NULL) + return; + + Len = atoi(&ptr1[15]); + ptr2 = strstr(ptr1, "\r\n\r\n"); + if (ptr2) + { + TotalLen = ptr2 +4 + Len - msg; + + if (TotalLen > Length) // Don't have it all + return; + } + else + return; + + val = strstr(ptr2, ""); + + if (val) + { + val += 7; + + RIG->RIGOK = 1; + PORT->RXLen -= TotalLen; + + memmove(PORT->RXBuffer, &PORT->RXBuffer[TotalLen], PORT->RXLen); + + // It is quite difficult to corrolate responses with commands, but we only poll for freq, mode and bandwidth + // and the responses can be easily identified + + if (strstr(val, "") || memcmp(val, "", 8) == 0) + { + // Reply to set command - may need to send next in set or use to send OK if interactive + + if (PORT->ScanEntry.Cmd2Msg[0]) + { + sprintf(cmd, "%s", PORT->ScanEntry.Cmd2Msg); + FLRIGSendCommand(PORT, "rig.set_mode", cmd); + PORT->ScanEntry.Cmd2Msg[0] = 0; + } + + else if (PORT->ScanEntry.Cmd3Msg[0] && strcmp(PORT->ScanEntry.Cmd3Msg, "0") != 0) + { + sprintf(cmd, "%s", PORT->ScanEntry.Cmd3Msg); + FLRIGSendCommand(PORT, "rig.set_bandwidth", cmd); + PORT->ScanEntry.Cmd3Msg[0] = 0; + } + + else if (!PORT->AutoPoll) + { + GetSemaphore(&Semaphore, 61); + SendResponse(RIG->Session, "Set OK"); + FreeSemaphore(&Semaphore); + PORT->AutoPoll = 1; // So we dond send another + } + } + else if(strstr(val, "")) + { + // Reply to get BW + + char * p1, * p2; + + p1 = strstr(val, ""); + + if (p1) + { + p1 += 7; + b1 = atoi(p1); + + p2 = strstr(p1, ""); + + if (p2) + { + p2 +=7; + b2 = atoi(p2); + } + } + + if (b1) + { + if (b2) + sprintf(RIG->WEB_MODE, "%s/%d:%d", RIG->ModeString, b1, b2); + else + sprintf(RIG->WEB_MODE, "%s/%d", RIG->ModeString, b1); + + MySetWindowText(RIG->hMODE, RIG->WEB_MODE); + } + } + else + { + // Either freq or mode. See if numeric + + double freq; + + strlop(val, '<'); + + freq = atof(val) / 1000000.0; + + if (freq > 0.0) + { + RIG->RigFreq = freq; + _gcvt(RIG->RigFreq, 9, RIG->Valchar); + + sprintf(RIG->WEB_FREQ,"%s", RIG->Valchar); + MySetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + + // Read Mode + + Len = sprintf(ReqBuf, Req, "rig.get_mode", ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + send(PORT->remoteSock, SendBuff, Len, 0); + + } + else + { + if (strlen(val)> 1) + { + strcpy(RIG->ModeString, val); + + if (b1) + { + if (b2) + sprintf(RIG->WEB_MODE, "%s/%d:%d", RIG->ModeString, b1, b2); + else + sprintf(RIG->WEB_MODE, "%s/%d", RIG->ModeString, b1); + } + else + SetWindowText(RIG->hMODE, RIG->WEB_MODE); + + MySetWindowText(RIG->hMODE, RIG->WEB_MODE); + + Len = sprintf(ReqBuf, Req, "rig.get_bw", ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + + send(PORT->remoteSock, SendBuff, Len, 0); + } + } + } + + + + // We now send all setting commands at once + + /* + + + if (memcmp(PORT->TXBuffer, "rig.set_vfo", 11) == 0) + { + // Set Freq - Send Set Mode if needed + + if (PORT->ScanEntry.Cmd2Msg[0]) + { + sprintf(cmd, "%s", PORT->ScanEntry.Cmd2Msg); + FLRIGSendCommand(PORT, "rig.set_mode", cmd); + if (strcmp(PORT->ScanEntry.Cmd3Msg, "0") != 0) + { + sprintf(cmd, "%s", PORT->ScanEntry.Cmd3Msg); + FLRIGSendCommand(PORT, "rig.set_bandwidth", cmd); + } + strcpy(RIG->ModeString, PORT->ScanEntry.Cmd2Msg); + } + else + { + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Set Freq OK"); + + strcpy(PORT->TXBuffer, "rig.get_vfo"); + Len = sprintf(ReqBuf, Req, "rig.get_vfo", ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + strcpy(PORT->TXBuffer, "rig.get_bw"); + Len = sprintf(ReqBuf, Req, "rig.get_bw", ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + + send(PORT->remoteSock, SendBuff, Len, 0); + } + continue; + } + + if (memcmp(PORT->TXBuffer, "rig.set_mode", 11) == 0) + { + strcpy(PORT->TXBuffer, "rig.get_vfo"); + Len = sprintf(ReqBuf, Req, "rig.get_vfo", ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + + send(PORT->remoteSock, SendBuff, Len, 0); + + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Set Freq and Mode OK"); + + continue; + } + + if (memcmp(PORT->TXBuffer, "rig.get_vfo", 11) == 0) + { + RIG->RigFreq = atof(val) / 1000000.0; + + _gcvt(RIG->RigFreq, 9, RIG->Valchar); + + sprintf(RIG->WEB_FREQ,"%s", RIG->Valchar); + MySetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + + strcpy(PORT->TXBuffer, "rig.get_mode"); + Len = sprintf(ReqBuf, Req, "rig.get_mode", ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + send(PORT->remoteSock, SendBuff, Len, 0); + + Len = sprintf(ReqBuf, Req, "rig.get_bw", ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + send(PORT->remoteSock, SendBuff, Len, 0); + continue; + } + + if (memcmp(PORT->TXBuffer, "rig.get_mode", 11) == 0) + { + strlop(val, '<'); + sprintf(RIG->WEB_MODE, "%s", val); + MySetWindowText(RIG->hMODE, RIG->WEB_MODE); + } + } + + */ + } + + PORT->Timeout = 0; + } + + /* + POST /RPC2 HTTP/1.1 + User-Agent: XMLRPC++ 0.8 + Host: 127.0.0.1:12345 + Content-type: text/xml + Content-length: 89 + + + rig.get_vfoA + + HTTP/1.1 200 OK + Server: XMLRPC++ 0.8 + Content-Type: text/xml + Content-length: 118 + + + + 14070000 + + */ + + +} + + + + + +void HLSetMode(SOCKET Sock, struct RIGINFO * RIG, unsigned char * Msg, char sep) +{ + char Resp[80]; + int Len; + char mode[80] = ""; + int filter = 0; + int n = sscanf(&Msg[1], "%s %d", mode, &filter); + + // Send to RIGCommand. Can't set Mode without Freq so need to use current + + // ?? Should be try to convert bandwidth to filter ?? + + RIG->Passband = filter; + + if (RIG->PORT->PortType == ICOM) // Needs a Filter + sprintf(Resp, "%d %s %s 1\n", 0, RIG->Valchar, mode); + else + sprintf(Resp, "%d %s %s\n", 0, RIG->Valchar, mode); + + GetSemaphore(&Semaphore, 60); + Rig_CommandEx(RIG->PORT, RIG, -1, Resp); + FreeSemaphore(&Semaphore); + + if (sep) + Len = sprintf(Resp, "set_mode: %s %d%cRPRT 0\n", mode, filter, sep); + else + Len = sprintf(Resp, "RPRT 0\n"); + + send(Sock, Resp, Len, 0); +} + + +void HLSetFreq(SOCKET Sock, struct RIGINFO * RIG, unsigned char * Msg, char sep) +{ + char Resp[80]; + int Len; + int freq = atoi(&Msg[1]); + + // Send to RIGCommand + + sprintf(Resp, "%d %f\n", 0, freq/1000000.0); + GetSemaphore(&Semaphore, 60); + Rig_CommandEx(RIG->PORT, RIG, -1, Resp); + FreeSemaphore(&Semaphore); + + if (sep) + Len = sprintf(Resp, "set_freq: %d%cRPRT 0\n", freq, sep); + else + Len = sprintf(Resp, "RPRT 0\n"); + + send(Sock, Resp, Len, 0); +} + + +void HLGetPTT(SOCKET Sock, struct RIGINFO * RIG, char sep) +{ + char Resp[80]; + int Len; + int ptt = 0; + + if (RIG->PTTTimer) + ptt = 1; + + if (sep) + Len = sprintf(Resp, "get_ptt:%cPTT: %d%cRPRT 0\n", sep, ptt, sep); + else + Len = sprintf(Resp, "%d\n", ptt); + + send(Sock, Resp, Len, 0); +} + +void HLSetPTT(SOCKET Sock, struct RIGINFO * RIG, unsigned char * Msg, char sep) +{ + char Resp[80]; + int Len; + int ptt = atoi(&Msg[1]); + + if (ptt) + Rig_PTTEx(RIG, 1, NULL); + else + Rig_PTTEx(RIG, 0, NULL); + + if (sep) + Len = sprintf(Resp, "set_ptt: %d%cRPRT 0\n", ptt, sep); + else + Len = sprintf(Resp, "RPRT 0\n"); + + send(Sock, Resp, Len, 0); +} + +void HLGetMode(SOCKET Sock, struct RIGINFO * RIG, char sep) +{ + char Resp[80]; + int Len; + + if (sep) + Len = sprintf(Resp, "get_mode:%cMode: %s%cPassband: %d%cRPRT 0\n", sep, RIG->ModeString, sep, RIG->Passband, sep); + else + Len = sprintf(Resp, "%s\n%d\n", RIG->ModeString, RIG->Passband); + + send(Sock, Resp, Len, 0); + +} + +void HLGetFreq(SOCKET Sock, struct RIGINFO * RIG, char sep) +{ + char Resp[80]; + int Len; + char freqval[64]; + double freq = atof(RIG->Valchar) * 1000000.0; + + sprintf(freqval, "%f", freq); + strlop(freqval, '.'); + + if (sep) + Len = sprintf(Resp, "get_freq:%cFrequency: %s%cRPRT 0\n", sep, freqval, sep); + else + Len = sprintf(Resp, "%s\n", freqval); + + send(Sock, Resp, Len, 0); +} + +void HLGetVFO(SOCKET Sock, struct RIGINFO * RIG, char sep) +{ + char Resp[80]; + int Len; + + if (sep) + Len = sprintf(Resp, "get_vfo:%s%cRPRT 0\n", "VFOA", sep); + else + Len = sprintf(Resp, "%s\n", "VFOA"); + + send(Sock, Resp, Len, 0); +} + +void HLGetSplit(SOCKET Sock, struct RIGINFO * RIG, char sep) +{ + char Resp[80]; + int Len; + + if (sep) + Len = sprintf(Resp, "get_vfo:%s%cRPRT 0\n", "VFOA", sep); + else + Len = sprintf(Resp, "0\n%s\n", "VFOA"); + + send(Sock, Resp, Len, 0); + +} + + + +int ProcessHAMLIBSlaveMessage(SOCKET Sock, struct RIGINFO * RIG, unsigned char * Msg, int MsgLen) +{ + // We only process a pretty small subset of rigctl messages + + // commands are generally a single character, upper case for set + // and lower case for get. If preceeded by + ; | or , response will + // be in extended form. + adds an LF between field, other values the + // supplied char is used as seperator. + + // At the moments we support freq (F) mode (m) and PTT (T) + + char sep = 0; + + if (Msg[0] == '#') + return 0; // Comment + + strlop(Msg, 13); + strlop(Msg, 10); + + // \ on front is used for long mode. Hopefully not used much + + // WSJT seems ro need \chk_vfo and \dump_state + + if (Msg[0] == '\\') + { + if (strcmp(&Msg[1], "chk_vfo") == 0) + { + char Reply[80]; + int Len = sprintf(Reply, "CHKVFO 0\n"); + send(Sock, Reply, Len, 0); + return 0; + } + + if (strcmp(&Msg[1], "dump_state") == 0) + { + char Reply[4096]; + int Len = sprintf(Reply, + "0\n" + "1\n" + "2\n" + "150000.000000 1500000000.000000 0x1ff -1 -1 0x10000003 0x3\n" + "0 0 0 0 0 0 0\n" + "0 0 0 0 0 0 0\n" + "0x1ff 1\n" + "0x1ff 0\n" + "0 0\n" + "0x1e 2400\n" + "0x2 500\n" + "0x1 8000\n" + "0x1 2400\n" + "0x20 15000\n" + "0x20 8000\n" + "0x40 230000\n" + "0 0\n" + "9990\n" + "9990\n" + "10000\n" + "0\n" + "10 \n" + "10 20 30 \n" + "0xffffffff\n" + "0xffffffff\n" + "0xf7ffffff\n" + "0x83ffffff\n" + "0xffffffff\n" + "0xffffffbf\n"); + + send(Sock, Reply, Len, 0); + return 0; + } + } + + if (Msg[0] == 'q' || Msg[0] == 'Q') + { + // close connection + + return 1; + } + + if (Msg[0] == '+') + { + sep = 10; + Msg++; + MsgLen --; + } + else if (Msg[0] == '_' || Msg[0] == '?') + { + } + else if (ispunct(Msg[0])) + { + sep = Msg[0]; + Msg++; + MsgLen --; + } + + switch (Msg[0]) + { + case 'f': // Get Freqency + + HLGetFreq(Sock, RIG, sep); + return 0; + + case 'm': // Get Mode + + HLGetMode(Sock, RIG, sep); + return 0; + + case 't': // Get PTT + + HLGetPTT(Sock, RIG, sep); + return 0; + + case 'v': // Get VFO + + HLGetVFO(Sock, RIG, sep); + return 0; + + case 's': // Get Split + + HLGetSplit(Sock, RIG, sep); + return 0; + + case 'F': + + HLSetFreq(Sock, RIG, Msg, sep); + return 0; + + case 'M': + + HLSetMode(Sock, RIG, Msg, sep); + return 0; + + case 'T': + + HLSetPTT(Sock, RIG, Msg, sep); + return 0; + } + return 0; +} + +int DecodeHAMLIBAddr(struct RIGPORTINFO * PORT, char * ptr) +{ + // Param is IPADDR:PORT. Only Allow numeric addresses + + struct sockaddr_in * destaddr = (SOCKADDR_IN *)&PORT->remoteDest; + + char * port = strlop(ptr, ':'); + + if (port == NULL) + return 0; + + destaddr->sin_family = AF_INET; + destaddr->sin_addr.s_addr = inet_addr(ptr); + destaddr->sin_port = htons(atoi(port)); + + return 1; +} + +VOID HAMLIBThread(struct RIGPORTINFO * PORT); + +VOID ConnecttoHAMLIB(struct RIGPORTINFO * PORT) +{ + if (HAMLIBMasterRunning) + _beginthread(HAMLIBThread, 0, (void *)PORT); + + return ; +} + +VOID HAMLIBThread(struct RIGPORTINFO * PORT) +{ + // Opens sockets and looks for data + + char Msg[255]; + int err, i, ret; + u_long param=1; + BOOL bcopt=TRUE; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + + if (PORT->remoteSock) + { + closesocket(PORT->remoteSock); + } + + PORT->remoteSock = 0; + PORT->remoteSock = socket(AF_INET,SOCK_STREAM,0); + + if (PORT->remoteSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for HAMLIB socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + PORT->CONNECTING = FALSE; + return; + } + + setsockopt(PORT->remoteSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + setsockopt(PORT->remoteSock, IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + if (connect(PORT->remoteSock,(LPSOCKADDR) &PORT->remoteDest,sizeof(PORT->remoteDest)) == 0) + { + // + // Connected successful + // + + ioctl(PORT->remoteSock, FIONBIO, ¶m); + } + else + { + if (PORT->Alerted == FALSE) + { + struct sockaddr_in * destaddr = (SOCKADDR_IN * )&PORT->remoteDest; + + err = WSAGetLastError(); + + sprintf(Msg, "Connect Failed for HAMLIB socket - error code = %d Addr %s\r\n", err, PORT->IOBASE); + + WritetoConsole(Msg); + PORT->Alerted = TRUE; + } + + closesocket(PORT->remoteSock); + + PORT->remoteSock = 0; + PORT->CONNECTING = FALSE; + return; + } + + PORT->CONNECTED = TRUE; + PORT->hDevice = (HANDLE)1; // simplifies check code + + PORT->Alerted = TRUE; + + while (PORT->CONNECTED) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(PORT->remoteSock,&readfs); + FD_SET(PORT->remoteSock,&errorfs); + + timeout.tv_sec = 5; + timeout.tv_usec = 0; + + ret = select((int)PORT->remoteSock + 1, &readfs, NULL, &errorfs, &timeout); + + if (HAMLIBMasterRunning == 0) + return; + + if (ret == SOCKET_ERROR) + { + Debugprintf("HAMLIB Select failed %d ", WSAGetLastError()); + goto Lost; + } + + if (ret > 0) + { + // See what happened + + if (FD_ISSET(PORT->remoteSock, &readfs)) + { + HAMLIBProcessMessage(PORT); + } + + if (FD_ISSET(PORT->remoteSock, &errorfs)) + { +Lost: + sprintf(Msg, "HAMLIB Connection lost for Addr %s\r\n", PORT->IOBASE); + WritetoConsole(Msg); + + PORT->CONNECTED = FALSE; + PORT->Alerted = FALSE; + PORT->hDevice = 0; // simplifies check code + + closesocket(PORT->remoteSock); + PORT->remoteSock = 0; + return; + } + + + continue; + } + else + { + } + } + sprintf(Msg, "HAMLIB Thread Terminated Addr %s\r\n", PORT->IOBASE); + WritetoConsole(Msg); +} + + + +void HAMLIBSlaveThread(struct RIGINFO * RIG) +{ + // Wait for connections and messages from HAMLIB Clients + + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + int ret; + unsigned int maxsock; + + HAMLIBSlaveRunning = 1; + + Consoleprintf("HAMLIB Slave Thread %d Running", RIG->HAMLIBPORT); + + while (HAMLIBSlaveRunning) + { + struct HAMLIBSOCK * Entry = RIG->Sockets; + struct HAMLIBSOCK * Prev; + + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(RIG->ListenSocket, &readfs); + FD_SET(RIG->ListenSocket, &errorfs); + + maxsock = RIG->ListenSocket; + + while (Entry && HAMLIBSlaveRunning) + { + FD_SET(Entry->Sock, &readfs); + FD_SET(Entry->Sock, &errorfs); + + if (Entry->Sock > maxsock) + maxsock = Entry->Sock; + + Entry = Entry->Next; + } + + timeout.tv_sec = 5; + timeout.tv_usec = 0; + + ret = select(maxsock + 1, &readfs, NULL, &errorfs, &timeout); + + if (HAMLIBSlaveRunning == 0) + return; + + if (ret == -1) + { + perror("listen select"); + continue; + } + + if (ret) + { + if (FD_ISSET(RIG->ListenSocket, &readfs)) + { + // Connection. Accept it and create a socket enty + + int addrlen = sizeof(struct sockaddr_in6); + struct sockaddr_in6 sin6; + struct HAMLIBSOCK * Entry = zalloc(sizeof(struct HAMLIBSOCK)); + + Entry->Sock = accept(RIG->ListenSocket, (struct sockaddr *)&sin6, &addrlen); + + if (RIG->Sockets) + Entry->Next = RIG->Sockets; + + RIG->Sockets = Entry; + } + + // See if any Data Sockets + + Entry = RIG->Sockets; + Prev = 0; + + while (Entry) + { + unsigned char MsgBuf[256]; + unsigned char * Msg = MsgBuf; + int MsgLen; + + if (FD_ISSET(Entry->Sock, &readfs)) + { + MsgLen = recv(Entry->Sock, Msg, 256, 0); + + if (MsgLen <= 0) + { + // Closed - close socket and remove from chain + + closesocket(Entry->Sock); + + if (Prev == 0) + RIG->Sockets = Entry->Next; + else + Prev->Next = Entry->Next; + + free (Entry); + break; + } + else + { + // Could have multiple messages in packet + // Terminator can be CR LF or CRLF + + char * ptr; + int Len; + + Msg[MsgLen] = 0; +Loop: + ptr = strlop(Msg, 10); + if (ptr == NULL) + strlop(Msg, 13); + + Len = strlen(Msg); + + if (ProcessHAMLIBSlaveMessage(Entry->Sock, RIG, Msg, Len) == 1) + { + // close request + + closesocket(Entry->Sock); + + if (Prev == 0) + RIG->Sockets = Entry->Next; + else + Prev->Next = Entry->Next; + + free (Entry); + break; + } + Msg = ptr; + + if (Msg) + { + while (Msg[0] == 10 || Msg[0] == 13) + Msg++; + + if (Msg[0]) + goto Loop; + } + } + } + + if (FD_ISSET(Entry->Sock, &errorfs)) + { + // Closed - close socket and remove from chai + + closesocket(Entry->Sock); + + if (Prev == 0) + RIG->Sockets = Entry->Next; + else + Prev->Next = Entry->Next; + + free (Entry); + break; + } + + // Check Next Client + + Prev = Entry; + Entry = Entry->Next; + } + } + } + Consoleprintf("HAMLIB Slave Thread %d Exited", RIG->HAMLIBPORT); +} + + +VOID FLRIGPoll(struct RIGPORTINFO * PORT) +{ + UCHAR * Poll = PORT->TXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; + int Len; + char ReqBuf[256]; + char SendBuff[256]; + + if (RIG->ScanStopped == 0) + if (RIG->ScanCounter) + RIG->ScanCounter--; + + if (PORT->Timeout) + { + PORT->Timeout--; + + if (PORT->Timeout) // Still waiting + return; + + SetWindowText(RIG->hFREQ, "------------------"); + SetWindowText(RIG->hMODE, "----------"); + strcpy(RIG->WEB_FREQ, "-----------");; + strcpy(RIG->WEB_MODE, "------"); + + RIG->RIGOK = FALSE; + return; + } + + // Send Data if avail, else send poll + + if (RIG->NumberofBands && RIG->RIGOK && (RIG->ScanStopped == 0)) + { + if (RIG->ScanCounter <= 0) + { + // Send Next Freq + + if (GetPermissionToChange(PORT, RIG)) + { + char cmd[80]; + double freq; + + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Change Freq to %9.4f", PORT->FreqPtr->Freq); + + _gcvt(PORT->FreqPtr->Freq / 1000000.0, 9, RIG->Valchar); // For MH + + // Send the Set Freq here, send set mode when we get a response + + memcpy(&PORT->ScanEntry, PORT->FreqPtr, sizeof(struct ScanEntry)); + + sprintf(cmd, "%s", PORT->FreqPtr->Cmd1Msg); + FLRIGSendCommand(PORT, "rig.set_vfo", cmd); + + // Update display as we don't get a response + + freq = atof(PORT->FreqPtr->Cmd1Msg) / 1000000.0; + + if (freq > 0.0) + { + RIG->RigFreq = freq; + _gcvt(RIG->RigFreq, 9, RIG->Valchar); + + sprintf(RIG->WEB_FREQ,"%s", RIG->Valchar); + MySetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + } + + + PORT->CmdSent = 1; + PORT->Retries = 0; + PORT->Timeout = 10; + PORT->AutoPoll = TRUE; + + // There isn't a response to a set command, so clear Scan Lock here + + ReleasePermission(RIG); // Release Perrmission + return; + } + } + } + + if (RIG->PollCounter) + { + RIG->PollCounter--; + if (RIG->PollCounter > 1) + return; + } + + if (RIG->RIGOK && (RIG->ScanStopped == 0) && RIG->NumberofBands) + return; // no point in reading freq if we are about to change it + + RIG->PollCounter = 10; // Once Per Sec + + // Read Frequency + + strcpy(Poll, "rig.get_vfo"); + + Len = sprintf(ReqBuf, Req, Poll, ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + + if (PORT->CONNECTED) + { + if (send(PORT->remoteSock, SendBuff, Len, 0) != Len) + { + if (PORT->remoteSock) + closesocket(PORT->remoteSock); + + PORT->remoteSock = 0; + PORT->CONNECTED = FALSE; + PORT->hDevice = 0; + return; + } + } + + PORT->Timeout = 10; + PORT->CmdSent = 0; + + PORT->AutoPoll = TRUE; + + return; +} + +VOID FLRIGSendCommand(struct RIGPORTINFO * PORT, char * Command, char * Value) +{ + int Len; + char ReqBuf[512]; + char SendBuff[512]; + char ValueString[256] =""; + + if (!PORT->CONNECTED) + return; + + sprintf(ValueString, "%s", Value); + + strcpy(PORT->TXBuffer, Command); + Len = sprintf(ReqBuf, Req, PORT->TXBuffer, ValueString); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + if (send(PORT->remoteSock, SendBuff, Len, 0) != Len) + { + if (PORT->remoteSock) + closesocket(PORT->remoteSock); + + PORT->remoteSock = 0; + PORT->CONNECTED = FALSE; + PORT->hDevice = 0; + } + + return; +} + + + +VOID FLRIGThread(struct RIGPORTINFO * PORT); + +VOID ConnecttoFLRIG(struct RIGPORTINFO * PORT) +{ + if (FLRIGRunning) + _beginthread(FLRIGThread, 0, (void *)PORT); + return ; +} + +VOID FLRIGThread(struct RIGPORTINFO * PORT) +{ + // Opens sockets and looks for data + + char Msg[255]; + int err, i, ret; + u_long param=1; + BOOL bcopt=TRUE; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + + if (PORT->remoteSock) + { + closesocket(PORT->remoteSock); + } + + PORT->remoteSock = 0; + PORT->remoteSock = socket(AF_INET,SOCK_STREAM,0); + + if (PORT->remoteSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for FLRIG socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + PORT->CONNECTING = FALSE; + return; + } + + setsockopt(PORT->remoteSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + setsockopt(PORT->remoteSock, IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + if (connect(PORT->remoteSock,(LPSOCKADDR) &PORT->remoteDest,sizeof(PORT->remoteDest)) == 0) + { + // + // Connected successful + // + + ioctl(PORT->remoteSock, FIONBIO, ¶m); + } + else + { + if (PORT->Alerted == FALSE) + { + struct sockaddr_in * destaddr = (SOCKADDR_IN * )&PORT->remoteDest; + + err = WSAGetLastError(); + + sprintf(Msg, "Connect Failed for FLRIG socket - error code = %d Port %d\r\n", + err, htons(destaddr->sin_port)); + + WritetoConsole(Msg); + PORT->Alerted = TRUE; + } + + closesocket(PORT->remoteSock); + + PORT->remoteSock = 0; + PORT->CONNECTING = FALSE; + return; + } + + PORT->CONNECTED = TRUE; + PORT->hDevice = (HANDLE)1; // simplifies check code + + PORT->Alerted = TRUE; + + while (PORT->CONNECTED && FLRIGRunning) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(PORT->remoteSock,&readfs); + FD_SET(PORT->remoteSock,&errorfs); + + timeout.tv_sec = 5; + timeout.tv_usec = 0; + + ret = select((int)PORT->remoteSock + 1, &readfs, NULL, &errorfs, &timeout); + + if (FLRIGRunning == 0) + return; + + if (ret == SOCKET_ERROR) + { + Debugprintf("FLRIG Select failed %d ", WSAGetLastError()); + goto Lost; + } + + if (ret > 0) + { + // See what happened + + if (FD_ISSET(PORT->remoteSock, &readfs)) + { + FLRIGProcessMessage(PORT); + } + + if (FD_ISSET(PORT->remoteSock, &errorfs)) + { +Lost: + sprintf(Msg, "FLRIG Connection lost for Port %s\r\n", PORT->IOBASE); + WritetoConsole(Msg); + + PORT->CONNECTED = FALSE; + PORT->Alerted = FALSE; + PORT->hDevice = 0; // simplifies check code + + closesocket(PORT->remoteSock); + PORT->remoteSock = 0; + return; + } + continue; + } + else + { + } + } + sprintf(Msg, "FLRIG Thread Terminated Port %s\r\n", PORT->IOBASE); + WritetoConsole(Msg); +} + + + + +// HID Support Code + +int HID_Read_Block(struct RIGPORTINFO * PORT) +{ + int Len; + unsigned char Msg[65] = ""; + + if (PORT->RXLen > 400) + PORT->RXLen = 0; + + // Don't try to read more than 64 + +#ifdef WIN32 + Len = rawhid_recv(0, Msg, 64, 100); +#else + Len = read(PORT->hDevice, Msg, 64); +#endif + + if (Len <= 0) + return 0; + + // First byte is actual length + + Len = Msg[0]; + + if (Len > 0) + { + if (Len < 64) // Max in HID Packet + { + memcpy(&PORT->RXBuffer[PORT->RXLen], Msg + 1, Len); + return Len; + } + } + return 0; +} + +void rawhid_close(int num); + +BOOL HID_Write_Block(struct RIGPORTINFO * PORT) +{ + int n = PORT->TXLen; + UCHAR * ptr = PORT->TXBuffer; + UCHAR Msg[64] = ""; + int ret, i; + + while (n) + { + i = n; + if (i > 63) + i = 63; + + Msg[0] = i; // Length on front + memcpy(&Msg[1], ptr, i); + ptr += i; + n -= i; + // n = hid_write(PORT->hDevice, PORT->TXBuffer, PORT->TXLen); +#ifdef WIN32 + ret = rawhid_send(0, Msg, 64, 100); // Always send 64 + + if (ret < 0) + { + Debugprintf("Rigcontrol HID Write Failed %d", errno); + rawhid_close(0); + PORT->hDevice = NULL; + return FALSE; + } +#else + ret = write(PORT->hDevice, Msg, 64); + + if (ret != 64) + { + printf ("Write to %s failed, n=%d, errno=%d\n", PORT->HIDDevice, ret, errno); + close (PORT->hDevice); + PORT->hDevice = 0; + return FALSE; + } + +// printf("HID Write %d\n", i); +#endif + } + return TRUE; +} + + +BOOL OpenHIDPort(struct RIGPORTINFO * PORT, VOID * Port, int Speed) +{ +#ifdef WIN32 + + if (PORT->HIDDevice== NULL) + return FALSE; + + PORT->hDevice = rawhid_open(PORT->HIDDevice); + + if (PORT->hDevice) + Debugprintf("Rigcontrol HID Device %s opened", PORT->HIDDevice); + + /* + handle = hid_open_path(PORT->HIDDevice); + + if (handle) + hid_set_nonblocking(handle, 1); + + PORT->hDevice = handle; + */ +#else + int fd; + unsigned int param = 1; + + if (PORT->HIDDevice== NULL) + return FALSE; + + fd = open (PORT->HIDDevice, O_RDWR); + + if (fd == -1) + { + printf ("Could not open %s, errno=%d\n", PORT->HIDDevice, errno); + return FALSE; + } + + ioctl(fd, FIONBIO, ¶m); + printf("Rigcontrol HID Device %s opened", PORT->HIDDevice); + + PORT->hDevice = fd; +#endif + if (PORT->hDevice == 0) + return (FALSE); + + return TRUE; +} + + +void CM108_set_ptt(struct RIGINFO *RIG, int PTTState) +{ + char io[5]; + hid_device *handle; + int n; + + io[0] = 0; + io[1] = 0; + io[2] = 1 << (3 - 1); + io[3] = PTTState << (3 - 1); + io[4] = 0; + + if (RIG->CM108Device == NULL) + return; + +#ifdef WIN32 + handle = hid_open_path(RIG->CM108Device); + + if (!handle) { + printf("unable to open device\n"); + return; + } + + n = hid_write(handle, io, 5); + if (n < 0) + { + Debugprintf("Unable to write()\n"); + Debugprintf("Error: %ls\n", hid_error(RIG->PORT->hDevice)); + } + + hid_close(handle); + +#else + + int fd; + + fd = open (RIG->CM108Device, O_WRONLY); + + if (fd == -1) + { + printf ("Could not open %s for write, errno=%d\n", RIG->CM108Device, errno); + return; + } + + io[0] = 0; + io[1] = 0; + io[2] = 1 << (3 - 1); + io[3] = PTTState << (3 - 1); + io[4] = 0; + + n = write (fd, io, 5); + if (n != 5) + { + printf ("Write to %s failed, n=%d, errno=%d\n", RIG->CM108Device, n, errno); + } + + close (fd); +#endif + return; + +} + +/* +int CRow; + +HANDLE hComPort, hSpeed, hRigType, hButton, hAddr, hLabel, hTimes, hFreqs, hBPQPort; + +VOID CreateRigConfigLine(HWND hDlg, struct RIGPORTINFO * PORT, struct RIGINFO * RIG) +{ + char Port[10]; + + hButton = CreateWindow(WC_BUTTON , "", WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON | WS_TABSTOP, + 10, CRow+5, 10,10, hDlg, NULL, hInstance, NULL); + + if (PORT->PortType == ICOM) + { + char Addr[10]; + + sprintf(Addr, "%X", RIG->RigAddr); + + hAddr = CreateWindow(WC_EDIT , Addr, WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER, + 305, CRow, 30,20, hDlg, NULL, hInstance, NULL); + + } + hLabel = CreateWindow(WC_EDIT , RIG->RigName, WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER, + 340, CRow, 60,20, hDlg, NULL, hInstance, NULL); + + sprintf(Port, "%d", RIG->PortRecord->PORTCONTROL.PORTNUMBER); + hBPQPort = CreateWindow(WC_EDIT , Port, WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER | ES_AUTOHSCROLL, + 405, CRow, 20, 20, hDlg, NULL, hInstance, NULL); + + hTimes = CreateWindow(WC_COMBOBOX , "", WS_CHILD | WS_VISIBLE | CBS_DROPDOWN | + WS_VSCROLL | WS_TABSTOP, + 430, CRow, 100,80, hDlg, NULL, hInstance, NULL); + + hFreqs = CreateWindow(WC_EDIT , RIG->FreqText, WS_CHILD | WS_VISIBLE| WS_TABSTOP | WS_BORDER | ES_AUTOHSCROLL, + 535, CRow, 300, 20, hDlg, NULL, hInstance, NULL); + + SendMessage(hTimes, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "0000:1159"); + SendMessage(hTimes, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "1200:2359"); + SendMessage(hTimes, CB_SETCURSEL, 0, 0); + + CRow += 30; + +} + +VOID CreatePortConfigLine(HWND hDlg, struct RIGPORTINFO * PORT) +{ + char Port[20]; + int i; + + hComPort = CreateWindow(WC_COMBOBOX , "", WS_CHILD | WS_VISIBLE | CBS_DROPDOWN | + WS_VSCROLL | WS_TABSTOP, + 30, CRow, 90, 160, hDlg, NULL, hInstance, NULL); + + for (i = 1; i < 256; i++) + { + sprintf(Port, "COM%d", i); + SendMessage(hComPort, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) Port); + } + + sprintf(Port, "COM%d", PORT->IOBASE); + + i = SendMessage(hComPort, CB_FINDSTRINGEXACT, 0,(LPARAM) Port); + + SendMessage(hComPort, CB_SETCURSEL, i, 0); + + + hSpeed = CreateWindow(WC_COMBOBOX , "", WS_CHILD | WS_VISIBLE | CBS_DROPDOWN | + WS_VSCROLL | WS_TABSTOP, + 120, CRow, 75, 80, hDlg, NULL, hInstance, NULL); + + SendMessage(hSpeed, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "1200"); + SendMessage(hSpeed, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "2400"); + SendMessage(hSpeed, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "4800"); + SendMessage(hSpeed, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "9600"); + SendMessage(hSpeed, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "19200"); + SendMessage(hSpeed, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "38400"); + + sprintf(Port, "%d", PORT->SPEED); + + i = SendMessage(hSpeed, CB_FINDSTRINGEXACT, 0, (LPARAM)Port); + + SendMessage(hSpeed, CB_SETCURSEL, i, 0); + + hRigType = CreateWindow(WC_COMBOBOX , "", WS_CHILD | WS_VISIBLE | CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP, + 200, CRow, 100,80, hDlg, NULL, hInstance, NULL); + + SendMessage(hRigType, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "ICOM"); + SendMessage(hRigType, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "YAESU"); + SendMessage(hRigType, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "KENWOOD"); + + SendMessage(hRigType, CB_SETCURSEL, PORT->PortType -1, 0); + +} + +INT_PTR CALLBACK ConfigDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + int Cmd = LOWORD(wParam); + + switch (message) + { + case WM_INITDIALOG: + { + struct RIGPORTINFO * PORT; + struct RIGINFO * RIG; + int i, p; + + CRow = 40; + + for (p = 0; p < NumberofPorts; p++) + { + PORT = PORTInfo[p]; + + CreatePortConfigLine(hDlg, PORT); + + for (i=0; i < PORT->ConfiguredRigs; i++) + { + RIG = &PORT->Rigs[i]; + CreateRigConfigLine(hDlg, PORT, RIG); + } + } + + + +// CreateWindow(WC_STATIC , "", WS_CHILD | WS_VISIBLE, +// 90, Row, 40,20, hDlg, NULL, hInstance, NULL); + +// CreateWindow(WC_STATIC , "", WS_CHILD | WS_VISIBLE, +// 135, Row, 100,20, hDlg, NULL, hInstance, NULL); + +return TRUE; + } + + case WM_SIZING: + { + return TRUE; + } + + case WM_ACTIVATE: + +// SendDlgItemMessage(hDlg, IDC_MESSAGE, EM_SETSEL, -1, 0); + + break; + + + case WM_COMMAND: + + if (Cmd == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + + return (INT_PTR)TRUE; + + break; + } + return (INT_PTR)FALSE; +} + +*/ + +#ifdef WIN32 + +/* Simple Raw HID functions for Windows - for use with Teensy RawHID example + * http://www.pjrc.com/teensy/rawhid.html + * Copyright (c) 2009 PJRC.COM, LLC + * + * rawhid_open - open 1 or more devices + * rawhid_recv - receive a packet + * rawhid_send - send a packet + * rawhid_close - close a device + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above description, website URL and copyright notice and this permission + * notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * Version 1.0: Initial Release + */ + +#include +#include +//#include +//#include +#include +//#include +//#include + +typedef USHORT USAGE; + + +typedef struct _HIDD_CONFIGURATION { + PVOID cookie; + ULONG size; + ULONG RingBufferSize; +} HIDD_CONFIGURATION, *PHIDD_CONFIGURATION; + +typedef struct _HIDD_ATTRIBUTES { + ULONG Size; + USHORT VendorID; + USHORT ProductID; + USHORT VersionNumber; +} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES; + + +typedef struct _HIDP_CAPS { + USAGE Usage; + USAGE UsagePage; + USHORT InputReportByteLength; + USHORT OutputReportByteLength; + USHORT FeatureReportByteLength; + USHORT Reserved[17]; + USHORT NumberLinkCollectionNodes; + USHORT NumberInputButtonCaps; + USHORT NumberInputValueCaps; + USHORT NumberInputDataIndices; + USHORT NumberOutputButtonCaps; + USHORT NumberOutputValueCaps; + USHORT NumberOutputDataIndices; + USHORT NumberFeatureButtonCaps; + USHORT NumberFeatureValueCaps; + USHORT NumberFeatureDataIndices; +} HIDP_CAPS, *PHIDP_CAPS; + + +typedef struct _HIDP_PREPARSED_DATA * PHIDP_PREPARSED_DATA; + + + +// a list of all opened HID devices, so the caller can +// simply refer to them by number +typedef struct hid_struct hid_t; +static hid_t *first_hid = NULL; +static hid_t *last_hid = NULL; +struct hid_struct { + HANDLE handle; + int open; + struct hid_struct *prev; + struct hid_struct *next; +}; +static HANDLE rx_event=NULL; +static HANDLE tx_event=NULL; +static CRITICAL_SECTION rx_mutex; +static CRITICAL_SECTION tx_mutex; + + +// private functions, not intended to be used from outside this file +static void add_hid(hid_t *h); +static hid_t * get_hid(int num); +static void free_all_hid(void); +void print_win32_err(void); + + + + +// rawhid_recv - receive a packet +// Inputs: +// num = device to receive from (zero based) +// buf = buffer to receive packet +// len = buffer's size +// timeout = time to wait, in milliseconds +// Output: +// number of bytes received, or -1 on error +// +int rawhid_recv(int num, void *buf, int len, int timeout) +{ + hid_t *hid; + unsigned char tmpbuf[516]; + OVERLAPPED ov; + DWORD r; + int n; + + if (sizeof(tmpbuf) < len + 1) return -1; + hid = get_hid(num); + if (!hid || !hid->open) return -1; + EnterCriticalSection(&rx_mutex); + ResetEvent(&rx_event); + memset(&ov, 0, sizeof(ov)); + ov.hEvent = rx_event; + if (!ReadFile(hid->handle, tmpbuf, len + 1, NULL, &ov)) { + if (GetLastError() != ERROR_IO_PENDING) goto return_error; + r = WaitForSingleObject(rx_event, timeout); + if (r == WAIT_TIMEOUT) goto return_timeout; + if (r != WAIT_OBJECT_0) goto return_error; + } + if (!GetOverlappedResult(hid->handle, &ov, &n, FALSE)) goto return_error; + LeaveCriticalSection(&rx_mutex); + if (n <= 0) return -1; + n--; + if (n > len) n = len; + memcpy(buf, tmpbuf + 1, n); + return n; +return_timeout: + CancelIo(hid->handle); + LeaveCriticalSection(&rx_mutex); + return 0; +return_error: + print_win32_err(); + LeaveCriticalSection(&rx_mutex); + return -1; +} + +// rawhid_send - send a packet +// Inputs: +// num = device to transmit to (zero based) +// buf = buffer containing packet to send +// len = number of bytes to transmit +// timeout = time to wait, in milliseconds +// Output: +// number of bytes sent, or -1 on error +// +int rawhid_send(int num, void *buf, int len, int timeout) +{ + hid_t *hid; + unsigned char tmpbuf[516]; + OVERLAPPED ov; + DWORD n, r; + + if (sizeof(tmpbuf) < len + 1) return -1; + hid = get_hid(num); + if (!hid || !hid->open) return -1; + EnterCriticalSection(&tx_mutex); + ResetEvent(&tx_event); + memset(&ov, 0, sizeof(ov)); + ov.hEvent = tx_event; + tmpbuf[0] = 0; + memcpy(tmpbuf + 1, buf, len); + if (!WriteFile(hid->handle, tmpbuf, len + 1, NULL, &ov)) { + if (GetLastError() != ERROR_IO_PENDING) goto return_error; + r = WaitForSingleObject(tx_event, timeout); + if (r == WAIT_TIMEOUT) goto return_timeout; + if (r != WAIT_OBJECT_0) goto return_error; + } + if (!GetOverlappedResult(hid->handle, &ov, &n, FALSE)) goto return_error; + LeaveCriticalSection(&tx_mutex); + if (n <= 0) return -1; + return n - 1; +return_timeout: + CancelIo(hid->handle); + LeaveCriticalSection(&tx_mutex); + return 0; +return_error: + print_win32_err(); + LeaveCriticalSection(&tx_mutex); + return -1; +} + +HANDLE rawhid_open(char * Device) +{ + DWORD index=0; + HANDLE h; + hid_t *hid; + int count=0; + + if (first_hid) free_all_hid(); + + if (!rx_event) + { + rx_event = CreateEvent(NULL, TRUE, TRUE, NULL); + tx_event = CreateEvent(NULL, TRUE, TRUE, NULL); + InitializeCriticalSection(&rx_mutex); + InitializeCriticalSection(&tx_mutex); + } + h = CreateFile(Device, GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + + if (h == INVALID_HANDLE_VALUE) + return 0; + + hid = (struct hid_struct *)malloc(sizeof(struct hid_struct)); + if (!hid) + { + CloseHandle(h); + return 0; + } + hid->handle = h; + hid->open = 1; + add_hid(hid); + + return h; +} + + +// rawhid_close - close a device +// +// Inputs: +// num = device to close (zero based) +// Output +// (nothing) +// +void rawhid_close(int num) +{ + hid_t *hid; + + hid = get_hid(num); + if (!hid || !hid->open) return; + + CloseHandle(hid->handle); + hid->handle = NULL; + hid->open = FALSE; +} + + + + +static void add_hid(hid_t *h) +{ + if (!first_hid || !last_hid) { + first_hid = last_hid = h; + h->next = h->prev = NULL; + return; + } + last_hid->next = h; + h->prev = last_hid; + h->next = NULL; + last_hid = h; +} + + +static hid_t * get_hid(int num) +{ + hid_t *p; + for (p = first_hid; p && num > 0; p = p->next, num--) ; + return p; +} + + +static void free_all_hid(void) +{ + hid_t *p, *q; + + for (p = first_hid; p; p = p->next) + { + CloseHandle(p->handle); + p->handle = NULL; + p->open = FALSE; + } + p = first_hid; + while (p) { + q = p; + p = p->next; + free(q); + } + first_hid = last_hid = NULL; +} + + + +void print_win32_err(void) +{ + char buf[256]; + DWORD err; + + err = GetLastError(); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, + 0, buf, sizeof(buf), NULL); + Debugprintf("err %ld: %s\n", err, buf); +} + +#endif + +// RTL_SDR support code + +char RTLModes[5][6] = {"FM", "AM", "USB", "LSB", "????"}; + + +void CheckAndProcessRTLUDP(struct RIGPORTINFO * PORT) +{ + int Length; + struct sockaddr_in rxaddr; + int addrlen = sizeof(struct sockaddr_in); + int Freq; + unsigned char RXBuffer[16]; + + struct RIGINFO * RIG = &PORT->Rigs[0]; + + Length = recvfrom(PORT->remoteSock, RXBuffer, 16, 0, (struct sockaddr *)&rxaddr, &addrlen); + + if (Length == 6) + { + long long MHz, Hz; + char CharMHz[16] = ""; + char CharHz[16] = ""; + int i; + int Mode; + + PORT->Timeout = 0; + RIG->RIGOK = TRUE; + + Freq = (RXBuffer[4] << 24) + (RXBuffer[3] << 16) + (RXBuffer[2] << 8) + RXBuffer[1]; + Mode = RXBuffer[5]; + + Freq += RIG->rxOffset; + + RIG->RigFreq = Freq / 1000000.0; + + // If we convert to float to display we get rounding errors, so convert to MHz and Hz to display + + MHz = Freq / 1000000; + + Hz = Freq - MHz * 1000000; + + sprintf(CharMHz, "%lld", MHz); + sprintf(CharHz, "%06lld", Hz); + + for (i = 5; i > 2; i--) + { + if (CharHz[i] == '0') + CharHz[i] = 0; + else + break; + } + + sprintf(RIG->WEB_FREQ,"%lld.%s", MHz, CharHz); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + strcpy(RIG->Valchar, RIG->WEB_FREQ); + + sprintf(RIG->WEB_MODE,"%s", RTLModes[Mode]); + + strcpy(RIG->ModeString, Modes[Mode]); + SetWindowText(RIG->hMODE, RIG->WEB_MODE); + + } +} + +VOID RTLUDPPoll(struct RIGPORTINFO * PORT) +{ + UCHAR * Poll = PORT->TXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on HAMLIB + char cmd[80]; + int len; + + if (RIG->ScanStopped == 0) + if (RIG->ScanCounter) + RIG->ScanCounter--; + + if (PORT->Timeout) + { + PORT->Timeout--; + + if (PORT->Timeout) // Still waiting + return; + + SetWindowText(RIG->hFREQ, "------------------"); + SetWindowText(RIG->hMODE, "----------"); + strcpy(RIG->WEB_FREQ, "-----------");; + strcpy(RIG->WEB_MODE, "------"); + + RIG->RIGOK = FALSE; + return; + } + + // Send Data if avail, else send poll + + if (RIG->NumberofBands && RIG->RIGOK && (RIG->ScanStopped == 0)) + { + if (RIG->ScanCounter <= 0) + { + // Send Next Freq + + if (GetPermissionToChange(PORT, RIG)) + { + int n; + + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Change Freq to %9.4f", PORT->FreqPtr->Freq); + + memcpy(PORT->TXBuffer, PORT->FreqPtr->Cmd1, PORT->FreqPtr->Cmd1Len); + PORT->TXLen = PORT->FreqPtr->Cmd1Len; + + _gcvt(PORT->FreqPtr->Freq / 1000000.0, 9, RIG->Valchar); // For MH + + n = sendto(PORT->remoteSock, PORT->TXBuffer, 5, 0, &PORT->remoteDest, sizeof(struct sockaddr)); + + if (PORT->TXLen == 10) + n = sendto(PORT->remoteSock, PORT->TXBuffer + 5, 5, 0, &PORT->remoteDest, sizeof(struct sockaddr)); + + PORT->TXBuffer[0] = 'P'; // Send Poll + + n = sendto(PORT->remoteSock, PORT->TXBuffer, 5, 0, &PORT->remoteDest, sizeof(struct sockaddr)); + + PORT->CmdSent = 1; + PORT->Retries = 0; + PORT->Timeout = 0; + PORT->AutoPoll = TRUE; + + // There isn't a response to a set command, so clear Scan Lock here + + ReleasePermission(RIG); // Release Perrmission + return; + } + } + } + + if (RIG->PollCounter) + { + RIG->PollCounter--; + if (RIG->PollCounter > 1) + return; + } + + if (RIG->RIGOK && (RIG->ScanStopped == 0) && RIG->NumberofBands) + return; // no point in reading freq if we are about to change it + + RIG->PollCounter = 10; // Once Per Sec + + // Read Frequency + + cmd[0] = 'P'; + + len = sendto(PORT->remoteSock, cmd, 5, 0, &PORT->remoteDest, sizeof(struct sockaddr)); + + PORT->Timeout = 10; + PORT->CmdSent = 0; + + PORT->AutoPoll = TRUE; + + return; +} + +VOID ConnecttoRTLUDP(struct RIGPORTINFO * PORT) +{ + char Msg[255]; + int i; + u_long param=1; + BOOL bcopt=TRUE; + struct sockaddr_in sinx; + + if (PORT->remoteSock) + { + closesocket(PORT->remoteSock); + } + + PORT->remoteSock = 0; + PORT->remoteSock = socket(AF_INET,SOCK_DGRAM,0); + + if (PORT->remoteSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for RTLUDP socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + PORT->CONNECTING = FALSE; + return; + } + + ioctl(PORT->remoteSock, FIONBIO, ¶m); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + PORT->CONNECTED = TRUE; + PORT->hDevice = (HANDLE)1; // simplifies check code + PORT->Alerted = TRUE; +} + + + + + + diff --git a/TNCEmulators-HPLaptop.c b/TNCEmulators-HPLaptop.c new file mode 100644 index 0000000..7497419 --- /dev/null +++ b/TNCEmulators-HPLaptop.c @@ -0,0 +1,6261 @@ +/* +Copyright 2001-2022 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 +*/ + + +// TNC Emulator Module for BPQ32 switch + +// Supports TNC2 and WA8DED Hostmode interfaces + + +#define _CRT_SECURE_NO_DEPRECATE + +#include "CHeaders.h" + +#define LF 10 +#define CR 13 + +#define Connect(stream) SessionControl(stream,1,0) +#define Disconnect(stream) SessionControl(stream,2,0) +#define ReturntoNode(stream) SessionControl(stream,3,0) +#define ConnectUsingAppl(stream, appl) SessionControl(stream, 0, appl) + +int APIENTRY SendMsg(int stream, char * msg, int len); + +VOID SENDPACKET(struct TNCDATA * TNC); +VOID CHECKCTS(struct TNCDATA * TNC); +VOID SETCOMMANDMODE(struct TNCDATA * TNC); +VOID SETCOMM00(struct TNCDATA * TNC); +VOID SENDREPLY(struct TNCDATA * TNC, char * Msg, int Len); +VOID TNCCOMMAND(struct TNCDATA * TNC); +VOID TNC2PutChar(struct TNCDATA * TNC, int Char); +VOID KBECHO(struct TNCDATA * TNC, int Char); +VOID KBNORM(struct TNCDATA * TNC, int Char); +VOID PUTCHARINBUFFER(struct TNCDATA * TNC, int Char); +VOID TNC2GetChar(struct TNCDATA * TNC, int * returnedchar, int * moretocome); +VOID CONNECTTONODE(struct TNCDATA * TNC); +DllImport int APIENTRY ChangeSessionCallsign(int Stream, unsigned char * AXCall); +DllImport int APIENTRY GetCallsign(int stream, char * callsign); +VOID GETDATA(struct TNCDATA * TNC); +VOID DOCONMODECHANGE(struct TNCDATA * TNC, int Stream); +VOID SEND_CONNECTED(struct TNCDATA * TNC, int ToStream); +VOID READCHANGE(int Stream); +VOID DOMONITORING(int NeedTrace); +int APIENTRY DecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp); +time_t APIENTRY GetRaw(int stream, char * msg, int * len, int * count); +BOOL TfPut(struct TNCDATA * TNC, UCHAR character); +int IntDecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp, unsigned long long Mask, BOOL APRS, BOOL MCTL); +int DATAPOLL(struct TNCDATA * TNC, struct StreamInfo * Channel); +int STATUSPOLL(struct TNCDATA * TNC, struct StreamInfo * Channel); +int DEDPROCESSHOSTPACKET(struct StreamInfo * Channel, struct TNCDATA * TNC); +VOID ProcessKPacket(struct TNCDATA * conn, UCHAR * rxbuffer, int Len); +VOID ProcessPacket(struct TNCDATA * conn, UCHAR * rxbuffer, int Len);; +int KANTConnected(struct TNCDATA * conn, struct StreamInfo * channel, int Stream); +int KANTDisconnected(struct TNCDATA * conn, struct StreamInfo * channel, int Stream); +VOID SendKISSData(struct TNCDATA * conn, UCHAR * txbuffer, int Len); +VOID ProcessSCSPacket(struct TNCDATA * conn, UCHAR * rxbuffer, int Length); +VOID TNCPoll(); +VOID DisableAppl(struct TNCDATA * TNC); +int BPQSerialSetPollDelay(HANDLE hDevice, int PollDelay); +int BPQSerialSendData(struct TNCDATA * TNC, UCHAR * Message,int MsgLen); +int BPQSerialGetData(struct TNCDATA * TNC, UCHAR * Message, unsigned int BufLen, ULONG * MsgLen); + +extern struct TNCDATA * TNCCONFIGTABLE; + +struct TNCDATA * TNC2TABLE; // malloc'ed +extern int NUMBEROFTNCPORTS; + +// MODEFLAG DEFINITIONS + +#define COMMAND 1 +#define TRANS 2 +#define CONV 4 + +// APPLFLAGS BITS + +//CMD_TO_APPL EQU 1B ; PASS COMMAND TO APPLICATION +//MSG_TO_USER EQU 10B ; SEND "CONNECTED" TO USER +//MSG_TO_APPL EQU 100B ; SEND "CONECTED" TO APPL + +extern char pgm[256]; + +int CloseDelay = 10; // Close after connect fail delay + +MESSAGE MONITORDATA; // RAW FRAME FROM NODE + +char NEWCALL[11]; + +//TABLELEN DW TYPE TNCDATA + +char LNKSTATEMSG[] = "Link state is: "; +char CONNECTEDMSG[] = "CONNECTED to "; +char WHATMSG[] = "Eh?\rcmd:"; +char CMDMSG[] = "cmd:"; + + +char DISCONNMSG[] = "\r*** DISCONNECTED\r"; + +char CONMSG1[] = "\r*** CONNECTED to "; +char CONCALL[10]; + + +char SIGNON[] = "\r\rG8BPQ TNC2 EMULATOR\r\r"; + +char CONMSG[] ="\r*** CONNECTED to "; +char SWITCH[] = "SWITCH\r"; +char SWITCHSP[] = "SWITCH "; + +char WAS[] = " was "; +char VIA[] = " via "; +char OFF[] = "OFF\r"; +char ON[] = "ON \r"; + +// BPQ Serial Device Support + +// On W2K and above, BPQVIrtualCOM.sys provides a pair of cross-connected devices, and a control channel +// to enumerate, add and delete devices. + +// On Win98 BPQVCOMM.VXD provides a single IOCTL interface, over which calls for each COM device are multiplexed + +#ifdef WIN32 + +#define IOCTL_SERIAL_SET_BAUD_RATE CTL_CODE(FILE_DEVICE_SERIAL_PORT, 1,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_QUEUE_SIZE CTL_CODE(FILE_DEVICE_SERIAL_PORT, 2,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_LINE_CONTROL CTL_CODE(FILE_DEVICE_SERIAL_PORT, 3,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_BREAK_ON CTL_CODE(FILE_DEVICE_SERIAL_PORT, 4,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_BREAK_OFF CTL_CODE(FILE_DEVICE_SERIAL_PORT, 5,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_IMMEDIATE_CHAR CTL_CODE(FILE_DEVICE_SERIAL_PORT, 6,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_TIMEOUTS CTL_CODE(FILE_DEVICE_SERIAL_PORT, 7,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_TIMEOUTS CTL_CODE(FILE_DEVICE_SERIAL_PORT, 8,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_DTR CTL_CODE(FILE_DEVICE_SERIAL_PORT, 9,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_CLR_DTR CTL_CODE(FILE_DEVICE_SERIAL_PORT,10,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_RESET_DEVICE CTL_CODE(FILE_DEVICE_SERIAL_PORT,11,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_RTS CTL_CODE(FILE_DEVICE_SERIAL_PORT,12,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_CLR_RTS CTL_CODE(FILE_DEVICE_SERIAL_PORT,13,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_XOFF CTL_CODE(FILE_DEVICE_SERIAL_PORT,14,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_XON CTL_CODE(FILE_DEVICE_SERIAL_PORT,15,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_WAIT_MASK CTL_CODE(FILE_DEVICE_SERIAL_PORT,16,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_WAIT_MASK CTL_CODE(FILE_DEVICE_SERIAL_PORT,17,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_WAIT_ON_MASK CTL_CODE(FILE_DEVICE_SERIAL_PORT,18,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_PURGE CTL_CODE(FILE_DEVICE_SERIAL_PORT,19,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_BAUD_RATE CTL_CODE(FILE_DEVICE_SERIAL_PORT,20,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_LINE_CONTROL CTL_CODE(FILE_DEVICE_SERIAL_PORT,21,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_CHARS CTL_CODE(FILE_DEVICE_SERIAL_PORT,22,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_CHARS CTL_CODE(FILE_DEVICE_SERIAL_PORT,23,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_HANDFLOW CTL_CODE(FILE_DEVICE_SERIAL_PORT,24,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_HANDFLOW CTL_CODE(FILE_DEVICE_SERIAL_PORT,25,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_MODEMSTATUS CTL_CODE(FILE_DEVICE_SERIAL_PORT,26,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_COMMSTATUS CTL_CODE(FILE_DEVICE_SERIAL_PORT,27,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_XOFF_COUNTER CTL_CODE(FILE_DEVICE_SERIAL_PORT,28,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_PROPERTIES CTL_CODE(FILE_DEVICE_SERIAL_PORT,29,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_DTRRTS CTL_CODE(FILE_DEVICE_SERIAL_PORT,30,METHOD_BUFFERED,FILE_ANY_ACCESS) + + +#define IOCTL_SERIAL_IS_COM_OPEN CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GETDATA CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x801,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SETDATA CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x802,METHOD_BUFFERED,FILE_ANY_ACCESS) + +#define IOCTL_SERIAL_SET_CTS CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x803,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_DSR CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x804,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_DCD CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x805,METHOD_BUFFERED,FILE_ANY_ACCESS) + +#define IOCTL_SERIAL_CLR_CTS CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x806,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_CLR_DSR CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x807,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_CLR_DCD CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x808,METHOD_BUFFERED,FILE_ANY_ACCESS) + +#define IOCTL_BPQ_ADD_DEVICE CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x809,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_BPQ_DELETE_DEVICE CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x80a,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_BPQ_LIST_DEVICES CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x80b,METHOD_BUFFERED,FILE_ANY_ACCESS) + +#define IOCTL_BPQ_SET_POLLDELAY CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x80c,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_BPQ_SET_DEBUGMASK CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x80d,METHOD_BUFFERED,FILE_ANY_ACCESS) + +#define W98_SERIAL_IS_COM_OPEN 0x800 +#define W98_SERIAL_GETDATA 0x801 +#define W98_SERIAL_SETDATA 0x802 + +#define W98_SERIAL_SET_CTS 0x803 +#define W98_SERIAL_SET_DSR 0x804 +#define W98_SERIAL_SET_DCD 0x805 + +#define W98_SERIAL_CLR_CTS 0x806 +#define W98_SERIAL_CLR_DSR 0x807 +#define W98_SERIAL_CLR_DCD 0x808 + +#define W98_BPQ_ADD_DEVICE 0x809 +#define W98_BPQ_DELETE_DEVICE 0x80a +#define W98_BPQ_LIST_DEVICES 0x80b + +#define W98_BPQ_SET_POLLDELAY 0x80c +#define W98_BPQ_SET_DEBUGMASK 0x80d + +#define W98_SERIAL_GET_COMMSTATUS 27 +#define W98_SERIAL_GET_DTRRTS 30 + +#define DebugModemStatus 1 +#define DebugCOMStatus 2 +#define DebugWaitCompletion 4 +#define DebugReadCompletion 8 + + +HANDLE hControl; + +BOOL Win98; + +typedef struct _SERIAL_STATUS { + ULONG Errors; + ULONG HoldReasons; + ULONG AmountInInQueue; + ULONG AmountInOutQueue; + BOOL EofReceived; + BOOL WaitForImmediate; +} SERIAL_STATUS,*PSERIAL_STATUS; + +#endif + +#ifndef WIN32 +// #include + +extern int posix_openpt (int __oflag); +extern int grantpt (int __fd); +extern int unlockpt (int __fd); +extern char *ptsname (int __fd); +extern int ptsname_r (int __fd, char *__buf, size_t __buflen); +extern int getpt (void); + +HANDLE LinuxOpenPTY(char * Name) +{ + // Open a Virtual COM Port + + HANDLE hDevice, slave;; + char slavedevice[80]; + int ret; + u_long param=1; + struct termios term; + +#ifdef MACBPQ + + // Create a pty pair + + openpty(&hDevice, &slave, &slavedevice[0], NULL, NULL); + close(slave); + +#else + + hDevice = posix_openpt(O_RDWR|O_NOCTTY); + + if (hDevice == -1) + { + perror("posix_openpt Create PTY pair failed"); + return -1; + } + if (grantpt (hDevice) == -1) + { + perror("grantpt Create PTY pair failed"); + return -1; + } + if (unlockpt (hDevice) == -1) + { + perror("unlockpt Create PTY pair failed"); + return -1; + } + if (ptsname_r(hDevice, slavedevice, 80) != 0) + { + perror("ptsname_r Create PTY pair failed"); + return -1; + } + +#endif + + printf("slave device: %s. ", slavedevice); + + if (tcgetattr(hDevice, &term) == -1) + { + perror("tty_speed: tcgetattr"); + return FALSE; + } + + cfmakeraw(&term); + + if (tcsetattr(hDevice, TCSANOW, &term) == -1) + { + perror("tcsetattr"); + return -1; + } + + ioctl(hDevice, FIONBIO, ¶m); + + chmod(slavedevice, S_IRUSR|S_IRGRP|S_IWUSR|S_IWGRP|S_IROTH|S_IWOTH); + + unlink (Name); + + ret = symlink (slavedevice, Name); + + if (ret == 0) + printf ("symlink to %s created\n", Name); + else + printf ("symlink to %s failed\n", Name); + + return hDevice; +} +#else + +HANDLE BPQOpenSerialPort(struct TNCDATA * TNC, DWORD * lasterror) +{ + // Open a Virtual COM Port + + int port = TNC->ComPort; + char szPort[80]; + HANDLE hDevice; + int Err; + + *lasterror=0; + + if (Win98) + { + sprintf( szPort, "\\\\.\\COM%d",port) ; + + hDevice = CreateFile( szPort, GENERIC_READ | GENERIC_WRITE, + 0, // exclusive access + NULL, // no security attrs + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + if (hDevice == (HANDLE) -1 ) + { + // If In Use(5) ok, else fail + + if (GetLastError() == 5) + return (HANDLE)(ptrdiff_t)(port<<16); // Port Number is a pseudohandle to the device + + return (HANDLE)(ptrdiff_t) - 1; + } + + CloseHandle(hDevice); + + return (HANDLE)(ptrdiff_t)(port<<16); // Port Number is a pseudohandle to the device + } + + // Try New Style VCOM firsrt + + sprintf( szPort, "\\\\.\\pipe\\BPQCOM%d", port ) ; + + hDevice = CreateFile( szPort, GENERIC_READ | GENERIC_WRITE, + 0, // exclusive access + NULL, // no security attrs + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + Err = GetLastError(); + + if (hDevice != (HANDLE) -1) + { + TNC->NewVCOM = TRUE; + TNC->PortEnabled = TRUE; + Err = GetFileType(hDevice); + } + else + { + + // Try old style + + sprintf(szPort, "\\\\.\\BPQ%d", port ) ; + + + hDevice = CreateFile( szPort, GENERIC_READ | GENERIC_WRITE, + 0, // exclusive access + NULL, // no security attrs + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + if (TNC->PollDelay) + BPQSerialSetPollDelay(hDevice, TNC->PollDelay); + + } + if (hDevice == (HANDLE) -1 ) + { + *lasterror=GetLastError(); + } + + return hDevice; +} +#endif + +int BPQSerialSetCTS(HANDLE hDevice) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + if (Win98) + return DeviceIoControl(hControl,(DWORD)hDevice | W98_SERIAL_SET_CTS,NULL,0,NULL,0, &bytesReturned,NULL); + else + return DeviceIoControl(hDevice,IOCTL_SERIAL_SET_CTS,NULL,0,NULL,0, &bytesReturned,NULL); + +#endif +} + +int BPQSerialSetDSR(HANDLE hDevice) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + if (Win98) + return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_SET_DSR,NULL,0,NULL,0, &bytesReturned,NULL); + else + return DeviceIoControl(hDevice,IOCTL_SERIAL_SET_DSR, NULL,0,NULL,0, &bytesReturned,NULL); +#endif +} + +int BPQSerialSetDCD(HANDLE hDevice) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + if (Win98) + return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_SET_DCD,NULL,0,NULL,0, &bytesReturned,NULL); + else + return DeviceIoControl(hDevice,IOCTL_SERIAL_SET_DCD,NULL,0,NULL,0, &bytesReturned,NULL); +#endif +} + +int BPQSerialClrCTS(HANDLE hDevice) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + if (Win98) + return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_CLR_CTS,NULL,0,NULL,0, &bytesReturned,NULL); + else + return DeviceIoControl(hDevice,IOCTL_SERIAL_CLR_CTS,NULL,0,NULL,0, &bytesReturned,NULL); +#endif +} +int BPQSerialClrDSR(HANDLE hDevice) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + if (Win98) + return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_CLR_DSR,NULL,0,NULL,0, &bytesReturned,NULL); + else + return DeviceIoControl(hDevice,IOCTL_SERIAL_CLR_DSR,NULL,0,NULL,0, &bytesReturned,NULL); +#endif +} + +int BPQSerialClrDCD(HANDLE hDevice) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + if (Win98) + return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_CLR_DCD,NULL,0,NULL,0, &bytesReturned,NULL); + else + return DeviceIoControl(hDevice,IOCTL_SERIAL_CLR_DCD, NULL,0,NULL,0, &bytesReturned,NULL); +#endif +} + +static int SendDataToTNC(struct TNCDATA * TNC, UCHAR * TXMsg, int n) +{ + // Used for all port types + +#ifdef WIN32 + + // WIN32 VCOM Used one of my Drivers + + if (TNC->VCOM) + BPQSerialSendData(TNC, TXMsg, n); + +#endif + + // Linux VCOM uses SOCAT Pairs and normal write + + return WriteCOMBlock(TNC->hDevice, TXMsg, n); +} + +int BPQSerialSendData(struct TNCDATA * TNC, UCHAR * Message,int MsgLen) +{ + HANDLE hDevice = TNC->hDevice; + ULONG bytesReturned; + + // Host Mode code calls BPQSerialSendData for all ports, so it a real port, pass on to real send routine + + if (!TNC->VCOM) + return WriteCOMBlock(TNC->hDevice, Message, MsgLen); + +#ifndef WIN32 + + // Linux usies normal IO for all ports + return WriteCOMBlock(TNC->hDevice, Message, MsgLen); + +#else + + if (MsgLen > 4096 ) return ERROR_INVALID_PARAMETER; + + if (Win98) + return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_SETDATA,Message,MsgLen,NULL,0, &bytesReturned,NULL); + else + { + if (TNC->NewVCOM) + { + // Have to escape all oxff chars, as these are used to get status info + + UCHAR NewMessage[1000]; + UCHAR * ptr1 = Message; + UCHAR * ptr2 = NewMessage; + UCHAR c; + + int Length = MsgLen; + + while (Length != 0) + { + c = *(ptr1++); + *(ptr2++) = c; + + if (c == 0xff) + { + *(ptr2++) = c; + MsgLen++; + } + Length--; + } + + return WriteFile(hDevice, NewMessage, MsgLen, &bytesReturned, NULL); + } + else + return DeviceIoControl(hDevice,IOCTL_SERIAL_SETDATA,Message,MsgLen,NULL,0, &bytesReturned,NULL); + } +#endif +} + +int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error); + +int GetDataFromTNC(struct TNCDATA * TNC, UCHAR * Message, unsigned int BufLen, ULONG * MsgLen) +{ + // Used for all port types + +#ifdef WIN32 + + // WIN32 VCOM Used one of my Drivers + + if (TNC->VCOM) + return BPQSerialGetData(TNC, Message, BufLen, MsgLen); + + *MsgLen = ReadCOMBlock(TNC->hDevice, Message, BufLen); + return 0; + +#else + + int Error = 0; + + if (TNC->VCOM == 0) + { + *MsgLen = ReadCOMBlock(TNC->hDevice, Message, BufLen); + return 0; + } + + // Linux VCOM uses SOCAT Pairs and normal Read + + // If the slave closes connection read returns 5. Need to trap and + // close/reopen. So use ReadCOMBlockEx + + *MsgLen = ReadCOMBlockEx(TNC->hDevice, Message, BufLen, &Error); + + if (Error == 5) + { + printf("Read error on TNCPORT %s - Restarting\n", TNC->PORTNAME); + close(TNC->hDevice); + TNC->hDevice = LinuxOpenPTY(TNC->PORTNAME); + } + return 0; + +#endif +} + + +int BPQSerialGetData(struct TNCDATA * TNC, UCHAR * Message, unsigned int BufLen, ULONG * MsgLen) +{ +#ifdef WIN32 + DWORD dwLength = 0; + DWORD Available = 0; + HANDLE hDevice = TNC->hDevice; + int Length, RealLen = 0; + + if (BufLen > 4096 ) return ERROR_INVALID_PARAMETER; + + if (Win98) + return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_GETDATA,NULL,0,Message,BufLen,MsgLen,NULL); + + if (TNC->NewVCOM) + { + int ret = PeekNamedPipe(hDevice, NULL, 0, NULL, &Available, NULL); + + if (ret == 0) + { + ret = GetLastError(); + + if (ret == ERROR_BROKEN_PIPE) + { + CloseHandle(hDevice); + hDevice = INVALID_HANDLE_VALUE; + return 0; + } + } + + if (Available > BufLen) + Available = BufLen; + + if (Available) + { + UCHAR * ptr1 = Message; + UCHAR * ptr2 = Message; + UCHAR c; + + ReadFile(hDevice, Message, Available, &Length, NULL); + + // Have to look for FF escape chars + + RealLen = Length; + + while (Length != 0) + { + c = *(ptr1++); + Length--; + + if (c == 0xff) + { + c = c = *(ptr1++); + Length--; + + if (c == 0xff) // ff ff means ff + { + RealLen--; + } + else + { + // This is connection statua from other end + + RealLen -= 2; +// TNC->PortEnabled = c; + continue; + } + } + *(ptr2++) = c; + } + } + *MsgLen = RealLen; + return 0; + } + + return DeviceIoControl(hDevice,IOCTL_SERIAL_GETDATA,NULL,0,Message,BufLen,MsgLen,NULL); +} +#else + return 0; +} +#endif + +int BPQSerialGetQCounts(HANDLE hDevice,ULONG * RXCount, ULONG * TXCount) +{ +#ifndef WIN32 + return 0; +#else + + SERIAL_STATUS Resp; + int MsgLen; + int ret; + + if (Win98) + ret = DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_GET_COMMSTATUS,NULL,0,&Resp,sizeof(SERIAL_STATUS),&MsgLen,NULL); + else + ret = DeviceIoControl(hDevice,IOCTL_SERIAL_GET_COMMSTATUS,NULL,0,&Resp,sizeof(SERIAL_STATUS),&MsgLen,NULL); + + *RXCount=Resp.AmountInInQueue; + *TXCount=Resp.AmountInOutQueue; + + return ret; +#endif +} + +int BPQSerialGetDeviceList(HANDLE hDevice,ULONG * Slot,ULONG * Port) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + return DeviceIoControl (hDevice,IOCTL_BPQ_LIST_DEVICES,Slot,4,Port,4,&bytesReturned,NULL); +#endif +} + +int BPQSerialIsCOMOpen(HANDLE hDevice,ULONG * Count) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + if (Win98) + return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_IS_COM_OPEN,NULL,0,Count,4,&bytesReturned,NULL); + else + return DeviceIoControl(hDevice,IOCTL_SERIAL_IS_COM_OPEN,NULL,0,Count,4,&bytesReturned,NULL); +#endif +} + +int BPQSerialGetDTRRTS(HANDLE hDevice, ULONG * Flags) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + if (Win98) + return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_GET_DTRRTS,NULL,0,Flags,4,&bytesReturned,NULL); + else + return DeviceIoControl(hDevice,IOCTL_SERIAL_GET_DTRRTS,NULL,0,Flags,4,&bytesReturned,NULL); +#endif +} + +int BPQSerialSetPollDelay(HANDLE hDevice, int PollDelay) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + if (Win98) + return DeviceIoControl(hControl, (UINT)hDevice | W98_BPQ_SET_POLLDELAY,&PollDelay,4,NULL,0, &bytesReturned,NULL); + else + return DeviceIoControl(hDevice,IOCTL_BPQ_SET_POLLDELAY,&PollDelay,4,NULL,0, &bytesReturned,NULL); + +#endif +} + +int BPQSerialSetDebugMask(HANDLE hDevice, int DebugMask) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + return DeviceIoControl(hDevice, IOCTL_BPQ_SET_DEBUGMASK, &DebugMask, 4, NULL, 0, &bytesReturned,NULL); +#endif +} + +void CheckForStreamChange(struct TNCDATA * TNC, int ToStream) +{ + // Send Stream Switched Message if changed + + char Msg[80]; + int Len; + + if (ToStream == TNC->RXStream) + return; + + TNC->RXStream = ToStream; + + // Send Message + + // |B:WA7GXD: + + if (TNC->StreamCall) + Len = sprintf(Msg, "%c%c:%s:", TNC->StreamSW, ToStream + 'A', TNC->TNC2Stream[ToStream]->RemoteCall); + else + Len = sprintf(Msg, "%c%c", TNC->StreamSW, ToStream + 'A'); + + SENDREPLY(TNC, Msg, Len); +} + +int LocalSessionState(int stream, int * state, int * change, BOOL ACK) +{ + // Get current Session State. Any state changed is ACK'ed + // automatically. See BPQHOST functions 4 and 5. + + // Local version without semaphore or checktimer + + BPQVECSTRUC * HOST = &BPQHOSTVECTOR[stream -1]; // API counts from 1 + + // CX = 0 if stream disconnected or CX = 1 if stream connected + // DX = 0 if no change of state since last read, or DX = 1 if + // the connected/disconnected state has changed since + // last read (ie. delta-stream status). + + // HOSTFLAGS = Bit 80 = Allocated + // Bit 40 = Disc Request + // Bit 20 = Stay Flag + // Bit 02 and 01 State Change Bits + + if ((HOST->HOSTFLAGS & 3) == 0) + // No Chaange + *change = 0; + else + *change = 1; + + if (HOST->HOSTSESSION) // LOCAL SESSION + // Connected + *state = 1; + else + *state = 0; + + if (ACK) + HOST->HOSTFLAGS &= 0xFC; // Clear Change Bits + + return 0; +} + + + + +VOID ONOFF(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ + // PROCESS COMMANDS WITH ON/OFF PARAM + + char Param; + UCHAR * valueptr; + UCHAR oldvalue, newvalue = 0xff; + + char Response[80]; + int len; + + _strupr(Tail); + Param = *Tail; + + valueptr = (UCHAR *)TNC + CMD->CMDFLAG;; + oldvalue = (UCHAR)*valueptr; + + switch(Param) + { + case ' ': + break; + case 'Y': + newvalue = 1; + break; + case 'N': + newvalue = 0; + break; + case 'O': + if (Tail[1] == 'N') + newvalue = 1; + else + newvalue = 0; + break; + } + + if (newvalue == 255) + { + len = sprintf(Response, "%s %s\r", CMD->String, (oldvalue)?"ON":"OFF"); + } + else + { + len = sprintf(Response, "%s was %s\r", CMD->String, (oldvalue)?"ON":"OFF"); + *valueptr = newvalue; + } + SENDREPLY(TNC, Response, len); +} + + + +VOID ONOFF_CONOK(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + ONOFF(TNC, Tail, CMD); + + // UPDATE APPL FLAGS ON NODE PORT + + if (TNC->CONOK) + SetAppl(TNCStream->BPQPort, TNC->APPLFLAGS, TNC->APPLICATION); + else + SetAppl(TNCStream->BPQPort, TNC->APPLFLAGS, 0); +} + +VOID SETMYCALL(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ + char Response[80]; + int len; + char Call[10] = " "; + + _strupr(Tail); + + if (*Tail == ' ') + { + // REQUEST FOR CURRENT STATE + + len = sprintf(Response, "MYCALL %s\r", TNC->MYCALL); + } + else + { + strlop(Tail,' ');; + memcpy(Call, Tail, (int)strlen(Tail) + 1); + len = sprintf(Response, "MYCALL was %s\r", TNC->MYCALL); + memcpy(TNC->MYCALL, Call, 10); + } + + SENDREPLY(TNC, Response, len); +} +VOID CTEXTCMD(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ + char Response[256]; + int len, n; + + if (*Tail == ' ') + { + // REQUEST FOR CURRENT STATE + + len = sprintf(Response, "CTEXT %s\r", TNC->CTEXT); + } + else + { + Tail[TNC->MSGLEN] = 0; + n = strlen(Tail) - 1; + while(n > 0 && Tail[n] == ' ') + Tail[n--] = 0; + + if (strlen(Tail) > 119) + Tail[119] = 0; + + len = sprintf(Response, "CTEXT was %s\r", TNC->CTEXT); + strcpy(TNC->CTEXT, Tail); + } + + SENDREPLY(TNC, Response, len); +} + +VOID BTEXT(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ +} +VOID VALUE(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ + // PROCESS COMMANDS WITH decimal value + + char Param = *Tail; + UCHAR * valueptr; + int oldvalue, newvalue; + + char Response[80]; + int len; + + valueptr = (UCHAR *)TNC + CMD->CMDFLAG;; + oldvalue = *valueptr; + + strlop(Tail, ' '); + + if (Tail[0]) + { + newvalue = atoi(Tail); + len = sprintf(Response, "%s was %d\r", CMD->String, oldvalue); + *valueptr = newvalue; + } + else + { + len = sprintf(Response, "%s %d\r", CMD->String, oldvalue); + } + SENDREPLY(TNC, Response, len); +} + +VOID VALHEX(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ + // PROCESS COMMANDS WITH decimal value + + char Param = *Tail; + + UCHAR * valueptr; + UINT * intvalueptr; + UINT oldvalue, newvalue; + + char Response[80]; + int len; + + valueptr = (UCHAR *)TNC + CMD->CMDFLAG;; + intvalueptr = (UINT *)valueptr; + + oldvalue = *intvalueptr; + + strlop(Tail, ' '); + + if (Tail[0]) + { + if (Tail[0] == '$') + newvalue = (UINT)strtol(Tail + 1, NULL, 16); + else + newvalue = (UINT)strtol(Tail, NULL, 0); + + len = sprintf(Response, "%s was $%x\r", CMD->String, oldvalue); + *intvalueptr = newvalue; + } + else + { + len = sprintf(Response, "%s $%x\r", CMD->String, oldvalue); + } + SENDREPLY(TNC, Response, len); +} + +VOID APPL_VALHEX(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ + int ApplNum = 1; + UINT APPLMASK; + + VALHEX(TNC, Tail, CMD); + + // UPDATE APPL FLAGS ON NODE PORT + + if (TNC->CONOK) + SetAppl(TNC->BPQPort, TNC->APPLFLAGS, TNC->APPLICATION); + else + SetAppl(TNC->BPQPort, TNC->APPLFLAGS, 0); + + // Set MYCALL to APPLCALL + + APPLMASK = TNC->APPLICATION; + ApplNum = 1; + + while (APPLMASK && (APPLMASK & 1) == 0) + { + ApplNum++; + APPLMASK >>= 1; + } + + if (TNC->CONOK && TNC->APPLICATION) + memcpy(TNC->MYCALL, GetApplCall(ApplNum), 10); + +} +VOID CSWITCH(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ + char Response[80]; + int len; + + len = sprintf(Response, "%s", CMDMSG); + SENDREPLY(TNC, Response, len); + + CONNECTTONODE(TNC); + +} +VOID CONMODE(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ + SENDREPLY(TNC, CMDMSG, 4); +} + +VOID TNCCONV(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + TNCStream->MODEFLAG |= CONV; + TNCStream->MODEFLAG &= ~(COMMAND+TRANS); +} + +VOID TNCNODE(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ + // CONNECT TO NODE + + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + TNCStream->VMSR |= 0x88; // SET CONNECTED + + TNCStream->MODEFLAG |= CONV; // INTO CONVERSE MODE + TNCStream->MODEFLAG &= ~(COMMAND+TRANS); + + CONNECTTONODE(TNC); +} + +VOID CStatus(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + char Response[80]; + int i, len; + char Call[10] = ""; + char Selected[3] = " "; + + for (i = 0; i < TNC->HOSTSTREAMS; i++) + { + char Selected[3] = " "; + TNCStream = TNC->TNC2Stream[i]; + + if (TNC->RXStream == i) + Selected[0] = 'I'; + + if (TNC->TXStream == i) + Selected[1] = 'O'; + + if (TNCStream->VMSR & 0x80) + { + GetCallsign(TNCStream->BPQPort, Call); + strlop(Call, ' '); + + len = sprintf(Response, "%c stream - %s CONNECTED to %s\r", i + 'A', Selected, Call); + } + else + { + len = sprintf(Response, "%c stream - %s DISCONNECTED\r", i + 'A', Selected); + } + + SENDREPLY(TNC, Response, len); + } +} + + +VOID TNCCONNECT(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + char Response[80]; + int len; + char Call[10] = ""; + + _strupr(Tail); + + if (*Tail == ' ') + { + // REQUEST FOR CURRENT STATE + + len = sprintf(Response, "%s", LNKSTATEMSG); + + if (TNCStream->VMSR & 0x80) + { + GetCallsign(TNCStream->BPQPort, Call); + strlop(Call, ' '); + + len = sprintf(Response, "%c Link state is: CONNECTED to %s\r", TNC->TXStream + 'A', Call); + } + else + { + len = sprintf(Response, "%c Link state is: DISCONNECTED\r", TNC->TXStream + 'A'); + } + + SENDREPLY(TNC, Response, len); + return; + } + + // CONNECT, BUT NOT TO SWITCH - CONNECT TO NODE, THEN PASS TO IT FOR PROCESSING + + TNCNODE(TNC, Tail, CMD); + READCHANGE(TNCStream->BPQPort); //CLEAR STATUS CHANGE (TO AVOID SUPURIOUS "CONNECTED TO") + + strcat(TNC->TONODEBUFFER, "\r"); + TNC->MSGLEN = (int)strlen(TNC->TONODEBUFFER); + + SENDPACKET(TNC); // Will now go to node + +} +VOID TNCDISC(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + Disconnect(TNCStream->BPQPort); + + SENDREPLY(TNC, CMDMSG, 4); +} + +VOID READCHANGE(int Stream) +{ + int dummy; + LocalSessionState(Stream, &dummy, &dummy, TRUE); +} + +VOID TNCRELEASE(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ + ReturntoNode(TNC->BPQPort); + + TNC->VMSR &= 0x7F; // DROP DCD + TNC->VMSR |= 8; //DELTA DCD + + SENDREPLY(TNC, CMDMSG, 4); +} +VOID TNCTRANS(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + // MAKE PRETTY SURE THIS ISNT A BIT OF STRAY DATA + + if (TNC->MSGLEN > 6) + return; + + TNCStream->MODEFLAG |= TRANS; + TNCStream->MODEFLAG &= ~(COMMAND+CONV); +} +static VOID RESTART(struct TNCDATA * TNC) +{ + // REINITIALISE CHANNEL + + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + TNC->PUTPTR = TNC->GETPTR = &TNC->TOUSERBUFFER[0]; + TNC->RXCOUNT = 0; + + TNC->VLSR = 0x20; + TNC->VMSR = 0x30; + + TNCStream->MODEFLAG = COMMAND; + TNC->SENDPAC = 13; + TNC->CRFLAG = 1; + TNC->MALL = 1; + TNC->MMASK = -1; // MONITOR MASK FOR PORTS + TNC->TPACLEN = PACLEN; // TNC PACLEN + + TNC->COMCHAR = 3; + TNC->CMDTIME = 10; // SYSTEM TIMER = 100MS + TNC->CURSOR = &TNC->TONODEBUFFER[0]; // RESET MESSAGE START + TNC->MSGLEN = 0; + + SENDREPLY(TNC, SIGNON, 23); +} + + +static VOID UNPROTOCMD(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ +} + + +CMDX COMMANDLIST[] = +{ + "AUTOLF ",2, ONOFF, offsetof(struct TNCDATA, AUTOLF), + "BBSMON ",6, ONOFF, offsetof(struct TNCDATA, BBSMON), + "BTEXT ",2,BTEXT,0, + "CONOK ",4,ONOFF_CONOK,offsetof(struct TNCDATA, CONOK), + "C SWITCH",8,CSWITCH,0, + "CBELL ",2,ONOFF,offsetof(struct TNCDATA, CBELL), + "CMDTIME ",2,VALUE, offsetof(struct TNCDATA, CMDTIME), + "CMSG ",4,ONOFF,offsetof(struct TNCDATA, CMSG), + "COMMAND ",3,VALHEX, offsetof(struct TNCDATA, COMCHAR), + "CONMODE ",4,CONMODE,0, + "CPACTIME",2,ONOFF,offsetof(struct TNCDATA, CPACTIME), + "CR ",2,ONOFF,offsetof(struct TNCDATA, CRFLAG), + "CSTATUS ",2,CStatus,0, + "CTEXT ",2,CTEXTCMD,0, + "APPLFLAG",5,APPL_VALHEX, offsetof(struct TNCDATA, APPLFLAGS), + "APPL ",4,APPL_VALHEX, offsetof(struct TNCDATA, APPLICATION), + "CONVERS ",4,TNCCONV,0, + "CONNECT ",1,TNCCONNECT,0, + "DISCONNE",1,TNCDISC,0, + "ECHO ",1,ONOFF,offsetof(struct TNCDATA, ECHOFLAG), + "FLOW ",4,ONOFF,offsetof(struct TNCDATA, FLOWFLAG), + "HEADERLN",2,ONOFF,offsetof(struct TNCDATA, HEADERLN), + "K ",1,TNCNODE,0, + "MTXFORCE",4,ONOFF,offsetof(struct TNCDATA, MTXFORCE), + "LCSTREAM",8,ONOFF, offsetof(struct TNCDATA, LCStream), + "LFIGNORE",3,ONOFF,offsetof(struct TNCDATA, LFIGNORE), + "MTX ",3,ONOFF,offsetof(struct TNCDATA, MTX), + "MALL ",2,ONOFF,offsetof(struct TNCDATA, MALL), + "MCOM ",4,ONOFF,offsetof(struct TNCDATA, MCOM), + "MCON ",2,ONOFF,offsetof(struct TNCDATA, MCON), + "MMASK ",2,VALHEX, offsetof(struct TNCDATA, MMASK), + "MONITOR ",3,ONOFF,offsetof(struct TNCDATA, TRACEFLAG), + "MYCALL ",2,SETMYCALL,0, + "NEWMODE ",2,ONOFF,offsetof(struct TNCDATA, NEWMODE), + "NODE ",3,TNCNODE,0, + "NOMODE ",2,ONOFF,offsetof(struct TNCDATA, NOMODE), + "SENDPAC ",2,VALHEX, offsetof(struct TNCDATA, SENDPAC), + "STREAMCA",8,ONOFF, offsetof(struct TNCDATA, StreamCall), + "STREAMDBL",7,ONOFF, offsetof(struct TNCDATA, StreamDbl), + "STREAMSW",3,VALHEX, offsetof(struct TNCDATA, StreamSW), + "PACLEN ",1,VALUE, offsetof(struct TNCDATA, TPACLEN), + "PASS ",3,VALHEX, offsetof(struct TNCDATA, PASSCHAR), + "RELEASE ",3,TNCRELEASE,0, + "RESTART ",7,RESTART,0, + "TRANS ",1,TNCTRANS,0, + "UNPROTO ",1,UNPROTOCMD,0, + "USERS ",2,VALUE, offsetof(struct TNCDATA, Users), +}; + +static CMDX * CMD = NULL; + +int NUMBEROFTNCCOMMANDS = sizeof(COMMANDLIST)/sizeof(CMDX); + +/*NEWVALUE DW 0 +HEXFLAG DB 0 + + +NUMBER DB 4 DUP (0),CR +NUMBERH "$0000",CR + +BADMSG "?bad parameter",CR,0 + +BTHDDR ,0 ; CHAIN + DB 0 ; PORT + DW 7 ; LENGTH + DB 0F0H ; PID +BTEXTFLD DB 0DH,256 DUP (0) + +CMDENDADDR,0 ; POINTER TO END OF COMMAND + +MBOPTIONBYTE DB 0 + +NORMCALL DB 10 DUP (0) +AX25CALL DB 7 DUP (0) + +CONNECTCALL DB 10 DUP (0) ; CALLSIGN IN CONNECT MESSAGE +DIGICALL DB 10 DUP (0) ; DIGI IN CONNECT COMMAND +AX25STRING DB 64 DUP (0) ; DIGI STRING IN AX25 FORMAT +DIGIPTR ,0 ; POINTER TO CURRENT DIGI IN STRING + +NORMLEN ,0 + + EVEN +*/ + +int TRANSDELAY = 10; // 1 SEC + +//UNPROTOCALL DB "UNPROTO",80 DUP (0) + +char MONBUFFER[1000]; + +VOID TNC2GetChar(struct TNCDATA * TNC, int * returnedchar, int * moretocome) +{ + // Poll Node + + if (TNC->Mode == 0) + GETDATA(TNC); + + *returnedchar = -1; + *moretocome = 0; + + if (TNC->RXCOUNT == 0) + return; + + *returnedchar = *(TNC->GETPTR++); + + if (TNC->GETPTR == &TNC->TOUSERBUFFER[TNCBUFFLEN]) + TNC->GETPTR = &TNC->TOUSERBUFFER[0]; + + *moretocome = --TNC->RXCOUNT; //ANY MORE? + + if (TNC->RXCOUNT < 128) // GETTING LOW? + { + if (TNC->RTSFLAG & 1) // RTS UP? + { + // RTS HAD BEEN DROPPED TO STOP OTHER END SENDING - RAISE IT AGAIN + + TNC->RTSFLAG &= 0xFE; + } + } +} + +int TNCGetVMSR(struct TNCDATA * TNC, struct TNC2StreamInfo * TNCStream, BOOL ClearDeltas) +{ + // On TNC2 Connected bit should come from current stream + // flow control bits from TNC record + + int val = TNC->VMSR; + + if (TNC->Mode == TNC2) + val |= TNCStream->VMSR; + + if (ClearDeltas) + { + TNC->VMSR &= 0xF0; // CLEAR DELTA BITS + + if (TNC->Mode == TNC2) + TNCStream->VMSR &= 0xF0; + } + return val; +} + +BOOL TNCRUNNING;; + +VOID TNCBGThread(void * unused) +{ + TNCRUNNING = TRUE; + + Sleep(5000); + + while (TNCRUNNING) + { + TNCPoll(); + Sleep(50); + } +} + +VOID AllocateDEDChannel(struct TNCDATA * TNC, int Num) +{ + struct StreamInfo * Channel = zalloc(sizeof(struct StreamInfo)); + char * PNptr; + + // Only show last element of name on Streams display + + PNptr = &TNC->PORTNAME[0]; + + while (strchr(PNptr, '/')) + PNptr = strchr(PNptr, '/') + 1; + + sprintf(pgm, "DED %s", PNptr); + + TNC->Channels[Num] = Channel; + Channel->BPQStream = FindFreeStream(); + READCHANGE(Channel->BPQStream); // Prevent Initial *** disconnected + Debugprintf("BPQ32 DED Stream %d BPQ Stream %d", Num, Channel->BPQStream ); + + if (TNC->MODE) // if host mode, set appl + SetAppl(Channel->BPQStream, TNC->APPLFLAGS, TNC->APPLICATION); + + strcpy(pgm, "bpq32.exe"); +} + + +BOOL InitializeTNCEmulator() +{ + int resp, i; + ULONG OpenCount = 0; + DWORD Errorval; + int ApplNum = 1; + UINT APPLMASK; + struct TNC2StreamInfo * TNCStream; + + struct TNCDATA * TNC = TNCCONFIGTABLE; + + TNC2TABLE = TNCCONFIGTABLE; + + while (TNC) + { + // Com Port may be a hardware device (ie /dev/ttyUSB0) COMn or VCOMn (BPQ Virtual COM) + + char * Baud = strlop(TNC->PORTNAME, ','); + char * PNptr; + + PNptr = &TNC->PORTNAME[0]; + + // Only show last element of name on Streams display + + while (strchr(PNptr, '/')) + { + PNptr = strchr(PNptr, '/') + 1; + } + switch (TNC->Mode) + { + case TNC2: + + sprintf(pgm, "TNC2 %s", PNptr); + + // Start with number of streams, can add or remove with USRES command + + if (TNC->Users == 0) + TNC->Users = TNC->HOSTSTREAMS; + + if (TNC->Users == 0) + TNC->Users = TNC->HOSTSTREAMS = 1; + else + TNC->HOSTSTREAMS = TNC->Users; + + Debugprintf("TNC2 USers = %d, HOSTSTREAMS = %d\n", TNC->Users, TNC->HOSTSTREAMS); + + + for (i = 0; i < TNC->HOSTSTREAMS; i++) + { + TNCStream = TNC->TNC2Stream[i] = zalloc(sizeof(struct TNC2StreamInfo)); + + TNCStream->BPQPort = FindFreeStream(); + + if + (TNCStream->BPQPort == 0) + { + Debugprintf("Insufficient free Streams for TNC2 Emulator"); + return FALSE; + } + + READCHANGE(TNCStream->BPQPort); // Prevent Initial *** disconnected + + TNCStream->MODEFLAG = COMMAND; + + if (TNC->CONOK) + SetAppl(TNCStream->BPQPort, TNC->APPLFLAGS, TNC->APPLICATION); + else + SetAppl(TNCStream->BPQPort, TNC->APPLFLAGS, 0); + + } + + strcpy(pgm, "bpq32.exe"); + + if (TNC->TPACLEN == 0) + TNC->TPACLEN = PACLEN; // TNC PACLEN + + break; + + case DED: + + if (TNC->HOSTSTREAMS == 0) + TNC->HOSTSTREAMS = 4; // Default + + TNC->MALL = 1; + TNC->MTX = 1; + TNC->MCOM = 1; + TNC->MMASK = -1; // MONITOR MASK FOR PORTS + TNC->TPACLEN = PACLEN; // TNC PACLEN + + for (i = 1; i <= TNC->HOSTSTREAMS; i++) + { + AllocateDEDChannel(TNC, i); // Also used by Y command handler + } + + TNC->Channels[0] = zalloc(sizeof(struct StreamInfo)); + memcpy(TNC->Channels[0], TNC->Channels[1], sizeof(struct StreamInfo)); // For monitoring + + break; + + case KANTRONICS: + + sprintf(pgm, "KANT %s", PNptr); + + if (TNC->HOSTSTREAMS == 0) + TNC->HOSTSTREAMS = 1; // Default + + for (i = 0; i <= TNC->HOSTSTREAMS; i++) + { + struct StreamInfo * Channel; + + // Use Stream zero for defaults + + TNC->Channels[i] = malloc(sizeof (struct StreamInfo)); + memset(TNC->Channels[i], 0, sizeof (struct StreamInfo)); + + Channel = TNC->Channels[i]; + + Channel->BPQStream = FindFreeStream(); + READCHANGE(Channel->BPQStream); // Prevent Initial *** disconnected + + Debugprintf("BPQ32 KANT Init Stream %d BPQ Stream %d", i, Channel->BPQStream ); + + // channel->Chan_TXQ = 0; + // channel->BPQStream = 0; + // channel->Connected = FALSE; + // channel->MYCall[0] = 0; + + } + break; + + case SCS: + + TNC->ECHOFLAG = 1; + + if (TNC->HOSTSTREAMS == 0) + TNC->HOSTSTREAMS = 1; // Default + + TNC->MALL = 1; + TNC->MCOM = 1; + TNC->MMASK = -1; // MONITOR MASK FOR PORTS + TNC->TPACLEN = PACLEN; // TNC PACLEN + + sprintf(pgm, "SCS %s", PNptr); + + for (i = 1; i <= TNC->HOSTSTREAMS; i++) + { + struct StreamInfo * Channel = zalloc(sizeof(struct StreamInfo)); + + TNC->Channels[i] = Channel; + + Channel->BPQStream = FindFreeStream(); + READCHANGE(Channel->BPQStream); // Prevent Initial *** disconnected + + Debugprintf("BPQ32 SCS Init Stream %d BPQ Stream %d", i, Channel->BPQStream ); + } + + TNC->Channels[0] = zalloc(sizeof(struct StreamInfo)); + memcpy(TNC->Channels[0], TNC->Channels[1], sizeof(struct StreamInfo)); // For monitoring + + strcpy(pgm, "bpq32.exe"); + + break; + + } + + if (Baud) + TNC->Speed = atoi(Baud); + else + TNC->VCOM = TRUE; + + if (_memicmp(TNC->PORTNAME, "COM", 3) == 0) + { + TNC->VCOM = FALSE; + } + else + { + if (_memicmp(TNC->PORTNAME, "VCOM", 4) == 0) + TNC->ComPort = atoi(&TNC->PORTNAME[4]); + } + if (TNC->VCOM == 0) + { + // Real port + + TNC->hDevice = OpenCOMPort(TNC->PORTNAME, TNC->Speed, TRUE, TRUE, FALSE, 0); + + TNC->PortEnabled = 1; + + TNC->RTS = 1; +// TNC->DTR = 1; + } + else + { + // VCOM Port +#ifdef WIN32 + TNC->hDevice = BPQOpenSerialPort(TNC, &Errorval); +#else + TNC->hDevice = LinuxOpenPTY(TNC->PORTNAME); +#endif + if (TNC->hDevice != (HANDLE) -1) + { + if (TNC->NewVCOM == 0) + { + resp = BPQSerialIsCOMOpen(TNC->hDevice, &OpenCount); + TNC->PortEnabled = OpenCount; + } + + resp = BPQSerialSetCTS(TNC->hDevice); + resp = BPQSerialSetDSR(TNC->hDevice); + + TNC->CTS = 1; + TNC->DSR = 1; + } + else + { + Consoleprintf("TNC - Open Failed for Port %s", TNC->PORTNAME); + TNC->hDevice = 0; + } + } + + if (TNC->hDevice) + { + // Set up buffer pointers + + TNC->PUTPTR = TNC->GETPTR = &TNC->TOUSERBUFFER[0]; + TNC->RXCOUNT = 0; + TNC->CURSOR = &TNC->TONODEBUFFER[0]; // RESET MESSAGE START + TNC->MSGLEN = 0; + + TNC->VLSR = 0x20; + TNC->VMSR = 0x30; + +/* PUSH ECX + + MOV ESI,OFFSET UNPROTOCALL + CALL DECODECALLSTRING + + LEA EDI,UNPROTO[EBX] + MOV ECX,56 + REP MOVSB ; UNPROTO ADDR + + POP ECX +*/ + + APPLMASK = TNC->APPLICATION; + ApplNum = 1; + + while (APPLMASK && (APPLMASK & 1) == 0) + { + ApplNum++; + APPLMASK >>= 1; + } + + memcpy(TNC->MYCALL, &APPLCALLTABLE[ApplNum-1].APPLCALL_TEXT, 10); + + if (TNC->MYCALL[0] < '0') + memcpy(TNC->MYCALL, MYNODECALL, 10); + + strlop(TNC->MYCALL, ' '); + } + + TNC = TNC->Next; + } + +#ifdef LINBPQ + strcpy(pgm, "LinBPQ"); +#else + strcpy(pgm, "bpq32.exe"); +#endif + Consoleprintf("TNC Emulator Init Complete"); + + _beginthread(TNCBGThread,0,0); + + return TRUE; +} + +VOID CloseTNCEmulator() +{ + struct TNCDATA * TNC = TNC2TABLE; // malloc'ed + int i, Stream; + + TNCRUNNING = FALSE; + + while (TNC) + { + if (TNC->Mode == TNC2) + { + Stream = TNC->BPQPort; + + SetAppl(Stream, 0, 0); + Disconnect(Stream); + READCHANGE(Stream); // Prevent Initial *** disconnected + DeallocateStream(Stream); + } + else + { + for (i = 1; i <= TNC->HOSTSTREAMS; i++) + { + Stream = TNC->Channels[i]->BPQStream; + + SetAppl(Stream, TNC->APPLFLAGS, 0); + Disconnect(Stream); + READCHANGE(Stream); // Prevent Initial *** disconnected + DeallocateStream(Stream); + } + } + CloseCOMPort(TNC->hDevice); + + TNC = TNC->Next; + } +} + +VOID TNCTimer() +{ + // 100 Ms Timer + + struct TNCDATA * TNC = TNC2TABLE; + struct StreamInfo * channel; + int n; + + int NeedTrace = 0; + + while (TNC) + { + if (TNC->Mode != TNC2) + goto NotTNC2; + + NeedTrace |= TNC->TRACEFLAG; //SEE IF ANY PORTS ARE MONITORING + + // CHECK FOR PACTIMER EXPIRY AND CMDTIME + + if (TNC->CMDTMR) + { + TNC->CMDTMR--; + + if (TNC->CMDTMR == 0) + { + // CMDTMR HAS EXPIRED - IF 3 COMM CHARS RECEIVED, ENTER COMMAND MODE + + if (TNC->COMCOUNT == 3) + { + // 3 ESCAPE CHARS RECEIVED WITH GUARDS - LEAVE TRAN MODE + + SETCOMM00(TNC); + + goto TIM100; //DONT RISK TRANSTIMER AND CMDTIME FIRING AT ONCE + } + + TNC->CMDTMR = 0; // RESET COUNT + goto TIM100; + + } + } + + if (TNC->TRANSTIMER) + { + TNC->TRANSTIMER--; + if (TNC->TRANSTIMER == 0) + { + if (TNC->MSGLEN) // ?MESSAGE ALREADY SENT + SENDPACKET(TNC); + } + } +TIM100: + // CHECK FLOW CONTROL + + if ((TNC->VMSR & 0x20)) // ALREADY OFF? + { + CHECKCTS(TNC); // No, so check + } + + goto NextTNC; + +NotTNC2: + + for (n = 1; n <= TNC->HOSTSTREAMS; n++) + { + channel = TNC->Channels[n]; + + if (channel->CloseTimer) + { + channel->CloseTimer--; + if (channel->CloseTimer == 0) + Disconnect(channel->BPQStream); + } + } +NextTNC: + TNC = TNC->Next; + } + DOMONITORING(NeedTrace); +} + +/* +#ifndef WIN32 + +int TNCReadCOMBlock(HANDLE fd, char * Block, int MaxLength, int * err) +{ + int Length; + + *err = 0; + + Length = read(fd, Block, MaxLength); + + if (Length < 0) + { + if (errno != 11 && errno != 35) // Would Block + *err = errno; + + return 0; + } + + return Length; +} + +#endif +*/ + +void CheckForConnectStatusChange(struct TNCDATA * TNC); +void CheckForHostStatusChange(struct TNCDATA * TNC); +void CheckForDataFromHost(struct TNCDATA * TNC); +void CheckForDataFromTerminal(struct TNCDATA * TNC); + +int isTNCBusy(struct TNCDATA * TNC) +{ + // if using old VCOM check Q + +#ifdef WIN32 + + if (TNC->VCOM) + { + if (TNC->NewVCOM == 0) + { + int TXCount, RXCount; + + BPQSerialGetQCounts(TNC->hDevice, &RXCount, &TXCount); + + if (TXCount > 4096) + return TRUE; // Busy + + return FALSE; + } + + // Windows New VCOM cant check (I think!) + } + +#endif + + // + + return FALSE; + +} + +VOID TNCPoll() +{ + struct TNCDATA * TNC = TNC2TABLE; // malloc'ed + + // This logic had got very convoluted. This Tries + // to rationalize it + + while (TNC) + { + if (TNC->hDevice) + { + CheckForConnectStatusChange(TNC); + CheckForHostStatusChange(TNC); + CheckForDataFromHost(TNC); + CheckForDataFromTerminal(TNC); + } + + TNC = TNC->Next; + } +} + +void CheckForConnectStatusChange(struct TNCDATA * TNC) +{ +#ifdef WIN32 + if (TNC->VCOM && TNC->NewVCOM == 0) + { + // Can check if other end is connected + + int ConCount = 0; + + BPQSerialIsCOMOpen(TNC->hDevice, &ConCount); + + if (TNC->PortEnabled == 1 && ConCount == 0) + + // Connection has just closed - if connected, disconnect stream + + // This should close all streams on multistream port + + SessionControl(TNC->BPQPort, 2, 0); + + if (TNC->PortEnabled != ConCount) + { + TNC->PortEnabled = ConCount; + } + } +#endif +} + +void CheckForHostStatusChange(struct TNCDATA * TNC) +{ + if (TNC->Mode == KANTRONICS) + { + // Have to poll for Data and State changes + + int n, state, change; + struct StreamInfo * Channel; + + for (n = 1; n <= TNC->HOSTSTREAMS; n++) + { + Channel = TNC->Channels[n]; + + SessionState(Channel->BPQStream, &state, &change); + + if (change == 1) + { + if (state == 1) + + // Connected + + KANTConnected(TNC, Channel, n); + else + KANTDisconnected(TNC, Channel, n); + } + } + return; + } + + // ?? Should other modes check here ?? +} + +void CheckForDataFromHost(struct TNCDATA * TNC) +{ + unsigned int n; + int retval, more; + char TXMsg[1000]; + ULONG Read = 0; + + // I think we should check for space in TNC to Terminal Buffer + + if (TNC->RXCOUNT + 500 > TNCBUFFLEN) + return; + + // Only KANTRONICS and TNC2 check for data here + // DED and SCS check when polled by Host Program + + if (TNC->Mode == KANTRONICS) + { + // Have to poll for Data and State changes + + int n, len, count; + struct StreamInfo * Channel; + UCHAR Buffer[400]; + + for (n = 1; n <= TNC->HOSTSTREAMS; n++) + { + Channel = TNC->Channels[n]; + + do + { + if (TNC->RXCOUNT + 500 > TNCBUFFLEN) + return; + + GetMsg(Channel->BPQStream, &Buffer[3], &len, &count); + + if (len > 0) + { + // If a failure, set a close timer (for Airmail, etc) + + if (strstr(&Buffer[3], "} Downlink connect needs port number") || + strstr(&Buffer[3], "} Failure with ") || + strstr(&Buffer[3], "} Sorry, ")) + Channel->CloseTimer = CloseDelay * 10; + + else + Channel->CloseTimer = 0; // Cancel Timer + + if (TNC->MODE) + { + Buffer[0] = 'D'; + Buffer[1] = '1'; + Buffer[2] = n + '@'; + SendKISSData(TNC, Buffer, len+3); + } + else + SendDataToTNC(TNC, &Buffer[3], len); + } + } + while (0); //(count > 0); + } + return; + } + + if (TNC->Mode == SCS) + return; + + n = 0; + +getloop: + + TNC2GetChar(TNC, &retval, &more); + + if (retval != -1) + TXMsg[n++] = retval; + + if (more > 0 && n < 1000) goto getloop; + + if (n > 0) + WriteCOMBlock(TNC->hDevice, TXMsg, n); + + return; + +} +/* Where does this go ?? + +// We look for change on current RX Stream + + retval = TNCGetVMSR(TNC, TNC->TNC2Stream[TNC->RXStream], TRUE); + + if ((retval & 8) == 8) //' DCD (Connected) Changed + { + TNC->DCD = (retval & 128) / 128; + + if (TNC->DCD == 1) + BPQSerialSetDCD(TNC->hDevice); + else + BPQSerialClrDCD(TNC->hDevice); + } + + if ((retval & 1) == 1) //' CTS (Flow Control) Changed + { + TNC->CTS = (retval & 16) / 16; + + if (TNC->CTS == 1) + BPQSerialSetCTS(TNC->hDevice); + else + BPQSerialClrCTS(TNC->hDevice); + + } + + BPQSerialGetDTRRTS(TNC->hDevice,&ModemStat); + + + if ((ModemStat & 1) != TNC->DTR) + { + TNC->DTR=!TNC->DTR; + } + + if ((ModemStat & 2) >> 1 != TNC->RTS) + { + TNC->RTS=!TNC->RTS; + } + + TNC = TNC->Next; + continue; + } +#endif + { + // Real Port or Linux Virtual + + int Read, n; + int retval, more; + char TXMsg[500]; +#ifndef WIN32 + int err; + + // We can tell if partner has gone on PTY Pair - read returns 5 + + if (TNC->Mode == KANTRONICS || TNC->Mode == SCS) + Read = TNCReadCOMBlock(TNC->hDevice, &TNC->TOUSERBUFFER[TNC->RXBPtr], 1000 - TNC->RXBPtr, &err); + else + Read = TNCReadCOMBlock(TNC->hDevice, rxbuffer, 1000, &err); + + if (err) + { + if (TNC->PortEnabled) + { + TNC->PortEnabled = FALSE; + DisableAppl(TNC); + Debugprintf("Device %s closed", TNC->PORTNAME); + } + } + else + TNC->PortEnabled = TRUE; + +#else + if (TNC->Mode == KANTRONICS || TNC->Mode == SCS) + Read = ReadCOMBlock(TNC->hDevice, &TNC->TOUSERBUFFER[TNC->RXBPtr], 1000 - TNC->RXBPtr); + else + Read = ReadCOMBlock(TNC->hDevice, rxbuffer, 1000); +#endif + + if (Read) + { + if (TNC->Mode == TNC2) + { + for (n = 0; n < Read; n++) + TNC2PutChar(TNC, rxbuffer[n]); + } + else if (TNC->Mode == DED) + { + for (n = 0; n < Read; n++) + TfPut(TNC, rxbuffer[n]); + } + else if (TNC->Mode == KANTRONICS) + { + TNC->RXBPtr += Read; + ProcessPacket(TNC, TNC->TOUSERBUFFER, TNC->RXBPtr); + } + else if (TNC->Mode == SCS) + { + TNC->RXBPtr += Read; + ProcessSCSPacket(TNC, TNC->TOUSERBUFFER, TNC->RXBPtr); + } + } + + n=0; + + getloopR: + + TNC2GetChar(TNC, &retval, &more); + + if (retval != -1) + TXMsg[n++] = retval; + + if (more > 0 && n < 500) goto getloopR; + + if (n > 0) + + { + resp = WriteCOMBlock(TNC->hDevice, TXMsg, n); + } + } + TNC = TNC->Next; + } +} +*/ + + +void CheckForDataFromTerminal(struct TNCDATA * TNC) +{ + unsigned int n; + char rxbuffer[1000]; + ULONG Read = 0, resp; + + if (TNC->Mode == KANTRONICS) + resp = GetDataFromTNC(TNC, &TNC->TOUSERBUFFER[TNC->RXBPtr], 1000 - TNC->RXBPtr, &Read); + + else if (TNC->Mode == SCS) + resp = GetDataFromTNC(TNC, &TNC->FROMUSERBUFFER[TNC->FROMUSERLEN], TNCBUFFLEN - TNC->FROMUSERLEN, &Read); + + else + resp = GetDataFromTNC(TNC, rxbuffer, 1000, &Read); + + if (Read) + { + if (TNC->Mode == TNC2) + { + for (n = 0; n < Read; n++) + TNC2PutChar(TNC, rxbuffer[n]); + } + else if (TNC->Mode == DED) + { + for (n = 0; n < Read; n++) + TfPut(TNC, rxbuffer[n]); + } + else if (TNC->Mode == KANTRONICS) + { + TNC->RXBPtr += Read; + ProcessPacket(TNC, TNC->TOUSERBUFFER, TNC->RXBPtr); + } + else if (TNC->Mode == SCS) + { + TNC->FROMUSERLEN += Read; + ProcessSCSPacket(TNC, TNC->FROMUSERBUFFER, TNC->FROMUSERLEN); + } + } +} + + +int APIENTRY SetTraceOptionsEx(int mask, int mtxparam, int mcomparam, int monUIOnly); + + +VOID DOMONITORING(int NeedTrace) +{ + // IF ANY PORTS HAVE MONITOR ENABLED, SET MONITOR BIT ON FIRST PORT + + struct TNCDATA * TNC = TNC2TABLE; // malloc'ed + int Tracebit = 0, len, count, n; + time_t Stamp; + ULONG SaveMMASK = MMASK; + BOOL SaveMTX = MTX; + BOOL SaveMCOM = MCOM; + BOOL SaveMUI = MUIONLY; + + if (NeedTrace) + Tracebit = 0x80; + + if (TNC->CONOK) + SetAppl(TNC->BPQPort, TNC->APPLFLAGS | Tracebit, TNC->APPLICATION); + else + SetAppl(TNC->BPQPort, TNC->APPLFLAGS | Tracebit, 0); + + Stamp = GetRaw(TNC->BPQPort, (char *)&MONITORDATA, &len, &count); + + if (len == 0) + return; + + len = DecodeFrame(&MONITORDATA, MONBUFFER, Stamp); + + while (TNC) + { + if (TNC->Mode == TNC2 && TNC->TRACEFLAG) + { + SetTraceOptionsEx(TNC->MMASK, TNC->MTX, TNC->MCOM, 0); + len = IntDecodeFrame(&MONITORDATA, MONBUFFER, Stamp, TNC->MMASK, FALSE, FALSE); +// printf("%d %d %d %d %d\n", len, MMASK, MTX, MCOM, MUIONLY); + SetTraceOptionsEx(SaveMMASK, SaveMTX, SaveMCOM, SaveMUI); + + if (len) + { + for (n = 0; n < len; n++) + { + PUTCHARINBUFFER(TNC, MONBUFFER[n]); + } + } + } + TNC=TNC->Next; + } +} + + +VOID TNC2PutChar(struct TNCDATA * TNC, int Char) +{ + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + if (TNCStream->MODEFLAG & COMMAND) + goto KEYB06C; // COMMAND MODE - SKIP TRANS TEST + + if (TNCStream->MODEFLAG & TRANS) + goto KEYB06T; // TRANS MODE + + // CONV MODE - SEE IF CPACTIME ON + + if (TNC->CPACTIME) + TNC->TRANSTIMER = TRANSDELAY; + + goto KEYB06; // PROCESS CHAR + +KEYB06T: + +// Transparent Mode - See if Escape Sequence Received (3 esc chars, with guard timer) + +// CHECK FOR COMMAND CHAR IF CMDTIME EXPIRED OR COMAND CHAR ALREADY RECEIVED + + if (TNC->COMCOUNT) + goto KBTRN3; // ALREADY GOT AT LEAST 1 + + if (TNC->CMDTMR) + goto KBTRN5; // LESS THAN CMDTIME SINCE LAST CHAR + +KBTRN3: + + if (Char != TNC->COMCHAR) + { + TNC->COMCOUNT = 0; + goto KBTRN5; // NOT COMMAND CHAR + } + + TNC->COMCOUNT++; + +KBTRN5: + + TNC->CMDTMR = TNC->CMDTIME; // REPRIME ESCAPE TIMER + + TNC->TRANSTIMER = TRANSDELAY; + + KBNORM(TNC, Char); + return; // TRANSPARENT MODE + +KEYB06: + +// STILL JUST CONV MODE + + if (Char != TNC->SENDPAC) + goto NOTSENDPAC; + +// SEND PACKET CHAR - SHOUD WE SEND IT? + + TNC->TRANSTIMER = 0; + + if (TNC->CRFLAG) + KBNORM(TNC, Char); // PUT CR IN BUFFER + + SENDPACKET(TNC); + return; + + +NOTSENDPAC: +KEYB06C: + + // COMMAND OR CONV MODE + + // Check for Escaped + + if (TNC->InEscape) + { + TNC->InEscape = 0; + KBNORM(TNC, Char); // Process as normal chars + return; + } + + if (TNC->InStreamSW) + { + TNC->InStreamSW = 0; + + if (Char != TNC->StreamSW) + { + // Switch TX Stream if valid + + int n; + + if (TNC->ECHOFLAG) + KBECHO(TNC, Char); + + if (TNC->LCStream) + Char = toupper(Char); + + n = Char - 'A'; + + if (n >= 0 && TNC->TNC2Stream[n]) + TNC->TXStream = n; + + return; + } + } + + if (Char == TNC->PASSCHAR) + { + TNC->InEscape = 1; + return; + } + + + + if (TNC->StreamSW && Char == TNC->StreamSW) + { + TNC->InStreamSW = 1; + + if (TNC->ECHOFLAG) + KBECHO(TNC, Char); + + return; + } + + if (Char < 32) // control + { + if (Char == 10 && TNC->LFIGNORE) + return; + + if (Char == 8) + { + if (TNC->MSGLEN == 0) + return; + + TNC->MSGLEN--; + TNC->CURSOR--; + + if (TNC->ECHOFLAG) + { + KBECHO(TNC, Char); // Delete char from display + KBECHO(TNC, ' '); + KBECHO(TNC, Char); + } + return; + } + + if (Char == 26) // Ctrl/Z + { + KBNORM(TNC, Char); // FOR MBX TERMINATOR + return; + } + + if (Char == TNC->COMCHAR) + { + SETCOMMANDMODE(TNC); + return; + } + + if (TNCStream->MODEFLAG & COMMAND) + { + if (Char == 0x14) // CTRL/T + { + TNC->TRACEFLAG ^= 1; + return; + } + + if (Char == 13) + { + KBNORM(TNC, 13); // PUT CR IN BUFFER + SENDPACKET(TNC); + return; + } + } + KBNORM(TNC, Char); // Process others as normal chars + } KBNORM(TNC, Char); +} + +VOID KBNORM(struct TNCDATA * TNC, int Char) +{ + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + if (TNC->MSGLEN > 256) + goto TOOLONG; // PROTECT BUFFER + + *(TNC->CURSOR++) = Char; + TNC->MSGLEN++; + +TOOLONG: + + if (TNC->ECHOFLAG) + KBECHO(TNC, Char); + + if (TNC->MSGLEN < TNCStream->TPACLEN) + return; + +// DONT APPLY PACLEN IN COMMAND MODE + + if (TNCStream->MODEFLAG & COMMAND) + return; + + SENDPACKET(TNC); // Send what we have +} + + +VOID SETCOMMANDMODE(struct TNCDATA * TNC) +{ + if (TNC->MSGLEN) + SENDPACKET(TNC); + + SETCOMM00(TNC); +} + +VOID SETCOMM00(struct TNCDATA * TNC) +{ + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + TNCStream->MODEFLAG |= COMMAND; // BACK TO COMMAND MODE + TNCStream->MODEFLAG &= ~(CONV+TRANS); + TNC->TRANSTIMER = 0; // CANCEL TRANS MODE SEND TIMER + TNC->AUTOSENDFLAG = 0; // IN CASE ALREADY SET + + CheckForStreamChange(TNC, TNC->TXStream); // Send Stream Switched Message if changed + + SENDREPLY(TNC, CMDMSG, 4); + + TNC->CURSOR = &TNC->TONODEBUFFER[0]; // RESET MESSAGE START + TNC->MSGLEN = 0; +} + + + +VOID SENDPACKET(struct TNCDATA * TNC) +{ + // SEE IF COMMAND STATE + + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + int Stream = 0; // Unprooto + + if (TNCStream->MODEFLAG & COMMAND) + { + TNCCOMMAND(TNC); // COMMAND TO TNC + TNC->CURSOR = &TNC->TONODEBUFFER[0]; // RESET MESSAGE START + TNC->MSGLEN = 0; + return; + } + + // IF CONNECTED, SEND TO L4 (COMMAND HANDLER OR DATA), + // OTHERWISE SEND AS AN UNPROTO FRAME (TO ALL PORTS) + + + if (TNCStream->VMSR & 0x80) // CONNECTED? + Stream = TNCStream->BPQPort; + + SendMsg(Stream, TNC->TONODEBUFFER, TNC->MSGLEN); + + TNC->CURSOR = &TNC->TONODEBUFFER[0]; // RESET MESSAGE START + TNC->MSGLEN = 0; + + CHECKCTS(TNC); // SEE IF NOW BUSY + + return; +} + +VOID KBECHO(struct TNCDATA * TNC, int Char) +{ + PUTCHARINBUFFER(TNC, Char); +} + +VOID TNCCOMMAND(struct TNCDATA * TNC) +{ + // PROCESS COMMAND TO TNC CODE + + char * ptr, * ptr1, * ptr2; + int n; + CMDX * CMD; + + *(--TNC->CURSOR) = 0; + + + strcat(TNC->TONODEBUFFER, " "); + + ptr = strchr(TNC->TONODEBUFFER, ' '); + + if (ptr) + { + // convert command to upper case, leave tail + + *ptr = 0; + _strupr(TNC->TONODEBUFFER); + *ptr = ' '; + } + + if (_memicmp(ptr, " switch", 7) == 0) + _strupr(ptr); // Special Case + + ptr1 = &TNC->TONODEBUFFER[0]; // + + n = 10; + + while ((*ptr1 == ' ' || *ptr1 == 0) && n--) + ptr1++; // STRIP LEADING SPACES and nulls (from keepalive) + + if (n == -1) + { + // Null command + + SENDREPLY(TNC, CMDMSG, 4); + return; + } + + ptr2 = ptr1; // Save + + CMD = &COMMANDLIST[0]; + n = 0; + + for (n = 0; n < NUMBEROFTNCCOMMANDS; n++) + { + int CL = CMD->CMDLEN; + + ptr1 = ptr2; + + // ptr1 is input command + + if (memcmp(CMD->String, ptr1, CL) == 0) + { + // Found match so far - check rest + + char * ptr2 = &CMD->String[CL]; + + ptr1 += CL; + + if (*(ptr1) != ' ') + { + while(*(ptr1) == *ptr2 && *(ptr1) != ' ') + { + ptr1++; + ptr2++; + } + } + + if (*(ptr1) == ' ') + { + ptr1++; // Skip space + + CMD->CMDPROC(TNC, ptr1, CMD); + SENDREPLY(TNC, CMDMSG, 4); + + return; + } + } + + CMD++; + + } + + SENDREPLY(TNC, WHATMSG, 8); + +} + +/* +; +UNPROTOCMD: +; +; EXTRACT CALLSIGN STRING +; + CMP BYTE PTR [ESI],20H + JE UNPROTODIS + + CMP BYTE PTR [ESI],"*" + JE CLEARUNPROTO + + CALL DECODECALLSTRING ; CONVERT TO AX25 FORMAT + + JZ UNPROTOOK + + JMP TNCDUFF + +CLEARUNPROTO: + + LEA EDI,UNPROTO[EBX] + MOV AL,0 + MOV ECX,63 + REP STOSB ; COPY IN + +UNPROTODIS: + + MOV AL,1 + CALL DISPLAYUNPROTO ; DISPLAY CURRENT SETTING + JMP SENDOK + +UNPROTOOK: + + PUSH ESI + MOV AL,0 + CALL DISPLAYUNPROTO ; DISPLAY OLD STRRING + POP ESI + + LEA EDI,UNPROTO[EBX] + MOV ECX,63 + REP MOVSB ; COPY IN +CONMODE: + JMP SENDOK +;CONMODE: + JMP KBRET + +DISPLAYUNPROTO: +; + PUSH EAX + + MOV ESI,OFFSET UNPROT + MOV ECX,8 + CALL PUTSTRINGINBUFFER + + MOV ESI,OFFSET WAS ; DISPLAY "was" + MOV ECX,5 + + POP EAX + OR AL,AL + JZ DISPU00 ; NO + + MOV ECX,1 ; LEAVE OUT "WAS" + +DISPU00: + + CALL PUTSTRINGINBUFFER + + LEA ESI,UNPROTO[EBX] + CMP BYTE PTR [ESI],40H + JBE DISPUPRET + + CALL CONVFROMAX25 + + PUSH ESI + + MOV ESI,OFFSET NORMCALL + + CALL PUTSTRINGINBUFFER + + POP ESI + + CMP BYTE PTR [ESI],0 + JE DISPUPRET + + PUSH ESI + + MOV ESI,OFFSET VIA + MOV ECX,5 + CALL PUTSTRINGINBUFFER + + POP ESI + +DISPUPLOOP: + + CALL CONVFROMAX25 + + PUSH ESI + MOV ESI,OFFSET NORMCALL + INC ECX + CALL PUTSTRINGINBUFFER + POP ESI + + CMP BYTE PTR [ESI],0 + JNE DISPUPLOOP + + +DISPUPRET: + MOV AL,0DH + CALL PUTCHARINBUFFER + RET + + +BTEXT: +; + CMP BYTE PTR [ESI],20H + JE BTDIS +; + PUSH ESI + MOV AL,0 + CALL DISPLAYBT ; DISPLAY OLD STRING + POP ESI + + MOV EDI,OFFSET BTEXTFLD + MOV ECX,255 +BTLOOP: + LODSB + STOSB + CMP ESI,CMDENDADDR ; END? + JE BTEND + + LOOP BTLOOP +BTEND: + XOR AL,AL + STOSB ; NULL ON END +; +; SET UP TO SEND IT AS A UI +; + MOV ECX,EDI + MOV ESI,OFFSET BTHDDR + SUB ECX,ESI + MOV MSGLENGTH[ESI],CX +; +; PASS TO SWITCH +; + MOV ESI,OFFSET BTEXTFLD + SUB ECX,6 ; DONT NEED HEADER + + MOV AH,12 ; UPDATE FUNCTIONS + MOV DX,1 ; UPDATE BT + + CALL NODE ; PASS TO NODE + + JMP SENDOK + +BTDIS: + MOV AL,1 + CALL DISPLAYBT ; DISPLAY CURRENT SETTING + JMP SENDOK + + +DISPLAYBT: +; + PUSH EAX + + MOV ESI,OFFSET BTCMD + MOV ECX,8 + CALL PUTSTRINGINBUFFER + + MOV ESI,OFFSET WAS ; DISPLAY "was" + MOV ECX,5 + + POP EAX + OR AL,AL + JZ DISPBT00 ; NO + + MOV ECX,1 ; LEAVE OUT "WAS" + +DISPBT00: + CALL PUTSTRINGINBUFFER + + MOV ESI,OFFSET BTEXTFLD +DISPBT10: + LODSB + OR AL,AL + JZ DISPBT20 + + CALL PUTCHARINBUFFER + + JMP DISPBT10 + +DISPBT20: + MOV AL,0DH + CALL PUTCHARINBUFFER + RET + + +*/ + +VOID DOCONMODECHANGE(struct TNCDATA * TNC, int Stream) +{ + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[Stream]; + + TNCStream->VMSR |= 0x88; // SET CONNECTED + + // IF NOMODE IS ON LEAVE IN TNC COMMAND MODE, ELSE PUT INTO CONV MODE + // (MAY NEED TO IMPLEMENT CONMODE SOMETIME) + + if (TNC->NOMODE) + return; + + TNCStream->MODEFLAG |= CONV; // INTO CONVERSE MODE + TNCStream->MODEFLAG &= ~(COMMAND+TRANS); +} + +VOID SENDREPLY(struct TNCDATA * TNC, char * Msg, int Len) +{ + int n = 0; + + CheckForStreamChange(TNC, TNC->TXStream); // Send Stream Switched Message if changed + + for (n= 0; n < Len; n++) + { + PUTCHARINBUFFER(TNC, Msg[n]); + } +} + + +VOID SEND_CONNECTED(struct TNCDATA * TNC, int ToStream) +{ + // SEND TAPR-STYLE *** CONNECTED TO CURRENT PORT + + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[ToStream]; + + int len; + char Response[128]; + char Call[11] = ""; + int paclen, dummy; + BPQVECSTRUC * SESS; + TRANSPORTENTRY * L4 = NULL; + int stream; + + GetConnectionInfo(TNCStream->BPQPort, Call, &dummy, &dummy, &paclen, &dummy, &dummy); + + if (paclen) + TNCStream->TPACLEN = paclen; + + if (TNCStream->MODEFLAG & TRANS) + return; //NOT IF TRANSPARENT + + strlop(Call, ' '); + + strcpy(TNCStream->RemoteCall, Call); + + CheckForStreamChange(TNC, ToStream); // Send Stream Switched Message if changed + + if (TNC->CBELL) + len = sprintf(Response, "%s%s%c\r", CONMSG, Call, 7); // Add BELL char + else + len = sprintf(Response, "%s%s\r", CONMSG, Call); + + SENDREPLY(TNC, Response, len); + + // If incoming session Send CTEXT if set + + stream = TNCStream->BPQPort; + stream--; // API uses 1 - 64 + + if (stream < 0 || stream > 63) + return; + + SESS = &BPQHOSTVECTOR[stream]; + + if (SESS && SESS->HOSTSESSION) + L4 = SESS->HOSTSESSION; + + if (L4 && (L4->L4CIRCUITTYPE & DOWNLINK)) + { + if (TNC->CMSG && TNC->CTEXT[0]) + { + // Add CTEXT + int n; + char Msg[256]; + + n = sprintf(Msg, "%s\r", TNC->CTEXT); + SendMsg(TNCStream->BPQPort, Msg, n); + } + + // if CHECK_FOR_ESC set in applflags send "^d to disconnect msg + + if ((TNC->APPLFLAGS & CHECK_FOR_ESC)) // If incoming session + { + char Msg[] = "Send ^D to disconnect\r"; + + SendMsg(TNCStream->BPQPort, Msg, (int)strlen(Msg)); + } + } +} + +VOID PUTCHARINBUFFER(struct TNCDATA * TNC, int Char) +{ + // CALLED BY L4 CODE TO PASS DATA TO VIRTUAL TNC + ; + if (TNC->RXCOUNT >= TNCBUFFLEN) + { + // OVERRUN - LOSE IT + + TNC->VLSR |= 2; // SET OVERRUN ERROR + return; + } + + TNC->VLSR &= ~2; // CLEAR OVERRRUN + + *(TNC->PUTPTR++) = Char; + TNC->RXCOUNT++; + + if (TNC->PUTPTR == &TNC->TOUSERBUFFER[TNCBUFFLEN]) + TNC->PUTPTR = &TNC->TOUSERBUFFER[0]; + + if(TNC->RXCOUNT > TNCBUFFLEN-300) // ALLOW FOR FULL PACKET + { + // BUFFER GETTING FULL - DROP RTS/DTR + + TNC->RTSFLAG |= 1; // SET BUSY + } + + if (Char == 13 && TNC->AUTOLF) + PUTCHARINBUFFER(TNC, 10); // Add LF +} + + +VOID CHECKCTS(struct TNCDATA * TNC) +{ + // SEE IF CROSS-SESSION STILL BUSY + + if (RXCount(TNC->BPQPort) > 4) + { + // Busy + + if ((TNC->VMSR & 0x10) == 0) // ALREADY OFF? + return; // No Change + + TNC->VMSR &= 0xef; // Drop CTS + TNC->VMSR |= 1; // Delta bit + return; + } + + // Not busy + + if (TNC->VMSR & 0x10) // ALREADY ON? + return; // No Change + + TNC->VMSR |= 0x11; // CTS AND DELTA CTS +} + + + +VOID CONNECTTONODE(struct TNCDATA * TNC) +{ + char AXCALL[7]; + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + int SaveAuthProg = AuthorisedProgram; + + AuthorisedProgram = 1; + SessionControl(TNCStream->BPQPort, 1, TNC->APPLICATION); + AuthorisedProgram = SaveAuthProg; + + ConvToAX25(TNC->MYCALL, AXCALL); + ChangeSessionCallsign(TNCStream->BPQPort, AXCALL); + + // Set default Paclen + + TNCStream->TPACLEN = TNC->TPACLEN; +} + + +VOID GETDATA(struct TNCDATA * TNC) +{ + // I'm sure this should only be called for TNC2 devices + + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + int state, change, InputLen, count, n, i; + char InputBuffer[512]; + + // LOOK FOR STATUS CHANGE + + for (i = 0; i < TNC->HOSTSTREAMS; i++) + { + TNCStream = TNC->TNC2Stream[i]; + + LocalSessionState(TNCStream->BPQPort, &state, &change, TRUE); + + if (change == 1) + { + if (state == 1) // Connected + { + SEND_CONNECTED(TNC, i); + DOCONMODECHANGE(TNC, i); // SET CONNECTED AND CHANGE MODE IF NEEDED + } + else + { + TNCStream->MODEFLAG |= COMMAND; + TNCStream->MODEFLAG &= ~(CONV+TRANS); + + TNCStream->VMSR &= 0x7F; // DROP DCD + TNCStream->VMSR |= 8; // DELTA DCD + + CheckForStreamChange(TNC, i); // Send Stream Switched Message if changed + SENDREPLY(TNC, DISCONNMSG, 18); + } + } + else + { + // No Change + + // VERIFY CURRENT STATE + + if (state == 1) // Connected + { + // SWITCH THINKS WE ARE CONNECTED + + if ((TNCStream->VMSR & 0x80) == 0) + { + // TNC Doesn't + + SEND_CONNECTED(TNC, i); + DOCONMODECHANGE(TNC, i); // SET CONNECTED AND CHANGE MODE IF NEEDED + } + } + else + { + // SWITCH THINKS WE ARE DISCONNECTED + + if (TNCStream->VMSR & 0x80) + { + // We Disagree, so force off + + TNCStream->MODEFLAG |= COMMAND; + TNCStream->MODEFLAG &= ~(CONV+TRANS); + + TNCStream->VMSR &= 0x7F; // DROP DCD + TNCStream->VMSR |= 8; // DELTA DCD + + CheckForStreamChange(TNC, i); // Send Stream Switched Message if changed + SENDREPLY(TNC, DISCONNMSG, 18); + } + } + } + + // SEE IF ANYTHING QUEUED + + if (TNC->RTSFLAG & 1) + continue; + + GetMsg(TNCStream->BPQPort, InputBuffer, &InputLen, &count); + + if (InputLen == 0) + continue; + + CheckForStreamChange(TNC, i); // Send Stream Switched Message if changed + + // if CHECK_FOR_ESC set in APPLFLAGS looks for Disconnect Escape + + if (TNC->APPLFLAGS & CHECK_FOR_ESC) + { + // look for ^D (or ^d) + + if (InputLen == 3) + { + if (_memicmp(InputBuffer, "^D\r", 3) == 0) + { + Disconnect(TNCStream->BPQPort); + continue; + } + } + } + + for (n = 0; n < InputLen; n++) + { + char c = InputBuffer[n]; + + if (TNC->StreamDbl && c == TNC->StreamSW) + PUTCHARINBUFFER(TNC, TNC->StreamSW); + + PUTCHARINBUFFER(TNC, c); + } + } +} + +// DED Mode Support + +unsigned char PARAMREPLY[]="* 0 0 64 10 4 4 10 100 18000 30 2 0 2\r\n"; + +#define PARAMPORT PARAMREPLY[2] + +#define LPARAMREPLY 39 + +unsigned char BADCMDREPLY[]="\x2" "INVALID COMMAND\x0"; + +#define LBADCMDREPLY 17 //sizeof BADCMDREPLY + +unsigned char DATABUSYMSG[]="\x2" "TNC BUSY - LINE IGNORED\x0"; +#define LDATABUSY 25 + +unsigned char BADCONNECT[]="\x2" "INVALID CALLSIGN\x0"; +#define LBADCONNECT 18 + +unsigned char BUSYMSG[]="BUSY fm SWITCH\x0"; + +//unsigned char CONSWITCH[]="\x3" "(1) CONNECTED to \x0"; + +unsigned char DEDSWITCH[]="\x1" "0:SWITCH \x0"; +#define LSWITCH 14 + +unsigned char NOTCONMSG[]="\x1" "CHANNEL NOT CONNECTED\x0"; +#define LNOTCON 23 + +unsigned char ALREADYCONMSG[]="You are already connected on another port\r"; +#define ALREADYLEN 45 + + +byte * EncodeCall(byte * Call); +VOID SENDENFORCINGPACLEN(struct StreamInfo * Channel, char * Msg, int Len); +VOID SENDCMDREPLY(struct TNCDATA * TNC, char * Msg, int Len); +int APIENTRY SetTraceOptionsEx(int mask, int mtxparam, int mcomparam, int monUIOnly); +int DOCOMMAND(struct TNCDATA * conn); +int PROCESSPOLL(struct TNCDATA * TNC, struct StreamInfo * Channel); + + + + +VOID PUTSTRING(struct TNCDATA * conn, UCHAR * Msg) +{ + int len = (int)strlen(Msg); + + while (len) + { + *(conn->PUTPTR++) = *(Msg++); + + if (conn->PUTPTR == &conn->TOUSERBUFFER[TNCBUFFLEN]) + conn->PUTPTR = (UCHAR *)&conn->TOUSERBUFFER; + + conn->RXCOUNT++; + + len--; + } +} + + +int PUTCHARx(struct TNCDATA * conn, UCHAR c) +{ + *(conn->PUTPTR++) = c; + + if (conn->PUTPTR == &conn->TOUSERBUFFER[TNCBUFFLEN]) + conn->PUTPTR = (UCHAR *)&conn->TOUSERBUFFER; + + conn->RXCOUNT++; + + return 0; +} + + + +VOID DisableAppl(struct TNCDATA * TNC) +{ + int i, Stream; + + for (i = 1; i <= TNC->HOSTSTREAMS; i++) + { + Stream = TNC->Channels[i]->BPQStream; + + SetAppl(Stream, TNC->APPLFLAGS, 0); + Disconnect(Stream); + READCHANGE(Stream); // Prevent Initial *** disconnected + } +} + +VOID EnableAppl(struct TNCDATA * TNC) +{ + int i; + + for (i = 1; i <= TNC->HOSTSTREAMS; i++) + { + SetAppl(TNC->Channels[i]->BPQStream, TNC->APPLFLAGS, TNC->APPLICATION); + } +} + +BOOL TfPut(struct TNCDATA * TNC, UCHAR character) +{ + struct StreamInfo * Channel; + TRANSPORTENTRY * L4 = NULL; + + if (!TNC->MODE) + goto CHARMODE; + +// HOST MODE + + if (TNC->HOSTSTATE == 0) + { + TNC->MSGCHANNEL = character; + TNC->HOSTSTATE++; + return TRUE; + } + + if (TNC->HOSTSTATE == 1) + { + TNC->MSGTYPE = character; + TNC->HOSTSTATE++; + return TRUE; + } + + if (TNC->HOSTSTATE == 2) + { + TNC->MSGCOUNT = character; + TNC->MSGLENGTH = character; + TNC->MSGCOUNT++; + TNC->MSGLENGTH++; + TNC->HOSTSTATE++; + + TNC->DEDCURSOR = &TNC->DEDTXBUFFER[0]; + return TRUE; + } + +// RECEIVING COMMAND/DATA + + *(TNC->DEDCURSOR++) = character; + + TNC->MSGCOUNT--; + + if (TNC->MSGCOUNT) + return TRUE; // MORE TO COME + + TNC->HOSTSTATE=0; + + if (TNC->MSGCHANNEL <= TNC->HOSTSTREAMS) + Channel = TNC->Channels[TNC->MSGCHANNEL]; + else + Channel = TNC->Channels[1]; + + DEDPROCESSHOSTPACKET(Channel, TNC); + + TNC->HOSTSTATE = 0; + + return TRUE; + + +CHARMODE: + + if (character == 0x11) return TRUE; + + if (character == 0x18) + { + // CANCEL INPUT + + TNC->CURSOR = (UCHAR *)&TNC->TONODEBUFFER; + + return(TRUE); + } + + *(TNC->CURSOR++) = character; + + if (character == 1 && (TNC->CURSOR > &TNC->TONODEBUFFER[4]) && *(TNC->CURSOR - 2) == 1 && *(TNC->CURSOR - 3) == 1) + { + // Looks like a resync request - Appl thinks we are in host mode + + TNC->MODE = 1; + TNC->CURSOR = (UCHAR *)&TNC->TONODEBUFFER; + EnableAppl(TNC); + + return(TRUE); + } + + + if (TNC->CURSOR == &TNC->TONODEBUFFER[300]) + TNC->CURSOR--; + + if (character == 0x0d) + { + // PROCESS COMMAND (UNLESS HOST MODE) + + *(TNC->CURSOR++) = 0; + + DOCOMMAND(TNC); + } + return TRUE; +} + + +int DEDPROCESSHOSTPACKET(struct StreamInfo * Channel, struct TNCDATA * TNC) +{ + UCHAR * TXBUFFERPTR; + int i; + int Work; + char WorkString[256]; + int State, Change, Count; + TRANSPORTENTRY * L4 = NULL; + unsigned char * MONCURSOR=0; + int SaveAuthProg = 0; + + TXBUFFERPTR = &TNC->DEDTXBUFFER[0]; + + if (Channel->Chan_TXQ == (UCHAR *)(ptrdiff_t) -1) + { + Channel->Chan_TXQ = 0; + } + + if (TNC->MSGTYPE != 0) + goto NOTDATA; + + goto HOSTDATAPACKET; + +//HOSTCMDS: +// DD 'G','I', 'J', 'C', 'D', 'L', '@', 'Y', 'M' +// DD POLL,ICMD,JCMD,CCMD,DCMD,LCMD,ATCOMMAND,YCMD,HOSTMON + +NOTDATA: + + if (TNC->DEDTXBUFFER[0] == 1) + { + // recovering + +// if (!TNC->Recovering) +// { +// sprintf(msg, "Port %d DED Recovery started\n", TNC->ComPort); +// OutputDebugString(msg); +// TNC->Recovering = TRUE; +// } + } + else + { + // Not recovery + +// if (TNC->Recovering) +// { +// sprintf(msg, "Port %d DED Recovery completed\n", TNC->ComPort); +// OutputDebugString(msg); +// TNC->Recovering = FALSE; +// } + + } + + if (TNC->DEDTXBUFFER[0] == 1) + goto DUFFHOSTCMD; + +// sprintf(msg,"DED CMD: Port %d CMD %c MSGCHANNEL %d\n", TNC->ComPort, TNC->TONODEBUFFER[0], MSGCHANNEL); +// OutputDebugString(msg); + + if (_memicmp(TNC->DEDTXBUFFER, "QRES", 4 == 0)) + goto SENDHOSTOK; + + switch (toupper(TNC->DEDTXBUFFER[0])) + { + case 1: + + goto DUFFHOSTCMD; + + case 'G': + + goto POLL; + + case 'I': + goto ICMD; + + case 'J': + + TNC->MODE = TNC->DEDTXBUFFER[5] & 1; + + if (TNC->MODE) + EnableAppl(TNC); + else + DisableAppl(TNC); + + goto SENDHOSTOK; + + case 'C': + goto CCMD; + + case 'D': + goto DCMD; + + case 'L': + goto LCMD; + + case '@': + goto ATCOMMAND; + + case 'Y': + goto YCMD; + + case 'E': + goto ECMD; + + case 'M': + + if (TNC->DEDTXBUFFER[1] == 'N') + goto DISABLEMONITOR; + + goto ENABLEMONITOR; + + case 'K': + case 'O': + goto SENDHOSTOK; + + case 'V': // Vesrion + + PUTCHARx(TNC, TNC->MSGCHANNEL); + PUTCHARx(TNC, 1); + PUTSTRING(TNC, "DSPTNC Firmware V.1.3a, (C) 2005-2010 SCS GmbH & Co."); + PUTCHARx(TNC, 0); + + return TRUE; + + default: + goto SENDHOSTOK; + +ATCOMMAND: + + if (TNC->DEDTXBUFFER[1] == 'B') + goto BUFFSTAT; + + if (TNC->DEDTXBUFFER[1] == 'M') + goto BUFFMIN; + +// Not recognised + + PUTCHARx(TNC, TNC->MSGCHANNEL); + + for (i=0; i < LBADCMDREPLY; i++) + { + PUTCHARx(TNC, BADCMDREPLY[i]); + } + + return TRUE; + + +BUFFMIN: + + Work = MINBUFFCOUNT; + goto BUFFCOMM; + +BUFFSTAT: + + Work = QCOUNT; + +BUFFCOMM: + + PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL + PUTCHARx(TNC, 1); + + sprintf(WorkString, "%d", Work); // Buffer count + + PUTSTRING(TNC, WorkString); + + PUTCHARx(TNC, 0); + return TRUE; + +ICMD: + + { + char * Call = &TNC->DEDTXBUFFER[1]; + int len; + char Reply[80]; + char ReplyCall[10]; + + + if (TNC->MSGLENGTH > 2) + { + // Save callsign + + TNC->DEDTXBUFFER[TNC->MSGLENGTH] = 0; + + if (*Call == ' ') + *Call++; // May have leading space + + _strupr(Call); + + memset(Channel->MYCall, ' ', 10); + memcpy(Channel->MYCall, Call, (int)strlen(Call)); + + Debugprintf("DED Host I chan %d call %s", TNC->MSGCHANNEL, Call); + + strcpy(ReplyCall, Call); + +/* + if (TNC->MSGCHANNEL == 0) // if setting zero, copy to all others + { + int i; + + for (i = 1; i <= TNC->HOSTSTREAMS; i++) + { + memcpy(TNC->Channels[i]->MYCall, TNC->Channels[0]->MYCall, 10); + Debugprintf("DED Capy to chan %d call %s", i, Channel->MYCall); + } + } +*/ + + + + + + + } + else + { + memcpy(ReplyCall, Channel->MYCall, 10); + strlop(ReplyCall, ' '); + } + + len = sprintf(Reply, "\x2%s", ReplyCall); + + SENDCMDREPLY(TNC, Reply, len + 1); // include the null + + return TRUE; + } +ECMD: + + goto SENDHOSTOK; + +DUFFHOSTCMD: + + PUTCHARx(TNC, TNC->MSGCHANNEL); + + for (i=0; i < LBADCMDREPLY; i++) + { + PUTCHARx(TNC, BADCMDREPLY[i]); + } + + return TRUE; + +ENABLEMONITOR: + + TNC->TRACEFLAG = 0x80; + goto MONCOM; + +DISABLEMONITOR: + + TNC->TRACEFLAG = 0; + +MONCOM: + + SetAppl(TNC->Channels[0]->BPQStream, 2 | TNC->TRACEFLAG, TNC->APPLICATION); + + goto SENDHOSTOK; + +CCMD: + +// CONNECT REQUEST + + if (TNC->MSGCHANNEL == 0) + goto SENDHOSTOK; // SETTING UNPROTO ADDR - JUST ACK IT + + *TNC->DEDCURSOR = 0; + + if (TNC->MSGLENGTH > 1) + goto REALCALL; + +// STATUS REQUEST - IF CONNECTED, GET CALL + + DEDSWITCH[3] = 0; + + GetCallsign(Channel->BPQStream, &DEDSWITCH[3]); + + Debugprintf("CCMD %d %d %s", TNC->MSGCHANNEL, TNC->Channels[TNC->MSGCHANNEL]->BPQStream, &DEDSWITCH[3]); + + if (DEDSWITCH[3] == 0) + SENDCMDREPLY(TNC, NOTCONMSG, LNOTCON); + else + SENDCMDREPLY(TNC, DEDSWITCH, LSWITCH); + + return TRUE; + +REALCALL: + +// If to Switch, just connect, else pass c command to Node + + Debugprintf("CCMD %d %s", TNC->MSGCHANNEL, TXBUFFERPTR); + + SaveAuthProg = AuthorisedProgram; + AuthorisedProgram =1; + Connect(Channel->BPQStream); + AuthorisedProgram = SaveAuthProg; + +// CONNECT WILL BE REPORTED VIA NORMAL STATUS CHANGE + + if (Channel->MYCall[0] > ' ') + ChangeSessionCallsign(Channel->BPQStream, EncodeCall(Channel->MYCall)); + else + ChangeSessionCallsign(Channel->BPQStream, EncodeCall(TNC->Channels[0]->MYCall)); + + _strupr(TXBUFFERPTR); + + if (strstr(TXBUFFERPTR, "SWITCH") == 0) // Not switch + { + char * Call, * Arg1; + char * Context; + char seps[] = " ,\r"; + + Call = strtok_s(TXBUFFERPTR + 1, seps, &Context); + Arg1 = strtok_s(NULL, seps, &Context); + + if (Arg1) + { + // Have a digi string + + // First digi is used as a port number. Any others are rwal digis or WINMOR/PACTOR + + if (Context[0]) + TNC->MSGLEN = sprintf(TXBUFFERPTR + 100, "C %s %s v %s\r", Arg1, Call, Context); + else + TNC->MSGLEN = sprintf(TXBUFFERPTR + 100, "C %s %s\r", Call, Arg1); + } + else + TNC->MSGLEN = sprintf(TXBUFFERPTR + 100, "C %s\r", Call); + + strcpy(TXBUFFERPTR, TXBUFFERPTR + 100); + + SendMsg(Channel->BPQStream, TXBUFFERPTR, TNC->MSGLEN); + +// READCHANGE(Channel->BPQStream); // Suppress Connected to Switch + + goto SENDHOSTOK; + } + } + + goto SENDHOSTOK; + +DCMD: + +// DISCONNECT REQUEST + + Disconnect(Channel->BPQStream); + + goto SENDHOSTOK; + +LCMD: + + PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL + PUTCHARx(TNC, 1); + +// GET STATE AND QUEUED BUFFERS + + if (TNC->MSGCHANNEL) + goto NORM_L; + +// TO MONITOR CHANNEL + +// SEE IF MONITORED FRAMES AVAILABLE + + if (MONCount(TNC->Channels[0]->BPQStream)) + Work = 0x31; + else + Work = 0x30; + + goto MON_L; + +NORM_L: + + LocalSessionState(Channel->BPQStream, &State, &Change, FALSE); + + if (State == 0) + Work = '0'; + else + Work = '4'; // AX.25 STATE + + PUTCHARx(TNC, Change + '0'); // Status Messages + + PUTCHARx(TNC, ' '); + +// GET OTHER QUEUE COUNTS + + Count = RXCount(Channel->BPQStream); + + sprintf(WorkString, "%d", Count); // message count + + PUTSTRING(TNC, WorkString); + PUTCHARx(TNC, ' '); + +// NOT SENT IS NUMBER ON OUR QUEUE, NOT ACKED NUMBER FROM SWITCH + + +// SEE HOW MANY BUFFERS ATTACHED TO Q HEADER IN BX + + Count = 0;// C_Q_COUNT(Channel->Chan_TXQ); + + sprintf(WorkString, "%d", Count); // message count + PUTSTRING(TNC, WorkString); + PUTCHARx(TNC, ' '); + + if (Count > 8) + Work = '8'; // Busy + + Count = CountFramesQueuedOnSession(L4); + + sprintf(WorkString, "%d", Count); // message count + PUTSTRING(TNC, WorkString); + PUTCHARx(TNC, ' '); + +MON_L: + + PUTCHARx(TNC, '0'); + PUTCHARx(TNC, ' '); + PUTCHARx(TNC, Work); + PUTCHARx(TNC, 0); + + return TRUE; + +HOSTDATAPACKET: + +// } +// { +// UCHAR msg[100]; + +// sprintf(msg,"Host Data Packet: Port %d\n", TNC->ComPort); +// OutputDebugString(msg); +// } +// + + +// IF WE ALREADY HAVE DATA QUEUED, ADD THIS IT QUEUE + +// if (Channel->Chan_TXQ) +// { + +// // COPY MESSAGE TO A CHAIN OF BUFFERS + +// if (QCOUNT < 10) +// goto CANTSEND; // NO SPACE - RETURN ERROR (?) + +//QUEUEFRAME: + +// C_Q_ADD(Channel->Chan_TXQ, COPYMSGTOBUFFERS()); // RETURNS EDI = FIRST (OR ONLY) FRAGMENT + +// goto SENDHOSTOK; + + // MAKE SURE NODE ISNT BUSY + + if (TNC->MSGCHANNEL == 0) // UNPROTO Channel + goto SendUnproto; + + Count = CountFramesQueuedOnSession(L4); + +// if (Count > 4 || QCOUNT < 40) +// goto QUEUEFRAME; + + // OK TO PASS TO NODE + + SENDENFORCINGPACLEN(Channel, TNC->DEDTXBUFFER, TNC->MSGLENGTH); + goto SENDHOSTOK; + +SendUnproto: + + SendMsg(0, TXBUFFERPTR, TNC->MSGLENGTH); + goto SENDHOSTOK; + +POLL: + + PROCESSPOLL(TNC, Channel); + return TRUE; + +YCMD: + + *TNC->DEDCURSOR = 0; + + Work = atoi(&TXBUFFERPTR[1]); + + if (Work == 0) + Work = 1; // Mustn't release last stream + + if (Work >= 0 && Work <= MAXSTREAMS) + { + int Stream; + + if (Work < TNC->HOSTSTREAMS) + { + // Need to get rid of some streams + + for (i = Work + 1; i <= TNC->HOSTSTREAMS; i++) + { + Stream = TNC->Channels[i]->BPQStream; + + if (Stream) + { + Disconnect(Stream); + READCHANGE(Stream); // Prevent Initial *** disconnected + + DeallocateStream(Stream); + + Debugprintf("DED YCMD Release Stream %d", Stream); + } + + free(TNC->Channels[i]); + TNC->Channels[i] = 0; + } + } + else + { + for (i = TNC->HOSTSTREAMS+1; i <= Work; i++) + { + AllocateDEDChannel(TNC, i); // Also used by Y command handler + } + } + TNC->HOSTSTREAMS = Work; + } + + /* + + Why is this here? + { + int Len=0; + UCHAR Message[1000]; + + while (TNC->RXCOUNT > 0) + { + Message[Len++]= *(TNC->PUTPTR++); + + TNC->RXCOUNT--; + + if (TNC->PUTPTR == &TNC->TOUSERBUFFER[TNCBUFFLEN]) + TNC->PUTPTR = (UCHAR *)&TNC->TOUSERBUFFER; + + if (Len > 900) + { + BPQSerialSendData(TNC, Message, Len); + Len = 0; + } + } + + if (Len > 0) + { + BPQSerialSendData(TNC, Message, Len); + } + } + _asm { + + popad + + + RET +*/ + +SENDHOSTOK: + + PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL + PUTCHARx(TNC, 0); // NOTHING DOING + + return TRUE; + +} +int PROCESSPOLL(struct TNCDATA * TNC, struct StreamInfo * Channel) +{ + int PollType; + + // ASK SWITCH FOR STATUS CHANGE OR ANY RECEIVED DATA + + if (TNC->MSGLENGTH == 1) + goto GENERALPOLL; + + PollType = 0; + +// HE'S BEING AWKWARD, AND USING SPECIFIC DATA/STATUS POLL + + if (TNC->TONODEBUFFER[1] == '0') + goto DATAONLY; + + STATUSPOLL(TNC, Channel); + +GENERALPOLL: + + if (STATUSPOLL(TNC, Channel)) + return TRUE; // Status was reported + +DATAONLY: + + if (DATAPOLL(TNC, Channel)) + return TRUE; // Data Sent + + goto SENDHOSTOK; // NOTHING DOING + + +SENDHOSTOK: + + PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL + PUTCHARx(TNC, 0); // NOTHING DOING + + return TRUE; +} + +int ConvertToDEDMonFormat(struct TNCDATA * TNC, char * Decoded, int Len, MESSAGE * Rawdata) +{ + // convert tnc2 format monitor to ded format + + unsigned char * MONCURSOR=0; + unsigned char MONHEADER[256]; + char * From, * To, * via, * ctl, *Context, *ptr, *iptr; + int pid, NR, NS, MonLen; + char rest[20]; + +/* + + From DEDHOST Documentation + + +Codes of 4 and 5 both signify a monitor header. This is a null-terminated +format message containing the + + fm {call} to {call} via {digipeaters} ctl {name} pid {hex} + +string that forms a monitor header. The monitor header is also identical to +the monitor header displayed in user mode. If the code was 4, the monitored +frame contained no information field, so the monitor header is all you get. If +you monitor KB6C responding to a connect request from me and then poll channel +0, you'll get: + + 0004666D204B42364320746F204B42354D552063746C2055612070494420463000 + ! ! f m K B 6 C t o K B 5 M U c t l U A p i d F 0 ! + ! ! ! + ! +---- Code = 4 (Monitor, no info) Null termination ----+ + +------- Channel = 0 (Monitor info is always on channel 0) + + If the code was 5, the monitored frame did contain an information field. In +this case, another G command to channel 0 will return the monitored information +with a code of 6. Since data transmissions must be transparent, the monitored +information is passed as a byte-count format transmission. That is, it is +preceded by a count byte (one less than the number of bytes in the field). No +null terminator is used in this case. Since codes 4, 5, and 6 pertain to +monitored information, they will be seen only on channel 0. If you hear KB6C +say "Hi" to NK6K, and then poll channel 0, you'll get: + + 0005666D204B42364320746F204E4B364B2063746C204930302070494420463000 + ! ! f m K B 6 C t o N K 6 K c t l I 0 0 p i d F 0 ! + ! ! ! ! ! + ! ! or whatever ----+-+ ! + ! ! ! + ! +---- Code = 5 (Monitor, info follows) Null termination ----+ + +------ Channel = 0 (Monitor info is always on channel 0) + +and then the very next poll to channel 0 will get: + + 00 06 02 48 69 0D + ! ! ! H i CR + ! ! ! ! + ! ! ! +---- (this is a data byte) + ! ! +---- Count = 2 (three bytes of data) + ! +------- Code = 6 (monitored information) + +---------- Channel = 0 (Monitor info is always on channel 0) + + +*/ + + iptr = strchr(&Decoded[10], ':'); // Info if present + + MONHEADER[0] = 4; // NO DATA FOLLOWS + MONCURSOR = &MONHEADER[1]; + + if (strstr(Decoded, "NET/ROM") || strstr(Decoded, "NODES br") || strstr(Decoded, "INP3 RIF")) + pid = 0xcf; + else + pid = 0xf0; + + From = strtok_s(&Decoded[10], ">", &Context); + To = strtok_s(NULL, " ", &Context); + + via = strlop(To, ','); + + Context = strchr(Context, '<'); + if (Context == 0) + return 0; + + ctl = strtok_s(NULL, ">", &Context); + + if (via) + MONCURSOR += sprintf(MONCURSOR, "fm %s to %s via %s ctl ", From, To, via); + else + MONCURSOR += sprintf(MONCURSOR, "fm %s to %s ctl ", From, To); + + rest[0] = 0; + + switch (ctl[1]) + { + case 'R': + + NR = ctl[strlen(ctl)-1] - 48; + strlop(ctl, ' '); + sprintf(rest, "%s%d", &ctl[1], NR); + break; + + case 'I': + + ptr = strchr(ctl, 'S'); + if (ptr) NS = ptr[1] - 48; + ptr = strchr(ctl, 'R'); + if (ptr) NR = ptr[1] - 48; + sprintf(rest, "I%d%d pid %X", NS, NR, pid); + + if (pid == 0xcf) + { + // NETROM - pass the raw data + + MonLen = Rawdata->LENGTH - (MSGHDDRLEN + 16); // Data portion of frame + memcpy(&TNC->MONBUFFER[2], &Rawdata->L2DATA[0], MonLen); + + MONHEADER[0] = 5; // Data to follow + TNC->MONFLAG = 1; // Data to follow + TNC->MONBUFFER[0] = 6; + TNC->MONLENGTH = MonLen + 2; + TNC->MONBUFFER[1] = (MonLen - 1); + } + else + { + if (iptr) + { + iptr += 2; // Skip colon and cr + MonLen = Len - (int)(iptr - Decoded); + if (MonLen > 256) + MonLen = 256; + + memcpy(&TNC->MONBUFFER[2], iptr, MonLen); + + + if (MonLen == 0) // No data + { + MONHEADER[0] = 4; // No Data to follow + TNC->MONFLAG = 0; // No Data to follow + } + else + { + MONHEADER[0] = 5; // Data to follow + TNC->MONFLAG = 1; // Data to follow + TNC->MONBUFFER[0] = 6; + TNC->MONLENGTH = MonLen + 2; + TNC->MONBUFFER[1] = (MonLen - 1); + } + } + } + break; + + case 'C': + + strcpy(rest, "SABM"); + break; + + case 'D': + + if (ctl[1] == 'M') + strcpy(rest, "DM"); + else + strcpy(rest, "DISC"); + + break; + + case 'U': + + if (ctl[2] == 'A') + strcpy(rest, "UA"); + else + { + // UI + + size_t MonLen;; + + MONHEADER[0] = 5; // Data to follow + sprintf(rest, "UI pid %X", pid); + TNC->MONFLAG = 1; // Data to follow + TNC->MONBUFFER[0] = 6; + + if (pid ==0xcf) + { + // NETROM - pass th raw data + + MonLen = Rawdata->LENGTH - (MSGHDDRLEN + 16); // Data portion of frame + memcpy(&TNC->MONBUFFER[2], &Rawdata->L2DATA[0], MonLen); + } + else + { + ptr = strchr(Context, ':'); + + if (ptr == 0) + { + TNC->MONFLAG = 0; + return 0; + } + + ptr += 2; // Skip colon and cr + MonLen = Len - (ptr - Decoded); + memcpy(&TNC->MONBUFFER[2], ptr, MonLen); + } + + if (MonLen == 0) // No data + { + MONHEADER[0] = 4; // No Data to follow + TNC->MONFLAG = 0; // No Data to follow + } + else + { + TNC->MONLENGTH = (int)MonLen + 2; + TNC->MONBUFFER[1] = (int)(MonLen - 1); + } + break; + } + + default: + rest[0] = 0; + } + + MONCURSOR += sprintf(MONCURSOR, "%s", rest); + + if (MONCURSOR == &MONHEADER[1]) + return 0; // NOTHING DOING + + *MONCURSOR++ = 0; // NULL TERMINATOR + + SENDCMDREPLY(TNC, MONHEADER, (int)(MONCURSOR - &MONHEADER[0])); + return 1; +} + +// GET THE CONTROL BYTE, TO SEE IF THIS FRAME IS TO BE DISPLAYED +/* + +static char CTL_MSG[]=" ctl "; +static char VIA_MSG[]=" via "; +static char PID_MSG[]=" pid "; +static char SABM_MSG[]="SABM"; +static char DISC_MSG[]="DISC"; +static char UA_MSG[]="UA"; + +static char DM_MSG []="DM"; +static char RR_MSG []="RR"; +static char RNR_MSG[]="RNR"; +static char I_MSG[]="I pid "; +static char UI_MSG[]="UI pid "; +static char FRMR_MSG[]="FRMR"; +static char REJ_MSG[]="REJ"; + + PUSH EDI + MOV ECX,8 ; MAX DIGIS +CTRLLOOP: + TEST BYTE PTR (MSGCONTROL-1)[EDI],1 + JNZ CTRLFOUND + + ADD EDI,7 + LOOP CTRLLOOP +; +; INVALID FRAME +; + POP EDI + RET + +CTRLFOUND: + MOV AL,MSGCONTROL[EDI] + + and AL,NOT PFBIT ; Remove P/F bit + mov FRAME_TYPE,AL + + + POP EDI +; + TEST AL,1 ; I FRAME + JZ IFRAME + + CMP AL,3 ; UI + JE OKTOTRACE ; ALWAYS DO UI + + CMP AL,FRMR + JE OKTOTRACE ; ALWAYS DO FRMR +; +; USEQ/CONTROL - TRACE IF MCOM ON +; + CMP MCOM,0 + JNE OKTOTRACE + + RET + +;-----------------------------------------------------------------------------; +; Check for MALL ; +;-----------------------------------------------------------------------------; + +IFRAME: + cmp MALL,0 + jne OKTOTRACE + + ret + +OKTOTRACE: +; +;-----------------------------------------------------------------------------; +; Get the port number of the received frame ; +;-----------------------------------------------------------------------------; +; +; CHECK FOR PORT SELECTIVE MONITORING +; + MOV CL,MSGPORT[EDI] + mov PORT_NO,CL + + DEC CL + MOV EAX,1 + SHL EAX,CL ; SHIFT BIT UP + + TEST MMASK,EAX + JNZ TRACEOK1 + + RET + +TRACEOK1: + + MOV FRMRFLAG,0 + push EDI + mov AH,MSGDEST+6[EDI] + mov AL,MSGORIGIN+6[EDI] + +; +; Display Origin Callsign ; +; + +; 0004666D204B42364320746F204B42354D552063746C2055612070494420463000 +; ! ! f m K B 6 C t o K B 5 M U c t l U A p i d F 0 ! +; ! ! ! +; ! +---- Code = 4 (Monitor, no info) Null termination ----+ + ; +------- Channel = 0 (Monitor info is always on channel 0) + + mov ESI,OFFSET FM_MSG + call NORMSTR + + lea ESI,MSGORIGIN[EDI] + call CONVFROMAX25 + mov ESI,OFFSET NORMCALL + call DISPADDR + + pop EDI + push EDI + + mov ESI,OFFSET TO_MSG + call NORMSTR +; +; Display Destination Callsign ; +; + lea ESI,MSGDEST[EDI] + call CONVFROMAX25 + mov ESI,OFFSET NORMCALL + call DISPADDR + + pop EDI + push EDI + + mov AX,MMSGLENGTH[EDI] + mov FRAME_LENGTH,AX + mov ECX,8 ; Max number of digi-peaters +; +; Display any Digi-Peaters ; +; + test MSGORIGIN+6[EDI],1 + jnz NO_MORE_DIGIS + + mov ESI,OFFSET VIA_MSG + call NORMSTR + jmp short skipspace + +NEXT_DIGI: + test MSGORIGIN+6[EDI],1 + jnz NO_MORE_DIGIS + + mov AL,' ' + call MONPUTCHAR +skipspace: + add EDI,7 + sub FRAME_LENGTH,7 ; Reduce length + + push EDI + push ECX + lea ESI,MSGORIGIN[EDI] + call CONVFROMAX25 ; Convert to call + + push EAX ; Last byte is in AH + + mov ESI,OFFSET NORMCALL + call DISPADDR + + pop EAX + + test AH,80H + jz NOT_REPEATED + + mov AL,'*' + call MONPUTCHAR + +NOT_REPEATED: + pop ECX + pop EDI + loop NEXT_DIGI + +NO_MORE_DIGIS: + +;----------------------------------------------------------------------------; +; Display ctl ; +;----------------------------------------------------------------------------; + + mov ESI,OFFSET CTL_MSG + call NORMSTR + +;-----------------------------------------------------------------------------; +; Start displaying the frame information ; +;-----------------------------------------------------------------------------; + + + mov INFO_FLAG,0 + + mov AL,FRAME_TYPE + + test AL,1 + jne NOT_I_FRAME + +;-----------------------------------------------------------------------------; +; Information frame ; +;-----------------------------------------------------------------------------; + + mov AL,'I' + call MONPUTCHAR + mov INFO_FLAG,1 + + mov ESI,OFFSET I_MSG + call NORMSTR + + lea ESI,MSGPID[EDI] + lodsb + + call BYTE_TO_HEX + + + jmp END_OF_TYPE + +NOT_I_FRAME: + +;-----------------------------------------------------------------------------; +; Un-numbered Information Frame ; +;-----------------------------------------------------------------------------; + + cmp AL,UI + jne NOT_UI_FRAME + + mov ESI,OFFSET UI_MSG + call NORMSTR + + lea ESI,MSGPID[EDI] + lodsb + + call BYTE_TO_HEX + + mov INFO_FLAG,1 + jmp END_OF_TYPE + +NOT_UI_FRAME: + test AL,10B + jne NOT_R_FRAME + +;-----------------------------------------------------------------------------; +; Process supervisory frames ; +;-----------------------------------------------------------------------------; + + + and AL,0FH ; Mask the interesting bits + cmp AL,RR + jne NOT_RR_FRAME + + mov ESI,OFFSET RR_MSG + call NORMSTR + jmp END_OF_TYPE + +NOT_RR_FRAME: + cmp AL,RNR + jne NOT_RNR_FRAME + + mov ESI,OFFSET RNR_MSG + call NORMSTR + jmp END_OF_TYPE + +NOT_RNR_FRAME: + cmp AL,REJ + jne NOT_REJ_FRAME + + mov ESI,OFFSET REJ_MSG + call NORMSTR + jmp SHORT END_OF_TYPE + +NOT_REJ_FRAME: + mov AL,'?' ; Print "?" + call MONPUTCHAR + jmp SHORT END_OF_TYPE + +; +; Process all other frame types ; +; + +NOT_R_FRAME: + cmp AL,UA + jne NOT_UA_FRAME + + mov ESI,OFFSET UA_MSG + call NORMSTR + jmp SHORT END_OF_TYPE + +NOT_UA_FRAME: + cmp AL,DM + jne NOT_DM_FRAME + + mov ESI,OFFSET DM_MSG + call NORMSTR + jmp SHORT END_OF_TYPE + +NOT_DM_FRAME: + cmp AL,SABM + jne NOT_SABM_FRAME + + mov ESI,OFFSET SABM_MSG + call NORMSTR + jmp SHORT END_OF_TYPE + +NOT_SABM_FRAME: + cmp AL,DISC + jne NOT_DISC_FRAME + + mov ESI,OFFSET DISC_MSG + call NORMSTR + jmp SHORT END_OF_TYPE + +NOT_DISC_FRAME: + cmp AL,FRMR + jne NOT_FRMR_FRAME + + mov ESI,OFFSET FRMR_MSG + call NORMSTR + MOV FRMRFLAG,1 + jmp SHORT END_OF_TYPE + +NOT_FRMR_FRAME: + mov AL,'?' + call MONPUTCHAR + +END_OF_TYPE: + + CMP FRMRFLAG,0 + JE NOTFRMR +; +; DISPLAY FRMR BYTES +; + lea ESI,MSGPID[EDI] + MOV CX,3 ; TESTING +FRMRLOOP: + lodsb + CALL BYTE_TO_HEX + + LOOP FRMRLOOP + + JMP NO_INFO + +NOTFRMR: + + MOVZX ECX,FRAME_LENGTH + + + cmp INFO_FLAG,1 ; Is it an information packet ? + jne NO_INFO + + + XOR AL,AL ; IN CASE EMPTY + + sub ECX,23 + CMP ecx,0 + je NO_INFO ; EMPTY I FRAME + +; +; PUT DATA IN MONBUFFER, LENGTH IN MONLENGTH +; + + pushad +} + TNC->MONFLAG = 1; + + _asm{ + + popad + + MOV MONHEADER,5 ; DATA FOLLOWS + + cmp ECX,257 + jb LENGTH_OK +; + mov ECX,256 +; +LENGTH_OK: +; + mov MonDataLen, ECX + + pushad + + } + + TNC->MONBUFFER[1] = MonDataLen & 0xff; + TNC->MONBUFFER[1]--; + + + TNC->MONLENGTH = MonDataLen+2; + + ptr1=&TNC->MONBUFFER[2]; + + _asm{ + + popad + + MOV EDI,ptr1 + +MONCOPY: + LODSB + CMP AL,7 ; REMOVE BELL + JNE MONC00 + + MOV AL,20H +MONC00: + STOSB + + LOOP MONCOPY + + POP EDI + RET + +NO_INFO: +; +; ADD CR UNLESS DATA ALREADY HAS ONE +; + CMP AL,CR + JE NOTANOTHER + + mov AL,CR + call MONPUTCHAR + +NOTANOTHER: +; + pop EDI + ret + +;----------------------------------------------------------------------------; +; Display ASCIIZ strings ; +;----------------------------------------------------------------------------; + +NORMSTR: + lodsb + cmp AL,0 ; End of String ? + je NORMSTR_RET ; Yes + call MONPUTCHAR + jmp SHORT NORMSTR + +NORMSTR_RET: + ret + +;-----------------------------------------------------------------------------; +; Display Callsign pointed to by SI ; +;-----------------------------------------------------------------------------; + +DISPADDR: + jcxz DISPADDR_RET + + lodsb + call MONPUTCHAR + + loop DISPADDR + +DISPADDR_RET: + ret + + +;-----------------------------------------------------------------------------; +; Convert byte in AL to nn format ; +;-----------------------------------------------------------------------------; + +DISPLAY_BYTE_2: + cmp AL,100 + jb TENS_2 + + sub AL,100 + jmp SHORT DISPLAY_BYTE_2 + +TENS_2: + mov AH,0 + +TENS_LOOP_2: + cmp AL,10 + jb TENS_LOOP_END_2 + + sub AL,10 + inc AH + jmp SHORT TENS_LOOP_2 + +TENS_LOOP_END_2: + push EAX + mov AL,AH + add AL,30H + call MONPUTCHAR + pop EAX + + add AL,30H + call MONPUTCHAR + + ret + +;-----------------------------------------------------------------------------; +; Convert byte in AL to Hex display ; +;-----------------------------------------------------------------------------; + +BYTE_TO_HEX: + push EAX + shr AL,1 + shr AL,1 + shr AL,1 + shr AL,1 + call NIBBLE_TO_HEX + pop EAX + call NIBBLE_TO_HEX + ret + +NIBBLE_TO_HEX: + and AL,0FH + cmp AL,10 + + jb LESS_THAN_10 + add AL,7 + +LESS_THAN_10: + add AL,30H + call MONPUTCHAR + ret + + + +CONVFROMAX25: +; +; CONVERT AX25 FORMAT CALL IN [SI] TO NORMAL FORMAT IN NORMCALL +; RETURNS LENGTH IN CX AND NZ IF LAST ADDRESS BIT IS SET +; + PUSH ESI ; SAVE + MOV EDI,OFFSET NORMCALL + MOV ECX,10 ; MAX ALPHANUMERICS + MOV AL,20H + REP STOSB ; CLEAR IN CASE SHORT CALL + MOV EDI,OFFSET NORMCALL + MOV CL,6 +CONVAX50: + LODSB + CMP AL,40H + JE CONVAX60 ; END IF CALL - DO SSID + + SHR AL,1 + STOSB + LOOP CONVAX50 +CONVAX60: + POP ESI + ADD ESI,6 ; TO SSID + LODSB + MOV AH,AL ; SAVE FOR LAST BIT TEST + SHR AL,1 + AND AL,0FH + JZ CONVAX90 ; NO SSID - FINISHED +; + MOV BYTE PTR [EDI],'-' + INC EDI + CMP AL,10 + JB CONVAX70 + SUB AL,10 + MOV BYTE PTR [EDI],'1' + INC EDI +CONVAX70: + ADD AL,30H ; CONVERT TO DIGIT + STOSB +CONVAX90: + MOV ECX,EDI + SUB ECX,OFFSET NORMCALL + MOV NORMLEN,ECX ; SIGNIFICANT LENGTH + + TEST AH,1 ; LAST BIT SET? + RET + + +PUTCHAR: + + pushad + push eax + push TNC + call PUTCHARx + pop eax + pop eax + popad + ret +} +} +*/ + + + +VOID SENDENFORCINGPACLEN(struct StreamInfo * Channel, char * Msg, int Len) +{ + int Paclen = 0; + + if (Len == 0) + return; + + if (BPQHOSTVECTOR[Channel->BPQStream-1].HOSTSESSION) + Paclen = BPQHOSTVECTOR[Channel->BPQStream-1].HOSTSESSION->SESSPACLEN; + + if (Paclen == 0) + goto nochange; // paclen not set + +fragloop: + + if (Len <= Paclen) + goto nochange; // msglen <= paclen + +// need to fragment + + SendMsg(Channel->BPQStream, Msg, Paclen); + + Msg += Paclen; + + Len -= Paclen; + + if (Len) + goto fragloop; + + return; + +nochange: + + SendMsg(Channel->BPQStream, Msg, Len); + return; +} + +int DOCOMMAND(struct TNCDATA * conn) +{ + char Errbuff[500]; + int i; + +// PROCESS NORMAL MODE COMMAND + + sprintf(Errbuff, "BPQHOST Port %d Normal Mode CMD %s\n",conn->ComPort, conn->TONODEBUFFER); + OutputDebugString(Errbuff); + +// IF ECHO ENABLED, ECHO IT + + if (conn->ECHOFLAG) + { + UCHAR * ptr = conn->TONODEBUFFER; + UCHAR c; + + do + { + c = *(ptr++); + + if (c == 0x1b) c = ':'; + + PUTCHARx(conn, c); + + } while (c != 13); + } + + if (conn->TONODEBUFFER[0] != 0x1b) + goto NOTCOMMAND; // DATA IN NORMAL MODE - IGNORE + + switch (toupper(conn->TONODEBUFFER[1])) + { + case 'J': + + if (conn->TONODEBUFFER[6] == 0x0d) + conn->MODE = 0; + else + conn->MODE = conn->TONODEBUFFER[6] & 1; + + if (conn->MODE) + EnableAppl(conn); + else + DisableAppl(conn); + + if (conn->MODE) + { + // send host mode ack + +// PUTCHARx(conn, 0); +// PUTCHARx(conn, 0); + + conn->CURSOR = (UCHAR *)&conn->TONODEBUFFER; + return 0; + } + + break; + + case 'E': + + conn->ECHOFLAG = conn->TONODEBUFFER[2] & 1; + break; + + case 'I': + { + // Save call + + char * Call = &conn->TONODEBUFFER[2]; + + *(conn->CURSOR - 2) = 0; + + for (i = 0; i <= conn->HOSTSTREAMS; i++) + { + strcpy(conn->Channels[i]->MYCall, Call); + } + + break;; + } + case 'P': + +// PARAMS COMMAND - RETURN FIXED STRING + + PARAMPORT = conn->TONODEBUFFER[2]; + + for (i=0; i < LPARAMREPLY; i++) + { + PUTCHARx(conn, PARAMREPLY[i]); + } + + break; + + case 'S': + case 'D': + + // Return Channel Not Connected + + PUTSTRING(conn, "* CHANNEL NOT CONNECTED *\r"); + + default: + + break; + + } + +// PUTCHARx(conn, 'c'); +// PUTCHARx(conn, 'm'); +// PUTCHARx(conn, 'd'); +// PUTCHARx(conn, ':'); +// PUTCHARx(conn, 13); + +NOTCOMMAND: + + conn->CURSOR = (UCHAR *)&conn->TONODEBUFFER; + + return 0; + +} + +VOID SENDCMDREPLY(struct TNCDATA * TNC, char * Msg, int Len) +{ + int n; + + if (Len == 0) + return; + + PUTCHARx(TNC, TNC->MSGCHANNEL); + + for (n = 0; n < Len; n++) + { + PUTCHARx(TNC, Msg[n]); + } +} + +int STATUSPOLL(struct TNCDATA * TNC, struct StreamInfo * Channel) +{ + // Status Poll + + int State, Change, i; + char WorkString[256]; + + if (TNC->MSGCHANNEL == 0) // Monitor Chan + return 0; + + LocalSessionState(Channel->BPQStream, &State, &Change, TRUE); + + if (Change == 0) + return 0; + + // PORT HAS CONNECTED OR DISCONNECTED - SEND STATUS CHANGE TO PC + + if (State == 0) + { + // DISCONNECTED + + i = sprintf(CONMSG, "\x3(%d) DISCONNECTED fm 0:SWITCH\r", TNC->MSGCHANNEL); + i++; + } + else + { + // GET CALLSIGN + + GetCallsign(Channel->BPQStream, WorkString); + strlop(WorkString, ' '); + i = sprintf(CONMSG, "\x3(%d) CONNECTED to %s\r", TNC->MSGCHANNEL, WorkString); + i++; + } + + SENDCMDREPLY(TNC, CONMSG, i); + return 1; +} + +int DATAPOLL(struct TNCDATA * TNC, struct StreamInfo * Channel) +{ + unsigned char NODEBUFFER[300]; // MESSAGE FROM NODE + int Len, Count, i; + time_t stamp; + char * ptr1; + + if (TNC->MSGCHANNEL == 0) + { + // POLL FOR MONITOR DATA + + if (TNC->MONFLAG == 0) + goto NOMONITOR; + + // HAVE ALREADY GOT DATA PART OF MON FRAME OT SEND + + TNC->MONFLAG = 0; + + ptr1 = (UCHAR *)&TNC->MONBUFFER; + + if (TNC->MONLENGTH) + { + SENDCMDREPLY(TNC, ptr1, TNC->MONLENGTH); + return TRUE; + } + + OutputDebugString("BPQHOST Mondata Flag Set with no data"); + +NOMONITOR: + + // SEE IF ANYTHING TO MONITOR + + stamp = GetRaw(TNC->Channels[0]->BPQStream, (char *)&MONITORDATA, &Len, &Count); + + if (Len) + { + // Use Normal Decode, then reformat to DED standard + + ULONG SaveMMASK = MMASK; + BOOL SaveMTX = MTX; + BOOL SaveMCOM = MCOM; + BOOL SaveMUI = MUIONLY; + unsigned char Decoded[1000]; + + SetTraceOptionsEx(TNC->MMASK, TNC->MTX, TNC->MCOM, 0); + Len = IntDecodeFrame(&MONITORDATA, Decoded, stamp, TNC->MMASK, FALSE, FALSE); + SetTraceOptionsEx(SaveMMASK, SaveMTX, SaveMCOM, SaveMUI); + + if (Len) + { + return ConvertToDEDMonFormat(TNC, Decoded, Len, &MONITORDATA); + } + } + return 0; + } + + // Look for session data + + GetMsg(Channel->BPQStream, NODEBUFFER, &Len, &Count); + + if (Len == 0) + return 0; + + if (Len > 256) + { + Debugprintf("BPQHOST Corrupt Length = %d", Len); + return 0; + } + + // SEND DATA + + // If a failure, set a close timer (for Airmail, etc) + + NODEBUFFER[Len] = 0; // For strstr + + if (strstr(NODEBUFFER, "} Downlink connect needs port number") || + strstr(NODEBUFFER, "} Error - TNC Not Ready") || + strstr(NODEBUFFER, "} Failure with ") || + strstr(NODEBUFFER, "} Sorry, ")) + Channel->CloseTimer = CloseDelay * 10; + else + Channel->CloseTimer = 0; // Cancel Timer + + PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL + PUTCHARx(TNC, 7); + PUTCHARx(TNC, Len - 1); + + for (i = 0; i < Len; i++) + { + PUTCHARx(TNC, NODEBUFFER[i]); + } + + return 1; // HAVE SEND SOMETHING +} + + + + + + + + + +// Kantronics Host Mode Stuff + +// Kantronics Host Mode Stuff + +#define FEND 0xC0 // KISS CONTROL CODES +#define FESC 0xDB +#define TFEND 0xDC +#define TFESC 0xDD + + +static VOID ProcessKHOSTPacket(struct TNCDATA * conn, UCHAR * rxbuffer, int Len); +VOID ProcessKNormCommand(struct TNCDATA * conn, UCHAR * rxbuffer); +VOID SendKISSData(struct TNCDATA * conn, UCHAR * txbuffer, int Len); +static int KissDecode(UCHAR * inbuff, UCHAR * outbuff, int len); +static int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len); +static int DoReceivedData(struct TNCDATA * conn, struct StreamInfo * channel); + + + +VOID ProcessPacket(struct TNCDATA * conn, UCHAR * rxbuffer, int Len) +{ + UCHAR * FendPtr; + size_t NewLen; + + if (!conn->MODE) + { + // In Terminal Mode - Pass to Term Mode Handler + + ProcessKPacket(conn, rxbuffer, Len); + return; + } + + // Split into KISS Packets. By far the most likely is a single KISS frame + // so treat as special case + + if (!(rxbuffer[0] == FEND)) + { + // Getting Non Host Data in Host Mode - Appl will have to sort the mess + // Discard any data + + conn->RXBPtr = 0; + return; + } + + conn->RXBPtr = 0; // Assume we will use all data in buffer - will reset if part packet received + + FendPtr = memchr(&rxbuffer[1], FEND, Len-1); + + if (FendPtr == &rxbuffer[Len-1]) + { + ProcessKHOSTPacket(conn, &rxbuffer[1], Len - 2); + return; + } + + if (FendPtr == NULL) + { + // We have a partial Packet - Save it + + conn->RXBPtr = Len; + memcpy(&conn->TOUSERBUFFER[0], rxbuffer, Len); + return; + } + + // Process the first Packet in the buffer + + NewLen = FendPtr - rxbuffer -1; + ProcessKHOSTPacket(conn, &rxbuffer[1], (int)NewLen ); + + // Loop Back + + ProcessPacket(conn, FendPtr+1, Len - (int)NewLen -2); + return; + +} + +VOID ProcessKPacket(struct TNCDATA * conn, UCHAR * rxbuffer, int Len) +{ + UCHAR Char; + UCHAR * cmdStart; + int cmdlen = 0; + + // we will often get a whole connamd at once, but may not, so be prepared to receive char by char + // Could also get more than one command per packet + + cmdStart = rxbuffer; + + if (rxbuffer[0] == FEND && rxbuffer[Len-1] == FEND) + { + // Term thinks it is hosr mode + + // Unless it is FEND FF FEND (exit KISS) or FEND Q FEND (exit host) + + if (rxbuffer[2] == FEND) + { + if (rxbuffer[1] == 255 || rxbuffer[1] == 'q') + { + // If any more , process it. + + if (Len == 3) + return; + + Len -= 3; + rxbuffer+= 3; + ProcessKPacket(conn, rxbuffer, Len); + return; + } + } + conn->MODE = 1; + return; + } + + while (Len > 0) + { + Char = *(rxbuffer++); + Len--; + cmdlen++; + +// if (conn->TermPtr > 120) conn->TermPtr = 120; // Prevent overflow + + if (conn->ECHOFLAG) BPQSerialSendData(conn, &Char, 1); + + if (Char == 0x0d) + { + // We have a command line + + *(rxbuffer-1) = 0; + ProcessKNormCommand(conn, cmdStart); + conn->RXBPtr -= cmdlen; + cmdlen = 0; + cmdStart = rxbuffer; + } + } +} + +VOID ProcessKNormCommand(struct TNCDATA * conn, UCHAR * rxbuffer) +{ +// UCHAR CmdReply[]="C00"; + UCHAR ResetReply[] = "\xC0\xC0S00\xC0"; + int Len; + + char seps[] = " \t\r"; + char * Command, * Arg1, * Arg2; + char * Context; + + if (conn->Channels[1]->Connected) + { + Len = (int)strlen(rxbuffer); + rxbuffer[Len] = 0x0d; + SendMsg(conn->Channels[1]->BPQStream, rxbuffer, Len+1); + return; + } + + Command = strtok_s(rxbuffer, seps, &Context); + Arg1 = strtok_s(NULL, seps, &Context); + Arg2 = strtok_s(NULL, seps, &Context); + + if (Command == NULL) + { + BPQSerialSendData(conn, "cmd:", 4); + return; + } + + if (_stricmp(Command, "RESET") == 0) + { + if (conn->nextMode) + BPQSerialSendData(conn, ResetReply, 6); + else + BPQSerialSendData(conn, "cmd:", 4); + + + conn->MODE = conn->nextMode; + + if (conn->MODE) + EnableAppl(conn); + else + DisableAppl(conn); + + return; + } + + if (_stricmp(Command, "K") == 0) + { + int SaveAuthProg = AuthorisedProgram; + + AuthorisedProgram = 1; + SessionControl(conn->Channels[1]->BPQStream, 1, 0); + AuthorisedProgram = SaveAuthProg; + + return; + } + + if (_memicmp(Command, "IN", 2) == 0) + { + if (Arg1) + { + if (_stricmp(Arg1, "HOST") == 0) + conn->nextMode = TRUE; + else + conn->nextMode = FALSE; + } + + BPQSerialSendData(conn, "INTFACE was TERMINAL\r", 21); + BPQSerialSendData(conn, "cmd:", 4); + return; + } + +//cmd: + +//INTFACE HOST +//INTFACE was TERMINAL +//cmd:RESET +//ÀÀS00À +//ÀC20XFLOW OFFÀ + + + //SendKISSData(conn, CmdReply, 3); + + BPQSerialSendData(conn, "cmd:", 4); + + + // Process Non-Hostmode Packet + + return; +} +int FreeBytes = 999; + +static VOID ProcessKHOSTPacket(struct TNCDATA * conn, UCHAR * rxbuffer, int Len) +{ + struct StreamInfo * channel; + UCHAR Command[80]; + UCHAR Reply[400]; + UCHAR TXBuff[400]; + UCHAR CmdReply[]="C00"; + + UCHAR Chan, Stream; + int i, j, TXLen, StreamNo; + + char * Cmd, * Arg1, * Arg2, * Arg3; + char * Context; + char seps[] = " \t\r\xC0"; + int CmdLen; + + if ((Len == 1) && ((rxbuffer[0] == 'q') || (rxbuffer[0] == 'Q'))) + { + // Force Back to Command Mode + + Sleep(3000); + conn->MODE = FALSE; + BPQSerialSendData(conn, "\r\r\rcmd:", 7); + return; + } + + if (rxbuffer[0] == '?') + { + // what is this ??? + + memcpy(Reply,CmdReply,3); + SendKISSData(conn, Reply, 3); + return; + } + + Chan = rxbuffer[1]; + Stream = rxbuffer[2]; + + StreamNo = Stream == '0' ? 0 : Stream - '@'; + + if (StreamNo > conn->HOSTSTREAMS) + { + SendKISSData(conn, "C00Invalid Stream", 17); + return; + } + + switch (rxbuffer[0]) + { + case 'C': + + // Command Packet. Extract Command + + if (Len > 80) Len = 80; + + memcpy(Command, &rxbuffer[3], Len-3); + Command[Len-3] = 0; + + Cmd = strtok_s(Command, seps, &Context); + Arg1 = strtok_s(NULL, seps, &Context); + Arg2 = strtok_s(NULL, seps, &Context); + Arg3 = strtok_s(NULL, seps, &Context); + CmdLen = (int)strlen(Cmd); + + if (_stricmp(Cmd, "S") == 0) + { + // Status + + FreeBytes = 2000; + + // Ideally I should do flow control by channel, but Paclink (at least) doesn't have a mechanism + + for (i = 1; i < conn->HOSTSTREAMS; i++) + { + if (conn->Channels[i]->Connected) + if (TXCount(conn->Channels[1]->BPQStream) > 10) + FreeBytes = 0; + } + + // This format works with Paclink and RMS Packet, but it doesn't seem to conform to the spec + + // I think maybe the Channel status should be in the same Frame. + + TXLen = sprintf(Reply, "C00FREE BYTES %d\r", FreeBytes); + SendKISSData(conn, Reply, TXLen); + + for (j=1; j <= conn->HOSTSTREAMS; j++) + { + channel = conn->Channels[j]; + + if (channel->Connected) + { + TXLen = sprintf(Reply, "C00%c/V stream - CONNECTED to %s", j + '@', "SWITCH"); + SendKISSData(conn, Reply, TXLen); + } +// else +// TXLen = sprintf(Reply, "C00%c/V stream - DISCONNECTED", j + '@'); + + } + return; + } + + if (_memicmp(Cmd, "C", CmdLen) == 0) + { + int Port; + struct StreamInfo * channel; + int BPQStream; + UCHAR * MYCall; + + // Connect. If command has a via string and first call is numeric use it as a port number + + if (StreamNo == 0) + { + Stream = 'A'; + StreamNo = 1; + } + + if (Arg2 && Arg3) + { + if (_memicmp(Arg2, "via", (int)strlen(Arg2)) == 0) + { + // Have a via string as 2nd param + + Port = atoi(Arg3); + { + if (Port > 0) // First Call is numeric + { + if (strlen(Context) > 0) // More Digis + TXLen = sprintf(TXBuff, "c %s %s v %s\r", Arg3, Arg1, Context); + else + TXLen = sprintf(TXBuff, "c %s %s\r", Arg3, Arg1); + } + else + { + // First Call Isn't Numeric. This won't work, as Digis without a port is invalid + + SendKISSData(conn, "C00Invalid via String (First must be Port)", 42); + return; + + } + } + } + else + TXLen = sprintf(TXBuff, "%s %s %s %s %s\r", Cmd, Arg1, Arg2, Arg3, Context); + + } + else + { + TXLen = sprintf(TXBuff, "C %s\r", Arg1); + } + + Reply[0] = 'C'; + Reply[1] = Chan; + Reply[2] = Stream; + SendKISSData(conn, Reply, 3); + + channel = conn->Channels[StreamNo]; + BPQStream = channel->BPQStream; + MYCall = (UCHAR *)&channel->MYCall; + + Connect(BPQStream); + + if (MYCall[0] > 0) + { + ChangeSessionCallsign(BPQStream, EncodeCall(MYCall)); + } + + SendMsg(conn->Channels[StreamNo]->BPQStream, TXBuff, TXLen); + + return; + + } + + if (_stricmp(Cmd, "D") == 0) + { + // Disconnect + + if (StreamNo == 0) + { + Stream = 'A'; + StreamNo = 1; + } + + SessionControl(conn->Channels[StreamNo]->BPQStream, 2, 0); + return; + } + + if (_memicmp(Cmd, "INT", 3) == 0) + { + SendKISSData(conn, "C00INTFACE HOST", 15); + return; + } + + if (_stricmp(Cmd, "PACLEN") == 0) + { + SendKISSData(conn, "C00PACLEN 128/128", 17); + return; + } + + if (_memicmp(Cmd, "MYCALL", CmdLen > 1 ? CmdLen : 2) == 0) + { + if (strlen(Arg1) < 30) + strcpy(conn->Channels[StreamNo]->MYCall, Arg1); + } + + memcpy(Reply,CmdReply,3); + SendKISSData(conn, Reply, 3); + return; + + case 'D': + + // Data to send + + + if (StreamNo > conn->HOSTSTREAMS) return; // Protect + + TXLen = KissDecode(&rxbuffer[3], TXBuff, Len-3); + SendMsg(conn->Channels[StreamNo]->BPQStream, TXBuff, TXLen); + + conn->Channels[StreamNo]->CloseTimer = 0; // Cancel Timer + + return; + + default: + + memcpy(Reply,CmdReply,3); + SendKISSData(conn, Reply, 3); + return; + } +} + +static int KissDecode(UCHAR * inbuff, UCHAR * outbuff, int len) +{ + int i,txptr=0; + UCHAR c; + + for (i=0;iBPQStream, ConnectingCall); + strlop(ConnectingCall, ' '); + + if (conn->MODE) + { + Len = sprintf (Msg, "S1%c*** CONNECTED to %s ", Stream + '@', ConnectingCall); + SendKISSData(conn, Msg, Len); + } + else + { + Len = sprintf (Msg, "*** CONNECTED to %s\r", ConnectingCall); + BPQSerialSendData(conn, Msg, Len); + BPQSerialSetDCD(conn->hDevice); + } + + channel->Connected = TRUE; + + return 0; + +} + +int KANTDisconnected (struct TNCDATA * conn, struct StreamInfo * channel, int Stream) +{ + UCHAR Msg[50]; + int Len; + + if (conn->MODE) + { + Len = sprintf (Msg, "S1%c*** DISCONNECTED", Stream + '@'); + SendKISSData(conn, Msg, Len); + } + else + { + BPQSerialSendData(conn, "*** DISCONNECTED\r", 17); + BPQSerialClrDCD(conn->hDevice); + } + + channel->Connected = FALSE; + channel->CloseTimer = 0; + + return 0; +} + +// SCS Mode Stuff + +unsigned short int compute_crc(unsigned char *buf,int len); +VOID EmCRCStuffAndSend(struct TNCDATA * conn, UCHAR * Msg, int Len); +int APIENTRY ChangeSessionPaclen(int Stream, int Paclen); + +int EmUnstuff(UCHAR * MsgIn, UCHAR * MsgOut, int len) +{ + int i, j=0; + + for (i=0; iOutgoingCall[0]) + strcpy(ConnectedCall, Channel->OutgoingCall); + else + GetCallsign(BPQStream, ConnectedCall); + + SCSReply[2] = HostStream; + SCSReply[3] = 3; + ReplyLen = sprintf(&SCSReply[4], "(%d) CONNECTED to %s", Channel->BPQStream, ConnectedCall); + ReplyLen += 5; + EmCRCStuffAndSend(conn, SCSReply, ReplyLen); + + Channel->Connected = TRUE; + return TRUE; + } + // Disconnected + + SCSReply[2] = HostStream; + SCSReply[3] = 3; + ReplyLen = sprintf(&SCSReply[4], "(%d) DISCONNECTED fm G8BPQ", Channel->BPQStream); + ReplyLen += 5; // Include Null + EmCRCStuffAndSend(conn, SCSReply, ReplyLen); + + Channel->Connected = FALSE; + return TRUE; + } + return FALSE; +} + +BOOL SCSCheckForData(struct TNCDATA * conn, struct StreamInfo * Channel, int HostStream, int BPQStream) +{ + int Length, Count; + + GetMsg(BPQStream, &SCSReply[5], &Length, &Count); + + if (Length == 0) + return FALSE; + + if (strstr(&SCSReply[5], "} Downlink connect needs port number") || + strstr(&SCSReply[5], "} Failure with ") || + strstr(&SCSReply[5], "} Sorry, ")) + Channel->CloseTimer = CloseDelay * 10; + else + Channel->CloseTimer = 0; // Cancel Timer + + SCSReply[2] = HostStream; + SCSReply[3] = 7; + SCSReply[4] = Length - 1; + + ReplyLen = Length + 5; + EmCRCStuffAndSend(conn, SCSReply, ReplyLen); + + return TRUE; +} + +VOID ProcessSCSHostFrame(struct TNCDATA * conn, UCHAR * Buffer, int Length) +{ + int Channel = Buffer[0]; + int Command = Buffer[1] & 0x3f; + int Len = Buffer[2]; + struct StreamInfo * channel; + UCHAR TXBuff[400]; + int BPQStream; + char * MYCall; + UCHAR Stream; + int TXLen, i; + BPQVECSTRUC * HOST; + + // SCS Channel 31 is the Pactor channel, mapped to the first stream + + if (Channel == 0) + Stream = -1; + else + if (Channel == 31) + Stream = 0; + else + Stream = Channel; + + channel = conn->Channels[Stream]; + + if (conn->Toggle == (Buffer[1] & 0x80) && (Buffer[1] & 0x40) == 0) + { + // Repeat Condition + + //EmCRCStuffAndSend(conn, SCSReply, ReplyLen); + //return; + } + + conn->Toggle = (Buffer[1] & 0x80); + conn->Toggle ^= 0x80; + +// if (Channel == 255 && Len == 0) + if (Channel == 255) + { + UCHAR * NextChan = &SCSReply[4]; + + // General Poll + + // See if any channels have anything avaiilable + + for (i = 1; i <= conn->HOSTSTREAMS; i++) + { + channel = conn->Channels[i]; + HOST = &BPQHOSTVECTOR[channel->BPQStream - 1]; // API counts from 1 + + if ((HOST->HOSTFLAGS & 3)) + { + *(NextChan++) = i + 1; // Something for this channel + continue; + } + + if (RXCount(channel->BPQStream)) + *(NextChan++) = i + 1; // Something for this channel + } + + *(NextChan++) = 0; + + SCSReply[2] = 255; + SCSReply[3] = 1; + + ReplyLen = (int)(NextChan - &SCSReply[0]); + + EmCRCStuffAndSend(conn, SCSReply, ReplyLen); + return; + } + + if (Channel == 254) // Status + { + // Extended Status Poll + + //TNC->Streams[0].PTCStatus0 = Status; + // TNC->Streams[0].PTCStatus1 = PactorLevel; // Pactor Level 1-4 + // TNC->Streams[0].PTCStatus2 = Msg[7]; // Speed Level + // Offset = Msg[8]; + + SCSReply[2] = 254; + SCSReply[3] = 7; // Status + SCSReply[4] = 3; // Len -1 + + if (conn->Channels[0]->Connected) + { + SCSReply[5] = 0x0AA; + SCSReply[6] = 3; + SCSReply[7] = 5; + SCSReply[8] = 128; + } + else + { + SCSReply[5] = 0; + SCSReply[6] = 0; + SCSReply[7] = 0; + SCSReply[8] = 0; + } + ReplyLen = 9; + EmCRCStuffAndSend(conn, SCSReply, 9); + return; + } + + + if (Command == 0) + { + // Data Frame + + SendMsg(channel->BPQStream, &Buffer[3], Buffer[2]+ 1); + + goto AckIt; + } + + switch (Buffer[3]) + { + case 'J': // JHOST + + conn->MODE = FALSE; + DisableAppl(conn); + + return; + + case 'G': // Specific Poll + + if (CheckStatusChange(conn, channel, Channel, channel->BPQStream)) + return; // It has sent reply + + if (SCSCheckForData(conn, channel, Channel, channel->BPQStream)) + return; // It has sent reply + + SCSReply[2] = Channel; + SCSReply[3] = 0; + ReplyLen = 4; + EmCRCStuffAndSend(conn, SCSReply, 4); + return; + + case 'C': // Connect + + // Could be real, or just C to request status + + if (Channel == 0) + goto AckIt; + + if (Length == 0) + { + // STATUS REQUEST - IF CONNECTED, GET CALL + + return; + } + Buffer[Length - 2] = 0; + + // Save call for connected report + + if (Buffer[5] = '%' ) // Pactor long path? + { + TXLen = sprintf(TXBuff, "C %s\r", &Buffer[6]); + strcpy(channel->OutgoingCall, &Buffer[6]); + } + else + { + TXLen = sprintf(TXBuff, "C %s\r", &Buffer[5]); + strcpy(channel->OutgoingCall, &Buffer[5]); + } + + BPQStream = channel->BPQStream; + MYCall = &channel->MYCall[0]; + + if (MYCall[0] == 0) + MYCall = (char *)&conn->MYCALL; + + Connect(BPQStream); + if (MYCall[0] > 0) + { + ChangeSessionCallsign(BPQStream, EncodeCall(MYCall)); + } + + ChangeSessionPaclen(BPQStream, 100); + + SendMsg(BPQStream, TXBuff, TXLen); + + AckIt: + + SCSReply[2] = Channel; + SCSReply[3] = 0; + ReplyLen = 4; + EmCRCStuffAndSend(conn, SCSReply, 4); + return; + + case 'D': + + // Disconnect + + Disconnect(channel->BPQStream); + goto AckIt; + + case '%': + + // %X commands + + switch (Buffer[4]) + { + case 'V': // Version + + SCSReply[2] = Channel; + SCSReply[3] = 1; + strcpy(&SCSReply[4], "4.8 1.32"); + ReplyLen = 13; + EmCRCStuffAndSend(conn, SCSReply, 13); + + return; + case 'M': + + default: + + SCSReply[2] = Channel; + SCSReply[3] = 1; + SCSReply[4] = 0; + + ReplyLen = 5; + EmCRCStuffAndSend(conn, SCSReply, 5); + + return; + } + case '@': + default: + + SCSReply[2] = Channel; + SCSReply[3] = 1; + SCSReply[4] = 0; + + ReplyLen = 5; + EmCRCStuffAndSend(conn, SCSReply, 5); + } +} + + +VOID ProcessSCSTextCommand(struct TNCDATA * conn, char * Command, int Len) +{ + // Command to SCS in non-Host mode. + + // We can probably just dump anything but JHOST 4 and MYCALL + + if (Len == 1) + goto SendPrompt; // Just a CR + + Debugprintf("%s", Command); + + if (_memicmp(Command, "JHOST4", 6) == 0) + { + conn->MODE = TRUE; + conn->Toggle = 0; + EnableAppl(conn); + + return; + } + + if (_memicmp(Command, "TERM 4", 6) == 0) + conn->Term4Mode = TRUE; + + else if (_memicmp(Command, "T 0", 3) == 0) + conn->Term4Mode = FALSE; + + else if (_memicmp(Command, "PAC 4", 5) == 0) + conn->PACMode = TRUE; + + if (_memicmp(Command, "MYC", 3) == 0) + { + char * ptr = strchr(Command, ' '); + char MYResp[80]; + + Command[Len-1] = 0; // Remove CR + + if (ptr && (strlen(ptr) > 2)) + { + strcpy(conn->MYCALL, ++ptr); + } + + sprintf(MYResp, "\rMycall: >%s<", conn->MYCALL); + PUTSTRING(conn, MYResp); + } + + else if (_memicmp(Command, "SYS SERN", 8) == 0) + { + char SerialNo[] = "\r\nSerial number: 0100000000000000"; + PUTSTRING(conn, SerialNo); + } + else + { + char SerialNo[] = "\rXXXX"; + PUTSTRING(conn, SerialNo); + } + +SendPrompt: + + if (conn->PACMode) + { + PUTCHARx(conn, 13); + PUTCHARx(conn, 'p'); + PUTCHARx(conn, 'a'); + PUTCHARx(conn, 'c'); + PUTCHARx(conn, ':'); + PUTCHARx(conn, ' '); + + return; + } + + if (conn->Term4Mode) + { + PUTCHARx(conn, 13); + PUTCHARx(conn, 4); + PUTCHARx(conn, 13); + PUTCHARx(conn, 'c'); + PUTCHARx(conn, 'm'); + PUTCHARx(conn, 'd'); + PUTCHARx(conn, ':'); + PUTCHARx(conn, ' '); + PUTCHARx(conn, 1); + } + else + { + PUTCHARx(conn, 13); + PUTCHARx(conn, 'c'); + PUTCHARx(conn, 'm'); + PUTCHARx(conn, 'd'); + PUTCHARx(conn, ':'); + PUTCHARx(conn, ' '); + } + + +/* + + if (conn->Term4Mode) + PUTCHARx(conn, 4); + + PUTCHARx(conn, 13); + PUTCHARx(conn, 'c'); + PUTCHARx(conn, 'm'); + PUTCHARx(conn, 'd'); + PUTCHARx(conn, ':'); + PUTCHARx(conn, ' '); + +*/ + return; +} + + +VOID ProcessSCSPacket(struct TNCDATA * conn, UCHAR * rxbuffer, int Length) +{ + unsigned short crc; + char UnstuffBuffer[500]; + + // DED mode doesn't have an end of frame delimiter. We need to know if we have a full frame + + // Fortunately this is a polled protocol, so we only get one frame at a time + + // If first char != 170, then probably a Terminal Mode Frame. Wait for CR on end + + // If first char is 170, we could check rhe length field, but that could be corrupt, as + // we haen't checked CRC. All I can think of is to check the CRC and if it is ok, assume frame is + // complete. If CRC is duff, we will eventually time out and get a retry. The retry code + // can clear the RC buffer + +Loop: + + if (rxbuffer[0] != 170) + { + UCHAR *ptr; + int cmdlen; + + // Char Mode Frame I think we need to see CR on end (and we could have more than one in buffer + + // If we think we are in host mode, then to could be noise - just discard. + + if (conn->MODE) + { + conn->FROMUSERLEN = 0; + return; + } + + rxbuffer[Length] = 0; + + if (rxbuffer[0] == 0) + { + // Just ignore + + conn->FROMUSERLEN--; + if (conn->FROMUSERLEN) + { + memmove(rxbuffer, rxbuffer+1, conn->FROMUSERLEN + 1); + Length--; + goto Loop; + } + return; + } + if (rxbuffer[0] == 0x1b) + { + // Just ignore (I think!) + + conn->FROMUSERLEN--; + if (conn->FROMUSERLEN) + { + memmove(rxbuffer, rxbuffer+1, conn->FROMUSERLEN + 1); + Length--; + goto Loop; + } + return; + } + + if (rxbuffer[0] == 0x1e) + { + // Status POLL + + conn->FROMUSERLEN--; + if (conn->FROMUSERLEN) + { + memmove(rxbuffer, rxbuffer+1, conn->FROMUSERLEN + 1); + Length--; + goto Loop; + } + PUTCHARx(conn, 30); + PUTCHARx(conn, 0x87); + if (conn->Term4Mode) + { + PUTCHARx(conn, 13); + PUTCHARx(conn, 4); + PUTCHARx(conn, 13); + PUTCHARx(conn, 'c'); + PUTCHARx(conn, 'm'); + PUTCHARx(conn, 'd'); + PUTCHARx(conn, ':'); + PUTCHARx(conn, ' '); + PUTCHARx(conn, 1); + } + else + { + PUTCHARx(conn, 13); + PUTCHARx(conn, 'c'); + PUTCHARx(conn, 'm'); + PUTCHARx(conn, 'd'); + PUTCHARx(conn, ':'); + PUTCHARx(conn, ' '); + } + + + return; + } + ptr = strchr(rxbuffer, 13); + + if (ptr == 0) + return; // Wait for rest of frame + + ptr++; + + cmdlen = (int)(ptr - rxbuffer); + + // Complete Char Mode Frame + + conn->FROMUSERLEN -= cmdlen; // Ready for next frame + + ProcessSCSTextCommand(conn, rxbuffer, cmdlen); + + if (conn->FROMUSERLEN) + { + memmove(rxbuffer, ptr, conn->FROMUSERLEN + 1); + + if (conn->Mode) + { + // now in host mode, so pass rest up a level + + ProcessSCSPacket(conn, conn->FROMUSERBUFFER, conn->FROMUSERLEN); + return; + } + goto Loop; + } + return; + } + + // Receiving a Host Mode frame + + if (Length < 6) // Minimum Frame Sise + return; + + if (rxbuffer[2] == 170) + { + // Retransmit Request + + conn->RXBPtr = 0; + return; // Ignore for now + } + + // Can't unstuff into same buffer - fails if partial msg received, and we unstuff twice + + + Length = EmUnstuff(&rxbuffer[2], &UnstuffBuffer[2], Length - 2); + + if (Length == -1) + { + // Unstuff returned an errors (170 not followed by 0) + + conn->FROMUSERLEN = 0; + return; // Ignore for now + } + crc = compute_crc(&UnstuffBuffer[2], Length); + + if (crc == 0xf0b8) // Good CRC + { + conn->FROMUSERLEN = 0; // Ready for next frame + ProcessSCSHostFrame(conn, &UnstuffBuffer[2], Length); + return; + } + + // Bad CRC - assume incomplete frame, and wait for rest. If it was a full bad frame, timeout and retry will recover link. + + return; +} + + +VOID EmCRCStuffAndSend(struct TNCDATA * conn, UCHAR * Msg, int Len) +{ + unsigned short int crc; + UCHAR StuffedMsg[500]; + int i, j; + + crc = compute_crc(&Msg[2], Len-2); + crc ^= 0xffff; + + Msg[Len++] = (crc&0xff); + Msg[Len++] = (crc>>8); + + for (i = j = 2; i < Len; i++) + { + StuffedMsg[j++] = Msg[i]; + if (Msg[i] == 170) + { + StuffedMsg[j++] = 0; + } + } + + if (j != i) + { + Len = j; + memcpy(Msg, StuffedMsg, j); + } + + Msg[0] = 170; + Msg[1] = 170; + + BPQSerialSendData(conn, Msg, Len); +} + + + \ No newline at end of file diff --git a/VARA-HPLaptop.c b/VARA-HPLaptop.c new file mode 100644 index 0000000..40a02c2 --- /dev/null +++ b/VARA-HPLaptop.c @@ -0,0 +1,2553 @@ +/* +Copyright 2001-2015 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 +*/ + +// +// Interface to allow G8BPQ switch to use VARA Virtual TNC in a form +// of ax.25 + + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + + +#include "CHeaders.h" + +#ifdef WIN32 +#include +#endif + +int (WINAPI FAR *GetModuleFileNameExPtr)(); +extern int (WINAPI FAR *EnumProcessesPtr)(); + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + +#include "bpq32.h" + +#include "tncinfo.h" + + +#define WSA_ACCEPT WM_USER + 1 +#define WSA_DATA WM_USER + 2 +#define WSA_CONNECT WM_USER + 3 + +static int Socket_Data(int sock, int error, int eventcode); + +int KillTNC(struct TNCINFO * TNC); +int RestartTNC(struct TNCINFO * TNC); +int KillPopups(struct TNCINFO * TNC); +VOID MoveWindows(struct TNCINFO * TNC); +int SendReporttoWL2K(struct TNCINFO * TNC); +char * CheckAppl(struct TNCINFO * TNC, char * Appl); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); +BOOL KillOldTNC(char * Path); +int VARASendData(struct TNCINFO * TNC, UCHAR * Buff, int Len); +VOID VARASendCommand(struct TNCINFO * TNC, char * Buff, BOOL Queue); +VOID VARAProcessDataPacket(struct TNCINFO * TNC, UCHAR * Data, int Length); +void CountRestarts(struct TNCINFO * TNC); + +#ifndef LINBPQ +BOOL CALLBACK EnumVARAWindowsProc(HWND hwnd, LPARAM lParam); +#endif + +static char ClassName[]="VARASTATUS"; +static char WindowTitle[] = "VARA"; +static int RigControlRow = 165; + +#define WINMOR +#define NARROWMODE 21 +#define WIDEMODE 22 + +#ifndef LINBPQ +#include +#endif + +extern char * PortConfig[33]; +extern int SemHeldByAPI; + +static RECT Rect; + +extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + + +BOOL VARAStopPort(struct PORTCONTROL * PORT) +{ + // Disable Port - close TCP Sockets or Serial Port + + struct TNCINFO * TNC = PORT->TNC; + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + TNC->Streams[0].Disconnecting = FALSE; + + if (TNC->TCPSock) + { + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPSock); + } + + if (TNC->TCPDataSock) + { + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPDataSock); + } + + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + + KillTNC(TNC); + + sprintf(PORT->TNC->WEB_COMMSSTATE, "%s", "Port Stopped"); + MySetWindowText(PORT->TNC->xIDC_COMMSSTATE, PORT->TNC->WEB_COMMSSTATE); + + return TRUE; +} + +int ConnecttoVARA(int port); + +BOOL VARAStartPort(struct PORTCONTROL * PORT) +{ + // Restart Port - Open Sockets or Serial Port + + struct TNCINFO * TNC = PORT->TNC; + + ConnecttoVARA(TNC->Port); + TNC->lasttime = time(NULL);; + + sprintf(PORT->TNC->WEB_COMMSSTATE, "%s", "Port Restarted"); + MySetWindowText(PORT->TNC->xIDC_COMMSSTATE, PORT->TNC->WEB_COMMSSTATE); + + return TRUE; +} + + + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + if (_stricmp(buf, "ADDR")) + return FALSE; // Must start with ADDR + + ptr = strtok(NULL, " \t\n\r"); + + BPQport = Port; + p_ipad = ptr; + + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + WINMORport = atoi(p_port); + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(WINMORport); + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(WINMORport+1); + + TNC->HostName = malloc(strlen(p_ipad)+1); + + if (TNC->HostName == NULL) return TRUE; + + strcpy(TNC->HostName,p_ipad); + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_stricmp(ptr, "PTT") == 0) + { + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + DecodePTTString(TNC, ptr); + ptr = strtok(NULL, " \t\n\r"); + } + } + } + + if (ptr) + { + if (_memicmp(ptr, "PATH", 4) == 0) + { + p_cmd = strtok(NULL, "\n\r"); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + } + + TNC->MaxConReq = 10; // Default + + // Read Initialisation lines + + while (TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + else if (_memicmp(buf, "BW2300", 6) == 0) + { + TNC->ARDOPCurrentMode[0] = 'W'; // Save current state for scanning + strcat(TNC->InitScript, buf); + TNC->DefaultMode = TNC->WL2KMode = 50; + } + else if (_memicmp(buf, "BW500", 5) == 0) + { + TNC->ARDOPCurrentMode[0] = 'N'; + strcat(TNC->InitScript, buf); + TNC->DefaultMode = TNC->WL2KMode = 53; + } + else if (_memicmp(buf, "BW2750", 6) == 0) + { + TNC->ARDOPCurrentMode[0] = 'W'; // Save current state for scanning + strcat(TNC->InitScript, buf); + TNC->DefaultMode = TNC->WL2KMode = 54; + } + else if (_memicmp(buf, "FM1200", 6) == 0) + TNC->DefaultMode = TNC->WL2KMode = 51; + else if (_memicmp(buf, "FM9600", 5) == 0) + TNC->DefaultMode = TNC->WL2KMode = 52; + else if (standardParams(TNC, buf) == FALSE) + strcat(TNC->InitScript, buf); + } + + return (TRUE); +} + +void VARAThread(void * portptr); +int ConnecttoVARA(int port); +VOID VARAProcessReceivedData(struct TNCINFO * TNC); +VOID VARAProcessReceivedControl(struct TNCINFO * TNC); +VOID VARAReleaseTNC(struct TNCINFO * TNC); +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + + +#define MAXBPQPORTS 32 + +static time_t ltime; + + +static SOCKADDR_IN sinx; +static SOCKADDR_IN rxaddr; + +static int addrlen=sizeof(sinx); + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + size_t datalen; + PMSGWITHLEN buffptr; + char txbuff[500]; + unsigned int bytes,txlen=0; + size_t Param; + HKEY hKey=0; + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct ScanEntry * Scan; + + if (TNC == NULL) + return 0; // Port not defined + + if (TNC->CONNECTED == 0) + { + // clear Q if not connected + + while(TNC->BPQtoWINMOR_Q) + { + buffptr = Q_REM(&TNC->BPQtoWINMOR_Q); + + if (buffptr) + ReleaseBuffer(buffptr); + } + } + + TNC->Streams[0].Disconnecting = FALSE; + + switch (fn) + { + case 8: + + return 0; + + case 7: + + // approx 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances + + // Check session limit timer + + + if ((STREAM->Connecting || STREAM->Connected) && !STREAM->Disconnecting) + { + if (TNC->SessionTimeLimit && STREAM->ConnectTime && time(NULL) > (TNC->SessionTimeLimit + STREAM->ConnectTime)) + { + VARASendCommand(TNC, "CLEANTXBUFFER\r", TRUE); + VARASendCommand(TNC, "DISCONNECT\r", TRUE); + STREAM->Disconnecting = TRUE; + TNC->SessionTimeLimit += 120; // Don't retrigger unless things have gone horribly wrong + } + } + + while (TNC->PortRecord->UI_Q) + { + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } + + if (TNC->Busy) // Count down to clear + { + if ((TNC->BusyFlags & CDBusy) == 0) // TNC Has reported not busy + { + TNC->Busy--; + if (TNC->Busy == 0) + SetWindowText(TNC->xIDC_CHANSTATE, "Clear"); + strcpy(TNC->WEB_CHANSTATE, "Clear"); + } + } + + if (TNC->BusyDelay) + { + // Still Busy? + + if (InterlockedCheckBusy(TNC) == FALSE) + { + // No, so send + + VARASendCommand(TNC, TNC->ConnectCmd, TRUE); + TNC->Streams[0].Connecting = TRUE; + TNC->Streams[0].ConnectTime = time(NULL); + + memset(TNC->Streams[0].RemoteCall, 0, 10); + memcpy(TNC->Streams[0].RemoteCall, &TNC->ConnectCmd[8], strlen(TNC->ConnectCmd)-10); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + free(TNC->ConnectCmd); + TNC->BusyDelay = 0; + } + else + { + // Wait Longer + + TNC->BusyDelay--; + + if (TNC->BusyDelay == 0) + { + // Timed out - Send Error Response + + PMSGWITHLEN buffptr = GetBuff(); + + TNC->Streams[0].Connecting = FALSE; + TNC->Streams[0].ConnectTime = time(NULL); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = 39; + memcpy(buffptr->Data,"Sorry, Can't Connect - Channel is busy\r", 39); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + free(TNC->ConnectCmd); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + } + } + } + + return 0; + + case 1: // poll + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + VARASendCommand(TNC, "DISCONNECT\r", TRUE); + } + } + + /* + { + struct tm * tm; + char Time[80]; + + TNC->Restarts++; + TNC->LastRestart = time(NULL); + + tm = gmtime(&TNC->LastRestart); + + sprintf_s(Time, sizeof(Time),"%04d/%02d/%02d %02d:%02dZ", + tm->tm_year +1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + MySetWindowText(TNC->xIDC_RESTARTTIME, Time); + strcpy(TNC->WEB_RESTARTTIME, Time); + + sprintf_s(Time, sizeof(Time),"%d", TNC->Restarts); + MySetWindowText(TNC->xIDC_RESTARTS, Time); + strcpy(TNC->WEB_RESTARTS, Time); +*/ + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] && TNC->Streams[0].Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + TNC->Streams[0].Attached = TRUE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4USER, TNC->Streams[0].MyCall); + TNC->Streams[0].MyCall[calllen] = 0; + + // Stop Listening, and set MYCALL to user's call + + VARASendCommand(TNC, "LISTEN OFF\r", TRUE); + + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command(-1, Msg); + } + + if (TNC->Streams[0].Attached) + CheckForDetach(TNC, 0, &TNC->Streams[0], TidyClose, ForcedClose, CloseComplete); + + if (TNC->Streams[0].ReportDISC) + { + TNC->Streams[0].ReportDISC = FALSE; + buff->PORT = 0; + return -1; + } + + if (TNC->CONNECTED == FALSE && TNC->CONNECTING == FALSE) + { + // See if time to reconnect + + time(<ime); + if (ltime - TNC->lasttime > 9 ) + { + TNC->lasttime = ltime; + ConnecttoVARA(port); + } + } + + // See if any frames for this port + + if (TNC->Streams[0].BPQtoPACTOR_Q) //Used for CTEXT + { + PMSGWITHLEN buffptr = Q_REM(&TNC->Streams[0].BPQtoPACTOR_Q); + txlen = (int)buffptr->Len; + memcpy(txbuff, buffptr->Data, txlen); + bytes = VARASendData(TNC, &txbuff[0], txlen); + STREAM->BytesTXed += bytes; + ReleaseBuffer(buffptr); + } + + + if (TNC->WINMORtoBPQ_Q != 0) + { + buffptr=Q_REM(&TNC->WINMORtoBPQ_Q); + + datalen = buffptr->Len; + + buff->PORT = 0; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA[0], buffptr->Data, datalen); + + datalen += sizeof(void *) + 4; + PutLengthinBuffer(buff, (int)datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + + return (0); + + case 2: // send + + if (!TNC->CONNECTED) + { + // Send Error Response + + PMSGWITHLEN buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len=36; + memcpy(buffptr->Data,"No Connection to VARA TNC\r", 36); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + return 0; // Don't try if not connected + } + + if (TNC->Streams[0].BPQtoPACTOR_Q) //Used for CTEXT + { + PMSGWITHLEN buffptr = Q_REM(&TNC->Streams[0].BPQtoPACTOR_Q); + txlen = (int)buffptr->Len; + memcpy(txbuff, buffptr->Data, txlen); + bytes=send(TNC->TCPDataSock, buff->L2DATA, txlen, 0); + STREAM->BytesTXed += bytes; + WritetoTrace(TNC, txbuff, txlen); + ReleaseBuffer(buffptr); + } + + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; // Discard *** connected + return 0; + } + + + txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); // 1 as no PID + + if (TNC->Streams[0].Connected) + { + STREAM->PacketsSent++; + + bytes=send(TNC->TCPDataSock, buff->L2DATA, txlen, 0); + STREAM->BytesTXed += bytes; + WritetoTrace(TNC, buff->L2DATA, txlen); + } + else + { + if (_memicmp(buff->L2DATA, "D\r", 2) == 0 || _memicmp(buff->L2DATA, "BYE\r", 4) == 0) + { + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + return 0; + } + + // See if Local command (eg RADIO) + + if (_memicmp(buff->L2DATA, "RADIO ", 6) == 0) + { + sprintf(buff->L2DATA, "%d %s", TNC->Port, &buff->L2DATA[6]); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK->CIRCUITINDEX, buff->L2DATA)) + { + } + else + { + PMSGWITHLEN buffptr = GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "%s", buff->L2DATA); + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + } + return 1; + } + + if (_memicmp(buff->L2DATA, "OVERRIDEBUSY", 12) == 0) + { + PMSGWITHLEN buffptr = GetBuff(); + + TNC->OverrideBusy = TRUE; + + if (buffptr) + { + buffptr->Len = sprintf(buffptr->Data, "VARA} OK\r"); + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + } + + return 0; + + } + + if (_memicmp(&buff->L2DATA[0], "SessionTimeLimit", 16) == 0) + { + if (buff->L2DATA[16] != 13) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->SessionTimeLimit = atoi(&buff->L2DATA[16]) * 60; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "VARA} OK\r"); + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + } + return 0; + } + } + + + if (_memicmp(&buff->L2DATA[0], "CODEC TRUE", 9) == 0) + TNC->StartSent = TRUE; + + if (_memicmp(&buff->L2DATA[0], "BW2300", 6) == 0) + { + TNC->ARDOPCurrentMode[0] = 'W'; // Save current state for scanning + TNC->WL2KMode = 50; + } + + if (_memicmp(&buff->L2DATA[0], "BW500", 5) == 0) + { + TNC->ARDOPCurrentMode[0] = 'N'; + TNC->WL2KMode = 53; + } + + if (_memicmp(&buff->L2DATA[0], "D\r", 2) == 0) + { + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + return 0; + } + + // See if a Connect Command. If so set Connecting + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + char Connect[80]; + char * ptr = strchr(&buff->L2DATA[2], 13); + + if (ptr) + *ptr = 0; + + _strupr(&buff->L2DATA[2]); + + sprintf(Connect, "CONNECT %s %s\r", TNC->Streams[0].MyCall, &buff->L2DATA[2]); + + // Need to set connecting here as if we delay for busy we may incorrectly process OK response + + TNC->Streams[0].Connecting = TRUE; + + // See if Busy + + if (InterlockedCheckBusy(TNC)) + { + // Channel Busy. Unless override set, wait + + if (TNC->OverrideBusy == 0) + { + // Save Command, and wait up to 10 secs + + sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + TNC->ConnectCmd = _strdup(Connect); + TNC->BusyDelay = TNC->BusyWait * 10; // BusyWait secs + return 0; + } + } + + TNC->OverrideBusy = FALSE; + + VARASendCommand(TNC, Connect, TRUE); + TNC->Streams[0].ConnectTime = time(NULL); + + + memset(TNC->Streams[0].RemoteCall, 0, 10); + strcpy(TNC->Streams[0].RemoteCall, &buff->L2DATA[2]); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + } + else + { + buff->L2DATA[(txlen++) - 1] = 13; + buff->L2DATA[(txlen) - 1] = 0; + VARASendCommand(TNC, &buff->L2DATA[0], TRUE); + } + } + return (0); + + case 3: + + // CHECK IF OK TO SEND (And check TNC Status) + + if (TNC->Streams[0].Attached == 0) + return TNC->CONNECTED << 8 | 1; + + return (TNC->CONNECTED << 8 | TNC->Streams[0].Disconnecting << 15); // OK + + + case 4: // reinit + + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPSock); + + if (TNC->PID && TNC->WeStartedTNC) + { + KillTNC(TNC); + RestartTNC(TNC); + } + return 0; + + case 5: // Close + + if (TNC->CONNECTED) + { +// GetSemaphore(&Semaphore, 52); +// VARASendCommand(TNC, "CLOSE", FALSE); +// FreeSemaphore(&Semaphore); +// Sleep(100); + } + + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPSock); + if (TNC->WeStartedTNC) + KillTNC(TNC); + + return 0; + + + case 6: // Scan Stop Interface + + Param = (size_t)buff; + + if (Param == 2) // Check Permission (shouldn't happen) + { + Debugprintf("Scan Check Permission called on VARA"); + return 1; // OK to change + } + + if (!TNC->CONNECTED) + return 0; // No connection so no interlock + + if (Param == 1) // Request Permission + { + if (TNC->ConnectPending == 0 && TNC->PTTState == 0) + { + VARASendCommand(TNC, "LISTEN OFF\r", TRUE); + TNC->GavePermission = TRUE; + return 0; // OK to Change + } + + if (TNC->ConnectPending) + TNC->ConnectPending--; // Time out if set too long + + if (!TNC->ConnectPending) + return 0; // OK to Change + + return TRUE; + } + + if (Param == 3) // Release Permission + { + if (TNC->GavePermission) + { + TNC->GavePermission = FALSE; + if (TNC->ARDOPCurrentMode[0] != 'S') // Skip + VARASendCommand(TNC, "LISTEN ON\r", TRUE); + } + return 0; + } + + // Param is Address of a struct ScanEntry + + Scan = (struct ScanEntry *)buff; + + if (Scan->VARAMode != TNC->ARDOPCurrentMode[0]) + { + // Mode changed + + if (TNC->ARDOPCurrentMode[0] == 'S') + { + VARASendCommand(TNC, "LISTEN ON\r", TRUE); + } + + if (Scan->VARAMode == 'W') // Set Wide Mode + { + VARASendCommand(TNC, "BW2300\r", TRUE); + TNC->WL2KMode = 50; + } + if (Scan->VARAMode == 'T') // Set Wide Mode + { + VARASendCommand(TNC, "BW2750\r", TRUE); + TNC->WL2KMode = 54; + } + else if (Scan->VARAMode == 'N') // Set Narrow Mode + { + VARASendCommand(TNC, "BW500\r", TRUE); + TNC->WL2KMode = 53; + } + else if (Scan->VARAMode == 'S') // Skip + { + VARASendCommand(TNC, "LISTEN OFF\r", TRUE); + } + + TNC->ARDOPCurrentMode[0] = Scan->VARAMode; + } + return 0; + } + return 0; +} + +void CountRestarts(struct TNCINFO * TNC) +{ + struct tm * tm; + char Time[80]; + + TNC->Restarts++; + TNC->LastRestart = time(NULL); + + tm = gmtime(&TNC->LastRestart); + + sprintf_s(Time, sizeof(Time),"%04d/%02d/%02d %02d:%02dZ", + tm->tm_year +1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + //MySetWindowText(TNC->xIDC_RESTARTTIME, Time); + //strcpy(TNC->WEB_RESTARTTIME, Time); + + sprintf_s(Time, sizeof(Time),"%d", TNC->Restarts); + MySetWindowText(TNC->xIDC_RESTARTS, Time); + strcpy(TNC->WEB_RESTARTS, Time); +} +/* +char WebProcTemplate[] = "" + "\r\n" + "" + "%s" + "" + "%s" + ""; +*/ +char WebProcTemplate[] = "" + "\r\n" + "\r\n" + "%s\r\n" + "

%s

"; + +char Menubit[] = "" + "\r\n" + "" + "Abort Session" + "Kill TNC" + "Kill and Restart TNC" + ""; + +char sliderBit[] = " TX Offset %d" + "\r\n" + "\r\n"; + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, WebProcTemplate, TNC->Port, TNC->Port, "VARA Status", "VARA Status"); + + if (LOCAL) + Len += sprintf(&Buff[Len], Menubit, TNC->TXOffset, TNC->TXOffset); + + if (TNC->TXFreq) + Len += sprintf(&Buff[Len], sliderBit, TNC->TXOffset, TNC->TXOffset); + + Len += sprintf(&Buff[Len], ""); + + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); + Len += sprintf(&Buff[Len], "", TNC->WEB_CHANSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_PROTOSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); +// Len += sprintf(&Buff[Len], "", TNC->WEB_RESTARTS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Channel State%s
S/N%s
Traffic%s
TNC Restarts
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + +VOID VARASuspendPort(struct TNCINFO * TNC) +{ + VARASendCommand(TNC, "LISTEN OFF\r", TRUE); +} + +VOID VARAReleasePort(struct TNCINFO * TNC) +{ + VARASendCommand(TNC, "LISTEN ON\r", TRUE); +} + + +void * VARAExtInit(EXTPORTDATA * PortEntry) +{ + int i, port; + char Msg[255]; + char * ptr; + APPLCALLS * APPL; + struct TNCINFO * TNC; + int AuxCount = 0; + char Appl[11]; + char * TempScript; + int line; + struct PORTCONTROL * PORT = &PortEntry->PORTCONTROL; + // + // Will be called once for each VARA port + // + // The Socket to connect to is in IOBASE + // + + port = PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC->AutoStartDelay == 0) + TNC->AutoStartDelay = 2000; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + + TNC->Port = port; + + TNC->ARDOPBuffer = malloc(8192); + TNC->ARDOPDataBuffer = malloc(8192); + + if (TNC->ProgramPath) + TNC->WeStartedTNC = 1; + + TNC->Hardware = H_VARA; + + if (TNC->BusyWait == 0) + TNC->BusyWait = 10; + + if (TNC->BusyHold == 0) + TNC->BusyHold = 1; + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + PortEntry->MAXHOSTMODESESSIONS = 1; + PortEntry->SCANCAPABILITIES = SIMPLE; // Scan Control - pending connect only + + PortEntry->PORTCONTROL.UICAPABLE = FALSE; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 236; + + TNC->SuspendPortProc = VARASuspendPort; + TNC->ReleasePortProc = VARAReleasePort; + + PortEntry->PORTCONTROL.PORTSTARTCODE = VARAStartPort; + PortEntry->PORTCONTROL.PORTSTOPCODE = VARAStopPort; + + TNC->ModemCentre = 1500; // WINMOR is always 1500 Offset + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // Set Essential Params and MYCALL + + // Put overridable ones on front, essential ones on end + + TempScript = zalloc(1000); + +// strcat(TempScript, "ROBUST False\r"); + + // Set MYCALL(S) + + if (TNC->LISTENCALLS) + { + sprintf(Msg, "MYCALL %s", TNC->LISTENCALLS); + } + else + { + sprintf(Msg, "MYCALL %s", TNC->NodeCall); + + for (i = 0; i < 32; i++) + { + APPL=&APPLCALLTABLE[i]; + + if (APPL->APPLCALL_TEXT[0] > ' ') + { + char * ptr; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + { + // *ptr++ = ' '; + *ptr = 0; + } + strcat(Msg, " "); + strcat(Msg, Appl); + AuxCount++; + if (AuxCount == 4) // Max 5 in MYCALL + break; + } + } + } + + strcat(Msg, "\r"); + + strcat(TempScript, Msg); + + strcat(TempScript, TNC->InitScript); + + free(TNC->InitScript); + TNC->InitScript = TempScript; + + + strcat(TNC->InitScript,"LISTEN ON\r"); + + strcpy(TNC->CurrentMYC, TNC->NodeCall); + + if (TNC->WL2K == NULL) + if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Alrerady decoded + TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo; + + // if mode hasn't been set explicitly or via WL2KREPORT set to HF Wide mode (BW2300) + + if (TNC->DefaultMode == 0) + { + if (TNC->WL2K && TNC->WL2K->mode >= 50 && TNC->WL2K->mode <= 53) // A VARA Mode + TNC->DefaultMode = TNC->WL2KMode = TNC->WL2K->mode; + else + TNC->DefaultMode = TNC->WL2KMode = 50; // Default to 2300 + } + + if (TNC->destaddr.sin_family == 0) + { + // not defined in config file, so use localhost and port from IOBASE + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(PortEntry->PORTCONTROL.IOBASE); + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(PortEntry->PORTCONTROL.IOBASE+1); + + TNC->HostName=malloc(10); + + if (TNC->HostName != NULL) + strcpy(TNC->HostName,"127.0.0.1"); + + } + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 520; + TNC->WebWinY = 500; + TNC->WebBuffer = zalloc(5000); + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + TNC->WEB_CHANSTATE = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_PROTOSTATE = zalloc(100); + TNC->WEB_RESTARTTIME = zalloc(100); + TNC->WEB_RESTARTS = zalloc(100); + + TNC->WEB_MODE = zalloc(20); + TNC->WEB_TRAFFIC = zalloc(100); + + +#ifndef LINBPQ + + line = 6; + + if (TNC->TXFreq) + { + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow + 22, PacWndProc, 550, 450, ForcedClose); + + InitCommonControls(); // loads common control's DLL + + CreateWindowEx(0, "STATIC", "TX Tune", WS_CHILD | WS_VISIBLE, 10,line,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TXTUNE = CreateWindowEx(0, TRACKBAR_CLASS, "", WS_CHILD | WS_VISIBLE, 116,line,200,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TXTUNEVAL = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE, 320,line,30,20, TNC->hDlg, NULL, hInstance, NULL); + + SendMessage(TNC->xIDC_TXTUNE, TBM_SETRANGE, (WPARAM) TRUE, (LPARAM) MAKELONG(-200, 200)); // min. & max. positions + + line += 22; + } + else + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 450, ForcedClose); + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,line,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,line,386,20, TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,line,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,line,520,20, TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,line,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,line,200,20, TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "Channel State", WS_CHILD | WS_VISIBLE, 10,line,110,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_CHANSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,line,144,20, TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "S/N", WS_CHILD | WS_VISIBLE,10,line,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_PROTOSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,line,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,line,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "0 0 0 0", WS_CHILD | WS_VISIBLE,line,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "TNC Restarts", WS_CHILD | WS_VISIBLE,10,line,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTS = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE,116,line,20,20 , TNC->hDlg, NULL, hInstance, NULL); + CreateWindowEx(0, "STATIC", "Last Restart", WS_CHILD | WS_VISIBLE,140,line,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTTIME = CreateWindowEx(0, "STATIC", "Never", WS_CHILD | WS_VISIBLE,250,line,200,20, TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 0,line,250,300, TNC->hDlg, NULL, hInstance, NULL); + + TNC->ClientHeight = 450; + TNC->ClientWidth = 500; + + TNC->hMenu = CreatePopupMenu(); + + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill VARA TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart VARA TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTARTAFTERFAILURE, "Restart TNC after failed Connection"); + CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + AppendMenu(TNC->hMenu, MF_STRING, ARDOP_ABORT, "Abort Current Session"); + + MoveWindows(TNC); +#endif + Consoleprintf("VARA Host %s %d", TNC->HostName, htons(TNC->destaddr.sin_port)); + + ConnecttoVARA(port); + + time(&TNC->lasttime); // Get initial time value + + return ExtProc; +} + +int ConnecttoVARA(int port) +{ + if (TNCInfo[port]->CONNECTING || TNCInfo[port]->PortRecord->PORTCONTROL.PortStopped) + return 0; + + _beginthread(VARAThread, 0, (void *)(size_t)port); + + return 0; +} + +VOID VARAThread(void * portptr) +{ + // Opens sockets and looks for data on control and data sockets. + + int port = (int)(size_t)portptr; + char Msg[255]; + int err, i, ret; + u_long param=1; + BOOL bcopt=TRUE; + struct hostent * HostEnt; + struct TNCINFO * TNC = TNCInfo[port]; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + char * ptr1; + char * ptr2; + PMSGWITHLEN buffptr; + + if (TNC->HostName == NULL) + return; + + TNC->BusyFlags = 0; + + TNC->CONNECTING = TRUE; + + Sleep(3000); // Allow init to complete + + if (TNCInfo[port]->PortRecord->PORTCONTROL.PortStopped) + { + TNC->CONNECTING = FALSE; + return; + } + + +// printf("Starting VARA Thread\n"); + +// if on Windows and Localhost see if TNC is running + +#ifdef WIN32 + + if (strcmp(TNC->HostName, "127.0.0.1") == 0) + { + // can only check if running on local host + + TNC->PID = GetListeningPortsPID(TNC->destaddr.sin_port); + + if (TNC->PID == 0) + goto TNCNotRunning; + + // Get the File Name in case we want to restart it. + + if (TNC->ProgramPath == NULL) + { + if (GetModuleFileNameExPtr) + { + HANDLE hProc; + char ExeName[256] = ""; + + hProc = OpenProcess(PROCESS_QUERY_INFORMATION |PROCESS_VM_READ, FALSE, TNC->PID); + + if (hProc) + { + GetModuleFileNameExPtr(hProc, 0, ExeName, 255); + CloseHandle(hProc); + + TNC->ProgramPath = _strdup(ExeName); + } + } + } + goto TNCRunning; + } + +#endif + +TNCNotRunning: + + // Not running or can't check, restart if we have a path + + if (TNC->ProgramPath) + { + Consoleprintf("Trying to (re)start TNC %s", TNC->ProgramPath); + + if (RestartTNC(TNC)) + CountRestarts(TNC); + + Sleep(TNC->AutoStartDelay); + } + +TNCRunning: + + if (TNC->Alerted == FALSE) + { + sprintf(TNC->WEB_COMMSSTATE, "Connecting to TNC"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + } + + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + TNC->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) + { + TNC->CONNECTING = FALSE; + sprintf(Msg, "Resolve Failed for VARA socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + return; // Resolve failed + } + memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + +// closesocket(TNC->TCPSock); +// closesocket(TNC->TCPDataSock); + + TNC->TCPSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for VARA socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->CONNECTING = FALSE; + return; + } + + TNC->TCPDataSock = socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPDataSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for VARA Data socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->CONNECTING = FALSE; + closesocket(TNC->TCPSock); + + return; + } + + setsockopt(TNC->TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + setsockopt(TNC->TCPDataSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); +// setsockopt(TNC->TCPDataSock, IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + +// printf("Trying to connect to VARA TNC\n"); + + if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) + { + // Connected successful + + goto VConnected; + + } + + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + sprintf(Msg, "Connect Failed for VARA socket - error code = %d Port %d\n", + err, htons(TNC->destaddr.sin_port)); + + WritetoConsole(Msg); + TNC->Alerted = TRUE; + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + } + +// printf("VARA Connect failed\n"); + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + + TNC->TCPSock = 0; + TNC->CONNECTING = FALSE; + return; + +VConnected: + + // Connect Data Port + + if (connect(TNC->TCPDataSock,(LPSOCKADDR) &TNC->Datadestaddr,sizeof(TNC->Datadestaddr)) == 0) + { + // + // Connected successful + // + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for VARA Data socket - error code = %d\r\n", err); + WritetoConsole(Msg); + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + TNC->Alerted = TRUE; + } + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->CONNECTING = FALSE; + + RestartTNC(TNC); + return; + } + + Sleep(1000); + + TNC->LastFreq = 0; + + TNC->CONNECTING = FALSE; + TNC->CONNECTED = TRUE; + TNC->BusyFlags = 0; + TNC->InputLen = 0; + + // Send INIT script + + // VARA needs each command in a separate send + + ptr1 = &TNC->InitScript[0]; + + // We should wait for first RDY. Cheat by queueing a null command + + GetSemaphore(&Semaphore, 52); + + while(TNC->BPQtoWINMOR_Q) + { + buffptr = Q_REM(&TNC->BPQtoWINMOR_Q); + + if (buffptr) + ReleaseBuffer(buffptr); + } + + + while (ptr1 && ptr1[0]) + { + unsigned char c; + + ptr2 = strchr(ptr1, 13); + + if (ptr2) + { + c = *(ptr2 + 1); // Save next char + *(ptr2 + 1) = 0; // Terminate string + } + VARASendCommand(TNC, ptr1, TRUE); + + if (ptr2) + *(1 + ptr2++) = c; // Put char back + + ptr1 = ptr2; + } + + TNC->Alerted = TRUE; + + sprintf(TNC->WEB_COMMSSTATE, "Connected to VARA TNC"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + FreeSemaphore(&Semaphore); + + sprintf(Msg, "Connected to VARA TNC Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + + #ifndef LINBPQ +// FreeSemaphore(&Semaphore); + Sleep(1000); // Give VARA time to update Window title + EnumWindows(EnumVARAWindowsProc, (LPARAM)TNC); +// GetSemaphore(&Semaphore, 52); +#endif + + while (TNC->CONNECTED) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(TNC->TCPSock,&readfs); + FD_SET(TNC->TCPSock,&errorfs); + + if (TNC->CONNECTED) FD_SET(TNC->TCPDataSock,&readfs); + +// FD_ZERO(&writefs); + +// if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->TCPDataSock,&writefs); // Need notification of busy clearing + + if (TNC->CONNECTING || TNC->CONNECTED) FD_SET(TNC->TCPDataSock,&errorfs); + + timeout.tv_sec = 90; + timeout.tv_usec = 0; // We should get messages more frequently that this + + ret = select((int)TNC->TCPDataSock + 1, &readfs, NULL, &errorfs, &timeout); + + if (ret == SOCKET_ERROR) + { + Debugprintf("VARA Select failed %d ", WSAGetLastError()); + goto Lost; + } + if (ret > 0) + { + // See what happened + + if (FD_ISSET(TNC->TCPSock, &readfs)) + { + GetSemaphore(&Semaphore, 52); + VARAProcessReceivedControl(TNC); + FreeSemaphore(&Semaphore); + } + + if (FD_ISSET(TNC->TCPDataSock, &readfs)) + { + GetSemaphore(&Semaphore, 52); + VARAProcessReceivedData(TNC); + FreeSemaphore(&Semaphore); + } + + if (FD_ISSET(TNC->TCPSock, &errorfs)) + { +Lost: + sprintf(Msg, "VARA Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + TNC->ConnectPending = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + break; + } + + if (FD_ISSET(TNC->TCPDataSock, &errorfs)) + { + sprintf(Msg, "VARA Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + break; + } + continue; + } + else + { + // 60 secs without data. Shouldn't happen + + continue; + + sprintf(Msg, "VARA No Data Timeout Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + +// sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); +// GetSemaphore(&Semaphore, 52); +// MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); +// FreeSemaphore(&Semaphore); + + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + +// GetSemaphore(&Semaphore, 52); +// VARASendCommand(TNC, "CODEC FALSE", FALSE); +// FreeSemaphore(&Semaphore); + + Sleep(100); + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPSock); + + Sleep(100); + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPDataSock); + +// if (TNC->PID && TNC->WeStartedTNC) +// { +// KillTNC(TNC); +// + break; + } + } + + if (TNC->TCPSock) + { + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPSock); + } + + if (TNC->TCPDataSock) + { + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPDataSock); + } + + sprintf(Msg, "VARA Thread Terminated Port %d\r\n", TNC->Port); + WritetoConsole(Msg); +} + + +VOID VARAProcessResponse(struct TNCINFO * TNC, UCHAR * Buffer, int MsgLen) +{ + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + Buffer[MsgLen - 1] = 0; // Remove CR + + TNC->TimeSinceLast = 0; + + if (_memicmp(Buffer, "PTT ON", 6) == 0) + { +// Debugprintf("PTT On"); + + TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + + if (TNC->PTTMode) + Rig_PTT(TNC, TRUE); + + return; + } + + if (_memicmp(Buffer, "PTT OFF", 6) == 0) + { +// Debugprintf("PTT Off"); + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); + + return; + } + + if (_memicmp(Buffer, "SN ", 3) == 0) + { + strcpy(TNC->WEB_PROTOSTATE, &Buffer[3]); + MySetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + TNC->SNR = atof(&Buffer[3]); + return; + } + + if (_stricmp(Buffer, "BUSY ON") == 0) + { + TNC->BusyFlags |= CDBusy; + TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + + MySetWindowText(TNC->xIDC_CHANSTATE, "Busy"); + strcpy(TNC->WEB_CHANSTATE, "Busy"); + + TNC->WinmorRestartCodecTimer = time(NULL); + return; + } + + if (_stricmp(Buffer, "BUSY OFF") == 0) + { + TNC->BusyFlags &= ~CDBusy; + if (TNC->BusyHold) + strcpy(TNC->WEB_CHANSTATE, "BusyHold"); + else + strcpy(TNC->WEB_CHANSTATE, "Clear"); + + MySetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + TNC->WinmorRestartCodecTimer = time(NULL); + return; + } + + + if (_memicmp(&Buffer[0], "PENDING", 7) == 0) // Save Pending state for scan control + { + TNC->ConnectPending = 6; // Time out after 6 Scanintervals + Debugprintf(Buffer); +// WritetoTrace(TNC, Buffer, MsgLen - 1); + return; + } + + if (_memicmp(&Buffer[0], "CANCELPENDING", 13) == 0) + { + TNC->ConnectPending = FALSE; + Debugprintf(Buffer); + + // If a callsign is present it is the calling station - add to MH + + if (TNC->SeenCancelPending == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 1); + TNC->SeenCancelPending = 1; + } + + if (Buffer[13] == ' ') + UpdateMH(TNC, &Buffer[14], '!', 'I'); + + return; + } + + TNC->SeenCancelPending = 0; + + if (strcmp(Buffer, "OK") == 0) + { + // Need to discard response to LISTEN OFF after attach + + if (TNC->DiscardNextOK) + { + TNC->DiscardNextOK = 0; + return; + } + + if (TNC->Streams[0].Connecting == TRUE) + return; // Discard response or it will mess up connect scripts + } + + if (_memicmp(Buffer, "BUFFER", 6) == 0) + { + Debugprintf(Buffer); + + sscanf(&Buffer[7], "%d", &TNC->Streams[0].BytesOutstanding); + + if (TNC->Streams[0].BytesOutstanding == 0) + { + // all sent + + if (TNC->Streams[0].Disconnecting) // Disconnect when all sent + { + if (STREAM->NeedDisc == 0) + STREAM->NeedDisc = 60; // 6 secs + } + } + else + { + // Make sure Node Keepalive doesn't kill session. + + TRANSPORTENTRY * SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + if (SESS) + { + SESS->L4KILLTIMER = 0; + SESS = SESS->L4CROSSLINK; + if (SESS) + SESS->L4KILLTIMER = 0; + } + } + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %s", + STREAM->BytesTXed, STREAM->BytesRXed, &Buffer[7]); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + + return; + } + + if (_memicmp(Buffer, "CONNECTED ", 10) == 0) + { + char Call[11]; + char * ptr; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + int Speed = 0; + + Debugprintf(Buffer); + WritetoTrace(TNC, Buffer, MsgLen - 1); + + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->PacketsSent = 0; + + strcpy(TNC->WEB_MODE, ""); + + if (strstr(Buffer, "2300")) + { + Speed = 50; + strcpy(TNC->WEB_MODE, "2300"); + } + else if (strstr(Buffer, "NARROW")) + { + Speed = 51; + strcpy(TNC->WEB_MODE, "NARROW"); + } + else if (strstr(Buffer, "WIDE")) + { + Speed = 52; + strcpy(TNC->WEB_MODE, "WIDE"); + } + else if (strstr(Buffer, "500")) + { + Speed = 53; + strcpy(TNC->WEB_MODE, "500"); + } + else if (strstr(Buffer, "2750")) + { + Speed = 54; + strcpy(TNC->WEB_MODE, "2750"); + } + + MySetWindowText(TNC->xIDC_MODE, TNC->WEB_MODE); + memcpy(Call, &Buffer[10], 10); + + ptr = strchr(Call, ' '); + if (ptr) *ptr = 0; + + // Get Target Call + + ptr = strchr(&Buffer[10], ' '); + + if (ptr) + { + memcpy(TNC->TargetCall, ++ptr, 10); + strlop(TNC->TargetCall, ' '); + } + + TNC->HadConnect = TRUE; + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] == 0) + { + TRANSPORTENTRY * SESS; + + // Incoming Connect + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit + + ProcessIncommingConnectEx(TNC, Call, 0, TRUE, TRUE); + + SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + if (Speed) + SESS->Mode = Speed; + else + SESS->Mode = TNC->WL2KMode; + + TNC->ConnectPending = FALSE; + + if (TNC->RIG && TNC->RIG != &TNC->DummyRig && strcmp(TNC->RIG->RigName, "PTT")) + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", TNC->Streams[0].RemoteCall, TNC->TargetCall, TNC->RIG->Valchar); + SESS->Frequency = (int)(atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq + if (SESS->Frequency == 1500) + { + // try to get from WL2K record + + if (WL2K) + SESS->Frequency = WL2K->Freq; + } + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, TNC->TargetCall); + if (WL2K) + SESS->Frequency = WL2K->Freq; + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Check for ExcludeList + + if (ExcludeList[0]) + { + if (CheckExcludeList(SESS->L4USER) == FALSE) + { + char Status[64]; + + TidyClose(TNC, 0); + sprintf(Status, "%d SCANSTART 15", TNC->Port); + Rig_Command(-1, Status); + Debugprintf("VARA Call from %s rejected", Call); + return; + } + } + + // IF WE HAVE A PERMITTED CALLS LIST, SEE IF HE IS IN IT + + if (TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS) + { + UCHAR * ptr = TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS; + + while (TRUE) + { + if (memcmp(SESS->L4USER, ptr, 6) == 0) // Ignore SSID + break; + + ptr += 7; + + if ((*ptr) == 0) // Not in list + { + char Status[64]; + + TidyClose(TNC, 0); + sprintf(Status, "%d SCANSTART 15", TNC->Port); + Rig_Command(-1, Status); + Debugprintf("VARA Call from %s not in ValidCalls - rejected", Call); + return; + } + } + } + + + // See which application the connect is for + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (_stricmp(TNC->TargetCall, Appl) == 0) + break; + } + + if (App < 32) + { + char AppName[13]; + + memcpy(AppName, &ApplPtr[App * sizeof(CMDX)], 12); + AppName[12] = 0; + + // if SendTandRtoRelay set and Appl is RMS change to RELAY + + if (TNC->SendTandRtoRelay && memcmp(AppName, "RMS ", 4) == 0 + && (strstr(Call, "-T" ) || strstr(Call, "-R"))) + strcpy(AppName, "RELAY "); + + // Make sure app is available + + if (CheckAppl(TNC, AppName)) + { + MsgLen = sprintf(Buffer, "%s\r", AppName); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = MsgLen; + memcpy(buffptr->Data, Buffer, MsgLen); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + TNC->SwallowSignon = TRUE; + + // Save Appl Call in case needed for + + } + else + { + char Msg[] = "Application not available\r"; + + // Send a Message, then a disconenct + + // Send CTEXT First + + + if (TNC->Streams[0].BPQtoPACTOR_Q) //Used for CTEXT + { + PMSGWITHLEN buffptr = Q_REM(&TNC->Streams[0].BPQtoPACTOR_Q); + int txlen = (int)buffptr->Len; + VARASendData(TNC, buffptr->Data, txlen); + ReleaseBuffer(buffptr); + } + + VARASendData(TNC, Msg, (int)strlen(Msg)); + STREAM->NeedDisc = 100; // 10 secs + } + } + return; + } + else + { + // Connect Complete + + char Reply[80]; + int ReplyLen; + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + ReplyLen = sprintf(Reply, "*** Connected to %s\r", TNC->TargetCall); + + buffptr->Len = ReplyLen; + memcpy(buffptr->Data, Reply, ReplyLen); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + TNC->Streams[0].Connecting = FALSE; + TNC->Streams[0].Connected = TRUE; // Subsequent data to data channel + + if (TNC->RIG && TNC->RIG->Valchar[0]) + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound Freq %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall, TNC->RIG->Valchar); + else + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + UpdateMH(TNC, TNC->TargetCall, '+', 'O'); + return; + } + } + + + if (_memicmp(Buffer, "DISCONNECTED", 12) == 0) + { + Debugprintf(Buffer); + + TNC->ConnectPending = FALSE; // Cancel Scan Lock + + if (TNC->StartSent) + { + TNC->StartSent = FALSE; // Disconnect reported following start codec + return; + } + + if (TNC->Streams[0].Connecting) + { + // Report Connect Failed, and drop back to command mode + + TNC->Streams[0].Connecting = FALSE; + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = sprintf(buffptr->Data, "VARA} Failure with %s\r", STREAM->RemoteCall); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + if (TNC->RestartAfterFailure) + { + if (TNC->ProgramPath) + KillTNC(TNC); + } + + return; + } + + WritetoTrace(TNC, Buffer, MsgLen - 1); + + // Release Session + + if (STREAM->Connected && STREAM->ConnectTime) + { + // Create a traffic record + + char logmsg[120]; + time_t Duration; + + Duration = time(NULL) - STREAM->ConnectTime; + + if (Duration == 0) + Duration = 1; + + sprintf(logmsg,"Port %2d %9s Bytes Sent %d BPS %d Bytes Received %d BPS %d Time %d Seconds", + TNC->Port, STREAM->RemoteCall, + STREAM->BytesTXed, (int)(STREAM->BytesTXed/Duration), + STREAM->BytesRXed, (int)(STREAM->BytesRXed/Duration), (int)Duration); + + Debugprintf(logmsg); + + STREAM->ConnectTime= 0; //Prevent retrigger + } + + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + + if (STREAM->Disconnecting) // + VARAReleaseTNC(TNC); + + STREAM->Disconnecting = FALSE; + + return; + } + + + if (_memicmp(Buffer, "IAMALIVE", 8) == 0) + { +// strcat(Buffer, "\r\n"); +// WritetoTrace(TNC, Buffer, strlen(Buffer)); + return; + } + +// Debugprintf(Buffer); + + if (_memicmp(Buffer, "FAULT", 5) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 3); +// return; + } + + if (_memicmp(Buffer, "REGISTERED", 9) == 0) + { + strcat(Buffer, "\r"); + WritetoTrace(TNC, Buffer, (int)strlen(Buffer)); + return; + } + + if (_memicmp(Buffer, "MISSING SOUNDCARD", 17) == 0) + { + strcat(Buffer, "\r"); + WritetoTrace(TNC, Buffer, (int)strlen(Buffer)); + return; + } + + // Others should be responses to commands + + // Return others to user (if attached but not connected) + + if (TNC->Streams[0].Attached == 0) + return; + + if (TNC->Streams[0].Connected) + return; + + if (MsgLen > 200) + MsgLen = 200; + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = sprintf(buffptr->Data, "VARA} %s\r", Buffer); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); +} + +VOID VARAProcessReceivedData(struct TNCINFO * TNC) +{ + int InputLen; + + InputLen = recv(TNC->TCPDataSock, TNC->ARDOPDataBuffer, 8192, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + // Does this mean closed? + +// closesocket(TNC->TCPSock); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + TNC->Streams[0].Disconnecting = FALSE; + + + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + + + return; + } + + TNC->DataInputLen += InputLen; + + VARAProcessDataPacket(TNC, TNC->ARDOPDataBuffer, TNC->DataInputLen); + TNC->DataInputLen=0; + return; +} + + + +VOID VARAProcessReceivedControl(struct TNCINFO * TNC) +{ + int InputLen, MsgLen; + char * ptr, * ptr2; + char Buffer[4096]; + + // shouldn't get several messages per packet, as each should need an ack + // May get message split over packets + + if (TNC->InputLen > 8000) // Shouldnt have packets longer than this + TNC->InputLen=0; + + InputLen=recv(TNC->TCPSock, &TNC->ARDOPBuffer[TNC->InputLen], 8192 - TNC->InputLen, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + closesocket(TNC->TCPSock); + + TNC->TCPSock = 0; + + TNC->CONNECTED = FALSE; + TNC->Streams[0].ReportDISC = TRUE; + + TNC->Streams[0].Disconnecting = FALSE; + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + return; + } + + TNC->InputLen += InputLen; + +loop: + + ptr = memchr(TNC->ARDOPBuffer, '\r', TNC->InputLen); + + if (ptr == 0) // CR in buffer + return; // Wait for it + + ptr2 = &TNC->ARDOPBuffer[TNC->InputLen]; + + if ((ptr2 - ptr) == 1) // CR + { + // Usual Case - single msg in buffer + + VARAProcessResponse(TNC, TNC->ARDOPBuffer, TNC->InputLen); + TNC->InputLen=0; + return; + } + else + { + MsgLen = TNC->InputLen - (int)(ptr2-ptr) + 1; // Include CR + + memcpy(Buffer, TNC->ARDOPBuffer, MsgLen); + + VARAProcessResponse(TNC, Buffer, MsgLen); + + if (TNC->InputLen < MsgLen) + { + TNC->InputLen = 0; + return; + } + memmove(TNC->ARDOPBuffer, ptr + 1, TNC->InputLen-MsgLen); + + TNC->InputLen -= MsgLen; + goto loop; + } + return; +} + + + +VOID VARAProcessDataPacket(struct TNCINFO * TNC, UCHAR * Data, int Length) +{ + // Info on Data Socket - just packetize and send on + + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + int PacLen = 236; + PMSGWITHLEN buffptr; + + TNC->TimeSinceLast = 0; + + STREAM->BytesRXed += Length; + + Data[Length] = 0; + Debugprintf("VARA: RXD %d bytes", Length); + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->BytesTXed, STREAM->BytesRXed,STREAM->BytesOutstanding); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + + // May need to fragment + + while (Length) + { + int Fraglen = Length; + + if (Length > PACLEN) + Fraglen = PACLEN; + + Length -= Fraglen; + + buffptr = GetBuff(); + + if (buffptr == 0) + return; // No buffers, so ignore + + memcpy(buffptr->Data, Data, Fraglen); + + WritetoTrace(TNC, Data, Fraglen); + + Data += Fraglen; + + buffptr->Len = Fraglen; + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + } + return; +} + +static VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // If all acked, send disc + + if (TNC->Streams[0].BytesOutstanding == 0) + VARASendCommand(TNC, "DISCONNECT\r", TRUE); +} + +static VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + char Abort[] = "ABORT\r"; + + VARASendCommand(TNC, Abort, TRUE); + WritetoTrace(TNC, Abort, 5); +} + +static VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + VARAReleaseTNC(TNC); + TNC->ARDOPCurrentMode[0] = 0; // Force Mode select on next scan change + + // Also reset mode in case incoming call has changed it + + if (TNC->DefaultMode == 50) + VARASendCommand(TNC, "BW2300\r", TRUE); + else if (TNC->DefaultMode == 53) + VARASendCommand(TNC, "BW500\r", TRUE); + else if (TNC->DefaultMode == 54) + VARASendCommand(TNC, "BW2750\r", TRUE); +} + + +VOID VARASendCommand(struct TNCINFO * TNC, char * Buff, BOOL Queue) +{ + int SentLen; + + if (Buff[0] == 0) // Terminal Keepalive? + return; + + if (memcmp(Buff, "LISTEN O", 8) == 0) + TNC->DiscardNextOK = TRUE; // Responding to LISTEN messes up forwarding + + if (TNC->CONNECTED == 0) + return; + + if (TNC->TCPSock) + { + SentLen = send(TNC->TCPSock, Buff, (int)strlen(Buff), 0); + + if (SentLen != strlen(Buff)) + { + int winerr=WSAGetLastError(); + char ErrMsg[80]; + + sprintf(ErrMsg, "VARA Write Failed for port %d - error code = %d\r\n", TNC->Port, winerr); + WritetoConsole(ErrMsg); + + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + TNC->CONNECTED = FALSE; + return; + } + } + return; +} + +int VARASendData(struct TNCINFO * TNC, UCHAR * Buff, int Len) +{ + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + int bytes=send(TNC->TCPDataSock,(const char FAR *)Buff, Len, 0); + STREAM->BytesTXed += bytes; + WritetoTrace(TNC, Buff, Len); + return bytes; +} + +#ifndef LINBPQ + +BOOL CALLBACK EnumVARAWindowsProc(HWND hwnd, LPARAM lParam) +{ + char wtext[128]; + struct TNCINFO * TNC = (struct TNCINFO *)lParam; + UINT ProcessId; + int n; + + n = GetWindowText(hwnd, wtext, 127); + + if (memcmp(wtext,"VARA", 4) == 0) + { + GetWindowThreadProcessId(hwnd, &ProcessId); + + if (TNC->PID == ProcessId) + { + // Our Process + + char msg[512]; + char ID[64] = ""; + int i = 29; + + memcpy(ID, TNC->PortRecord->PORTCONTROL.PORTDESCRIPTION, 30); + + while (ID[i] == ' ') + ID[i--] = 0; + + wtext[n] = 0; + sprintf (msg, "BPQ %s - %s", ID, wtext); + SetWindowText(hwnd, msg); + return FALSE; + } + } + + return (TRUE); +} +#endif + +VOID VARAReleaseTNC(struct TNCINFO * TNC) +{ + // Set mycall back to Node or Port Call, and Start Scanner + + UCHAR TXMsg[1000]; + +// ARDOPChangeMYC(TNC, TNC->NodeCall); + + VARASendCommand(TNC, "LISTEN ON\r", TRUE); + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Start Scanner + + if (TNC->DefaultRadioCmd) + { + sprintf(TXMsg, "%d %s", TNC->Port, TNC->DefaultRadioCmd); + Rig_Command(-1, TXMsg); + } + + sprintf(TXMsg, "%d SCANSTART 15", TNC->Port); + Rig_Command(-1, TXMsg); + + ReleaseOtherPorts(TNC); +} + + + + + + diff --git a/Versions-HPLaptop.h b/Versions-HPLaptop.h new file mode 100644 index 0000000..42c9d9f --- /dev/null +++ b/Versions-HPLaptop.h @@ -0,0 +1,125 @@ + +#ifdef Kernel + +#define Vers 5,2,9,2 +#define Verstring "5.2.9.2\0" +#define Datestring "September 2012" +#define VerComments "G8BPQ Packet Switch V5.2.9.2\0" +#define VerCopyright "Copyright © 2001-2012 John Wiseman G8BPQ\0" +#define VerDesc "BPQ32 Switch\0" + +#endif + +#define KVers 6,0,23,51 +#define KVerstring "6.0.23.51\0" + +#ifdef CKernel + +#define Vers KVers +#define Verstring KVerstring +#define Datestring "March 2023" +#define VerComments "G8BPQ Packet Switch (C Version)" KVerstring +#define VerCopyright "Copyright © 2001-2023 John Wiseman G8BPQ\0" +#define VerDesc "BPQ32 Switch\0" +#define VerProduct "BPQ32" + +#endif + +#ifdef TermTCP + +#define Vers 1,0,16,1 +#define Verstring "1.0.16.1\0" +#define VerComments "Internet Terminal for G8BPQ Packet Switch\0" +#define VerCopyright "Copyright © 2011-2023 John Wiseman G8BPQ\0" +#define VerDesc "Simple TCP Terminal Program for G8BPQ Switch\0" +#define VerProduct "BPQTermTCP" + +#endif + +#ifdef BPQTerm + +#define Vers 2,2,5,1 +#define Verstring "2.2.5.1\0" +#define VerComments "Simple Terminal for G8BPQ Packet Switch\0" +#define VerCopyright "Copyright © 1999-2023 John Wiseman G8BPQ\0" +#define VerDesc "Simple Terminal Program for G8BPQ Switch\0" +#define VerProduct "BPQTerminal" + +#endif + +#ifdef BPQTermMDI + +#define Vers 2,2,0,3 +#define Verstring "2.2.0.3\0" +#define VerComments "MDI Terminal for G8BPQ Packet Switch\0" +#define VerCopyright "Copyright © 1999-2023 John Wiseman G8BPQ\0" +#define VerDesc "MDI Terminal Program for G8BPQ Switch\0" + +#endif + +#ifdef MAIL + +#define Vers KVers +#define Verstring KVerstring +#define VerComments "Mail server for G8BPQ Packet Switch\0" +#define VerCopyright "Copyright © 2009-2023 John Wiseman G8BPQ\0" +#define VerDesc "Mail server for G8BPQ's 32 Bit Switch\0" +#define VerProduct "BPQMail" + +#endif + +#ifdef HOSTMODES + +#define Vers 1,1,8,1 +#define Verstring "1.1.8.1\0" +//#define SPECIALVERSION "Test 3" +#define VerComments "Host Modes Emulator for G8BPQ Packet Switch\0" +#define VerCopyright "Copyright © 2009-2019 John Wiseman G8BPQ\0" +#define VerDesc "Host Modes Emulator for G8BPQ's 32 Bit Switch\0" +#define VerProduct "BPQHostModes" + +#endif + + +#ifdef UIUTIL + +#define Vers 0,1,3,1 +#define Verstring "0.1.3.1\0" +#define VerComments "Beacon Utility for G8BPQ Packet Switch\0" +#define VerCopyright "Copyright © 2011-2019 John Wiseman G8BPQ\0" +#define VerDesc "Beacon Utility for G8BPQ Switch\0" +#define VerProduct "BPQUIUtil" + +#endif + +#ifdef AUTH + +#define Vers 0,1,0,0 +#define Verstring "0.1.0.0\0" +#define VerComments "Password Generation Utility for G8BPQ Packet Switch\0" +#define VerCopyright "Copyright © 2011-2023 John Wiseman G8BPQ\0" +#define VerDesc "Password Generation Utility for G8BPQ Switch\0" + +#endif + +#ifdef APRS + +#define Vers KVers +#define Verstring KVerstring +#define VerComments "APRS Client for G8BPQ Switch\0" +#define VerCopyright "Copyright © 2012-2023 John Wiseman G8BPQ\0" +#define VerDesc "APRS Client for G8BPQ Switch\0" +#define VerProduct "BPQAPRS" + +#endif + +#ifdef CHAT + +#define Vers KVers +#define Verstring KVerstring +#define VerComments "Chat server for G8BPQ Packet Switch\0" +#define VerCopyright "Copyright © 2009-2023 John Wiseman G8BPQ\0" +#define VerDesc "Chat server for G8BPQ's 32 Bit Switch\0" +#define VerProduct "BPQChat" + +#endif diff --git a/Versions.h b/Versions.h index 0d1f361..cdd51ef 100644 --- a/Versions.h +++ b/Versions.h @@ -10,8 +10,8 @@ #endif -#define KVers 6,0,23,55 -#define KVerstring "6.0.23.55\0" +#define KVers 6,0,23,56 +#define KVerstring "6.0.23.56\0" #ifdef CKernel diff --git a/WebMail-HPLaptop.c b/WebMail-HPLaptop.c new file mode 100644 index 0000000..f76584b --- /dev/null +++ b/WebMail-HPLaptop.c @@ -0,0 +1,6171 @@ +/* +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 +*/ + +#define _CRT_SECURE_NO_DEPRECATE + +#include "CHeaders.h" +#include "bpqmail.h" + +#define MAIL +#include "httpconnectioninfo.h" + +#ifdef WIN32 +//#include "C:\Program Files (x86)\GnuWin32\include\iconv.h" +#else +#include +#include +#endif + +static struct HTTPConnectionInfo * FindSession(char * Key); +int APIENTRY SessionControl(int stream, int command, int param); +int SetupNodeMenu(char * Buff); +VOID SetMultiStringValue(char ** values, char * Multi); +char * GetTemplateFromFile(int Version, char * FN); +VOID FormatTime(char * Time, time_t cTime); +struct MsgInfo * GetMsgFromNumber(int msgno); +BOOL CheckUserMsg(struct MsgInfo * Msg, char * Call, BOOL SYSOP); +BOOL OkToKillMessage(BOOL SYSOP, char * Call, struct MsgInfo * Msg); +int DisplayWebForm(struct HTTPConnectionInfo * Session, struct MsgInfo * Msg, char * FileName, char * XML, char * Reply, char * RawMessage, int RawLen); +struct HTTPConnectionInfo * AllocateWebMailSession(); +VOID SaveNewMessage(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest, int InputLen); +void ConvertTitletoUTF8(char * Title, char * UTF8Title); +char *stristr (char *ch1, char *ch2); +char * ReadTemplate(char * FormSet, char * DirName, char * FileName); +VOID DoStandardTemplateSubsitutions(struct HTTPConnectionInfo * Session, char * txtFile); +BOOL CheckifPacket(char * Via); +int GetHTMLFormSet(char * FormSet); +void ProcessFormInput(struct HTTPConnectionInfo * Session, char * input, char * Reply, int * RLen, int InputLen); +char * WebFindPart(char ** Msg, char * Boundary, int * PartLen, char * End); +struct HTTPConnectionInfo * FindWMSession(char * Key); +int SendWebMailHeaderEx(char * Reply, char * Key, struct HTTPConnectionInfo * Session, char * Alert); +char * BuildFormMessage(struct HTTPConnectionInfo * Session, struct MsgInfo * Msg, char * Keys[1000], char * Values[1000], int NumKeys); +char * FindXMLVariable(WebMailInfo * WebMail, char * Var); +int ReplyToFormsMessage(struct HTTPConnectionInfo * Session, struct MsgInfo * Msg, char * Reply, BOOL Reenter); +BOOL ParsetxtTemplate(struct HTTPConnectionInfo * Session, struct HtmlFormDir * Dir, char * FN, BOOL isReply); +VOID UpdateFormAction(char * Template, char * Key); +BOOL APIENTRY GetAPRSLatLon(double * PLat, double * PLon); +BOOL APIENTRY GetAPRSLatLonString(char * PLat, char * PLon); +void FreeWebMailFields(WebMailInfo * WebMail); +VOID BuildXMLAttachment(struct HTTPConnectionInfo * Session, char * Keys[1000], char * Values[1000], int NumKeys); +VOID SaveTemplateMessage(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest); +VOID DownloadAttachments(struct HTTPConnectionInfo * Session, char * Reply, int * RLen, char * Rest); +VOID getAttachmentList(struct HTTPConnectionInfo * Session, char * Reply, int * RLen, char * Rest); +char * BuildB2Header(WebMailInfo * WebMail, struct MsgInfo * Msg, char ** ToCalls, int Calls); +VOID FormatTime2(char * Time, time_t cTime); +VOID ProcessSelectResponse(struct HTTPConnectionInfo * Session, char * URLParams); +VOID ProcessAskResponse(struct HTTPConnectionInfo * Session, char * URLParams); +char * CheckFile(struct HtmlFormDir * Dir, char * FN); +VOID GetPage(struct HTTPConnectionInfo * Session, char * NodeURL); +VOID SendTemplateSelectScreen(struct HTTPConnectionInfo * Session, char *URLParams, int InputLen); +BOOL isAMPRMsg(char * Addr); +char * doXMLTransparency(char * string); +Dll BOOL APIENTRY APISendAPRSMessage(char * Text, char * ToCall); + +extern char NodeTail[]; +extern char BBSName[10]; + +extern char LTFROMString[2048]; +extern char LTTOString[2048]; +extern char LTATString[2048]; + + UCHAR BPQDirectory[260]; + +int LineCount = 35; // Lines per page on message list + +// Forms + +struct HtmlFormDir ** HtmlFormDirs = NULL; +int FormDirCount = 0; + +struct HtmlForm +{ + char * FileName; + BOOL HasInitial; + BOOL HasViewer; + BOOL HasReply; + BOOL HasReplyViewer; +}; + +struct HtmlFormDir +{ + char * FormSet; + char * DirName; + struct HtmlForm ** Forms; + int FormCount; + struct HtmlFormDir ** Dirs; // Nested Directories + int DirCount; +}; + + +char FormDirList[4][MAX_PATH] = {"Standard_Templates", "Standard Templates", "Local_Templates"}; + +static char PassError[] = "

Sorry, User or Password is invalid - please try again

"; +static char BusyError[] = "

Sorry, No sessions available - please try later

"; + +extern char MailSignon[]; + +char WebMailSignon[] = "BPQ32 Mail Server Access" + "

BPQ32 Mail Server %s Access

" + "

Please enter Callsign and Password to access WebMail

" + "
" + "" + "" + "
User
Password
" + "

"; + +static char MsgInputPage[] = "" + "" + "" + "" + "" + "

Webmail Interface - Message Input Form

" + "
" + "
" + "To      %s
" + "Subject      " +// "" +// "
" + "" + "
Type    " + "" + " BID
" + "" + "" + "
" + "
" + "
"; + +static char CheckFormMsgPage[] = "" + "" + "" + "

Webmail Forms Interface - Check Message

" + "
" + + "
" + "To      
" + "CC      
" + "Subject " +//"" + "
Type    " + "" + " BID

" + "
" + + "
" + "
"; + + +extern char * WebMailTemplate; +extern char * WebMailMsgTemplate; +extern char * jsTemplate; + +static char *dat[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; +char *longday[] = {"Sunday", "Monday", "Tusday", "Wednesday", "Thusday", "Friday", "Saturday"}; + +static struct HTTPConnectionInfo * WebSessionList = NULL; // active WebMail sessions + +#ifdef LINBPQ +UCHAR * GetBPQDirectory(); +#endif + +void UndoTransparency(char * input); + +#ifndef LINBPQ + +void UndoTransparency(char * input) +{ + char * ptr1, * ptr2; + char c; + int hex; + + if (input == NULL) + return; + + ptr1 = ptr2 = input; + + // Convert any %xx constructs + + while (1) + { + c = *(ptr1++); + + if (c == 0) + break; + + if (c == '%') + { + c = *(ptr1++); + if(isdigit(c)) + hex = (c - '0') << 4; + else + hex = (tolower(c) - 'a' + 10) << 4; + + c = *(ptr1++); + if(isdigit(c)) + hex += (c - '0'); + else + hex += (tolower(c) - 'a' + 10); + + *(ptr2++) = hex; + } + else if (c == '+') + *(ptr2++) = 32; + else + *(ptr2++) = c; + } + *ptr2 = 0; +} +#endif + +void ReleaseWebMailStruct(WebMailInfo * WebMail) +{ + // release any malloc'ed resources + + if (WebMail == NULL) + return; + + FreeWebMailFields(WebMail); + free(WebMail); + return; +} + +VOID FreeWebMailMallocs() +{ + // called when closing. Not really needed, but simplifies tracking down real memory leaks + + struct HTTPConnectionInfo * Session, * SaveNext; + int i; + Session = WebSessionList; + + while (Session) + { + SaveNext = Session->Next; + + // Release amy malloc'ed resouces + + ReleaseWebMailStruct(Session->WebMail); + free(Session); + Session = SaveNext; + } + + for (i = 0; i < FormDirCount; i++) + { + struct HtmlFormDir * Dir = HtmlFormDirs[i]; + + int j; + + for (j = 0; j < Dir->FormCount; j++) + { + free(Dir->Forms[j]->FileName); + free(Dir->Forms[j]); + } + + if (Dir->DirCount) + { + struct HtmlFormDir * SubDir; + + int k, l; + + for (l = 0; l < Dir->DirCount; l++) + { + SubDir = Dir->Dirs[l]; + + for (k = 0; k < Dir->Dirs[l]->FormCount; k++) + { + free(SubDir->Forms[k]->FileName); + free(SubDir->Forms[k]); + } + free(SubDir->DirName); + free(SubDir->Forms); + free(SubDir->FormSet); + + free(Dir->Dirs[l]); + } + } + free(Dir->DirName); + free(Dir->Forms); + free(Dir->FormSet); + free(Dir); + } + + free(HtmlFormDirs); + return; +} + +char * initMultipartUnpack(char ** Input) +{ + // Check if Multipart and return Boundary. Update Input to first part + + // look through header for Content-Type line, and if multipart + // find boundary string. + + char * ptr, * ptr2; + char Boundary[128]; + BOOL Multipart = FALSE; + + ptr = *Input; + + while(*ptr != 13) + { + ptr2 = strchr(ptr, 10); // Find CR + + while(ptr2[1] == ' ' || ptr2[1] == 9) // Whitespace - continuation line + ptr2 = strchr(&ptr2[1], 10); // Find CR + + if (_memicmp(ptr, "Content-Type: ", 14) == 0) + { + char Line[256] = ""; + char * ptr3; + size_t len = ptr2-ptr-14; + + if (len >255) + return NULL; + + memcpy(Line, &ptr[14], len); + + if (_memicmp(Line, "Multipart/", 10) == 0) + { + ptr3 = stristr(Line, "boundary"); + if (ptr3) + { + ptr3+=9; + + if ((*ptr3) == '"') + ptr3++; + + strcpy(Boundary, ptr3); + ptr3 = strchr(Boundary, '"'); + if (ptr3) *ptr3 = 0; + ptr3 = strchr(Boundary, 13); // CR + if (ptr3) *ptr3 = 0; + break; + } + else + return NULL; // Can't do anything without a boundary ?? + } + } + ptr = ptr2; + ptr++; + } + + // Find First part - there is a boundary before it + + ptr = strstr(ptr2, Boundary); + + // Next should be crlf then part + + ptr = strstr(ptr, "\r\n"); + + if (ptr) + ptr += 2; // Over CRLF + + *Input = ptr; // Return first part or NULL + return _strdup(Boundary); +} + +BOOL unpackPart(char * Boundary, char ** Input, char ** Name, char ** Value, int * ValLen, char * End) +{ + // Format seems to be +/* + ------WebKitFormBoundaryABJaEbBWB5SuAHmq + Content-Disposition: form-data; name="Subj" + + subj + ------WebKitFormBoundaryABJaEbBWB5SuAHmq + Content-Disposition: form-data; name="myFile[]"; filename="exiftool.txt" + Content-Type: text/plain + + c:\exiftool "-filenameTo = _strdup(Value); + else if (strcmp(Name, "CC") == 0) + WebMail->CC = _strdup(Value); + else if (strcmp(Name, "Subj") == 0) + WebMail->Subject = _strdup(Value); + else if (strcmp(Name, "Type") == 0) + WebMail->Type = Value[0]; + else if (strcmp(Name, "BID") == 0) + WebMail->BID = _strdup(Value); + else if (strcmp(Name, "Msg") == 0) + WebMail->Body = _strdup(Value); + + else if (_memicmp(Name, "myFile[]", 8) == 0) + { + // Get File Name from param string - myFile[]"; filename="exiftool.txt" \r\nContent-Type: text/plain + + char * fn = strstr(Name, "filename="); + char * endfn; + if (fn) + { + fn += 10; + + endfn = strchr(fn, '"'); + if (endfn) + { + *endfn = 0; + + if (strlen(fn)) + { + WebMail->FileName[WebMail->Files] = _strdup(fn); + WebMail->FileBody[WebMail->Files] = malloc(ValLength); + memcpy(WebMail->FileBody[WebMail->Files], Value, ValLength); + WebMail->FileLen[WebMail->Files++] = ValLength; + } + } + } + } + + else if (_memicmp(Name, "myFile2[]", 8) == 0) + { + // Get File Name from param string - myFile[]"; filename="exiftool.txt" \r\nContent-Type: text/plain + + char * fn = strstr(Name, "filename="); + char * endfn; + if (fn) + { + fn += 10; + + endfn = strchr(fn, '"'); + if (endfn) + { + *endfn = 0; + + if (strlen(fn)) + { + WebMail->Header = malloc(ValLength + 1); + memcpy(WebMail->Header, Value, ValLength + 1); + WebMail->HeaderLen = RemoveLF(WebMail->Header, ValLength); + } + } + } + } + + else if (_memicmp(Name, "myFile3[]", 8) == 0) + { + // Get File Name from param string - myFile[]"; filename="exiftool.txt" \r\nContent-Type: text/plain + + char * fn = strstr(Name, "filename="); + char * endfn; + if (fn) + { + fn += 10; + + endfn = strchr(fn, '"'); + if (endfn) + { + *endfn = 0; + + if (strlen(fn)) + { + WebMail->Footer = malloc(ValLength + 1); + memcpy(WebMail->Footer, Value, ValLength + 1); + WebMail->FooterLen = RemoveLF(WebMail->Footer, ValLength); + } + } + } + } + + return TRUE; +} + + +struct HTTPConnectionInfo * AllocateWebMailSession() +{ + int KeyVal; + struct HTTPConnectionInfo * Session, * SaveNext; + time_t NOW = time(NULL); + + // First see if any session records havent been used for a while + + Session = WebSessionList; + + while (Session) + { + if (NOW - Session->WebMailLastUsed > 1200) // 20 Mins + { + SaveNext = Session->Next; + + // Release amy malloc'ed resouces + + ReleaseWebMailStruct(Session->WebMail); + + memset(Session, 0, sizeof(struct HTTPConnectionInfo)); + + Session->Next = SaveNext; + goto UseThis; + } + Session = Session->Next; + } + + Session = zalloc(sizeof(struct HTTPConnectionInfo)); + + if (Session == NULL) + return NULL; + + if (WebSessionList) + Session->Next = WebSessionList; + + WebSessionList = Session; + +UseThis: + + Session->WebMail = zalloc(sizeof(WebMailInfo)); + + KeyVal = ((rand() % 100) + 1); + + KeyVal *= (int)time(NULL); + + sprintf(Session->Key, "%c%08X", 'W', KeyVal); + + return Session; +} + +struct HTTPConnectionInfo * FindWMSession(char * Key) +{ + struct HTTPConnectionInfo * Session = WebSessionList; + + while (Session) + { + if (strcmp(Session->Key, Key) == 0) + { + Session->WebMailLastUsed = time(NULL); + return Session; + } + Session = Session->Next; + } + + return NULL; +} + + +// Build list of available forms + +VOID ProcessFormDir(char * FormSet, char * DirName, struct HtmlFormDir *** xxx, int * DirCount) +{ + struct HtmlFormDir * FormDir; + struct HtmlFormDir ** FormDirs = *xxx; + struct HtmlForm * Form; + char Search[MAX_PATH]; + int count = *DirCount; + +#ifdef WIN32 + HANDLE hFind = INVALID_HANDLE_VALUE; + WIN32_FIND_DATA ffd; +#else + DIR *dir; + struct dirent *entry; + char name[256]; +#endif + + FormDir = zalloc(sizeof (struct HtmlFormDir)); + + FormDir->DirName = _strdup(DirName); + FormDir->FormSet = _strdup(FormSet); + FormDirs=realloc(FormDirs, (count + 1) * sizeof(void *)); + FormDirs[count++] = FormDir; + + *DirCount = count; + *xxx = FormDirs; + + + // Scan Directory for .txt files + + sprintf(Search, "%s/%s/%s/*", GetBPQDirectory(), FormSet, DirName); + + // Find the first file in the directory. + +#ifdef WIN32 + + hFind = FindFirstFile(Search, &ffd); + + if (INVALID_HANDLE_VALUE == hFind) + return; + + do + { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + char Dir[MAX_PATH]; + + if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0) + continue; + + // Recurse in subdir + + sprintf(Dir, "%s/%s", DirName, ffd.cFileName); + + ProcessFormDir(FormSet, Dir, &FormDir->Dirs, &FormDir->DirCount); + + continue; + + } + + // Add to list + + Form = zalloc(sizeof (struct HtmlForm)); + + Form->FileName = _strdup(ffd.cFileName); + + FormDir->Forms=realloc(FormDir->Forms, (FormDir->FormCount + 1) * sizeof(void *)); + FormDir->Forms[FormDir->FormCount++] = Form; + } + + while (FindNextFile(hFind, &ffd) != 0); + + FindClose(hFind); + +#else + + sprintf(Search, "%s/%s/%s", GetBPQDirectory(), FormSet, DirName); + + if (!(dir = opendir(Search))) + { + Debugprintf("%s %d %d", "cant open forms dir", errno, dir); + return ; + } + while ((entry = readdir(dir)) != NULL) + { + if (entry->d_type == DT_DIR) + { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + + Debugprintf("Recurse %s/%s/%s", FormSet, DirName, entry->d_name); + continue; + + } + // see if initial html + +// if (stristr(entry->d_name, "initial.html")) + { + // Add to list + + Form = zalloc(sizeof (struct HtmlForm)); + + Form->FileName = _strdup(entry->d_name); + + FormDir->Forms=realloc(FormDir->Forms, (FormDir->FormCount + 1) * sizeof(void *)); + FormDir->Forms[FormDir->FormCount++] = Form; + } + } + closedir(dir); +#endif + return; +} + +int GetHTMLForms() +{ + int n = 0; + + while (FormDirList[n][0]) + GetHTMLFormSet(FormDirList[n++]); + + return 0; +} + +int GetHTMLFormSet(char * FormSet) +{ + int i; + +#ifdef WIN32 + + WIN32_FIND_DATA ffd; + char szDir[MAX_PATH]; + HANDLE hFind = INVALID_HANDLE_VALUE; + DWORD dwError=0; + + sprintf(szDir, "%s/%s/*", BPQDirectory, FormSet); + + // Find the first file in the directory. + + hFind = FindFirstFile(szDir, &ffd); + + if (INVALID_HANDLE_VALUE == hFind) + { + // Accept either + return 0; + } + + // Scan all directories looking for file + + do + { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0) + continue; + + // Add to Directory List + + ProcessFormDir(FormSet, ffd.cFileName, &HtmlFormDirs, &FormDirCount); + } + } + + while (FindNextFile(hFind, &ffd) != 0); + + FindClose(hFind); + +#else + + DIR *dir; + struct dirent *entry; + char name[256]; + + sprintf(name, "%s/%s", BPQDirectory, FormSet); + + if (!(dir = opendir(name))) + { + Debugprintf("cant open forms dir %s %d %d", name, errno, dir); + return 0; + } + + while ((entry = readdir(dir)) != NULL) + { + if (entry->d_type == DT_DIR) + { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + + // Add to Directory List + + ProcessFormDir(FormSet, entry->d_name, &HtmlFormDirs, &FormDirCount); + } + } + closedir(dir); +#endif + + // List for testing + + return 0; + + Debugprintf("%d form dirs", FormDirCount); + + for (i = 0; i < FormDirCount; i++) + { + struct HtmlFormDir * Dir = HtmlFormDirs[i]; + + int j; + Debugprintf("%3d %s", Dir->FormCount, Dir->DirName); + + for (j = 0; j < Dir->FormCount; j++) + Debugprintf(" %s", Dir->Forms[j]->FileName); + + if (Dir->DirCount) + { + int k, l; + + for (l = 0; l < Dir->DirCount; l++) + { + Debugprintf("Subdir %3d %s", Dir->Dirs[l]->DirCount, Dir->Dirs[l]->DirName); + for (k = 0; k < Dir->Dirs[l]->FormCount; k++) + Debugprintf(" %s", Dir->Dirs[l]->Forms[k]->FileName); + } + } + } + + + return 0; +} + + +static int compare(const void *arg1, const void *arg2) +{ + // Compare Calls. Fortunately call is at start of stuct + + return _stricmp(*(char**)arg1 , *(char**)arg2); +} + + +int SendWebMailHeader(char * Reply, char * Key, struct HTTPConnectionInfo * Session) +{ + return SendWebMailHeaderEx(Reply, Key, Session, NULL); +} + + +int SendWebMailHeaderEx(char * Reply, char * Key, struct HTTPConnectionInfo * Session, char * Alert) +{ + // Ex includes an alert string to be sent before message + + struct UserInfo * User = Session->User; + char Messages[245000]; + int m; + struct MsgInfo * Msg; + char * ptr = Messages; + int n = NumberofMessages; //LineCount; + char Via[64]; + int Count = 0; + + Messages[0] = 0; + + if (Alert && Alert[0]) + ptr += sprintf(Messages, "", Alert, Key); + + ptr += sprintf(ptr, "%s", " # Date XX Len To @ From Subject\r\n\r\n"); + + for (m = LatestMsg; m >= 1; m--) + { + if (ptr > &Messages[244000]) + break; // protect buffer + + Msg = GetMsgFromNumber(m); + + if (Msg == 0 || Msg->type == 0 || Msg->status == 0) + continue; // Protect against corrupt messages + + if (Msg && CheckUserMsg(Msg, User->Call, User->flags & F_SYSOP)) + { + char UTF8Title[128]; + char * EncodedTitle; + + // List if it is the right type and in the page range we want + + if (Session->WebMailTypes[0] && strchr(Session->WebMailTypes, Msg->type) == 0) + continue; + + // All Types or right Type. Check Mine Flag + + if (Session->WebMailMine) + { + // Only list if to or from me + + if (strcmp(User->Call, Msg->to) != 0 && strcmp(User->Call, Msg->from) != 0) + continue; + } + + if (Count++ < Session->WebMailSkip) + continue; + + strcpy(Via, Msg->via); + strlop(Via, '.'); + + // make sure title is HTML safe (no < > etc) and UTF 8 encoded + + EncodedTitle = doXMLTransparency(Msg->title); + + ConvertTitletoUTF8(EncodedTitle, UTF8Title); + + free(EncodedTitle); + + ptr += sprintf(ptr, "%6d %s %c%c %5d %-8s%-8s%-8s%s\r\n", + Key, Msg->number, Msg->number, + FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type, + Msg->status, Msg->length, Msg->to, Via, + Msg->from, UTF8Title); + + n--; + + if (n == 0) + break; + } + } + + if (WebMailTemplate == NULL) + WebMailTemplate = GetTemplateFromFile(6, "WebMailPage.txt"); + + return sprintf(Reply, WebMailTemplate, BBSName, User->Call, Key, Key, Key, Key, Key, Key, Key, Key, Messages); +} + +int ViewWebMailMessage(struct HTTPConnectionInfo * Session, char * Reply, int Number, BOOL DisplayHTML) +{ + char * Key = Session->Key; + struct UserInfo * User = Session->User; + WebMailInfo * WebMail = Session->WebMail; + char * DisplayStyle; + + char Message[200000] = ""; + struct MsgInfo * Msg; + char * ptr = Message; + char * MsgBytes, * Save; + int msgLen; + + char FullTo[100]; + char UTF8Title[128]; + int Index; + char * crcrptr; + char DownLoad[256] = ""; + + DisplayStyle = "textarea"; // Prevents interpretation of html and xml + + Msg = GetMsgFromNumber(Number); + + if (Msg == NULL) + { + ptr += sprintf(ptr, "Message %d not found\r\n", Number); + return sprintf(Reply, WebMailTemplate, BBSName, User->Call, Key, Key, Key, Key, Key, Key, Key, Message); + } + + // New Display so free any old values + + FreeWebMailFields(WebMail); + + WebMail->CurrentMessageIndex = Number; + + + if (!CheckUserMsg(Msg, User->Call, User->flags & F_SYSOP)) + { + ptr += sprintf(ptr, "Message %d not for you\r", Number); + return sprintf(Reply, WebMailTemplate, BBSName, User->Call, Key, Key, Key, Key, Key, Key, Key, Message); + } + + if (_stricmp(Msg->to, "RMS") == 0) + sprintf(FullTo, "RMS:%s", Msg->via); + else + if (Msg->to[0] == 0) + sprintf(FullTo, "smtp:%s", Msg->via); + else + strcpy(FullTo, Msg->to); + + // make sure title is UTF 8 encoded + + ConvertTitletoUTF8(Msg->title, UTF8Title); + + // if a B2 message diplay B2 Header instead of a locally generated one + + if ((Msg->B2Flags & B2Msg) == 0) + { + ptr += sprintf(ptr, "From: %s%s\nTo: %s\nType/Status: %c%c\nDate/Time: %s\nBid: %s\nTitle: %s\n\n", + Msg->from, Msg->emailfrom, FullTo, Msg->type, Msg->status, FormatDateAndTime((time_t)Msg->datecreated, FALSE), Msg->bid, UTF8Title); + } + + MsgBytes = Save = ReadMessageFile(Number); + + msgLen = Msg->length; + + if (Msg->type == 'P') + Index = PMSG; + else if (Msg->type == 'B') + Index = BMSG; + else + Index = TMSG; + + if (MsgBytes) + { + if (Msg->B2Flags & B2Msg) + { + char * ptr1; + + // if message has attachments, display them if plain text + + if (Msg->B2Flags & Attachments) + { + int BodyLen, NewLen; + int i; + char *ptr2, *attptr; + + sprintf(DownLoad, "Save Attachments", Key, Msg->number); + + WebMail->Files = 0; + + ptr1 = MsgBytes; + + // ptr += sprintf(ptr, "Message has Attachments\r\n\r\n"); + + while(*ptr1 != 13) + { + ptr2 = strchr(ptr1, 10); // Find CR + + if (memcmp(ptr1, "Body: ", 6) == 0) + { + BodyLen = atoi(&ptr1[6]); + } + + if (memcmp(ptr1, "File: ", 6) == 0) + { + char * ptr3 = strchr(&ptr1[6], ' '); // Find Space + *(ptr2 - 1) = 0; + + WebMail->FileLen[WebMail->Files] = atoi(&ptr1[6]); + WebMail->FileName[WebMail->Files++] = _strdup(&ptr3[1]); + *(ptr2 - 1) = ' '; // put space back + } + + ptr1 = ptr2; + ptr1++; + } + + ptr1 += 2; // Over Blank Line and Separator + + // ptr1 is pointing to body. Save for possible reply + + WebMail->Body = malloc(BodyLen + 2); + memcpy(WebMail->Body, ptr1, BodyLen); + WebMail->Body[BodyLen] = 0; + + *(ptr1 + BodyLen) = 0; + + ptr += sprintf(ptr, "%s", MsgBytes); // B2 Header and Body + + ptr1 += BodyLen + 2; // to first file + + // Save pointers to file + + attptr = ptr1; + + for (i = 0; i < WebMail->Files; i++) + { + WebMail->FileBody[i] = malloc(WebMail->FileLen[i]); + memcpy(WebMail->FileBody[i], attptr, WebMail->FileLen[i]); + attptr += (WebMail->FileLen[i] + 2); + } + + // if first (only??) attachment is XML and filename + // starts "RMS_Express_Form" process as HTML Form + + if (DisplayHTML && _memicmp(ptr1, "FileName[0], "RMS_Express_Form_", 16) == 0) + { + int Len = DisplayWebForm(Session, Msg, WebMail->FileName[0], ptr1, Reply, MsgBytes, BodyLen + 32); // 32 for added "has attachments" + free(MsgBytes); + + // Flag as read + + if ((_stricmp(Msg->to, User->Call) == 0) || ((User->flags & F_SYSOP) && (_stricmp(Msg->to, "SYSOP") == 0))) + { + if ((Msg->status != 'K') && (Msg->status != 'H') && (Msg->status != 'F') && (Msg->status != 'D')) + { + if (Msg->status != 'Y') + { + Msg->status = 'Y'; + Msg->datechanged=time(NULL); + SaveMessageDatabase(); + + } + } + } + + return Len; + } + + for (i = 0; i < WebMail->Files; i++) + { + int n; + char * p = ptr1; + char c; + + // Check if message is probably binary + + int BinCount = 0; + + NewLen = WebMail->FileLen[i]; + + for (n = 0; n < NewLen; n++) + { + c = *p; + + if (c==0 || (c & 128)) + BinCount++; + + p++; + + } + + if (BinCount > NewLen/10) + { + // File is probably Binary + + ptr += sprintf(ptr, "\rAttachment %s is a binary file\r", WebMail->FileName[i]); + } + else + { + *(ptr1 + NewLen) = 0; + ptr += sprintf(ptr, "\rAttachment %s\r\r", WebMail->FileName[i]); + RemoveLF(ptr1, NewLen + 1); // Removes LF after CR but not on its own + + ptr += sprintf(ptr, "%s\r\r", ptr1); + + User->Total.MsgsSent[Index] ++; + User->Total.BytesForwardedOut[Index] += NewLen; + } + + ptr1 += WebMail->FileLen[i]; + ptr1 +=2; // Over separator + } + + free(Save); + + ptr += sprintf(ptr, "\r\r[End of Message #%d from %s]\r", Number, Msg->from); + + RemoveLF(Message, (int)strlen(Message) + 1); // Removes LF after CR but not on its own + + if ((_stricmp(Msg->to, User->Call) == 0) || ((User->flags & F_SYSOP) && (_stricmp(Msg->to, "SYSOP") == 0))) + { + if ((Msg->status != 'K') && (Msg->status != 'H') && (Msg->status != 'F') && (Msg->status != 'D')) + { + if (Msg->status != 'Y') + { + Msg->status = 'Y'; + Msg->datechanged=time(NULL); + SaveMessageDatabase(); + } + } + } + return sprintf(Reply, WebMailMsgTemplate, BBSName, User->Call, Msg->number, Msg->number, Key, Msg->number, Key, DownLoad, Key, Key, Key, DisplayStyle, Message, DisplayStyle); + } + + // Remove B2 Headers (up to the File: Line) + + // ptr1 = strstr(MsgBytes, "Body:"); + + // if (ptr1) + // MsgBytes = ptr1; + } + + // Body may have cr cr lf which causes double space + + crcrptr = strstr(MsgBytes, "\r\r\n"); + + while (crcrptr) + { + *crcrptr = ' '; + crcrptr = strstr(crcrptr, "\r\r\n"); + } + + // Remove lf chars + + msgLen = RemoveLF(MsgBytes, msgLen); + + User->Total.MsgsSent[Index] ++; + // User->Total.BytesForwardedOut[Index] += Length; + + + // if body not UTF-8, convert it + + if (WebIsUTF8(MsgBytes, msgLen) == FALSE) + { + // With Windows it is simple - convert using current codepage + // I think the only reliable way is to convert to unicode and back + + size_t origlen = msgLen + 1; + + UCHAR * BufferB = malloc(2 * origlen); + +#ifdef WIN32 + + WCHAR * BufferW = malloc(2 * origlen); + int wlen; + int len = (int)origlen; + + wlen = MultiByteToWideChar(CP_ACP, 0, MsgBytes, len, BufferW, (int)(origlen * 2)); + len = WideCharToMultiByte(CP_UTF8, 0, BufferW, wlen, BufferB, (int)(origlen * 2), NULL, NULL); + + free(Save); + Save = MsgBytes = BufferB; + free(BufferW); + msgLen = len - 1; // exclude NULL + +#else + int left = 2 * msgLen; + int len = msgLen; + UCHAR * BufferBP = BufferB; + iconv_t * icu = NULL; + + if (icu == NULL) + icu = iconv_open("UTF-8", "CP1252"); + + iconv(icu, NULL, NULL, NULL, NULL); // Reset State Machine + iconv(icu, &MsgBytes, &len, (char ** __restrict__)&BufferBP, &left); + + free(Save); + Save = MsgBytes = BufferB; + msgLen = strlen(MsgBytes); + +#endif + + } + + + // ptr += sprintf(ptr, "%s", MsgBytes); + + memcpy(ptr, MsgBytes, msgLen); + ptr += msgLen; + ptr[0] = 0; + + free(Save); + + ptr += sprintf(ptr, "\r\r[End of Message #%d from %s]\r", Number, Msg->from); + + if ((_stricmp(Msg->to, User->Call) == 0) || ((User->flags & F_SYSOP) && (_stricmp(Msg->to, "SYSOP") == 0))) + { + if ((Msg->status != 'K') && (Msg->status != 'H') && (Msg->status != 'F') && (Msg->status != 'D')) + { + if (Msg->status != 'Y') + { + Msg->status = 'Y'; + Msg->datechanged=time(NULL); + SaveMessageDatabase(); + } + } + } + } + else + { + ptr += sprintf(ptr, "File for Message %d not found\r", Number); + } + + if (DisplayHTML && stristr(Message, "html>")) + DisplayStyle = "div"; // Use div so HTML and XML are interpreted + + + + + + return sprintf(Reply, WebMailMsgTemplate, BBSName, User->Call, Msg->number, Msg->number, Key, Msg->number, Key, DownLoad, Key, Key, Key, DisplayStyle, Message, DisplayStyle); +} + +int KillWebMailMessage(char * Reply, char * Key, struct UserInfo * User, int Number) +{ + struct MsgInfo * Msg; + char Message[100] = ""; + + Msg = GetMsgFromNumber(Number); + + if (Msg == NULL) + { + sprintf(Message, "Message %d not found", Number); + goto returnit; + } + + if (OkToKillMessage(User->flags & F_SYSOP, User->Call, Msg)) + { + FlagAsKilled(Msg, TRUE); + sprintf(Message, "Message #%d Killed\r", Number); + goto returnit; + } + + sprintf(Message, "Not your message\r"); + +returnit: + return sprintf(Reply, WebMailMsgTemplate, BBSName, User->Call, Msg->number, Msg->number, Key, Msg->number, Key, "", Key, Key, Key, "div", Message, "div"); +} + +void freeKeys(KeyValues * Keys) +{ + while (Keys->Key) + { + free(Keys->Key); + free(Keys->Value); + Keys++; + } +} + +void FreeWebMailFields(WebMailInfo * WebMail) +{ + // release any malloc'ed resources + + int i; + char * SaveReply; + int * SaveRlen; + + if (WebMail == NULL) + return; + + if (WebMail->txtFile) + free(WebMail->txtFile); + + if (WebMail->txtFileName) + free(WebMail->txtFileName); + + if (WebMail->InputHTMLName) + free(WebMail->InputHTMLName); + + if (WebMail->DisplayHTMLName) + free(WebMail->DisplayHTMLName); + + if (WebMail->ReplyHTMLName) + free(WebMail->ReplyHTMLName); + + if (WebMail->To) + free(WebMail->To); + if (WebMail->CC) + free(WebMail->CC); + if (WebMail->Subject) + free(WebMail->Subject); + if (WebMail->BID) + free(WebMail->BID); + if (WebMail->Body) + free(WebMail->Body); + if (WebMail->XML) + free(WebMail->XML); + if (WebMail->XMLName) + free(WebMail->XMLName); + + if (WebMail->OrigTo) + free(WebMail->OrigTo); + if (WebMail->OrigSubject) + free(WebMail->OrigSubject); + if (WebMail->OrigBID) + free(WebMail->OrigBID); + if (WebMail->OrigBody) + free(WebMail->OrigBody); + + freeKeys(WebMail->txtKeys); + freeKeys(WebMail->XMLKeys); + + for (i = 0; i < WebMail->Files; i++) + { + free(WebMail->FileBody[i]); + free(WebMail->FileName[i]); + } + + if (WebMail->Header) + free(WebMail->Header); + if (WebMail->Footer) + free(WebMail->Footer); + + SaveReply = WebMail->Reply; + SaveRlen = WebMail->RLen; + + memset(WebMail, 0, sizeof(WebMailInfo)); + + WebMail->Reply = SaveReply; + WebMail->RLen = SaveRlen; + + return; +} + + +void ProcessWebMailMessage(struct HTTPConnectionInfo * Session, char * Key, BOOL LOCAL, char * Method, char * NodeURL, char * input, char * Reply, int * RLen, int InputLen) +{ + char * URLParams = strlop(Key, '&'); + int ReplyLen; + char Appl = 'M'; + + // Webmail doesn't use the normal Mail Key. + + // webscript.js doesn't need a key + + if (_stricmp(NodeURL, "/WebMail/webscript.js") == 0) + { + if (jsTemplate) + free(jsTemplate); + + jsTemplate = GetTemplateFromFile(2, "webscript.js"); + + ReplyLen = sprintf(Reply, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n" + "Cache-Control: max-age=60\r\nContent-Type: text/javascript\r\n\r\n%s", (int)strlen(jsTemplate), jsTemplate); + *RLen = ReplyLen; + return; + } + + // Neither do js or file downloads + + if (_memicmp(NodeURL, "/WebMail/WMFile/", 16) == 0) + { + int FileSize; + char * MsgBytes; + char MsgFile[512]; + FILE * hFile; + size_t ReadLen; + char TimeString[64]; + char FileTimeString[64]; + struct stat STAT; + char * FN = &NodeURL[16]; + char * fileBit = FN; + char * ext; + + UndoTransparency(FN); + ext = strchr(FN, '.'); + + sprintf(MsgFile, "%s/%s", BPQDirectory, FN); + + while (strchr(fileBit, '/')) + fileBit = strlop(fileBit, '/'); + + if (stat(MsgFile, &STAT) == -1) + { + *RLen = sprintf(Reply, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n"); + return; + } + + hFile = fopen(MsgFile, "rb"); + + if (hFile == 0) + { + *RLen = sprintf(Reply, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n"); + return; + } + + FileSize = STAT.st_size; + MsgBytes = malloc(FileSize + 1); + ReadLen = fread(MsgBytes, 1, FileSize, hFile); + + fclose(hFile); + + FormatTime2(FileTimeString, STAT.st_ctime); + FormatTime2(TimeString, time(NULL)); + + if (_stricmp(ext, ".htm") == 0 || _stricmp(ext, ".html") == 0 || _stricmp(ext, ".css") == 0 || _stricmp(ext, ".js") == 0) + { + *RLen = sprintf(Reply, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n" + "Content-Type: text/css\r\n" + "Date: %s\r\n" + "Last-Modified: %s\r\n" + "\r\n%s", FileSize, TimeString, FileTimeString, MsgBytes); + } + else + { + *RLen = sprintf(Reply, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n" + "Content-Type: application/octet-stream\r\n" + "Content-Disposition: attachment; filename=\"%s\"\r\n" + "Date: %s\r\n" + "Last-Modified: %s\r\n" + "\r\n%s", FileSize, fileBit, TimeString, FileTimeString, MsgBytes); + + } + free (MsgBytes); + return; + } + + Session = NULL; + + if (Key && Key[0]) + Session = FindWMSession(Key); + + if (Session == NULL) + { + // Lost Session + + if (LOCAL) + { + Session = AllocateWebMailSession(); + + Key = Session->Key; + + if (SYSOPCall[0]) + Session->User = LookupCall(SYSOPCall); + else + Session->User = LookupCall(BBSName); + + if (Session->User) + { + strcpy(NodeURL, "/WebMail/WebMail"); + Session->WebMailSkip = 0; + Session->WebMailLastUsed = time(NULL); + } + } + else + { + // Send Login Page unless Signon request + + if (_stricmp(NodeURL, "/WebMail/Signon") != 0 || strcmp(Method, "POST") != 0) + { + ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName); + *RLen = ReplyLen; + return; + } + } + } + + if (strcmp(Method, "POST") == 0) + { + if (_stricmp(NodeURL, "/WebMail/Signon") == 0) + { + char * msg = strstr(input, "\r\n\r\n"); // End of headers + char * user, * password, * Key; + char Msg[128]; + int n; + + if (msg) + { + struct UserInfo * User; + + if (strstr(msg, "Cancel=Cancel")) + { + *RLen = sprintf(Reply, ""); + return; + } + // Webmail Gets Here with a dummy Session + + Session = AllocateWebMailSession(); + Session->WebMail->Reply = Reply; + Session->WebMail->RLen = RLen; + + + Key = Session->Key; + + user = strtok_s(&msg[9], "&", &Key); + password = strtok_s(NULL, "=", &Key); + password = Key; + + Session->User = User = LookupCall(user); + + if (User) + { + // Check Password + + if (password[0] && strcmp(User->pass, password) == 0) + { + // send Message Index + + Session->WebMailLastUsed = time(NULL); + Session->WebMailSkip = 0; + Session->WebMailMine = FALSE; + + if (WebMailTemplate) + { + free(WebMailTemplate); + WebMailTemplate = NULL; + } + + if (User->flags & F_Excluded) + { + n = sprintf_s(Msg, sizeof(Msg), "Webmail Connect from %s Rejected by Exclude Flag", _strupr(user)); + WriteLogLine(NULL, '|',Msg, n, LOG_BBS); + ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName); + *RLen = ReplyLen; + return; + } + + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + n=sprintf_s(Msg, sizeof(Msg), "Webmail Connect from %s", _strupr(user)); + WriteLogLine(NULL, '|',Msg, n, LOG_BBS); + + return; + } + + } + + // Bad User or Pass + + ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName); + *RLen = ReplyLen; + return; + } + } + + Session->WebMail->Reply = Reply; + Session->WebMail->RLen = RLen; + + if (_stricmp(NodeURL, "/WebMail/EMSave") == 0) + { + // Save New Message + + SaveNewMessage(Session, input, Reply, RLen, Key, InputLen); + return; + } + + if (_stricmp(NodeURL, "/WebMail/Submit") == 0) + { + // Get the POST data from the page and place in message + + char * param = strstr(input, "\r\n\r\n"); // End of headers + WebMailInfo * WebMail = Session->WebMail; + + if (WebMail == NULL) + return; // Can't proceed if we have no info on form + + ProcessFormInput(Session, input, Reply, RLen, InputLen); + return; + } + + if (_stricmp(NodeURL, "/WebMail/FormMsgSave") == 0) + { + // Save New Message + + SaveTemplateMessage(Session, input, Reply, RLen, Key); + return; + } + + if (_stricmp(NodeURL, "/WebMail/GetTemplates") == 0) + { + SendTemplateSelectScreen(Session, input, InputLen); + return; + } + + // End of POST section + } + + if (_stricmp(NodeURL, "/WebMail/WMLogout") == 0) + { + Session->Key[0] = 0; + Session->WebMailLastUsed = 0; + ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName); + *RLen = ReplyLen; + return; + } + + if ((_stricmp(NodeURL, "/WebMail/MailEntry") == 0) || + (_stricmp(NodeURL, "/WebMail") == 0) || + (_stricmp(NodeURL, "/WebMail/") == 0)) + { + // Entry from Menu if signed in, continue. If not and Localhost + // signin as sysop. + + if (Session->User == NULL) + { + // Not yet signed in + + if (LOCAL) + { + // Webmail Gets Here with a dummy Session + + Session = AllocateWebMailSession(); + Session->WebMail->Reply = Reply; + Session->WebMail->RLen = RLen; + + Key = Session->Key; + + if (SYSOPCall[0]) + Session->User = LookupCall(SYSOPCall); + else + Session->User = LookupCall(BBSName); + + if (Session->User) + { + strcpy(NodeURL, "/WebMail/WebMail"); + Session->WebMailSkip = 0; + Session->WebMailLastUsed = time(NULL); + } + } + else + { + // Send Login Page + + ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName); + *RLen = ReplyLen; + return; + } + } + } + + Session->WebMail->Reply = Reply; + Session->WebMail->RLen = RLen; + + if (_stricmp(NodeURL, "/WebMail/WebMail") == 0) + { + if (WebMailTemplate) + { + free(WebMailTemplate); + WebMailTemplate = NULL; + } + + Session->WebMailSkip = 0; + Session->WebMailMine = FALSE; + + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + return; + } + + if (_stricmp(NodeURL, "/WebMail/WMAll") == 0) + { + Session->WebMailSkip = 0; + Session->WebMailTypes[0] = 0; + Session->WebMailMine = FALSE; + + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + return; + } + + if (_stricmp(NodeURL, "/WebMail/WMB") == 0) + { + Session->WebMailSkip = 0; + strcpy(Session->WebMailTypes, "B"); + Session->WebMailMine = FALSE; + + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + return; + } + + if (_stricmp(NodeURL, "/WebMail/WMP") == 0) + { + Session->WebMailSkip = 0; + strcpy(Session->WebMailTypes, "P"); + Session->WebMailMine = FALSE; + + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + return; + } + + if (_stricmp(NodeURL, "/WebMail/WMT") == 0) + { + Session->WebMailSkip = 0; + strcpy(Session->WebMailTypes, "T"); + Session->WebMailMine = FALSE; + + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + return; + } + + if (_stricmp(NodeURL, "/WebMail/WMMine") == 0) + { + Session->WebMailSkip = 0; + Session->WebMailTypes[0] = 0; + Session->WebMailMine = TRUE; + + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + return; + } + + + if (_stricmp(NodeURL, "/WebMail/WMSame") == 0) + { + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + return; + } + + if (_stricmp(NodeURL, "/WebMail/WMAuto") == 0) + { + // Auto Refresh Version of index page. Uses Web Sockets + + char Page[4096]; + + char WebSockPage[] = + "\r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + "\r\n" + "\r\n" + + "\r\n" + "WebMail \r\n" + "\r\n" + + "\r\n" + "

%s Webmail Interface - User %s - Message List

\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "
BullsPersonalNTSAll TypesMineAuto RefreshSend MessageLogoutNode Menu
\r\n" + "
\r\n" + + "
Waiting for data...
\r\n" + "\r\n"; + + sprintf(Page, WebSockPage, Key, Key ,BBSName, Session->User->Call, Key, Key, Key, Key, Key, Key, Key, Key); + + *RLen = sprintf(Reply, "%s", Page); + return; + } + + + if (memcmp(NodeURL, "/WebMail/QuoteOriginal/", 15) == 0) + { + // Reply to Message + + int n, len; + struct MsgInfo * Msg; + char Message[100] = ""; + char Title[100]; + char * MsgBytes, * Save, * NewBytes; + char * ptr; + char * ptr1, * ptr2; + char * EncodedTitle; + + n = Session->WebMail->CurrentMessageIndex; + + Msg = GetMsgFromNumber(n); + + if (Msg == NULL) + { + sprintf(Message, "Message %d not found", n); + *RLen = sprintf(Reply, "%s", Message); + return; + } + + Session->WebMail->Msg = Msg; + + if (stristr(Msg->title, "Re:") == 0) + sprintf(Title, "Re:%s", Msg->title); + else + sprintf(Title, "%s", Msg->title); + + MsgBytes = Save = ReadMessageFile(n); + + + ptr = NewBytes = malloc((Msg->length * 2) + 256); + + // Copy a line at a time with "> " in front of each + + ptr += sprintf(ptr, "%s", "\r\n\r\n\r\n\r\n\r\nOriginal Message\r\n\r\n> "); + + ptr1 = ptr2 = MsgBytes; + len = (int)strlen(MsgBytes); + + while (len-- > 0) + { + *ptr++ = *ptr1; + + if (*(ptr1) == '\n') + { + *ptr++ = '>'; + *ptr++ = ' '; + } + + ptr1++; + } + + *ptr++ = 0; + + EncodedTitle = doXMLTransparency(Msg->title); + + *RLen = sprintf(Reply, MsgInputPage, Key, Msg->from, "", EncodedTitle , NewBytes); + + free(EncodedTitle); + + free(MsgBytes); + free(NewBytes); + + return; + } + + + + if (memcmp(NodeURL, "/WebMail/Reply/", 15) == 0) + { + // Reply to Message + + int n = atoi(&NodeURL[15]); + struct MsgInfo * Msg; + char Message[100] = ""; + char Title[100]; + char * EncodedTitle; + + // Quote Original + + char Button[] = + "      " + ""; + + char Temp[1024]; + char ReplyAddr[128]; + + Msg = GetMsgFromNumber(n); + + if (Msg == NULL) + { + sprintf(Message, "Message %d not found", n); + *RLen = sprintf(Reply, "%s", Message); + return; + } + + Session->WebMail->Msg = Msg; + + // See if the message was displayed in an HTML form with a reply template + + *RLen = ReplyToFormsMessage(Session, Msg, Reply, FALSE); + + // If couldn't build reply form use normal text reply + + if (*RLen) + return; + + + sprintf(Temp, Button, Key); + + if (stristr(Msg->title, "Re:") == 0) + sprintf(Title, "Re:%s", Msg->title); + else + sprintf(Title, "%s", Msg->title); + + strcpy(ReplyAddr, Msg->from); + strcat(ReplyAddr, Msg->emailfrom); + + EncodedTitle = doXMLTransparency(Msg->title); + + *RLen = sprintf(Reply, MsgInputPage, Key, Msg->from, Temp, EncodedTitle , ""); + + free(EncodedTitle); + return; + } + + if (strcmp(NodeURL, "/WebMail/WM") == 0) + { + // Read Message + + int n = 0; + + if (URLParams) + n = atoi(URLParams); + + if (WebMailMsgTemplate) + free(WebMailMsgTemplate); + + WebMailMsgTemplate = GetTemplateFromFile(5, "WebMailMsg.txt"); + + *RLen = ViewWebMailMessage(Session, Reply, n, TRUE); + + return; + } + + if (strcmp(NodeURL, "/WebMail/WMPrev") == 0) + { + // Read Previous Message + + int m; + struct MsgInfo * Msg; + struct UserInfo * User = Session->User; + + + for (m = Session->WebMail->CurrentMessageIndex - 1; m >= 1; m--) + { + + Msg = GetMsgFromNumber(m); + + if (Msg == 0 || Msg->type == 0 || Msg->status == 0) + continue; // Protect against corrupt messages + + if (Msg && CheckUserMsg(Msg, User->Call, User->flags & F_SYSOP)) + { + // Display if it is the right type and in the page range we want + + if (Session->WebMailTypes[0] && strchr(Session->WebMailTypes, Msg->type) == 0) + continue; + + // All Types or right Type. Check Mine Flag + + if (Session->WebMailMine) + { + // Only list if to or from me + + if (strcmp(User->Call, Msg->to) != 0 && strcmp(User->Call, Msg->from) != 0) + continue; + } + + *RLen = ViewWebMailMessage(Session, Reply, m, TRUE); + + return; + } + } + + // No More + + *RLen = sprintf(Reply, "", Session->Key); + return; + + } + + if (strcmp(NodeURL, "/WebMail/WMNext") == 0) + { + // Read Previous Message + + int m; + struct MsgInfo * Msg; + struct UserInfo * User = Session->User; + + for (m = Session->WebMail->CurrentMessageIndex + 1; m <= LatestMsg; m++) + { + Msg = GetMsgFromNumber(m); + + if (Msg == 0 || Msg->type == 0 || Msg->status == 0) + continue; // Protect against corrupt messages + + if (Msg && CheckUserMsg(Msg, User->Call, User->flags & F_SYSOP)) + { + // Display if it is the right type and in the page range we want + + if (Session->WebMailTypes[0] && strchr(Session->WebMailTypes, Msg->type) == 0) + continue; + + // All Types or right Type. Check Mine Flag + + if (Session->WebMailMine) + { + // Only list if to or from me + + if (strcmp(User->Call, Msg->to) != 0 && strcmp(User->Call, Msg->from) != 0) + continue; + } + + *RLen = ViewWebMailMessage(Session, Reply, m, TRUE); + + return; + } + } + + // No More + + *RLen = sprintf(Reply, "", Session->Key); + return; + + } + + + if (strcmp(NodeURL, "/WebMail/DisplayText") == 0) + { + // Read Message + + int n = 0; + + if (URLParams) + n = atoi(URLParams); + + if (WebMailMsgTemplate) + free(WebMailMsgTemplate); + + WebMailMsgTemplate = GetTemplateFromFile(5, "WebMailMsg.txt"); + + *RLen = ViewWebMailMessage(Session, Reply, n, FALSE); + + return; + } + if (memcmp(NodeURL, "/WebMail/WMDel/", 15) == 0) + { + // Kill Message + + int n = atoi(&NodeURL[15]); + + *RLen = KillWebMailMessage(Reply, Session->Key, Session->User, n); + + return; + } + + if (_stricmp(NodeURL, "/WebMail/NewMsg") == 0) + { + // Add HTML Template Button if we have any HTML Form + + char Button[] = + "      " + ""; + + char Temp[1024]; + + FreeWebMailFields(Session->WebMail); // Tidy up for new message + + sprintf(Temp, Button, Key); + + if (FormDirCount == 0) + *RLen = sprintf(Reply, MsgInputPage, Key, "", "", "", ""); + else + *RLen = sprintf(Reply, MsgInputPage, Key, "", Temp, "", ""); + + return; + } + + if (_memicmp(NodeURL, "/WebMail/GetPage/", 17) == 0) + { + // Read and Parse Template File + + GetPage(Session, NodeURL); + return; + } + + if (_memicmp(NodeURL, "/WebMail/GetList/", 17) == 0) + { + // Send Select Template Popup + + char * SubDir; + int DirNo = 0; + int SubDirNo = 0; + char popup[10000]; + + char popuphddr[] = + + "" + "" + "

" + "Select Required Template from %s

" + "

", popup); + + *RLen = sprintf(Reply, "%s", popup); + return; + } + + if (_stricmp(NodeURL, "/WebMail/DL") == 0) + { + getAttachmentList(Session, Reply, RLen, URLParams); + return; + } + + if (_stricmp(NodeURL, "/WebMail/GetDownLoad") == 0) + { + DownloadAttachments(Session, Reply, RLen, URLParams); + return; + } + + if (_stricmp(NodeURL, "/WebMail/DoSelect") == 0) + { + // User has selected item from Template "; + + char NewGroup [] = + "" + "

", popup); + + *WebMail->RLen = sprintf(WebMail->Reply, "%s", popup); + + free(Boundary); + return; +} + +static char WinlinkAddr[] = "WINLINK.ORG"; + +VOID SaveNewMessage(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest, int InputLen) +{ + int i, ReplyLen = 0; + struct MsgInfo * Msg; + FILE * hFile; + int Template=0; + char * via = NULL; + BIDRec * BIDRec; + char MsgFile[MAX_PATH]; + size_t WriteLen=0; + char * HDest; + char * HDestCopy; + char * HDestRest; + char * Vptr = NULL; + char * FileList = NULL; + char Prompt[256] = "Message Saved"; + char OrigTo[256]; + WebMailInfo * WebMail = Session->WebMail; + struct UserInfo * user; + CIRCUIT conn; + + // So we can have attachments input is now Content-Type: multipart/form-data; + + char * Input; + size_t MsgLen = 0; + char * Boundary; + + strcpy(conn.Callsign, Session->User->Call); + + Input = MsgPtr; + + Boundary = initMultipartUnpack(&Input); + + if (Boundary == NULL) + return; // Can't work without one + + // Input points to start of part. Normally preceeded by \r\n which is Boundary Terminator. If preceeded by -- we have used last part + + while(Input && Input[-1] != '-') + { + char * Name, * Value; + int ValLen; + + if (unpackPart(Boundary, &Input, &Name, &Value, &ValLen, MsgPtr + InputLen) == FALSE) + { + // ReportCorrupt(WebMail); + free(Boundary); + return; + } + if (SaveInputValue(WebMail, Name, Value, ValLen) == FALSE) + { + *RLen = sprintf(Reply, "", Session->Key); + return; + } + } + + + if (WebMail->txtFileName) + { + // Processing Form Input + + SaveTemplateMessage(Session, MsgPtr, Reply, RLen, Rest); + + // Prevent re-entry + + free(WebMail->txtFileName); + WebMail->txtFileName = NULL; + + return; + } + + // If we aren't using a template then all the information is in the WebMail fields, as we haven't been here before. + + strlop(WebMail->BID, ' '); + if (strlen(WebMail->BID) > 12) + WebMail->BID[12] = 0; + + UndoTransparency(WebMail->BID); + UndoTransparency(WebMail->To); + UndoTransparency(WebMail->Subject); + UndoTransparency(WebMail->Body); + + MsgLen = strlen(WebMail->Body); + + // We will need to mess about with To field. Create a copy so the original can go in B2 header if we use one + + if (WebMail->To[0] == 0) + { + *RLen = sprintf(Reply, "%s", ""); + FreeWebMailFields(WebMail); // We will reprocess message and attachments so reinitialise + return; + } + + if (strlen(WebMail->To) > 255) + { + *RLen = sprintf(Reply, "%s", ""); + FreeWebMailFields(WebMail); // We will reprocess message and attachments so reinitialise + return; + } + + HDest = _strdup(WebMail->To); + + if (strlen(WebMail->BID)) + { + if (LookupBID(WebMail->BID)) + { + // Duplicate bid + *RLen = sprintf(Reply, "%s", ""); + FreeWebMailFields(WebMail); // We will reprocess message and attachments so reinitialise + return; + } + } + + if (WebMail->Type == 'B') + { + if (RefuseBulls) + { + *RLen = sprintf(Reply, "%s", ""); + FreeWebMailFields(WebMail); // We will reprocess message and attachments so reinitialise + return; + } + } + + // ?? Can we just loop though the rest of the code to allow multiple dests ?? + + HDestCopy = HDest; + + while (HDest && HDest[0]) + { + HDestRest = strlop(HDest, ';'); + + Msg = AllocateMsgRecord(); + + // Set number here so they remain in sequence + + Msg->number = ++LatestMsg; + MsgnotoMsg[Msg->number] = Msg; + + strcpy(Msg->from, Session->User->Call); + + if (_memicmp(HDest, "rms:", 4) == 0 || _memicmp(HDest, "rms/", 4) == 0) + { + Vptr=&HDest[4]; + strcpy(Msg->to, "RMS"); + } + else if (_memicmp(HDest, "smtp:", 5) == 0) + { + if (ISP_Gateway_Enabled) + { + Vptr=&HDest[5]; + Msg->to[0] = 0; + } + } + else if (strchr(HDest, '@')) + { + strcpy(OrigTo, HDest); + + Vptr = strlop(HDest, '@'); + + if (Vptr) + { + // If looks like a valid email address, treat as such + + if (strlen(HDest) > 6 || !CheckifPacket(Vptr)) + { + // Assume Email address + + Vptr = OrigTo; + + if (FindRMS() || strchr(Vptr, '!')) // have RMS or source route + strcpy(Msg->to, "RMS"); + else if (ISP_Gateway_Enabled) + Msg->to[0] = 0; + else if (isAMPRMsg(OrigTo)) + strcpy(Msg->to, "RMS"); // Routing will redirect it + else + { + *RLen = sprintf(Reply, "%s", ""); + FreeWebMailFields(WebMail); // We will reprocess message and attachments so reinitialise + return; + + } + } + else + strcpy(Msg->to, _strupr(HDest)); + } + } + else + { + // No @ + + if (strlen(HDest) > 6) + HDest[6] = 0; + + strcpy(Msg->to, _strupr(HDest)); + } + + if (SendBBStoSYSOPCall) + if (_stricmp(HDest, BBSName) == 0) + strcpy(Msg->to, SYSOPCall); + + if (Vptr) + { + if (strlen(Vptr) > 40) + Vptr[40] = 0; + + strcpy(Msg->via, _strupr(Vptr)); + } + else + { + // No via. If not local user try to add BBS + + struct UserInfo * ToUser = LookupCall(Msg->to); + + if (ToUser) + { + // Local User. If Home BBS is specified, use it + + if (ToUser->flags & F_RMSREDIRECT) + { + // sent to Winlink + + strcpy(Msg->via, WinlinkAddr); + sprintf(Prompt, "Redirecting to winlink.org\r"); + } + else if (ToUser->HomeBBS[0]) + { + strcpy(Msg->via, ToUser->HomeBBS); + sprintf(Prompt, "%s added from HomeBBS. Message Saved", Msg->via); + } + } + else + { + // Not local user - Check WP + + WPRecP WP = LookupWP(Msg->to); + + if (WP) + { + strcpy(Msg->via, WP->first_homebbs); + sprintf(Prompt, "%s added from WP", Msg->via); + } + } + } + + if (strlen(WebMail->Subject) > 60) + WebMail->Subject[60] = 0; + + strcpy(Msg->title, WebMail->Subject); + Msg->type = WebMail->Type; + + if (Session->User->flags & F_HOLDMAIL) + { + int Length=0; + char * MailBuffer = malloc(100); + char Title[100]; + + Msg->status = 'H'; + + Length = sprintf(MailBuffer, "Message %d Held\r\n", Msg->number); + sprintf(Title, "Message %d Held - %s", Msg->number, "User has Hold Messages flag set"); + SendMessageToSYSOP(Title, MailBuffer, Length); + } + else + Msg->status = 'N'; + + if (strlen(WebMail->BID) == 0) + sprintf(Msg->bid, "%d_%s", LatestMsg, BBSName); + else + strcpy(Msg->bid, WebMail->BID); + + Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + BIDRec = AllocateBIDRecord(); + + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + Msg->length = (int)MsgLen + WebMail->HeaderLen + WebMail->FooterLen; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); + + // BuildFormMessage(Session, Msg); + + if (WebMail->Files) + { + // Send as B2 + + char * B2Header = BuildB2Header(WebMail, Msg, NULL, 0); + + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + WriteLen = fwrite(B2Header, 1, strlen(B2Header), hFile); + WriteLen += fwrite(WebMail->Header, 1, WebMail->HeaderLen, hFile); + WriteLen += fwrite(WebMail->Body, 1, MsgLen, hFile); + WriteLen += fwrite(WebMail->Footer, 1, WebMail->FooterLen, hFile); + WriteLen += fwrite("\r\n", 1, 2, hFile); + + for (i = 0; i < WebMail->Files; i++) + { + WriteLen += fwrite(WebMail->FileBody[i], 1, WebMail->FileLen[i], hFile); + WriteLen += fwrite("\r\n", 1, 2, hFile); + } + fclose(hFile); + free(B2Header); + + Msg->length = (int)WriteLen; + } + } + else + { + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + WriteLen += fwrite(WebMail->Header, 1, WebMail->HeaderLen, hFile); + WriteLen += fwrite(WebMail->Body, 1, MsgLen, hFile); + WriteLen += fwrite(WebMail->Footer, 1, WebMail->FooterLen, hFile); + fclose(hFile); + } + } + MatchMessagetoBBSList(Msg, &conn); + + BuildNNTPList(Msg); // Build NNTP Groups list + + if (EnableUI) + SendMsgUI(Msg); + + user = LookupCall(Msg->to); + + if (user && (user->flags & F_APRSMFOR)) + { + char APRS[128]; + char Call[16]; + int SSID = user->flags >> 28; + + if (SSID) + sprintf(Call, "%s-%d", Msg->to, SSID); + else + strcpy(Call, Msg->to); + + sprintf(APRS, "New BBS Message %s From %s", Msg->title, Msg->from); + APISendAPRSMessage(APRS, Call); + } + + HDest = HDestRest; + } + + *RLen = SendWebMailHeaderEx(Reply, Session->Key, Session, Prompt); + + SaveMessageDatabase(); + SaveBIDDatabase(); + FreeWebMailFields(WebMail); + free(HDestCopy); + + return; +} + + + + + + +// RMS Express Forms Support + +char * GetHTMLViewerTemplate(char * FN) +{ + int i, j, k, l; + + // Seach list of forms for base file (without .html) + + for (i = 0; i < FormDirCount; i++) + { + struct HtmlFormDir * Dir = HtmlFormDirs[i]; + + for (j = 0; j < Dir->FormCount; j++) + { + if (strcmp(FN, Dir->Forms[j]->FileName) == 0) + { + return CheckFile(Dir, FN); + } + } + + if (Dir->DirCount) + { + for (l = 0; l < Dir->DirCount; l++) + { + for (k = 0; k < Dir->Dirs[l]->FormCount; k++) + { + if (strcmp(FN, Dir->Dirs[l]->Forms[k]->FileName) == 0) + { + return CheckFile(Dir, Dir->Dirs[l]->Forms[k]->FileName); + } + } + } + } + } + + return NULL; +} +VOID GetReply(struct HTTPConnectionInfo * Session, char * NodeURL) +{ +} + +VOID GetPage(struct HTTPConnectionInfo * Session, char * NodeURL) +{ + // Read the HTML Template file and do any needed substitutions + + WebMailInfo * WebMail = Session->WebMail; + KeyValues * txtKey = WebMail->txtKeys; + + int DirNo; + char * ptr; + int FileNo = 0; + char * SubDir; + int SubDirNo; + int i; + struct HtmlFormDir * Dir; + char * Template; + char * inptr; + char FormDir[MAX_PATH] = ""; + char FN[MAX_PATH] = ""; + char * InputName = NULL; // HTML to input message + char * ReplyName = NULL; + char * To = NULL; + char * CC = NULL; + char * BID = NULL; + char Type = 0; + char * Subject = NULL; + char * MsgBody = NULL; + char * varptr; + char * endptr; + size_t varlen, vallen = 0; + char val[256]=""; // replacement text + char var[100] = "\""; + char * MsgBytes; + char Submit[64]; + + if (NodeURL == NULL) + { + //rentry after processing or substitutions + + // if Dir not specified search all for Filename + + if (Dir == NULL) + { + for (i = 0; i < FormDirCount; i++) + { + int n; + + Dir = HtmlFormDirs[i]; + + MsgBytes = CheckFile(Dir, WebMail->txtFileName); + if (MsgBytes) + goto gotFile; + + // Recurse any Subdirs + + n = 0; + while (n < Dir->DirCount) + { + MsgBytes = CheckFile(Dir->Dirs[n], FN); + if (MsgBytes) + { + Dir = Dir->Dirs[n]; + goto gotFile; + } + n++; + } + } + return; + } + else + MsgBytes = CheckFile(Dir, WebMail->txtFileName); + +gotFile: + + WebMail->Dir = Dir; + + if (WebMail->txtFile) + free(WebMail->txtFile); + + WebMail->txtFile = MsgBytes; + +reEnter: + + if (ParsetxtTemplate(Session, Dir, WebMail->txtFileName, FALSE) == FALSE) + { + // Template has or + + if (WebMail->InputHTMLName == NULL) + { + // This is a plain text template without HTML +/* + if (To == NULL) + To = ""; + + if (To[0] == 0 && WebMail->To && WebMail->To[0]) + To = WebMail->To; + + if (CC == NULL) + CC = ""; + + if (CC[0] == 0 && WebMail->CC && WebMail->CC[0]) + CC = WebMail->CC; + + if (Subject == NULL) + Subject = ""; + + if (Subject[0] == 0 && WebMail->Subject && WebMail->Subject[0]) + Subject = WebMail->Subject; + + if (MsgBody == NULL) + MsgBody = ""; + + if (MsgBody[0] == 0 && WebMail->Body && WebMail->Body[0]) + MsgBody = WebMail->Body; + + *WebMail->RLen = sprintf(WebMail->Reply, CheckFormMsgPage, Session->Key, To, CC, Subject, MsgBody); + */ + return *WebMail->RLen; + } + + Template = CheckFile(WebMail->Dir, WebMail->InputHTMLName); + + if (Template == NULL) + { + // Missing HTML + + *WebMail->RLen = sprintf(WebMail->Reply, "", WebMail->InputHTMLName); + return *WebMail->RLen; + } + + // I've going to update the template in situ, as I can't see a better way + // of making sure all occurances of variables in any order are substituted. + // The space allocated to Template is twice the size of the file + // to allow for insertions + + UpdateFormAction(Template, Session->Key); // Update "Submit" Action + + // Search for "{var }" strings in form and replace with + // corresponding variable from XML + + while (txtKey->Key) + { + char Key[256] = "{var "; + + strcpy(&Key[5], txtKey->Key); + strcat(Key, "}"); + + inptr = Template; + varptr = stristr(inptr, Key); + + while (varptr) + { + // Move the remaining message up/down the buffer to make space for substitution + + varlen = (int)strlen(Key); + if (txtKey->Value) + vallen = (int)strlen(txtKey->Value); + else vallen = 0; + + endptr = varptr + varlen; + + memmove(varptr + vallen, endptr, strlen(endptr) + 1); // copy null on end + memcpy(varptr, txtKey->Value, vallen); + + inptr = endptr + 1; + + varptr = stristr(inptr, Key); + } + txtKey++; + } + + // Remove from end as we add it on later + + ptr = stristr(Template, ""); + + if (ptr) + *ptr = 0; + + Len = sprintf(Reply, "%s", Template); + free(Template); + return Len; +} + +char * CheckFile(struct HtmlFormDir * Dir, char * FN) +{ + struct stat STAT; + FILE * hFile; + char MsgFile[MAX_PATH]; + char * MsgBytes; + int ReadLen; + int FileSize; + +#ifndef WIN32 + + // Need to do case insensitive file search + + DIR *dir; + struct dirent *entry; + char name[256]; + + sprintf(name, "%s/%s/%s", BPQDirectory, Dir->FormSet, Dir->DirName); + + if (!(dir = opendir(name))) + { + Debugprintf("cant open forms dir %s %s %d", Dir->DirName, name, errno); + return 0; + } + + while ((entry = readdir(dir)) != NULL) + { + if (entry->d_type == DT_DIR) + continue; + + if (stricmp(entry->d_name, FN) == 0) + { + sprintf(MsgFile, "%s/%s/%s/%s", GetBPQDirectory(), Dir->FormSet, Dir->DirName, entry->d_name); + break; + } + } + closedir(dir); + +#else + + sprintf(MsgFile, "%s/%s/%s/%s", GetBPQDirectory(), Dir->FormSet, Dir->DirName, FN); + +#endif + + if (stat(MsgFile, &STAT) != -1) + { + hFile = fopen(MsgFile, "rb"); + + if (hFile == 0) + { + MsgBytes = _strdup("File is missing"); + return MsgBytes; + } + + FileSize = STAT.st_size; + MsgBytes = malloc(FileSize * 10); // Allow plenty of room for template substitution + ReadLen = (int)fread(MsgBytes, 1, FileSize, hFile); + MsgBytes[FileSize] = 0; + fclose(hFile); + + return MsgBytes; + } + return NULL; +} + +BOOL DoSelectPrompt(struct HTTPConnectionInfo * Session, char * Select) +{ + // Send a Popup window to select value. Reply handling code will update template then reenter ParsetxtTemplate + + char popuphddr[] = + + "" + "" + "
%s

" + "" + "
'); + + if (ptr) + *ptr = 0; + + ptr = SelCopy; + + if (*ptr == '"') + { + // String has " " round it + + ptr++; + + ptr1 = strchr(ptr, '"'); + if (ptr1 == NULL) + goto returnDuff; + + *(ptr1++) = 0; + prompt = ptr; + } + else + { + // Normal comma terminated + + ptr1 = strchr(ptr, ','); + if (ptr1 == NULL) + goto returnDuff; + + *(ptr1++) = 0; + prompt = ptr; + } + + ptr = ptr1; + + while (ptr && ptr[0]) + { + if (*ptr == '"') + { + // String has " " round it + + ptr++; + + ptr1 = strchr(ptr, '"'); + if (ptr1 == NULL) + goto returnDuff; + + *(ptr1++) = 0; + while(ptr1 && *ptr1 == ',') + ptr1++; + } + else + { + // Normal comma terminated + + ptr1 = strchr(ptr, ','); + if (ptr1) + *(ptr1++) = 0; + } + + var[vars++] = ptr; + + ptr = ptr1; + } + + sprintf(popup, popuphddr, Session->Key, prompt, vars + 1); + + for (i = 0; i < vars; i++) + { + char * key = strlop(var[i], '='); + + if (key == NULL) + key = var[i]; + + sprintf(popup, "%s

", popup); + + *WebMail->RLen = sprintf(WebMail->Reply, "%s", popup); + free(SelCopy); + return TRUE; + +returnDuff: + *WebMail->RLen = sprintf(WebMail->Reply, "", Session->Key); + free(SelCopy); + return TRUE; + +} + +BOOL DoAskPrompt(struct HTTPConnectionInfo * Session, char * Select) +{ + return TRUE; +} + +VOID ProcessSelectResponse(struct HTTPConnectionInfo * Session, char * URLParams) +{ + // User has entered a response for a Template RLen = sprintf(WebMail->Reply, "", Session->Key); + return; + } + + varptr = Select; + endptr = strchr(Select, '>'); + + if (endptr == NULL) + { + *WebMail->RLen = sprintf(WebMail->Reply, "", Session->Key); + return; + } + + *endptr = 0; + varlen = endptr - varptr; + + + valptr = URLParams; + + // Move the remaining message up/down the buffer to make space for substitution + + vallen = strlen(valptr); + + endptr = varptr + varlen; + + memcpy(varptr, valptr, vallen); + memmove(varptr + vallen, endptr + 1, strlen(endptr + 1) + 1); // copy null on end + + if (WebMail->isReply) + *WebMail->RLen = ReplyToFormsMessage(Session, Session->Msg, WebMail->Reply, TRUE); + else + GetPage(Session, NULL); + + return ; +} + +BOOL ParsetxtTemplate(struct HTTPConnectionInfo * Session, struct HtmlFormDir * Dir, char * FN, BOOL isReply) +{ + WebMailInfo * WebMail = Session->WebMail; + KeyValues * txtKey = WebMail->txtKeys; + + char * MsgBytes; + + char * txtFile; + char * ptr, *ptr1; + char * InputName = NULL; // HTML to input message + char * ReplyName = NULL; + char * To = NULL; + char * Subject = NULL; + char * MsgBody = NULL; + char * Select = NULL; + char * Ask = NULL; + + char Date[16]; + char UDate[16]; + char DateTime[32]; + char UDateTime[32]; + char Day[16]; + char UDay[16]; + char UDTG[32]; + char Seq[16]; + char FormDir[MAX_PATH]; + double Lat; + double Lon; + char LatString[32], LonString[32], GPSString[32]; + BOOL GPSOK; + + struct tm * tm; + time_t NOW; + + // Template is now read before entering here + + MsgBytes = WebMail->txtFile; + + // if Template uses tm_year + 1900,tm->tm_mon + 1, tm->tm_mday); + + sprintf(DateTime, "%04d-%02d-%02d %02d:%02d:%02d", + tm->tm_year + 1900,tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + + strcpy(Day, longday[tm->tm_wday]); + + tm = gmtime(&NOW); + + sprintf(UDate, "%04d-%02d-%02dZ", + tm->tm_year + 1900,tm->tm_mon + 1, tm->tm_mday); + + sprintf(UDateTime, "%04d-%02d-%02d %02d:%02d:%02dZ", + tm->tm_year + 100,tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + + sprintf(UDTG, "%02d%02d%02dZ %s %04d", + tm->tm_mday, tm->tm_hour, tm->tm_min, month[tm->tm_mon], tm->tm_year + 1900); + + strcpy(UDay, longday[tm->tm_wday]); + + sprintf(Seq, "%d", Session->User->WebSeqNo); + sprintf(FormDir, "/WebMail/WMFile/%s/%s/", WebMail->Dir->FormSet, WebMail->Dir->DirName); + + // Keep SeqNo at front + + txtKey->Key = _strdup(""); + txtKey++->Value = _strdup(Seq); + + txtKey->Key = _strdup(""); + txtKey++->Value = _strdup(DateTime); + txtKey->Key = _strdup(""); + txtKey++->Value = _strdup(UDateTime); + txtKey->Key = _strdup(""); + txtKey++->Value = _strdup(Date); + txtKey->Key = _strdup(""); + txtKey++->Value = _strdup(UDate); + txtKey->Key = _strdup("