6.0.23.25

This commit is contained in:
g8bpq 2022-11-12 15:00:02 +00:00
parent 9b5f7ccff6
commit e3db09d255
28 changed files with 1245 additions and 141 deletions

View File

@ -445,7 +445,7 @@ void ProcessMailHTTPMessage(struct HTTPConnectionInfo * Session, char * Method,
if (strstr(input, "Host: 127.0.0.1"))
LOCAL = TRUE;
if (Session->TNC == 1)
if (Session->TNC == (void *)1) // Re-using an address as a flag
LOCAL = TRUE;
NodeURL = strtok_s(URL, "?", &Context);
@ -2842,6 +2842,8 @@ int SendUserDetails(struct HTTPConnectionInfo * Session, char * Reply, char * Ke
#ifdef WIN32
int ProcessWebmailWebSock(char * MsgPtr, char * OutBuffer);
static char PipeFileName[] = "\\\\.\\pipe\\BPQMailWebPipe";
static DWORD WINAPI InstanceThread(LPVOID lpvParam)
@ -2867,16 +2869,16 @@ static DWORD WINAPI InstanceThread(LPVOID lpvParam)
char * ptr;
// Debugprintf("InstanceThread created, receiving and processing messages.");
// The thread's parameter is a handle to a pipe object instance.
// The thread's parameter is a handle to a pipe object instance.
hPipe = (HANDLE) lpvParam;
// Read client requests from the pipe. This simplistic code only allows messages
// up to BUFSIZE characters in length.
// First block is the HTTPConnectionInfo record, rest is request
n = ReadFile(hPipe, &Session, sizeof (struct HTTPConnectionInfo), &n, NULL);
// Get the data
fSuccess = ReadFile(hPipe, Buffer, 250000, &InputLen, NULL);
if (!fSuccess || InputLen == 0)
@ -2885,13 +2887,20 @@ static DWORD WINAPI InstanceThread(LPVOID lpvParam)
Debugprintf("InstanceThread: client disconnected.", GetLastError());
else
Debugprintf("InstanceThread ReadFile failed, GLE=%d.", GetLastError());
return 1;
}
else
{
Buffer[InputLen] = 0;
MsgPtr = &Buffer[0];
if (memcmp(MsgPtr, "WMRefresh", 9) == 0)
{
OutputLen = ProcessWebmailWebSock(MsgPtr, OutBuffer);
}
else
{
strcpy(URL, MsgPtr);
ptr = strstr(URL, " HTTP");
@ -2902,6 +2911,7 @@ static DWORD WINAPI InstanceThread(LPVOID lpvParam)
Method = strtok_s(URL, " ", &Context);
ProcessMailHTTPMessage(&Session, Method, Context, MsgPtr, OutBuffer, &OutputLen, InputLen);
}
WriteFile(hPipe, &Session, sizeof (struct HTTPConnectionInfo), &n, NULL);
WriteFile(hPipe, OutBuffer, OutputLen, &cbWritten, NULL);
@ -2909,7 +2919,7 @@ static DWORD WINAPI InstanceThread(LPVOID lpvParam)
FlushFileBuffers(hPipe);
DisconnectNamedPipe(hPipe);
CloseHandle(hPipe);
}
return 1;
}

View File

@ -58,10 +58,13 @@ extern char LOC[7];
extern BPQVECSTRUC ** BPQHOSTVECPTR;
UCHAR * GetLogDirectory();
DllExport int APIENTRY SessionStateNoAck(int stream, int * state);
int RefreshWebMailIndex();
#else
__declspec(dllimport) BPQVECSTRUC ** BPQHOSTVECPTR;
typedef char * (WINAPI FAR *FARPROCZ)();
typedef int (WINAPI FAR *FARPROCX)();
FARPROCZ pGetLOC;
FARPROCX pRefreshWebMailIndex;
#endif
@ -1507,6 +1510,13 @@ VOID SaveMessageDatabase()
// SaveConfig(ConfigName); // Message Headers now in main config
// return;
#ifdef LINBPQ
RefreshWebMailIndex();
#else
if (pRefreshWebMailIndex)
pRefreshWebMailIndex();
#endif
Handle = fopen(MsgDatabasePath, "wb");
if (Handle == NULL)

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioUserFile
ProjectType="Visual C++"
Version="8.00"
ShowAllFiles="false"
>
<Configurations>
<Configuration
Name="Debug|Win32"
>
<DebugSettings
Command="$(TargetPath)"
WorkingDirectory=""
CommandArguments=""
Attach="false"
DebuggerType="3"
Remote="1"
RemoteMachine="DESKTOP-TGEL8RC"
RemoteCommand=""
HttpUrl=""
PDBPath=""
SQLDebugging=""
Environment=""
EnvironmentMerge="true"
DebuggerFlavor=""
MPIRunCommand=""
MPIRunArguments=""
MPIRunWorkingDirectory=""
ApplicationCommand=""
ApplicationArguments=""
ShimCommand=""
MPIAcceptMode=""
MPIAcceptFilter=""
/>
</Configuration>
<Configuration
Name="Release|Win32"
>
<DebugSettings
Command="$(TargetPath)"
WorkingDirectory=""
CommandArguments=""
Attach="false"
DebuggerType="3"
Remote="1"
RemoteMachine="DESKTOP-TGEL8RC"
RemoteCommand=""
HttpUrl=""
PDBPath=""
SQLDebugging=""
Environment=""
EnvironmentMerge="true"
DebuggerFlavor=""
MPIRunCommand=""
MPIRunArguments=""
MPIRunWorkingDirectory=""
ApplicationCommand=""
ApplicationArguments=""
ShimCommand=""
MPIAcceptMode=""
MPIAcceptFilter=""
/>
</Configuration>
</Configurations>
</VisualStudioUserFile>

View File

@ -1101,7 +1101,7 @@
// Fix ' in Webmail subject (8)
// Change web buttons to white on black when pressed (10)
// Add auto-refresh option to Webmail index page (25)
#include "bpqmail.h"
#define MAIL
@ -1116,6 +1116,7 @@ typedef int (WINAPI FAR *FARPROCZ)();
FARPROCX pDllBPQTRACE;
FARPROCZ pGetLOC;
FARPROCX pRefreshWebMailIndex;
BOOL WINE = FALSE;
@ -1879,6 +1880,7 @@ BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
pDllBPQTRACE = GetProcAddress(ExtDriver,"_DllBPQTRACE@8");
pGetLOC = GetProcAddress(ExtDriver,"_GetLOC@0");
pRefreshWebMailIndex = GetProcAddress(ExtDriver,"_RefreshWebMailIndex@0");
if (pGetLOC)
{

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioUserFile
ProjectType="Visual C++"
Version="8.00"
ShowAllFiles="false"
>
<Configurations>
<Configuration
Name="Debug|Win32"
>
<DebugSettings
Command="$(TargetPath)"
WorkingDirectory=""
CommandArguments=""
Attach="false"
DebuggerType="3"
Remote="1"
RemoteMachine="DESKTOP-TGEL8RC"
RemoteCommand=""
HttpUrl=""
PDBPath=""
SQLDebugging=""
Environment=""
EnvironmentMerge="true"
DebuggerFlavor=""
MPIRunCommand=""
MPIRunArguments=""
MPIRunWorkingDirectory=""
ApplicationCommand=""
ApplicationArguments=""
ShimCommand=""
MPIAcceptMode=""
MPIAcceptFilter=""
/>
</Configuration>
<Configuration
Name="Release|Win32"
>
<DebugSettings
Command="$(TargetPath)"
WorkingDirectory=""
CommandArguments=""
Attach="false"
DebuggerType="3"
Remote="1"
RemoteMachine="DESKTOP-TGEL8RC"
RemoteCommand=""
HttpUrl=""
PDBPath=""
SQLDebugging=""
Environment=""
EnvironmentMerge="true"
DebuggerFlavor=""
MPIRunCommand=""
MPIRunArguments=""
MPIRunWorkingDirectory=""
ApplicationCommand=""
ApplicationArguments=""
ShimCommand=""
MPIAcceptMode=""
MPIAcceptFilter=""
/>
</Configuration>
</Configurations>
</VisualStudioUserFile>

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioUserFile
ProjectType="Visual C++"
Version="8.00"
ShowAllFiles="false"
>
<Configurations>
<Configuration
Name="Debug|Win32"
>
<DebugSettings
Command="$(TargetPath)"
WorkingDirectory=""
CommandArguments=""
Attach="false"
DebuggerType="3"
Remote="1"
RemoteMachine="SKIGACER"
RemoteCommand=""
HttpUrl=""
PDBPath=""
SQLDebugging=""
Environment=""
EnvironmentMerge="true"
DebuggerFlavor=""
MPIRunCommand=""
MPIRunArguments=""
MPIRunWorkingDirectory=""
ApplicationCommand=""
ApplicationArguments=""
ShimCommand=""
MPIAcceptMode=""
MPIAcceptFilter=""
/>
</Configuration>
<Configuration
Name="Release|Win32"
>
<DebugSettings
Command="$(TargetPath)"
WorkingDirectory=""
CommandArguments=""
Attach="false"
DebuggerType="3"
Remote="1"
RemoteMachine="SKIGACER"
RemoteCommand=""
HttpUrl=""
PDBPath=""
SQLDebugging=""
Environment=""
EnvironmentMerge="true"
DebuggerFlavor=""
MPIRunCommand=""
MPIRunArguments=""
MPIRunWorkingDirectory=""
ApplicationCommand=""
ApplicationArguments=""
ShimCommand=""
MPIAcceptMode=""
MPIAcceptFilter=""
/>
</Configuration>
</Configurations>
</VisualStudioUserFile>

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioUserFile
ProjectType="Visual C++"
Version="8.00"
ShowAllFiles="false"
>
<Configurations>
<Configuration
Name="Debug|Win32"
>
<DebugSettings
Command="$(TargetPath)"
WorkingDirectory=""
CommandArguments=""
Attach="false"
DebuggerType="3"
Remote="1"
RemoteMachine="DESKTOP-TGEL8RC"
RemoteCommand=""
HttpUrl=""
PDBPath=""
SQLDebugging=""
Environment=""
EnvironmentMerge="true"
DebuggerFlavor=""
MPIRunCommand=""
MPIRunArguments=""
MPIRunWorkingDirectory=""
ApplicationCommand=""
ApplicationArguments=""
ShimCommand=""
MPIAcceptMode=""
MPIAcceptFilter=""
/>
</Configuration>
<Configuration
Name="Release|Win32"
>
<DebugSettings
Command="$(TargetPath)"
WorkingDirectory=""
CommandArguments=""
Attach="false"
DebuggerType="3"
Remote="1"
RemoteMachine="DESKTOP-TGEL8RC"
RemoteCommand=""
HttpUrl=""
PDBPath=""
SQLDebugging=""
Environment=""
EnvironmentMerge="true"
DebuggerFlavor=""
MPIRunCommand=""
MPIRunArguments=""
MPIRunWorkingDirectory=""
ApplicationCommand=""
ApplicationArguments=""
ShimCommand=""
MPIAcceptMode=""
MPIAcceptFilter=""
/>
</Configuration>
</Configurations>
</VisualStudioUserFile>

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioUserFile
ProjectType="Visual C++"
Version="8.00"
ShowAllFiles="false"
>
<Configurations>
<Configuration
Name="Debug|Win32"
>
<DebugSettings
Command="$(TargetPath)"
WorkingDirectory=""
CommandArguments=""
Attach="false"
DebuggerType="3"
Remote="1"
RemoteMachine="DESKTOP-TGEL8RC"
RemoteCommand=""
HttpUrl=""
PDBPath=""
SQLDebugging=""
Environment=""
EnvironmentMerge="true"
DebuggerFlavor=""
MPIRunCommand=""
MPIRunArguments=""
MPIRunWorkingDirectory=""
ApplicationCommand=""
ApplicationArguments=""
ShimCommand=""
MPIAcceptMode=""
MPIAcceptFilter=""
/>
</Configuration>
<Configuration
Name="Release|Win32"
>
<DebugSettings
Command="$(TargetPath)"
WorkingDirectory=""
CommandArguments=""
Attach="false"
DebuggerType="3"
Remote="1"
RemoteMachine="DESKTOP-TGEL8RC"
RemoteCommand=""
HttpUrl=""
PDBPath=""
SQLDebugging=""
Environment=""
EnvironmentMerge="true"
DebuggerFlavor=""
MPIRunCommand=""
MPIRunArguments=""
MPIRunWorkingDirectory=""
ApplicationCommand=""
ApplicationArguments=""
ShimCommand=""
MPIAcceptMode=""
MPIAcceptFilter=""
/>
</Configuration>
</Configurations>
</VisualStudioUserFile>

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioUserFile
ProjectType="Visual C++"
Version="8.00"
ShowAllFiles="false"
>
<Configurations>
<Configuration
Name="Debug|Win32"
>
<DebugSettings
Command="$(TargetPath)"
WorkingDirectory=""
CommandArguments=""
Attach="false"
DebuggerType="3"
Remote="1"
RemoteMachine="SKIGACER"
RemoteCommand=""
HttpUrl=""
PDBPath=""
SQLDebugging=""
Environment=""
EnvironmentMerge="true"
DebuggerFlavor=""
MPIRunCommand=""
MPIRunArguments=""
MPIRunWorkingDirectory=""
ApplicationCommand=""
ApplicationArguments=""
ShimCommand=""
MPIAcceptMode=""
MPIAcceptFilter=""
/>
</Configuration>
<Configuration
Name="Release|Win32"
>
<DebugSettings
Command="$(TargetPath)"
WorkingDirectory=""
CommandArguments=""
Attach="false"
DebuggerType="3"
Remote="1"
RemoteMachine="SKIGACER"
RemoteCommand=""
HttpUrl=""
PDBPath=""
SQLDebugging=""
Environment=""
EnvironmentMerge="true"
DebuggerFlavor=""
MPIRunCommand=""
MPIRunArguments=""
MPIRunWorkingDirectory=""
ApplicationCommand=""
ApplicationArguments=""
ShimCommand=""
MPIAcceptMode=""
MPIAcceptFilter=""
/>
</Configuration>
</Configurations>
</VisualStudioUserFile>

View File

@ -1117,6 +1117,9 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses
// 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)
#define CKernel
@ -1350,6 +1353,7 @@ 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();
@ -1508,6 +1512,7 @@ BOOL ReconfigFlag = FALSE;
BOOL RigReconfigFlag = FALSE;
BOOL APRSReconfigFlag = FALSE;
BOOL CloseAllNeeded = FALSE;
BOOL NeedWebMailRefresh = FALSE;
int AttachedPIDList[100] = {0};
@ -2127,6 +2132,9 @@ VOID TimerProcX()
if (PMActive) Poll_PM();
if (RigActive) Rig_Poll();
if (NeedWebMailRefresh)
DoRefreshWebMailIndex();
CheckGuardZone();
if (APRSActive)

View File

@ -74,7 +74,7 @@
<Tool
Name="VCLinkerTool"
AdditionalOptions=" /section:_BPQDATA,srw"
AdditionalDependencies="WS2_32.Lib winmm.lib DbgHelp.lib comctl32.lib Iphlpapi.lib setupapi.lib ..\lib\libconfig.lib miniupnpc.lib zlibstat.lib"
AdditionalDependencies="WS2_32.Lib winmm.lib DbgHelp.lib comctl32.lib Iphlpapi.lib setupapi.lib ..\lib\libconfigd.lib miniupnpc.lib zlibstat.lib"
OutputFile="c:\DevProgs\BPQ32\bpq32.dll"
LinkIncremental="2"
IgnoreAllDefaultLibraries="false"

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioUserFile
ProjectType="Visual C++"
Version="8.00"
ShowAllFiles="false"
>
<Configurations>
<Configuration
Name="Debug|Win32"
>
<DebugSettings
Command="C:\Devprogs\BPQ32\bpq32.exe"
WorkingDirectory=""
CommandArguments=""
Attach="false"
DebuggerType="3"
Remote="1"
RemoteMachine="DESKTOP-TGEL8RC"
RemoteCommand=""
HttpUrl=""
PDBPath=""
SQLDebugging=""
Environment=""
EnvironmentMerge="true"
DebuggerFlavor=""
MPIRunCommand=""
MPIRunArguments=""
MPIRunWorkingDirectory=""
ApplicationCommand=""
ApplicationArguments=""
ShimCommand=""
MPIAcceptMode=""
MPIAcceptFilter=""
/>
</Configuration>
<Configuration
Name="Release|Win32"
>
<DebugSettings
Command=""
WorkingDirectory=""
CommandArguments=""
Attach="false"
DebuggerType="3"
Remote="1"
RemoteMachine="DESKTOP-TGEL8RC"
RemoteCommand=""
HttpUrl=""
PDBPath=""
SQLDebugging=""
Environment=""
EnvironmentMerge="true"
DebuggerFlavor=""
MPIRunCommand=""
MPIRunArguments=""
MPIRunWorkingDirectory=""
ApplicationCommand=""
ApplicationArguments=""
ShimCommand=""
MPIAcceptMode=""
MPIAcceptFilter=""
/>
</Configuration>
</Configurations>
</VisualStudioUserFile>

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioUserFile
ProjectType="Visual C++"
Version="8.00"
ShowAllFiles="false"
>
<Configurations>
<Configuration
Name="Debug|Win32"
>
<DebugSettings
Command="C:\DevProgs\BPQ32\bpq32.exe"
WorkingDirectory=""
CommandArguments=""
Attach="false"
DebuggerType="3"
Remote="1"
RemoteMachine="SKIGACER"
RemoteCommand=""
HttpUrl=""
PDBPath=""
SQLDebugging=""
Environment=""
EnvironmentMerge="true"
DebuggerFlavor=""
MPIRunCommand=""
MPIRunArguments=""
MPIRunWorkingDirectory=""
ApplicationCommand=""
ApplicationArguments=""
ShimCommand=""
MPIAcceptMode=""
MPIAcceptFilter=""
/>
</Configuration>
<Configuration
Name="Release|Win32"
>
<DebugSettings
Command=""
WorkingDirectory=""
CommandArguments=""
Attach="false"
DebuggerType="3"
Remote="1"
RemoteMachine="SKIGACER"
RemoteCommand=""
HttpUrl=""
PDBPath=""
SQLDebugging=""
Environment=""
EnvironmentMerge="true"
DebuggerFlavor=""
MPIRunCommand=""
MPIRunArguments=""
MPIRunWorkingDirectory=""
ApplicationCommand=""
ApplicationArguments=""
ShimCommand=""
MPIAcceptMode=""
MPIAcceptFilter=""
/>
</Configuration>
</Configurations>
</VisualStudioUserFile>

View File

@ -2978,7 +2978,11 @@ void ProcessDAEMONJSON(struct TNCINFO * TNC, char * Msg, int Len)
"\"tuning_range_fmax\":\"%3.1f\"," // 50.0
"\"tx_audio_level\":\"125\","
"\"respond_to_cq\":\"True\","
"\"rx_buffer_size\":\"16\"}]}\n";
"\"rx_buffer_size\":\"16\","
"\"rx_buffer_size\":\"16\","
"\"enable_explorer\": \"False\""
"}]}\n";
char Command[2048];

View File

@ -2068,7 +2068,7 @@ doHeader:
if (Session == 0)
Session = &Dummy;
Session->TNC = LOCAL; // TNC only used for Web Terminal Sessions
Session->TNC = (void *)LOCAL; // TNC only used for Web Terminal Sessions
ProcessMailHTTPMessage(Session, Method, Context, MsgPtr, _REPLYBUFFER, &ReplyLen, MsgLen);
@ -2195,7 +2195,7 @@ doHeader:
if (Session == 0)
Session = &Dummy;
Session->TNC = LOCAL; // TNC is only used on Web Terminal Sessions
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);
@ -4325,6 +4325,99 @@ void SendRigWebPage()
}
}
// 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
{
InputLen -= 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;
}

View File

@ -85,6 +85,7 @@ VOID ProcessMBLLine(CIRCUIT * conn, struct UserInfo * user, UCHAR* Buffer, int l
SendCompressed(conn, Msg);
FBBputs(conn, ">\r");
Msg->status = 'Y'; // Mark as read
SaveMessageDatabase();
}
else
{

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioUserFile
ProjectType="Visual C++"
Version="8.00"
ShowAllFiles="false"
>
<Configurations>
<Configuration
Name="Debug|Win32"
>
<DebugSettings
Command="$(TargetPath)"
WorkingDirectory=""
CommandArguments=""
Attach="false"
DebuggerType="3"
Remote="1"
RemoteMachine="DESKTOP-TGEL8RC"
RemoteCommand=""
HttpUrl=""
PDBPath=""
SQLDebugging=""
Environment=""
EnvironmentMerge="true"
DebuggerFlavor=""
MPIRunCommand=""
MPIRunArguments=""
MPIRunWorkingDirectory=""
ApplicationCommand=""
ApplicationArguments=""
ShimCommand=""
MPIAcceptMode=""
MPIAcceptFilter=""
/>
</Configuration>
<Configuration
Name="Release|Win32"
>
<DebugSettings
Command="$(TargetPath)"
WorkingDirectory=""
CommandArguments=""
Attach="false"
DebuggerType="3"
Remote="1"
RemoteMachine="DESKTOP-TGEL8RC"
RemoteCommand=""
HttpUrl=""
PDBPath=""
SQLDebugging=""
Environment=""
EnvironmentMerge="true"
DebuggerFlavor=""
MPIRunCommand=""
MPIRunArguments=""
MPIRunWorkingDirectory=""
ApplicationCommand=""
ApplicationArguments=""
ShimCommand=""
MPIAcceptMode=""
MPIAcceptFilter=""
/>
</Configuration>
</Configurations>
</VisualStudioUserFile>

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioUserFile
ProjectType="Visual C++"
Version="8.00"
ShowAllFiles="false"
>
<Configurations>
<Configuration
Name="Debug|Win32"
>
<DebugSettings
Command="$(TargetPath)"
WorkingDirectory="c:\linbpq"
CommandArguments=""
Attach="false"
DebuggerType="3"
Remote="1"
RemoteMachine="SKIGACER"
RemoteCommand=""
HttpUrl=""
PDBPath=""
SQLDebugging=""
Environment=""
EnvironmentMerge="true"
DebuggerFlavor="0"
MPIRunCommand=""
MPIRunArguments=""
MPIRunWorkingDirectory=""
ApplicationCommand=""
ApplicationArguments=""
ShimCommand=""
MPIAcceptMode=""
MPIAcceptFilter=""
/>
</Configuration>
<Configuration
Name="Release|Win32"
>
<DebugSettings
Command="$(TargetPath)"
WorkingDirectory=""
CommandArguments=""
Attach="false"
DebuggerType="3"
Remote="1"
RemoteMachine="SKIGACER"
RemoteCommand=""
HttpUrl=""
PDBPath=""
SQLDebugging=""
Environment=""
EnvironmentMerge="true"
DebuggerFlavor=""
MPIRunCommand=""
MPIRunArguments=""
MPIRunWorkingDirectory=""
ApplicationCommand=""
ApplicationArguments=""
ShimCommand=""
MPIAcceptMode=""
MPIAcceptFilter=""
/>
</Configuration>
</Configurations>
</VisualStudioUserFile>

View File

@ -1467,6 +1467,7 @@ NOHA:
}
// We should choose the BBS with most matching elements (ie match on #23.GBR better that GBR)
// If SendPtoMultiple is set I think we send to any with same mtch level
for (bbs = BBSChain; bbs; bbs = bbs->BBSNext)
{

View File

@ -88,6 +88,7 @@ int DataSocket_ReadDRATS(struct TNCINFO * TNC, struct ConnectionInfo * sockptr,
void processDRATSFrame(unsigned char * Message, int Len, struct ConnectionInfo * sockptr);
void DRATSConnectionLost(struct ConnectionInfo * sockptr);
int BuildRigCtlPage(char * _REPLYBUFFER);
void ProcessWebmailWebSockThread(void * conn);
#ifndef LINBPQ
extern HKEY REGTREE;
@ -915,7 +916,7 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff)
{
case 7:
// 100 mS Timer. Now needed, as Poll can be called more ferquently in some circimstances
// 100 mS Timer. Now needed, as Poll can be called more frequently in some circuymstances
while (TNC->PortRecord->UI_Q) // Release anything accidentally put on UI_Q
{
@ -1317,6 +1318,9 @@ static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL)
{
char Addr[100];
Tel_Format_Addr(sockptr, Addr);
if (sockptr->WebSocks)
sprintf(msg,"<tr><td>Websock<%s</td><td>&nbsp;</td><td>&nbsp;</td></tr>", Addr);
else
sprintf(msg,"<tr><td>HTTP<%s</td><td>&nbsp;</td><td>&nbsp;</td></tr>", Addr);
}
else if (sockptr->DRATSMode)
@ -4953,11 +4957,21 @@ int DataSocket_ReadHTTP(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, S
sprintf(RigCMD, "%s PTT", &MsgPtr[6]);
Rig_Command(-1, RigCMD);
}
else if (memcmp(sockptr->WebURL, "WMRefresh", 9) == 0)
{
sockcopy = malloc(sizeof(struct ConnectionInfo));
sockptr->TNC = TNC;
sockptr->LastSendTime = REALTIMETICKS;
memcpy(sockcopy, sockptr, sizeof(struct ConnectionInfo));
_beginthread(ProcessWebmailWebSockThread, 2048000, (VOID *)sockcopy); // Needs big stack
return 0;
}
}
else
Debugprintf("WebSock Opcode %d Msg %s", Opcode, &MsgPtr[6]);
}
sockptr->InputLen = 0;
return 0;
}
@ -4988,16 +5002,17 @@ int DataSocket_ReadHTTP(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, S
memcpy(sockcopy, sockptr, sizeof(struct ConnectionInfo));
if(strstr(MsgPtr, "Upgrade: websocket"))
{
if(RigWebPage[0])
{
int LOCAL = 0, COOKIE = 0;
char * HostPtr;
char * ptr;
sockptr->WebSocks = 1;
memcpy(sockptr->WebURL, &MsgPtr[5], 15);
ShowConnections(TNC);
memcpy(sockptr->WebURL, &MsgPtr[5], 31);
strlop(sockptr->WebURL, ' ');
if (RigWebPage)
RigWebPage[0] = 0;
HostPtr = strstr(MsgPtr, "Host: ");
@ -5039,9 +5054,9 @@ int DataSocket_ReadHTTP(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, S
sockptr->WebSecure = LOCAL | COOKIE;
}
}
}
_beginthread(ProcessHTTPMessage, 2048000, (VOID *)sockcopy);
_beginthread(ProcessHTTPMessage, 2048000, (VOID *)sockcopy); // Needs big stack
sockptr->InputLen = 0;
return 0;
@ -5103,7 +5118,7 @@ int ShowConnections(struct TNCINFO * TNC)
SendMessage(TNC->hMonitor,LB_RESETCONTENT,0,0);
for (n = 0; n <= TNC->TCPInfo->CurrentSockets; n++)
for (n = 1; n <= TNC->TCPInfo->CurrentSockets; n++)
{
sockptr=TNC->Streams[n].ConnectionInfo;
@ -5119,6 +5134,10 @@ int ShowConnections(struct TNCINFO * TNC)
{
char Addr[100];
Tel_Format_Addr(sockptr, Addr);
if (sockptr->WebSocks)
sprintf(msg, "Websock From %s", Addr);
else
sprintf(msg, "HTTP From %s", Addr);
}
else if (sockptr->DRATSMode)
@ -6873,7 +6892,12 @@ VOID SHOWTELNET(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX
{
char Addr[100];
Tel_Format_Addr(sockptr, Addr);
if (sockptr->WebSocks)
sprintf(msg, "Websock From %s", Addr);
else
sprintf(msg, "HTTP From %s", Addr);
}
else if (sockptr->DRATSMode)
{
@ -6895,3 +6919,83 @@ VOID SHOWTELNET(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX
SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}
// Refresh any Web Socket Webmail index display
// Called whenever message database is changed
#ifdef LINBPQ
int DoRefreshWebMailIndex();
int RefreshWebMailIndex()
{
DoRefreshWebMailIndex();
}
#else
// Have to pass request from BPQMail to DLL as socket can only be accessed in calling process
// Pass request back to WebMail via pipe
// Code must run in bpq32 process, so set flag here and call code from Timer Routine
extern BOOL NeedWebMailRefresh;
DllExport int APIENTRY RefreshWebMailIndex()
{
NeedWebMailRefresh = 1;
return 0;
}
#endif
int DoRefreshWebMailIndex()
{
// Loop through all sockets and pick out WebMail Index Connections
int i, n;
struct ConnectionInfo * sockptr;
struct ConnectionInfo * sockcopy;
struct TNCINFO * TNC;
struct TCPINFO * TCP;
#ifndef LINBPQ
NeedWebMailRefresh = 0;
#endif
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 && memcmp(sockptr->WebURL, "WMRefresh", 9) == 0)
{
sockcopy = malloc(sizeof(struct ConnectionInfo));
sockptr->TNC = TNC;
sockptr->LastSendTime = REALTIMETICKS;
memcpy(sockcopy, sockptr, sizeof(struct ConnectionInfo));
_beginthread(ProcessWebmailWebSockThread, 2048000, (VOID *)sockcopy); // Needs big stack
}
}
}
}
}
}
return 0;
}

2
VARA.c
View File

@ -1162,7 +1162,7 @@ void * VARAExtInit(EXTPORTDATA * PortEntry)
if (TNC->TXFreq)
{
CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow + 22, PacWndProc, 500, 450, ForcedClose);
CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow + 22, PacWndProc, 550, 450, ForcedClose);
InitCommonControls(); // loads common control's DLL

View File

@ -10,8 +10,8 @@
#endif
#define KVers 6,0,23,24
#define KVerstring "6.0.23.24\0"
#define KVers 6,0,23,25
#define KVerstring "6.0.23.25\0"
#ifdef CKernel

212
WebMail.c
View File

@ -954,7 +954,7 @@ int SendWebMailHeaderEx(char * Reply, char * Key, struct HTTPConnectionInfo * Se
if (WebMailTemplate == NULL)
WebMailTemplate = GetTemplateFromFile(6, "WebMailPage.txt");
return sprintf(Reply, WebMailTemplate, BBSName, User->Call, Key, Key, Key, Key, Key, Key, Key, Messages);
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)
@ -1119,6 +1119,8 @@ int ViewWebMailMessage(struct HTTPConnectionInfo * Session, char * Reply, int Nu
{
Msg->status = 'Y';
Msg->datechanged=time(NULL);
SaveMessageDatabase();
}
}
}
@ -1185,6 +1187,7 @@ int ViewWebMailMessage(struct HTTPConnectionInfo * Session, char * Reply, int Nu
{
Msg->status = 'Y';
Msg->datechanged=time(NULL);
SaveMessageDatabase();
}
}
}
@ -1281,6 +1284,7 @@ int ViewWebMailMessage(struct HTTPConnectionInfo * Session, char * Reply, int Nu
{
Msg->status = 'Y';
Msg->datechanged=time(NULL);
SaveMessageDatabase();
}
}
}
@ -1791,6 +1795,98 @@ void ProcessWebMailMessage(struct HTTPConnectionInfo * Session, char * Key, BOOL
return;
}
if (_stricmp(NodeURL, "/WebMail/WMAuto") == 0)
{
// Auto Refresh Version of index page. Uses Web Sockets
char Page[4096];
char WebSockPage[] =
"<!-- Version 6 8/11/2018 -->\r\n"
"<!DOCTYPE html> \r\n"
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\"> \r\n"
"<head> \r\n"
"<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\"/> \r\n"
"<style type=\"text/css\">\r\n"
"pre {margin-left: 4px;white-space: pre} \r\n"
"#main{width:700px;position:absolute;left:0px;border:2px solid;background-color: #ffffff;}\r\n"
"</style>\r\n"
"<script src=\"/WebMail/webscript.js\"></script>\r\n"
"<script type = \"text/javascript\">\r\n"
"var ws;"
"function Init()"
"{"
" if (\"WebSocket\" in window)"
" {"
" // open a web socket. Get address from URL\r\n"
" var text = window.location.href;"
" var result = text.substring(7);"
" var myArray = result.split('/', 1);"
" ws = new WebSocket('ws://' + myArray[0] + '/WMRefresh&%s');\r\n"
" ws.onopen = function() {\r\n"
" // Web Socket is connected\r\n"
" const div = document.getElementById('main');\r\n"
" div.innerHTML = 'Websock Connected'\r\n"
" ws.send('WMRefresh&%s');"
" };\r\n"
" ws.onmessage = function (evt)"
" {"
" var received_msg = evt.data;\r\n"
" const div = document.getElementById('main');\r\n"
" div.innerHTML = received_msg\r\n"
" };"
" ws.onclose = function()"
" {"
" // websocket is closed.\r\n"
" const div = document.getElementById('main');\r\n"
" div.innerHTML = 'Websock Connection Lost';\r\n"
" };"
" initialize(120);"
" }"
" else"
" {"
" // The browser doesn't support WebSocket\r\n"
" const div = document.getElementById('main');\r\n"
" div.innerHTML = 'WebSocket not supported by your Browser - AutoRefresh not availble'\r\n"
" }"
"}"
"</script>\r\n"
"<title>WebMail</title> \r\n"
"</head>\r\n"
"<body background=/background.jpg onload=Init() onresize=initialize(120)>\r\n"
"<h3 align=center> %s Webmail Interface - User %s - Message List</h3>\r\n"
"<table align=center border=1 cellpadding=2 bgcolor=white><tr>\r\n"
"\r\n"
"<td><a href=/WebMail/WMB?%s>Bulls</a></td>\r\n"
"<td><a href=/WebMail/WMP?%s>Personal</a></td>\r\n"
"<td><a href=/WebMail/WMT?%s>NTS</a></td>\r\n"
"<td><a href=/WebMail/WMALL?%s>All Types</a></td>\r\n"
"<td><a href=/WebMail/WMMine?%s>Mine</a></td>\r\n"
"<td><a href=/WebMail/WMAuto?%s>Auto Refresh</a></td>\r\n"
"<td><a href=\"#\" onclick=\"newmsg('%s'); return false;\">Send Message</a></td>\r\n"
"<td><a href=/WebMail/WMLogout?%s>Logout</a></td>\r\n"
"<td><a href=/>Node Menu</a></td></tr></table>\r\n"
"<br>\r\n"
"<div align=left id=main style=overflow:scroll;>Waiting for data...</div>\r\n"
"</body></html>\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
@ -2657,7 +2753,6 @@ VOID SaveNewMessage(struct HTTPConnectionInfo * Session, char * MsgPtr, char * R
BuildNNTPList(Msg); // Build NNTP Groups list
if (EnableUI)
SendMsgUI(Msg);
@ -5959,5 +6054,118 @@ char * WebFindPart(char ** Msg, char * Boundary, int * PartLen, char * End)
}
int ProcessWebmailWebSock(char * MsgPtr, char * OutBuffer)
{
int Len = 129;
char * Key = strlop(MsgPtr, '&');
struct HTTPConnectionInfo * Session;
struct UserInfo * User;
int m;
struct MsgInfo * Msg;
char * ptr = &OutBuffer[4];
int n = NumberofMessages;
char Via[64];
int Count = 0;
if (Key == 0)
return 0;
Session = FindWMSession(Key);
if (Session == NULL)
return 0;
User = Session->User;
// Outbuffer is 250000
// ptr += sprintf(ptr, "<div align=left id=\"main\" style=\"overflow:scroll;\">\r\n"
ptr += sprintf(ptr, "<pre align=left>");
ptr += sprintf(ptr, "%s", " # Date XX Len To @ From Subject\r\n\r\n");
for (m = LatestMsg; m >= 1; m--)
{
if (ptr > &OutBuffer[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, "<a href=/WebMail/WM?%s&%d>%6d</a> %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;
}
}
ptr += sprintf(ptr, "%s</pre> \r\n", ptr);
Len = ptr - &OutBuffer[4];
OutBuffer[0] = 0x81; // Fin, Data
if (Len < 126)
{
OutBuffer[1] = Len;
memmove(&OutBuffer[2], &OutBuffer[4], Len);
return Len + 2;
}
else
{
OutBuffer[1] = 126; // Unmasked, Extended Len
OutBuffer[2] = Len >> 8;
OutBuffer[3] = Len & 0xff;
return Len + 4;
}
}

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioUserFile
ProjectType="Visual C++"
Version="8.00"
ShowAllFiles="false"
>
<Configurations>
<Configuration
Name="Debug|Win32"
>
<DebugSettings
Command="$(TargetPath)"
WorkingDirectory=""
CommandArguments=""
Attach="false"
DebuggerType="3"
Remote="1"
RemoteMachine="SKIGACER"
RemoteCommand=""
HttpUrl=""
PDBPath=""
SQLDebugging=""
Environment=""
EnvironmentMerge="true"
DebuggerFlavor=""
MPIRunCommand=""
MPIRunArguments=""
MPIRunWorkingDirectory=""
ApplicationCommand=""
ApplicationArguments=""
ShimCommand=""
MPIAcceptMode=""
MPIAcceptFilter=""
/>
</Configuration>
<Configuration
Name="Release|Win32"
>
<DebugSettings
Command="$(TargetPath)"
WorkingDirectory=""
CommandArguments=""
Attach="false"
DebuggerType="3"
Remote="1"
RemoteMachine="SKIGACER"
RemoteCommand=""
HttpUrl=""
PDBPath=""
SQLDebugging=""
Environment=""
EnvironmentMerge="true"
DebuggerFlavor=""
MPIRunCommand=""
MPIRunArguments=""
MPIRunWorkingDirectory=""
ApplicationCommand=""
ApplicationArguments=""
ShimCommand=""
MPIAcceptMode=""
MPIAcceptFilter=""
/>
</Configuration>
</Configurations>
</VisualStudioUserFile>

View File

@ -1,53 +0,0 @@
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69])
AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS])
AC_CONFIG_SRCDIR([AEAPactor.c])
AC_CONFIG_HEADERS([config.h])
# Checks for programs.
AC_PROG_CXX
AC_PROG_CC
# Checks for libraries.
# FIXME: Replace `main' with a function in `-lconfig':
AC_CHECK_LIB([config], [main])
# FIXME: Replace `main' with a function in `-lm':
AC_CHECK_LIB([m], [main])
# FIXME: Replace `main' with a function in `-lpcap':
AC_CHECK_LIB([pcap], [main])
# FIXME: Replace `main' with a function in `-lpthread':
AC_CHECK_LIB([pthread], [main])
# FIXME: Replace `main' with a function in `-lrt':
AC_CHECK_LIB([rt], [main])
# Checks for header files.
AC_PATH_X
AC_CHECK_HEADERS([arpa/inet.h fcntl.h malloc.h netdb.h netinet/in.h stddef.h stdlib.h string.h sys/ioctl.h sys/socket.h sys/time.h syslog.h termios.h unistd.h wchar.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_CHECK_HEADER_STDBOOL
AC_C_INLINE
AC_TYPE_INT16_T
AC_TYPE_INT32_T
AC_TYPE_INT64_T
AC_TYPE_INT8_T
AC_TYPE_PID_T
AC_TYPE_SIZE_T
AC_TYPE_UINT16_T
AC_TYPE_UINT32_T
AC_TYPE_UINT64_T
AC_TYPE_UINT8_T
AC_CHECK_TYPES([ptrdiff_t])
# Checks for library functions.
AC_FUNC_FORK
AC_FUNC_MALLOC
AC_FUNC_MKTIME
AC_FUNC_MMAP
AC_FUNC_REALLOC
AC_CHECK_FUNCS([bzero clock_gettime floor ftruncate getcwd gethostbyaddr gethostbyname gethostname inet_ntoa memchr memmove memset mkdir munmap pow select socket strchr strrchr strstr strtol])
AC_CONFIG_FILES([makefile])
AC_OUTPUT

View File

@ -2,7 +2,7 @@
// HTTP Session Control. Used In Kernel HTTPCode, BBSHTMLConfig
// and ChatHTMLConfig
// On Windows ghanges to layout or length of this struct require rebuilding BPQ32.dll, BPQMail and BPQChat
// On Windows changes to layout or length of this struct require rebuilding BPQ32.dll, BPQMail and BPQChat
struct HTTPConnectionInfo // Used for Web Server for thread-specific stuff
{

View File

@ -68,7 +68,7 @@ struct ConnectionInfo
struct ADIF * ADIF; // ADIF Logging info
int WebSocks;
char WebURL[16]; // URL for WebSocket Connection
char WebURL[32]; // URL for WebSocket Connection
int WebSecure; // Set if secure session
};

View File

@ -394,6 +394,7 @@ char * WebMailPagetxt()
"<td><a href=/WebMail/WMT?%s>NTS</a></td>\r\n"
"<td><a href=/WebMail/WMALL?%s>All Types</a></td>\r\n"
"<td><a href=/WebMail/WMMine?%s>Mine</a></td>\r\n"
"<td><a href=/WebMail/WMAuto?%s>Auto Refresh</a></td>\r\n"
"<td><a href=\"#\" onclick=\"newmsg('%s'); return false;\">Send Message</a></td>\r\n"
"<td><a href=/WebMail/WMLogout?%s>Logout</a></td>\r\n"
"<td><a href=/>Node Menu</a></td></tr></table>\r\n"