diff --git a/.vs/ConfigWinRPR/v15/Browse.VC.db b/.vs/ConfigWinRPR/v15/Browse.VC.db new file mode 100644 index 0000000..f2d4dc9 Binary files /dev/null and b/.vs/ConfigWinRPR/v15/Browse.VC.db differ diff --git a/.vs/ConfigWinRPR/v15/ipch/AutoPCH/2888e545bd29f298/CONFIGWINRPR.ipch b/.vs/ConfigWinRPR/v15/ipch/AutoPCH/2888e545bd29f298/CONFIGWINRPR.ipch new file mode 100644 index 0000000..ac4d240 Binary files /dev/null and b/.vs/ConfigWinRPR/v15/ipch/AutoPCH/2888e545bd29f298/CONFIGWINRPR.ipch differ diff --git a/ARDOP.c b/ARDOP.c index f4a0824..6d9c843 100644 --- a/ARDOP.c +++ b/ARDOP.c @@ -4360,7 +4360,7 @@ VOID ARDOPDoTNCReinit(struct TNCINFO * TNC) if (TNC->ReinitState == 2) // In Term State, Sending Initialisation Commands { - Debugprintf("DOTNCReinit Complete - Entering Hostmode"); + Debugprintf("DOTNCReinit %d Complete - Entering Hostmode", TNC->Port); TNC->TXBuffer[2] = 0; TNC->Toggle = 0; diff --git a/BBSHTMLConfig.c b/BBSHTMLConfig.c index a69cab5..f965f27 100644 --- a/BBSHTMLConfig.c +++ b/BBSHTMLConfig.c @@ -1194,8 +1194,9 @@ int SendMessageDetails(struct MsgInfo * Msg, char * Reply, char * Key) { USER = UserRecPtr[n]; - if ((USER->flags & F_BBS) && USER->BBSNumber) - bbs[i++] = USER; + if ((USER->flags & F_BBS)) + if (USER->BBSNumber) + bbs[i++] = USER; } qsort((void *)bbs, i, sizeof(void *), compare ); diff --git a/BBSUtilities.c b/BBSUtilities.c index 0bf600b..a90fb2d 100644 --- a/BBSUtilities.c +++ b/BBSUtilities.c @@ -414,7 +414,7 @@ void WriteLogLine(CIRCUIT * conn, int Flag, char * Msg, int MsgLen, int Flags) // Don't close/reopen logs every time - if ((LT - LastLogTime[Flags]) > 60) +// if ((LT - LastLogTime[Flags]) > 60) { LastLogTime[Flags] = LT; fclose(LogHandle[Flags]); @@ -1005,7 +1005,12 @@ Next: user = UserRecPtr[i]; if (user->flags & F_BBS) + { + if (user->BBSNumber == 0) + user->BBSNumber = FindFreeBBSNumber(); + CheckBBSNumber(user->BBSNumber); + } } SortBBSChain(); @@ -9846,7 +9851,7 @@ BOOL GetConfig(char * ConfigName) return(EXIT_FAILURE); } -#ifdef FREEBSD +#if IBCONFIG_VER_MINOR > 4 config_set_option(&cfg, CONFIG_OPTION_AUTOCONVERT, 1); #else config_set_auto_convert (&cfg, 1); diff --git a/BPQChat.aps b/BPQChat.aps new file mode 100644 index 0000000..579f2ad Binary files /dev/null and b/BPQChat.aps differ diff --git a/BPQChat.rc b/BPQChat.rc index ec9c161..11b919a 100644 --- a/BPQChat.rc +++ b/BPQChat.rc @@ -1,4 +1,4 @@ -// Microsoft Visual C++ generated resource script. +//Microsoft Developer Studio generated resource script. // #include "chatrc.h" @@ -19,19 +19,21 @@ #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// -// English (United States) resources +// English (U.S.) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) +#endif //_WIN32 ///////////////////////////////////////////////////////////////////////////// // // Dialog // -IDD_PROPPAGE_LARGE DIALOG 0, 0, 235, 156 -STYLE DS_SETFONT | WS_CHILD | WS_DISABLED | WS_CAPTION +IDD_PROPPAGE_LARGE DIALOG DISCARDABLE 0, 0, 235, 156 +STYLE WS_CHILD | WS_DISABLED | WS_CAPTION CAPTION "Property Page" FONT 8, "MS Sans Serif" BEGIN @@ -44,7 +46,7 @@ END // #ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO +GUIDELINES DESIGNINFO MOVEABLE PURE BEGIN IDD_PROPPAGE_LARGE, DIALOG BEGIN @@ -56,29 +58,33 @@ BEGIN END #endif // APSTUDIO_INVOKED -#endif // English (United States) resources +#endif // English (U.S.) resources ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// -// English (United Kingdom) resources +// English (U.K.) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) +#ifdef _WIN32 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK #pragma code_page(1252) +#endif //_WIN32 ///////////////////////////////////////////////////////////////////////////// // // Dialog // -BPQMAILCHAT DIALOG 120, 50, 294, 165 -STYLE DS_SETFONT | DS_3DLOOK | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +BPQMAILCHAT DIALOG DISCARDABLE 120, 50, 294, 165 +STYLE DS_3DLOOK | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | + WS_THICKFRAME CAPTION "G8BPQ Chat Server 1.0.0.10 Beta July 2009" CLASS "BPQMailChat" FONT 8, "FixedSys" BEGIN - LTEXT " User Callsign Stream Conf Queue RTT",101,3,4,184,10 + LTEXT " User Callsign Stream Conf Queue RTT",101,3,4, + 184,10 LTEXT "UTC",IDC_STATIC,197,5,15,10 LTEXT "Local",IDC_STATIC,241,5,21,10 LTEXT "",IDC_UTC,215,5,25,10 @@ -96,8 +102,9 @@ BEGIN LTEXT "0",IDC_CONSEM,274,135,20,10 END -CONSOLEWINDOW DIALOG 17, 25, 400, 301 -STYLE DS_SETFONT | DS_3DLOOK | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CONSOLEWINDOW DIALOG DISCARDABLE 17, 25, 400, 301 +STYLE DS_3DLOOK | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | + WS_THICKFRAME CAPTION "Chat Console" MENU CONSOLEMENU CLASS "CONSOLEWINDOW" @@ -106,108 +113,134 @@ BEGIN EDITTEXT 118,24,228,348,15,ES_AUTOHSCROLL | ES_NOHIDESEL END -BPQMONWINDOW DIALOG 17, 25, 400, 300 -STYLE DS_SETFONT | DS_3DLOOK | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +BPQMONWINDOW DIALOG DISCARDABLE 17, 25, 400, 300 +STYLE DS_3DLOOK | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | + WS_THICKFRAME CAPTION "Chat Monitor" MENU MENU_2 CLASS "BPQMONWINDOW" FONT 8, "FixedSys" BEGIN - LISTBOX 121,6,25,290,109,LBS_MULTIPLESEL | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_HSCROLL + LISTBOX 121,6,25,290,109,LBS_MULTIPLESEL | LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_HSCROLL END -BPQDEBUGWINDOW DIALOG 17, 25, 400, 300 -STYLE DS_SETFONT | DS_3DLOOK | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +BPQDEBUGWINDOW DIALOG DISCARDABLE 17, 25, 400, 300 +STYLE DS_3DLOOK | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | + WS_THICKFRAME CAPTION "Chat Debug Window" MENU MENU_3 CLASS "BPQDEBUGWINDOW" FONT 8, "FixedSys" BEGIN - LISTBOX 122,5,156,290,109,LBS_MULTIPLESEL | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_HSCROLL + LISTBOX 122,5,156,290,109,LBS_MULTIPLESEL | LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_HSCROLL END -CHAT_CONFIG DIALOGEX 0, 0, 371, 296 -STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CHAT_CONFIG DIALOG DISCARDABLE 0, 0, 372, 318 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Configuration" -FONT 8, "System", 0, 0, 0x0 +FONT 8, "System" BEGIN - LTEXT "Chat Appl Number",IDC_STATIC,91,85,61,8 - EDITTEXT ID_CHATAPPL,159,83,29,14 - LTEXT "Nodes to link to",IDC_STATIC,159,106,53,8 - EDITTEXT ID_CHATNODES,29,119,313,60,ES_MULTILINE | ES_UPPERCASE | ES_AUTOVSCROLL | ES_WANTRETURN | WS_VSCROLL - DEFPUSHBUTTON "Save",SAVENODES,160,183,50,14,BS_CENTER | BS_VCENTER - LTEXT "The Application Number defines which BPQ32 Application gives access to the Chat Server. Note this is the APPLNumber (1-32) not an Application Mask, as uses in many BPQ32 programs.",IDC_STATIC,10,10,353,18 - LTEXT "The Nodes to link to box defines which other Chat Nodes should be connected to, or from which connections may be accepted. The format is ALIAS:CALL, eg BPQCHT:G8BPQ-4. Note these must be directly connectable - ie in your NODES table.",IDC_STATIC,10,30,355,25 - LTEXT "The Callsign of the Chat Node is not defined here - It is obtained from the BPQ32 APPLCALL parameter corresponding to the Chat Appl Number.",IDC_STATIC,10,60,360,25 - LTEXT "Streams",IDC_STATIC,199,85,34,8 - EDITTEXT ID_STREAMS,236,83,29,14 - LTEXT "Chat Welcome Message",IDC_STATIC,142,202,82,8 - EDITTEXT IDM_CHATUSERMSG,15,214,340,54,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL - DEFPUSHBUTTON "Save Welcome Message",SAVEWELCOME,140,274,91,14,BS_CENTER | BS_VCENTER + LTEXT "Chat Appl Number",IDC_STATIC,91,110,61,8 + EDITTEXT ID_CHATAPPL,159,108,29,14 + LTEXT "Nodes to link to",IDC_STATIC,159,126,53,8 + EDITTEXT ID_CHATNODES,29,141,313,60,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_WANTRETURN | WS_VSCROLL + DEFPUSHBUTTON "Save",SAVENODES,160,204,50,14,BS_CENTER | BS_VCENTER + LTEXT "The Application Number defines which BPQ32 Application gives access to the Chat Server. Note this is the APPLNumber (1-32) not an Application Mask, as uses in many BPQ32 programs.", + IDC_STATIC,10,10,353,18 + LTEXT "The Nodes to link to box defines which other Chat Nodes should be connected to, or from which connections may be accepted. The format is ALIAS:CALL, eg BPQCHT:G8BPQ-4.", + IDC_STATIC,10,31,355,22 + LTEXT "The Callsign of the Chat Node is not defined here - it is obtained from the BPQ32 APPLCALL parameter corresponding to the Chat Appl Number.", + IDC_STATIC,10,76,360,21 + LTEXT "Streams",IDC_STATIC,199,110,34,8 + EDITTEXT ID_STREAMS,236,108,29,14 + LTEXT "Chat Welcome Message",IDC_STATIC,142,222,82,8 + EDITTEXT IDM_CHATUSERMSG,15,235,340,54,ES_MULTILINE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | + WS_VSCROLL + DEFPUSHBUTTON "Save Welcome Message",SAVEWELCOME,140,296,91,14, + BS_CENTER | BS_VCENTER + LTEXT " If the node is not directly connectable (ie is not in your NODES table) you can add a connect script. This consists of a series of commands seperared by |, eg NOTCHT:G8BPQ-4|C 3 GM8BPQ-9|CHAT", + IDC_STATIC,9,52,355,24 END -IDD_USERADDED_BOX DIALOG 176, 132, 129, 68 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_THICKFRAME +IDD_USERADDED_BOX DIALOG DISCARDABLE 176, 132, 129, 68 +STYLE DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_THICKFRAME FONT 8, "System" BEGIN DEFPUSHBUTTON "Ok",0,47,48,36,17,BS_CENTER | BS_VCENTER LTEXT "Label0",5050,5,10,117,32 END -WELCOMEMSG DIALOG 26, 5, 381, 266 -STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE +WELCOMEMSG DIALOG DISCARDABLE 26, 5, 381, 266 +STYLE WS_CHILD | WS_VISIBLE FONT 8, "System" BEGIN LTEXT "Normal User Welcome Message",IDC_STATIC,5,7,130,8 - EDITTEXT IDM_USERMSG,5,20,340,45,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN + EDITTEXT IDM_USERMSG,5,20,340,45,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_WANTRETURN LTEXT "Chat Welcome Message",IDC_STATIC,5,67,130,8 - EDITTEXT IDM_CHATUSERMSG,5,80,340,45,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN + EDITTEXT IDM_CHATUSERMSG,5,80,340,45,ES_MULTILINE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN LTEXT "New User Welcome Message",IDC_STATIC,5,127,130,8 - EDITTEXT IDM_NEWUSERMSG,5,140,340,45,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN + EDITTEXT IDM_NEWUSERMSG,5,140,340,45,ES_MULTILINE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN LTEXT "Expert User Welcome Message",IDC_STATIC,5,187,130,8 - EDITTEXT IDM_EXPERTUSERMSG,5,200,340,25,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN + EDITTEXT IDM_EXPERTUSERMSG,5,200,340,25,ES_MULTILINE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN DEFPUSHBUTTON "Save",IDM_MSGSAVE,166,250,50,14,BS_CENTER | BS_VCENTER - LTEXT "$U : Callsign of the user $I : First name of the user $X Messages for user $x Unread messages",IDC_STATIC,5,228,369,8 - LTEXT "$L : Number of the latest message $N : Number of active messages. $Z : Last message read by user",IDC_STATIC,5,238,365,10 + LTEXT "$U : Callsign of the user $I : First name of the user $X Messages for user $x Unread messages", + IDC_STATIC,5,228,369,8 + LTEXT "$L : Number of the latest message $N : Number of active messages. $Z : Last message read by user", + IDC_STATIC,5,238,365,10 END -IDD_CHATCOLCONFIG DIALOG 0, 0, 224, 120 -STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +IDD_CHATCOLCONFIG DIALOG DISCARDABLE 0, 0, 224, 120 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Chat Colour Configuration" FONT 8, "System" BEGIN DEFPUSHBUTTON "Save",IDOK,50,95,50,14 PUSHBUTTON "Close",IDCANCEL,120,95,50,14 - COMBOBOX IDC_CHATCALLS,10,5,100,60,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP - COMBOBOX IDC_CHATCOLOURS,115,5,100,60,CBS_DROPDOWNLIST | CBS_OWNERDRAWFIXED | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_CHATCALLS,10,5,100,60,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_CHATCOLOURS,115,5,100,60,CBS_DROPDOWNLIST | + CBS_OWNERDRAWFIXED | WS_VSCROLL | WS_TABSTOP END -IDD_UPDATECHATMAP DIALOG 0, 0, 274, 146 -STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +IDD_UPDATECHATMAP DIALOG DISCARDABLE 0, 0, 274, 146 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Update Chat Map" FONT 8, "System" BEGIN - LTEXT "Click Help for full information about the Chat Network Map",IDC_STATIC,5,10,195,15 + LTEXT "Click Help for full information about the Chat Network Map", + IDC_STATIC,5,10,195,15 DEFPUSHBUTTON "? Help",IDC_MAPHELP,220,10,33,11,BS_CENTER | BS_VCENTER LTEXT "Position",IDC_STATIC,5,30,35,10 EDITTEXT IDC_MAPPOSITION,5,45,145,15,ES_AUTOHSCROLL LTEXT "Popup Box Text. ",IDC_STATIC,5,65,70,9 - EDITTEXT IDC_POPUPTEXT,5,80,200,35,ES_MULTILINE | ES_AUTOHSCROLL | WS_HSCROLL + EDITTEXT IDC_POPUPTEXT,5,80,200,35,ES_MULTILINE | ES_AUTOHSCROLL | + WS_HSCROLL LTEXT "Popup Mode",IDC_STATIC,215,80,49,10 - CONTROL "Hover",IDC_HOVER,"Button",BS_AUTORADIOBUTTON,219,92,45,10 - CONTROL "Click",IDC_CLICK,"Button",BS_AUTORADIOBUTTON,219,104,45,10 + CONTROL "Hover",IDC_HOVER,"Button",BS_AUTORADIOBUTTON,219,92,45, + 10 + CONTROL "Click",IDC_CLICK,"Button",BS_AUTORADIOBUTTON,219,104,45, + 10 DEFPUSHBUTTON "Send to Map System",IDSENDTOMAP,5,125,90,14 DEFPUSHBUTTON "Save",IDOK,100,125,35,14 PUSHBUTTON "Cancel",IDCANCEL,140,125,40,14 END -IDD_HRHELP DIALOG 0, 0, 415, 182 -STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +IDD_HRHELP DIALOG DISCARDABLE 0, 0, 415, 182 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Hierarchical Forwarding Help" FONT 8, "System" BEGIN DEFPUSHBUTTON "OK",IDOK,182,158,50,14 - EDITTEXT IDC_HRTEXT,4,4,405,150,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL | WS_HSCROLL + EDITTEXT IDC_HRTEXT,4,4,405,150,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL | WS_HSCROLL END @@ -217,7 +250,7 @@ END // #ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO +GUIDELINES DESIGNINFO MOVEABLE PURE BEGIN "BPQMAILCHAT", DIALOG BEGIN @@ -226,6 +259,8 @@ BEGIN CHAT_CONFIG, DIALOG BEGIN + RIGHTMARGIN, 371 + BOTTOMMARGIN, 296 END "IDD_USERADDED_BOX", DIALOG @@ -249,7 +284,7 @@ END // Menu // -IDC_BPQMailChat MENU +IDC_BPQMailChat MENU DISCARDABLE BEGIN POPUP "Actions" BEGIN @@ -278,7 +313,7 @@ BEGIN END END -CONSOLEMENU MENU +CONSOLEMENU MENU DISCARDABLE BEGIN POPUP "Options" BEGIN @@ -298,7 +333,7 @@ BEGIN END END -MENU_2 MENU +MENU_2 MENU DISCARDABLE BEGIN POPUP "Monitor" BEGIN @@ -311,7 +346,7 @@ BEGIN END END -MENU_3 MENU +MENU_3 MENU DISCARDABLE BEGIN POPUP "Edit" BEGIN @@ -326,7 +361,7 @@ END // Accelerator // -IDC_TELNETSERVER ACCELERATORS +IDC_TELNETSERVER ACCELERATORS MOVEABLE PURE BEGIN "?", IDM_ABOUT, ASCII, ALT "/", IDM_ABOUT, ASCII, ALT @@ -339,12 +374,12 @@ END // TEXTINCLUDE // -1 TEXTINCLUDE +1 TEXTINCLUDE MOVEABLE PURE BEGIN "chatrc.h\0" END -2 TEXTINCLUDE +2 TEXTINCLUDE MOVEABLE PURE BEGIN "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" "#include ""windows.h""\r\n" @@ -356,18 +391,18 @@ BEGIN "\0" END -3 TEXTINCLUDE +3 TEXTINCLUDE MOVEABLE PURE BEGIN "#include ""..\\StdVer.inc""\r\n" "\0" END -1 TEXTINCLUDE +1 TEXTINCLUDE MOVEABLE PURE BEGIN "resource.h\0" END -3 TEXTINCLUDE +3 TEXTINCLUDE MOVEABLE PURE BEGIN "#include ""..\\StdVer.inc""\r\n" "\r\n" @@ -382,9 +417,9 @@ END // AFX_DIALOG_LAYOUT // -CHAT_CONFIG AFX_DIALOG_LAYOUT +CHAT_CONFIG AFX_DIALOG_LAYOUT MOVEABLE PURE BEGIN - 0 + 0x0000 END @@ -393,13 +428,13 @@ END // String Table // -STRINGTABLE +STRINGTABLE DISCARDABLE BEGIN IDS_APP_TITLE "BPQMailChat" IDC_BPQMailChat "BPQMailChat" END -#endif // English (United Kingdom) resources +#endif // English (U.K.) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/BPQChat.vcproj.DESKTOP-TGEL8RC.John.user b/BPQChat.vcproj.DESKTOP-TGEL8RC.John.user index 32abbfd..40182c4 100644 --- a/BPQChat.vcproj.DESKTOP-TGEL8RC.John.user +++ b/BPQChat.vcproj.DESKTOP-TGEL8RC.John.user @@ -1,65 +1,65 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/BPQChat.vcproj.SKIGACER.johnw.user b/BPQChat.vcproj.SKIGACER.johnw.user index bbece07..b5b0536 100644 --- a/BPQChat.vcproj.SKIGACER.johnw.user +++ b/BPQChat.vcproj.SKIGACER.johnw.user @@ -1,65 +1,65 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/BPQMail.c b/BPQMail.c index 4f53b48..fa2b149 100644 --- a/BPQMail.c +++ b/BPQMail.c @@ -1103,6 +1103,9 @@ // 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) +// Impotove validation of extended FC message (32) +// Improve WP check for SYSTEM as a callsihn (33) + #include "bpqmail.h" #define MAIL diff --git a/BPQMail.vcproj.DESKTOP-TGEL8RC.John.user b/BPQMail.vcproj.DESKTOP-TGEL8RC.John.user index 32abbfd..40182c4 100644 --- a/BPQMail.vcproj.DESKTOP-TGEL8RC.John.user +++ b/BPQMail.vcproj.DESKTOP-TGEL8RC.John.user @@ -1,65 +1,65 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/BPQMail.vcproj.SKIGACER.johnw.user b/BPQMail.vcproj.SKIGACER.johnw.user index bbece07..b5b0536 100644 --- a/BPQMail.vcproj.SKIGACER.johnw.user +++ b/BPQMail.vcproj.SKIGACER.johnw.user @@ -1,65 +1,65 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/BPQRemotePTT.vcproj.DESKTOP-TGEL8RC.John.user b/BPQRemotePTT.vcproj.DESKTOP-TGEL8RC.John.user index 32abbfd..40182c4 100644 --- a/BPQRemotePTT.vcproj.DESKTOP-TGEL8RC.John.user +++ b/BPQRemotePTT.vcproj.DESKTOP-TGEL8RC.John.user @@ -1,65 +1,65 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/BPQWinAPP.vcproj.DESKTOP-TGEL8RC.John.user b/BPQWinAPP.vcproj.DESKTOP-TGEL8RC.John.user index 32abbfd..40182c4 100644 --- a/BPQWinAPP.vcproj.DESKTOP-TGEL8RC.John.user +++ b/BPQWinAPP.vcproj.DESKTOP-TGEL8RC.John.user @@ -1,65 +1,65 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/BPQWinAPP.vcproj.SKIGACER.johnw.user b/BPQWinAPP.vcproj.SKIGACER.johnw.user index bbece07..b5b0536 100644 --- a/BPQWinAPP.vcproj.SKIGACER.johnw.user +++ b/BPQWinAPP.vcproj.SKIGACER.johnw.user @@ -1,65 +1,65 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/Bpq32.c b/Bpq32.c index 5c01509..a229b3a 100644 --- a/Bpq32.c +++ b/Bpq32.c @@ -1127,7 +1127,11 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses // Add APRS Igate RXOnly option (29) // Fix RMC message handling with prefixes other than GP (29) // Add GPSD support for APRS (30) -// Attempt to tix Tracker/WinRPR reconnect code (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) + + #define CKernel diff --git a/CBPQ32.vcproj.DESKTOP-TGEL8RC.John.user b/CBPQ32.vcproj.DESKTOP-TGEL8RC.John.user index 5fba351..fdd7820 100644 --- a/CBPQ32.vcproj.DESKTOP-TGEL8RC.John.user +++ b/CBPQ32.vcproj.DESKTOP-TGEL8RC.John.user @@ -1,65 +1,65 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/CBPQ32.vcproj.HPLAPTOP.johnw.user b/CBPQ32.vcproj.HPLAPTOP.johnw.user new file mode 100644 index 0000000..58f2ea6 --- /dev/null +++ b/CBPQ32.vcproj.HPLAPTOP.johnw.user @@ -0,0 +1,65 @@ + + + + + + + + + + + diff --git a/CBPQ32.vcproj.SKIGACER.johnw.user b/CBPQ32.vcproj.SKIGACER.johnw.user index 216aac3..03fb73a 100644 --- a/CBPQ32.vcproj.SKIGACER.johnw.user +++ b/CBPQ32.vcproj.SKIGACER.johnw.user @@ -1,65 +1,65 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/ConfigWinRPR.vcxproj.user b/ConfigWinRPR.vcxproj.user new file mode 100644 index 0000000..6e2aec7 --- /dev/null +++ b/ConfigWinRPR.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Events.c b/Events.c index fa32436..c7714ba 100644 --- a/Events.c +++ b/Events.c @@ -1,109 +1,109 @@ -/* -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 - -#include "compatbits.h" -#include - -VOID __cdecl Debugprintf(const char * format, ...); - -#ifndef WIN32 - -#define APIENTRY -#define DllExport -#define VOID void - -#else -#include -#endif - -extern BOOL EventsEnabled; - -// Runs use specified routine on certain event -#ifndef WIN32 - -void RunEventProgram(char * Program, char * Param) -{ - char * arg_list[] = {Program, NULL, NULL}; - pid_t child_pid; - - if (EventsEnabled == 0) - return; - - signal(SIGCHLD, SIG_IGN); // Silently (and portably) reap children. - - if (Param && Param[0]) - arg_list[1] = Param; - - // Fork and Exec Specified program - - // Duplicate this process. - - child_pid = fork (); - - if (child_pid == -1) - { - printf ("Event fork() Failed\n"); - return; - } - - if (child_pid == 0) - { - execvp (arg_list[0], arg_list); - - // The execvp function returns only if an error occurs. - - printf ("Failed to run %s\n", arg_list[0]); - exit(0); // Kill the new process - } - -#else - -DllExport int APIENTRY RunEventProgram(char * Program, char * Param) -{ - int n = 0; - char cmdLine[256]; - - STARTUPINFO SInfo; // pointer to STARTUPINFO - PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION - - if (EventsEnabled == 0) - return 0; - - - SInfo.cb=sizeof(SInfo); - SInfo.lpReserved=NULL; - SInfo.lpDesktop=NULL; - SInfo.lpTitle=NULL; - SInfo.dwFlags=0; - SInfo.cbReserved2=0; - SInfo.lpReserved2=NULL; - - sprintf(cmdLine, "%s %s", Program, Param); - - if (!CreateProcess(NULL, cmdLine, NULL, NULL, FALSE,0 ,NULL ,NULL, &SInfo, &PInfo)) - Debugprintf("Failed to Start %s Error %d ", Program, GetLastError()); - -#endif - - return 0; -} +/* +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 + +#include "compatbits.h" +#include + +VOID __cdecl Debugprintf(const char * format, ...); + +#ifndef WIN32 + +#define APIENTRY +#define DllExport +#define VOID void + +#else +#include +#endif + +extern BOOL EventsEnabled; + +// Runs use specified routine on certain event +#ifndef WIN32 + +void RunEventProgram(char * Program, char * Param) +{ + char * arg_list[] = {Program, NULL, NULL}; + pid_t child_pid; + + if (EventsEnabled == 0) + return; + + signal(SIGCHLD, SIG_IGN); // Silently (and portably) reap children. + + if (Param && Param[0]) + arg_list[1] = Param; + + // Fork and Exec Specified program + + // Duplicate this process. + + child_pid = fork (); + + if (child_pid == -1) + { + printf ("Event fork() Failed\n"); + return; + } + + if (child_pid == 0) + { + execvp (arg_list[0], arg_list); + + // The execvp function returns only if an error occurs. + + printf ("Failed to run %s\n", arg_list[0]); + exit(0); // Kill the new process + } + +#else + +DllExport void APIENTRY RunEventProgram(char * Program, char * Param) +{ + int n = 0; + char cmdLine[256]; + + STARTUPINFO SInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION + + if (EventsEnabled == 0) + return; + + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + sprintf(cmdLine, "%s %s", Program, Param); + + if (!CreateProcess(NULL, cmdLine, NULL, NULL, FALSE,0 ,NULL ,NULL, &SInfo, &PInfo)) + Debugprintf("Failed to Start %s Error %d ", Program, GetLastError()); + +#endif + + return; +} diff --git a/FBBRoutines.c b/FBBRoutines.c index 64886af..84b09d6 100644 --- a/FBBRoutines.c +++ b/FBBRoutines.c @@ -551,7 +551,6 @@ ok: // FC EM A3EDD4P00P55 377 281 0 - /* @@ -576,7 +575,7 @@ ok: if (ptr == NULL) goto badparam2; - // Relay In RO mode adds @MPS@R to the MID. Non't know why (yet!) + // Relay In RO mode adds @MPS@R to the MID. Don't know why (yet!) MPS = strlop(ptr, '@'); if (MPS) @@ -605,7 +604,7 @@ ok: char * To = strtok_s(NULL, seps, &Context); char * Type = strtok_s(NULL, seps, &Context); - if (From && To && ATBBS && CheckRejFilters(From, To, ATBBS, NULL, *Type)) + if (From && To && ATBBS && Type && CheckRejFilters(From, To, ATBBS, NULL, *Type)) { memset(FBBHeader, 0, sizeof(struct FBBHeaderLine)); // Clear header conn->FBBReplyChars[conn->FBBReplyIndex++] = '-'; diff --git a/FreeDATA-DESKTOP-TGEL8RC.c b/FreeDATA-DESKTOP-TGEL8RC.c new file mode 100644 index 0000000..0c4173e --- /dev/null +++ b/FreeDATA-DESKTOP-TGEL8RC.c @@ -0,0 +1,4124 @@ +/* +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 +*/ + +// +// Interface to allow G8BPQ switch to use FreeData TNC + + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + +#ifndef WIN32 +#ifndef MACBPQ +#include +#endif +#endif + +#include "CHeaders.h" +#include "bpq32.h" +#include "tncinfo.h" + +#define SD_BOTH 0x02 + +int KillTNC(struct TNCINFO * TNC); +int RestartTNC(struct TNCINFO * TNC); + +int (WINAPI FAR *GetModuleFileNameExPtr)(); +extern int (WINAPI FAR *EnumProcessesPtr)(); +static int Socket_Data(int sock, int error, int eventcode); +VOID MoveWindows(struct TNCINFO * TNC); +static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); +VOID SendInitScript(struct TNCINFO * TNC); +int ProcessEscape(UCHAR * TXMsg); +static void SendPoll(struct TNCINFO * TNC); +void SendMode(struct TNCINFO * TNC); +static int ConnecttoFreeData(int port); +void ConnectTNCPort(struct TNCINFO * TNC); +int FreeDataSendCommand(struct TNCINFO * TNC, char * data); +static void SendPing(struct TNCINFO * TNC, char * Call); +static void SendCQ(struct TNCINFO * TNC); +char * stristr (char *ch1, char *ch2); +int zEncode(unsigned char * in, unsigned char * out, int len, unsigned char * Banned); +static void SendDataMsg(struct TNCINFO * TNC, char * Call, char * Msg, int Len); +static int SendAsRaw(struct TNCINFO * TNC, char * Call, char * myCall, char * Msg, int Len); +static int SendAsFile(struct TNCINFO * TNC, char * Call, char * Msg, int Len); +char * byte_base64_encode(char *str, int len); +void xdecodeblock( unsigned char in[4], unsigned char out[3] ); +void FlushData(struct TNCINFO * TNC); +void CountRestarts(struct TNCINFO * TNC); +void StopTNC(struct TNCINFO * TNC); +int FreeDataConnect(struct TNCINFO * TNC, char * Call); +int FreeDataDisconnect(struct TNCINFO * TNC); +int FreeGetData(struct TNCINFO * TNC); +static void SendBeacon(struct TNCINFO * TNC, int Interval); + +static char ClassName[]="FREEDATASTATUS"; +static char WindowTitle[] = "FreeData Modem"; +static int RigControlRow = 205; + +#ifndef WIN32 +#include +#else +#include +#endif + +extern char * PortConfig[33]; +extern int SemHeldByAPI; + +static RECT Rect; + +extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + +static int ProcessLine(char * buf, int Port); + +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + +#define MAXRXSIZE 512000 // Sets max size for file transfer (less base64 overhead + +char * gen_uuid() +{ + char v[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + int i; + + //3fb17ebc-bc38-4939-bc8b-74f2443281d4 + //8 dash 4 dash 4 dash 4 dash 12 + + static char buf[37] = {0}; + + //gen random for all spaces because lazy + for (i = 0; i < 36; ++i) + { + buf[i] = v[rand()%16]; + } + + //put dashes in place + buf[8] = '-'; + buf[13] = '-'; + buf[18] = '-'; + buf[23] = '-'; + + //needs end byte + buf[36] = '\0'; + + return buf; +} + +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 = TNCInfo[Port]; + 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->FreeDataInfo = zalloc(sizeof(struct FreeDataINFO)); + +// TNC->FreeDataInfo->useBaseCall = 1; // Default + + 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->TCPPort = WINMORport; + + 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); + } + } + + // 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; + } + + if (_memicmp(buf, "CAPTURE", 7) == 0) + { + TNC->FreeDataInfo->Capture = _strdup(&buf[8]); + strlop(TNC->FreeDataInfo->Capture, 13); + } + else if (_memicmp(buf, "PLAYBACK", 8) == 0) + { + TNC->FreeDataInfo->Playback = _strdup(&buf[9]); + strlop(TNC->FreeDataInfo->Playback, 13); + } + + else if (_memicmp(buf, "LOGDIR ", 7) == 0) + TNC->LogPath = _strdup(&buf[7]); + + else if (_memicmp(buf, "HAMLIBHOST", 10) == 0) + { + TNC->FreeDataInfo->hamlibHost = _strdup(&buf[11]); + strlop(TNC->FreeDataInfo->hamlibHost, 13); + } + + else if (_memicmp(buf, "TuningRange", 11) == 0) + TNC->FreeDataInfo->TuningRange = atoi(&buf[12]); + + else if (_memicmp(buf, "LimitBandWidth", 14) == 0) + TNC->FreeDataInfo->LimitBandWidth = atoi(&buf[14]); + + else if (_memicmp(buf, "HAMLIBPORT", 10) == 0) + TNC->FreeDataInfo->hamlibPort = atoi(&buf[11]); + + else if (_memicmp(buf, "USEBASECALL", 11) == 0) + TNC->FreeDataInfo->useBaseCall = atoi(&buf[12]); + + else if (_memicmp(buf, "RXDIRECTORY", 11) == 0) + { + TNC->FreeDataInfo->RXDir = _strdup(&buf[12]); + strlop(TNC->FreeDataInfo->RXDir, 13); + } + + else if (standardParams(TNC, buf) == FALSE) + strcat(TNC->InitScript, buf); + + } + + return (TRUE); +} + +char * Config; +static char * ptr1, * ptr2; + +int FreeDataGetLine(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; +} + +BOOL FreeDataReadConfigFile(int Port, int ProcLine()) +{ + char buf[256],errbuf[256]; + + Config = PortConfig[Port]; + + if (Config) + { + // Using config from bpq32.cfg + + if (strlen(Config) == 0) + { + return TRUE; + } + + ptr1 = Config; + ptr2 = strchr(ptr1, 13); + + 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); +} + + + +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 VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen) +{ + if (TNC->hDevice) + { + // FreeData mode. Queue to Hostmode driver + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = EncLen; + memcpy(&buffptr->Data[0], Encoded, EncLen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + TNC->Streams[Stream].FramesQueued++; + + return; + } +} + + +VOID FreeDataChangeMYC(struct TNCINFO * TNC, char * Call) +{ + UCHAR TXMsg[100]; + int datalen; + + if (strcmp(Call, TNC->CurrentMYC) == 0) + return; // No Change + + strcpy(TNC->CurrentMYC, Call); + + datalen = sprintf(TXMsg, "MYCALL %s\r", Call); +// FreeDataSendCommand(TNC, TXMsg); +} + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + int datalen; + PMSGWITHLEN buffptr; +// char txbuff[500]; + unsigned int txlen = 0; + UCHAR * TXMsg; + + size_t Param; + int Stream = 0; + 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 + + switch (fn) + { + case 7: + + // 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances + + SendPoll(TNC); + + // Check for buffered data to send + + if (TNC->FreeDataInfo->toSendTimeout) + { + TNC->FreeDataInfo->toSendTimeout--; + if (TNC->FreeDataInfo->toSendTimeout <= 0) + FlushData(TNC); + } + + return 0; + + case 1: // poll + +// FreeDataCheckRX(TNC); + + if (TNC->DAEMONCONNECTED == FALSE && TNC->DAEMONCONNECTING == FALSE) + { + // See if time to reconnect + + time(<ime); + if (ltime - TNC->lasttime > 19 ) + { + TNC->lasttime = ltime; + ConnecttoFreeData(port); + } + } + + + while (TNC->PortRecord->UI_Q) + { + int datalen; + char * Buffer; + char FECMsg[512]; + char Call[12] = " "; + struct _MESSAGE * buffptr; + int CallLen; + char * ptr = FECMsg; + + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + +/* if (TNC->CONNECTED == 0 || + TNC->Streams[0].Connecting || + TNC->Streams[0].Connected) + { + // discard if TNC not connected or sesison active + + ReleaseBuffer(buffptr); + continue; + } +*/ + datalen = buffptr->LENGTH - MSGHDDRLEN; + Buffer = &buffptr->DEST[0]; // Raw Frame + Buffer[datalen] = 0; + + // Frame has ax.25 format header. Convert to Text + + CallLen = ConvFromAX25(Buffer + 7, Call); // Origin + memcpy(ptr, Call, CallLen); + ptr += CallLen; + + *ptr++ = '!'; + + CallLen = ConvFromAX25(Buffer, Call); // Dest + memcpy(ptr, Call, CallLen); + ptr += CallLen; + + Buffer += 14; // TO Digis + datalen -= 14; + + while ((Buffer[-1] & 1) == 0) + { + *ptr++ = ','; + CallLen = ConvFromAX25(Buffer, Call); + memcpy(ptr, Call, CallLen); + ptr += CallLen; + Buffer += 7; // End of addr + datalen -= 7; + } + + *ptr++ = '_'; + *ptr++ = 'U'; // UI Frame + *ptr++ = 0; // delimit calls + + if (Buffer[0] == 3) // UI + { + Buffer += 2; + datalen -= 2; + } + +// FreeDataSendSingleData(TNC, FECMsg, Buffer, datalen); + + ReleaseBuffer(buffptr); + } + + + if (TNC->DiscPending) + { + TNC->DiscPending--; + + if (TNC->DiscPending == 0) + { + // Too long in Disc Pending - Kill and Restart TNC + } + } + + + for (Stream = 0; Stream <= 2; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + FreeDataDisconnect(TNC); + strcpy(TNC->WEB_TNCSTATE, "Disconnecting"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + } + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && STREAM->Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + Debugprintf("FreeData New Attach Stream %d", Stream); + + STREAM->Attached = TRUE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, STREAM->MyCall); + TNC->Streams[Stream].MyCall[calllen] = 0; + + + FreeDataChangeMYC(TNC, TNC->Streams[0].MyCall); + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command(-1, Msg); + } + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + } + + // See if any frames for this port + + for (Stream = 0; Stream <= 2; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->BPQtoPACTOR_Q) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)Q_REM(&STREAM->BPQtoPACTOR_Q); + UCHAR * data = &buffptr->Data[0]; + STREAM->FramesQueued--; + txlen = (int)buffptr->Len; + + SendAsFile(TNC, TNC->FreeDataInfo->farCall, data, txlen); + } + + if (STREAM->PACTORtoBPQ_Q != 0) + { + buffptr = (PMSGWITHLEN)Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = Stream; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + + if (STREAM->ReportDISC) // May need a delay so treat as a counter + { + STREAM->ReportDISC--; + if (STREAM->ReportDISC == 0) + { + buff->PORT = Stream; +// STREAM->Connected = 0; +// STREAM->Attached = 0; + return -1; + } + } + } + return (0); + + case 2: // send + + Stream = buff->PORT; + + if (!TNC->TNCCONNECTED) + { + // Send Error Response + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = 36; + memcpy(&buffptr->Data[0], "No Connection to TNC\r", 36); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return 0; // Don't try if not connected + } + + STREAM = &TNC->Streams[Stream]; + + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; // Discard *** connected + return 0; + } + + txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); // 1 as no PID + TXMsg = &buff->L2DATA[0]; + TXMsg[txlen] = 0; + + if (STREAM->Connected) + { + STREAM->PacketsSent++; + + SendDataMsg(TNC, TNC->FreeDataInfo->farCall, buff->L2DATA, txlen); + return 1; + } + + if (TNC->FreeDataInfo->Chat) + { + // Chat Mode - Send to other end + + char reply[512] = "m"; + char * p; + int Len; + + if (_stricmp(TXMsg, "/ex\r") == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + TNC->FreeDataInfo->Chat = 0; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} Chat with %s ended. \r", TNC->FreeDataInfo->ChatCall); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + } + + // Send as chat message + + //m�;send_message�;123�;64730c5c-d32c-47b4-9b11-c958fd07a185�;hhhhhhhhhhhhhhhhhh + //�;�;plain/text�; + + strlop(TXMsg, 13); + + strcpy(&reply[2], ";send_message"); + strcpy(&reply[16], ";123"); + reply[21] = ';'; + strcpy(&reply[22], gen_uuid()); + sprintf(&reply[59], ";%s\n", TXMsg); + + p = strchr(&reply[59], 0); + + p[1] = ';'; + strcpy(&p[3], ";plain/text"); + p[15] = ';'; + + Len = &p[16] - reply; + + SendAsRaw(TNC, TNC->FreeDataInfo->ChatCall, "", reply, Len); + + return 0; + } + + + + if (_memicmp(&buff->L2DATA[0], "D\r", 2) == 0 || _memicmp(&buff->L2DATA[0], "BYE\r", 4) == 0) + { + STREAM->ReportDISC = TRUE; // Tell Node + TNC->FreeDataInfo->Chat = 0; + return 0; + } + + + // See if Local command (eg RADIO) + + if (_memicmp(&buff->L2DATA[0], "RADIO ", 6) == 0) + { + sprintf(&buff->L2DATA[0], "%d %s", TNC->Port, &buff->L2DATA[6]); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK->CIRCUITINDEX, &buff->L2DATA[0])) + { + } + else + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", &buff->L2DATA[0]); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "OVERRIDEBUSY", 12) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->OverrideBusy = TRUE; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} OK\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + } + + if (_memicmp(&buff->L2DATA[0], "PING ", 5) == 0) + { + char * Call = &buff->L2DATA[5]; + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + strlop(Call, 13); + strlop(Call, ' '); + SendPing(TNC, _strupr(Call)); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} Ping Sent\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + + } + + if (_memicmp(&buff->L2DATA[0], "CQ", 2) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + SendCQ(TNC); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} CQ Sent\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + + } + + if (_memicmp(&buff->L2DATA[0], "CHAT ", 5) == 0) + { + char * Call = &buff->L2DATA[5]; + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + strlop(Call, 13); + strlop(Call, ' '); + + TNC->FreeDataInfo->Chat = 1; + memcpy(TNC->FreeDataInfo->ChatCall, _strupr(Call), 10); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} Chat with %s. Enter /ex to exit\r", Call); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + } + + + if (_memicmp(&TXMsg[0], "BEACON ", 7) == 0) + { + int Interval = atoi(&TXMsg[7]); + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + SendBeacon(TNC, Interval); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} Ok\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + } + + + + + // See if a Connect Command. + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + char Connect[80]; + char loppedCall[10]; + char toCall[10]; + + char * ptr = strchr(&buff->L2DATA[2], 13); + + if (ptr) + *ptr = 0; + + // FreeDATA doesn't have the concept of a connection, so need to simulate it between the nodes + _strupr(&buff->L2DATA[2]); + + if (strlen(&buff->L2DATA[2]) > 9) + buff->L2DATA[11] = 0; + + strcpy(TNC->FreeDataInfo->toCall, &buff->L2DATA[2]); + strcpy(loppedCall, TNC->FreeDataInfo->toCall); + if (TNC->FreeDataInfo->useBaseCall) + strlop(loppedCall, '-'); + strcpy(TNC->FreeDataInfo->farCall, loppedCall); + + // MYCALL and Target call are end to end concepts - the TNC cam can only use one call, set at TNC start. and no SSID's + // Messages are sent at TNC level to the tnc call, so we send our tnc call to the other end + + + txlen = sprintf(Connect, "C %s %s %s ", &buff->L2DATA[2], STREAM->MyCall, TNC->FreeDataInfo->ourCall); + + // 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; + + memset(STREAM->RemoteCall, 0, 10); + strcpy(STREAM->RemoteCall, &buff->L2DATA[2]); + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->PacketsSent = 0; + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", STREAM->MyCall, STREAM->RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + +// FreeDataSendCommand(TNC, Connect); + FreeDataConnect(TNC, STREAM->RemoteCall); + STREAM->Connecting = TRUE; + return 0; + + } + + // Normal data. Send to TNC + + // The TNC doesn't have any commands, so send error message + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} Not connected\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + // strlop(buff->L2DATA, 13); + // txlen = SendDataMsg(TNC, TNC->, buff->L2DATA); + // FreeDataSendData(TNC, TXMsg, txlen); + + return 0; + + case 3: + + // CHECK IF OK TO SEND (And check TNC Status) + + Stream = (int)(size_t)buff; + + // FreeData TNC can buffer unlimited data + + if (TNC->Streams[Stream].Attached == 0) + return (TNC->TNCCONNECTED != 0) << 8 | 1; + + return ((TNC->TNCCONNECTED != 0) << 8 | TNC->Streams[Stream].Disconnecting << 15); // OK + + + case 4: // reinit7 + + return 0; + + case 5: // Close + + StopTNC(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 FreeDATA"); + return 1; // OK to change + } + + if (Param == 1) // Request Permission + { + if (!TNC->CONNECTED) + return 0; // No connection so no interlock + + if (TNC->ConnectPending == 0 && TNC->PTTState == 0) + { + TNC->FreeDataInfo->CONOK = 0; + TNC->GavePermission = TRUE; + return 0; // OK to Change + } + + if (TNC->ConnectPending) + TNC->ConnectPending--; // Time out if set too long + + return TRUE; + } + + if (Param == 3) // Release Permission + { + if (TNC->GavePermission) + { + TNC->GavePermission = FALSE; + if (TNC->ARDOPCurrentMode[0] != 'S') // Skip + TNC->FreeDataInfo->CONOK = 1; + + } + return 0; + } + + // Param is Address of a struct ScanEntry + + Scan = (struct ScanEntry *)buff; + return 0; + } + return 0; +} + +VOID FreeDataReleaseTNC(struct TNCINFO * TNC) +{ + // Set mycall back to Node or Port Call, and Start Scanner + + UCHAR TXMsg[1000]; + + FreeDataChangeMYC(TNC, TNC->NodeCall); + + // Start Scanner + + sprintf(TXMsg, "%d SCANSTART 15", TNC->Port); + + Rig_Command(-1, TXMsg); + + ReleaseOtherPorts(TNC); + +} + +VOID FreeDataSuspendPort(struct TNCINFO * TNC) +{ + TNC->FreeDataInfo->CONOK = 0; +} + +VOID FreeDataReleasePort(struct TNCINFO * TNC) +{ + TNC->FreeDataInfo->CONOK = 1; +} + + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, "" + "" + "FreDATA Status" + "

FreeData Status" + "

", + TNC->Port); + + + 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
Proto State%s
Traffic%s
TNC Restarts
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + +#ifndef LINBPQ + +#define BGCOLOUR RGB(236,233,216) +static HBRUSH RedBrush = NULL; +HBRUSH GreenBrush; +HBRUSH BlueBrush; +static HBRUSH bgBrush = NULL; + +extern HWND ClientWnd, FrameWnd; +extern int OffsetH, OffsetW; + +extern HMENU hMainFrameMenu; +extern HMENU hBaseMenu; +extern HANDLE hInstance; + +extern HKEY REGTREE; + + +/* +static LRESULT CALLBACK PacWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + MINMAXINFO * mmi; + PAINTSTRUCT ps; + HDC hdc; + + 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(ps.hdc, RedBrush); + SelectObject(ps.hdc, GreenBrush); +// SelectObject(ps.hdc, GetStockObject(GRAY_BRUSH)); + + EndPaint(hWnd, &ps); + 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")) + { + 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: + + KillTNC(TNC); + break; + + case WINMOR_RESTART: + + 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_DESTROY: + + break; + } + return DefMDIChildProc(hWnd, message, wParam, lParam); +} +*/ + +#endif + + + +VOID * FreeDataExtInit(EXTPORTDATA * PortEntry) +{ + int port; + char Msg[255]; + char * ptr; + struct TNCINFO * TNC; + char * TempScript; + u_long param = 1; + int line; + + srand(time(NULL)); + + port = PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + + if (TNC->FreeDataInfo->TuningRange == 0) + TNC->FreeDataInfo->TuningRange = 50; + +#ifndef LINBPQ + + if (bgBrush == NULL) + { + bgBrush = CreateSolidBrush(BGCOLOUR); + RedBrush = CreateSolidBrush(RGB(255,0,0)); + GreenBrush = CreateSolidBrush(RGB(0,255,0)); + BlueBrush = CreateSolidBrush(RGB(0,0,255)); + } + +#endif + + Consoleprintf("FreeData Host %s %d", TNC->HostName, TNC->TCPPort); + + TNC->Port = port; + TNC->Hardware = H_FREEDATA; + + TNC->ARDOPDataBuffer = malloc(MAXRXSIZE); + TNC->ARDOPBuffer = malloc(8192); + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + memcpy(TNC->FreeDataInfo->ourCall, TNC->NodeCall, 10); + strlop(TNC->FreeDataInfo->ourCall, ' '); + if (TNC->FreeDataInfo->useBaseCall) + strlop(TNC->FreeDataInfo->ourCall, '-'); + + if (TNC->FreeDataInfo->hamlibHost == 0) + TNC->FreeDataInfo->hamlibHost = _strdup("127.0.0.1"); + + 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; + + if (TNC->PacketChannels > 1) + TNC->PacketChannels = 1; + + PortEntry->MAXHOSTMODESESSIONS = TNC->PacketChannels + 1; + + PortEntry->SCANCAPABILITIES = SIMPLE; // Scan Control - pending connect only + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + + PortEntry->PORTCONTROL.UICAPABLE = TRUE; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 236; + + TNC->SuspendPortProc = FreeDataSuspendPort; + TNC->ReleasePortProc = FreeDataReleasePort; + +// PortEntry->PORTCONTROL.PORTSTARTCODE = KAMStartPort; +// PortEntry->PORTCONTROL.PORTSTOPCODE = KAMStopPort; + + + 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); + + // cant think of any yet + + if (TNC->InitScript) + { + strcat(TempScript, TNC->InitScript); + free(TNC->InitScript); + } + + TNC->InitScript = TempScript; + + // Set MYCALL + + sprintf(Msg, "MYCALL %s\r", TNC->NodeCall); + strcat(TNC->InitScript, Msg); + + strcpy(TNC->CurrentMYC, TNC->NodeCall); + + if (TNC->WL2K == NULL) + if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Alrerady decoded + TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo; + + 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, 500, 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,450,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,80,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", "Proto State", 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,116,line,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); + + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 0,170,250,300, TNC->hDlg, NULL, hInstance, NULL); + + TNC->ClientHeight = 450; + TNC->ClientWidth = 500; + + TNC->hMenu = CreatePopupMenu(); + + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill TNC TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart TNC 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 + + time(&TNC->lasttime); // Get initial time value + + ConnecttoFreeData(port); + TNC->FreeDataInfo->CONOK = 1; + + return ExtProc; +} + + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // We don't get data acks, so can't check for bytes outstanding + + FreeDataDisconnect(TNC); +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + FreeDataDisconnect(TNC); +} + + + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + if (Stream == 0) + { + FreeDataReleaseTNC(TNC); + } +} + +VOID FreeDataAbort(struct TNCINFO * TNC) +{ + FreeDataSendCommand(TNC, "ABORT\r"); +} + +// Host Mode Stuff (we reuse some routines in SCSPactor) + +VOID FreeDataDoTermModeTimeout(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->ReinitState == 0) + { + //Checking if in Terminal Mode - Try to set back to Term Mode + + TNC->ReinitState = 1; + return; + } + + if (TNC->ReinitState == 1) + { + // Forcing back to Term Mode + + TNC->ReinitState = 0; + return; + } + + if (TNC->ReinitState == 3) + { + return; + } +} + +static RECT Rect1 = {30, 160, 400, 195}; + +int zEncode(unsigned char * in, unsigned char * out, int len, unsigned char * Banned) +{ + // Replace forbidden chars with =xx + + unsigned char * ptr = out; + unsigned char c; + + while (len--) + { + c = *(in++); + + if (strchr(&Banned[0], c)) + { + *(out++) = '='; + *(out++) = (c >> 4) + 'A'; + *(out++) = (c & 15) + 'A'; + } + else + *(out++) = c; + } + + return (out - ptr); +} + + + +VOID FreeDataProcessTNCMessage(struct TNCINFO * TNC, char * Call, unsigned char * Msg, int Len) +{ + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FreeDataINFO * Modem = TNC->FreeDataInfo; + char * toCall, * fromCall, * tncCall, *Context; + char * ptr; + char a, b; + unsigned char axcall[7]; + char AppName[13] = ""; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + TRANSPORTENTRY * SESS; + + // First Byte of Message is Type. Messages can be commands or short (<120) data packets + // Data is encoded with =xx replacing restricted chars + + Msg[Len] = 0; + + switch (Msg[0]) + { + case 'C': + + // Connect Request. C G8BPQ-10 GM8BPQ-2 (Target, Origin) + + toCall = strtok_s(&Msg[2], " ", &Context); + fromCall = strtok_s(NULL, " ", &Context); + tncCall = strtok_s(NULL, " ", &Context); + + strcpy(TNC->FreeDataInfo->farCall, tncCall); + + ConvToAX25Ex(fromCall, axcall); // Allow -T and -R SSID's for MPS + + // Check for ExcludeList + + if (ExcludeList[0]) + { + if (CheckExcludeList(axcall) == FALSE) + { + Debugprintf("FreeData Call from %s rejected", fromCall); + + // Send 'd' + + Sleep(1000); + FreeDataDisconnect(TNC); + 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(axcall, ptr, 6) == 0) // Ignore SSID + break; + + ptr += 7; + + if ((*ptr) == 0) // Not in list + { + Sleep(1000); + FreeDataSendCommand(TNC, "d"); + + Debugprintf("FreeDara 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(toCall, Appl) == 0) + break; + } + + if (App < 32) + { + + 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)) + { + Sleep(1000); + FreeDataSendCommand(TNC, "dApplication not Available"); + return; + } + } + + ProcessIncommingConnectEx(TNC, fromCall, 0, TRUE, TRUE); + SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + // if connect to an application, send command + + if (AppName[0]) + { + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf(buffptr->Data, "%s\r", AppName); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + TNC->SwallowSignon = TRUE; + } + } + + 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, toCall, 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; + } + } + SESS->Mode = TNC->WL2KMode; + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, toCall); + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + SESS->Mode = WL2K->mode; + } + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->PacketsSent = 0; + STREAM->Connected = TRUE; + + // Send Connect ACK + Sleep(1000); + FreeDataSendCommand(TNC, "c"); + return; + + case 'c': + + // Connect ACK + + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s", STREAM->MyCall, STREAM->RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + STREAM->Connected = TRUE; + STREAM->Connecting = FALSE; + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "*** Connected to %s\r", TNC->FreeDataInfo->toCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->FramesQueued++; + + return; + + case 'D': + + // Disconnect Command + + FreeDataSendCommand(TNC, "d"); + + // Drop through to disconnect this end + + case 'd': + + // Disconnect complete (response to sending "D") + // Or connect refused in response to "C" + + if (STREAM->Connecting) + { + // Connection Refused - If there is a message, pass to appl + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->Connecting = FALSE; + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + if (Msg[1]) + buffptr->Len = sprintf(buffptr->Data, "Connect Rejected - %s\r", &Msg[1]); + else + buffptr->Len = sprintf(buffptr->Data, "Connect Rejected\r"); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->FramesQueued++; + return; + } + + // Release Session + + if (STREAM->Connected) + { + // 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->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + STREAM->Disconnecting = FALSE; + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + return; + + case 'B': + + // Was Base64, but has been expanded - just send to User + + // If len > blocksize, fragment + + Len--; + Msg++; // Remove Type + + while (Len > 256) + { + buffptr = GetBuff(); + buffptr->Len = 256; + memcpy(buffptr->Data, Msg, 256); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + WritetoTrace(TNC, Msg, 256); + Len -= 256; + Msg += 256; + STREAM->BytesRXed += 256; + + } + + buffptr = GetBuff(); + buffptr->Len = Len; + memcpy(buffptr->Data, Msg, Len); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + WritetoTrace(TNC, Msg, Len); + STREAM->BytesRXed += Len; + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->BytesTXed - TNC->FreeDataInfo->toSendCount, STREAM->BytesRXed, TNC->FreeDataInfo->toSendCount); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + + return; + + case 'I': + + // Encoded Response + + // Undo = transparency + + ptr = Msg + 1; + + while (ptr = strchr(ptr, '=')) + { + // Next two chars are a hex value + + a = ptr[1] - 'A'; + b = ptr[2] - 'A'; + memmove(ptr, ptr + 2, Len); + ptr[0] = (a << 4) + b; + ptr++; + } + + buffptr = GetBuff(); + buffptr->Len = sprintf(buffptr->Data, "%s", &Msg[1]); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + return; + + case 'f': + + // FreeDATA File Transfer + + // Seems to be f null fn null data + //f.;Makefile.;.;123123123.; + { + char * FN; + time_t CRC; + int FileLen; + char Filename[256]; + FILE * fp1; + unsigned char * ptr; + char Text[64]; + int textLen; + + + if (TNC->FreeDataInfo->RXDir == NULL) + { + Debugprintf("FreeDATA RXDIRECTORY not set - file transfer ignored"); + return; + } + + FN = _strdup(&Msg[3]); + ptr = &Msg[4] + strlen(FN); + + ptr = ptr + strlen(ptr) + 2; + CRC = atoi(ptr); + ptr = ptr + strlen(ptr) + 2; + + FileLen = Len - (ptr - Msg); + + sprintf(Filename, "%s\\%s", TNC->FreeDataInfo->RXDir, FN); + + fp1 = fopen(Filename, "wb"); + + if (fp1) + { + fwrite(ptr, 1, FileLen, fp1); + fclose(fp1); + textLen = sprintf(Text, "File %s received from %s \r", FN, Call); + WritetoTrace(TNC, Text, textLen); + } + else + Debugprintf("FreeDATA - File %s create failed %s", Filename); + + + free(FN); + return; + + } + + + } + + if (STREAM->Attached == 0) + return; + + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", Msg); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + +} + + +VOID FreeDataProcessNewConnect(struct TNCINFO * TNC, char * fromCall, char * toCall) +{ + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FreeDataINFO * Modem = TNC->FreeDataInfo; + char * tncCall, *Context; + char * ptr; + char a, b; + unsigned char axcall[7]; + char AppName[13] = ""; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + TRANSPORTENTRY * SESS; + + strcpy(TNC->FreeDataInfo->farCall, fromCall); + + ConvToAX25Ex(fromCall, axcall); // Allow -T and -R SSID's for MPS + + // Check for ExcludeList + + if (ExcludeList[0]) + { + if (CheckExcludeList(axcall) == FALSE) + { + Debugprintf("FreeData Call from %s rejected", fromCall); + + // Send 'd' + + Sleep(1000); + FreeDataDisconnect(TNC); + 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(axcall, ptr, 6) == 0) // Ignore SSID + break; + + ptr += 7; + + if ((*ptr) == 0) // Not in list + { + Sleep(1000); + FreeDataDisconnect(TNC); + + Debugprintf("FreeData Call from %s not in ValidCalls - rejected", fromCall); + return; + } + } + } + + // The TNC responds to any SSID so we can use incomming call as Appl Call + // No we can't - it responds but reports the configured call not the called call +/* + // 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(toCall, Appl) == 0) + break; + } + + if (App < 32) + { + + 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(fromCall, "-T" ) || strstr(fromCall, "-R"))) + strcpy(AppName, "RELAY "); + + // Make sure app is available + + if (!CheckAppl(TNC, AppName)) + { + Sleep(1000); + FreeDataSendCommand(TNC, "dApplication not Available"); + return; + } + } + +*/ + + ProcessIncommingConnectEx(TNC, fromCall, 0, TRUE, TRUE); + SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + // if connect to an application, send command + + if (AppName[0]) + { + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf(buffptr->Data, "%s\r", AppName); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + TNC->SwallowSignon = TRUE; + } + } + + 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, toCall, 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; + } + } + SESS->Mode = TNC->WL2KMode; + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, toCall); + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + SESS->Mode = WL2K->mode; + } + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->PacketsSent = 0; + STREAM->Connected = TRUE; + + return; + +} + +VOID FreeDataProcessConnectAck(struct TNCINFO * TNC, char * Call, unsigned char * Msg, int Len) +{ + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FreeDataINFO * Modem = TNC->FreeDataInfo; + char * toCall, * fromCall, * tncCall, *Context; + char * ptr; + char a, b; + unsigned char axcall[7]; + char AppName[13] = ""; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + TRANSPORTENTRY * SESS; + + + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s", STREAM->MyCall, STREAM->RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + STREAM->Connected = TRUE; + STREAM->Connecting = FALSE; + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "*** Connected to %s\r", TNC->FreeDataInfo->toCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->FramesQueued++; + + return; + +} +extern char LOC[7]; +/* +Line 104: if received_json["type"] == 'PING' and received_json["command"] == "PING": +Line 111: if received_json["type"] == 'ARQ' and received_json["command"] == "sendFile": +Line 142: if received_json["type"] == 'ARQ' and received_json["command"] == "sendMessage": +Line 173: if received_json["type"] == 'ARQ' and received_json["command"] == "stopTransmission": +Line 182: if received_json["type"] == 'GET' and received_json["command"] == 'STATION_INFO': +Line 195: if received_json["type"] == 'GET' and received_json["command"] == 'TNC_STATE': +Line 244: if received_json["type"] == 'GET' and received_json["command"] == 'RX_BUFFER': +Line 258: if received_json["type"] == 'GET' and received_json["command"] == 'RX_MSG_BUFFER': +Line 272: if received_json["type"] == 'SET' and received_json["command"] == 'DEL_RX_BUFFER': +Line 275: if received_json["type"] == 'SET' and received_json["command"] == 'DEL_RX_MSG_BUFFER': +*/ + +//{\"type\" : \"ARQ\", \"command\" : \"sendMessage\", \"dxcallsign\" : \"G8BPQ\", \"mode\" : \"10\", \"n_frames\" : \"1\", \"data\" : \"Hello Hello\" , \"checksum\" : \"123\", \"timestamp\" : 1642580748576} + + + +static unsigned char BANNED[] = {'"', '=', ':', '{', '}', '[', ']', '/', 13, 0}; // I think only need to escape = ": CR Null + + +static void SendDataMsg(struct TNCINFO * TNC, char * Call, char * Msg, int Len) +{ + // We can't base64 encode chunks. so buffer as original data and encode on send + + SendAsFile(TNC, TNC->FreeDataInfo->farCall, Msg, Len); + WritetoTrace(TNC, Msg, Len); + + return; +} + + + +static int SendAsRaw(struct TNCINFO * TNC, char * Call, char * myCall, char * Msg, int Len) +{ + char Message[16284]; + char * Base64; + + // TNC now only supports send_raw, with base64 encoded data + + char Template[] = "{\"type\" : \"arq\", \"command\" : \"send_raw\", \"uuid\" : \"%s\",\"parameter\":" + "[{\"dxcallsign\" : \"%s\", \"mode\": \"255\", \"n_frames\" : \"1\", \"data\" : \"%s\"}]}\n"; + + + Base64 = byte_base64_encode(Msg, Len); + + Len = sprintf(Message, Template, gen_uuid(), Call, Base64); + + free(Base64); + return send(TNC->TCPDataSock, Message, Len, 0); +} + +void FlushData(struct TNCINFO * TNC) +{ + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FreeDataINFO * Info = TNC->FreeDataInfo; + int Len = Info->toSendCount; + + // We need to flag as data (B) then base64 encode it + + memmove(&Info->toSendData[1], Info->toSendData, Len); + Info->toSendData[0] = 'B'; + Len++; + + SendAsRaw(TNC, Info->farCall, Info->ourCall, Info->toSendData, Len); + + Info->toSendCount = 0; + Info->toSendTimeout = 0; + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->BytesTXed - TNC->FreeDataInfo->toSendCount, STREAM->BytesRXed, TNC->FreeDataInfo->toSendCount); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + +} + +static int SendAsFile(struct TNCINFO * TNC, char * Call, char * Msg, int Len) +{ + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FreeDataINFO * Info = TNC->FreeDataInfo; + + // Add to buffer + + if ((Info->toSendCount + Len) > 8192) // Reasonable Limit + { + // Send the buffered bit + + FlushData(TNC); + } + + memcpy(&Info->toSendData[Info->toSendCount], Msg, Len); + Info->toSendCount += Len; + Info->toSendTimeout = 10; // About a second + + STREAM->BytesTXed += Len; + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->BytesTXed - TNC->FreeDataInfo->toSendCount, STREAM->BytesRXed, TNC->FreeDataInfo->toSendCount); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + + return Len; +} + +static int SendTNCCommand(struct TNCINFO * TNC, char * Type, char * Command) +{ + char Message[256]; + int Len; + + Len = sprintf(Message, "{\"type\" : \"%s\", \"command\" : \"%s\"}\n", Type, Command); + return send(TNC->TCPDataSock, Message, Len, 0); +} + +static void SendPoll(struct TNCINFO * TNC) +{ + char DataMsgPoll[] = "{\"type\" : \"GET\", \"command\" : \"RX_MSG_BUFFER\"}\n"; +// char DaemonPoll[] = "{\"type\" : \"GET\", \"command\" : \"DAEMON_STATE\"}\n"; + char TNCPoll[] = "{\"type\" : \"GET\", \"command\" : \"TNC_STATE\", \"timestamp\" : 0}\n"; + + // Poll daemon rapidly until tnc is connected, then every 5 secs + + if ((TNC->PollDelay++ > 50))// || !TNC->TNCCONNECTED) && TNC->DAEMONCONNECTED) + { +// ret = send(TNC->TCPSock, (char *)&DaemonPoll, strlen(DaemonPoll), 0); + TNC->PollDelay = 0; + + } + + if (TNC->TNCCONNECTED) + { +// ret = send(TNC->TCPDataSock, (char *)&TNCPoll, strlen(TNCPoll), 0); + +// if (TNC->PollDelay & 1) +// ret = SendTNCCommand(TNC, "GET", "RX_BUFFER"); +// else if (TNC->PollDelay & 2) +// ret = SendTNCCommand(TNC, "GET", "RX_MSG_BUFFER"); +// else +// ret = send(TNC->TCPDataSock, (char *)&TNCPoll, strlen(TNCPoll), 0); + + + } + return; +} + +static void SendPing(struct TNCINFO * TNC, char * Call) +{ + char Ping[] = "{\"type\" : \"ping\", \"command\" : \"ping\", \"dxcallsign\" : \"%s\", \"timestamp\" : %d}\n"; + char Message[256]; + int Len, ret; + + Len = sprintf(Message, Ping, Call, time(NULL)); + ret = send(TNC->TCPDataSock, (char *)&Message, Len, 0); +} + +static void SendCQ(struct TNCINFO * TNC) +{ + char CQ[] = "{\"type\" : \"broadcast\", \"command\" : \"cqcqcq\"}\n"; + + char Message[256]; + int Len, ret; + + Len = sprintf(Message, CQ); + ret = send(TNC->TCPDataSock, (char *)&Message, Len, 0); +} + +static void SendBeacon(struct TNCINFO * TNC, int Interval) +{ + char Template1[] = "{\"type\" : \"broadcast\", \"command\" : \"start_beacon\", \"parameter\" : \"%d\"}\n"; + char Template2[] = "{\"type\" : \"broadcast\", \"command\" : \"stop_beacon\"}\n"; + + char Message[256]; + int Len, ret; + + if (Interval > 0) + Len = sprintf(Message, Template1, Interval); + else + Len = sprintf(Message, Template2); + + ret = send(TNC->TCPDataSock, (char *)&Message, Len, 0); +} + + +unsigned short int compute_crc(unsigned char *buf,int len); + + +int FreeDataWriteCommBlock(struct TNCINFO * TNC) +{ + if (TNC->hDevice) + return WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); + + return 0; +} + +char * getObjectFromArray(char * Msg) +{ + // This gets the next object from an array ({} = object, [] = array + // We look for the end of the object same number of { and }, teminate after } and return pointer to next object + // So we have terminated Msg, and returned next object in array + + // Only call if Msg is the next object in array + + + char * ptr = Msg; + char c; + + int Open = 0; + int Close = 0; + + while (c = *(ptr++)) + { + if (c == '{') Open ++; else if (c == '}') Close ++; + + if (Open == Close) + { + *(ptr++) = 0; + return ptr; + } + } + return 0; +} +/* + ["DATACHANNEL;RECEIVEDOPENER","ARQ;RECEIVING","ARQ;RECEIVING;SUCCESS"] [{"DXCALLSIGN":"GM8BPQ +{"DXCALLSIGN":"GM8BPQ","DXGRID":"","TIMESTAMP":1642847440,"RXDATA":[{"dt":"f","fn":"config.json", +"ft":"application\/json","d":"data:application\/json;base64, +ewogICAgInRuY19ob3N0IiA6ICIxMjcuMC4wLjEiLAogICAgInRuY19wb3J0IiA6ICIzMDAwIiwKICAgICJkYWVtb25faG9zdCIgOiAiMTI3LjAuMC4xIiwKICAgICJkYWVtb25fcG9ydCIgOiAiMzAwMSIsCiAgICAibXljYWxsIiA6ICJBQTBBQSIsCiAgICAibXlncmlkIiA6ICJBQTExZWEiICAgIAp9" +,"crc":"123123123"}]} +*/ +void ProcessFileObject(struct TNCINFO * TNC, char * This) +{ + char * Call; + char * LOC; + char * FN; + char * Type; + char * ptr, * ptr2; + int Len, NewLen; + + Call = strchr(This, ':'); + Call += 2; + This = strlop(Call, '"'); + + LOC = strchr(This, ':'); + LOC += 2; + This = strlop(LOC, '"'); + + FN = strstr(This, "fn"); + FN += 5; + This = strlop(FN, '"'); + + Type = strstr(This, "base64"); + Type += 7; + This = strlop(Type, '"'); + + // Decode Base64 + + Len = strlen(Type); + + Debugprintf("RX %d %s %d", TNC->Port, FN, Len); + + ptr = ptr2 = Type; + + while (Len > 0) + { + xdecodeblock(ptr, ptr2); + ptr += 4; + ptr2 += 3; + Len -= 4; + } + + NewLen = (int)(ptr2 - Type); + + if (*(ptr-1) == '=') + NewLen--; + + if (*(ptr-2) == '=') + NewLen--; + + Type[NewLen] = 0; + + Type --; + + Type[0] = 'B'; ; // Base64 Info + + FreeDataProcessTNCMessage(TNC, Call, Type, NewLen + 1); + + +} + +void ProcessMessageObject(struct TNCINFO * TNC, char * This) +{ + // This gets Message from a RX_MSG_BUFFER array element. + + char * Call; + char * LOC; + char * Type; + char * Msg; + int Len; + char * ptr, * ptr2; + + char * ID; + char * TYPE; + char * SEQ; + char * UUID; + char * TEXT; + char * NOIDEA; + char * FORMAT; + char * FILENAME; + int fileLen; + + int n; + + + Call = strstr(This, "dxcallsign"); + Call += 13; + This = strlop(Call, '"'); + + LOC = strchr(This, ':'); + LOC += 2; + This = strlop(LOC, '"'); + + Msg = strstr(This, "\"data\""); + Msg += 8; + This = strlop(Msg, '"'); + + // Decode Base64 + + // FreeData replaces / with \/ so need to undo + + ptr2 = strstr(Msg, "\\/"); + + while (ptr2) + { + memmove(ptr2, ptr2 + 1, strlen(ptr2)); + ptr2 = strstr(ptr2, "\\/"); + } + + Len = strlen(Msg); + + ptr = ptr2 = Msg; + + while (Len > 0) + { + xdecodeblock(ptr, ptr2); + ptr += 4; + ptr2 += 3; + Len -= 4; + } + + Len = (int)(ptr2 - Msg); + + if (*(ptr-1) == '=') + Len--; + + if (*(ptr-2) == '=') + Len--; + + Msg[Len] = 0; + +//m�;send_message�;123�;64730c5c-d32c-47b4-9b11-c958fd07a185�;hhhhhhhhhhhhhhhhhh +//�;�;plain/text�; + +//m;send_message;123;64730c5c-d32c-47b4-9b11-c958fd07a185;hhhhhhhhhhhhhhhhhh +//;;plain/text; + + // Message elements seem to be delimited by null ; + // Guessing labels + + ID = Msg; + + if (ID[0] == 'B') + { + // BPQ Message + + struct STREAMINFO * STREAM = &TNC->Streams[0]; + PMSGWITHLEN buffptr; + + + if (STREAM->Attached) + { + if (STREAM->Connected == 1 && STREAM->Connecting == 0) + { + char * Line = &ID[1]; + Len -= 1; + + while (Len > 256) + { + buffptr = GetBuff(); + buffptr->Len = 256; + memcpy(buffptr->Data, Line, 256); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + WritetoTrace(TNC, Line, 256); + Len -= 256; + Line += 256; + STREAM->BytesRXed += 256; + } + + buffptr = GetBuff(); + buffptr->Len = Len; + memcpy(buffptr->Data, Line, Len); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + WritetoTrace(TNC, Line, Len); + STREAM->BytesRXed += Len; + + } + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->BytesTXed - TNC->FreeDataInfo->toSendCount, STREAM->BytesRXed, TNC->FreeDataInfo->toSendCount); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + } + return; + } + + n = strlen(ID) + 2; + Msg += n; + Len -= n; + + if (ID[0] == 'm') + { + // ?? Chat ?? comes from a send raw ?? + + struct STREAMINFO * STREAM = &TNC->Streams[0]; + PMSGWITHLEN buffptr; + + TYPE = Msg; + n = strlen(TYPE) + 2; + Msg += n; + Len -= n; + + SEQ = Msg; + n = strlen(SEQ) + 2; + Msg += n; + Len -= n; + + UUID = Msg; + n = strlen(UUID) + 2; + Msg += n; + Len -= n; + + TEXT = Msg; + n = strlen(TEXT) + 2; + Msg += n; + Len -= n; + + NOIDEA = Msg; + n = strlen(NOIDEA) + 2; + Msg += n; + Len -= n; + + FORMAT = Msg; + n = strlen(FORMAT) + 2; + Msg += n; + Len -= n; + + // if Atached, send to user + + if (STREAM->Attached) + { + if (STREAM->Connected == 0 && STREAM->Connecting == 0) + { + // Just attached - send as Chat Message + + char Line[560]; + char * rest; + + // Send line by line + + rest = strlop(TEXT, 10); // FreeData chat uses LF + + while (TEXT && TEXT[0]) + { + Len = strlen(TEXT); + if (Len > 512) + TEXT[512] = 0; + + Len = sprintf(Line, "Chat From %-10s%s\r", Call, TEXT); + + while (Len > 256) + { + buffptr = GetBuff(); + buffptr->Len = 256; + memcpy(buffptr->Data, Line, 256); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + WritetoTrace(TNC, Line, 256); + Len -= 256; + TEXT += 256; + STREAM->BytesRXed += 256; + } + + buffptr = GetBuff(); + buffptr->Len = Len; + memcpy(buffptr->Data, Line, Len); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + WritetoTrace(TNC, Line, Len); + STREAM->BytesRXed += Len; + + TEXT = rest; + rest = strlop(TEXT, 10); // FreeData chat ues LF + } + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->BytesTXed - TNC->FreeDataInfo->toSendCount, STREAM->BytesRXed, TNC->FreeDataInfo->toSendCount); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + } + } + else + { + // Send Not Available Message +//m�;send_message�;123�;64730c5c-d32c-47b4-9b11-c958fd07a185�;hhhhhhhhhhhhhhhhhh +//�;�;plain/text�; + + char reply[512] = "m"; + char * p; + + strcat(TEXT, "\r"); + WritetoTrace(TNC, TEXT, strlen(TEXT)); + + strcpy(&reply[2], ";send_message"); + strcpy(&reply[16], ";123"); + reply[21] = ';'; + strcpy(&reply[22], gen_uuid()); + sprintf(&reply[59], ";Message received but user not on line\n"); + + p = strchr(&reply[59], 0); + + p[1] = ';'; + strcpy(&p[3], ";plain/text"); + p[15] = ';'; + + Len = &p[16] - reply; + + SendAsRaw(TNC, Call, TNC->FreeDataInfo->ourCall, reply, Len); + } + return; + + } + else if (ID[0] == 'f') + { + // File Tranfer + + char Filename[256]; + FILE * fp1; + char Text[64]; + int textLen; + + + FILENAME = Msg; + n = strlen(FILENAME) + 2; + Msg += n; + Len -= n; + + TYPE = Msg; + n = strlen(TYPE) + 2; + Msg += n; + Len -= n; + + SEQ = Msg; // ?? Maybe = 123123123 + n = strlen(SEQ) + 2; + Msg += n; + Len -= n; + + TEXT = Msg; // The file + fileLen = Len; + + if (TNC->FreeDataInfo->RXDir == NULL) + { + Debugprintf("FreeDATA RXDIRECTORY not set - file transfer ignored"); + return; + } + + sprintf(Filename, "%s\\%s", TNC->FreeDataInfo->RXDir, FILENAME); + + fp1 = fopen(Filename, "wb"); + + if (fp1) + { + fwrite(TEXT, 1, fileLen, fp1); + fclose(fp1); + textLen = sprintf(Text, "File %s received from %s \r", FILENAME, Call); + WritetoTrace(TNC, Text, textLen); + } + else + Debugprintf("FreeDATA - File %s create failed %s", Filename); + + return; + } + else if (ID[0] == 'm') + { + + } + + + + + + + + + + +// FreeDataProcessTNCMessage(TNC, Call, Msg, strlen(Msg)); +} + +void processJSONINFO(struct TNCINFO * TNC, char * Info, char * Call, double snr) +{ + char * LOC = ""; + char * ptr, * Context; + + // Info is an array. Normally only one element, but should check + + ptr = strtok_s(&Info[1], ",]", &Context); + + while (ptr && ptr[1]) + { + if (strstr(ptr, "BEACON;RECEIVING")) + { + char CQ[64]; + int Len; + + Len = sprintf(CQ, "Beacon received from %s SNR %3.1f\r", Call, snr); + WritetoTrace(TNC, CQ, Len); + + // Add to MH + + if (Call) + UpdateMH(TNC, Call, '!', 'I'); + } + if (strstr(ptr, "PING;RECEIVING")) + { + // Add to MH + + if (Call) + UpdateMH(TNC, Call, '!', 'I'); + } + else if (strstr(ptr, "CQ;RECEIVING")) + { + char CQ[64]; + int Len; + + Len = sprintf(CQ, "CQ received from %s SNR %3.1f\r", Call, snr); + WritetoTrace(TNC, CQ, Len); + + // Add to MH + + UpdateMH(TNC, Call, '!', 'I'); + } + else if (strstr(ptr, "PING;RECEIVEDACK")) + { + char Msg[128]; + int Len; + + Len = sprintf(Msg, "Ping Response from %s SNR %3.1f\r", Call, snr); + FreeDataProcessTNCMessage(TNC, Call, Msg, Len); + + // Add to MH + + UpdateMH(TNC, Call, '!', 'I'); + } + else if (strstr(ptr, "TRANSMITTING;FAILED")) + { + // Failed to send a message - if it was a connect request tell appl + + struct STREAMINFO * STREAM = &TNC->Streams[0]; + PMSGWITHLEN buffptr; + + if (STREAM->Connecting) + { + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->Connecting = FALSE; + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "*** Connect Failed\r"); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->FramesQueued++; + } + } + ptr = strtok_s(NULL, ",]", &Context); + } +} + + + + + + +char * getJSONValue(char * Msg, char * Key) +{ + char * ptr, *ptr2, *value = 0; + int vallen, keylen = strlen(Key); + char c; + + // We Null Terminate the value, so we must look for keys in reverse order + + ptr = strstr(Msg, Key); + + if (ptr) + { + ptr += (keylen + 1); + + if (*(ptr) == '[') + { + // Array + + int Open = 0; + int Close = 0; + + ptr2 = ptr; + + while (c = *(ptr++)) + { + if (c == '[') + Open ++; + else if (c == ']') + Close ++; + + if (Open == Close) + { + vallen = ptr - ptr2; + value = ptr2; + value[vallen] = 0; + return value; + } + } + } + else if (*(ptr) == '\"') + { + // String + + ptr2 = ptr; + ptr = strchr(ptr + 1, '\"'); + if (ptr) + { + ptr++; + vallen = ptr - ptr2; + value = ptr2; + value[vallen] = 0; + } + } + } + return value; +} + + +char stopTNC[] = "{\"type\" : \"SET\", \"command\": \"STOPTNC\" , \"parameter\": \"---\" }\r"; + + +void ProcessDAEMONJSON(struct TNCINFO * TNC, char * Msg, int Len) +{ + char * ptr; + char * capture, * playback; + + + if (memcmp(Msg, "{\"command\":\"daemon_state\"", 25) == 0) + { +/* + {"COMMAND":"DAEMON_STATE","DAEMON_STATE":[{"STATUS":"stopped"}], + "PYTHON_VERSION":"3.9", + "HAMLIB_VERSION":"4.0", + "INPUT_DEVICES":[{"ID":5,"NAME":"pulse"},{"ID":9,"NAME":"default"}], + "OUTPUT_DEVICES":[{"ID":0,"NAME":"bcm2835 Headphones: - (hw:0,0)"},{"ID":1,"NAME":"sysdefault"},{"ID":2,"NAME":"lavrate"},{"ID":3,"NAME":"samplerate"},{"ID":4,"NAME":"speexrate"},{"ID":5,"NAME":"pulse"},{"ID":6,"NAME":"upmix"},{"ID":7,"NAME":"vdownmix"},{"ID":8,"NAME":"dmix"},{"ID":9,"NAME":"default"}], + "SERIAL_DEVICES":[{"PORT":"\/dev\/ttyAMA0","DESCRIPTION":"ttyAMA0 [1dff]"}], + "CPU":"39.6", + "RAM":"38.6", + "VERSION":"0.1-prototype"} +*/ + + Msg += 25; + + playback = getJSONValue(Msg, "\"output_devices\""); + capture = getJSONValue(Msg, "\"input_devices\""); + + if (TNC->CaptureDevices) + free(TNC->CaptureDevices); + + TNC->CaptureDevices = _strdup(capture); + + if (TNC->PlaybackDevices) + free(TNC->PlaybackDevices); + + TNC->PlaybackDevices = _strdup(playback); + + ptr = getJSONValue(Msg, "\"daemon_state\""); + + if (strstr(ptr, "stopped")) + { + TNC->FreeDataInfo->TNCRunning = 0; + // if we have Capture and Playback devices, then start the TNC, else look in message for them + + if (TNC->CaptureDevices) + { + if (TNC->FreeDataInfo->startingTNC == 0) + { + // Find Capture and Playback indices + + struct FreeDataINFO * Info = TNC->FreeDataInfo; + char * devptr = stristr(TNC->CaptureDevices, Info->Capture); + int capindex = -1, playindex = -1; + char startTNC[] = "{\"type\":\"set\",\"command\":\"start_tnc\"," + "\"parameter\":" + "[{\"mycall\":\"%s\"," + "\"mygrid\":\"%s\"," + "\"rx_audio\":\"%d\"," + "\"tx_audio\":\"%d\"," + "\"radiocontrol\":\"disabled\"," + "\"devicename\":\"RIG_MODEL_NETRIGCTL\"," + "\"deviceport\":\"COM99\"," + "\"pttprotocol\":\"USB\"," + "\"pttport\":\"COM99\"," + "\"serialspeed\":\"19200\"," + "\"data_bits\":\"8\"," + "\"stop_bits\":\"1\"," + "\"handshake\":\"None\"," + "\"rigctld_ip\":\"%s\"," + "\"rigctld_port\":\"%d\"," + "\"enable_scatter\":\"False\"," + "\"enable_fft\":\"False\"," + "\"enable_fsk\":\"False\"," + "\"low_bandwidth_mode\":\"%s\"," //False + "\"tuning_range_fmin\":\"%3.1f\"," //-50.0 + "\"tuning_range_fmax\":\"%3.1f\"," // 50.0 + "\"tx_audio_level\":\"125\"," + "\"respond_to_cq\":\"True\"," + "\"rx_buffer_size\":\"16\"}]}\n"; + + + char Command[2048]; + int Len; + + if (devptr) + { + while (*(--devptr) != '{'); // find start of subparam + capindex = atoi(&devptr[6]); + } + + devptr = stristr(TNC->PlaybackDevices, Info->Playback); + + if (devptr) + { + while (*(--devptr) != '{'); // find start of subparam + playindex = atoi(&devptr[6]); + } + + if (capindex > -1 && playindex > -1) + { + Len = sprintf(Command, startTNC, TNC->FreeDataInfo->ourCall, LOC, capindex, playindex, + TNC->FreeDataInfo->hamlibHost, TNC->FreeDataInfo->hamlibPort, + TNC->FreeDataInfo->LimitBandWidth ? "True" : "False", + TNC->FreeDataInfo->TuningRange * -1.0, + TNC->FreeDataInfo->TuningRange * 1.0); + + send(TNC->TCPSock, Command, Len, 0); +// TNC->FreeDataInfo->startingTNC = 5; + } + } + } + else + { + ptr = getJSONValue(Msg, "\"version\""); + + } + return; + } + TNC->FreeDataInfo->TNCRunning = 1; + + if (TNC->TNCCONNECTED == FALSE && TNC->TNCCONNECTING == FALSE) + ConnectTNCPort(TNC); + + return; + + } + + Debugprintf(Msg); +} + +void StopTNC(struct TNCINFO * TNC) +{ + char stopTNC[] = "{\"type\" : \"set\", \"command\": \"stop_tnc\" , \"parameter\": \"---\"}"; + + if (TNC->TNCCONNECTED) + { + send(TNC->TCPSock, stopTNC, strlen(stopTNC), 0); + closesocket(TNC->TCPDataSock); + } +} + + +void ProcessTNCJSON(struct TNCINFO * TNC, char * Msg, int Len) +{ + char * ptr; + + if (memcmp(Msg, "{\"ptt\":", 7) == 0) + { + if (strstr(Msg, "True")) + { +// TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + + if (TNC->PTTMode) + Rig_PTT(TNC, TRUE); + } + else + { + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); + } + return; + } + + if (memcmp(Msg, "{\"command_response\"", 19) == 0) + { + Debugprintf("%d %s", TNC->Port, Msg); + return; + } + + + if (memcmp(Msg, "{\"command\":\"tnc_state\"", 22) == 0) + { +/* +{"command":"tnc_state","ptt_state":"False","tnc_state":"IDLE","arq_state":"False","arq_session":"False", +"arq_session_state":"disconnected","audio_rms":"0","snr":"0","frequency":"None","speed_level":"1", +"mode":"None","bandwidth":"None","fft":"[0]","channel_busy":"False","scatter":[],"rx_buffer_length":"0", +"rx_msg_buffer_length":"0","arq_bytes_per_minute":"0","arq_bytes_per_minute_burst":"0","arq_compression_factor":"0", +"arq_transmission_percent":"0","total_bytes":"0","beacon_state":"False", +"stations":[],"mycallsign":"GM8BPQ-6","dxcallsign":"AA0AA","dxgrid":""} +*/ + char * LOC = 0; + char * Stations; + char * myCall = 0; + char * farCall = 0; + double snr; + int arqstate = 0; + int rx_buffer_length = 0; + int rx_msg_buffer_length = 0; + + Msg += 23; + + ptr = strstr(Msg, "rx_buffer_length"); + + if (ptr) + rx_buffer_length = atoi(&ptr[19]); + + ptr = strstr(Msg, "rx_msg_buffer_length"); + + if (ptr) + rx_msg_buffer_length = atoi(&ptr[23]); + + ptr = strstr(Msg, "snr"); + + if (ptr) + snr = atof(ptr + 6); + + Stations = getJSONValue(Msg, "\"stations\""); + + if (Stations) + { + ptr = Stations + strlen(Stations) + 1; + LOC = getJSONValue(ptr, "\"dxgrid\""); + farCall = getJSONValue(ptr, "\"dxcallsign\""); + myCall = getJSONValue(ptr, "\"mycallsign\""); + + if (myCall && farCall) + { + myCall++; + strlop(myCall, '"'); + farCall++; + strlop(farCall, '"'); + } + } + + // Look for changes in arq_session_state + + ptr = strstr(Msg, "\"arq_session_state\""); + + if (ptr) + { + struct STREAMINFO * STREAM = &TNC->Streams[0]; + ptr += 21; + + if (memcmp(ptr, "disconnected", 10) == 0) + { + if (TNC->FreeDataInfo->arqstate != 1) + { + TNC->FreeDataInfo->arqstate = 1; + Debugprintf("%d arq_session_state %s", TNC->Port, "disconnected"); + } + + // if connected this is a new disconnect + + if (STREAM->Connected) + { + // 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->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + STREAM->Disconnecting = FALSE; + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + } + } + else if (memcmp(ptr, "connecting", 10) == 0) + { + if (TNC->FreeDataInfo->arqstate != 2) + { + TNC->FreeDataInfo->arqstate = 2; + Debugprintf("%d arq_session_state %s", TNC->Port, "connecting"); + } + } + else if (memcmp(ptr, "connected", 9) == 0) + { + // if connection is idle this is an incoming connect + + if (TNC->FreeDataInfo->arqstate != 3) + { + TNC->FreeDataInfo->arqstate = 3; + Debugprintf("%d arq_session_state %s", TNC->Port, "connected"); + } + + if (STREAM->Connecting == FALSE && STREAM->Connected == FALSE) + { + FreeDataProcessNewConnect(TNC, farCall, myCall); + } + + // if connecting it is a connect ack + + else if (STREAM->Connecting) + { + FreeDataProcessConnectAck(TNC, farCall, Msg, Len); + } + } + + else if (memcmp(ptr, "disconnecting", 12) == 0) + { + if (TNC->FreeDataInfo->arqstate != 4) + { + TNC->FreeDataInfo->arqstate = 4; + Debugprintf("%d arq_session_state %s", TNC->Port, "disconnecting"); + } + } + else if (memcmp(ptr, "failed", 5) == 0) + { + PMSGWITHLEN buffptr; + + if (TNC->FreeDataInfo->arqstate != 5) + { + TNC->FreeDataInfo->arqstate = 5; + Debugprintf("%d arq_session_state %s", TNC->Port, "failed"); + } + + if (STREAM->Connecting) + { + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->Connecting = FALSE; + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "*** Connect Failed\r"); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->FramesQueued++; + } + } + } + + if (rx_buffer_length || rx_msg_buffer_length) + FreeGetData(TNC); + + ptr = getJSONValue(Msg, "\"info\""); + + if (ptr == NULL) + return; + + if (strcmp(ptr, "[]") != 0) + { + + processJSONINFO(TNC, ptr, farCall, snr); + Debugprintf("%d %s %s", TNC->Port, ptr, farCall); + } + + return; + } + + if (memcmp(Msg, "{\"freedata\":\"tnc-message\"", 25) == 0) + { + char * mycall = strstr(Msg, "mycall"); + char * dxcall = strstr(Msg, "dxcall"); + char * dxgrid = strstr(Msg, "dxgrid"); + char * snrptr = strstr(Msg, "snr"); + float snr = 0; + char CQ[64]; + int Len; + + Msg += 26; + + if (mycall && dxcall && dxgrid) + { + mycall += 13; + strlop(mycall, '"'); + + dxcall += 13; + strlop(dxcall, '"'); + + dxgrid += 9; + strlop(dxgrid, '"'); + } + + + if (dxcall && strstr(dxcall, "-0")) + strlop(dxcall, '-'); + + if (snrptr) + snr = atof(&snrptr[6]); + + if (memcmp(Msg, "\"beacon\":\"received\"", 18) == 0) + { + if (mycall && dxcall && dxgrid) + { + Len = sprintf(CQ, "Beacon received from %s SNR %3.1f", dxcall, snr); + WritetoTrace(TNC, CQ, Len); + + // Add to MH + + if (dxcall) + UpdateMH(TNC, dxcall, '!', 'I'); + + return; + } + } + + if (memcmp(Msg, "\"cq\":\"received\"", 14) == 0) + { + if (mycall && dxcall && dxgrid) + { + Len = sprintf(CQ, "CQ received from %s", dxcall); + WritetoTrace(TNC, CQ, Len); + + // Add to MH + + if (dxcall) + UpdateMH(TNC, dxcall, '!', 'I'); + + return; + } + } + + if (memcmp(Msg, "\"ping\":\"received\"", 16) == 0) + { + if (mycall && dxcall && dxgrid) + { + Len = sprintf(CQ, "PING received from %s SNR %3.1f", dxcall, snr); + WritetoTrace(TNC, CQ, Len); + + // Add to MH + + if (dxcall) + UpdateMH(TNC, dxcall, '!', 'I'); + + return; + } + } + + if (memcmp(Msg, "\"ping\":\"acknowledge\"", 16) == 0) + { + if (mycall && dxcall && dxgrid) + { + char Msg[128]; + + Len = sprintf(Msg, "Ping Response from %s SNR %3.1f\r", dxcall, snr); + FreeDataProcessTNCMessage(TNC, dxcall, Msg, Len); + + UpdateMH(TNC, dxcall, '!', 'I'); + + return; + } + } + + + + + + + Debugprintf("%d %s", TNC->Port, Msg); + return; + } + + if (memcmp(Msg, "{\"command\":\"rx_buffer\"", 22) == 0) + { + char * Next, * This; + + // Delete from TNC + + SendTNCCommand(TNC, "set", "del_rx_buffer"); Msg += 22; + + ptr = getJSONValue(Msg, "\"eof\""); + ptr = getJSONValue(Msg, "\"data-array\""); + + This = ptr; + + if (This[1] == '{') // Array of objects + { + This++; + do + { + Next = getObjectFromArray(This); + ProcessMessageObject(TNC, This); + This = Next; + + } while (Next && Next[0] == '{'); + } + + return; + } + + Debugprintf("%d %s", TNC->Port, Msg); + + +// {"COMMAND":"RX_BUFFER","DATA-ARRAY":[],"EOF":"EOF"} +/* {"COMMAND":"RX_BUFFER","DATA-ARRAY":[{"DXCALLSIGN":"GM8BPQ","DXGRID":"","TIMESTAMP":1642579504, +"RXDATA":[{"dt":"f","fn":"main.js","ft":"text\/javascript" +,"d":"data:text\/javascript;base64,Y29uc3Qge.....9KTsK","crc":"123123123"}]}],"EOF":"EOF"} + + + +{"arq":"received","uuid":"a1346319-6eb0-42aa-b5a0-c9493c8ccdca","timestamp":1645812393,"dxcallsign":"G8BPQ-2","dxgrid":"","data":"QyBHOEJQUS0yIEc4QlBRLTIgRzhCUFEtMiA="} +{"ptt":"True"} + + +*/ + if (memcmp(Msg, "{\"arq\":\"received\"", 17) == 0) + { + int NewLen; + char * ptr, *ptr2, *Type; + char * Call = 0; + char * myCall = 0; + + Msg += 17; + + ptr = getJSONValue(Msg, "\"data\""); + Type = ++ptr; + + // Decode Base64 + + // FreeData replaces / with \/ so need to undo + + ptr2 = strstr(Type, "\\/"); + + while (ptr2) + { + memmove(ptr2, ptr2 + 1, strlen(ptr2)); + ptr2 = strstr(ptr2, "\\/"); + } + + Len = strlen(Type) - 1; + + // Debugprintf("RX %d %s %d", TNC->Port, FN, Len); + + ptr = ptr2 = Type; + + while (Len > 0) + { + xdecodeblock(ptr, ptr2); + ptr += 4; + ptr2 += 3; + Len -= 4; + } + + NewLen = (int)(ptr2 - Type); + + if (*(ptr-1) == '=') + NewLen--; + + if (*(ptr-2) == '=') + NewLen--; + + Type[NewLen] = 0; + + myCall = getJSONValue(Msg, "\"mycallsign\""); + Call = getJSONValue(Msg, "\"dxcallsign\""); + + if (Call) + { + Call++; + strlop(Call, '"'); + } + + if (myCall) + { + myCall++; + strlop(myCall, '"'); + } + + + FreeDataProcessTNCMessage(TNC, Call, Type, NewLen); + + return; + } + + if (memcmp(Msg, "{\"COMMAND\":\"RX_MSG_BUFFER\"", 26) == 0) + { + char * Next, * This; + + Msg += 26; + ptr = getJSONValue(Msg, "\"EOF\""); + ptr = getJSONValue(Msg, "\"DATA-ARRAY\""); + + This = ptr; + + if (This[1] == '{') // Array of objects + { + This++; + do { + Next = getObjectFromArray(This); + ProcessMessageObject(TNC, This); + This = Next; + } while (Next && Next[0] == '{'); + + // Delete from TNC + + SendTNCCommand(TNC, "SET", "DEL_RX_MSG_BUFFER"); + } + + + return; + } +} + +int FreeDataConnect(struct TNCINFO * TNC, char * Call) +{ + char Connect[] = "{\"type\" : \"arq\", \"command\": \"connect\" , \"dxcallsign\": \"%s\"}\n"; + char Msg[128]; + int Len; + + Len = sprintf(Msg, Connect, Call); + + return send(TNC->TCPDataSock, Msg, Len, 0); +} + +int FreeDataDisconnect(struct TNCINFO * TNC) +{ + char Disconnect[] = "{\"type\" : \"arq\", \"command\": \"disconnect\"}\n"; + char Msg[128]; + int Len; + +// return FreeDataSendCommand(TNC, "D"); + + Len = sprintf(Msg, Disconnect); + + return send(TNC->TCPDataSock, Msg, Len, 0); +} + + +int FreeGetData(struct TNCINFO * TNC) +{ + char GetData[] = "{\"type\" : \"get\", \"command\": \"rx_buffer\"}\n"; + char Msg[128]; + int Len; + + Len = sprintf(Msg, GetData); + + return send(TNC->TCPDataSock, Msg, Len, 0); +} + +int FreeDataSendCommand(struct TNCINFO * TNC, char * Msg) +{ + // Commands are simulated as Messages to the remote BPQ. The TNC itself does not handle any commands + + // First Byte of MSG is a Type - Command or Data. MSG has a limited character set Use =xx for Now. + + // Current Types - C = Connect, D = Disconnect, I = info + + SendAsRaw(TNC, TNC->FreeDataInfo->farCall, TNC->FreeDataInfo->ourCall, Msg, strlen(Msg)); + return 0; +} + +void FreeDataProcessDaemonMsg(struct TNCINFO * TNC) +{ + int InputLen, MsgLen; + char * ptr; + int OpenBraces; + int CloseBraces; + char c; + + + // 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], 8191 - TNC->InputLen, 0); + + if (InputLen == 8191) + c = 0; + + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + // Does this mean closed? + + closesocket(TNC->TCPSock); + + TNC->TCPSock = 0; + + TNC->DAEMONCONNECTED = FALSE; + TNC->Streams[0].ReportDISC = TRUE; + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + return; + } + + TNC->InputLen += InputLen; + +loop: + TNC->ARDOPBuffer[TNC->InputLen] = 0; // So we cat use string functions + + // Message should be json. We know the format, so don't need a general parser, but need to know if complete. + // I think counting { and } and stopping if equal should work; + + + if (TNC->ARDOPBuffer[0] != '{') + { + TNC->InputLen = 0; + return; + } + + ptr = &TNC->ARDOPBuffer[0]; + + OpenBraces = 0; + CloseBraces = 0; + + while (c = *(ptr++)) + { + if (c == '{') + OpenBraces ++; + else if (c == '}') + CloseBraces ++; + + if (OpenBraces == CloseBraces) + { + MsgLen = ptr - (char * )TNC->ARDOPBuffer; + + ProcessDAEMONJSON(TNC, TNC->ARDOPBuffer, MsgLen); + + if (TNC->InputLen == 0 || *ptr == 0) + { + TNC->InputLen = 0; + return; + } + + // More in buffer + + memmove(TNC->ARDOPBuffer, ptr, TNC->InputLen - MsgLen); + + TNC->InputLen -= MsgLen; + goto loop; + } + + } + // Message Incomplete - wait for rest; +} + + + + + + +void FreeDataProcessTNCMsg(struct TNCINFO * TNC) +{ + int DataInputLen, MsgLen; + char * ptr, * endptr; + int maxlen; + + // May get message split over packets or multiple messages per packet + + // A complete file transfer arrives as one message, so can bw very long + + + if (TNC->DataInputLen > MAXRXSIZE) // Shouldnt have packets longer than this + TNC->DataInputLen=0; + + maxlen = MAXRXSIZE - TNC->DataInputLen; + + if (maxlen >1400) + maxlen = 1400; + + DataInputLen = recv(TNC->TCPDataSock, &TNC->ARDOPDataBuffer[TNC->DataInputLen], maxlen, 0); + + if (DataInputLen == 0 || DataInputLen == SOCKET_ERROR) + { + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + closesocket(TNC->TCPDataSock); + + TNC->TCPDataSock = 0; + + TNC->TNCCONNECTED = FALSE; + + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + STREAM->Disconnecting = FALSE; + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + return; + } + + TNC->DataInputLen += DataInputLen; + + TNC->ARDOPDataBuffer[TNC->DataInputLen] = 0; // So we can use string functions + + // Message should be json. We know the format, so don't need a general parser, but need to know if complete. + // I think counting { and } and stopping if equal should work; + +// Debugprintf(TNC->ARDOPDataBuffer); + + //I think now messages end with LF + +loop: + + endptr = strchr(TNC->ARDOPDataBuffer, 10); + + if (endptr == 0) + return; + + *(endptr) = 0; + + if (TNC->ARDOPDataBuffer[0] != '{') + { + TNC->DataInputLen = 0; + return; + } + + ptr = &TNC->ARDOPDataBuffer[0]; + + MsgLen = endptr - ptr; + + ProcessTNCJSON(TNC, ptr, MsgLen); + + // MsgLen doesnt include lf + + MsgLen++; + + if (TNC->DataInputLen == MsgLen) + { + TNC->DataInputLen = 0; + return; + } + + // More in buffer + + ptr += MsgLen; + TNC->DataInputLen -= MsgLen; + + memmove(TNC->ARDOPDataBuffer, ptr, TNC->DataInputLen + 1); + + goto loop; + + // Message Incomplete - wait for rest; +} + + + +VOID FreeDataThread(void * portptr); + +int ConnecttoFreeData(int port) +{ + _beginthread(FreeDataThread, 0, (void *)(size_t)port); + + return 0; +} + +static SOCKADDR_IN sinx; +static SOCKADDR_IN rxaddr; + + +VOID FreeDataThread(void * portptr) +{ + // FreeData TNC has two sessions, not sure why! + + // Messages are JSON encapulated + // Opens deamon socket. TNC socket is only available once Start TNC command sent + // Looks for data on socket(s) + + 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; + + if (TNC->HostName == NULL) + return; + + TNC->BusyFlags = 0; + + TNC->DAEMONCONNECTING = TRUE; + + Sleep(3000); // Allow init to complete + +// printf("Starting FreeDATA 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 Daemon"); + 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->DAEMONCONNECTING = FALSE; + sprintf(Msg, "Resolve Failed for FreeData Host - 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->TCPDataSock); +// closesocket(TNC->TCPSock); + + TNC->TCPDataSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPDataSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for FreeData TNC socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->DAEMONCONNECTING = FALSE; + return; + } + + TNC->TCPSock = socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for FreeData Data Daemon socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->DAEMONCONNECTING = FALSE; + closesocket(TNC->TCPDataSock); + + return; + } + + setsockopt(TNC->TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + setsockopt(TNC->TCPSock, 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; + + // Connect Daemon Port + + if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->Datadestaddr,sizeof(TNC->Datadestaddr)) == 0) + { + // + // Connected successful + // + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for FreeData Daemon socket - error code = %d\r\n", err); + WritetoConsole(Msg); + sprintf(TNC->WEB_COMMSSTATE, "Connection to Daemon failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + TNC->Alerted = TRUE; + } + + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + TNC->DAEMONCONNECTING = FALSE; + + RestartTNC(TNC); + return; + } + + Sleep(1000); + + TNC->LastFreq = 0; + + TNC->DAEMONCONNECTING = FALSE; + TNC->DAEMONCONNECTED = TRUE; + TNC->BusyFlags = 0; + TNC->InputLen = 0; + TNC->Alerted = FALSE; + + TNC->Alerted = TRUE; + + sprintf(TNC->WEB_COMMSSTATE, "Connected to FreeData Daemon"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + sprintf(Msg, "Connected to FreeData Daemon 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->DAEMONCONNECTED || TNC->TNCCONNECTED) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + if (TNC->DAEMONCONNECTED) + FD_SET(TNC->TCPSock,&readfs); + + if (TNC->TCPSock) + FD_SET(TNC->TCPSock,&errorfs); + + if (TNC->TNCCONNECTED) + FD_SET(TNC->TCPDataSock,&readfs); + +// FD_ZERO(&writefs); + +// if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->TCPSock,&writefs); // Need notification of busy clearing + + if (TNC->TNCCONNECTING || TNC->TNCCONNECTED) 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->TCPSock + 1, &readfs, NULL, &errorfs, &timeout); + + if (ret == SOCKET_ERROR) + { + Debugprintf("FreeData Select failed %d ", WSAGetLastError()); + goto Lost; + } + if (ret > 0) + { + // See what happened + + if (FD_ISSET(TNC->TCPDataSock, &readfs)) + { + GetSemaphore(&Semaphore, 52); + FreeDataProcessTNCMsg(TNC); + FreeSemaphore(&Semaphore); + } + + if (FD_ISSET(TNC->TCPSock, &readfs)) + { + GetSemaphore(&Semaphore, 52); + FreeDataProcessDaemonMsg(TNC); + FreeSemaphore(&Semaphore); + } + + if (FD_ISSET(TNC->TCPDataSock, &errorfs)) + { +Lost: + sprintf(Msg, "FreeData Daemon Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to Daemon lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->TNCCONNECTED = 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->TCPDataSock); + TNC->TCPDataSock = 0; + } + + if (FD_ISSET(TNC->TCPSock, &errorfs)) + { + sprintf(Msg, "FreeData Daemon Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to Daemon lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->DAEMONCONNECTED = 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); + TNC->TCPSock = 0; + } + continue; + } + else + { + } + } + + if (TNC->TCPDataSock) + { + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPDataSock); + } + + if (TNC->TCPSock) + { + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPSock); + } + + sprintf(Msg, "FreeData Thread Terminated Port %d\r\n", TNC->Port); + WritetoConsole(Msg); +} + +void ConnectTNCPort(struct TNCINFO * TNC) +{ + char Msg[255]; + int err; + int bcopt = TRUE; + + TNC->TNCCONNECTING = TRUE; + + TNC->TCPDataSock = socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPDataSock == INVALID_SOCKET) + { + sprintf(Msg, "Socket Failed for FreeData TNC socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->TNCCONNECTING = FALSE; + return; + } + + setsockopt(TNC->TCPDataSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + setsockopt(TNC->TCPDataSock, IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + if (connect(TNC->TCPDataSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) + { + // Connected successful + + sprintf(TNC->WEB_COMMSSTATE, "Connected to FreeData TNC"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + TNC->TNCCONNECTING = FALSE; + TNC->TNCCONNECTED = TRUE; + TNC->Alerted = FALSE; + return; + } + + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + sprintf(Msg, "Connect Failed for FreeData TNC socket - error code = %d Port %d\n", + err, htons(TNC->destaddr.sin_port)); + + WritetoConsole(Msg); + TNC->Alerted = TRUE; + TNC->TNCCONNECTING = FALSE; + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + } + + closesocket(TNC->TCPDataSock); + + TNC->TCPDataSock = 0; + TNC->TNCCONNECTING = FALSE; + return; +} + diff --git a/FreeDATA-HPLaptop-2.c b/FreeDATA-HPLaptop-2.c new file mode 100644 index 0000000..abf9b7c --- /dev/null +++ b/FreeDATA-HPLaptop-2.c @@ -0,0 +1,4654 @@ +/* +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 +*/ + +// +// Interface to allow G8BPQ switch to use FreeData TNC + + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + +#ifndef WIN32 +#ifndef MACBPQ +#include +#endif +#endif + +#include "CHeaders.h" +#include "bpq32.h" +#include "tncinfo.h" + +#define SD_BOTH 0x02 + +#define FREEDATABUFLEN 16384 // TCP buffer size + +int KillTNC(struct TNCINFO * TNC); +static int RestartTNC(struct TNCINFO * TNC); + +int (WINAPI FAR *GetModuleFileNameExPtr)(); +extern int (WINAPI FAR *EnumProcessesPtr)(); +static int Socket_Data(int sock, int error, int eventcode); +VOID MoveWindows(struct TNCINFO * TNC); +static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); +VOID SendInitScript(struct TNCINFO * TNC); +int ProcessEscape(UCHAR * TXMsg); +static void SendPoll(struct TNCINFO * TNC); +void SendMode(struct TNCINFO * TNC); +static int ConnecttoFreeData(int port); +void ConnectTNCPort(struct TNCINFO * TNC); +int FreeDataSendCommand(struct TNCINFO * TNC, char * data); +static void SendPing(struct TNCINFO * TNC, char * Call); +static void SendCQ(struct TNCINFO * TNC); +char * stristr (char *ch1, char *ch2); +int zEncode(unsigned char * in, unsigned char * out, int len, unsigned char * Banned); +static void SendDataMsg(struct TNCINFO * TNC, char * Call, char * Msg, int Len); +static int SendAsRaw(struct TNCINFO * TNC, char * Call, char * myCall, char * Msg, int Len); +static int SendAsFile(struct TNCINFO * TNC, char * Call, char * Msg, int Len); +char * byte_base64_encode(char *str, int len); +void xdecodeblock( unsigned char in[4], unsigned char out[3] ); +void FlushData(struct TNCINFO * TNC); +void CountRestarts(struct TNCINFO * TNC); +void StopTNC(struct TNCINFO * TNC); +int FreeDataConnect(struct TNCINFO * TNC, char * Call); +int FreeDataDisconnect(struct TNCINFO * TNC); +int FreeGetData(struct TNCINFO * TNC); +static void SendBeacon(struct TNCINFO * TNC, int Interval); +void buildParamString(struct TNCINFO * TNC, char * line); +VOID FreeDataSuspendPort(struct TNCINFO * TNC); +VOID FreeDataReleasePort(struct TNCINFO * TNC); + + +static char ClassName[]="FREEDATASTATUS"; +static char WindowTitle[] = "FreeData Modem"; +static int RigControlRow = 205; + +#ifndef WIN32 +#include +#define MAXPNAMELEN 32 +#else +#include +#endif + +extern char * PortConfig[33]; +extern int SemHeldByAPI; + +static RECT Rect; + +extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + +static int ProcessLine(char * buf, int Port); + +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + +#define MAXRXSIZE 512000 // Sets max size for file transfer (less base64 overhead + +int CaptureCount = 0; +int PlaybackCount = 0; + +int CaptureIndex = -1; // Card number +int PlayBackIndex = -1; + + + +char CaptureNames[16][MAXPNAMELEN + 2] = { "" }; +char PlaybackNames[16][MAXPNAMELEN + 2] = { "" }; + + +#ifdef WIN32 + +#include + +#pragma comment(lib, "winmm.lib") + +WAVEFORMATEX wfx = { WAVE_FORMAT_PCM, 1, 12000, 24000, 2, 16, 0 }; + +WAVEOUTCAPS pwoc; +WAVEINCAPS pwic; + + +char * CaptureDevices = NULL; +char * PlaybackDevices = NULL; + +HWAVEOUT hWaveOut = 0; +HWAVEIN hWaveIn = 0; + +#endif + + +char * gen_uuid() +{ + char v[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + int i; + + //3fb17ebc-bc38-4939-bc8b-74f2443281d4 + //8 dash 4 dash 4 dash 4 dash 12 + + static char buf[37] = {0}; + + //gen random for all spaces because lazy + for (i = 0; i < 36; ++i) + { + buf[i] = v[rand()%16]; + } + + //put dashes in place + buf[8] = '-'; + buf[13] = '-'; + buf[18] = '-'; + buf[23] = '-'; + + //needs end byte + buf[36] = '\0'; + + return buf; +} + +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 = TNCInfo[Port]; + 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->FreeDataInfo = zalloc(sizeof(struct FreeDataINFO)); + +// TNC->FreeDataInfo->useBaseCall = 1; // Default + + 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->TCPPort = WINMORport; + + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(WINMORport); + + 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); + } + } + + // 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; + } + + if (_memicmp(buf, "CAPTURE", 7) == 0) + { + TNC->FreeDataInfo->Capture = _strupr(_strdup(&buf[8])); + strlop(TNC->FreeDataInfo->Capture, 13); + } + else if (_memicmp(buf, "PLAYBACK", 8) == 0) + { + TNC->FreeDataInfo->Playback = _strupr(_strdup(&buf[9])); + strlop(TNC->FreeDataInfo->Playback, 13); + } + + else if (_memicmp(buf, "LOGDIR ", 7) == 0) + TNC->LogPath = _strdup(&buf[7]); + + else if (_memicmp(buf, "HAMLIBHOST", 10) == 0) + { + TNC->FreeDataInfo->hamlibHost = _strdup(&buf[11]); + strlop(TNC->FreeDataInfo->hamlibHost, 13); + } + + else if (_memicmp(buf, "TuningRange", 11) == 0) + TNC->FreeDataInfo->TuningRange = atoi(&buf[12]); + + else if (_memicmp(buf, "TXLevel", 6) == 0) + TNC->FreeDataInfo->TXLevel = atoi(&buf[7]); + + else if (_memicmp(buf, "Explorer", 8) == 0) + TNC->FreeDataInfo->Explorer = atoi(&buf[9]); + + + else if (_memicmp(buf, "LimitBandWidth", 14) == 0) + TNC->FreeDataInfo->LimitBandWidth = atoi(&buf[15]); + + else if (_memicmp(buf, "HAMLIBPORT", 10) == 0) + TNC->FreeDataInfo->hamlibPort = atoi(&buf[11]); + + else if (_memicmp(buf, "USEBASECALL", 11) == 0) + TNC->FreeDataInfo->useBaseCall = atoi(&buf[12]); + + else if (_memicmp(buf, "RXDIRECTORY", 11) == 0) + { + TNC->FreeDataInfo->RXDir = _strdup(&buf[12]); + strlop(TNC->FreeDataInfo->RXDir, 13); + } + + else if (standardParams(TNC, buf) == FALSE) + strcat(TNC->InitScript, buf); + + } + + return (TRUE); +} + +char * Config; +static char * ptr1, * ptr2; + +int FreeDataGetLine(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; +} + +BOOL FreeDataReadConfigFile(int Port, int ProcLine()) +{ + char buf[256],errbuf[256]; + + Config = PortConfig[Port]; + + if (Config) + { + // Using config from bpq32.cfg + + if (strlen(Config) == 0) + { + return TRUE; + } + + ptr1 = Config; + ptr2 = strchr(ptr1, 13); + + 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); +} + + + +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 VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen) +{ + if (TNC->hDevice) + { + // FreeData mode. Queue to Hostmode driver + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = EncLen; + memcpy(&buffptr->Data[0], Encoded, EncLen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + TNC->Streams[Stream].FramesQueued++; + + return; + } +} + + +VOID FreeDataChangeMYC(struct TNCINFO * TNC, char * Call) +{ + UCHAR TXMsg[100]; + int datalen; + + if (strcmp(Call, TNC->CurrentMYC) == 0) + return; // No Change + + strcpy(TNC->CurrentMYC, Call); + + datalen = sprintf(TXMsg, "MYCALL %s\r", Call); +// FreeDataSendCommand(TNC, TXMsg); +} + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + int datalen; + PMSGWITHLEN buffptr; +// char txbuff[500]; + unsigned int txlen = 0; + UCHAR * TXMsg; + + size_t Param; + int Stream = 0; + 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 + + switch (fn) + { + case 7: + + // 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances + + SendPoll(TNC); + + // Check for buffered data to send + + if (TNC->FreeDataInfo->toSendTimeout) + { + TNC->FreeDataInfo->toSendTimeout--; + if (TNC->FreeDataInfo->toSendTimeout <= 0) + FlushData(TNC); + } + + return 0; + + case 1: // poll + +// FreeDataCheckRX(TNC); + + if (TNC->TNCCONNECTED == FALSE && TNC->TNCCONNECTING == FALSE) + { + // See if time to reconnect + + time(<ime); + if (ltime - TNC->lasttime > 9 ) + { + TNC->lasttime = ltime; + ConnecttoFreeData(port); + } + } + + + while (TNC->PortRecord->UI_Q) + { + int datalen; + char * Buffer; + char FECMsg[512]; + char Call[12] = " "; + struct _MESSAGE * buffptr; + int CallLen; + char * ptr = FECMsg; + + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + +/* if (TNC->CONNECTED == 0 || + TNC->Streams[0].Connecting || + TNC->Streams[0].Connected) + { + // discard if TNC not connected or sesison active + + ReleaseBuffer(buffptr); + continue; + } +*/ + datalen = buffptr->LENGTH - MSGHDDRLEN; + Buffer = &buffptr->DEST[0]; // Raw Frame + Buffer[datalen] = 0; + + // Frame has ax.25 format header. Convert to Text + + CallLen = ConvFromAX25(Buffer + 7, Call); // Origin + memcpy(ptr, Call, CallLen); + ptr += CallLen; + + *ptr++ = '!'; + + CallLen = ConvFromAX25(Buffer, Call); // Dest + memcpy(ptr, Call, CallLen); + ptr += CallLen; + + Buffer += 14; // TO Digis + datalen -= 14; + + while ((Buffer[-1] & 1) == 0) + { + *ptr++ = ','; + CallLen = ConvFromAX25(Buffer, Call); + memcpy(ptr, Call, CallLen); + ptr += CallLen; + Buffer += 7; // End of addr + datalen -= 7; + } + + *ptr++ = '_'; + *ptr++ = 'U'; // UI Frame + *ptr++ = 0; // delimit calls + + if (Buffer[0] == 3) // UI + { + Buffer += 2; + datalen -= 2; + } + +// FreeDataSendSingleData(TNC, FECMsg, Buffer, datalen); + + ReleaseBuffer(buffptr); + } + + + if (TNC->DiscPending) + { + TNC->DiscPending--; + + if (TNC->DiscPending == 0) + { + // Too long in Disc Pending - Kill and Restart TNC + } + } + + + for (Stream = 0; Stream <= 2; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + FreeDataDisconnect(TNC); + strcpy(TNC->WEB_TNCSTATE, "Disconnecting"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + } + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && STREAM->Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + Debugprintf("FreeData New Attach Stream %d", Stream); + + STREAM->Attached = TRUE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, STREAM->MyCall); + TNC->Streams[Stream].MyCall[calllen] = 0; + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + // Stop Listening, and set MYCALL to user's call + + FreeDataSuspendPort(TNC); + FreeDataChangeMYC(TNC, TNC->Streams[0].MyCall); + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command(-1, Msg); + } + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + } + + // See if any frames for this port + + for (Stream = 0; Stream <= 2; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->BPQtoPACTOR_Q) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)Q_REM(&STREAM->BPQtoPACTOR_Q); + UCHAR * data = &buffptr->Data[0]; + STREAM->FramesQueued--; + txlen = (int)buffptr->Len; + + SendAsFile(TNC, TNC->FreeDataInfo->farCall, data, txlen); + } + + if (STREAM->PACTORtoBPQ_Q != 0) + { + buffptr = (PMSGWITHLEN)Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = Stream; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + + if (STREAM->ReportDISC) // May need a delay so treat as a counter + { + STREAM->ReportDISC--; + if (STREAM->ReportDISC == 0) + { + buff->PORT = Stream; +// STREAM->Connected = 0; +// STREAM->Attached = 0; + return -1; + } + } + } + return (0); + + case 2: // send + + Stream = buff->PORT; + + if (!TNC->TNCCONNECTED) + { + // Send Error Response + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = 36; + memcpy(&buffptr->Data[0], "No Connection to TNC\r", 36); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return 0; // Don't try if not connected + } + + STREAM = &TNC->Streams[Stream]; + + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; // Discard *** connected + return 0; + } + + txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); // 1 as no PID + TXMsg = &buff->L2DATA[0]; + TXMsg[txlen] = 0; + + if (STREAM->Connected) + { + STREAM->PacketsSent++; + + SendDataMsg(TNC, TNC->FreeDataInfo->farCall, buff->L2DATA, txlen); + return 1; + } + + if (TNC->FreeDataInfo->Chat) + { + // Chat Mode - Send to other end + + char reply[512] = "m"; + char * p; + int Len; + + if (_stricmp(TXMsg, "/ex\r") == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + TNC->FreeDataInfo->Chat = 0; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} Chat with %s ended. \r", TNC->FreeDataInfo->ChatCall); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + } + + // Send as chat message + + //m�;send_message�;123�;64730c5c-d32c-47b4-9b11-c958fd07a185�;hhhhhhhhhhhhhhhhhh + //�;�;plain/text�; + + strlop(TXMsg, 13); + + strcpy(&reply[2], ";send_message"); + strcpy(&reply[16], ";123"); + reply[21] = ';'; + strcpy(&reply[22], gen_uuid()); + sprintf(&reply[59], ";%s\n", TXMsg); + + p = strchr(&reply[59], 0); + + p[1] = ';'; + strcpy(&p[3], ";plain/text"); + p[15] = ';'; + + Len = &p[16] - reply; + + SendAsRaw(TNC, TNC->FreeDataInfo->ChatCall, "", reply, Len); + + return 0; + } + + + + if (_memicmp(&buff->L2DATA[0], "D\r", 2) == 0 || _memicmp(&buff->L2DATA[0], "BYE\r", 4) == 0) + { + STREAM->ReportDISC = TRUE; // Tell Node + TNC->FreeDataInfo->Chat = 0; + return 0; + } + + + // See if Local command (eg RADIO) + + if (_memicmp(&buff->L2DATA[0], "RADIO ", 6) == 0) + { + sprintf(&buff->L2DATA[0], "%d %s", TNC->Port, &buff->L2DATA[6]); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK->CIRCUITINDEX, &buff->L2DATA[0])) + { + } + else + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", &buff->L2DATA[0]); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "OVERRIDEBUSY", 12) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->OverrideBusy = TRUE; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} OK\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + } + + if (_memicmp(&buff->L2DATA[0], "PING ", 5) == 0) + { + char * Call = &buff->L2DATA[5]; + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + strlop(Call, 13); + strlop(Call, ' '); + SendPing(TNC, _strupr(Call)); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} Ping Sent\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + + } + + if (_memicmp(&buff->L2DATA[0], "CQ", 2) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + SendCQ(TNC); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} CQ Sent\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + + } + + + if (_memicmp(&buff->L2DATA[0], "CQ", 2) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + SendCQ(TNC); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} CQ Sent\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + + } + + if (_memicmp(&buff->L2DATA[0], "TXLEVEL ", 8) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + int Level = atoi(&buff->L2DATA[8]); + char TXL[] = "{\"type\" : \"set\", \"command\" : \"tx_audio_level\", \"value\": \"%d\"}\n"; + char Message[256]; + int Len, ret; + + Len = sprintf(Message, TXL, Level); + ret = send(TNC->TCPDataSock, (char *)&Message, Len, 0); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} TXLevel Set\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 0; + } + + if (_memicmp(&buff->L2DATA[0], "SENDTEST", 8) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + char TXF[] = "{\"type\" : \"set\", \"command\" : \"send_test_frame\"}\n"; + char Message[256]; + int Len, ret; + + Len = sprintf(Message, TXF); + ret = send(TNC->TCPDataSock, (char *)&Message, Len, 0); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} Test Frame Requested\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 0; + } + + if (_memicmp(&buff->L2DATA[0], "CHAT ", 5) == 0) + { + char * Call = &buff->L2DATA[5]; + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + strlop(Call, 13); + strlop(Call, ' '); + + TNC->FreeDataInfo->Chat = 1; + memcpy(TNC->FreeDataInfo->ChatCall, _strupr(Call), 10); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} Chat with %s. Enter /ex to exit\r", Call); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + } + + + if (_memicmp(&TXMsg[0], "BEACON ", 7) == 0) + { + int Interval = atoi(&TXMsg[7]); + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + SendBeacon(TNC, Interval); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} Ok\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + } + + + + + // See if a Connect Command. + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + char Connect[80]; + char loppedCall[10]; + char toCall[10]; + + char * ptr = strchr(&buff->L2DATA[2], 13); + + if (ptr) + *ptr = 0; + + // FreeDATA doesn't have the concept of a connection, so need to simulate it between the nodes + _strupr(&buff->L2DATA[2]); + + if (strlen(&buff->L2DATA[2]) > 9) + buff->L2DATA[11] = 0; + + strcpy(TNC->FreeDataInfo->toCall, &buff->L2DATA[2]); + strcpy(loppedCall, TNC->FreeDataInfo->toCall); + if (TNC->FreeDataInfo->useBaseCall) + strlop(loppedCall, '-'); + strcpy(TNC->FreeDataInfo->farCall, loppedCall); + + // MYCALL and Target call are end to end concepts - the TNC cam can only use one call, set at TNC start. and no SSID's + // Messages are sent at TNC level to the tnc call, so we send our tnc call to the other end + + + txlen = sprintf(Connect, "C %s %s %s ", &buff->L2DATA[2], STREAM->MyCall, TNC->FreeDataInfo->ourCall); + + // 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; + + memset(STREAM->RemoteCall, 0, 10); + strcpy(STREAM->RemoteCall, &buff->L2DATA[2]); + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->PacketsSent = 0; + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", STREAM->MyCall, STREAM->RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + +// FreeDataSendCommand(TNC, Connect); + FreeDataConnect(TNC, STREAM->RemoteCall); + STREAM->Connecting = TRUE; + return 0; + + } + + // Normal data. Send to TNC + + // The TNC doesn't have any commands, so send error message + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} Not connected\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + // strlop(buff->L2DATA, 13); + // txlen = SendDataMsg(TNC, TNC->, buff->L2DATA); + // FreeDataSendData(TNC, TXMsg, txlen); + + return 0; + + case 3: + + // CHECK IF OK TO SEND (And check TNC Status) + + Stream = (int)(size_t)buff; + + // FreeData TNC can buffer unlimited data + + if (TNC->Streams[Stream].Attached == 0) + return (TNC->TNCCONNECTED != 0) << 8 | 1; + + return ((TNC->TNCCONNECTED != 0) << 8 | TNC->Streams[Stream].Disconnecting << 15); // OK + + case 5: // Close + + StopTNC(TNC); + + // Drop through + + case 4: // reinit7 + + if (TNC->TCPDataSock) + { + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPDataSock); + } + + 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 FreeDATA"); + return 1; // OK to change + } + + if (Param == 1) // Request Permission + { + if (!TNC->CONNECTED) + return 0; // No connection so no interlock + + if (TNC->ConnectPending == 0 && TNC->PTTState == 0) + { + FreeDataSuspendPort(TNC); + TNC->GavePermission = TRUE; + return 0; // OK to Change + } + + if (TNC->ConnectPending) + TNC->ConnectPending--; // Time out if set too long + + return TRUE; + } + + if (Param == 3) // Release Permission + { + if (TNC->GavePermission) + { + TNC->GavePermission = FALSE; + if (TNC->ARDOPCurrentMode[0] != 'S') // Skip + FreeDataReleasePort(TNC); + } + return 0; + } + + // Param is Address of a struct ScanEntry + + Scan = (struct ScanEntry *)buff; + return 0; + } + return 0; +} + +VOID FreeDataReleaseTNC(struct TNCINFO * TNC) +{ + // Set mycall back to Node or Port Call, and Start Scanner + + UCHAR TXMsg[1000]; + + FreeDataChangeMYC(TNC, TNC->NodeCall); + + // Start Scanner + + sprintf(TXMsg, "%d SCANSTART 15", TNC->Port); + + Rig_Command(-1, TXMsg); + + FreeDataReleasePort(TNC); + ReleaseOtherPorts(TNC); +} +VOID FreeDataSuspendPort(struct TNCINFO * TNC) +{ + char CMD[] = "{\"type\" : \"set\", \"command\" : \"respond_to_cq\", \"value\": \"False\"}\n"; + send(TNC->TCPDataSock, CMD, strlen(CMD), 0); +} + +VOID FreeDataReleasePort(struct TNCINFO * TNC) +{ + char CMD[] = "{\"type\" : \"set\", \"command\" : \"respond_to_cq\", \"value\": \"True\"}\n"; + send(TNC->TCPDataSock, CMD, strlen(CMD), 0); +} + + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, "" + "" + "FreDATA Status" + "

FreeData Status" + "

", + TNC->Port); + + + 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
Proto State%s
Traffic%s
TNC Restarts
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + +#ifndef LINBPQ + +#define BGCOLOUR RGB(236,233,216) +static HBRUSH RedBrush = NULL; +HBRUSH GreenBrush; +HBRUSH BlueBrush; +static HBRUSH bgBrush = NULL; + +extern HWND ClientWnd, FrameWnd; +extern int OffsetH, OffsetW; + +extern HMENU hMainFrameMenu; +extern HMENU hBaseMenu; +extern HANDLE hInstance; + +extern HKEY REGTREE; + + +/* +static LRESULT CALLBACK PacWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + MINMAXINFO * mmi; + PAINTSTRUCT ps; + HDC hdc; + + 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(ps.hdc, RedBrush); + SelectObject(ps.hdc, GreenBrush); +// SelectObject(ps.hdc, GetStockObject(GRAY_BRUSH)); + + EndPaint(hWnd, &ps); + 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")) + { + 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: + + KillTNC(TNC); + break; + + case WINMOR_RESTART: + + 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_DESTROY: + + break; + } + return DefMDIChildProc(hWnd, message, wParam, lParam); +} +*/ + +#endif + + + +VOID * FreeDataExtInit(EXTPORTDATA * PortEntry) +{ + int port; + char Msg[255]; + char * ptr; + struct TNCINFO * TNC; + char * TempScript; + u_long param = 1; + int line; + int i; + + srand(time(NULL)); + + port = PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + + if (TNC->FreeDataInfo->TuningRange == 0) + TNC->FreeDataInfo->TuningRange = 50; + + if (TNC->FreeDataInfo->TXLevel == 0) + TNC->FreeDataInfo->TXLevel = 50; + + if (TNC->AutoStartDelay == 0) + TNC->AutoStartDelay = 2000; + +#ifndef LINBPQ + + if (bgBrush == NULL) + { + bgBrush = CreateSolidBrush(BGCOLOUR); + RedBrush = CreateSolidBrush(RGB(255,0,0)); + GreenBrush = CreateSolidBrush(RGB(0,255,0)); + BlueBrush = CreateSolidBrush(RGB(0,0,255)); + } + +#endif + + Consoleprintf("FreeData Host %s %d", TNC->HostName, TNC->TCPPort); + + TNC->Port = port; + TNC->Hardware = H_FREEDATA; + + TNC->WeStartedTNC = 1; + + TNC->ARDOPDataBuffer = malloc(MAXRXSIZE); + TNC->ARDOPBuffer = malloc(FREEDATABUFLEN); + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + memcpy(TNC->FreeDataInfo->ourCall, TNC->NodeCall, 10); + strlop(TNC->FreeDataInfo->ourCall, ' '); + if (TNC->FreeDataInfo->useBaseCall) + strlop(TNC->FreeDataInfo->ourCall, '-'); + + 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; + + if (TNC->PacketChannels > 1) + TNC->PacketChannels = 1; + + PortEntry->MAXHOSTMODESESSIONS = TNC->PacketChannels + 1; + + PortEntry->SCANCAPABILITIES = SIMPLE; // Scan Control - pending connect only + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + + PortEntry->PORTCONTROL.UICAPABLE = TRUE; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 236; + + TNC->SuspendPortProc = FreeDataSuspendPort; + TNC->ReleasePortProc = FreeDataReleasePort; + +// PortEntry->PORTCONTROL.PORTSTARTCODE = KAMStartPort; +// PortEntry->PORTCONTROL.PORTSTOPCODE = KAMStopPort; + + + 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); + + // cant think of any yet + + if (TNC->InitScript) + { + strcat(TempScript, TNC->InitScript); + free(TNC->InitScript); + } + + TNC->InitScript = TempScript; + + // Set MYCALL + + sprintf(Msg, "MYCALL %s\r", TNC->NodeCall); + strcat(TNC->InitScript, Msg); + + strcpy(TNC->CurrentMYC, TNC->NodeCall); + + if (TNC->WL2K == NULL) + if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Already decoded + TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo; + + PortEntry->PORTCONTROL.TNC = TNC; + + // Build SSID List + + if (TNC->LISTENCALLS) + { + strcpy(TNC->FreeDataInfo->SSIDList, TNC->LISTENCALLS); + } + else + { + APPLCALLS * APPL; + char Appl[11] = ""; + char * List = TNC->FreeDataInfo->SSIDList; + char * SSIDptr; + int SSID; + int Listptr; + + // list is a set of numbers separated by spaces eg 0 2 10 + + SSIDptr = strchr(TNC->NodeCall, '-'); + if (SSIDptr) + SSID = atoi(SSIDptr + 1); + else + SSID = 0; + + Listptr = sprintf(List, "%d", SSID); + + for (i = 0; i < 32; i++) + { + APPL=&APPLCALLTABLE[i]; + + if (APPL->APPLCALL_TEXT[0] > ' ') + { + char * ptr; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + + SSIDptr = strchr(Appl, '-'); + if (SSIDptr) + SSID = atoi(SSIDptr + 1); + else + SSID = 0; + + Listptr += sprintf(&List[Listptr], " %d", SSID); + } + } + List[Listptr] = 0; + } + + 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, 500, 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,450,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,80,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", "Proto State", 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,116,line,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); + + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 0,170,250,300, TNC->hDlg, NULL, hInstance, NULL); + + TNC->ClientHeight = 450; + TNC->ClientWidth = 500; + + TNC->hMenu = CreatePopupMenu(); + + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill Freedata TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart Freedata 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 + + strcpy(TNC->WEB_CHANSTATE, "Idle"); + SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE) + + // Convert sound card name to index + +#ifdef WIN32 + + if (CaptureDevices == NULL) // DOn't do it again if more that one port + { + CaptureCount = waveInGetNumDevs(); + + CaptureDevices = malloc((MAXPNAMELEN + 2) * CaptureCount); + CaptureDevices[0] = 0; + + printf("Capture Devices"); + + for (i = 0; i < CaptureCount; i++) + { + waveInOpen(&hWaveIn, i, &wfx, 0, 0, CALLBACK_NULL); //WAVE_MAPPER + waveInGetDevCaps((UINT_PTR)hWaveIn, &pwic, sizeof(WAVEINCAPS)); + + if (CaptureDevices) + strcat(CaptureDevices, ","); + strcat(CaptureDevices, pwic.szPname); + Debugprintf("%d %s", i + 1, pwic.szPname); + memcpy(&CaptureNames[i][0], pwic.szPname, MAXPNAMELEN); + _strupr(&CaptureNames[i][0]); + } + + PlaybackCount = waveOutGetNumDevs(); + + PlaybackDevices = malloc((MAXPNAMELEN + 2) * PlaybackCount); + PlaybackDevices[0] = 0; + + Debugprintf("Playback Devices"); + + for (i = 0; i < PlaybackCount; i++) + { + waveOutOpen(&hWaveOut, i, &wfx, 0, 0, CALLBACK_NULL); //WAVE_MAPPER + waveOutGetDevCaps((UINT_PTR)hWaveOut, &pwoc, sizeof(WAVEOUTCAPS)); + + if (PlaybackDevices[0]) + strcat(PlaybackDevices, ","); + strcat(PlaybackDevices, pwoc.szPname); + Debugprintf("%i %s", i + 1, pwoc.szPname); + memcpy(&PlaybackNames[i][0], pwoc.szPname, MAXPNAMELEN); + _strupr(&PlaybackNames[i][0]); + waveOutClose(hWaveOut); + } + } + +#endif + + time(&TNC->lasttime); // Get initial time value + + ConnecttoFreeData(port); + return ExtProc; +} + + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // We don't get data acks, so can't check for bytes outstanding + + FreeDataDisconnect(TNC); +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + FreeDataDisconnect(TNC); +} + + + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + if (Stream == 0) + { + FreeDataReleaseTNC(TNC); + } + + + +} + +VOID FreeDataAbort(struct TNCINFO * TNC) +{ + FreeDataSendCommand(TNC, "ABORT\r"); +} + +// Host Mode Stuff (we reuse some routines in SCSPactor) + +VOID FreeDataDoTermModeTimeout(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->ReinitState == 0) + { + //Checking if in Terminal Mode - Try to set back to Term Mode + + TNC->ReinitState = 1; + return; + } + + if (TNC->ReinitState == 1) + { + // Forcing back to Term Mode + + TNC->ReinitState = 0; + return; + } + + if (TNC->ReinitState == 3) + { + return; + } +} + +static RECT Rect1 = {30, 160, 400, 195}; + +int zEncode(unsigned char * in, unsigned char * out, int len, unsigned char * Banned) +{ + // Replace forbidden chars with =xx + + unsigned char * ptr = out; + unsigned char c; + + while (len--) + { + c = *(in++); + + if (strchr(&Banned[0], c)) + { + *(out++) = '='; + *(out++) = (c >> 4) + 'A'; + *(out++) = (c & 15) + 'A'; + } + else + *(out++) = c; + } + + return (out - ptr); +} + + + +VOID FreeDataProcessTNCMessage(struct TNCINFO * TNC, char * Call, unsigned char * Msg, int Len) +{ + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FreeDataINFO * Modem = TNC->FreeDataInfo; + char * toCall, * fromCall, * tncCall, *Context; + char * ptr; + char a, b; + unsigned char axcall[7]; + char AppName[13] = ""; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + TRANSPORTENTRY * SESS; + + // First Byte of Message is Type. Messages can be commands or short (<120) data packets + // Data is encoded with =xx replacing restricted chars + + Msg[Len] = 0; + + switch (Msg[0]) + { + case 'C': + + // Connect Request. C G8BPQ-10 GM8BPQ-2 (Target, Origin) + + toCall = strtok_s(&Msg[2], " ", &Context); + fromCall = strtok_s(NULL, " ", &Context); + tncCall = strtok_s(NULL, " ", &Context); + + strcpy(TNC->FreeDataInfo->farCall, tncCall); + + ConvToAX25Ex(fromCall, axcall); // Allow -T and -R SSID's for MPS + + // Check for ExcludeList + + if (ExcludeList[0]) + { + if (CheckExcludeList(axcall) == FALSE) + { + Debugprintf("FreeData Call from %s rejected", fromCall); + + // Send 'd' + + Sleep(1000); + FreeDataDisconnect(TNC); + 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(axcall, ptr, 6) == 0) // Ignore SSID + break; + + ptr += 7; + + if ((*ptr) == 0) // Not in list + { + Sleep(1000); + FreeDataSendCommand(TNC, "d"); + + Debugprintf("FreeDara 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(toCall, Appl) == 0) + break; + } + + if (App < 32) + { + + 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)) + { + Sleep(1000); + FreeDataSendCommand(TNC, "dApplication not Available"); + return; + } + } + + ProcessIncommingConnectEx(TNC, fromCall, 0, TRUE, TRUE); + SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + // if connect to an application, send command + + if (AppName[0]) + { + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf(buffptr->Data, "%s\r", AppName); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + TNC->SwallowSignon = TRUE; + } + } + + 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, toCall, 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; + } + } + SESS->Mode = TNC->WL2KMode; + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, toCall); + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + SESS->Mode = WL2K->mode; + } + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->PacketsSent = 0; + STREAM->Connected = TRUE; + + // Send Connect ACK + Sleep(1000); + FreeDataSendCommand(TNC, "c"); + return; + + case 'c': + + // Connect ACK + + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s", STREAM->MyCall, STREAM->RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + STREAM->Connected = TRUE; + STREAM->Connecting = FALSE; + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "*** Connected to %s\r", TNC->FreeDataInfo->toCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->FramesQueued++; + + return; + + case 'D': + + // Disconnect Command + + FreeDataSendCommand(TNC, "d"); + + // Drop through to disconnect this end + + case 'd': + + // Disconnect complete (response to sending "D") + // Or connect refused in response to "C" + + if (STREAM->Connecting) + { + // Connection Refused - If there is a message, pass to appl + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->Connecting = FALSE; + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + if (Msg[1]) + buffptr->Len = sprintf(buffptr->Data, "Connect Rejected - %s\r", &Msg[1]); + else + buffptr->Len = sprintf(buffptr->Data, "Connect Rejected\r"); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->FramesQueued++; + return; + } + + // Release Session + + if (STREAM->Connected) + { + // 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->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + STREAM->Disconnecting = FALSE; + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + return; + + case 'B': + + // Was Base64, but has been expanded - just send to User + + // If len > blocksize, fragment + + Len--; + Msg++; // Remove Type + + while (Len > 256) + { + buffptr = GetBuff(); + buffptr->Len = 256; + memcpy(buffptr->Data, Msg, 256); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + WritetoTrace(TNC, Msg, 256); + Len -= 256; + Msg += 256; + STREAM->BytesRXed += 256; + + } + + buffptr = GetBuff(); + buffptr->Len = Len; + memcpy(buffptr->Data, Msg, Len); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + WritetoTrace(TNC, Msg, Len); + STREAM->BytesRXed += Len; + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->BytesTXed - TNC->FreeDataInfo->toSendCount, STREAM->BytesRXed, TNC->FreeDataInfo->toSendCount); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + + return; + + case 'I': + + // Encoded Response + + // Undo = transparency + + ptr = Msg + 1; + + while (ptr = strchr(ptr, '=')) + { + // Next two chars are a hex value + + a = ptr[1] - 'A'; + b = ptr[2] - 'A'; + memmove(ptr, ptr + 2, Len); + ptr[0] = (a << 4) + b; + ptr++; + } + + buffptr = GetBuff(); + buffptr->Len = sprintf(buffptr->Data, "%s", &Msg[1]); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + return; + + case 'f': + + // FreeDATA File Transfer + + // Seems to be f null fn null data + //f.;Makefile.;.;123123123.; + { + char * FN; + time_t CRC; + int FileLen; + char Filename[256]; + FILE * fp1; + unsigned char * ptr; + char Text[64]; + int textLen; + + + if (TNC->FreeDataInfo->RXDir == NULL) + { + Debugprintf("FreeDATA RXDIRECTORY not set - file transfer ignored"); + return; + } + + FN = _strdup(&Msg[3]); + ptr = &Msg[4] + strlen(FN); + + ptr = ptr + strlen(ptr) + 2; + CRC = atoi(ptr); + ptr = ptr + strlen(ptr) + 2; + + FileLen = Len - (ptr - Msg); + + sprintf(Filename, "%s\\%s", TNC->FreeDataInfo->RXDir, FN); + + fp1 = fopen(Filename, "wb"); + + if (fp1) + { + fwrite(ptr, 1, FileLen, fp1); + fclose(fp1); + textLen = sprintf(Text, "File %s received from %s \r", FN, Call); + WritetoTrace(TNC, Text, textLen); + } + else + Debugprintf("FreeDATA - File %s create failed %s", Filename); + + + free(FN); + return; + + } + + + } + + if (STREAM->Attached == 0) + return; + + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", Msg); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + +} + + +VOID FreeDataProcessNewConnect(struct TNCINFO * TNC, char * fromCall, char * toCall) +{ + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FreeDataINFO * Modem = TNC->FreeDataInfo; + char * tncCall, *Context; + char * ptr; + char a, b; + unsigned char axcall[7]; + char AppName[13] = ""; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + TRANSPORTENTRY * SESS; + + strcpy(TNC->FreeDataInfo->farCall, fromCall); + + ConvToAX25Ex(fromCall, axcall); // Allow -T and -R SSID's for MPS + + // Check for ExcludeList + + if (ExcludeList[0]) + { + if (CheckExcludeList(axcall) == FALSE) + { + Debugprintf("FreeData Call from %s rejected", fromCall); + + // Send 'd' + + Sleep(1000); + FreeDataDisconnect(TNC); + 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(axcall, ptr, 6) == 0) // Ignore SSID + break; + + ptr += 7; + + if ((*ptr) == 0) // Not in list + { + Sleep(1000); + FreeDataDisconnect(TNC); + + Debugprintf("FreeData Call from %s not in ValidCalls - rejected", fromCall); + return; + } + } + } + + // The TNC responds to any SSID so we can use incomming call as Appl Call + // No we can't - it responds but reports the configured call not the called call + + // 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(toCall, Appl) == 0) + break; + } + + if (App < 32) + { + + 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(fromCall, "-T" ) || strstr(fromCall, "-R"))) + strcpy(AppName, "RELAY "); + + // Make sure app is available + + if (!CheckAppl(TNC, AppName)) + { + Sleep(1000); + FreeDataSendCommand(TNC, "dApplication not Available"); + return; + } + } + + ProcessIncommingConnectEx(TNC, fromCall, 0, TRUE, TRUE); + SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + // if connect to an application, send command + + if (AppName[0]) + { + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf(buffptr->Data, "%s\r", AppName); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + TNC->SwallowSignon = TRUE; + } + } + + 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, toCall, 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; + } + } + SESS->Mode = TNC->WL2KMode; + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, toCall); + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + SESS->Mode = WL2K->mode; + } + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->PacketsSent = 0; + STREAM->Connected = TRUE; + + return; + +} + +VOID FreeDataProcessConnectAck(struct TNCINFO * TNC, char * Call, unsigned char * Msg, int Len) +{ + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FreeDataINFO * Modem = TNC->FreeDataInfo; + char * toCall, * fromCall, * tncCall, *Context; + char * ptr; + char a, b; + unsigned char axcall[7]; + char AppName[13] = ""; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + TRANSPORTENTRY * SESS; + + + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s", STREAM->MyCall, STREAM->RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + STREAM->Connected = TRUE; + STREAM->Connecting = FALSE; + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "*** Connected to %s\r", TNC->FreeDataInfo->toCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->FramesQueued++; + + return; + +} +extern char LOC[7]; +/* +Line 104: if received_json["type"] == 'PING' and received_json["command"] == "PING": +Line 111: if received_json["type"] == 'ARQ' and received_json["command"] == "sendFile": +Line 142: if received_json["type"] == 'ARQ' and received_json["command"] == "sendMessage": +Line 173: if received_json["type"] == 'ARQ' and received_json["command"] == "stopTransmission": +Line 182: if received_json["type"] == 'GET' and received_json["command"] == 'STATION_INFO': +Line 195: if received_json["type"] == 'GET' and received_json["command"] == 'TNC_STATE': +Line 244: if received_json["type"] == 'GET' and received_json["command"] == 'RX_BUFFER': +Line 258: if received_json["type"] == 'GET' and received_json["command"] == 'RX_MSG_BUFFER': +Line 272: if received_json["type"] == 'SET' and received_json["command"] == 'DEL_RX_BUFFER': +Line 275: if received_json["type"] == 'SET' and received_json["command"] == 'DEL_RX_MSG_BUFFER': +*/ + +//{\"type\" : \"ARQ\", \"command\" : \"sendMessage\", \"dxcallsign\" : \"G8BPQ\", \"mode\" : \"10\", \"n_frames\" : \"1\", \"data\" : \"Hello Hello\" , \"checksum\" : \"123\", \"timestamp\" : 1642580748576} + + + +static unsigned char BANNED[] = {'"', '=', ':', '{', '}', '[', ']', '/', 13, 0}; // I think only need to escape = ": CR Null + + +static void SendDataMsg(struct TNCINFO * TNC, char * Call, char * Msg, int Len) +{ + // We can't base64 encode chunks. so buffer as original data and encode on send + + SendAsFile(TNC, TNC->FreeDataInfo->farCall, Msg, Len); + WritetoTrace(TNC, Msg, Len); + + return; +} + + + +static int SendAsRaw(struct TNCINFO * TNC, char * Call, char * myCall, char * Msg, int Len) +{ + char Message[16284]; + char * Base64; + + // TNC now only supports send_raw, with base64 encoded data + + char Template[] = "{\"type\" : \"arq\", \"command\" : \"send_raw\", \"uuid\" : \"%s\",\"parameter\":" + "[{\"dxcallsign\" : \"%s\", \"mode\": \"255\", \"n_frames\" : \"1\", \"data\" : \"%s\"}]}\n"; + + + Base64 = byte_base64_encode(Msg, Len); + + Len = sprintf(Message, Template, gen_uuid(), Call, Base64); + + free(Base64); + return send(TNC->TCPDataSock, Message, Len, 0); +} + +void FlushData(struct TNCINFO * TNC) +{ + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FreeDataINFO * Info = TNC->FreeDataInfo; + int Len = Info->toSendCount; + + // We need to flag as data (B) then base64 encode it + + memmove(&Info->toSendData[1], Info->toSendData, Len); + Info->toSendData[0] = 'B'; + Len++; + + SendAsRaw(TNC, Info->farCall, Info->ourCall, Info->toSendData, Len); + + Info->toSendCount = 0; + Info->toSendTimeout = 0; + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->BytesTXed - TNC->FreeDataInfo->toSendCount, STREAM->BytesRXed, TNC->FreeDataInfo->toSendCount); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + +} + +static int SendAsFile(struct TNCINFO * TNC, char * Call, char * Msg, int Len) +{ + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FreeDataINFO * Info = TNC->FreeDataInfo; + + // Add to buffer + + if ((Info->toSendCount + Len) > 8192) // Reasonable Limit + { + // Send the buffered bit + + FlushData(TNC); + } + + memcpy(&Info->toSendData[Info->toSendCount], Msg, Len); + Info->toSendCount += Len; + Info->toSendTimeout = 10; // About a second + + STREAM->BytesTXed += Len; + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->BytesTXed - TNC->FreeDataInfo->toSendCount, STREAM->BytesRXed, TNC->FreeDataInfo->toSendCount); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + + return Len; +} + +static int SendTNCCommand(struct TNCINFO * TNC, char * Type, char * Command) +{ + char Message[256]; + int Len; + + Len = sprintf(Message, "{\"type\" : \"%s\", \"command\" : \"%s\"}\n", Type, Command); + return send(TNC->TCPDataSock, Message, Len, 0); +} + +static void SendPoll(struct TNCINFO * TNC) +{ + return; +} + +static void SendPing(struct TNCINFO * TNC, char * Call) +{ + char Ping[] = "{\"type\" : \"ping\", \"command\" : \"ping\", \"dxcallsign\" : \"%s\", \"timestamp\" : %d}\n"; + char Message[256]; + int Len, ret; + + Len = sprintf(Message, Ping, Call, time(NULL)); + ret = send(TNC->TCPDataSock, (char *)&Message, Len, 0); +} + +static void SendCQ(struct TNCINFO * TNC) +{ + char CQ[] = "{\"type\" : \"broadcast\", \"command\" : \"cqcqcq\"}\n"; + + char Message[256]; + int Len, ret; + + Len = sprintf(Message, CQ); + ret = send(TNC->TCPDataSock, (char *)&Message, Len, 0); +} + +static void SendBeacon(struct TNCINFO * TNC, int Interval) +{ + char Template1[] = "{\"type\" : \"broadcast\", \"command\" : \"start_beacon\", \"parameter\" : \"%d\"}\n"; + char Template2[] = "{\"type\" : \"broadcast\", \"command\" : \"stop_beacon\"}\n"; + + char Message[256]; + int Len, ret; + + if (Interval > 0) + Len = sprintf(Message, Template1, Interval); + else + Len = sprintf(Message, Template2); + + ret = send(TNC->TCPDataSock, (char *)&Message, Len, 0); +} + + +unsigned short int compute_crc(unsigned char *buf,int len); + + +int FreeDataWriteCommBlock(struct TNCINFO * TNC) +{ + if (TNC->hDevice) + return WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); + + return 0; +} + +char * getObjectFromArray(char * Msg) +{ + // This gets the next object from an array ({} = object, [] = array + // We look for the end of the object same number of { and }, teminate after } and return pointer to next object + // So we have terminated Msg, and returned next object in array + + // Only call if Msg is the next object in array + + + char * ptr = Msg; + char c; + + int Open = 0; + int Close = 0; + + while (c = *(ptr++)) + { + if (c == '{') Open ++; else if (c == '}') Close ++; + + if (Open == Close) + { + *(ptr++) = 0; + return ptr; + } + } + return 0; +} +/* + ["DATACHANNEL;RECEIVEDOPENER","ARQ;RECEIVING","ARQ;RECEIVING;SUCCESS"] [{"DXCALLSIGN":"GM8BPQ +{"DXCALLSIGN":"GM8BPQ","DXGRID":"","TIMESTAMP":1642847440,"RXDATA":[{"dt":"f","fn":"config.json", +"ft":"application\/json","d":"data:application\/json;base64, +ewogICAgInRuY19ob3N0IiA6ICIxMjcuMC4wLjEiLAogICAgInRuY19wb3J0IiA6ICIzMDAwIiwKICAgICJkYWVtb25faG9zdCIgOiAiMTI3LjAuMC4xIiwKICAgICJkYWVtb25fcG9ydCIgOiAiMzAwMSIsCiAgICAibXljYWxsIiA6ICJBQTBBQSIsCiAgICAibXlncmlkIiA6ICJBQTExZWEiICAgIAp9" +,"crc":"123123123"}]} +*/ +void ProcessFileObject(struct TNCINFO * TNC, char * This) +{ + char * Call; + char * LOC; + char * FN; + char * Type; + char * ptr, * ptr2; + int Len, NewLen; + + Call = strchr(This, ':'); + Call += 2; + This = strlop(Call, '"'); + + LOC = strchr(This, ':'); + LOC += 2; + This = strlop(LOC, '"'); + + FN = strstr(This, "fn"); + FN += 5; + This = strlop(FN, '"'); + + Type = strstr(This, "base64"); + Type += 7; + This = strlop(Type, '"'); + + // Decode Base64 + + Len = strlen(Type); + + Debugprintf("RX %d %s %d", TNC->Port, FN, Len); + + ptr = ptr2 = Type; + + while (Len > 0) + { + xdecodeblock(ptr, ptr2); + ptr += 4; + ptr2 += 3; + Len -= 4; + } + + NewLen = (int)(ptr2 - Type); + + if (*(ptr-1) == '=') + NewLen--; + + if (*(ptr-2) == '=') + NewLen--; + + Type[NewLen] = 0; + + Type --; + + Type[0] = 'B'; ; // Base64 Info + + FreeDataProcessTNCMessage(TNC, Call, Type, NewLen + 1); + + +} + +void ProcessMessageObject(struct TNCINFO * TNC, char * This) +{ + // This gets Message from a RX_MSG_BUFFER array element. + + char * Call; + char * LOC; + char * Type; + char * Msg; + int Len; + char * ptr, * ptr2; + + char * ID; + char * TYPE; + char * SEQ; + char * UUID; + char * TEXT; + char * NOIDEA; + char * FORMAT; + char * FILENAME; + int fileLen; + + int n; + + + Call = strstr(This, "dxcallsign"); + Call += 13; + This = strlop(Call, '"'); + + LOC = strchr(This, ':'); + LOC += 2; + This = strlop(LOC, '"'); + + Msg = strstr(This, "\"data\""); + Msg += 8; + This = strlop(Msg, '"'); + + // Decode Base64 + + // FreeData replaces / with \/ so need to undo + + ptr2 = strstr(Msg, "\\/"); + + while (ptr2) + { + memmove(ptr2, ptr2 + 1, strlen(ptr2)); + ptr2 = strstr(ptr2, "\\/"); + } + + Len = strlen(Msg); + + ptr = ptr2 = Msg; + + while (Len > 0) + { + xdecodeblock(ptr, ptr2); + ptr += 4; + ptr2 += 3; + Len -= 4; + } + + Len = (int)(ptr2 - Msg); + + if (*(ptr-1) == '=') + Len--; + + if (*(ptr-2) == '=') + Len--; + + Msg[Len] = 0; + +//m�;send_message�;123�;64730c5c-d32c-47b4-9b11-c958fd07a185�;hhhhhhhhhhhhhhhhhh +//�;�;plain/text�; + +//m;send_message;123;64730c5c-d32c-47b4-9b11-c958fd07a185;hhhhhhhhhhhhhhhhhh +//;;plain/text; + + // Message elements seem to be delimited by null ; + // Guessing labels + + ID = Msg; + + if (ID[0] == 'B') + { + // BPQ Message + + struct STREAMINFO * STREAM = &TNC->Streams[0]; + PMSGWITHLEN buffptr; + + + if (STREAM->Attached) + { + if (STREAM->Connected == 1 && STREAM->Connecting == 0) + { + char * Line = &ID[1]; + Len -= 1; + + while (Len > 256) + { + buffptr = GetBuff(); + buffptr->Len = 256; + memcpy(buffptr->Data, Line, 256); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + WritetoTrace(TNC, Line, 256); + Len -= 256; + Line += 256; + STREAM->BytesRXed += 256; + } + + buffptr = GetBuff(); + buffptr->Len = Len; + memcpy(buffptr->Data, Line, Len); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + WritetoTrace(TNC, Line, Len); + STREAM->BytesRXed += Len; + + } + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->BytesTXed - TNC->FreeDataInfo->toSendCount, STREAM->BytesRXed, TNC->FreeDataInfo->toSendCount); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + } + return; + } + + n = strlen(ID) + 2; + Msg += n; + Len -= n; + + if (ID[0] == 'm') + { + // ?? Chat ?? comes from a send raw ?? + + struct STREAMINFO * STREAM = &TNC->Streams[0]; + PMSGWITHLEN buffptr; + + TYPE = Msg; + n = strlen(TYPE) + 2; + Msg += n; + Len -= n; + + SEQ = Msg; + n = strlen(SEQ) + 2; + Msg += n; + Len -= n; + + UUID = Msg; + n = strlen(UUID) + 2; + Msg += n; + Len -= n; + + TEXT = Msg; + n = strlen(TEXT) + 2; + Msg += n; + Len -= n; + + NOIDEA = Msg; + n = strlen(NOIDEA) + 2; + Msg += n; + Len -= n; + + FORMAT = Msg; + n = strlen(FORMAT) + 2; + Msg += n; + Len -= n; + + // if Atached, send to user + + if (STREAM->Attached) + { + if (STREAM->Connected == 0 && STREAM->Connecting == 0) + { + // Just attached - send as Chat Message + + char Line[560]; + char * rest; + + // Send line by line + + rest = strlop(TEXT, 10); // FreeData chat uses LF + + while (TEXT && TEXT[0]) + { + Len = strlen(TEXT); + if (Len > 512) + TEXT[512] = 0; + + Len = sprintf(Line, "Chat From %-10s%s\r", Call, TEXT); + + while (Len > 256) + { + buffptr = GetBuff(); + buffptr->Len = 256; + memcpy(buffptr->Data, Line, 256); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + WritetoTrace(TNC, Line, 256); + Len -= 256; + TEXT += 256; + STREAM->BytesRXed += 256; + } + + buffptr = GetBuff(); + buffptr->Len = Len; + memcpy(buffptr->Data, Line, Len); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + WritetoTrace(TNC, Line, Len); + STREAM->BytesRXed += Len; + + TEXT = rest; + rest = strlop(TEXT, 10); // FreeData chat ues LF + } + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->BytesTXed - TNC->FreeDataInfo->toSendCount, STREAM->BytesRXed, TNC->FreeDataInfo->toSendCount); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + } + } + else + { + // Send Not Available Message +//m�;send_message�;123�;64730c5c-d32c-47b4-9b11-c958fd07a185�;hhhhhhhhhhhhhhhhhh +//�;�;plain/text�; + + char reply[512] = "m"; + char * p; + + strcat(TEXT, "\r"); + WritetoTrace(TNC, TEXT, strlen(TEXT)); + + strcpy(&reply[2], ";send_message"); + strcpy(&reply[16], ";123"); + reply[21] = ';'; + strcpy(&reply[22], gen_uuid()); + sprintf(&reply[59], ";Message received but user not on line\n"); + + p = strchr(&reply[59], 0); + + p[1] = ';'; + strcpy(&p[3], ";plain/text"); + p[15] = ';'; + + Len = &p[16] - reply; + + SendAsRaw(TNC, Call, TNC->FreeDataInfo->ourCall, reply, Len); + } + return; + + } + else if (ID[0] == 'f') + { + // File Tranfer + + char Filename[256]; + FILE * fp1; + char Text[64]; + int textLen; + + + FILENAME = Msg; + n = strlen(FILENAME) + 2; + Msg += n; + Len -= n; + + TYPE = Msg; + n = strlen(TYPE) + 2; + Msg += n; + Len -= n; + + SEQ = Msg; // ?? Maybe = 123123123 + n = strlen(SEQ) + 2; + Msg += n; + Len -= n; + + TEXT = Msg; // The file + fileLen = Len; + + if (TNC->FreeDataInfo->RXDir == NULL) + { + Debugprintf("FreeDATA RXDIRECTORY not set - file transfer ignored"); + return; + } + + sprintf(Filename, "%s\\%s", TNC->FreeDataInfo->RXDir, FILENAME); + + fp1 = fopen(Filename, "wb"); + + if (fp1) + { + fwrite(TEXT, 1, fileLen, fp1); + fclose(fp1); + textLen = sprintf(Text, "File %s received from %s \r", FILENAME, Call); + WritetoTrace(TNC, Text, textLen); + } + else + Debugprintf("FreeDATA - File %s create failed %s", Filename); + + return; + } + else if (ID[0] == 'm') + { + + } + + + + + + + + + + +// FreeDataProcessTNCMessage(TNC, Call, Msg, strlen(Msg)); +} + +void processJSONINFO(struct TNCINFO * TNC, char * Info, char * Call, double snr) +{ + char * LOC = ""; + char * ptr, * Context; + + // Info is an array. Normally only one element, but should check + + ptr = strtok_s(&Info[1], ",]", &Context); + + while (ptr && ptr[1]) + { + if (strstr(ptr, "BEACON;RECEIVING")) + { + char CQ[64]; + int Len; + + Len = sprintf(CQ, "Beacon received from %s SNR %3.1f\r", Call, snr); + WritetoTrace(TNC, CQ, Len); + + // Add to MH + + if (Call) + UpdateMH(TNC, Call, '!', 'I'); + } + if (strstr(ptr, "PING;RECEIVING")) + { + // Add to MH + + if (Call) + UpdateMH(TNC, Call, '!', 'I'); + } + else if (strstr(ptr, "CQ;RECEIVING")) + { + char CQ[64]; + int Len; + + Len = sprintf(CQ, "CQ received from %s SNR %3.1f\r", Call, snr); + WritetoTrace(TNC, CQ, Len); + + // Add to MH + + UpdateMH(TNC, Call, '!', 'I'); + } + else if (strstr(ptr, "PING;RECEIVEDACK")) + { + char Msg[128]; + int Len; + + Len = sprintf(Msg, "Ping Response from %s SNR %3.1f\r", Call, snr); + FreeDataProcessTNCMessage(TNC, Call, Msg, Len); + + // Add to MH + + UpdateMH(TNC, Call, '!', 'I'); + } + else if (strstr(ptr, "TRANSMITTING;FAILED")) + { + // Failed to send a message - if it was a connect request tell appl + + struct STREAMINFO * STREAM = &TNC->Streams[0]; + PMSGWITHLEN buffptr; + + if (STREAM->Connecting) + { + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->Connecting = FALSE; + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "*** Connect Failed\r"); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->FramesQueued++; + } + } + ptr = strtok_s(NULL, ",]", &Context); + } +} + + + + + + +char * getJSONValue(char * Msg, char * Key) +{ + char * ptr, *ptr2, *value = 0; + int vallen, keylen = strlen(Key); + char c; + + // We Null Terminate the value, so we must look for keys in reverse order + + ptr = strstr(Msg, Key); + + if (ptr) + { + ptr += (keylen + 1); + + if (*(ptr) == '[') + { + // Array + + int Open = 0; + int Close = 0; + + ptr2 = ptr; + + while (c = *(ptr++)) + { + if (c == '[') + Open ++; + else if (c == ']') + Close ++; + + if (Open == Close) + { + vallen = ptr - ptr2; + value = ptr2; + value[vallen] = 0; + return value; + } + } + } + else if (*(ptr) == '\"') + { + // String + + ptr2 = ptr; + ptr = strchr(ptr + 1, '\"'); + if (ptr) + { + ptr++; + vallen = ptr - ptr2; + value = ptr2; + value[vallen] = 0; + } + } + } + return value; +} + + +char stopTNC[] = "{\"type\" : \"SET\", \"command\": \"STOPTNC\" , \"parameter\": \"---\" }\r"; + + +void StopTNC(struct TNCINFO * TNC) +{ + if (TNC->TCPDataSock) + closesocket(TNC->TCPDataSock); +} + + +void ProcessTNCJSON(struct TNCINFO * TNC, char * Msg, int Len) +{ + char * ptr; + + if (memcmp(Msg, "{\"ptt\":", 7) == 0) + { + if (strstr(Msg, "True")) + { +// TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + + if (TNC->PTTMode) + Rig_PTT(TNC, TRUE); + } + else + { + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); + } + return; + } + + if (memcmp(Msg, "{\"command_response\"", 19) == 0) + { + Debugprintf("%d %s", TNC->Port, Msg); + return; + } + + + if (memcmp(Msg, "{\"command\":\"tnc_state\"", 22) == 0) + { +/* +{"command":"tnc_state","ptt_state":"False","tnc_state":"IDLE","arq_state":"False","arq_session":"False", +"arq_session_state":"disconnected","audio_rms":"0","snr":"0","frequency":"None","speed_level":"1", +"mode":"None","bandwidth":"None","fft":"[0]","channel_busy":"False","scatter":[],"rx_buffer_length":"0", +"rx_msg_buffer_length":"0","arq_bytes_per_minute":"0","arq_bytes_per_minute_burst":"0","arq_compression_factor":"0", +"arq_transmission_percent":"0","total_bytes":"0","beacon_state":"False", +"stations":[],"mycallsign":"GM8BPQ-6","dxcallsign":"AA0AA","dxgrid":""} +*/ + char * LOC = 0; + char * Stations; + char * myCall = 0; + char * farCall = 0; + double snr; + int arqstate = 0; + int rx_buffer_length = 0; + int rx_msg_buffer_length = 0; + + Msg += 23; + + ptr = strstr(Msg, "tnc_state"); + + if (ptr) + { + if (ptr[12] == 'B' && TNC->Busy == FALSE) + { + TNC->Busy = TRUE; + strcpy(TNC->WEB_CHANSTATE, "Busy"); + SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + } + else if (ptr[12] == 'I' && TNC->Busy) + { + TNC->Busy = FALSE; + strcpy(TNC->WEB_CHANSTATE, "Idle"); + SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + } + } + + ptr = strstr(Msg, "rx_buffer_length"); + + if (ptr) + rx_buffer_length = atoi(&ptr[19]); + + ptr = strstr(Msg, "rx_msg_buffer_length"); + + if (ptr) + rx_msg_buffer_length = atoi(&ptr[23]); + + ptr = strstr(Msg, "snr"); + + if (ptr) + snr = atof(ptr + 6); + + Stations = getJSONValue(Msg, "\"stations\""); + + if (Stations) + { + ptr = Stations + strlen(Stations) + 1; + LOC = getJSONValue(ptr, "\"dxgrid\""); + farCall = getJSONValue(ptr, "\"dxcallsign\""); + myCall = getJSONValue(ptr, "\"mycallsign\""); + + if (myCall && farCall) + { + myCall++; + strlop(myCall, '"'); + farCall++; + strlop(farCall, '"'); + } + } + + // Look for changes in arq_session_state + + ptr = strstr(Msg, "\"arq_session_state\""); + + if (ptr) + { + struct STREAMINFO * STREAM = &TNC->Streams[0]; + ptr += 21; + + if (memcmp(ptr, "disconnected", 10) == 0) + { + if (TNC->FreeDataInfo->arqstate != 1) + { + TNC->FreeDataInfo->arqstate = 1; + Debugprintf("%d arq_session_state %s", TNC->Port, "disconnected"); + } + + // if connected this is a new disconnect + + if (STREAM->Connected) + { + // 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->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + STREAM->Disconnecting = FALSE; + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + } + } + else if (memcmp(ptr, "connecting", 10) == 0) + { + if (TNC->FreeDataInfo->arqstate != 2) + { + TNC->FreeDataInfo->arqstate = 2; + Debugprintf("%d arq_session_state %s", TNC->Port, "connecting"); + } + } + else if (memcmp(ptr, "connected", 9) == 0) + { + // if connection is idle this is an incoming connect + + if (TNC->FreeDataInfo->arqstate != 3) + { + TNC->FreeDataInfo->arqstate = 3; + Debugprintf("%d arq_session_state %s", TNC->Port, "connected"); + } + + if (STREAM->Connecting == FALSE && STREAM->Connected == FALSE) + { + FreeDataProcessNewConnect(TNC, farCall, myCall); + } + + // if connecting it is a connect ack + + else if (STREAM->Connecting) + { + FreeDataProcessConnectAck(TNC, farCall, Msg, Len); + } + } + + else if (memcmp(ptr, "disconnecting", 12) == 0) + { + if (TNC->FreeDataInfo->arqstate != 4) + { + TNC->FreeDataInfo->arqstate = 4; + Debugprintf("%d arq_session_state %s", TNC->Port, "disconnecting"); + } + } + else if (memcmp(ptr, "failed", 5) == 0) + { + PMSGWITHLEN buffptr; + + if (TNC->FreeDataInfo->arqstate != 5) + { + TNC->FreeDataInfo->arqstate = 5; + Debugprintf("%d arq_session_state %s", TNC->Port, "failed"); + } + + if (STREAM->Connecting) + { + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->Connecting = FALSE; + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "*** Connect Failed\r"); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->FramesQueued++; + } + } + } + + if (rx_buffer_length || rx_msg_buffer_length) + FreeGetData(TNC); + + ptr = getJSONValue(Msg, "\"info\""); + + if (ptr == NULL) + return; + + if (strcmp(ptr, "[]") != 0) + { + + processJSONINFO(TNC, ptr, farCall, snr); + Debugprintf("%d %s %s", TNC->Port, ptr, farCall); + } + + return; + } + + if (memcmp(Msg, "{\"freedata\":\"tnc-message\"", 25) == 0) + { + char * mycall = strstr(Msg, "mycall"); + char * dxcall = strstr(Msg, "dxcall"); + char * dxgrid = strstr(Msg, "dxgrid"); + char * snrptr = strstr(Msg, "snr"); + float snr = 0; + char CQ[64]; + int Len; + + Msg += 26; + + if (mycall && dxcall && dxgrid) + { + mycall += 13; + strlop(mycall, '"'); + + dxcall += 13; + strlop(dxcall, '"'); + + dxgrid += 9; + strlop(dxgrid, '"'); + } + + + if (dxcall && strstr(dxcall, "-0")) + strlop(dxcall, '-'); + + if (snrptr) + snr = atof(&snrptr[6]); + + if (memcmp(Msg, "\"beacon\":\"received\"", 18) == 0) + { + if (mycall && dxcall && dxgrid) + { + Len = sprintf(CQ, "Beacon received from %s SNR %3.1f", dxcall, snr); + WritetoTrace(TNC, CQ, Len); + + // Add to MH + + if (dxcall) + UpdateMH(TNC, dxcall, '!', 'I'); + + return; + } + } + + if (memcmp(Msg, "\"cq\":\"received\"", 14) == 0) + { + if (mycall && dxcall && dxgrid) + { + Len = sprintf(CQ, "CQ received from %s", dxcall); + WritetoTrace(TNC, CQ, Len); + + // Add to MH + + if (dxcall) + UpdateMH(TNC, dxcall, '!', 'I'); + + return; + } + } + + if (memcmp(Msg, "\"ping\":\"received\"", 16) == 0) + { + if (mycall && dxcall && dxgrid) + { + Len = sprintf(CQ, "PING received from %s SNR %3.1f", dxcall, snr); + WritetoTrace(TNC, CQ, Len); + + // Add to MH + + if (dxcall) + UpdateMH(TNC, dxcall, '!', 'I'); + + return; + } + } + + if (memcmp(Msg, "\"ping\":\"acknowledge\"", 16) == 0) + { + if (mycall && dxcall && dxgrid) + { + char Msg[128]; + + Len = sprintf(Msg, "Ping Response from %s SNR %3.1f\r", dxcall, snr); + FreeDataProcessTNCMessage(TNC, dxcall, Msg, Len); + + UpdateMH(TNC, dxcall, '!', 'I'); + + return; + } + } + + + + + + + Debugprintf("%d %s", TNC->Port, Msg); + return; + } + + if (memcmp(Msg, "{\"command\":\"rx_buffer\"", 22) == 0) + { + char * Next, * This; + + // Delete from TNC + + SendTNCCommand(TNC, "set", "del_rx_buffer"); Msg += 22; + + ptr = getJSONValue(Msg, "\"eof\""); + ptr = getJSONValue(Msg, "\"data-array\""); + + This = ptr; + + if (This[1] == '{') // Array of objects + { + This++; + do + { + Next = getObjectFromArray(This); + ProcessMessageObject(TNC, This); + This = Next; + + } while (Next && Next[0] == '{'); + } + + return; + } + + Debugprintf("%d %s", TNC->Port, Msg); + + +// {"COMMAND":"RX_BUFFER","DATA-ARRAY":[],"EOF":"EOF"} +/* {"COMMAND":"RX_BUFFER","DATA-ARRAY":[{"DXCALLSIGN":"GM8BPQ","DXGRID":"","TIMESTAMP":1642579504, +"RXDATA":[{"dt":"f","fn":"main.js","ft":"text\/javascript" +,"d":"data:text\/javascript;base64,Y29uc3Qge.....9KTsK","crc":"123123123"}]}],"EOF":"EOF"} + + + +{"arq":"received","uuid":"a1346319-6eb0-42aa-b5a0-c9493c8ccdca","timestamp":1645812393,"dxcallsign":"G8BPQ-2","dxgrid":"","data":"QyBHOEJQUS0yIEc4QlBRLTIgRzhCUFEtMiA="} +{"ptt":"True"} + + +*/ + if (memcmp(Msg, "{\"arq\":\"received\"", 17) == 0) + { + int NewLen; + char * ptr, *ptr2, *Type; + char * Call = 0; + char * myCall = 0; + + Msg += 17; + + ptr = getJSONValue(Msg, "\"data\""); + Type = ++ptr; + + // Decode Base64 + + // FreeData replaces / with \/ so need to undo + + ptr2 = strstr(Type, "\\/"); + + while (ptr2) + { + memmove(ptr2, ptr2 + 1, strlen(ptr2)); + ptr2 = strstr(ptr2, "\\/"); + } + + Len = strlen(Type) - 1; + + // Debugprintf("RX %d %s %d", TNC->Port, FN, Len); + + ptr = ptr2 = Type; + + while (Len > 0) + { + xdecodeblock(ptr, ptr2); + ptr += 4; + ptr2 += 3; + Len -= 4; + } + + NewLen = (int)(ptr2 - Type); + + if (*(ptr-1) == '=') + NewLen--; + + if (*(ptr-2) == '=') + NewLen--; + + Type[NewLen] = 0; + + myCall = getJSONValue(Msg, "\"mycallsign\""); + Call = getJSONValue(Msg, "\"dxcallsign\""); + + if (Call) + { + Call++; + strlop(Call, '"'); + } + + if (myCall) + { + myCall++; + strlop(myCall, '"'); + } + + + FreeDataProcessTNCMessage(TNC, Call, Type, NewLen); + + return; + } + + if (memcmp(Msg, "{\"COMMAND\":\"RX_MSG_BUFFER\"", 26) == 0) + { + char * Next, * This; + + Msg += 26; + ptr = getJSONValue(Msg, "\"EOF\""); + ptr = getJSONValue(Msg, "\"DATA-ARRAY\""); + + This = ptr; + + if (This[1] == '{') // Array of objects + { + This++; + do { + Next = getObjectFromArray(This); + ProcessMessageObject(TNC, This); + This = Next; + } while (Next && Next[0] == '{'); + + // Delete from TNC + + SendTNCCommand(TNC, "SET", "DEL_RX_MSG_BUFFER"); + } + + + return; + } +} + +int FreeDataConnect(struct TNCINFO * TNC, char * Call) +{ + char Connect[] = "{\"type\" : \"arq\", \"command\": \"connect\" , \"dxcallsign\": \"%s\"}\n"; + char Msg[128]; + int Len; + + Len = sprintf(Msg, Connect, Call); + + return send(TNC->TCPDataSock, Msg, Len, 0); +} + +int FreeDataDisconnect(struct TNCINFO * TNC) +{ + char Disconnect[] = "{\"type\" : \"arq\", \"command\": \"disconnect\"}\n"; + char Msg[128]; + int Len; + +// return FreeDataSendCommand(TNC, "D"); + + Len = sprintf(Msg, Disconnect); + + return send(TNC->TCPDataSock, Msg, Len, 0); +} + + +int FreeGetData(struct TNCINFO * TNC) +{ + char GetData[] = "{\"type\" : \"get\", \"command\": \"rx_buffer\"}\n"; + char Msg[128]; + int Len; + + Len = sprintf(Msg, GetData); + + return send(TNC->TCPDataSock, Msg, Len, 0); +} + +int FreeDataSendCommand(struct TNCINFO * TNC, char * Msg) +{ + // Commands are simulated as Messages to the remote BPQ. The TNC itself does not handle any commands + + // First Byte of MSG is a Type - Command or Data. MSG has a limited character set Use =xx for Now. + + // Current Types - C = Connect, D = Disconnect, I = info + + SendAsRaw(TNC, TNC->FreeDataInfo->farCall, TNC->FreeDataInfo->ourCall, Msg, strlen(Msg)); + return 0; +} + +void FreeDataProcessTNCMsg(struct TNCINFO * TNC) +{ + int DataInputLen, MsgLen; + char * ptr, * endptr; + int maxlen; + + // May get message split over packets or multiple messages per packet + + // A complete file transfer arrives as one message, so can bw very long + + + if (TNC->DataInputLen > MAXRXSIZE) // Shouldnt have packets longer than this + TNC->DataInputLen=0; + + maxlen = MAXRXSIZE - TNC->DataInputLen; + + if (maxlen >1400) + maxlen = 1400; + + DataInputLen = recv(TNC->TCPDataSock, &TNC->ARDOPDataBuffer[TNC->DataInputLen], maxlen, 0); + + if (DataInputLen == 0 || DataInputLen == SOCKET_ERROR) + { + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + closesocket(TNC->TCPDataSock); + + TNC->TCPDataSock = 0; + + TNC->TNCCONNECTED = FALSE; + + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + STREAM->Disconnecting = FALSE; + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + return; + } + + TNC->DataInputLen += DataInputLen; + + TNC->ARDOPDataBuffer[TNC->DataInputLen] = 0; // So we can use string functions + + // Message should be json. We know the format, so don't need a general parser, but need to know if complete. + // I think counting { and } and stopping if equal should work; + +// Debugprintf(TNC->ARDOPDataBuffer); + + //I think now messages end with LF + +loop: + + endptr = strchr(TNC->ARDOPDataBuffer, 10); + + if (endptr == 0) + return; + + *(endptr) = 0; + + if (TNC->ARDOPDataBuffer[0] != '{') + { + TNC->DataInputLen = 0; + return; + } + + ptr = &TNC->ARDOPDataBuffer[0]; + + MsgLen = endptr - ptr; + + ProcessTNCJSON(TNC, ptr, MsgLen); + + // MsgLen doesnt include lf + + MsgLen++; + + if (TNC->DataInputLen == MsgLen) + { + TNC->DataInputLen = 0; + return; + } + + // More in buffer + + ptr += MsgLen; + TNC->DataInputLen -= MsgLen; + + memmove(TNC->ARDOPDataBuffer, ptr, TNC->DataInputLen + 1); + + goto loop; + + // Message Incomplete - wait for rest; +} + + + +VOID FreeDataThread(void * portptr); + +int ConnecttoFreeData(int port) +{ + _beginthread(FreeDataThread, 0, (void *)(size_t)port); + + return 0; +} + +static SOCKADDR_IN sinx; +static SOCKADDR_IN rxaddr; + + +VOID FreeDataThread(void * portptr) +{ + // Messages are JSON encapulated + // Now We run tnc directly so don't open daemon socket + // Looks for data on socket(s) + + 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 Params[512]; + + if (TNC->HostName == NULL) + return; + + buildParamString(TNC, Params); + + TNC->BusyFlags = 0; + + TNC->TNCCONNECTING = TRUE; + + Sleep(3000); // Allow init to complete + +// printf("Starting FreeDATA 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->Datadestaddr.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->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->Datadestaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) + { + TNC->TNCCONNECTING = FALSE; + sprintf(Msg, "Resolve Failed for FreeData Host - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + return; // Resolve failed + } + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + + TNC->TCPDataSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPDataSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for FreeData TNC socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->TNCCONNECTING = FALSE; + return; + } + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + // Connect TNC 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 FreeData TNC 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->TCPDataSock); + TNC->TCPDataSock = 0; + TNC->TNCCONNECTING = FALSE; + + return; + } + + Sleep(1000); + + TNC->LastFreq = 0; + + TNC->TNCCONNECTING = FALSE; + TNC->TNCCONNECTED = TRUE; + TNC->BusyFlags = 0; + TNC->InputLen = 0; + TNC->Alerted = FALSE; + + TNC->Alerted = TRUE; + + sprintf(TNC->WEB_COMMSSTATE, "Connected to FreeData TNC"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + sprintf(Msg, "Connected to FreeData 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->TNCCONNECTED) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + if (TNC->TNCCONNECTED) + FD_SET(TNC->TCPDataSock,&readfs); + + if (TNC->TNCCONNECTING || TNC->TNCCONNECTED) FD_SET(TNC->TCPDataSock,&errorfs); + + timeout.tv_sec = 300; + timeout.tv_usec = 0; + + ret = select((int)TNC->TCPDataSock + 1, &readfs, NULL, &errorfs, &timeout); + + if (ret == SOCKET_ERROR) + { + Debugprintf("FreeData Select failed %d ", WSAGetLastError()); + goto Lost; + } + + // If nothing doing send get rx_buffer as link validation poll + + if (ret == 0) + { + char GetData[] = "{\"type\" : \"get\", \"command\": \"rx_buffer\"}\n"; + int Len; + + Len = send(TNC->TCPDataSock, GetData, strlen(GetData), 0); + + if (Len != strlen(GetData)) + goto closeThread; + } + else + { + // See what happened + + if (FD_ISSET(TNC->TCPDataSock, &readfs)) + { + GetSemaphore(&Semaphore, 52); + FreeDataProcessTNCMsg(TNC); + FreeSemaphore(&Semaphore); + } + + if (FD_ISSET(TNC->TCPDataSock, &errorfs)) + { +Lost: + sprintf(Msg, "FreeData TNC 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->TNCCONNECTED = 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->TCPDataSock); + TNC->TCPDataSock = 0; + } + + continue; + } + } + +closeThread: + + if (TNC->TCPDataSock) + { + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPDataSock); + } + + sprintf(Msg, "FreeData Thread Terminated Port %d\r\n", TNC->Port); + WritetoConsole(Msg); +} + +void ConnectTNCPort(struct TNCINFO * TNC) +{ + char Msg[255]; + int err; + int bcopt = TRUE; + + TNC->TNCCONNECTING = TRUE; + + TNC->TCPDataSock = socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPDataSock == INVALID_SOCKET) + { + sprintf(Msg, "Socket Failed for FreeData TNC socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->TNCCONNECTING = FALSE; + return; + } + + setsockopt(TNC->TCPDataSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + setsockopt(TNC->TCPDataSock, IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + if (connect(TNC->TCPDataSock,(LPSOCKADDR) &TNC->Datadestaddr,sizeof(TNC->Datadestaddr)) == 0) + { + // Connected successful + + sprintf(TNC->WEB_COMMSSTATE, "Connected to FreeData TNC"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + TNC->TNCCONNECTING = FALSE; + TNC->TNCCONNECTED = TRUE; + TNC->Alerted = FALSE; + return; + } + + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + sprintf(Msg, "Connect Failed for FreeData TNC socket - error code = %d Port %d\n", + err, htons(TNC->Datadestaddr.sin_port)); + + WritetoConsole(Msg); + TNC->Alerted = TRUE; + TNC->TNCCONNECTING = FALSE; + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + } + + closesocket(TNC->TCPDataSock); + + TNC->TCPDataSock = 0; + TNC->TNCCONNECTING = FALSE; + return; +} + +void buildParamString(struct TNCINFO * TNC, char * line) +{ + // choices=[, "direct", "rigctl", "rigctld"], + + struct FreeDataINFO * FDI = TNC->FreeDataInfo; + int capindex = -1, playindex = -1; + int i; + + // Python adds sound mapper on front and counts Playback after Capture + + for (i = 0; i < CaptureCount; i++) + { + if (strstr(&CaptureNames[i][0], TNC->FreeDataInfo->Capture)) + { + capindex = i + 1; + break; + } + } + + for (i = 0; i < PlaybackCount; i++) + { + if (strstr(&PlaybackNames[i][0], TNC->FreeDataInfo->Playback)) + { + playindex = i + CaptureCount + 2; + break; + } + } + + sprintf(line, + "--mycall %s --ssid %s --mygrid %s --rx %d --tx %d --port %d --radiocontrol %s " + "--tuning_range_fmin %3.1f --tuning_range_fmax %3.1f --tx-audio-level %d", + + FDI->ourCall, FDI->SSIDList, LOC, capindex, playindex, TNC->TCPPort, FDI->hamlibHost ? "rigctld" : "disabled", + FDI->TuningRange * -1.0, FDI->TuningRange * 1.0, FDI->TXLevel); + + if (FDI->hamlibHost) + sprintf(line, "%s --rigctld_ip %s --rigctld_port %d", line, FDI->hamlibHost, FDI->hamlibPort); + + if (FDI->LimitBandWidth) + strcat(line, " --500hz"); + + if (FDI->Explorer) + strcat(line, " --explorer"); + + + // Add these to the end if needed + // --scatter + // --fft + // --fsk + // --qrv (respond to cq) + +} + +#ifndef WIN32 + +#include + +char ** WriteDevices = NULL; +int WriteDeviceCount = 0; + +char ** ReadDevices = NULL; +int ReadDeviceCount = 0; + + +int GetOutputDeviceCollection() +{ + // Get all the suitable devices and put in a list for GetNext to return + + snd_ctl_t *handle= NULL; + snd_pcm_t *pcm= NULL; + snd_ctl_card_info_t *info; + snd_pcm_info_t *pcminfo; + snd_pcm_hw_params_t *pars; + snd_pcm_format_mask_t *fmask; + char NameString[256]; + + Debugprintf("Playback Devices\n"); + + // free old struct if called again + +// while (WriteDeviceCount) +// { +// WriteDeviceCount--; +// free(WriteDevices[WriteDeviceCount]); +// } + +// if (WriteDevices) +// free(WriteDevices); + + WriteDevices = NULL; + WriteDeviceCount = 0; + + // Add virtual device ARDOP so ALSA plugins can be used if needed + + WriteDevices = realloc(WriteDevices,(WriteDeviceCount + 1) * sizeof(WriteDevices)); + + // Get Device List from ALSA + + snd_ctl_card_info_alloca(&info); + snd_pcm_info_alloca(&pcminfo); + snd_pcm_hw_params_alloca(&pars); + snd_pcm_format_mask_alloca(&fmask); + + char hwdev[80]; + unsigned min, max, ratemin, ratemax; + int card, err, dev, nsubd; + snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK; + + card = -1; + + if (snd_card_next(&card) < 0) + { + Debugprintf("No Devices"); + return 0; + } + + while (card >= 0) + { + sprintf(hwdev, "hw:%d", card); + err = snd_ctl_open(&handle, hwdev, 0); + err = snd_ctl_card_info(handle, info); + + Debugprintf("Card %d, ID `%s', name `%s'", card, snd_ctl_card_info_get_id(info), + snd_ctl_card_info_get_name(info)); + + + dev = -1; + + if(snd_ctl_pcm_next_device(handle, &dev) < 0) + { + // Card has no devices + + snd_ctl_close(handle); + + WriteDevices = realloc(WriteDevices,(WriteDeviceCount + 1) * sizeof(WriteDevices)); + WriteDevices[WriteDeviceCount++] = strupr(strdup("DummyDevice")); + + goto nextcard; + } + + while (dev >= 0) + { + snd_pcm_info_set_device(pcminfo, dev); + snd_pcm_info_set_subdevice(pcminfo, 0); + snd_pcm_info_set_stream(pcminfo, stream); + + err = snd_ctl_pcm_info(handle, pcminfo); + + + if (err == -ENOENT) + goto nextdevice; + + nsubd = snd_pcm_info_get_subdevices_count(pcminfo); + + Debugprintf(" Device hw:%d,%d ID `%s', name `%s', %d subdevices (%d available)", + card, dev, snd_pcm_info_get_id(pcminfo), snd_pcm_info_get_name(pcminfo), + nsubd, snd_pcm_info_get_subdevices_avail(pcminfo)); + + sprintf(hwdev, "hw:%d,%d", card, dev); + + err = snd_pcm_open(&pcm, hwdev, stream, SND_PCM_NONBLOCK); + + if (err) + { + Debugprintf("Error %d opening output device", err); + goto nextdevice; + } + + // Get parameters for this device + + err = snd_pcm_hw_params_any(pcm, pars); + + snd_pcm_hw_params_get_channels_min(pars, &min); + snd_pcm_hw_params_get_channels_max(pars, &max); + + snd_pcm_hw_params_get_rate_min(pars, &ratemin, NULL); + snd_pcm_hw_params_get_rate_max(pars, &ratemax, NULL); + + if( min == max ) + if( min == 1 ) + Debugprintf(" 1 channel, sampling rate %u..%u Hz", ratemin, ratemax); + else + Debugprintf(" %d channels, sampling rate %u..%u Hz", min, ratemin, ratemax); + else + Debugprintf(" %u..%u channels, sampling rate %u..%u Hz", min, max, ratemin, ratemax); + + // Add device to list + + sprintf(NameString, "hw:%d,%d %s(%s)", card, dev, + snd_pcm_info_get_name(pcminfo), snd_ctl_card_info_get_name(info)); + + WriteDevices = realloc(WriteDevices,(WriteDeviceCount + 1) * sizeof(WriteDevices)); + WriteDevices[WriteDeviceCount++] = strupr(strdup(NameString)); + + snd_pcm_close(pcm); + pcm= NULL; + +nextdevice: + if (snd_ctl_pcm_next_device(handle, &dev) < 0) + break; + } + snd_ctl_close(handle); + +nextcard: + + Debugprintf(""); + + if (snd_card_next(&card) < 0) // No more cards + break; + } + + return WriteDeviceCount; +} + +int GetNextOutputDevice(char * dest, int max, int n) +{ + if (n >= WriteDeviceCount) + return 0; + + strcpy(dest, WriteDevices[n]); + return strlen(dest); +} + + +int GetInputDeviceCollection() +{ + // Get all the suitable devices and put in a list for GetNext to return + + snd_ctl_t *handle= NULL; + snd_pcm_t *pcm= NULL; + snd_ctl_card_info_t *info; + snd_pcm_info_t *pcminfo; + snd_pcm_hw_params_t *pars; + snd_pcm_format_mask_t *fmask; + char NameString[256]; + + Debugprintf("Capture Devices\n"); + + ReadDevices = NULL; + ReadDeviceCount = 0; + + ReadDevices = realloc(ReadDevices,(ReadDeviceCount + 1) * sizeof(ReadDevices)); + + // Get Device List from ALSA + + snd_ctl_card_info_alloca(&info); + snd_pcm_info_alloca(&pcminfo); + snd_pcm_hw_params_alloca(&pars); + snd_pcm_format_mask_alloca(&fmask); + + char hwdev[80]; + unsigned min, max, ratemin, ratemax; + int card, err, dev, nsubd; + snd_pcm_stream_t stream = SND_PCM_STREAM_CAPTURE; + + card = -1; + + if(snd_card_next(&card) < 0) + { + Debugprintf("No Devices"); + return 0; + } + + while(card >= 0) + { + sprintf(hwdev, "hw:%d", card); + err = snd_ctl_open(&handle, hwdev, 0); + err = snd_ctl_card_info(handle, info); + +// Debugprintf("Card %d, ID `%s', name `%s'", card, snd_ctl_card_info_get_id(info), +// snd_ctl_card_info_get_name(info)); + + dev = -1; + + if (snd_ctl_pcm_next_device(handle, &dev) < 0) // No Devicdes + { + snd_ctl_close(handle); + + ReadDevices = realloc(ReadDevices,(ReadDeviceCount + 1) * sizeof(ReadDevices)); + ReadDevices[ReadDeviceCount++] = strupr(strdup("DummyDevice")); + + Debugprintf("%d %s", ReadDeviceCount, "DummyDevice"); + goto nextcard; + } + + while(dev >= 0) + { + snd_pcm_info_set_device(pcminfo, dev); + snd_pcm_info_set_subdevice(pcminfo, 0); + snd_pcm_info_set_stream(pcminfo, stream); + err= snd_ctl_pcm_info(handle, pcminfo); + + if (err == -ENOENT) + goto nextdevice; + + nsubd= snd_pcm_info_get_subdevices_count(pcminfo); +// Debugprintf(" Device hw:%d,%d ID `%s', name `%s', %d subdevices (%d available)", +// card, dev, snd_pcm_info_get_id(pcminfo), snd_pcm_info_get_name(pcminfo), +// nsubd, snd_pcm_info_get_subdevices_avail(pcminfo)); + + sprintf(hwdev, "hw:%d,%d", card, dev); +/* + err = snd_pcm_open(&pcm, hwdev, stream, SND_PCM_NONBLOCK); + + if (err) + { + Debugprintf("Error %d opening input device", err); + goto nextdevice; + } + + err = snd_pcm_hw_params_any(pcm, pars); + + snd_pcm_hw_params_get_channels_min(pars, &min); + snd_pcm_hw_params_get_channels_max(pars, &max); + snd_pcm_hw_params_get_rate_min(pars, &ratemin, NULL); + snd_pcm_hw_params_get_rate_max(pars, &ratemax, NULL); + + if( min == max ) + if( min == 1 ) + Debugprintf(" 1 channel, sampling rate %u..%u Hz", ratemin, ratemax); + else + Debugprintf(" %d channels, sampling rate %u..%u Hz", min, ratemin, ratemax); + else + Debugprintf(" %u..%u channels, sampling rate %u..%u Hz", min, max, ratemin, ratemax); +*/ + sprintf(NameString, "hw:%d,%d %s(%s)", card, dev, + snd_pcm_info_get_name(pcminfo), snd_ctl_card_info_get_name(info)); + + Debugprintf("%d %s", ReadDeviceCount, NameString); + + ReadDevices = realloc(ReadDevices,(ReadDeviceCount + 1) * sizeof(ReadDevices)); + ReadDevices[ReadDeviceCount++] = strupr(strdup(NameString)); + +nextdevice: + if (snd_ctl_pcm_next_device(handle, &dev) < 0) + break; + } + snd_ctl_close(handle); +nextcard: + + Debugprintf(""); + if (snd_card_next(&card) < 0 ) + break; + } + return ReadDeviceCount; +} + +int GetNextInputDevice(char * dest, int max, int n) +{ + if (n >= ReadDeviceCount) + return 0; + + strcpy(dest, ReadDevices[n]); + return strlen(dest); +} + +#endif + + +// We need a local restart tnc as we need to add params and start a python progrm on Linux + +BOOL KillOldTNC(char * Path); + +static BOOL RestartTNC(struct TNCINFO * TNC) +{ + if (TNC->ProgramPath == NULL || TNC->DontRestart) + return 0; + + if (_memicmp(TNC->ProgramPath, "REMOTE:", 7) == 0) + { + int n; + + // Try to start TNC on a remote host + + SOCKET sock = socket(AF_INET,SOCK_DGRAM,0); + struct sockaddr_in destaddr; + + Debugprintf("trying to restart TNC %s", TNC->ProgramPath); + + if (sock == INVALID_SOCKET) + return 0; + + destaddr.sin_family = AF_INET; + destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + destaddr.sin_port = htons(8500); + + if (destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + struct hostent * HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) + return 0; // Resolve failed + + memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + + n = sendto(sock, TNC->ProgramPath, (int)strlen(TNC->ProgramPath), 0, (struct sockaddr *)&destaddr, sizeof(destaddr)); + + Debugprintf("Restart TNC - sendto returned %d", n); + + Sleep(100); + closesocket(sock); + + return 1; // Cant tell if it worked, but assume ok + } + + // Not Remote + + // Add parameters to command string + +#ifndef WIN32 + { + char * arg_list[64]; + char rxVal[16]; + char txVal[16]; + char tunePlus[16]; + char tuneMinus[16]; + char portVal[16]; + char txLevelVal[16]; + char RigPort[16]; + int n = 0; + + pid_t child_pid; + + struct FreeDataINFO * FDI = TNC->FreeDataInfo; + int capindex = -1, playindex = -1; + int i; + + if (ReadDeviceCount == 0) + { + GetOutputDeviceCollection(); + GetInputDeviceCollection(); + } + + // + + for (i = 0; i < ReadDeviceCount; i++) + { + printf("%s %s\n", &ReadDevices[i][0], TNC->FreeDataInfo->Capture); + if (strstr(&ReadDevices[i][0], TNC->FreeDataInfo->Capture)) + { + capindex = i; + break; + } + } + + + for (i = 0; i < WriteDeviceCount; i++) + { + printf("%s %s\n", &WriteDevices[i][0], TNC->FreeDataInfo->Playback); + + if (strstr(&WriteDevices[i][0], TNC->FreeDataInfo->Playback)) + { + playindex = i; + break; + } + } + + + capindex=playindex=3; + + printf("%d %d \n", capindex, playindex); + + signal(SIGCHLD, SIG_IGN); // Silently (and portably) reap children. + + arg_list[n++] = "python3"; + arg_list[n++] = TNC->ProgramPath; + arg_list[n++] = "--mycall"; + arg_list[n++] = FDI->ourCall; + arg_list[n++] = "--ssid"; + arg_list[n++] = FDI->SSIDList; + arg_list[n++] = "--mygrid"; + arg_list[n++] = LOC; + arg_list[n++] = "--rx"; + sprintf(rxVal, "%d", capindex); + arg_list[n++] = rxVal; + arg_list[n++] = "--tx"; + sprintf(txVal, "%d", playindex); + arg_list[n++] = txVal; + arg_list[n++] = "--port"; + sprintf(portVal, "%d", TNC->TCPPort); + arg_list[n++] = portVal; + arg_list[n++] = "--radiocontrol"; + arg_list[n++] = FDI->hamlibHost ? "rigctld" : "disabled"; + arg_list[n++] = "--tuning_range_fmin"; + sprintf(tuneMinus, "%3.1f", FDI->TuningRange * -1.0); + arg_list[n++] = tuneMinus; + arg_list[n++] = "--tuning_range_fmax"; + sprintf(tunePlus, "%3.1f", FDI->TuningRange * 1.0); + arg_list[n++] = tunePlus; + arg_list[n++] = "--tx-audio-level"; + sprintf(txLevelVal, "%d", FDI->TXLevel); + arg_list[n++] = txLevelVal; + + if (FDI->hamlibHost) + { + arg_list[n++] = "--rigctld_ip"; + arg_list[n++] = FDI->hamlibHost; + arg_list[n++] = "--rigctld_port"; + sprintf(RigPort, "%d", FDI->hamlibPort); + arg_list[n++] = RigPort; + } + + if (FDI->LimitBandWidth) + arg_list[n++] = "--500hz"; + + if (FDI->Explorer) + arg_list[n++] = "--explorer"; + + arg_list[n++] = 0; + + n = 0; + + // Fork and Exec TNC + + printf("Trying to start %s\n", TNC->ProgramPath); + + /* Duplicate this process. */ + + child_pid = fork (); + + if (child_pid == -1) + { + printf ("StartTNC fork() Failed\n"); + return 0; + } + + if (child_pid == 0) + { + execvp (arg_list[0], arg_list); + + /* The execvp function returns only if an error occurs. */ + + printf ("Failed to start TNC\n"); + exit(0); // Kill the new process + } + printf("Started TNC\n"); + sleep(5000); + + return TRUE; + } +#else + + { + int n = 0; + + STARTUPINFO SInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION + char workingDirectory[256]; + char commandLine[512]; + char Params[512]; + + int i = strlen(TNC->ProgramPath); + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + Debugprintf("RestartTNC Called for %s", TNC->ProgramPath); + + + strcpy(workingDirectory, TNC->ProgramPath); + + while (i--) + { + if (workingDirectory[i] == '\\' || workingDirectory[i] == '/') + { + workingDirectory[i] = 0; + break; + } + } + + buildParamString(TNC, Params); + + while (KillOldTNC(TNC->ProgramPath) && n++ < 100) + { + Sleep(100); + } + + sprintf(commandLine, "\"%s\" %s", TNC->ProgramPath, Params); + + if (CreateProcess(NULL, commandLine, NULL, NULL, FALSE,0, NULL, workingDirectory, &SInfo, &PInfo)) + { + Debugprintf("Restart TNC OK"); + TNC->PID = PInfo.dwProcessId; + return TRUE; + } + else + { + Debugprintf("Restart TNC Failed %d ", GetLastError()); + return FALSE; + } + } +#endif + return 0; +} diff --git a/FreeDATA.c b/FreeDATA.c index ad09a4e..7bd4afc 100644 --- a/FreeDATA.c +++ b/FreeDATA.c @@ -41,7 +41,7 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses #define FREEDATABUFLEN 16384 // TCP buffer size int KillTNC(struct TNCINFO * TNC); -int RestartTNC(struct TNCINFO * TNC); +static int RestartTNC(struct TNCINFO * TNC); int (WINAPI FAR *GetModuleFileNameExPtr)(); extern int (WINAPI FAR *EnumProcessesPtr)(); @@ -72,6 +72,10 @@ int FreeDataConnect(struct TNCINFO * TNC, char * Call); int FreeDataDisconnect(struct TNCINFO * TNC); int FreeGetData(struct TNCINFO * TNC); static void SendBeacon(struct TNCINFO * TNC, int Interval); +void buildParamString(struct TNCINFO * TNC, char * line); +VOID FreeDataSuspendPort(struct TNCINFO * TNC); +VOID FreeDataReleasePort(struct TNCINFO * TNC); + static char ClassName[]="FREEDATASTATUS"; static char WindowTitle[] = "FreeData Modem"; @@ -79,6 +83,7 @@ static int RigControlRow = 205; #ifndef WIN32 #include +#define MAXPNAMELEN 32 #else #include #endif @@ -96,6 +101,39 @@ VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); #define MAXRXSIZE 512000 // Sets max size for file transfer (less base64 overhead +int CaptureCount = 0; +int PlaybackCount = 0; + +int CaptureIndex = -1; // Card number +int PlayBackIndex = -1; + + + +char CaptureNames[16][MAXPNAMELEN + 2] = { "" }; +char PlaybackNames[16][MAXPNAMELEN + 2] = { "" }; + + +#ifdef WIN32 + +#include + +#pragma comment(lib, "winmm.lib") + +WAVEFORMATEX wfx = { WAVE_FORMAT_PCM, 1, 12000, 24000, 2, 16, 0 }; + +WAVEOUTCAPS pwoc; +WAVEINCAPS pwic; + + +char * CaptureDevices = NULL; +char * PlaybackDevices = NULL; + +HWAVEOUT hWaveOut = 0; +HWAVEIN hWaveIn = 0; + +#endif + + char * gen_uuid() { char v[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; @@ -163,6 +201,8 @@ static int ProcessLine(char * buf, int Port) TNC->InitScript = malloc(1000); TNC->InitScript[0] = 0; + + TNC->MaxConReq = 10; // Default if (p_ipad == NULL) p_ipad = strtok(NULL, " \t\n\r"); @@ -177,10 +217,8 @@ static int ProcessLine(char * buf, int Port) TNC->TCPPort = WINMORport; - 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->Datadestaddr.sin_port = htons(WINMORport); TNC->HostName = malloc(strlen(p_ipad)+1); @@ -234,12 +272,12 @@ static int ProcessLine(char * buf, int Port) if (_memicmp(buf, "CAPTURE", 7) == 0) { - TNC->FreeDataInfo->Capture = _strdup(&buf[8]); + TNC->FreeDataInfo->Capture = _strupr(_strdup(&buf[8])); strlop(TNC->FreeDataInfo->Capture, 13); } else if (_memicmp(buf, "PLAYBACK", 8) == 0) { - TNC->FreeDataInfo->Playback = _strdup(&buf[9]); + TNC->FreeDataInfo->Playback = _strupr(_strdup(&buf[9])); strlop(TNC->FreeDataInfo->Playback, 13); } @@ -255,6 +293,13 @@ static int ProcessLine(char * buf, int Port) else if (_memicmp(buf, "TuningRange", 11) == 0) TNC->FreeDataInfo->TuningRange = atoi(&buf[12]); + else if (_memicmp(buf, "TXLevel", 6) == 0) + TNC->FreeDataInfo->TXLevel = atoi(&buf[7]); + + else if (_memicmp(buf, "Explorer", 8) == 0) + TNC->FreeDataInfo->Explorer = atoi(&buf[9]); + + else if (_memicmp(buf, "LimitBandWidth", 14) == 0) TNC->FreeDataInfo->LimitBandWidth = atoi(&buf[15]); @@ -428,12 +473,12 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) // FreeDataCheckRX(TNC); - if (TNC->DAEMONCONNECTED == FALSE && TNC->DAEMONCONNECTING == FALSE) + if (TNC->TNCCONNECTED == FALSE && TNC->TNCCONNECTING == FALSE) { // See if time to reconnect time(<ime); - if (ltime - TNC->lasttime > 9 ) + if (ltime - TNC->lasttime > 19 ) { TNC->lasttime = ltime; ConnecttoFreeData(port); @@ -551,14 +596,17 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, STREAM->MyCall); TNC->Streams[Stream].MyCall[calllen] = 0; - - - FreeDataChangeMYC(TNC, TNC->Streams[0].MyCall); - + // Stop other ports in same group SuspendOtherPorts(TNC); - + + // Stop Listening, and set MYCALL to user's call + + FreeDataSuspendPort(TNC); + FreeDataChangeMYC(TNC, TNC->Streams[0].MyCall); + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); @@ -788,7 +836,43 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) } return 0; + } + if (_memicmp(&buff->L2DATA[0], "TXLEVEL ", 8) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + int Level = atoi(&buff->L2DATA[8]); + char TXL[] = "{\"type\" : \"set\", \"command\" : \"tx_audio_level\", \"value\": \"%d\"}\n"; + char Message[256]; + int Len, ret; + + Len = sprintf(Message, TXL, Level); + ret = send(TNC->TCPDataSock, (char *)&Message, Len, 0); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} TXLevel Set\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 0; + } + + if (_memicmp(&buff->L2DATA[0], "SENDTEST", 8) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + char TXF[] = "{\"type\" : \"set\", \"command\" : \"send_test_frame\"}\n"; + char Message[256]; + int Len, ret; + + Len = sprintf(Message, TXF); + ret = send(TNC->TCPDataSock, (char *)&Message, Len, 0); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} Test Frame Requested\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 0; } if (_memicmp(&buff->L2DATA[0], "CHAT ", 5) == 0) @@ -830,16 +914,13 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) } - - // See if a Connect Command. if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect { char Connect[80]; char loppedCall[10]; - char toCall[10]; - + char * ptr = strchr(&buff->L2DATA[2], 13); if (ptr) @@ -945,12 +1026,9 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) closesocket(TNC->TCPDataSock); } - if (TNC->TCPSock) - { - shutdown(TNC->TCPSock, SD_BOTH); - Sleep(100); - closesocket(TNC->TCPSock); - } + if (TNC->WeStartedTNC) + KillTNC(TNC); + return 0; @@ -971,7 +1049,7 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) if (TNC->ConnectPending == 0 && TNC->PTTState == 0) { - TNC->FreeDataInfo->CONOK = 0; + FreeDataSuspendPort(TNC); TNC->GavePermission = TRUE; return 0; // OK to Change } @@ -988,8 +1066,7 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) { TNC->GavePermission = FALSE; if (TNC->ARDOPCurrentMode[0] != 'S') // Skip - TNC->FreeDataInfo->CONOK = 1; - + FreeDataReleasePort(TNC); } return 0; } @@ -1016,18 +1093,21 @@ VOID FreeDataReleaseTNC(struct TNCINFO * TNC) Rig_Command(-1, TXMsg); + FreeDataReleasePort(TNC); ReleaseOtherPorts(TNC); } VOID FreeDataSuspendPort(struct TNCINFO * TNC) { - TNC->FreeDataInfo->CONOK = 0; +// char CMD[] = "{\"type\" : \"set\", \"command\" : \"listen\", \"state\": \"False\"}\n"; +// send(TNC->TCPDataSock, CMD, strlen(CMD), 0); } VOID FreeDataReleasePort(struct TNCINFO * TNC) { - TNC->FreeDataInfo->CONOK = 1; + char CMD[] = "{\"type\" : \"set\", \"command\" : \"listen\", \"state\": \"True\"}\n"; + send(TNC->TCPDataSock, CMD, strlen(CMD), 0); } @@ -1296,6 +1376,7 @@ VOID * FreeDataExtInit(EXTPORTDATA * PortEntry) char * TempScript; u_long param = 1; int line; + int i; srand(time(NULL)); @@ -1318,6 +1399,12 @@ VOID * FreeDataExtInit(EXTPORTDATA * PortEntry) if (TNC->FreeDataInfo->TuningRange == 0) TNC->FreeDataInfo->TuningRange = 50; + if (TNC->FreeDataInfo->TXLevel == 0) + TNC->FreeDataInfo->TXLevel = 50; + + if (TNC->AutoStartDelay == 0) + TNC->AutoStartDelay = 2000; + #ifndef LINBPQ if (bgBrush == NULL) @@ -1335,6 +1422,8 @@ VOID * FreeDataExtInit(EXTPORTDATA * PortEntry) TNC->Port = port; TNC->Hardware = H_FREEDATA; + TNC->WeStartedTNC = 1; + TNC->ARDOPDataBuffer = malloc(MAXRXSIZE); TNC->ARDOPBuffer = malloc(FREEDATABUFLEN); @@ -1350,9 +1439,6 @@ VOID * FreeDataExtInit(EXTPORTDATA * PortEntry) if (TNC->FreeDataInfo->useBaseCall) strlop(TNC->FreeDataInfo->ourCall, '-'); - if (TNC->FreeDataInfo->hamlibHost == 0) - TNC->FreeDataInfo->hamlibHost = _strdup("127.0.0.1"); - if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; @@ -1406,11 +1492,56 @@ VOID * FreeDataExtInit(EXTPORTDATA * PortEntry) strcpy(TNC->CurrentMYC, TNC->NodeCall); if (TNC->WL2K == NULL) - if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Alrerady decoded + if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Already decoded TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo; PortEntry->PORTCONTROL.TNC = TNC; + // Build SSID List + + if (TNC->LISTENCALLS) + { + strcpy(TNC->FreeDataInfo->SSIDList, TNC->LISTENCALLS); + } + else + { + APPLCALLS * APPL; + char Appl[11] = ""; + char * List = TNC->FreeDataInfo->SSIDList; + char * SSIDptr; + int SSID; + int Listptr; + + // list is a set of numbers separated by spaces eg 0 2 10 + + SSIDptr = strchr(TNC->NodeCall, '-'); + if (SSIDptr) + SSID = atoi(SSIDptr + 1); + else + SSID = 0; + + Listptr = sprintf(List, "%d", SSID); + + for (i = 0; i < 32; i++) + { + APPL=&APPLCALLTABLE[i]; + + if (APPL->APPLCALL_TEXT[0] > ' ') + { + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + + SSIDptr = strchr(Appl, '-'); + if (SSIDptr) + SSID = atoi(SSIDptr + 1); + else + SSID = 0; + + Listptr += sprintf(&List[Listptr], " %d", SSID); + } + } + List[Listptr] = 0; + } + TNC->WebWindowProc = WebProc; TNC->WebWinX = 520; TNC->WebWinY = 500; @@ -1427,7 +1558,6 @@ VOID * FreeDataExtInit(EXTPORTDATA * PortEntry) TNC->WEB_MODE = zalloc(20); TNC->WEB_TRAFFIC = zalloc(100); - #ifndef LINBPQ line = 6; @@ -1443,7 +1573,7 @@ VOID * FreeDataExtInit(EXTPORTDATA * PortEntry) 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 @@ -1451,7 +1581,7 @@ VOID * FreeDataExtInit(EXTPORTDATA * PortEntry) 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,450,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); @@ -1459,15 +1589,15 @@ VOID * FreeDataExtInit(EXTPORTDATA * PortEntry) line += 22; CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,line,80,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", "Proto State", WS_CHILD | WS_VISIBLE,10,line,80,20, TNC->hDlg, NULL, hInstance, NULL); + CreateWindowEx(0, "STATIC", "Proto State", 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,116,line,374,20 , TNC->hDlg, NULL, hInstance, NULL); @@ -1479,28 +1609,79 @@ VOID * FreeDataExtInit(EXTPORTDATA * PortEntry) TNC->xIDC_RESTARTTIME = CreateWindowEx(0, "STATIC", "Never", WS_CHILD | WS_VISIBLE,250,line,200,20, TNC->hDlg, NULL, hInstance, NULL); TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | - LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, - 0,170,250,300, TNC->hDlg, NULL, hInstance, NULL); + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 0,170,250,300, TNC->hDlg, NULL, hInstance, NULL); TNC->ClientHeight = 450; TNC->ClientWidth = 500; TNC->hMenu = CreatePopupMenu(); - AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill TNC TNC"); - AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart TNC TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill Freedata TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart Freedata 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 + + strcpy(TNC->WEB_CHANSTATE, "Idle"); + SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + + // Convert sound card name to index + +#ifdef WIN32 + + if (CaptureDevices == NULL) // DOn't do it again if more that one port + { + CaptureCount = waveInGetNumDevs(); + + CaptureDevices = malloc((MAXPNAMELEN + 2) * CaptureCount); + CaptureDevices[0] = 0; + + printf("Capture Devices"); + + for (i = 0; i < CaptureCount; i++) + { + waveInOpen(&hWaveIn, i, &wfx, 0, 0, CALLBACK_NULL); //WAVE_MAPPER + waveInGetDevCaps((UINT_PTR)hWaveIn, &pwic, sizeof(WAVEINCAPS)); + + if (CaptureDevices) + strcat(CaptureDevices, ","); + strcat(CaptureDevices, pwic.szPname); + Debugprintf("%d %s", i + 1, pwic.szPname); + memcpy(&CaptureNames[i][0], pwic.szPname, MAXPNAMELEN); + _strupr(&CaptureNames[i][0]); + } + + PlaybackCount = waveOutGetNumDevs(); + + PlaybackDevices = malloc((MAXPNAMELEN + 2) * PlaybackCount); + PlaybackDevices[0] = 0; + + Debugprintf("Playback Devices"); + + for (i = 0; i < PlaybackCount; i++) + { + waveOutOpen(&hWaveOut, i, &wfx, 0, 0, CALLBACK_NULL); //WAVE_MAPPER + waveOutGetDevCaps((UINT_PTR)hWaveOut, &pwoc, sizeof(WAVEOUTCAPS)); + + if (PlaybackDevices[0]) + strcat(PlaybackDevices, ","); + strcat(PlaybackDevices, pwoc.szPname); + Debugprintf("%i %s", i + 1, pwoc.szPname); + memcpy(&PlaybackNames[i][0], pwoc.szPname, MAXPNAMELEN); + _strupr(&PlaybackNames[i][0]); + waveOutClose(hWaveOut); + } + } + #endif time(&TNC->lasttime); // Get initial time value ConnecttoFreeData(port); - TNC->FreeDataInfo->CONOK = 1; - return ExtProc; } @@ -1981,9 +2162,7 @@ VOID FreeDataProcessNewConnect(struct TNCINFO * TNC, char * fromCall, char * toC PMSGWITHLEN buffptr; struct STREAMINFO * STREAM = &TNC->Streams[0]; struct FreeDataINFO * Modem = TNC->FreeDataInfo; - char * tncCall, *Context; char * ptr; - char a, b; unsigned char axcall[7]; char AppName[13] = ""; APPLCALLS * APPL; @@ -2039,7 +2218,7 @@ VOID FreeDataProcessNewConnect(struct TNCINFO * TNC, char * fromCall, char * toC // The TNC responds to any SSID so we can use incomming call as Appl Call // No we can't - it responds but reports the configured call not the called call -/* + // See which application the connect is for for (App = 0; App < 32; App++) @@ -2077,7 +2256,7 @@ VOID FreeDataProcessNewConnect(struct TNCINFO * TNC, char * fromCall, char * toC } } -*/ + ProcessIncommingConnectEx(TNC, fromCall, 0, TRUE, TRUE); SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; @@ -2141,18 +2320,6 @@ VOID FreeDataProcessConnectAck(struct TNCINFO * TNC, char * Call, unsigned char PMSGWITHLEN buffptr; struct STREAMINFO * STREAM = &TNC->Streams[0]; struct FreeDataINFO * Modem = TNC->FreeDataInfo; - char * toCall, * fromCall, * tncCall, *Context; - char * ptr; - char a, b; - unsigned char axcall[7]; - char AppName[13] = ""; - APPLCALLS * APPL; - char * ApplPtr = APPLS; - int App; - char Appl[10]; - struct WL2KInfo * WL2K = TNC->WL2K; - TRANSPORTENTRY * SESS; - sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s", STREAM->MyCall, STREAM->RemoteCall); MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); @@ -2284,32 +2451,6 @@ static int SendTNCCommand(struct TNCINFO * TNC, char * Type, char * Command) static void SendPoll(struct TNCINFO * TNC) { - char DataMsgPoll[] = "{\"type\" : \"GET\", \"command\" : \"RX_MSG_BUFFER\"}\n"; -// char DaemonPoll[] = "{\"type\" : \"GET\", \"command\" : \"DAEMON_STATE\"}\n"; - char TNCPoll[] = "{\"type\" : \"GET\", \"command\" : \"TNC_STATE\", \"timestamp\" : 0}\n"; - - // Poll daemon rapidly until tnc is connected, then every 5 secs - - if ((TNC->PollDelay++ > 50))// || !TNC->TNCCONNECTED) && TNC->DAEMONCONNECTED) - { -// ret = send(TNC->TCPSock, (char *)&DaemonPoll, strlen(DaemonPoll), 0); - TNC->PollDelay = 0; - - } - - if (TNC->TNCCONNECTED) - { -// ret = send(TNC->TCPDataSock, (char *)&TNCPoll, strlen(TNCPoll), 0); - -// if (TNC->PollDelay & 1) -// ret = SendTNCCommand(TNC, "GET", "RX_BUFFER"); -// else if (TNC->PollDelay & 2) -// ret = SendTNCCommand(TNC, "GET", "RX_MSG_BUFFER"); -// else -// ret = send(TNC->TCPDataSock, (char *)&TNCPoll, strlen(TNCPoll), 0); - - - } return; } @@ -2462,7 +2603,6 @@ void ProcessMessageObject(struct TNCINFO * TNC, char * This) char * Call; char * LOC; - char * Type; char * Msg; int Len; char * ptr, * ptr2; @@ -2918,147 +3058,10 @@ char * getJSONValue(char * Msg, char * Key) char stopTNC[] = "{\"type\" : \"SET\", \"command\": \"STOPTNC\" , \"parameter\": \"---\" }\r"; -void ProcessDAEMONJSON(struct TNCINFO * TNC, char * Msg, int Len) -{ - char * ptr; - char * capture, * playback; - - - if (memcmp(Msg, "{\"command\":\"daemon_state\"", 25) == 0) - { -/* - {"COMMAND":"DAEMON_STATE","DAEMON_STATE":[{"STATUS":"stopped"}], - "PYTHON_VERSION":"3.9", - "HAMLIB_VERSION":"4.0", - "INPUT_DEVICES":[{"ID":5,"NAME":"pulse"},{"ID":9,"NAME":"default"}], - "OUTPUT_DEVICES":[{"ID":0,"NAME":"bcm2835 Headphones: - (hw:0,0)"},{"ID":1,"NAME":"sysdefault"},{"ID":2,"NAME":"lavrate"},{"ID":3,"NAME":"samplerate"},{"ID":4,"NAME":"speexrate"},{"ID":5,"NAME":"pulse"},{"ID":6,"NAME":"upmix"},{"ID":7,"NAME":"vdownmix"},{"ID":8,"NAME":"dmix"},{"ID":9,"NAME":"default"}], - "SERIAL_DEVICES":[{"PORT":"\/dev\/ttyAMA0","DESCRIPTION":"ttyAMA0 [1dff]"}], - "CPU":"39.6", - "RAM":"38.6", - "VERSION":"0.1-prototype"} -*/ - - Msg += 25; - - playback = getJSONValue(Msg, "\"output_devices\""); - capture = getJSONValue(Msg, "\"input_devices\""); - - if (TNC->CaptureDevices) - free(TNC->CaptureDevices); - - TNC->CaptureDevices = _strdup(capture); - - if (TNC->PlaybackDevices) - free(TNC->PlaybackDevices); - - TNC->PlaybackDevices = _strdup(playback); - - ptr = getJSONValue(Msg, "\"daemon_state\""); - - if (strstr(ptr, "stopped")) - { - TNC->FreeDataInfo->TNCRunning = 0; - // if we have Capture and Playback devices, then start the TNC, else look in message for them - - if (TNC->CaptureDevices) - { - if (TNC->FreeDataInfo->startingTNC == 0) - { - // Find Capture and Playback indices - - struct FreeDataINFO * Info = TNC->FreeDataInfo; - char * devptr = stristr(TNC->CaptureDevices, Info->Capture); - int capindex = -1, playindex = -1; - char startTNC[] = "{\"type\":\"set\",\"command\":\"start_tnc\"," - "\"parameter\":" - "[{\"mycall\":\"%s\"," - "\"mygrid\":\"%s\"," - "\"rx_audio\":\"%d\"," - "\"tx_audio\":\"%d\"," - "\"radiocontrol\":\"disabled\"," - "\"devicename\":\"RIG_MODEL_NETRIGCTL\"," - "\"deviceport\":\"COM99\"," - "\"pttprotocol\":\"USB\"," - "\"pttport\":\"COM99\"," - "\"serialspeed\":\"19200\"," - "\"data_bits\":\"8\"," - "\"stop_bits\":\"1\"," - "\"handshake\":\"None\"," - "\"rigctld_ip\":\"%s\"," - "\"rigctld_port\":\"%d\"," - "\"enable_scatter\":\"False\"," - "\"enable_fft\":\"False\"," - "\"enable_fsk\":\"False\"," - "\"low_bandwidth_mode\":\"%s\"," //False - "\"tuning_range_fmin\":\"%3.1f\"," //-50.0 - "\"tuning_range_fmax\":\"%3.1f\"," // 50.0 - "\"tx_audio_level\":\"125\"," - "\"respond_to_cq\":\"True\"," - "\"rx_buffer_size\":\"16\"," - "\"rx_buffer_size\":\"16\"," - "\"enable_explorer\": \"False\"" - - "}]}\n"; - - - char Command[2048]; - int Len; - - if (devptr) - { - while (*(--devptr) != '{'); // find start of subparam - capindex = atoi(&devptr[6]); - } - - devptr = stristr(TNC->PlaybackDevices, Info->Playback); - - if (devptr) - { - while (*(--devptr) != '{'); // find start of subparam - playindex = atoi(&devptr[6]); - } - - if (capindex > -1 && playindex > -1) - { - Len = sprintf(Command, startTNC, TNC->FreeDataInfo->ourCall, LOC, capindex, playindex, - TNC->FreeDataInfo->hamlibHost, TNC->FreeDataInfo->hamlibPort, - TNC->FreeDataInfo->LimitBandWidth ? "True" : "False", - TNC->FreeDataInfo->TuningRange * -1.0, - TNC->FreeDataInfo->TuningRange * 1.0); - - send(TNC->TCPSock, Command, Len, 0); -// TNC->FreeDataInfo->startingTNC = 5; - } - } - } - else - { - ptr = getJSONValue(Msg, "\"version\""); - - } - return; - } - TNC->FreeDataInfo->TNCRunning = 1; - - if (TNC->TNCCONNECTED == FALSE && TNC->TNCCONNECTING == FALSE) - ConnectTNCPort(TNC); - - return; - - } - - Debugprintf(Msg); -} - void StopTNC(struct TNCINFO * TNC) { - char stopTNC[] = "{\"type\" : \"set\", \"command\": \"stop_tnc\" , \"parameter\": \"---\"}"; - - if (TNC->TNCCONNECTED) - { - send(TNC->TCPSock, stopTNC, strlen(stopTNC), 0); + if (TNC->TCPDataSock) closesocket(TNC->TCPDataSock); - } } @@ -3089,6 +3092,151 @@ void ProcessTNCJSON(struct TNCINFO * TNC, char * Msg, int Len) return; } + if (memcmp(Msg, "{\"freedata\"", 10) == 0) + { + // {"freedata":"tnc-message","arq":"session","status":"connected","mycallsign":"G8BPQ-10","dxcallsign":"G8BPQ-0"} + // {"freedata":"tnc-message","arq":"session","status":"connected","heartbeat":"transmitting","mycallsign":"G8BPQ-10","dxcallsign":"G8BPQ-0"} + + char myCall[12] = ""; + char farCall[12] = ""; + char * ptr2; + + ptr = strstr(Msg, "\"tnc-message\",\"arq\":\"session\""); + + if (ptr) + { + ptr = strstr(ptr, "status"); + + if (ptr) + { + struct STREAMINFO * STREAM = &TNC->Streams[0]; + ptr += 9; + + ptr2 = strstr(ptr, "mycallsign"); + + if (ptr2) + { + memcpy(myCall, &ptr2[13], 10); + strlop(myCall, '"'); + } + + ptr2 = strstr(ptr, "dxcallsign"); + + if (ptr2) + { + memcpy(farCall, &ptr2[13], 10); + strlop(farCall, '"'); + } + + if (memcmp(ptr, "disconnected", 10) == 0) + { + if (TNC->FreeDataInfo->arqstate != 1) + { + TNC->FreeDataInfo->arqstate = 1; + Debugprintf("%d arq_session_state %s", TNC->Port, "disconnected"); + } + + // if connected this is a new disconnect + + if (STREAM->Connected) + { + // 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->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + STREAM->Disconnecting = FALSE; + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + } + } + else if (memcmp(ptr, "connecting", 10) == 0) + { + if (TNC->FreeDataInfo->arqstate != 2) + { + TNC->FreeDataInfo->arqstate = 2; + Debugprintf("%d arq_session_state %s", TNC->Port, "connecting"); + } + } + else if (memcmp(ptr, "connected", 9) == 0) + { + // if connection is idle this is an incoming connect + + if (TNC->FreeDataInfo->arqstate != 3) + { + TNC->FreeDataInfo->arqstate = 3; + Debugprintf("%d arq_session_state %s", TNC->Port, "connected"); + } + + if (STREAM->Connecting == FALSE && STREAM->Connected == FALSE) + { + FreeDataProcessNewConnect(TNC, farCall, myCall); + } + + // if connecting it is a connect ack + + else if (STREAM->Connecting) + { + FreeDataProcessConnectAck(TNC, farCall, Msg, Len); + } + } + + else if (memcmp(ptr, "disconnecting", 12) == 0) + { + if (TNC->FreeDataInfo->arqstate != 4) + { + TNC->FreeDataInfo->arqstate = 4; + Debugprintf("%d arq_session_state %s", TNC->Port, "disconnecting"); + } + } + else if (memcmp(ptr, "failed", 5) == 0) + { + PMSGWITHLEN buffptr; + + if (TNC->FreeDataInfo->arqstate != 5) + { + TNC->FreeDataInfo->arqstate = 5; + Debugprintf("%d arq_session_state %s", TNC->Port, "failed"); + } + + if (STREAM->Connecting) + { + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->Connecting = FALSE; + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "*** Connect Failed\r"); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->FramesQueued++; + } + } + } + + Debugprintf("%d %s", TNC->Port, Msg); + return; + } + +} if (memcmp(Msg, "{\"command\":\"tnc_state\"", 22) == 0) { @@ -3111,6 +3259,24 @@ void ProcessTNCJSON(struct TNCINFO * TNC, char * Msg, int Len) Msg += 23; + ptr = strstr(Msg, "tnc_state"); + + if (ptr) + { + if (ptr[12] == 'B' && TNC->Busy == FALSE) + { + TNC->Busy = TRUE; + strcpy(TNC->WEB_CHANSTATE, "Busy"); + SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + } + else if (ptr[12] == 'I' && TNC->Busy) + { + TNC->Busy = FALSE; + strcpy(TNC->WEB_CHANSTATE, "Idle"); + SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + } + } + ptr = strstr(Msg, "rx_buffer_length"); if (ptr) @@ -3521,11 +3687,11 @@ void ProcessTNCJSON(struct TNCINFO * TNC, char * Msg, int Len) int FreeDataConnect(struct TNCINFO * TNC, char * Call) { - char Connect[] = "{\"type\" : \"arq\", \"command\": \"connect\" , \"dxcallsign\": \"%s\"}\n"; + char Connect[] = "{\"type\" : \"arq\", \"command\": \"connect\" , \"dxcallsign\": \"%s\", \"attempts\": \"%d\"}\n"; char Msg[128]; int Len; - Len = sprintf(Msg, Connect, Call); + Len = sprintf(Msg, Connect, Call, TNC->MaxConReq); return send(TNC->TCPDataSock, Msg, Len, 0); } @@ -3567,100 +3733,6 @@ int FreeDataSendCommand(struct TNCINFO * TNC, char * Msg) return 0; } -void FreeDataProcessDaemonMsg(struct TNCINFO * TNC) -{ - int InputLen, MsgLen; - char * ptr; - int OpenBraces; - int CloseBraces; - char c; - - - // 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], FREEDATABUFLEN - 1 - TNC->InputLen, 0); - - if (InputLen == FREEDATABUFLEN - 1) - c = 0; - - - if (InputLen == 0 || InputLen == SOCKET_ERROR) - { - // Does this mean closed? - - closesocket(TNC->TCPSock); - - TNC->TCPSock = 0; - - TNC->DAEMONCONNECTED = FALSE; - TNC->Streams[0].ReportDISC = TRUE; - - sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); - MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); - - return; - } - - TNC->InputLen += InputLen; - -loop: - TNC->ARDOPBuffer[TNC->InputLen] = 0; // So we cat use string functions - - // Message should be json. We know the format, so don't need a general parser, but need to know if complete. - // I think counting { and } and stopping if equal should work; - - - if (TNC->ARDOPBuffer[0] != '{') - { - TNC->InputLen = 0; - return; - } - - ptr = &TNC->ARDOPBuffer[0]; - - OpenBraces = 0; - CloseBraces = 0; - - while (c = *(ptr++)) - { - if (c == '{') - OpenBraces ++; - else if (c == '}') - CloseBraces ++; - - if (OpenBraces == CloseBraces) - { - MsgLen = ptr - (char * )TNC->ARDOPBuffer; - - ProcessDAEMONJSON(TNC, TNC->ARDOPBuffer, MsgLen); - - if (TNC->InputLen == 0 || *ptr == 0) - { - TNC->InputLen = 0; - return; - } - - // More in buffer - - memmove(TNC->ARDOPBuffer, ptr, TNC->InputLen - MsgLen); - - TNC->InputLen -= MsgLen; - goto loop; - } - - } - // Message Incomplete - wait for rest; -} - - - - - - void FreeDataProcessTNCMsg(struct TNCINFO * TNC) { int DataInputLen, MsgLen; @@ -3776,10 +3848,8 @@ static SOCKADDR_IN rxaddr; VOID FreeDataThread(void * portptr) { - // FreeData TNC has two sessions, not sure why! - // Messages are JSON encapulated - // Opens deamon socket. TNC socket is only available once Start TNC command sent + // Now We run tnc directly so don't open daemon socket // Looks for data on socket(s) int port = (int)(size_t)portptr; @@ -3792,13 +3862,16 @@ VOID FreeDataThread(void * portptr) fd_set readfs; fd_set errorfs; struct timeval timeout; + char Params[512]; if (TNC->HostName == NULL) return; + buildParamString(TNC, Params); + TNC->BusyFlags = 0; - TNC->DAEMONCONNECTING = TRUE; + TNC->TNCCONNECTING = TRUE; Sleep(3000); // Allow init to complete @@ -3812,7 +3885,7 @@ VOID FreeDataThread(void * portptr) { // can only check if running on local host - TNC->PID = GetListeningPortsPID(TNC->destaddr.sin_port); + TNC->PID = GetListeningPortsPID(TNC->Datadestaddr.sin_port); if (TNC->PID == 0) goto TNCNotRunning; @@ -3846,28 +3919,27 @@ TNCNotRunning: // Not running or can't check, restart if we have a path -/* if (TNC->ProgramPath) + if (TNC->ProgramPath) { - Consoleprintf("Trying to (re)start TNC %s", 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 Daemon"); + 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) + if (TNC->Datadestaddr.sin_addr.s_addr == INADDR_NONE) { // Resolve name to address @@ -3875,18 +3947,14 @@ TNCRunning: if (!HostEnt) { - TNC->DAEMONCONNECTING = FALSE; + TNC->TNCCONNECTING = FALSE; sprintf(Msg, "Resolve Failed for FreeData Host - 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->TCPDataSock); -// closesocket(TNC->TCPSock); - TNC->TCPDataSock=socket(AF_INET,SOCK_STREAM,0); if (TNC->TCPDataSock == INVALID_SOCKET) @@ -3894,33 +3962,17 @@ TNCRunning: i=sprintf(Msg, "Socket Failed for FreeData TNC socket - error code = %d\r\n", WSAGetLastError()); WritetoConsole(Msg); - TNC->DAEMONCONNECTING = FALSE; + TNC->TNCCONNECTING = FALSE; return; } - TNC->TCPSock = socket(AF_INET,SOCK_STREAM,0); - - if (TNC->TCPSock == INVALID_SOCKET) - { - i=sprintf(Msg, "Socket Failed for FreeData Data Daemon socket - error code = %d\r\n", WSAGetLastError()); - WritetoConsole(Msg); - - TNC->DAEMONCONNECTING = FALSE; - closesocket(TNC->TCPDataSock); - - return; - } - - setsockopt(TNC->TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); - setsockopt(TNC->TCPSock, 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; - // Connect Daemon Port + // Connect TNC Port - if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->Datadestaddr,sizeof(TNC->Datadestaddr)) == 0) + if (connect(TNC->TCPDataSock,(LPSOCKADDR) &TNC->Datadestaddr,sizeof(TNC->Datadestaddr)) == 0) { // // Connected successful @@ -3931,18 +3983,17 @@ TNCRunning: if (TNC->Alerted == FALSE) { err=WSAGetLastError(); - i=sprintf(Msg, "Connect Failed for FreeData Daemon socket - error code = %d\r\n", err); + i=sprintf(Msg, "Connect Failed for FreeData TNC socket - error code = %d\r\n", err); WritetoConsole(Msg); - sprintf(TNC->WEB_COMMSSTATE, "Connection to Daemon failed"); + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); TNC->Alerted = TRUE; } - closesocket(TNC->TCPSock); - TNC->TCPSock = 0; - TNC->DAEMONCONNECTING = FALSE; + closesocket(TNC->TCPDataSock); + TNC->TCPDataSock = 0; + TNC->TNCCONNECTING = FALSE; - RestartTNC(TNC); return; } @@ -3950,18 +4001,18 @@ TNCRunning: TNC->LastFreq = 0; - TNC->DAEMONCONNECTING = FALSE; - TNC->DAEMONCONNECTED = TRUE; + TNC->TNCCONNECTING = FALSE; + TNC->TNCCONNECTED = TRUE; TNC->BusyFlags = 0; TNC->InputLen = 0; TNC->Alerted = FALSE; TNC->Alerted = TRUE; - sprintf(TNC->WEB_COMMSSTATE, "Connected to FreeData Daemon"); + sprintf(TNC->WEB_COMMSSTATE, "Connected to FreeData TNC"); MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); - sprintf(Msg, "Connected to FreeData Daemon Port %d\r\n", TNC->Port); + sprintf(Msg, "Connected to FreeData TNC Port %d\r\n", TNC->Port); WritetoConsole(Msg); @@ -3972,27 +4023,20 @@ TNCRunning: // GetSemaphore(&Semaphore, 52); #endif - while (TNC->DAEMONCONNECTED || TNC->TNCCONNECTED) + while (TNC->TNCCONNECTED) { FD_ZERO(&readfs); FD_ZERO(&errorfs); - if (TNC->DAEMONCONNECTED) - FD_SET(TNC->TCPSock,&readfs); - - if (TNC->TCPSock) - FD_SET(TNC->TCPSock,&errorfs); - if (TNC->TNCCONNECTED) FD_SET(TNC->TCPDataSock,&readfs); - - + if (TNC->TNCCONNECTING || TNC->TNCCONNECTED) FD_SET(TNC->TCPDataSock,&errorfs); timeout.tv_sec = 300; timeout.tv_usec = 0; - ret = select((int)TNC->TCPSock + 1, &readfs, NULL, &errorfs, &timeout); + ret = select((int)TNC->TCPDataSock + 1, &readfs, NULL, &errorfs, &timeout); if (ret == SOCKET_ERROR) { @@ -4023,20 +4067,13 @@ TNCRunning: FreeSemaphore(&Semaphore); } - if (FD_ISSET(TNC->TCPSock, &readfs)) - { - GetSemaphore(&Semaphore, 52); - FreeDataProcessDaemonMsg(TNC); - FreeSemaphore(&Semaphore); - } - if (FD_ISSET(TNC->TCPDataSock, &errorfs)) { Lost: sprintf(Msg, "FreeData TNC Connection lost for Port %d\r\n", TNC->Port); WritetoConsole(Msg); - sprintf(TNC->WEB_COMMSSTATE, "Connection to Daemon lost"); + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); TNC->TNCCONNECTED = FALSE; @@ -4052,26 +4089,6 @@ Lost: TNC->TCPDataSock = 0; } - if (FD_ISSET(TNC->TCPSock, &errorfs)) - { - sprintf(Msg, "FreeData Daemon Connection lost for Port %d\r\n", TNC->Port); - WritetoConsole(Msg); - - sprintf(TNC->WEB_COMMSSTATE, "Connection to Daemon lost"); - MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); - - TNC->DAEMONCONNECTED = 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); - TNC->TCPSock = 0; - } continue; } } @@ -4085,13 +4102,6 @@ closeThread: closesocket(TNC->TCPDataSock); } - if (TNC->TCPSock) - { - shutdown(TNC->TCPSock, SD_BOTH); - Sleep(100); - closesocket(TNC->TCPSock); - } - sprintf(Msg, "FreeData Thread Terminated Port %d\r\n", TNC->Port); WritetoConsole(Msg); } @@ -4118,7 +4128,7 @@ void ConnectTNCPort(struct TNCINFO * TNC) setsockopt(TNC->TCPDataSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); setsockopt(TNC->TCPDataSock, IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); - if (connect(TNC->TCPDataSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) + if (connect(TNC->TCPDataSock,(LPSOCKADDR) &TNC->Datadestaddr,sizeof(TNC->Datadestaddr)) == 0) { // Connected successful @@ -4134,7 +4144,7 @@ void ConnectTNCPort(struct TNCINFO * TNC) { err=WSAGetLastError(); sprintf(Msg, "Connect Failed for FreeData TNC socket - error code = %d Port %d\n", - err, htons(TNC->destaddr.sin_port)); + err, htons(TNC->Datadestaddr.sin_port)); WritetoConsole(Msg); TNC->Alerted = TRUE; @@ -4151,3 +4161,606 @@ void ConnectTNCPort(struct TNCINFO * TNC) return; } +void buildParamString(struct TNCINFO * TNC, char * line) +{ + // choices=[, "direct", "rigctl", "rigctld"], + + struct FreeDataINFO * FDI = TNC->FreeDataInfo; + int capindex = -1, playindex = -1; + int i; + + // Python adds sound mapper on front and counts Playback after Capture + + for (i = 0; i < CaptureCount; i++) + { + if (strstr(&CaptureNames[i][0], TNC->FreeDataInfo->Capture)) + { + capindex = i + 1; + break; + } + } + + for (i = 0; i < PlaybackCount; i++) + { + if (strstr(&PlaybackNames[i][0], TNC->FreeDataInfo->Playback)) + { + playindex = i + CaptureCount + 2; + break; + } + } + + sprintf(line, + "--mycall %s --ssid %s --mygrid %s --rx %d --tx %d --port %d --radiocontrol %s " + "--tuning_range_fmin %3.1f --tuning_range_fmax %3.1f --tx-audio-level %d", + + FDI->ourCall, FDI->SSIDList, LOC, capindex, playindex, TNC->TCPPort, FDI->hamlibHost ? "rigctld" : "disabled", + FDI->TuningRange * -1.0, FDI->TuningRange * 1.0, FDI->TXLevel); + + if (FDI->hamlibHost) + sprintf(line, "%s --rigctld_ip %s --rigctld_port %d", line, FDI->hamlibHost, FDI->hamlibPort); + + if (FDI->LimitBandWidth) + strcat(line, " --500hz"); + + if (FDI->Explorer) + strcat(line, " --explorer"); + + + // Add these to the end if needed + // --scatter + // --fft + // --fsk + // --qrv (respond to cq) + +} + +#ifndef WIN32 + +#include + +char ** WriteDevices = NULL; +int WriteDeviceCount = 0; + +char ** ReadDevices = NULL; +int ReadDeviceCount = 0; + + +int GetOutputDeviceCollection() +{ + // Get all the suitable devices and put in a list for GetNext to return + + snd_ctl_t *handle= NULL; + snd_pcm_t *pcm= NULL; + snd_ctl_card_info_t *info; + snd_pcm_info_t *pcminfo; + snd_pcm_hw_params_t *pars; + snd_pcm_format_mask_t *fmask; + char NameString[256]; + + Debugprintf("Playback Devices\n"); + + // free old struct if called again + +// while (WriteDeviceCount) +// { +// WriteDeviceCount--; +// free(WriteDevices[WriteDeviceCount]); +// } + +// if (WriteDevices) +// free(WriteDevices); + + WriteDevices = NULL; + WriteDeviceCount = 0; + + // Add virtual device ARDOP so ALSA plugins can be used if needed + + WriteDevices = realloc(WriteDevices,(WriteDeviceCount + 1) * sizeof(WriteDevices)); + + // Get Device List from ALSA + + snd_ctl_card_info_alloca(&info); + snd_pcm_info_alloca(&pcminfo); + snd_pcm_hw_params_alloca(&pars); + snd_pcm_format_mask_alloca(&fmask); + + char hwdev[80]; + unsigned min, max, ratemin, ratemax; + int card, err, dev, nsubd; + snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK; + + card = -1; + + if (snd_card_next(&card) < 0) + { + Debugprintf("No Devices"); + return 0; + } + + while (card >= 0) + { + sprintf(hwdev, "hw:%d", card); + err = snd_ctl_open(&handle, hwdev, 0); + err = snd_ctl_card_info(handle, info); + + Debugprintf("Card %d, ID `%s', name `%s'", card, snd_ctl_card_info_get_id(info), + snd_ctl_card_info_get_name(info)); + + + dev = -1; + + if(snd_ctl_pcm_next_device(handle, &dev) < 0) + { + // Card has no devices + + snd_ctl_close(handle); + + WriteDevices = realloc(WriteDevices,(WriteDeviceCount + 1) * sizeof(WriteDevices)); + WriteDevices[WriteDeviceCount++] = strupr(strdup("DummyDevice")); + + goto nextcard; + } + + while (dev >= 0) + { + snd_pcm_info_set_device(pcminfo, dev); + snd_pcm_info_set_subdevice(pcminfo, 0); + snd_pcm_info_set_stream(pcminfo, stream); + + err = snd_ctl_pcm_info(handle, pcminfo); + + + if (err == -ENOENT) + goto nextdevice; + + nsubd = snd_pcm_info_get_subdevices_count(pcminfo); + + Debugprintf(" Device hw:%d,%d ID `%s', name `%s', %d subdevices (%d available)", + card, dev, snd_pcm_info_get_id(pcminfo), snd_pcm_info_get_name(pcminfo), + nsubd, snd_pcm_info_get_subdevices_avail(pcminfo)); + + sprintf(hwdev, "hw:%d,%d", card, dev); + + err = snd_pcm_open(&pcm, hwdev, stream, SND_PCM_NONBLOCK); + + if (err) + { + Debugprintf("Error %d opening output device", err); + goto nextdevice; + } + + // Get parameters for this device + + err = snd_pcm_hw_params_any(pcm, pars); + + snd_pcm_hw_params_get_channels_min(pars, &min); + snd_pcm_hw_params_get_channels_max(pars, &max); + + snd_pcm_hw_params_get_rate_min(pars, &ratemin, NULL); + snd_pcm_hw_params_get_rate_max(pars, &ratemax, NULL); + + if( min == max ) + if( min == 1 ) + Debugprintf(" 1 channel, sampling rate %u..%u Hz", ratemin, ratemax); + else + Debugprintf(" %d channels, sampling rate %u..%u Hz", min, ratemin, ratemax); + else + Debugprintf(" %u..%u channels, sampling rate %u..%u Hz", min, max, ratemin, ratemax); + + // Add device to list + + sprintf(NameString, "hw:%d,%d %s(%s)", card, dev, + snd_pcm_info_get_name(pcminfo), snd_ctl_card_info_get_name(info)); + + WriteDevices = realloc(WriteDevices,(WriteDeviceCount + 1) * sizeof(WriteDevices)); + WriteDevices[WriteDeviceCount++] = strupr(strdup(NameString)); + + snd_pcm_close(pcm); + pcm= NULL; + +nextdevice: + if (snd_ctl_pcm_next_device(handle, &dev) < 0) + break; + } + snd_ctl_close(handle); + +nextcard: + + Debugprintf(""); + + if (snd_card_next(&card) < 0) // No more cards + break; + } + + return WriteDeviceCount; +} + +int GetNextOutputDevice(char * dest, int max, int n) +{ + if (n >= WriteDeviceCount) + return 0; + + strcpy(dest, WriteDevices[n]); + return strlen(dest); +} + + +int GetInputDeviceCollection() +{ + // Get all the suitable devices and put in a list for GetNext to return + + snd_ctl_t *handle= NULL; + snd_pcm_t *pcm= NULL; + snd_ctl_card_info_t *info; + snd_pcm_info_t *pcminfo; + snd_pcm_hw_params_t *pars; + snd_pcm_format_mask_t *fmask; + char NameString[256]; + + Debugprintf("Capture Devices\n"); + + ReadDevices = NULL; + ReadDeviceCount = 0; + + ReadDevices = realloc(ReadDevices,(ReadDeviceCount + 1) * sizeof(ReadDevices)); + + // Get Device List from ALSA + + snd_ctl_card_info_alloca(&info); + snd_pcm_info_alloca(&pcminfo); + snd_pcm_hw_params_alloca(&pars); + snd_pcm_format_mask_alloca(&fmask); + + char hwdev[80]; + unsigned min, max, ratemin, ratemax; + int card, err, dev, nsubd; + snd_pcm_stream_t stream = SND_PCM_STREAM_CAPTURE; + + card = -1; + + if(snd_card_next(&card) < 0) + { + Debugprintf("No Devices"); + return 0; + } + + while(card >= 0) + { + sprintf(hwdev, "hw:%d", card); + err = snd_ctl_open(&handle, hwdev, 0); + err = snd_ctl_card_info(handle, info); + +// Debugprintf("Card %d, ID `%s', name `%s'", card, snd_ctl_card_info_get_id(info), +// snd_ctl_card_info_get_name(info)); + + dev = -1; + + if (snd_ctl_pcm_next_device(handle, &dev) < 0) // No Devicdes + { + snd_ctl_close(handle); + + ReadDevices = realloc(ReadDevices,(ReadDeviceCount + 1) * sizeof(ReadDevices)); + ReadDevices[ReadDeviceCount++] = strupr(strdup("DummyDevice")); + + Debugprintf("%d %s", ReadDeviceCount, "DummyDevice"); + goto nextcard; + } + + while(dev >= 0) + { + snd_pcm_info_set_device(pcminfo, dev); + snd_pcm_info_set_subdevice(pcminfo, 0); + snd_pcm_info_set_stream(pcminfo, stream); + err= snd_ctl_pcm_info(handle, pcminfo); + + if (err == -ENOENT) + goto nextdevice; + + nsubd= snd_pcm_info_get_subdevices_count(pcminfo); +// Debugprintf(" Device hw:%d,%d ID `%s', name `%s', %d subdevices (%d available)", +// card, dev, snd_pcm_info_get_id(pcminfo), snd_pcm_info_get_name(pcminfo), +// nsubd, snd_pcm_info_get_subdevices_avail(pcminfo)); + + sprintf(hwdev, "hw:%d,%d", card, dev); +/* + err = snd_pcm_open(&pcm, hwdev, stream, SND_PCM_NONBLOCK); + + if (err) + { + Debugprintf("Error %d opening input device", err); + goto nextdevice; + } + + err = snd_pcm_hw_params_any(pcm, pars); + + snd_pcm_hw_params_get_channels_min(pars, &min); + snd_pcm_hw_params_get_channels_max(pars, &max); + snd_pcm_hw_params_get_rate_min(pars, &ratemin, NULL); + snd_pcm_hw_params_get_rate_max(pars, &ratemax, NULL); + + if( min == max ) + if( min == 1 ) + Debugprintf(" 1 channel, sampling rate %u..%u Hz", ratemin, ratemax); + else + Debugprintf(" %d channels, sampling rate %u..%u Hz", min, ratemin, ratemax); + else + Debugprintf(" %u..%u channels, sampling rate %u..%u Hz", min, max, ratemin, ratemax); +*/ + sprintf(NameString, "hw:%d,%d %s(%s)", card, dev, + snd_pcm_info_get_name(pcminfo), snd_ctl_card_info_get_name(info)); + + Debugprintf("%d %s", ReadDeviceCount, NameString); + + ReadDevices = realloc(ReadDevices,(ReadDeviceCount + 1) * sizeof(ReadDevices)); + ReadDevices[ReadDeviceCount++] = strupr(strdup(NameString)); + +nextdevice: + if (snd_ctl_pcm_next_device(handle, &dev) < 0) + break; + } + snd_ctl_close(handle); +nextcard: + + Debugprintf(""); + if (snd_card_next(&card) < 0 ) + break; + } + return ReadDeviceCount; +} + +int GetNextInputDevice(char * dest, int max, int n) +{ + if (n >= ReadDeviceCount) + return 0; + + strcpy(dest, ReadDevices[n]); + return strlen(dest); +} + +#endif + + +// We need a local restart tnc as we need to add params and start a python progrm on Linux + +BOOL KillOldTNC(char * Path); + +static BOOL RestartTNC(struct TNCINFO * TNC) +{ + if (TNC->ProgramPath == NULL || TNC->DontRestart) + return 0; + + if (_memicmp(TNC->ProgramPath, "REMOTE:", 7) == 0) + { + int n; + + // Try to start TNC on a remote host + + SOCKET sock = socket(AF_INET,SOCK_DGRAM,0); + struct sockaddr_in destaddr; + + Debugprintf("trying to restart TNC %s", TNC->ProgramPath); + + if (sock == INVALID_SOCKET) + return 0; + + destaddr.sin_family = AF_INET; + destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + destaddr.sin_port = htons(8500); + + if (destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + struct hostent * HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) + return 0; // Resolve failed + + memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + + n = sendto(sock, TNC->ProgramPath, (int)strlen(TNC->ProgramPath), 0, (struct sockaddr *)&destaddr, sizeof(destaddr)); + + Debugprintf("Restart TNC - sendto returned %d", n); + + Sleep(100); + closesocket(sock); + + return 1; // Cant tell if it worked, but assume ok + } + + // Not Remote + + // Add parameters to command string + +#ifndef WIN32 + { + char * arg_list[64]; + char rxVal[16]; + char txVal[16]; + char tunePlus[16]; + char tuneMinus[16]; + char portVal[16]; + char txLevelVal[16]; + char RigPort[16]; + int n = 0; + + pid_t child_pid; + + struct FreeDataINFO * FDI = TNC->FreeDataInfo; + int capindex = -1, playindex = -1; + int i; + + if (ReadDeviceCount == 0) + { + GetOutputDeviceCollection(); + GetInputDeviceCollection(); + } + + // + + for (i = 0; i < ReadDeviceCount; i++) + { + printf("%s %s\n", &ReadDevices[i][0], TNC->FreeDataInfo->Capture); + if (strstr(&ReadDevices[i][0], TNC->FreeDataInfo->Capture)) + { + capindex = i; + break; + } + } + + + for (i = 0; i < WriteDeviceCount; i++) + { + printf("%s %s\n", &WriteDevices[i][0], TNC->FreeDataInfo->Playback); + + if (strstr(&WriteDevices[i][0], TNC->FreeDataInfo->Playback)) + { + playindex = i; + break; + } + } + + + capindex=playindex=3; + + printf("%d %d \n", capindex, playindex); + + signal(SIGCHLD, SIG_IGN); // Silently (and portably) reap children. + + arg_list[n++] = "python3"; + arg_list[n++] = TNC->ProgramPath; + arg_list[n++] = "--mycall"; + arg_list[n++] = FDI->ourCall; + arg_list[n++] = "--ssid"; + arg_list[n++] = FDI->SSIDList; + arg_list[n++] = "--mygrid"; + arg_list[n++] = LOC; + arg_list[n++] = "--rx"; + sprintf(rxVal, "%d", capindex); + arg_list[n++] = rxVal; + arg_list[n++] = "--tx"; + sprintf(txVal, "%d", playindex); + arg_list[n++] = txVal; + arg_list[n++] = "--port"; + sprintf(portVal, "%d", TNC->TCPPort); + arg_list[n++] = portVal; + arg_list[n++] = "--radiocontrol"; + arg_list[n++] = FDI->hamlibHost ? "rigctld" : "disabled"; + arg_list[n++] = "--tuning_range_fmin"; + sprintf(tuneMinus, "%3.1f", FDI->TuningRange * -1.0); + arg_list[n++] = tuneMinus; + arg_list[n++] = "--tuning_range_fmax"; + sprintf(tunePlus, "%3.1f", FDI->TuningRange * 1.0); + arg_list[n++] = tunePlus; + arg_list[n++] = "--tx-audio-level"; + sprintf(txLevelVal, "%d", FDI->TXLevel); + arg_list[n++] = txLevelVal; + + if (FDI->hamlibHost) + { + arg_list[n++] = "--rigctld_ip"; + arg_list[n++] = FDI->hamlibHost; + arg_list[n++] = "--rigctld_port"; + sprintf(RigPort, "%d", FDI->hamlibPort); + arg_list[n++] = RigPort; + } + + if (FDI->LimitBandWidth) + arg_list[n++] = "--500hz"; + + if (FDI->Explorer) + arg_list[n++] = "--explorer"; + + arg_list[n++] = 0; + + n = 0; + + // Fork and Exec TNC + + printf("Trying to start %s\n", TNC->ProgramPath); + + /* Duplicate this process. */ + + child_pid = fork (); + + if (child_pid == -1) + { + printf ("StartTNC fork() Failed\n"); + return 0; + } + + if (child_pid == 0) + { + execvp (arg_list[0], arg_list); + + /* The execvp function returns only if an error occurs. */ + + printf ("Failed to start TNC\n"); + exit(0); // Kill the new process + } + printf("Started TNC\n"); + sleep(5000); + + return TRUE; + } +#else + + { + int n = 0; + + STARTUPINFO SInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION + char workingDirectory[256]; + char commandLine[512]; + char Params[512]; + + int i = strlen(TNC->ProgramPath); + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + Debugprintf("RestartTNC Called for %s", TNC->ProgramPath); + + + strcpy(workingDirectory, TNC->ProgramPath); + + while (i--) + { + if (workingDirectory[i] == '\\' || workingDirectory[i] == '/') + { + workingDirectory[i] = 0; + break; + } + } + + buildParamString(TNC, Params); + + if (TNC->PID) + { + KillTNC(TNC); + Sleep(100); + } + + sprintf(commandLine, "\"%s\" %s", TNC->ProgramPath, Params); + + if (CreateProcess(NULL, commandLine, NULL, NULL, FALSE,0, NULL, workingDirectory, &SInfo, &PInfo)) + { + Debugprintf("Restart TNC OK"); + TNC->PID = PInfo.dwProcessId; + return TRUE; + } + else + { + Debugprintf("Restart TNC Failed %d ", GetLastError()); + return FALSE; + } + } +#endif + return 0; +} diff --git a/FreeDATA.c.bak b/FreeDATA.c.bak new file mode 100644 index 0000000..a7b5b1d --- /dev/null +++ b/FreeDATA.c.bak @@ -0,0 +1,4631 @@ +/* +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 +*/ + +// +// Interface to allow G8BPQ switch to use FreeData TNC + + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + +#ifndef WIN32 +#ifndef MACBPQ +#include +#endif +#endif + +#include "CHeaders.h" +#include "bpq32.h" +#include "tncinfo.h" + +#define SD_BOTH 0x02 + +#define FREEDATABUFLEN 16384 // TCP buffer size + +int KillTNC(struct TNCINFO * TNC); +static int RestartTNC(struct TNCINFO * TNC); + +int (WINAPI FAR *GetModuleFileNameExPtr)(); +extern int (WINAPI FAR *EnumProcessesPtr)(); +static int Socket_Data(int sock, int error, int eventcode); +VOID MoveWindows(struct TNCINFO * TNC); +static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); +VOID SendInitScript(struct TNCINFO * TNC); +int ProcessEscape(UCHAR * TXMsg); +static void SendPoll(struct TNCINFO * TNC); +void SendMode(struct TNCINFO * TNC); +static int ConnecttoFreeData(int port); +void ConnectTNCPort(struct TNCINFO * TNC); +int FreeDataSendCommand(struct TNCINFO * TNC, char * data); +static void SendPing(struct TNCINFO * TNC, char * Call); +static void SendCQ(struct TNCINFO * TNC); +char * stristr (char *ch1, char *ch2); +int zEncode(unsigned char * in, unsigned char * out, int len, unsigned char * Banned); +static void SendDataMsg(struct TNCINFO * TNC, char * Call, char * Msg, int Len); +static int SendAsRaw(struct TNCINFO * TNC, char * Call, char * myCall, char * Msg, int Len); +static int SendAsFile(struct TNCINFO * TNC, char * Call, char * Msg, int Len); +char * byte_base64_encode(char *str, int len); +void xdecodeblock( unsigned char in[4], unsigned char out[3] ); +void FlushData(struct TNCINFO * TNC); +void CountRestarts(struct TNCINFO * TNC); +void StopTNC(struct TNCINFO * TNC); +int FreeDataConnect(struct TNCINFO * TNC, char * Call); +int FreeDataDisconnect(struct TNCINFO * TNC); +int FreeGetData(struct TNCINFO * TNC); +static void SendBeacon(struct TNCINFO * TNC, int Interval); +void buildParamString(struct TNCINFO * TNC, char * line); + +static char ClassName[]="FREEDATASTATUS"; +static char WindowTitle[] = "FreeData Modem"; +static int RigControlRow = 205; + +#ifndef WIN32 +#include +#define MAXPNAMELEN 32 +#else +#include +#endif + +extern char * PortConfig[33]; +extern int SemHeldByAPI; + +static RECT Rect; + +extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + +static int ProcessLine(char * buf, int Port); + +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + +#define MAXRXSIZE 512000 // Sets max size for file transfer (less base64 overhead + +int CaptureCount = 0; +int PlaybackCount = 0; + +int CaptureIndex = -1; // Card number +int PlayBackIndex = -1; + + + +char CaptureNames[16][MAXPNAMELEN + 2] = { "" }; +char PlaybackNames[16][MAXPNAMELEN + 2] = { "" }; + + +#ifdef WIN32 + +#include + +#pragma comment(lib, "winmm.lib") + +WAVEFORMATEX wfx = { WAVE_FORMAT_PCM, 1, 12000, 24000, 2, 16, 0 }; + +WAVEOUTCAPS pwoc; +WAVEINCAPS pwic; + + +char * CaptureDevices = NULL; +char * PlaybackDevices = NULL; + +HWAVEOUT hWaveOut = 0; +HWAVEIN hWaveIn = 0; + +#endif + + +char * gen_uuid() +{ + char v[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + int i; + + //3fb17ebc-bc38-4939-bc8b-74f2443281d4 + //8 dash 4 dash 4 dash 4 dash 12 + + static char buf[37] = {0}; + + //gen random for all spaces because lazy + for (i = 0; i < 36; ++i) + { + buf[i] = v[rand()%16]; + } + + //put dashes in place + buf[8] = '-'; + buf[13] = '-'; + buf[18] = '-'; + buf[23] = '-'; + + //needs end byte + buf[36] = '\0'; + + return buf; +} + +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 = TNCInfo[Port]; + 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->FreeDataInfo = zalloc(sizeof(struct FreeDataINFO)); + +// TNC->FreeDataInfo->useBaseCall = 1; // Default + + 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->TCPPort = WINMORport; + + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(WINMORport); + + 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); + } + } + + // 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; + } + + if (_memicmp(buf, "CAPTURE", 7) == 0) + { + TNC->FreeDataInfo->Capture = _strupr(_strdup(&buf[8])); + strlop(TNC->FreeDataInfo->Capture, 13); + } + else if (_memicmp(buf, "PLAYBACK", 8) == 0) + { + TNC->FreeDataInfo->Playback = _strupr(_strdup(&buf[9])); + strlop(TNC->FreeDataInfo->Playback, 13); + } + + else if (_memicmp(buf, "LOGDIR ", 7) == 0) + TNC->LogPath = _strdup(&buf[7]); + + else if (_memicmp(buf, "HAMLIBHOST", 10) == 0) + { + TNC->FreeDataInfo->hamlibHost = _strdup(&buf[11]); + strlop(TNC->FreeDataInfo->hamlibHost, 13); + } + + else if (_memicmp(buf, "TuningRange", 11) == 0) + TNC->FreeDataInfo->TuningRange = atoi(&buf[12]); + + else if (_memicmp(buf, "TXLevel", 6) == 0) + TNC->FreeDataInfo->TXLevel = atoi(&buf[7]); + + else if (_memicmp(buf, "Explorer", 8) == 0) + TNC->FreeDataInfo->Explorer = atoi(&buf[9]); + + + else if (_memicmp(buf, "LimitBandWidth", 14) == 0) + TNC->FreeDataInfo->LimitBandWidth = atoi(&buf[15]); + + else if (_memicmp(buf, "HAMLIBPORT", 10) == 0) + TNC->FreeDataInfo->hamlibPort = atoi(&buf[11]); + + else if (_memicmp(buf, "USEBASECALL", 11) == 0) + TNC->FreeDataInfo->useBaseCall = atoi(&buf[12]); + + else if (_memicmp(buf, "RXDIRECTORY", 11) == 0) + { + TNC->FreeDataInfo->RXDir = _strdup(&buf[12]); + strlop(TNC->FreeDataInfo->RXDir, 13); + } + + else if (standardParams(TNC, buf) == FALSE) + strcat(TNC->InitScript, buf); + + } + + return (TRUE); +} + +char * Config; +static char * ptr1, * ptr2; + +int FreeDataGetLine(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; +} + +BOOL FreeDataReadConfigFile(int Port, int ProcLine()) +{ + char buf[256],errbuf[256]; + + Config = PortConfig[Port]; + + if (Config) + { + // Using config from bpq32.cfg + + if (strlen(Config) == 0) + { + return TRUE; + } + + ptr1 = Config; + ptr2 = strchr(ptr1, 13); + + 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); +} + + + +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 VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen) +{ + if (TNC->hDevice) + { + // FreeData mode. Queue to Hostmode driver + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = EncLen; + memcpy(&buffptr->Data[0], Encoded, EncLen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + TNC->Streams[Stream].FramesQueued++; + + return; + } +} + + +VOID FreeDataChangeMYC(struct TNCINFO * TNC, char * Call) +{ + UCHAR TXMsg[100]; + int datalen; + + if (strcmp(Call, TNC->CurrentMYC) == 0) + return; // No Change + + strcpy(TNC->CurrentMYC, Call); + + datalen = sprintf(TXMsg, "MYCALL %s\r", Call); +// FreeDataSendCommand(TNC, TXMsg); +} + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + int datalen; + PMSGWITHLEN buffptr; +// char txbuff[500]; + unsigned int txlen = 0; + UCHAR * TXMsg; + + size_t Param; + int Stream = 0; + 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 + + switch (fn) + { + case 7: + + // 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances + + SendPoll(TNC); + + // Check for buffered data to send + + if (TNC->FreeDataInfo->toSendTimeout) + { + TNC->FreeDataInfo->toSendTimeout--; + if (TNC->FreeDataInfo->toSendTimeout <= 0) + FlushData(TNC); + } + + return 0; + + case 1: // poll + +// FreeDataCheckRX(TNC); + + if (TNC->TNCCONNECTED == FALSE && TNC->TNCCONNECTING == FALSE) + { + // See if time to reconnect + + time(<ime); + if (ltime - TNC->lasttime > 19 ) + { + TNC->lasttime = ltime; + ConnecttoFreeData(port); + } + } + + + while (TNC->PortRecord->UI_Q) + { + int datalen; + char * Buffer; + char FECMsg[512]; + char Call[12] = " "; + struct _MESSAGE * buffptr; + int CallLen; + char * ptr = FECMsg; + + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + +/* if (TNC->CONNECTED == 0 || + TNC->Streams[0].Connecting || + TNC->Streams[0].Connected) + { + // discard if TNC not connected or sesison active + + ReleaseBuffer(buffptr); + continue; + } +*/ + datalen = buffptr->LENGTH - MSGHDDRLEN; + Buffer = &buffptr->DEST[0]; // Raw Frame + Buffer[datalen] = 0; + + // Frame has ax.25 format header. Convert to Text + + CallLen = ConvFromAX25(Buffer + 7, Call); // Origin + memcpy(ptr, Call, CallLen); + ptr += CallLen; + + *ptr++ = '!'; + + CallLen = ConvFromAX25(Buffer, Call); // Dest + memcpy(ptr, Call, CallLen); + ptr += CallLen; + + Buffer += 14; // TO Digis + datalen -= 14; + + while ((Buffer[-1] & 1) == 0) + { + *ptr++ = ','; + CallLen = ConvFromAX25(Buffer, Call); + memcpy(ptr, Call, CallLen); + ptr += CallLen; + Buffer += 7; // End of addr + datalen -= 7; + } + + *ptr++ = '_'; + *ptr++ = 'U'; // UI Frame + *ptr++ = 0; // delimit calls + + if (Buffer[0] == 3) // UI + { + Buffer += 2; + datalen -= 2; + } + +// FreeDataSendSingleData(TNC, FECMsg, Buffer, datalen); + + ReleaseBuffer(buffptr); + } + + + if (TNC->DiscPending) + { + TNC->DiscPending--; + + if (TNC->DiscPending == 0) + { + // Too long in Disc Pending - Kill and Restart TNC + } + } + + + for (Stream = 0; Stream <= 2; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + FreeDataDisconnect(TNC); + strcpy(TNC->WEB_TNCSTATE, "Disconnecting"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + } + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && STREAM->Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + Debugprintf("FreeData New Attach Stream %d", Stream); + + STREAM->Attached = TRUE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, STREAM->MyCall); + TNC->Streams[Stream].MyCall[calllen] = 0; + + + FreeDataChangeMYC(TNC, TNC->Streams[0].MyCall); + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command(-1, Msg); + } + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + } + + // See if any frames for this port + + for (Stream = 0; Stream <= 2; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->BPQtoPACTOR_Q) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)Q_REM(&STREAM->BPQtoPACTOR_Q); + UCHAR * data = &buffptr->Data[0]; + STREAM->FramesQueued--; + txlen = (int)buffptr->Len; + + SendAsFile(TNC, TNC->FreeDataInfo->farCall, data, txlen); + } + + if (STREAM->PACTORtoBPQ_Q != 0) + { + buffptr = (PMSGWITHLEN)Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = Stream; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + + if (STREAM->ReportDISC) // May need a delay so treat as a counter + { + STREAM->ReportDISC--; + if (STREAM->ReportDISC == 0) + { + buff->PORT = Stream; +// STREAM->Connected = 0; +// STREAM->Attached = 0; + return -1; + } + } + } + return (0); + + case 2: // send + + Stream = buff->PORT; + + if (!TNC->TNCCONNECTED) + { + // Send Error Response + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = 36; + memcpy(&buffptr->Data[0], "No Connection to TNC\r", 36); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return 0; // Don't try if not connected + } + + STREAM = &TNC->Streams[Stream]; + + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; // Discard *** connected + return 0; + } + + txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); // 1 as no PID + TXMsg = &buff->L2DATA[0]; + TXMsg[txlen] = 0; + + if (STREAM->Connected) + { + STREAM->PacketsSent++; + + SendDataMsg(TNC, TNC->FreeDataInfo->farCall, buff->L2DATA, txlen); + return 1; + } + + if (TNC->FreeDataInfo->Chat) + { + // Chat Mode - Send to other end + + char reply[512] = "m"; + char * p; + int Len; + + if (_stricmp(TXMsg, "/ex\r") == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + TNC->FreeDataInfo->Chat = 0; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} Chat with %s ended. \r", TNC->FreeDataInfo->ChatCall); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + } + + // Send as chat message + + //m�;send_message�;123�;64730c5c-d32c-47b4-9b11-c958fd07a185�;hhhhhhhhhhhhhhhhhh + //�;�;plain/text�; + + strlop(TXMsg, 13); + + strcpy(&reply[2], ";send_message"); + strcpy(&reply[16], ";123"); + reply[21] = ';'; + strcpy(&reply[22], gen_uuid()); + sprintf(&reply[59], ";%s\n", TXMsg); + + p = strchr(&reply[59], 0); + + p[1] = ';'; + strcpy(&p[3], ";plain/text"); + p[15] = ';'; + + Len = &p[16] - reply; + + SendAsRaw(TNC, TNC->FreeDataInfo->ChatCall, "", reply, Len); + + return 0; + } + + + + if (_memicmp(&buff->L2DATA[0], "D\r", 2) == 0 || _memicmp(&buff->L2DATA[0], "BYE\r", 4) == 0) + { + STREAM->ReportDISC = TRUE; // Tell Node + TNC->FreeDataInfo->Chat = 0; + return 0; + } + + + // See if Local command (eg RADIO) + + if (_memicmp(&buff->L2DATA[0], "RADIO ", 6) == 0) + { + sprintf(&buff->L2DATA[0], "%d %s", TNC->Port, &buff->L2DATA[6]); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK->CIRCUITINDEX, &buff->L2DATA[0])) + { + } + else + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", &buff->L2DATA[0]); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "OVERRIDEBUSY", 12) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->OverrideBusy = TRUE; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} OK\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + } + + if (_memicmp(&buff->L2DATA[0], "PING ", 5) == 0) + { + char * Call = &buff->L2DATA[5]; + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + strlop(Call, 13); + strlop(Call, ' '); + SendPing(TNC, _strupr(Call)); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} Ping Sent\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + + } + + if (_memicmp(&buff->L2DATA[0], "CQ", 2) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + SendCQ(TNC); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} CQ Sent\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + + } + + + if (_memicmp(&buff->L2DATA[0], "CQ", 2) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + SendCQ(TNC); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} CQ Sent\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + + } + + if (_memicmp(&buff->L2DATA[0], "TXLEVEL ", 8) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + int Level = atoi(&buff->L2DATA[8]); + char TXL[] = "{\"type\" : \"set\", \"command\" : \"tx_audio_level\", \"value\": \"%d\"}\n"; + char Message[256]; + int Len, ret; + + Len = sprintf(Message, TXL, Level); + Debugprintf(Message); + ret = send(TNC->TCPDataSock, (char *)&Message, Len, 0); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} TXLevel Set\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 0; + } + + if (_memicmp(&buff->L2DATA[0], "SENDTEST", 8) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + char TXF[] = "{\"type\" : \"set\", \"command\" : \"send_test_frame\"}\n"; + char Message[256]; + int Len, ret; + + Len = sprintf(Message, TXF); + ret = send(TNC->TCPDataSock, (char *)&Message, Len, 0); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} Test Frame Requested\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 0; + } + + if (_memicmp(&buff->L2DATA[0], "CHAT ", 5) == 0) + { + char * Call = &buff->L2DATA[5]; + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + strlop(Call, 13); + strlop(Call, ' '); + + TNC->FreeDataInfo->Chat = 1; + memcpy(TNC->FreeDataInfo->ChatCall, _strupr(Call), 10); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} Chat with %s. Enter /ex to exit\r", Call); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + } + + + if (_memicmp(&TXMsg[0], "BEACON ", 7) == 0) + { + int Interval = atoi(&TXMsg[7]); + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + SendBeacon(TNC, Interval); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} Ok\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + } + + + + + // See if a Connect Command. + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + char Connect[80]; + char loppedCall[10]; + char toCall[10]; + + char * ptr = strchr(&buff->L2DATA[2], 13); + + if (ptr) + *ptr = 0; + + // FreeDATA doesn't have the concept of a connection, so need to simulate it between the nodes + _strupr(&buff->L2DATA[2]); + + if (strlen(&buff->L2DATA[2]) > 9) + buff->L2DATA[11] = 0; + + strcpy(TNC->FreeDataInfo->toCall, &buff->L2DATA[2]); + strcpy(loppedCall, TNC->FreeDataInfo->toCall); + if (TNC->FreeDataInfo->useBaseCall) + strlop(loppedCall, '-'); + strcpy(TNC->FreeDataInfo->farCall, loppedCall); + + // MYCALL and Target call are end to end concepts - the TNC cam can only use one call, set at TNC start. and no SSID's + // Messages are sent at TNC level to the tnc call, so we send our tnc call to the other end + + + txlen = sprintf(Connect, "C %s %s %s ", &buff->L2DATA[2], STREAM->MyCall, TNC->FreeDataInfo->ourCall); + + // 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; + + memset(STREAM->RemoteCall, 0, 10); + strcpy(STREAM->RemoteCall, &buff->L2DATA[2]); + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->PacketsSent = 0; + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", STREAM->MyCall, STREAM->RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + +// FreeDataSendCommand(TNC, Connect); + FreeDataConnect(TNC, STREAM->RemoteCall); + STREAM->Connecting = TRUE; + return 0; + + } + + // Normal data. Send to TNC + + // The TNC doesn't have any commands, so send error message + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} Not connected\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + // strlop(buff->L2DATA, 13); + // txlen = SendDataMsg(TNC, TNC->, buff->L2DATA); + // FreeDataSendData(TNC, TXMsg, txlen); + + return 0; + + case 3: + + // CHECK IF OK TO SEND (And check TNC Status) + + Stream = (int)(size_t)buff; + + // FreeData TNC can buffer unlimited data + + if (TNC->Streams[Stream].Attached == 0) + return (TNC->TNCCONNECTED != 0) << 8 | 1; + + return ((TNC->TNCCONNECTED != 0) << 8 | TNC->Streams[Stream].Disconnecting << 15); // OK + + case 5: // Close + + StopTNC(TNC); + + // Drop through + + case 4: // reinit7 + + if (TNC->TCPDataSock) + { + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPDataSock); + } + + 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 FreeDATA"); + return 1; // OK to change + } + + if (Param == 1) // Request Permission + { + if (!TNC->CONNECTED) + return 0; // No connection so no interlock + + if (TNC->ConnectPending == 0 && TNC->PTTState == 0) + { + TNC->FreeDataInfo->CONOK = 0; + TNC->GavePermission = TRUE; + return 0; // OK to Change + } + + if (TNC->ConnectPending) + TNC->ConnectPending--; // Time out if set too long + + return TRUE; + } + + if (Param == 3) // Release Permission + { + if (TNC->GavePermission) + { + TNC->GavePermission = FALSE; + if (TNC->ARDOPCurrentMode[0] != 'S') // Skip + TNC->FreeDataInfo->CONOK = 1; + + } + return 0; + } + + // Param is Address of a struct ScanEntry + + Scan = (struct ScanEntry *)buff; + return 0; + } + return 0; +} + +VOID FreeDataReleaseTNC(struct TNCINFO * TNC) +{ + // Set mycall back to Node or Port Call, and Start Scanner + + UCHAR TXMsg[1000]; + + FreeDataChangeMYC(TNC, TNC->NodeCall); + + // Start Scanner + + sprintf(TXMsg, "%d SCANSTART 15", TNC->Port); + + Rig_Command(-1, TXMsg); + + ReleaseOtherPorts(TNC); + +} + +VOID FreeDataSuspendPort(struct TNCINFO * TNC) +{ + TNC->FreeDataInfo->CONOK = 0; +} + +VOID FreeDataReleasePort(struct TNCINFO * TNC) +{ + TNC->FreeDataInfo->CONOK = 1; +} + + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, "" + "" + "FreDATA Status" + "

FreeData Status" + "

", + TNC->Port); + + + 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
Proto State%s
Traffic%s
TNC Restarts
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + +#ifndef LINBPQ + +#define BGCOLOUR RGB(236,233,216) +static HBRUSH RedBrush = NULL; +HBRUSH GreenBrush; +HBRUSH BlueBrush; +static HBRUSH bgBrush = NULL; + +extern HWND ClientWnd, FrameWnd; +extern int OffsetH, OffsetW; + +extern HMENU hMainFrameMenu; +extern HMENU hBaseMenu; +extern HANDLE hInstance; + +extern HKEY REGTREE; + + +/* +static LRESULT CALLBACK PacWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + MINMAXINFO * mmi; + PAINTSTRUCT ps; + HDC hdc; + + 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(ps.hdc, RedBrush); + SelectObject(ps.hdc, GreenBrush); +// SelectObject(ps.hdc, GetStockObject(GRAY_BRUSH)); + + EndPaint(hWnd, &ps); + 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")) + { + 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: + + KillTNC(TNC); + break; + + case WINMOR_RESTART: + + 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_DESTROY: + + break; + } + return DefMDIChildProc(hWnd, message, wParam, lParam); +} +*/ + +#endif + + + +VOID * FreeDataExtInit(EXTPORTDATA * PortEntry) +{ + int port; + char Msg[255]; + char * ptr; + struct TNCINFO * TNC; + char * TempScript; + u_long param = 1; + int line; + int i; + + srand(time(NULL)); + + port = PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + + if (TNC->FreeDataInfo->TuningRange == 0) + TNC->FreeDataInfo->TuningRange = 50; + + if (TNC->FreeDataInfo->TXLevel == 0) + TNC->FreeDataInfo->TXLevel = 50; + + if (TNC->AutoStartDelay == 0) + TNC->AutoStartDelay = 2000; + +#ifndef LINBPQ + + if (bgBrush == NULL) + { + bgBrush = CreateSolidBrush(BGCOLOUR); + RedBrush = CreateSolidBrush(RGB(255,0,0)); + GreenBrush = CreateSolidBrush(RGB(0,255,0)); + BlueBrush = CreateSolidBrush(RGB(0,0,255)); + } + +#endif + + Consoleprintf("FreeData Host %s %d", TNC->HostName, TNC->TCPPort); + + TNC->Port = port; + TNC->Hardware = H_FREEDATA; + + TNC->WeStartedTNC = 1; + + TNC->ARDOPDataBuffer = malloc(MAXRXSIZE); + TNC->ARDOPBuffer = malloc(FREEDATABUFLEN); + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + memcpy(TNC->FreeDataInfo->ourCall, TNC->NodeCall, 10); + strlop(TNC->FreeDataInfo->ourCall, ' '); + if (TNC->FreeDataInfo->useBaseCall) + strlop(TNC->FreeDataInfo->ourCall, '-'); + + 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; + + if (TNC->PacketChannels > 1) + TNC->PacketChannels = 1; + + PortEntry->MAXHOSTMODESESSIONS = TNC->PacketChannels + 1; + + PortEntry->SCANCAPABILITIES = SIMPLE; // Scan Control - pending connect only + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + + PortEntry->PORTCONTROL.UICAPABLE = TRUE; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 236; + + TNC->SuspendPortProc = FreeDataSuspendPort; + TNC->ReleasePortProc = FreeDataReleasePort; + +// PortEntry->PORTCONTROL.PORTSTARTCODE = KAMStartPort; +// PortEntry->PORTCONTROL.PORTSTOPCODE = KAMStopPort; + + + 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); + + // cant think of any yet + + if (TNC->InitScript) + { + strcat(TempScript, TNC->InitScript); + free(TNC->InitScript); + } + + TNC->InitScript = TempScript; + + // Set MYCALL + + sprintf(Msg, "MYCALL %s\r", TNC->NodeCall); + strcat(TNC->InitScript, Msg); + + strcpy(TNC->CurrentMYC, TNC->NodeCall); + + if (TNC->WL2K == NULL) + if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Already decoded + TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo; + + PortEntry->PORTCONTROL.TNC = TNC; + + // Build SSID List + + if (TNC->LISTENCALLS) + { + strcpy(TNC->FreeDataInfo->SSIDList, TNC->LISTENCALLS); + } + else + { + APPLCALLS * APPL; + char Appl[11] = ""; + char * List = TNC->FreeDataInfo->SSIDList; + char * SSIDptr; + int SSID; + int Listptr; + + // list is a set of numbers separated by spaces eg 0 2 10 + + SSIDptr = strchr(TNC->NodeCall, '-'); + if (SSIDptr) + SSID = atoi(SSIDptr + 1); + else + SSID = 0; + + Listptr = sprintf(List, "%d", SSID); + + for (i = 0; i < 32; i++) + { + APPL=&APPLCALLTABLE[i]; + + if (APPL->APPLCALL_TEXT[0] > ' ') + { + char * ptr; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + + SSIDptr = strchr(Appl, '-'); + if (SSIDptr) + SSID = atoi(SSIDptr + 1); + else + SSID = 0; + + Listptr += sprintf(&List[Listptr], " %d", SSID); + } + } + List[Listptr] = 0; + } + + 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, 500, 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,450,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,80,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", "Proto State", 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,116,line,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); + + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 0,170,250,300, TNC->hDlg, NULL, hInstance, NULL); + + TNC->ClientHeight = 450; + TNC->ClientWidth = 500; + + TNC->hMenu = CreatePopupMenu(); + + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill Freedata TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart Freedata 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 + + // Convert sound card name to index + +#ifdef WIN32 + + if (CaptureDevices == NULL) // DOn't do it again if more that one port + { + CaptureCount = waveInGetNumDevs(); + + CaptureDevices = malloc((MAXPNAMELEN + 2) * CaptureCount); + CaptureDevices[0] = 0; + + printf("Capture Devices"); + + for (i = 0; i < CaptureCount; i++) + { + waveInOpen(&hWaveIn, i, &wfx, 0, 0, CALLBACK_NULL); //WAVE_MAPPER + waveInGetDevCaps((UINT_PTR)hWaveIn, &pwic, sizeof(WAVEINCAPS)); + + if (CaptureDevices) + strcat(CaptureDevices, ","); + strcat(CaptureDevices, pwic.szPname); + Debugprintf("%d %s", i + 1, pwic.szPname); + memcpy(&CaptureNames[i][0], pwic.szPname, MAXPNAMELEN); + _strupr(&CaptureNames[i][0]); + } + + PlaybackCount = waveOutGetNumDevs(); + + PlaybackDevices = malloc((MAXPNAMELEN + 2) * PlaybackCount); + PlaybackDevices[0] = 0; + + Debugprintf("Playback Devices"); + + for (i = 0; i < PlaybackCount; i++) + { + waveOutOpen(&hWaveOut, i, &wfx, 0, 0, CALLBACK_NULL); //WAVE_MAPPER + waveOutGetDevCaps((UINT_PTR)hWaveOut, &pwoc, sizeof(WAVEOUTCAPS)); + + if (PlaybackDevices[0]) + strcat(PlaybackDevices, ","); + strcat(PlaybackDevices, pwoc.szPname); + Debugprintf("%i %s", i + 1, pwoc.szPname); + memcpy(&PlaybackNames[i][0], pwoc.szPname, MAXPNAMELEN); + _strupr(&PlaybackNames[i][0]); + waveOutClose(hWaveOut); + } + } + +#endif + + time(&TNC->lasttime); // Get initial time value + + ConnecttoFreeData(port); + TNC->FreeDataInfo->CONOK = 1; + + return ExtProc; +} + + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // We don't get data acks, so can't check for bytes outstanding + + FreeDataDisconnect(TNC); +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + FreeDataDisconnect(TNC); +} + + + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + if (Stream == 0) + { + FreeDataReleaseTNC(TNC); + } +} + +VOID FreeDataAbort(struct TNCINFO * TNC) +{ + FreeDataSendCommand(TNC, "ABORT\r"); +} + +// Host Mode Stuff (we reuse some routines in SCSPactor) + +VOID FreeDataDoTermModeTimeout(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->ReinitState == 0) + { + //Checking if in Terminal Mode - Try to set back to Term Mode + + TNC->ReinitState = 1; + return; + } + + if (TNC->ReinitState == 1) + { + // Forcing back to Term Mode + + TNC->ReinitState = 0; + return; + } + + if (TNC->ReinitState == 3) + { + return; + } +} + +static RECT Rect1 = {30, 160, 400, 195}; + +int zEncode(unsigned char * in, unsigned char * out, int len, unsigned char * Banned) +{ + // Replace forbidden chars with =xx + + unsigned char * ptr = out; + unsigned char c; + + while (len--) + { + c = *(in++); + + if (strchr(&Banned[0], c)) + { + *(out++) = '='; + *(out++) = (c >> 4) + 'A'; + *(out++) = (c & 15) + 'A'; + } + else + *(out++) = c; + } + + return (out - ptr); +} + + + +VOID FreeDataProcessTNCMessage(struct TNCINFO * TNC, char * Call, unsigned char * Msg, int Len) +{ + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FreeDataINFO * Modem = TNC->FreeDataInfo; + char * toCall, * fromCall, * tncCall, *Context; + char * ptr; + char a, b; + unsigned char axcall[7]; + char AppName[13] = ""; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + TRANSPORTENTRY * SESS; + + // First Byte of Message is Type. Messages can be commands or short (<120) data packets + // Data is encoded with =xx replacing restricted chars + + Msg[Len] = 0; + + switch (Msg[0]) + { + case 'C': + + // Connect Request. C G8BPQ-10 GM8BPQ-2 (Target, Origin) + + toCall = strtok_s(&Msg[2], " ", &Context); + fromCall = strtok_s(NULL, " ", &Context); + tncCall = strtok_s(NULL, " ", &Context); + + strcpy(TNC->FreeDataInfo->farCall, tncCall); + + ConvToAX25Ex(fromCall, axcall); // Allow -T and -R SSID's for MPS + + // Check for ExcludeList + + if (ExcludeList[0]) + { + if (CheckExcludeList(axcall) == FALSE) + { + Debugprintf("FreeData Call from %s rejected", fromCall); + + // Send 'd' + + Sleep(1000); + FreeDataDisconnect(TNC); + 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(axcall, ptr, 6) == 0) // Ignore SSID + break; + + ptr += 7; + + if ((*ptr) == 0) // Not in list + { + Sleep(1000); + FreeDataSendCommand(TNC, "d"); + + Debugprintf("FreeDara 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(toCall, Appl) == 0) + break; + } + + if (App < 32) + { + + 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)) + { + Sleep(1000); + FreeDataSendCommand(TNC, "dApplication not Available"); + return; + } + } + + ProcessIncommingConnectEx(TNC, fromCall, 0, TRUE, TRUE); + SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + // if connect to an application, send command + + if (AppName[0]) + { + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf(buffptr->Data, "%s\r", AppName); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + TNC->SwallowSignon = TRUE; + } + } + + 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, toCall, 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; + } + } + SESS->Mode = TNC->WL2KMode; + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, toCall); + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + SESS->Mode = WL2K->mode; + } + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->PacketsSent = 0; + STREAM->Connected = TRUE; + + // Send Connect ACK + Sleep(1000); + FreeDataSendCommand(TNC, "c"); + return; + + case 'c': + + // Connect ACK + + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s", STREAM->MyCall, STREAM->RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + STREAM->Connected = TRUE; + STREAM->Connecting = FALSE; + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "*** Connected to %s\r", TNC->FreeDataInfo->toCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->FramesQueued++; + + return; + + case 'D': + + // Disconnect Command + + FreeDataSendCommand(TNC, "d"); + + // Drop through to disconnect this end + + case 'd': + + // Disconnect complete (response to sending "D") + // Or connect refused in response to "C" + + if (STREAM->Connecting) + { + // Connection Refused - If there is a message, pass to appl + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->Connecting = FALSE; + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + if (Msg[1]) + buffptr->Len = sprintf(buffptr->Data, "Connect Rejected - %s\r", &Msg[1]); + else + buffptr->Len = sprintf(buffptr->Data, "Connect Rejected\r"); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->FramesQueued++; + return; + } + + // Release Session + + if (STREAM->Connected) + { + // 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->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + STREAM->Disconnecting = FALSE; + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + return; + + case 'B': + + // Was Base64, but has been expanded - just send to User + + // If len > blocksize, fragment + + Len--; + Msg++; // Remove Type + + while (Len > 256) + { + buffptr = GetBuff(); + buffptr->Len = 256; + memcpy(buffptr->Data, Msg, 256); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + WritetoTrace(TNC, Msg, 256); + Len -= 256; + Msg += 256; + STREAM->BytesRXed += 256; + + } + + buffptr = GetBuff(); + buffptr->Len = Len; + memcpy(buffptr->Data, Msg, Len); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + WritetoTrace(TNC, Msg, Len); + STREAM->BytesRXed += Len; + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->BytesTXed - TNC->FreeDataInfo->toSendCount, STREAM->BytesRXed, TNC->FreeDataInfo->toSendCount); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + + return; + + case 'I': + + // Encoded Response + + // Undo = transparency + + ptr = Msg + 1; + + while (ptr = strchr(ptr, '=')) + { + // Next two chars are a hex value + + a = ptr[1] - 'A'; + b = ptr[2] - 'A'; + memmove(ptr, ptr + 2, Len); + ptr[0] = (a << 4) + b; + ptr++; + } + + buffptr = GetBuff(); + buffptr->Len = sprintf(buffptr->Data, "%s", &Msg[1]); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + return; + + case 'f': + + // FreeDATA File Transfer + + // Seems to be f null fn null data + //f.;Makefile.;.;123123123.; + { + char * FN; + time_t CRC; + int FileLen; + char Filename[256]; + FILE * fp1; + unsigned char * ptr; + char Text[64]; + int textLen; + + + if (TNC->FreeDataInfo->RXDir == NULL) + { + Debugprintf("FreeDATA RXDIRECTORY not set - file transfer ignored"); + return; + } + + FN = _strdup(&Msg[3]); + ptr = &Msg[4] + strlen(FN); + + ptr = ptr + strlen(ptr) + 2; + CRC = atoi(ptr); + ptr = ptr + strlen(ptr) + 2; + + FileLen = Len - (ptr - Msg); + + sprintf(Filename, "%s\\%s", TNC->FreeDataInfo->RXDir, FN); + + fp1 = fopen(Filename, "wb"); + + if (fp1) + { + fwrite(ptr, 1, FileLen, fp1); + fclose(fp1); + textLen = sprintf(Text, "File %s received from %s \r", FN, Call); + WritetoTrace(TNC, Text, textLen); + } + else + Debugprintf("FreeDATA - File %s create failed %s", Filename); + + + free(FN); + return; + + } + + + } + + if (STREAM->Attached == 0) + return; + + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", Msg); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + +} + + +VOID FreeDataProcessNewConnect(struct TNCINFO * TNC, char * fromCall, char * toCall) +{ + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FreeDataINFO * Modem = TNC->FreeDataInfo; + char * tncCall, *Context; + char * ptr; + char a, b; + unsigned char axcall[7]; + char AppName[13] = ""; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + TRANSPORTENTRY * SESS; + + strcpy(TNC->FreeDataInfo->farCall, fromCall); + + ConvToAX25Ex(fromCall, axcall); // Allow -T and -R SSID's for MPS + + // Check for ExcludeList + + if (ExcludeList[0]) + { + if (CheckExcludeList(axcall) == FALSE) + { + Debugprintf("FreeData Call from %s rejected", fromCall); + + // Send 'd' + + Sleep(1000); + FreeDataDisconnect(TNC); + 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(axcall, ptr, 6) == 0) // Ignore SSID + break; + + ptr += 7; + + if ((*ptr) == 0) // Not in list + { + Sleep(1000); + FreeDataDisconnect(TNC); + + Debugprintf("FreeData Call from %s not in ValidCalls - rejected", fromCall); + return; + } + } + } + + // The TNC responds to any SSID so we can use incomming call as Appl Call + // No we can't - it responds but reports the configured call not the called call +/* + // 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(toCall, Appl) == 0) + break; + } + + if (App < 32) + { + + 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(fromCall, "-T" ) || strstr(fromCall, "-R"))) + strcpy(AppName, "RELAY "); + + // Make sure app is available + + if (!CheckAppl(TNC, AppName)) + { + Sleep(1000); + FreeDataSendCommand(TNC, "dApplication not Available"); + return; + } + } + +*/ + + ProcessIncommingConnectEx(TNC, fromCall, 0, TRUE, TRUE); + SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + // if connect to an application, send command + + if (AppName[0]) + { + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf(buffptr->Data, "%s\r", AppName); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + TNC->SwallowSignon = TRUE; + } + } + + 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, toCall, 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; + } + } + SESS->Mode = TNC->WL2KMode; + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, toCall); + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + SESS->Mode = WL2K->mode; + } + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->PacketsSent = 0; + STREAM->Connected = TRUE; + + return; + +} + +VOID FreeDataProcessConnectAck(struct TNCINFO * TNC, char * Call, unsigned char * Msg, int Len) +{ + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FreeDataINFO * Modem = TNC->FreeDataInfo; + char * toCall, * fromCall, * tncCall, *Context; + char * ptr; + char a, b; + unsigned char axcall[7]; + char AppName[13] = ""; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + TRANSPORTENTRY * SESS; + + + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s", STREAM->MyCall, STREAM->RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + STREAM->Connected = TRUE; + STREAM->Connecting = FALSE; + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "*** Connected to %s\r", TNC->FreeDataInfo->toCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->FramesQueued++; + + return; + +} +extern char LOC[7]; +/* +Line 104: if received_json["type"] == 'PING' and received_json["command"] == "PING": +Line 111: if received_json["type"] == 'ARQ' and received_json["command"] == "sendFile": +Line 142: if received_json["type"] == 'ARQ' and received_json["command"] == "sendMessage": +Line 173: if received_json["type"] == 'ARQ' and received_json["command"] == "stopTransmission": +Line 182: if received_json["type"] == 'GET' and received_json["command"] == 'STATION_INFO': +Line 195: if received_json["type"] == 'GET' and received_json["command"] == 'TNC_STATE': +Line 244: if received_json["type"] == 'GET' and received_json["command"] == 'RX_BUFFER': +Line 258: if received_json["type"] == 'GET' and received_json["command"] == 'RX_MSG_BUFFER': +Line 272: if received_json["type"] == 'SET' and received_json["command"] == 'DEL_RX_BUFFER': +Line 275: if received_json["type"] == 'SET' and received_json["command"] == 'DEL_RX_MSG_BUFFER': +*/ + +//{\"type\" : \"ARQ\", \"command\" : \"sendMessage\", \"dxcallsign\" : \"G8BPQ\", \"mode\" : \"10\", \"n_frames\" : \"1\", \"data\" : \"Hello Hello\" , \"checksum\" : \"123\", \"timestamp\" : 1642580748576} + + + +static unsigned char BANNED[] = {'"', '=', ':', '{', '}', '[', ']', '/', 13, 0}; // I think only need to escape = ": CR Null + + +static void SendDataMsg(struct TNCINFO * TNC, char * Call, char * Msg, int Len) +{ + // We can't base64 encode chunks. so buffer as original data and encode on send + + SendAsFile(TNC, TNC->FreeDataInfo->farCall, Msg, Len); + WritetoTrace(TNC, Msg, Len); + + return; +} + + + +static int SendAsRaw(struct TNCINFO * TNC, char * Call, char * myCall, char * Msg, int Len) +{ + char Message[16284]; + char * Base64; + + // TNC now only supports send_raw, with base64 encoded data + + char Template[] = "{\"type\" : \"arq\", \"command\" : \"send_raw\", \"uuid\" : \"%s\",\"parameter\":" + "[{\"dxcallsign\" : \"%s\", \"mode\": \"255\", \"n_frames\" : \"1\", \"data\" : \"%s\"}]}\n"; + + + Base64 = byte_base64_encode(Msg, Len); + + Len = sprintf(Message, Template, gen_uuid(), Call, Base64); + + free(Base64); + return send(TNC->TCPDataSock, Message, Len, 0); +} + +void FlushData(struct TNCINFO * TNC) +{ + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FreeDataINFO * Info = TNC->FreeDataInfo; + int Len = Info->toSendCount; + + // We need to flag as data (B) then base64 encode it + + memmove(&Info->toSendData[1], Info->toSendData, Len); + Info->toSendData[0] = 'B'; + Len++; + + SendAsRaw(TNC, Info->farCall, Info->ourCall, Info->toSendData, Len); + + Info->toSendCount = 0; + Info->toSendTimeout = 0; + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->BytesTXed - TNC->FreeDataInfo->toSendCount, STREAM->BytesRXed, TNC->FreeDataInfo->toSendCount); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + +} + +static int SendAsFile(struct TNCINFO * TNC, char * Call, char * Msg, int Len) +{ + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FreeDataINFO * Info = TNC->FreeDataInfo; + + // Add to buffer + + if ((Info->toSendCount + Len) > 8192) // Reasonable Limit + { + // Send the buffered bit + + FlushData(TNC); + } + + memcpy(&Info->toSendData[Info->toSendCount], Msg, Len); + Info->toSendCount += Len; + Info->toSendTimeout = 10; // About a second + + STREAM->BytesTXed += Len; + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->BytesTXed - TNC->FreeDataInfo->toSendCount, STREAM->BytesRXed, TNC->FreeDataInfo->toSendCount); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + + return Len; +} + +static int SendTNCCommand(struct TNCINFO * TNC, char * Type, char * Command) +{ + char Message[256]; + int Len; + + Len = sprintf(Message, "{\"type\" : \"%s\", \"command\" : \"%s\"}\n", Type, Command); + return send(TNC->TCPDataSock, Message, Len, 0); +} + +static void SendPoll(struct TNCINFO * TNC) +{ + return; +} + +static void SendPing(struct TNCINFO * TNC, char * Call) +{ + char Ping[] = "{\"type\" : \"ping\", \"command\" : \"ping\", \"dxcallsign\" : \"%s\", \"timestamp\" : %d}\n"; + char Message[256]; + int Len, ret; + + Len = sprintf(Message, Ping, Call, time(NULL)); + ret = send(TNC->TCPDataSock, (char *)&Message, Len, 0); +} + +static void SendCQ(struct TNCINFO * TNC) +{ + char CQ[] = "{\"type\" : \"broadcast\", \"command\" : \"cqcqcq\"}\n"; + + char Message[256]; + int Len, ret; + + Len = sprintf(Message, CQ); + ret = send(TNC->TCPDataSock, (char *)&Message, Len, 0); +} + +static void SendBeacon(struct TNCINFO * TNC, int Interval) +{ + char Template1[] = "{\"type\" : \"broadcast\", \"command\" : \"start_beacon\", \"parameter\" : \"%d\"}\n"; + char Template2[] = "{\"type\" : \"broadcast\", \"command\" : \"stop_beacon\"}\n"; + + char Message[256]; + int Len, ret; + + if (Interval > 0) + Len = sprintf(Message, Template1, Interval); + else + Len = sprintf(Message, Template2); + + ret = send(TNC->TCPDataSock, (char *)&Message, Len, 0); +} + + +unsigned short int compute_crc(unsigned char *buf,int len); + + +int FreeDataWriteCommBlock(struct TNCINFO * TNC) +{ + if (TNC->hDevice) + return WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); + + return 0; +} + +char * getObjectFromArray(char * Msg) +{ + // This gets the next object from an array ({} = object, [] = array + // We look for the end of the object same number of { and }, teminate after } and return pointer to next object + // So we have terminated Msg, and returned next object in array + + // Only call if Msg is the next object in array + + + char * ptr = Msg; + char c; + + int Open = 0; + int Close = 0; + + while (c = *(ptr++)) + { + if (c == '{') Open ++; else if (c == '}') Close ++; + + if (Open == Close) + { + *(ptr++) = 0; + return ptr; + } + } + return 0; +} +/* + ["DATACHANNEL;RECEIVEDOPENER","ARQ;RECEIVING","ARQ;RECEIVING;SUCCESS"] [{"DXCALLSIGN":"GM8BPQ +{"DXCALLSIGN":"GM8BPQ","DXGRID":"","TIMESTAMP":1642847440,"RXDATA":[{"dt":"f","fn":"config.json", +"ft":"application\/json","d":"data:application\/json;base64, +ewogICAgInRuY19ob3N0IiA6ICIxMjcuMC4wLjEiLAogICAgInRuY19wb3J0IiA6ICIzMDAwIiwKICAgICJkYWVtb25faG9zdCIgOiAiMTI3LjAuMC4xIiwKICAgICJkYWVtb25fcG9ydCIgOiAiMzAwMSIsCiAgICAibXljYWxsIiA6ICJBQTBBQSIsCiAgICAibXlncmlkIiA6ICJBQTExZWEiICAgIAp9" +,"crc":"123123123"}]} +*/ +void ProcessFileObject(struct TNCINFO * TNC, char * This) +{ + char * Call; + char * LOC; + char * FN; + char * Type; + char * ptr, * ptr2; + int Len, NewLen; + + Call = strchr(This, ':'); + Call += 2; + This = strlop(Call, '"'); + + LOC = strchr(This, ':'); + LOC += 2; + This = strlop(LOC, '"'); + + FN = strstr(This, "fn"); + FN += 5; + This = strlop(FN, '"'); + + Type = strstr(This, "base64"); + Type += 7; + This = strlop(Type, '"'); + + // Decode Base64 + + Len = strlen(Type); + + Debugprintf("RX %d %s %d", TNC->Port, FN, Len); + + ptr = ptr2 = Type; + + while (Len > 0) + { + xdecodeblock(ptr, ptr2); + ptr += 4; + ptr2 += 3; + Len -= 4; + } + + NewLen = (int)(ptr2 - Type); + + if (*(ptr-1) == '=') + NewLen--; + + if (*(ptr-2) == '=') + NewLen--; + + Type[NewLen] = 0; + + Type --; + + Type[0] = 'B'; ; // Base64 Info + + FreeDataProcessTNCMessage(TNC, Call, Type, NewLen + 1); + + +} + +void ProcessMessageObject(struct TNCINFO * TNC, char * This) +{ + // This gets Message from a RX_MSG_BUFFER array element. + + char * Call; + char * LOC; + char * Type; + char * Msg; + int Len; + char * ptr, * ptr2; + + char * ID; + char * TYPE; + char * SEQ; + char * UUID; + char * TEXT; + char * NOIDEA; + char * FORMAT; + char * FILENAME; + int fileLen; + + int n; + + + Call = strstr(This, "dxcallsign"); + Call += 13; + This = strlop(Call, '"'); + + LOC = strchr(This, ':'); + LOC += 2; + This = strlop(LOC, '"'); + + Msg = strstr(This, "\"data\""); + Msg += 8; + This = strlop(Msg, '"'); + + // Decode Base64 + + // FreeData replaces / with \/ so need to undo + + ptr2 = strstr(Msg, "\\/"); + + while (ptr2) + { + memmove(ptr2, ptr2 + 1, strlen(ptr2)); + ptr2 = strstr(ptr2, "\\/"); + } + + Len = strlen(Msg); + + ptr = ptr2 = Msg; + + while (Len > 0) + { + xdecodeblock(ptr, ptr2); + ptr += 4; + ptr2 += 3; + Len -= 4; + } + + Len = (int)(ptr2 - Msg); + + if (*(ptr-1) == '=') + Len--; + + if (*(ptr-2) == '=') + Len--; + + Msg[Len] = 0; + +//m�;send_message�;123�;64730c5c-d32c-47b4-9b11-c958fd07a185�;hhhhhhhhhhhhhhhhhh +//�;�;plain/text�; + +//m;send_message;123;64730c5c-d32c-47b4-9b11-c958fd07a185;hhhhhhhhhhhhhhhhhh +//;;plain/text; + + // Message elements seem to be delimited by null ; + // Guessing labels + + ID = Msg; + + if (ID[0] == 'B') + { + // BPQ Message + + struct STREAMINFO * STREAM = &TNC->Streams[0]; + PMSGWITHLEN buffptr; + + + if (STREAM->Attached) + { + if (STREAM->Connected == 1 && STREAM->Connecting == 0) + { + char * Line = &ID[1]; + Len -= 1; + + while (Len > 256) + { + buffptr = GetBuff(); + buffptr->Len = 256; + memcpy(buffptr->Data, Line, 256); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + WritetoTrace(TNC, Line, 256); + Len -= 256; + Line += 256; + STREAM->BytesRXed += 256; + } + + buffptr = GetBuff(); + buffptr->Len = Len; + memcpy(buffptr->Data, Line, Len); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + WritetoTrace(TNC, Line, Len); + STREAM->BytesRXed += Len; + + } + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->BytesTXed - TNC->FreeDataInfo->toSendCount, STREAM->BytesRXed, TNC->FreeDataInfo->toSendCount); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + } + return; + } + + n = strlen(ID) + 2; + Msg += n; + Len -= n; + + if (ID[0] == 'm') + { + // ?? Chat ?? comes from a send raw ?? + + struct STREAMINFO * STREAM = &TNC->Streams[0]; + PMSGWITHLEN buffptr; + + TYPE = Msg; + n = strlen(TYPE) + 2; + Msg += n; + Len -= n; + + SEQ = Msg; + n = strlen(SEQ) + 2; + Msg += n; + Len -= n; + + UUID = Msg; + n = strlen(UUID) + 2; + Msg += n; + Len -= n; + + TEXT = Msg; + n = strlen(TEXT) + 2; + Msg += n; + Len -= n; + + NOIDEA = Msg; + n = strlen(NOIDEA) + 2; + Msg += n; + Len -= n; + + FORMAT = Msg; + n = strlen(FORMAT) + 2; + Msg += n; + Len -= n; + + // if Atached, send to user + + if (STREAM->Attached) + { + if (STREAM->Connected == 0 && STREAM->Connecting == 0) + { + // Just attached - send as Chat Message + + char Line[560]; + char * rest; + + // Send line by line + + rest = strlop(TEXT, 10); // FreeData chat uses LF + + while (TEXT && TEXT[0]) + { + Len = strlen(TEXT); + if (Len > 512) + TEXT[512] = 0; + + Len = sprintf(Line, "Chat From %-10s%s\r", Call, TEXT); + + while (Len > 256) + { + buffptr = GetBuff(); + buffptr->Len = 256; + memcpy(buffptr->Data, Line, 256); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + WritetoTrace(TNC, Line, 256); + Len -= 256; + TEXT += 256; + STREAM->BytesRXed += 256; + } + + buffptr = GetBuff(); + buffptr->Len = Len; + memcpy(buffptr->Data, Line, Len); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + WritetoTrace(TNC, Line, Len); + STREAM->BytesRXed += Len; + + TEXT = rest; + rest = strlop(TEXT, 10); // FreeData chat ues LF + } + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->BytesTXed - TNC->FreeDataInfo->toSendCount, STREAM->BytesRXed, TNC->FreeDataInfo->toSendCount); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + } + } + else + { + // Send Not Available Message +//m�;send_message�;123�;64730c5c-d32c-47b4-9b11-c958fd07a185�;hhhhhhhhhhhhhhhhhh +//�;�;plain/text�; + + char reply[512] = "m"; + char * p; + + strcat(TEXT, "\r"); + WritetoTrace(TNC, TEXT, strlen(TEXT)); + + strcpy(&reply[2], ";send_message"); + strcpy(&reply[16], ";123"); + reply[21] = ';'; + strcpy(&reply[22], gen_uuid()); + sprintf(&reply[59], ";Message received but user not on line\n"); + + p = strchr(&reply[59], 0); + + p[1] = ';'; + strcpy(&p[3], ";plain/text"); + p[15] = ';'; + + Len = &p[16] - reply; + + SendAsRaw(TNC, Call, TNC->FreeDataInfo->ourCall, reply, Len); + } + return; + + } + else if (ID[0] == 'f') + { + // File Tranfer + + char Filename[256]; + FILE * fp1; + char Text[64]; + int textLen; + + + FILENAME = Msg; + n = strlen(FILENAME) + 2; + Msg += n; + Len -= n; + + TYPE = Msg; + n = strlen(TYPE) + 2; + Msg += n; + Len -= n; + + SEQ = Msg; // ?? Maybe = 123123123 + n = strlen(SEQ) + 2; + Msg += n; + Len -= n; + + TEXT = Msg; // The file + fileLen = Len; + + if (TNC->FreeDataInfo->RXDir == NULL) + { + Debugprintf("FreeDATA RXDIRECTORY not set - file transfer ignored"); + return; + } + + sprintf(Filename, "%s\\%s", TNC->FreeDataInfo->RXDir, FILENAME); + + fp1 = fopen(Filename, "wb"); + + if (fp1) + { + fwrite(TEXT, 1, fileLen, fp1); + fclose(fp1); + textLen = sprintf(Text, "File %s received from %s \r", FILENAME, Call); + WritetoTrace(TNC, Text, textLen); + } + else + Debugprintf("FreeDATA - File %s create failed %s", Filename); + + return; + } + else if (ID[0] == 'm') + { + + } + + + + + + + + + + +// FreeDataProcessTNCMessage(TNC, Call, Msg, strlen(Msg)); +} + +void processJSONINFO(struct TNCINFO * TNC, char * Info, char * Call, double snr) +{ + char * LOC = ""; + char * ptr, * Context; + + // Info is an array. Normally only one element, but should check + + ptr = strtok_s(&Info[1], ",]", &Context); + + while (ptr && ptr[1]) + { + if (strstr(ptr, "BEACON;RECEIVING")) + { + char CQ[64]; + int Len; + + Len = sprintf(CQ, "Beacon received from %s SNR %3.1f\r", Call, snr); + WritetoTrace(TNC, CQ, Len); + + // Add to MH + + if (Call) + UpdateMH(TNC, Call, '!', 'I'); + } + if (strstr(ptr, "PING;RECEIVING")) + { + // Add to MH + + if (Call) + UpdateMH(TNC, Call, '!', 'I'); + } + else if (strstr(ptr, "CQ;RECEIVING")) + { + char CQ[64]; + int Len; + + Len = sprintf(CQ, "CQ received from %s SNR %3.1f\r", Call, snr); + WritetoTrace(TNC, CQ, Len); + + // Add to MH + + UpdateMH(TNC, Call, '!', 'I'); + } + else if (strstr(ptr, "PING;RECEIVEDACK")) + { + char Msg[128]; + int Len; + + Len = sprintf(Msg, "Ping Response from %s SNR %3.1f\r", Call, snr); + FreeDataProcessTNCMessage(TNC, Call, Msg, Len); + + // Add to MH + + UpdateMH(TNC, Call, '!', 'I'); + } + else if (strstr(ptr, "TRANSMITTING;FAILED")) + { + // Failed to send a message - if it was a connect request tell appl + + struct STREAMINFO * STREAM = &TNC->Streams[0]; + PMSGWITHLEN buffptr; + + if (STREAM->Connecting) + { + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->Connecting = FALSE; + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "*** Connect Failed\r"); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->FramesQueued++; + } + } + ptr = strtok_s(NULL, ",]", &Context); + } +} + + + + + + +char * getJSONValue(char * Msg, char * Key) +{ + char * ptr, *ptr2, *value = 0; + int vallen, keylen = strlen(Key); + char c; + + // We Null Terminate the value, so we must look for keys in reverse order + + ptr = strstr(Msg, Key); + + if (ptr) + { + ptr += (keylen + 1); + + if (*(ptr) == '[') + { + // Array + + int Open = 0; + int Close = 0; + + ptr2 = ptr; + + while (c = *(ptr++)) + { + if (c == '[') + Open ++; + else if (c == ']') + Close ++; + + if (Open == Close) + { + vallen = ptr - ptr2; + value = ptr2; + value[vallen] = 0; + return value; + } + } + } + else if (*(ptr) == '\"') + { + // String + + ptr2 = ptr; + ptr = strchr(ptr + 1, '\"'); + if (ptr) + { + ptr++; + vallen = ptr - ptr2; + value = ptr2; + value[vallen] = 0; + } + } + } + return value; +} + + +char stopTNC[] = "{\"type\" : \"SET\", \"command\": \"STOPTNC\" , \"parameter\": \"---\" }\r"; + + +void StopTNC(struct TNCINFO * TNC) +{ + if (TNC->TCPDataSock) + closesocket(TNC->TCPDataSock); +} + + +void ProcessTNCJSON(struct TNCINFO * TNC, char * Msg, int Len) +{ + char * ptr; + + if (memcmp(Msg, "{\"ptt\":", 7) == 0) + { + if (strstr(Msg, "True")) + { +// TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + + if (TNC->PTTMode) + Rig_PTT(TNC, TRUE); + } + else + { + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); + } + return; + } + + if (memcmp(Msg, "{\"command_response\"", 19) == 0) + { + Debugprintf("%d %s", TNC->Port, Msg); + return; + } + + + if (memcmp(Msg, "{\"command\":\"tnc_state\"", 22) == 0) + { +/* +{"command":"tnc_state","ptt_state":"False","tnc_state":"IDLE","arq_state":"False","arq_session":"False", +"arq_session_state":"disconnected","audio_rms":"0","snr":"0","frequency":"None","speed_level":"1", +"mode":"None","bandwidth":"None","fft":"[0]","channel_busy":"False","scatter":[],"rx_buffer_length":"0", +"rx_msg_buffer_length":"0","arq_bytes_per_minute":"0","arq_bytes_per_minute_burst":"0","arq_compression_factor":"0", +"arq_transmission_percent":"0","total_bytes":"0","beacon_state":"False", +"stations":[],"mycallsign":"GM8BPQ-6","dxcallsign":"AA0AA","dxgrid":""} +*/ + char * LOC = 0; + char * Stations; + char * myCall = 0; + char * farCall = 0; + double snr; + int arqstate = 0; + int rx_buffer_length = 0; + int rx_msg_buffer_length = 0; + + Msg += 23; + + ptr = strstr(Msg, "rx_buffer_length"); + + if (ptr) + rx_buffer_length = atoi(&ptr[19]); + + ptr = strstr(Msg, "rx_msg_buffer_length"); + + if (ptr) + rx_msg_buffer_length = atoi(&ptr[23]); + + ptr = strstr(Msg, "snr"); + + if (ptr) + snr = atof(ptr + 6); + + Stations = getJSONValue(Msg, "\"stations\""); + + if (Stations) + { + ptr = Stations + strlen(Stations) + 1; + LOC = getJSONValue(ptr, "\"dxgrid\""); + farCall = getJSONValue(ptr, "\"dxcallsign\""); + myCall = getJSONValue(ptr, "\"mycallsign\""); + + if (myCall && farCall) + { + myCall++; + strlop(myCall, '"'); + farCall++; + strlop(farCall, '"'); + } + } + + // Look for changes in arq_session_state + + ptr = strstr(Msg, "\"arq_session_state\""); + + if (ptr) + { + struct STREAMINFO * STREAM = &TNC->Streams[0]; + ptr += 21; + + if (memcmp(ptr, "disconnected", 10) == 0) + { + if (TNC->FreeDataInfo->arqstate != 1) + { + TNC->FreeDataInfo->arqstate = 1; + Debugprintf("%d arq_session_state %s", TNC->Port, "disconnected"); + } + + // if connected this is a new disconnect + + if (STREAM->Connected) + { + // 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->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + STREAM->Disconnecting = FALSE; + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + } + } + else if (memcmp(ptr, "connecting", 10) == 0) + { + if (TNC->FreeDataInfo->arqstate != 2) + { + TNC->FreeDataInfo->arqstate = 2; + Debugprintf("%d arq_session_state %s", TNC->Port, "connecting"); + } + } + else if (memcmp(ptr, "connected", 9) == 0) + { + // if connection is idle this is an incoming connect + + if (TNC->FreeDataInfo->arqstate != 3) + { + TNC->FreeDataInfo->arqstate = 3; + Debugprintf("%d arq_session_state %s", TNC->Port, "connected"); + } + + if (STREAM->Connecting == FALSE && STREAM->Connected == FALSE) + { + FreeDataProcessNewConnect(TNC, farCall, myCall); + } + + // if connecting it is a connect ack + + else if (STREAM->Connecting) + { + FreeDataProcessConnectAck(TNC, farCall, Msg, Len); + } + } + + else if (memcmp(ptr, "disconnecting", 12) == 0) + { + if (TNC->FreeDataInfo->arqstate != 4) + { + TNC->FreeDataInfo->arqstate = 4; + Debugprintf("%d arq_session_state %s", TNC->Port, "disconnecting"); + } + } + else if (memcmp(ptr, "failed", 5) == 0) + { + PMSGWITHLEN buffptr; + + if (TNC->FreeDataInfo->arqstate != 5) + { + TNC->FreeDataInfo->arqstate = 5; + Debugprintf("%d arq_session_state %s", TNC->Port, "failed"); + } + + if (STREAM->Connecting) + { + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->Connecting = FALSE; + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "*** Connect Failed\r"); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->FramesQueued++; + } + } + } + + if (rx_buffer_length || rx_msg_buffer_length) + FreeGetData(TNC); + + ptr = getJSONValue(Msg, "\"info\""); + + if (ptr == NULL) + return; + + if (strcmp(ptr, "[]") != 0) + { + + processJSONINFO(TNC, ptr, farCall, snr); + Debugprintf("%d %s %s", TNC->Port, ptr, farCall); + } + + return; + } + + if (memcmp(Msg, "{\"freedata\":\"tnc-message\"", 25) == 0) + { + char * mycall = strstr(Msg, "mycall"); + char * dxcall = strstr(Msg, "dxcall"); + char * dxgrid = strstr(Msg, "dxgrid"); + char * snrptr = strstr(Msg, "snr"); + float snr = 0; + char CQ[64]; + int Len; + + Msg += 26; + + if (mycall && dxcall && dxgrid) + { + mycall += 13; + strlop(mycall, '"'); + + dxcall += 13; + strlop(dxcall, '"'); + + dxgrid += 9; + strlop(dxgrid, '"'); + } + + + if (dxcall && strstr(dxcall, "-0")) + strlop(dxcall, '-'); + + if (snrptr) + snr = atof(&snrptr[6]); + + if (memcmp(Msg, "\"beacon\":\"received\"", 18) == 0) + { + if (mycall && dxcall && dxgrid) + { + Len = sprintf(CQ, "Beacon received from %s SNR %3.1f", dxcall, snr); + WritetoTrace(TNC, CQ, Len); + + // Add to MH + + if (dxcall) + UpdateMH(TNC, dxcall, '!', 'I'); + + return; + } + } + + if (memcmp(Msg, "\"cq\":\"received\"", 14) == 0) + { + if (mycall && dxcall && dxgrid) + { + Len = sprintf(CQ, "CQ received from %s", dxcall); + WritetoTrace(TNC, CQ, Len); + + // Add to MH + + if (dxcall) + UpdateMH(TNC, dxcall, '!', 'I'); + + return; + } + } + + if (memcmp(Msg, "\"ping\":\"received\"", 16) == 0) + { + if (mycall && dxcall && dxgrid) + { + Len = sprintf(CQ, "PING received from %s SNR %3.1f", dxcall, snr); + WritetoTrace(TNC, CQ, Len); + + // Add to MH + + if (dxcall) + UpdateMH(TNC, dxcall, '!', 'I'); + + return; + } + } + + if (memcmp(Msg, "\"ping\":\"acknowledge\"", 16) == 0) + { + if (mycall && dxcall && dxgrid) + { + char Msg[128]; + + Len = sprintf(Msg, "Ping Response from %s SNR %3.1f\r", dxcall, snr); + FreeDataProcessTNCMessage(TNC, dxcall, Msg, Len); + + UpdateMH(TNC, dxcall, '!', 'I'); + + return; + } + } + + + + + + + Debugprintf("%d %s", TNC->Port, Msg); + return; + } + + if (memcmp(Msg, "{\"command\":\"rx_buffer\"", 22) == 0) + { + char * Next, * This; + + // Delete from TNC + + SendTNCCommand(TNC, "set", "del_rx_buffer"); Msg += 22; + + ptr = getJSONValue(Msg, "\"eof\""); + ptr = getJSONValue(Msg, "\"data-array\""); + + This = ptr; + + if (This[1] == '{') // Array of objects + { + This++; + do + { + Next = getObjectFromArray(This); + ProcessMessageObject(TNC, This); + This = Next; + + } while (Next && Next[0] == '{'); + } + + return; + } + + Debugprintf("%d %s", TNC->Port, Msg); + + +// {"COMMAND":"RX_BUFFER","DATA-ARRAY":[],"EOF":"EOF"} +/* {"COMMAND":"RX_BUFFER","DATA-ARRAY":[{"DXCALLSIGN":"GM8BPQ","DXGRID":"","TIMESTAMP":1642579504, +"RXDATA":[{"dt":"f","fn":"main.js","ft":"text\/javascript" +,"d":"data:text\/javascript;base64,Y29uc3Qge.....9KTsK","crc":"123123123"}]}],"EOF":"EOF"} + + + +{"arq":"received","uuid":"a1346319-6eb0-42aa-b5a0-c9493c8ccdca","timestamp":1645812393,"dxcallsign":"G8BPQ-2","dxgrid":"","data":"QyBHOEJQUS0yIEc4QlBRLTIgRzhCUFEtMiA="} +{"ptt":"True"} + + +*/ + if (memcmp(Msg, "{\"arq\":\"received\"", 17) == 0) + { + int NewLen; + char * ptr, *ptr2, *Type; + char * Call = 0; + char * myCall = 0; + + Msg += 17; + + ptr = getJSONValue(Msg, "\"data\""); + Type = ++ptr; + + // Decode Base64 + + // FreeData replaces / with \/ so need to undo + + ptr2 = strstr(Type, "\\/"); + + while (ptr2) + { + memmove(ptr2, ptr2 + 1, strlen(ptr2)); + ptr2 = strstr(ptr2, "\\/"); + } + + Len = strlen(Type) - 1; + + // Debugprintf("RX %d %s %d", TNC->Port, FN, Len); + + ptr = ptr2 = Type; + + while (Len > 0) + { + xdecodeblock(ptr, ptr2); + ptr += 4; + ptr2 += 3; + Len -= 4; + } + + NewLen = (int)(ptr2 - Type); + + if (*(ptr-1) == '=') + NewLen--; + + if (*(ptr-2) == '=') + NewLen--; + + Type[NewLen] = 0; + + myCall = getJSONValue(Msg, "\"mycallsign\""); + Call = getJSONValue(Msg, "\"dxcallsign\""); + + if (Call) + { + Call++; + strlop(Call, '"'); + } + + if (myCall) + { + myCall++; + strlop(myCall, '"'); + } + + + FreeDataProcessTNCMessage(TNC, Call, Type, NewLen); + + return; + } + + if (memcmp(Msg, "{\"COMMAND\":\"RX_MSG_BUFFER\"", 26) == 0) + { + char * Next, * This; + + Msg += 26; + ptr = getJSONValue(Msg, "\"EOF\""); + ptr = getJSONValue(Msg, "\"DATA-ARRAY\""); + + This = ptr; + + if (This[1] == '{') // Array of objects + { + This++; + do { + Next = getObjectFromArray(This); + ProcessMessageObject(TNC, This); + This = Next; + } while (Next && Next[0] == '{'); + + // Delete from TNC + + SendTNCCommand(TNC, "SET", "DEL_RX_MSG_BUFFER"); + } + + + return; + } +} + +int FreeDataConnect(struct TNCINFO * TNC, char * Call) +{ + char Connect[] = "{\"type\" : \"arq\", \"command\": \"connect\" , \"dxcallsign\": \"%s\"}\n"; + char Msg[128]; + int Len; + + Len = sprintf(Msg, Connect, Call); + + return send(TNC->TCPDataSock, Msg, Len, 0); +} + +int FreeDataDisconnect(struct TNCINFO * TNC) +{ + char Disconnect[] = "{\"type\" : \"arq\", \"command\": \"disconnect\"}\n"; + char Msg[128]; + int Len; + +// return FreeDataSendCommand(TNC, "D"); + + Len = sprintf(Msg, Disconnect); + + return send(TNC->TCPDataSock, Msg, Len, 0); +} + + +int FreeGetData(struct TNCINFO * TNC) +{ + char GetData[] = "{\"type\" : \"get\", \"command\": \"rx_buffer\"}\n"; + char Msg[128]; + int Len; + + Len = sprintf(Msg, GetData); + + return send(TNC->TCPDataSock, Msg, Len, 0); +} + +int FreeDataSendCommand(struct TNCINFO * TNC, char * Msg) +{ + // Commands are simulated as Messages to the remote BPQ. The TNC itself does not handle any commands + + // First Byte of MSG is a Type - Command or Data. MSG has a limited character set Use =xx for Now. + + // Current Types - C = Connect, D = Disconnect, I = info + + SendAsRaw(TNC, TNC->FreeDataInfo->farCall, TNC->FreeDataInfo->ourCall, Msg, strlen(Msg)); + return 0; +} + +void FreeDataProcessTNCMsg(struct TNCINFO * TNC) +{ + int DataInputLen, MsgLen; + char * ptr, * endptr; + int maxlen; + + // May get message split over packets or multiple messages per packet + + // A complete file transfer arrives as one message, so can bw very long + + + if (TNC->DataInputLen > MAXRXSIZE) // Shouldnt have packets longer than this + TNC->DataInputLen=0; + + maxlen = MAXRXSIZE - TNC->DataInputLen; + + if (maxlen >1400) + maxlen = 1400; + + DataInputLen = recv(TNC->TCPDataSock, &TNC->ARDOPDataBuffer[TNC->DataInputLen], maxlen, 0); + + if (DataInputLen == 0 || DataInputLen == SOCKET_ERROR) + { + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + closesocket(TNC->TCPDataSock); + + TNC->TCPDataSock = 0; + + TNC->TNCCONNECTED = FALSE; + + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + STREAM->Disconnecting = FALSE; + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + return; + } + + TNC->DataInputLen += DataInputLen; + + TNC->ARDOPDataBuffer[TNC->DataInputLen] = 0; // So we can use string functions + + // Message should be json. We know the format, so don't need a general parser, but need to know if complete. + // I think counting { and } and stopping if equal should work; + +// Debugprintf(TNC->ARDOPDataBuffer); + + //I think now messages end with LF + +loop: + + endptr = strchr(TNC->ARDOPDataBuffer, 10); + + if (endptr == 0) + return; + + *(endptr) = 0; + + if (TNC->ARDOPDataBuffer[0] != '{') + { + TNC->DataInputLen = 0; + return; + } + + ptr = &TNC->ARDOPDataBuffer[0]; + + MsgLen = endptr - ptr; + + ProcessTNCJSON(TNC, ptr, MsgLen); + + // MsgLen doesnt include lf + + MsgLen++; + + if (TNC->DataInputLen == MsgLen) + { + TNC->DataInputLen = 0; + return; + } + + // More in buffer + + ptr += MsgLen; + TNC->DataInputLen -= MsgLen; + + memmove(TNC->ARDOPDataBuffer, ptr, TNC->DataInputLen + 1); + + goto loop; + + // Message Incomplete - wait for rest; +} + + + +VOID FreeDataThread(void * portptr); + +int ConnecttoFreeData(int port) +{ + _beginthread(FreeDataThread, 0, (void *)(size_t)port); + + return 0; +} + +static SOCKADDR_IN sinx; +static SOCKADDR_IN rxaddr; + + +VOID FreeDataThread(void * portptr) +{ + // Messages are JSON encapulated + // Now We run tnc directly so don't open daemon socket + // Looks for data on socket(s) + + 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 Params[512]; + + if (TNC->HostName == NULL) + return; + + buildParamString(TNC, Params); + + TNC->BusyFlags = 0; + + TNC->TNCCONNECTING = TRUE; + + Sleep(3000); // Allow init to complete + +// printf("Starting FreeDATA 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->Datadestaddr.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->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->Datadestaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) + { + TNC->TNCCONNECTING = FALSE; + sprintf(Msg, "Resolve Failed for FreeData Host - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + return; // Resolve failed + } + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + + TNC->TCPDataSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPDataSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for FreeData TNC socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->TNCCONNECTING = FALSE; + return; + } + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + // Connect TNC 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 FreeData TNC 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->TCPDataSock); + TNC->TCPDataSock = 0; + TNC->TNCCONNECTING = FALSE; + + RestartTNC(TNC); + return; + } + + Sleep(1000); + + TNC->LastFreq = 0; + + TNC->TNCCONNECTING = FALSE; + TNC->TNCCONNECTED = TRUE; + TNC->BusyFlags = 0; + TNC->InputLen = 0; + TNC->Alerted = FALSE; + + TNC->Alerted = TRUE; + + sprintf(TNC->WEB_COMMSSTATE, "Connected to FreeData TNC"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + sprintf(Msg, "Connected to FreeData 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->TNCCONNECTED) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + if (TNC->TNCCONNECTED) + FD_SET(TNC->TCPDataSock,&readfs); + + if (TNC->TNCCONNECTING || TNC->TNCCONNECTED) FD_SET(TNC->TCPDataSock,&errorfs); + + timeout.tv_sec = 300; + timeout.tv_usec = 0; + + ret = select((int)TNC->TCPDataSock + 1, &readfs, NULL, &errorfs, &timeout); + + if (ret == SOCKET_ERROR) + { + Debugprintf("FreeData Select failed %d ", WSAGetLastError()); + goto Lost; + } + + // If nothing doing send get rx_buffer as link validation poll + + if (ret == 0) + { + char GetData[] = "{\"type\" : \"get\", \"command\": \"rx_buffer\"}\n"; + int Len; + + Len = send(TNC->TCPDataSock, GetData, strlen(GetData), 0); + + if (Len != strlen(GetData)) + goto closeThread; + } + else + { + // See what happened + + if (FD_ISSET(TNC->TCPDataSock, &readfs)) + { + GetSemaphore(&Semaphore, 52); + FreeDataProcessTNCMsg(TNC); + FreeSemaphore(&Semaphore); + } + + if (FD_ISSET(TNC->TCPDataSock, &errorfs)) + { +Lost: + sprintf(Msg, "FreeData TNC 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->TNCCONNECTED = 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->TCPDataSock); + TNC->TCPDataSock = 0; + } + + continue; + } + } + +closeThread: + + if (TNC->TCPDataSock) + { + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPDataSock); + } + + sprintf(Msg, "FreeData Thread Terminated Port %d\r\n", TNC->Port); + WritetoConsole(Msg); +} + +void ConnectTNCPort(struct TNCINFO * TNC) +{ + char Msg[255]; + int err; + int bcopt = TRUE; + + TNC->TNCCONNECTING = TRUE; + + TNC->TCPDataSock = socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPDataSock == INVALID_SOCKET) + { + sprintf(Msg, "Socket Failed for FreeData TNC socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->TNCCONNECTING = FALSE; + return; + } + + setsockopt(TNC->TCPDataSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + setsockopt(TNC->TCPDataSock, IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + if (connect(TNC->TCPDataSock,(LPSOCKADDR) &TNC->Datadestaddr,sizeof(TNC->Datadestaddr)) == 0) + { + // Connected successful + + sprintf(TNC->WEB_COMMSSTATE, "Connected to FreeData TNC"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + TNC->TNCCONNECTING = FALSE; + TNC->TNCCONNECTED = TRUE; + TNC->Alerted = FALSE; + return; + } + + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + sprintf(Msg, "Connect Failed for FreeData TNC socket - error code = %d Port %d\n", + err, htons(TNC->Datadestaddr.sin_port)); + + WritetoConsole(Msg); + TNC->Alerted = TRUE; + TNC->TNCCONNECTING = FALSE; + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + } + + closesocket(TNC->TCPDataSock); + + TNC->TCPDataSock = 0; + TNC->TNCCONNECTING = FALSE; + return; +} + +void buildParamString(struct TNCINFO * TNC, char * line) +{ + // choices=[, "direct", "rigctl", "rigctld"], + + struct FreeDataINFO * FDI = TNC->FreeDataInfo; + int capindex = -1, playindex = -1; + int i; + + // Python adds sound mapper on front and counts Playback after Capture + + for (i = 0; i < CaptureCount; i++) + { + if (strstr(&CaptureNames[i][0], TNC->FreeDataInfo->Capture)) + { + capindex = i + 1; + break; + } + } + + for (i = 0; i < PlaybackCount; i++) + { + if (strstr(&PlaybackNames[i][0], TNC->FreeDataInfo->Playback)) + { + playindex = i + CaptureCount + 2; + break; + } + } + + sprintf(line, + "--mycall %s --ssid %s --mygrid %s --rx %d --tx %d --port %d --radiocontrol %s " + "--tuning_range_fmin %3.1f --tuning_range_fmax %3.1f --tx-audio-level %d", + + FDI->ourCall, FDI->SSIDList, LOC, capindex, playindex, TNC->TCPPort, FDI->hamlibHost ? "rigctld" : "disabled", + FDI->TuningRange * -1.0, FDI->TuningRange * 1.0, FDI->TXLevel); + + if (FDI->hamlibHost) + sprintf(line, "%s --rigctld_ip %s --rigctld_port %d", line, FDI->hamlibHost, FDI->hamlibPort); + + if (FDI->LimitBandWidth) + strcat(line, " --500hz"); + + if (FDI->Explorer) + strcat(line, " --explorer"); + + + // Add these to the end if needed + // --scatter + // --fft + // --fsk + // --qrv (respond to cq) + +} + +#ifndef WIN32 + +#include + +char ** WriteDevices = NULL; +int WriteDeviceCount = 0; + +char ** ReadDevices = NULL; +int ReadDeviceCount = 0; + + +int GetOutputDeviceCollection() +{ + // Get all the suitable devices and put in a list for GetNext to return + + snd_ctl_t *handle= NULL; + snd_pcm_t *pcm= NULL; + snd_ctl_card_info_t *info; + snd_pcm_info_t *pcminfo; + snd_pcm_hw_params_t *pars; + snd_pcm_format_mask_t *fmask; + char NameString[256]; + + Debugprintf("Playback Devices\n"); + + // free old struct if called again + +// while (WriteDeviceCount) +// { +// WriteDeviceCount--; +// free(WriteDevices[WriteDeviceCount]); +// } + +// if (WriteDevices) +// free(WriteDevices); + + WriteDevices = NULL; + WriteDeviceCount = 0; + + // Add virtual device ARDOP so ALSA plugins can be used if needed + + WriteDevices = realloc(WriteDevices,(WriteDeviceCount + 1) * sizeof(WriteDevices)); + + // Get Device List from ALSA + + snd_ctl_card_info_alloca(&info); + snd_pcm_info_alloca(&pcminfo); + snd_pcm_hw_params_alloca(&pars); + snd_pcm_format_mask_alloca(&fmask); + + char hwdev[80]; + unsigned min, max, ratemin, ratemax; + int card, err, dev, nsubd; + snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK; + + card = -1; + + if (snd_card_next(&card) < 0) + { + Debugprintf("No Devices"); + return 0; + } + + while (card >= 0) + { + sprintf(hwdev, "hw:%d", card); + err = snd_ctl_open(&handle, hwdev, 0); + err = snd_ctl_card_info(handle, info); + + Debugprintf("Card %d, ID `%s', name `%s'", card, snd_ctl_card_info_get_id(info), + snd_ctl_card_info_get_name(info)); + + + dev = -1; + + if(snd_ctl_pcm_next_device(handle, &dev) < 0) + { + // Card has no devices + + snd_ctl_close(handle); + + WriteDevices = realloc(WriteDevices,(WriteDeviceCount + 1) * sizeof(WriteDevices)); + WriteDevices[WriteDeviceCount++] = strupr(strdup("DummyDevice")); + + goto nextcard; + } + + while (dev >= 0) + { + snd_pcm_info_set_device(pcminfo, dev); + snd_pcm_info_set_subdevice(pcminfo, 0); + snd_pcm_info_set_stream(pcminfo, stream); + + err = snd_ctl_pcm_info(handle, pcminfo); + + + if (err == -ENOENT) + goto nextdevice; + + nsubd = snd_pcm_info_get_subdevices_count(pcminfo); + + Debugprintf(" Device hw:%d,%d ID `%s', name `%s', %d subdevices (%d available)", + card, dev, snd_pcm_info_get_id(pcminfo), snd_pcm_info_get_name(pcminfo), + nsubd, snd_pcm_info_get_subdevices_avail(pcminfo)); + + sprintf(hwdev, "hw:%d,%d", card, dev); + + err = snd_pcm_open(&pcm, hwdev, stream, SND_PCM_NONBLOCK); + + if (err) + { + Debugprintf("Error %d opening output device", err); + goto nextdevice; + } + + // Get parameters for this device + + err = snd_pcm_hw_params_any(pcm, pars); + + snd_pcm_hw_params_get_channels_min(pars, &min); + snd_pcm_hw_params_get_channels_max(pars, &max); + + snd_pcm_hw_params_get_rate_min(pars, &ratemin, NULL); + snd_pcm_hw_params_get_rate_max(pars, &ratemax, NULL); + + if( min == max ) + if( min == 1 ) + Debugprintf(" 1 channel, sampling rate %u..%u Hz", ratemin, ratemax); + else + Debugprintf(" %d channels, sampling rate %u..%u Hz", min, ratemin, ratemax); + else + Debugprintf(" %u..%u channels, sampling rate %u..%u Hz", min, max, ratemin, ratemax); + + // Add device to list + + sprintf(NameString, "hw:%d,%d %s(%s)", card, dev, + snd_pcm_info_get_name(pcminfo), snd_ctl_card_info_get_name(info)); + + WriteDevices = realloc(WriteDevices,(WriteDeviceCount + 1) * sizeof(WriteDevices)); + WriteDevices[WriteDeviceCount++] = strupr(strdup(NameString)); + + snd_pcm_close(pcm); + pcm= NULL; + +nextdevice: + if (snd_ctl_pcm_next_device(handle, &dev) < 0) + break; + } + snd_ctl_close(handle); + +nextcard: + + Debugprintf(""); + + if (snd_card_next(&card) < 0) // No more cards + break; + } + + return WriteDeviceCount; +} + +int GetNextOutputDevice(char * dest, int max, int n) +{ + if (n >= WriteDeviceCount) + return 0; + + strcpy(dest, WriteDevices[n]); + return strlen(dest); +} + + +int GetInputDeviceCollection() +{ + // Get all the suitable devices and put in a list for GetNext to return + + snd_ctl_t *handle= NULL; + snd_pcm_t *pcm= NULL; + snd_ctl_card_info_t *info; + snd_pcm_info_t *pcminfo; + snd_pcm_hw_params_t *pars; + snd_pcm_format_mask_t *fmask; + char NameString[256]; + + Debugprintf("Capture Devices\n"); + + ReadDevices = NULL; + ReadDeviceCount = 0; + + ReadDevices = realloc(ReadDevices,(ReadDeviceCount + 1) * sizeof(ReadDevices)); + + // Get Device List from ALSA + + snd_ctl_card_info_alloca(&info); + snd_pcm_info_alloca(&pcminfo); + snd_pcm_hw_params_alloca(&pars); + snd_pcm_format_mask_alloca(&fmask); + + char hwdev[80]; + unsigned min, max, ratemin, ratemax; + int card, err, dev, nsubd; + snd_pcm_stream_t stream = SND_PCM_STREAM_CAPTURE; + + card = -1; + + if(snd_card_next(&card) < 0) + { + Debugprintf("No Devices"); + return 0; + } + + while(card >= 0) + { + sprintf(hwdev, "hw:%d", card); + err = snd_ctl_open(&handle, hwdev, 0); + err = snd_ctl_card_info(handle, info); + +// Debugprintf("Card %d, ID `%s', name `%s'", card, snd_ctl_card_info_get_id(info), +// snd_ctl_card_info_get_name(info)); + + dev = -1; + + if (snd_ctl_pcm_next_device(handle, &dev) < 0) // No Devicdes + { + snd_ctl_close(handle); + + ReadDevices = realloc(ReadDevices,(ReadDeviceCount + 1) * sizeof(ReadDevices)); + ReadDevices[ReadDeviceCount++] = strupr(strdup("DummyDevice")); + + Debugprintf("%d %s", ReadDeviceCount, "DummyDevice"); + goto nextcard; + } + + while(dev >= 0) + { + snd_pcm_info_set_device(pcminfo, dev); + snd_pcm_info_set_subdevice(pcminfo, 0); + snd_pcm_info_set_stream(pcminfo, stream); + err= snd_ctl_pcm_info(handle, pcminfo); + + if (err == -ENOENT) + goto nextdevice; + + nsubd= snd_pcm_info_get_subdevices_count(pcminfo); +// Debugprintf(" Device hw:%d,%d ID `%s', name `%s', %d subdevices (%d available)", +// card, dev, snd_pcm_info_get_id(pcminfo), snd_pcm_info_get_name(pcminfo), +// nsubd, snd_pcm_info_get_subdevices_avail(pcminfo)); + + sprintf(hwdev, "hw:%d,%d", card, dev); +/* + err = snd_pcm_open(&pcm, hwdev, stream, SND_PCM_NONBLOCK); + + if (err) + { + Debugprintf("Error %d opening input device", err); + goto nextdevice; + } + + err = snd_pcm_hw_params_any(pcm, pars); + + snd_pcm_hw_params_get_channels_min(pars, &min); + snd_pcm_hw_params_get_channels_max(pars, &max); + snd_pcm_hw_params_get_rate_min(pars, &ratemin, NULL); + snd_pcm_hw_params_get_rate_max(pars, &ratemax, NULL); + + if( min == max ) + if( min == 1 ) + Debugprintf(" 1 channel, sampling rate %u..%u Hz", ratemin, ratemax); + else + Debugprintf(" %d channels, sampling rate %u..%u Hz", min, ratemin, ratemax); + else + Debugprintf(" %u..%u channels, sampling rate %u..%u Hz", min, max, ratemin, ratemax); +*/ + sprintf(NameString, "hw:%d,%d %s(%s)", card, dev, + snd_pcm_info_get_name(pcminfo), snd_ctl_card_info_get_name(info)); + + Debugprintf("%d %s", ReadDeviceCount, NameString); + + ReadDevices = realloc(ReadDevices,(ReadDeviceCount + 1) * sizeof(ReadDevices)); + ReadDevices[ReadDeviceCount++] = strupr(strdup(NameString)); + +nextdevice: + if (snd_ctl_pcm_next_device(handle, &dev) < 0) + break; + } + snd_ctl_close(handle); +nextcard: + + Debugprintf(""); + if (snd_card_next(&card) < 0 ) + break; + } + return ReadDeviceCount; +} + +int GetNextInputDevice(char * dest, int max, int n) +{ + if (n >= ReadDeviceCount) + return 0; + + strcpy(dest, ReadDevices[n]); + return strlen(dest); +} + +#endif + + +// We need a local restart tnc as we need to add params and start a python progrm on Linux + +BOOL KillOldTNC(char * Path); + +static BOOL RestartTNC(struct TNCINFO * TNC) +{ + if (TNC->ProgramPath == NULL || TNC->DontRestart) + return 0; + + if (_memicmp(TNC->ProgramPath, "REMOTE:", 7) == 0) + { + int n; + + // Try to start TNC on a remote host + + SOCKET sock = socket(AF_INET,SOCK_DGRAM,0); + struct sockaddr_in destaddr; + + Debugprintf("trying to restart TNC %s", TNC->ProgramPath); + + if (sock == INVALID_SOCKET) + return 0; + + destaddr.sin_family = AF_INET; + destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + destaddr.sin_port = htons(8500); + + if (destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + struct hostent * HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) + return 0; // Resolve failed + + memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + + n = sendto(sock, TNC->ProgramPath, (int)strlen(TNC->ProgramPath), 0, (struct sockaddr *)&destaddr, sizeof(destaddr)); + + Debugprintf("Restart TNC - sendto returned %d", n); + + Sleep(100); + closesocket(sock); + + return 1; // Cant tell if it worked, but assume ok + } + + // Not Remote + + // Add parameters to command string + +#ifndef WIN32 + { + char * arg_list[64]; + char rxVal[16]; + char txVal[16]; + char tunePlus[16]; + char tuneMinus[16]; + char portVal[16]; + char txLevelVal[16]; + char RigPort[16]; + int n = 0; + + pid_t child_pid; + + struct FreeDataINFO * FDI = TNC->FreeDataInfo; + int capindex = -1, playindex = -1; + int i; + + if (ReadDeviceCount == 0) + { + GetOutputDeviceCollection(); + GetInputDeviceCollection(); + } + + // + + for (i = 0; i < ReadDeviceCount; i++) + { + printf("%s %s\n", &ReadDevices[i][0], TNC->FreeDataInfo->Capture); + if (strstr(&ReadDevices[i][0], TNC->FreeDataInfo->Capture)) + { + capindex = i; + break; + } + } + + + for (i = 0; i < WriteDeviceCount; i++) + { + printf("%s %s\n", &WriteDevices[i][0], TNC->FreeDataInfo->Playback); + + if (strstr(&WriteDevices[i][0], TNC->FreeDataInfo->Playback)) + { + playindex = i; + break; + } + } + + + capindex=playindex=3; + + printf("%d %d \n", capindex, playindex); + + signal(SIGCHLD, SIG_IGN); // Silently (and portably) reap children. + + arg_list[n++] = "python3"; + arg_list[n++] = TNC->ProgramPath; + arg_list[n++] = "--mycall"; + arg_list[n++] = FDI->ourCall; + arg_list[n++] = "--ssid"; + arg_list[n++] = FDI->SSIDList; + arg_list[n++] = "--mygrid"; + arg_list[n++] = LOC; + arg_list[n++] = "--rx"; + sprintf(rxVal, "%d", capindex); + arg_list[n++] = rxVal; + arg_list[n++] = "--tx"; + sprintf(txVal, "%d", playindex); + arg_list[n++] = txVal; + arg_list[n++] = "--port"; + sprintf(portVal, "%d", TNC->TCPPort); + arg_list[n++] = portVal; + arg_list[n++] = "--radiocontrol"; + arg_list[n++] = FDI->hamlibHost ? "rigctld" : "disabled"; + arg_list[n++] = "--tuning_range_fmin"; + sprintf(tuneMinus, "%3.1f", FDI->TuningRange * -1.0); + arg_list[n++] = tuneMinus; + arg_list[n++] = "--tuning_range_fmax"; + sprintf(tunePlus, "%3.1f", FDI->TuningRange * 1.0); + arg_list[n++] = tunePlus; + arg_list[n++] = "--tx-audio-level"; + sprintf(txLevelVal, "%d", FDI->TXLevel); + arg_list[n++] = txLevelVal; + + if (FDI->hamlibHost) + { + arg_list[n++] = "--rigctld_ip"; + arg_list[n++] = FDI->hamlibHost; + arg_list[n++] = "--rigctld_port"; + sprintf(RigPort, "%d", FDI->hamlibPort); + arg_list[n++] = RigPort; + } + + if (FDI->LimitBandWidth) + arg_list[n++] = "--500hz"; + + if (FDI->Explorer) + arg_list[n++] = "--explorer"; + + arg_list[n++] = 0; + + n = 0; + + // Fork and Exec TNC + + printf("Trying to start %s\n", TNC->ProgramPath); + + /* Duplicate this process. */ + + child_pid = fork (); + + if (child_pid == -1) + { + printf ("StartTNC fork() Failed\n"); + return 0; + } + + if (child_pid == 0) + { + execvp (arg_list[0], arg_list); + + /* The execvp function returns only if an error occurs. */ + + printf ("Failed to start TNC\n"); + exit(0); // Kill the new process + } + printf("Started TNC\n"); + sleep(5000); + + return TRUE; + } +#else + + { + int n = 0; + + STARTUPINFO SInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION + char workingDirectory[256]; + char commandLine[512]; + char Params[512]; + + int i = strlen(TNC->ProgramPath); + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + Debugprintf("RestartTNC Called for %s", TNC->ProgramPath); + + + strcpy(workingDirectory, TNC->ProgramPath); + + while (i--) + { + if (workingDirectory[i] == '\\' || workingDirectory[i] == '/') + { + workingDirectory[i] = 0; + break; + } + } + + buildParamString(TNC, Params); + + if (TNC->PID) + { + KillTNC(TNC); + Sleep(100); + } + + sprintf(commandLine, "\"%s\" %s", TNC->ProgramPath, Params); + + if (CreateProcess(NULL, commandLine, NULL, NULL, FALSE,0, NULL, workingDirectory, &SInfo, &PInfo)) + { + Debugprintf("Restart TNC OK"); + TNC->PID = PInfo.dwProcessId; + return TRUE; + } + else + { + Debugprintf("Restart TNC Failed %d ", GetLastError()); + return FALSE; + } + } +#endif + return 0; +} diff --git a/HALDriver.c b/HALDriver.c index 23196fd..0282457 100644 --- a/HALDriver.c +++ b/HALDriver.c @@ -837,9 +837,9 @@ VOID HALPoll(int Port) UCHAR * MsgPtr; unsigned char TXMsg[500]; - buffptr = (UINT * )STREAM->BPQtoPACTOR_Q; - datalen=buffptr->Len; - MsgPtr = (UCHAR *)buffptr->Data; + buffptr = (PMSGWITHLEN)STREAM->BPQtoPACTOR_Q; + datalen = buffptr->Len; + MsgPtr = buffptr->Data; if (STREAM->Connected) { diff --git a/HFCommon.c b/HFCommon.c index 6dff2b0..106228b 100644 --- a/HFCommon.c +++ b/HFCommon.c @@ -65,6 +65,7 @@ VOID 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; @@ -210,7 +211,8 @@ LRESULT CALLBACK PacWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPara { if (TNC->ProgramPath) { - if (strstr(TNC->ProgramPath, " TNC") || strstr(TNC->ProgramPath, "ARDOP") || strstr(TNC->ProgramPath, "VARA")) + 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); @@ -311,7 +313,6 @@ LRESULT CALLBACK PacWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPara case WM_HSCROLL: { - DWORD dwPos; // current position of slider char value[16]; switch (LOWORD(wParam)) @@ -765,10 +766,10 @@ IdTag (random alphanumeric, 12 chars) } } - if (ADIF == NULL || ADIF->LOC[0] == 0) + if (ADIF == NULL || ADIF->LOC[0] == 0 || ADIF->Call[0] == 0) return TRUE; - if (ADIF->StartTime == 0 || ADIF->ServerSID[0] == 0) + if (ADIF->StartTime == 0 || ADIF->ServerSID[0] == 0 || ADIF->CMSCall[0] == 0) return TRUE; T = time(NULL); @@ -1853,6 +1854,9 @@ int standardParams(struct TNCINFO * TNC, char * buf) 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) diff --git a/HanksRT.c b/HanksRT.c index d01852b..0a8eca5 100644 --- a/HanksRT.c +++ b/HanksRT.c @@ -1259,6 +1259,7 @@ void chkctl(ChatCIRCUIT *ckt_from, char * Buffer, int Len) user->lastrealmsgtime = user->lastmsgtime = time(NULL); text_tellu(user, f1, NULL, o_topic); + HistoryCount = AddtoHistory(user, f1); for (ckt_to = circuit_hd; ckt_to; ckt_to = ckt_to->next) { diff --git a/MailNode.vcproj.DESKTOP-TGEL8RC.John.user b/MailNode.vcproj.DESKTOP-TGEL8RC.John.user index 32abbfd..40182c4 100644 --- a/MailNode.vcproj.DESKTOP-TGEL8RC.John.user +++ b/MailNode.vcproj.DESKTOP-TGEL8RC.John.user @@ -1,65 +1,65 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/MailNode.vcproj.SKIGACER.johnw.user b/MailNode.vcproj.SKIGACER.johnw.user index 8da606c..7be11b6 100644 --- a/MailNode.vcproj.SKIGACER.johnw.user +++ b/MailNode.vcproj.SKIGACER.johnw.user @@ -1,65 +1,65 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/RCa22388 b/RCa22388 new file mode 100644 index 0000000..e5492c1 Binary files /dev/null and b/RCa22388 differ diff --git a/SCSTracker.c b/SCSTracker.c index 3f53000..b08e5a6 100644 --- a/SCSTracker.c +++ b/SCSTracker.c @@ -857,6 +857,7 @@ static void DEDCheckRX(struct TNCINFO * TNC) TNC->HOSTSTATE = 0; TNC->Timeout = 0; TNC->RXLen = 0; + TNC->TermReinitCount = 0; return; } @@ -1052,15 +1053,23 @@ VOID DEDPoll(int Port) // Can't use retries, as we have no way of detecting lost chars. Have to re-init on timeout -// if (TNC->HostMode == 0 || TNC->ReinitState == 10) // 10 is Recovery Mode -// { -// DoTermModeTimeout(TNC); -// return; -// } + if (TNC->HostMode == 0 || TNC->ReinitState == 10) // 10 is Recovery Mode + { + TNC->TermReinitCount++; + + if (TNC->TermReinitCount == 10) + goto reinit; + + DoTermModeTimeout(TNC); + return; + } // Timed out in host mode - Clear any connection and reinit the TNC Debugprintf("DEDHOST - Link to TNC Lost Port %d", TNC->Port); + +reinit: + TNC->TNCOK = FALSE; sprintf(TNC->WEB_COMMSSTATE, "%s Open but TNC not responding", TNC->PortRecord->PORTCONTROL.SerialPortName); @@ -1069,6 +1078,7 @@ VOID DEDPoll(int Port) TNC->HostMode = 0; TNC->ReinitState = 0; + TNC->TermReinitCount = 0; CloseCOMPort(TNC->hDevice); OpenCOMMPort(TNC, TNC->PortRecord->PORTCONTROL.SerialPortName, TNC->PortRecord->PORTCONTROL.BAUDRATE, TRUE); @@ -1388,7 +1398,7 @@ VOID DEDPoll(int Port) } Poll[2] = datalen - 1; - memcpy(&Poll[3], buffptr + 2, datalen); + memcpy(&Poll[3], buffptr->Data, datalen); ReleaseBuffer(buffptr); @@ -1612,6 +1622,7 @@ VOID DoTermModeTimeout(struct TNCINFO * TNC) TNC->hDevice =(HANDLE)0; TNC->ReopenTimer = 290; TNC->HostMode = FALSE; + TNC->TermReinitCount = 0; return; } @@ -1623,6 +1634,7 @@ VOID DoTermModeTimeout(struct TNCINFO * TNC) TNC->HostMode = TRUE; TNC->IntCmdDelay = 10; + TNC->TermReinitCount = 0; return; } @@ -1698,6 +1710,7 @@ VOID TrkProcessTermModeResponse(struct TNCINFO * TNC) { TNC->HostMode = TRUE; TNC->Timeout = 0; + TNC->TermReinitCount = 0; } return; } @@ -1726,6 +1739,7 @@ VOID TrkProcessDEDFrame(struct TNCINFO * TNC) TNC->ReinitState = 0; TNC->RXLen = 0; TNC->HOSTSTATE = 0; + TNC->TermReinitCount = 0; Debugprintf("TRK - Resync Complete"); return; diff --git a/VARA.c b/VARA.c index 4636579..eeba91c 100644 --- a/VARA.c +++ b/VARA.c @@ -865,8 +865,8 @@ void CountRestarts(struct TNCINFO * TNC) 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); + //MySetWindowText(TNC->xIDC_RESTARTTIME, Time); + //strcpy(TNC->WEB_RESTARTTIME, Time); sprintf_s(Time, sizeof(Time),"%d", TNC->Restarts); MySetWindowText(TNC->xIDC_RESTARTS, Time); diff --git a/Versions.h b/Versions.h index 10640c7..74f6219 100644 --- a/Versions.h +++ b/Versions.h @@ -10,8 +10,8 @@ #endif -#define KVers 6,0,23,30 -#define KVerstring "6.0.23.30\0" +#define KVers 6,0,23,33 +#define KVerstring "6.0.23.33\0" #ifdef CKernel diff --git a/WINMOR.c b/WINMOR.c index 12078f3..a18a97c 100644 --- a/WINMOR.c +++ b/WINMOR.c @@ -608,7 +608,7 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) } } - if (TNC->BusyDelay) + if (TNC->ConnectCmd && TNC->BusyDelay) { // Still Busy? @@ -626,6 +626,8 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); free(TNC->ConnectCmd); + TNC->ConnectCmd = 0; + TNC->BusyDelay = 0; } else @@ -3013,13 +3015,13 @@ BOOL RestartTNC(struct TNCINFO * TNC) return TRUE; } #else - { int n = 0; STARTUPINFO SInfo; // pointer to STARTUPINFO PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION -// char workingDirectory[256]; + char workingDirectory[256]; + int i = strlen(TNC->ProgramPath); SInfo.cb=sizeof(SInfo); SInfo.lpReserved=NULL; @@ -3031,13 +3033,23 @@ BOOL RestartTNC(struct TNCINFO * TNC) Debugprintf("RestartTNC Called for %s", TNC->ProgramPath); + strcpy(workingDirectory, TNC->ProgramPath); + + while (i--) + { + if (workingDirectory[i] == '\\' || workingDirectory[i] == '/') + { + workingDirectory[i] = 0; + break; + } + } + while (KillOldTNC(TNC->ProgramPath) && n++ < 100) { Sleep(100); } - - if (CreateProcess(NULL, TNC->ProgramPath, NULL, NULL, FALSE,0 ,NULL ,NULL, &SInfo, &PInfo)) + if (CreateProcess(NULL, TNC->ProgramPath, NULL, NULL, FALSE,0, NULL, workingDirectory, &SInfo, &PInfo)) { Debugprintf("Restart TNC OK"); TNC->PID = PInfo.dwProcessId; diff --git a/WPRoutines.c b/WPRoutines.c index 97c8828..856c0ec 100644 --- a/WPRoutines.c +++ b/WPRoutines.c @@ -49,6 +49,38 @@ WPRec * AllocateWPRecord() return WP; } +int BadCall(char * Call) +{ + if (_stricmp(Call, "RMS") == 0) + return 1; + + if (_stricmp(Call, "SYSTEM") == 0) + return 1; + + if (_stricmp(Call, "SWITCH") == 0) + return 1; + + if (_stricmp(Call, "SYSOP") == 0) + return 1; + + if (_memicmp(Call, "SMTP", 4) == 0) + return 1; + + if (_memicmp(Call, "SMTP:", 5) == 0) + return 1; + + if (_stricmp(Call, "AMPR") == 0) + return 1; + + if (_stricmp(Call, "FILE") == 0) + return 1; + + if (_memicmp(Call, "MCAST", 5) == 0) + return 1; + + return 0; +} + extern config_t cfg; VOID GetWPDatabase() @@ -267,31 +299,7 @@ WPOK:; if (strchr(WPRec.callsign, ':')) continue; - if (_stricmp(WPRec.callsign, "RMS") == 0) - continue; - - if (_stricmp(WPRec.callsign, "SYSTEM") == 0) - continue; - - if (_stricmp(WPRec.callsign, "SWITCH") == 0) - continue; - - if (_stricmp(WPRec.callsign, "SYSOP") == 0) - continue; - - if (_memicmp(WPRec.callsign, "SMTP", 4) == 0) - continue; - - if (_memicmp(WPRec.callsign, "SMTP:", 5) == 0) - continue; - - if (_stricmp(WPRec.callsign, "AMPR") == 0) - continue; - - if (_stricmp(WPRec.callsign, "FILE") == 0) - continue; - - if (_memicmp(WPRec.callsign, "MCAST", 5) == 0) + if (BadCall(WPRec.callsign)) continue; WP = LookupWP(WPRec.callsign); @@ -354,31 +362,7 @@ Next: if (strchr(WPRec.callsign, ':')) goto Next; - if (_stricmp(WPRec.callsign, "RMS") == 0) - goto Next; - - if (_stricmp(WPRec.callsign, "SYSTEM") == 0) - goto Next; - - if (_stricmp(WPRec.callsign, "SWITCH") == 0) - goto Next; - - if (_stricmp(WPRec.callsign, "SYSOP") == 0) - goto Next; - - if (_memicmp(WPRec.callsign, "SMTP", 4) == 0) - goto Next; - - if (_memicmp(WPRec.callsign, "SMTP:", 5) == 0) - goto Next; - - if (_stricmp(WPRec.callsign, "AMPR") == 0) - goto Next; - - if (_stricmp(WPRec.callsign, "FILE") == 0) - goto Next; - - if (_memicmp(WPRec.callsign, "MCAST", 5) == 0) + if (BadCall(WPRec.callsign)) goto Next; WP = LookupWP(WPRec.callsign); @@ -800,31 +784,7 @@ VOID GetWPBBSInfo(char * Rline) memcpy(QTH, ptr2 + 1, ptr1 - ptr2 - 1); } - if (_stricmp(Call, "RMS") == 0) - return; - - if (_stricmp(Call, "SYSTEM") == 0) - return; - - if (_stricmp(Call, "SWITCH") == 0) - return; - - if (_stricmp(Call, "SYSOP") == 0) - return; - - if (_memicmp(Call, "SMTP", 4) == 0) - return; - - if (_memicmp(Call, "SMTP:", 5) == 0) - return; - - if (_stricmp(Call, "AMPR") == 0) - return; - - if (_stricmp(Call, "FILE") == 0) - return; - - if (_memicmp(Call, "MCAST", 5) == 0) + if (BadCall(Call)) return; WP = LookupWP(Call); @@ -898,6 +858,9 @@ VOID GetWPInfoFromRLine(char * From, char * FirstRLine, time_t RLineTime) char * ptr1 = strchr(FirstRLine, '@'); char * ptr2 = strchr(FirstRLine, '\r'); + if (BadCall(From)) + return; + if (!ptr1) return; // Duff @@ -1074,31 +1037,7 @@ it will not be replaced. This flag will be used in case the WP update messages a if (strchr(Call, ':')) break; - if (_stricmp(Call, "RMS") == 0) - break; - - if (_stricmp(Call, "SWITCH") == 0) - break; - - if (_stricmp(Call, "SYSTEM") == 0) - break; - - if (_stricmp(Call, "SYSOP") == 0) - break; - - if (_memicmp(Call, "SMTP", 4) == 0) - break; - - if (_memicmp(Call, "SMTP:", 5) == 0) - break; - - if (_stricmp(Call, "AMPR") == 0) - break; - - if (_stricmp(Call, "FILE") == 0) - break; - - if (_memicmp(Call, "MCAST", 5) == 0) + if (BadCall(Call)) break; WP = LookupWP(Call); @@ -1242,37 +1181,7 @@ VOID UpdateWPWithUserInfo(struct UserInfo * user) if (strchr(user->Call, ':')) return; - if (_stricmp(user->Call, "RMS") == 0) - return; - - if (_stricmp(user->Call, "SYSTEM") == 0) - return; - - if (_stricmp(user->Call, "SWITCH") == 0) - return; - - if (_stricmp(user->Call, "SYSOP") == 0) - return; - - if (_memicmp(user->Call, "SMTP", 4) == 0) - return; - - if (_memicmp(user->Call, "SMTP:", 5) == 0) - return; - - if (_stricmp(user->Call, "AMPR") == 0) - return; - - if (_stricmp(user->Call, "FILE") == 0) - return; - - if (_stricmp(user->Call, "SYNC") == 0) - return; - - if (_memicmp(user->Call, "MCAST", 5) == 0) - return; - - if (_stricmp(user->Call, user->Name) == 0) + if (BadCall(user->Call)) return; if (!WP) diff --git a/WinRPR.c b/WinRPR.c index c0ab501..25609c1 100644 --- a/WinRPR.c +++ b/WinRPR.c @@ -245,7 +245,7 @@ static BOOL RestartTNC(struct TNCINFO * TNC) Sleep(100); } - if (CreateProcess(NULL, TNC->ProgramPath, NULL, NULL, FALSE,0 ,NULL ,workingDirectory, &SInfo, &PInfo)) + if (CreateProcess(NULL, TNC->ProgramPath, NULL, NULL, FALSE,0, NULL, workingDirectory, &SInfo, &PInfo)) { Debugprintf("Restart TNC OK"); TNC->PID = PInfo.dwProcessId; diff --git a/WinmorControl.vcproj.SKIGACER.johnw.user b/WinmorControl.vcproj.SKIGACER.johnw.user index bbece07..b5b0536 100644 --- a/WinmorControl.vcproj.SKIGACER.johnw.user +++ b/WinmorControl.vcproj.SKIGACER.johnw.user @@ -1,65 +1,65 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/bpqchat.c b/bpqchat.c index 904a5dd..6a28e7d 100644 --- a/bpqchat.c +++ b/bpqchat.c @@ -64,6 +64,8 @@ // Check for and remove names set to *RTL // Add option to run user program when chat user connects (27) // Add History (28) +// Add connect scripts to config page text (31) +// Fix History (31) #include "BPQChat.h" #include "Dbghelp.h" @@ -1010,7 +1012,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) break; case IDM_CONFIG: - i = DialogBox(hInst, MAKEINTRESOURCE(CHAT_CONFIG), hWnd, ConfigWndProc); + i = DialogBox(hInst, MAKEINTRESOURCE(CHAT_CONFIG), hWnd, ConfigWndProc); i = WSAGetLastError(); break; diff --git a/bpqmail.h b/bpqmail.h index f6ce20e..603d850 100644 --- a/bpqmail.h +++ b/bpqmail.h @@ -20,7 +20,7 @@ #define _WINSOCK_DEPRECATED_NO_WARNINGS #define LIBCONFIG_STATIC -#include "libconfig.h" +#include #include "compatbits.h" diff --git a/config.ini b/config.ini new file mode 100644 index 0000000..cfefd65 --- /dev/null +++ b/config.ini @@ -0,0 +1,40 @@ +[NETWORK] +#network settings +tncport = 3004 + +[STATION] +#station settings +mycall = GM8BPQ-2 +mygrid = IO68VL + +[AUDIO] +#audio settings +rx = 8 +tx = 16 +txaudiolevel = 125 + +[RADIO] +#radio settings +radiocontrol = disabled +devicename = RIG_MODEL_NETRIGCTL +deviceport = COM99 +serialspeed = 19200 +pttprotocol = USB +pttport = COM99 +data_bits = 8 +stop_bits = 1 +handshake = None +rigctld_ip = 127.0.0.1 +rigctld_port = 0 + +[TNC] +#tnc settings +scatter = False +fft = False +narrowband = True +fmin = -50.0 +fmax = 50.0 +qrv = True +rxbuffersize = 16 +explorer = False + diff --git a/libconfig.h b/libconfig.h deleted file mode 100644 index 08414ce..0000000 --- a/libconfig.h +++ /dev/null @@ -1,321 +0,0 @@ -/* ---------------------------------------------------------------------------- - libconfig - A library for processing structured configuration files - Copyright (C) 2005-2010 Mark A Lindner - - This file is part of libconfig. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public License - as published by the Free Software Foundation; either version 2.1 of - the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, see - . - ---------------------------------------------------------------------------- -*/ - -#ifndef __libconfig_h -#define __libconfig_h - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) -#if defined(LIBCONFIG_STATIC) -#define LIBCONFIG_API -#elif defined(LIBCONFIG_EXPORTS) -#define LIBCONFIG_API __declspec(dllexport) -#else /* ! LIBCONFIG_EXPORTS */ -#define LIBCONFIG_API __declspec(dllimport) -#endif /* LIBCONFIG_STATIC */ -#else /* ! WIN32 */ -#define LIBCONFIG_API -#endif /* WIN32 */ - -#define LIBCONFIG_VER_MAJOR 1 -#define LIBCONFIG_VER_MINOR 4 -#define LIBCONFIG_VER_REVISION 9 - -#include - -#define CONFIG_TYPE_NONE 0 -#define CONFIG_TYPE_GROUP 1 -#define CONFIG_TYPE_INT 2 -#define CONFIG_TYPE_INT64 3 -#define CONFIG_TYPE_FLOAT 4 -#define CONFIG_TYPE_STRING 5 -#define CONFIG_TYPE_BOOL 6 -#define CONFIG_TYPE_ARRAY 7 -#define CONFIG_TYPE_LIST 8 - -#define CONFIG_FORMAT_DEFAULT 0 -#define CONFIG_FORMAT_HEX 1 - -#define CONFIG_OPTION_AUTOCONVERT 0x01 - -#define CONFIG_TRUE (1) -#define CONFIG_FALSE (0) - -typedef union config_value_t -{ - int ival; - long long llval; - double fval; - char *sval; - struct config_list_t *list; -} config_value_t; - -typedef struct config_setting_t -{ - char *name; - short type; - short format; - config_value_t value; - struct config_setting_t *parent; - struct config_t *config; - void *hook; - unsigned int line; - const char *file; -} config_setting_t; - -typedef enum -{ - CONFIG_ERR_NONE = 0, - CONFIG_ERR_FILE_IO = 1, - CONFIG_ERR_PARSE = 2 -} config_error_t; - -typedef struct config_list_t -{ - unsigned int length; - config_setting_t **elements; -} config_list_t; - -typedef struct config_t -{ - config_setting_t *root; - void (*destructor)(void *); - unsigned short flags; - unsigned short tab_width; - short default_format; - const char *include_dir; - const char *error_text; - const char *error_file; - int error_line; - config_error_t error_type; - const char **filenames; - unsigned int num_filenames; -} config_t; - -extern LIBCONFIG_API int config_read(config_t *config, FILE *stream); -extern LIBCONFIG_API void config_write(const config_t *config, FILE *stream); - -extern LIBCONFIG_API void config_set_default_format(config_t *config, - short format); - -extern LIBCONFIG_API void config_set_auto_convert(config_t *config, int flag); -extern LIBCONFIG_API int config_get_auto_convert(const config_t *config); - -extern LIBCONFIG_API int config_read_string(config_t *config, const char *str); - -extern LIBCONFIG_API int config_read_file(config_t *config, - const char *filename); -extern LIBCONFIG_API int config_write_file(config_t *config, - const char *filename); - -extern LIBCONFIG_API void config_set_destructor(config_t *config, - void (*destructor)(void *)); -extern LIBCONFIG_API void config_set_include_dir(config_t *config, - const char *include_dir); - -extern LIBCONFIG_API void config_init(config_t *config); -extern LIBCONFIG_API void config_destroy(config_t *config); - -extern LIBCONFIG_API int config_setting_get_int( - const config_setting_t *setting); -extern LIBCONFIG_API long long config_setting_get_int64( - const config_setting_t *setting); -extern LIBCONFIG_API double config_setting_get_float( - const config_setting_t *setting); -extern LIBCONFIG_API int config_setting_get_bool( - const config_setting_t *setting); -extern LIBCONFIG_API const char *config_setting_get_string( - const config_setting_t *setting); - -extern LIBCONFIG_API int config_setting_lookup_int( - const config_setting_t *setting, const char *name, int *value); -extern LIBCONFIG_API int config_setting_lookup_int64( - const config_setting_t *setting, const char *name, long long *value); -extern LIBCONFIG_API int config_setting_lookup_float( - const config_setting_t *setting, const char *name, double *value); -extern LIBCONFIG_API int config_setting_lookup_bool( - const config_setting_t *setting, const char *name, int *value); -extern LIBCONFIG_API int config_setting_lookup_string( - const config_setting_t *setting, const char *name, const char **value); - -extern LIBCONFIG_API int config_setting_set_int(config_setting_t *setting, - int value); -extern LIBCONFIG_API int config_setting_set_int64(config_setting_t *setting, - long long value); -extern LIBCONFIG_API int config_setting_set_float(config_setting_t *setting, - double value); -extern LIBCONFIG_API int config_setting_set_bool(config_setting_t *setting, - int value); -extern LIBCONFIG_API int config_setting_set_string(config_setting_t *setting, - const char *value); - -extern LIBCONFIG_API int config_setting_set_format(config_setting_t *setting, - short format); -extern LIBCONFIG_API short config_setting_get_format( - const config_setting_t *setting); - -extern LIBCONFIG_API int config_setting_get_int_elem( - const config_setting_t *setting, int idx); -extern LIBCONFIG_API long long config_setting_get_int64_elem( - const config_setting_t *setting, int idx); -extern LIBCONFIG_API double config_setting_get_float_elem( - const config_setting_t *setting, int idx); -extern LIBCONFIG_API int config_setting_get_bool_elem( - const config_setting_t *setting, int idx); -extern LIBCONFIG_API const char *config_setting_get_string_elem( - const config_setting_t *setting, int idx); - -extern LIBCONFIG_API config_setting_t *config_setting_set_int_elem( - config_setting_t *setting, int idx, int value); -extern LIBCONFIG_API config_setting_t *config_setting_set_int64_elem( - config_setting_t *setting, int idx, long long value); -extern LIBCONFIG_API config_setting_t *config_setting_set_float_elem( - config_setting_t *setting, int idx, double value); -extern LIBCONFIG_API config_setting_t *config_setting_set_bool_elem( - config_setting_t *setting, int idx, int value); -extern LIBCONFIG_API config_setting_t *config_setting_set_string_elem( - config_setting_t *setting, int idx, const char *value); - -#define /* const char * */ config_get_include_dir(/* const config_t * */ C) \ - ((C)->include_dir) - -#define /* int */ config_setting_type(/* const config_setting_t * */ S) \ - ((S)->type) - -#define /* int */ config_setting_is_group(/* const config_setting_t * */ S) \ - ((S)->type == CONFIG_TYPE_GROUP) -#define /* int */ config_setting_is_array(/* const config_setting_t * */ S) \ - ((S)->type == CONFIG_TYPE_ARRAY) -#define /* int */ config_setting_is_list(/* const config_setting_t * */ S) \ - ((S)->type == CONFIG_TYPE_LIST) - -#define /* int */ config_setting_is_aggregate( \ - /* const config_setting_t * */ S) \ - (((S)->type == CONFIG_TYPE_GROUP) || ((S)->type == CONFIG_TYPE_LIST) \ - || ((S)->type == CONFIG_TYPE_ARRAY)) - -#define /* int */ config_setting_is_number(/* const config_setting_t * */ S) \ - (((S)->type == CONFIG_TYPE_INT) \ - || ((S)->type == CONFIG_TYPE_INT64) \ - || ((S)->type == CONFIG_TYPE_FLOAT)) - -#define /* int */ config_setting_is_scalar(/* const config_setting_t * */ S) \ - (((S)->type == CONFIG_TYPE_BOOL) || ((S)->type == CONFIG_TYPE_STRING) \ - || config_setting_is_number(S)) - -#define /* const char * */ config_setting_name( \ - /* const config_setting_t * */ S) \ - ((S)->name) - -#define /* config_setting_t * */ config_setting_parent( \ - /* const config_setting_t * */ S) \ - ((S)->parent) - -#define /* int */ config_setting_is_root( \ - /* const config_setting_t * */ S) \ - ((S)->parent ? CONFIG_FALSE : CONFIG_TRUE) - -extern LIBCONFIG_API int config_setting_index(const config_setting_t *setting); - -extern LIBCONFIG_API int config_setting_length( - const config_setting_t *setting); -extern LIBCONFIG_API config_setting_t *config_setting_get_elem( - const config_setting_t *setting, unsigned int idx); - -extern LIBCONFIG_API config_setting_t *config_setting_get_member( - const config_setting_t *setting, const char *name); - -extern LIBCONFIG_API config_setting_t *config_setting_add( - config_setting_t *parent, const char *name, int type); -extern LIBCONFIG_API int config_setting_remove(config_setting_t *parent, - const char *name); -extern LIBCONFIG_API int config_setting_remove_elem(config_setting_t *parent, - unsigned int idx); -extern LIBCONFIG_API void config_setting_set_hook(config_setting_t *setting, - void *hook); - -#define config_setting_get_hook(S) ((S)->hook) - -extern LIBCONFIG_API config_setting_t *config_lookup(const config_t *config, - const char *path); -extern LIBCONFIG_API config_setting_t *config_lookup_from( - config_setting_t *setting, const char *path); - -extern LIBCONFIG_API int config_lookup_int(const config_t *config, - const char *path, int *value); -extern LIBCONFIG_API int config_lookup_int64(const config_t *config, - const char *path, - long long *value); -extern LIBCONFIG_API int config_lookup_float(const config_t *config, - const char *path, double *value); -extern LIBCONFIG_API int config_lookup_bool(const config_t *config, - const char *path, int *value); -extern LIBCONFIG_API int config_lookup_string(const config_t *config, - const char *path, - const char **value); - -#define /* config_setting_t * */ config_root_setting( \ - /* const config_t * */ C) \ - ((C)->root) - -#define /* void */ config_set_default_format(/* config_t * */ C, \ - /* short */ F) \ - (C)->default_format = (F) - -#define /* short */ config_get_default_format(/* config_t * */ C) \ - ((C)->default_format) - -#define /* void */ config_set_tab_width(/* config_t * */ C, \ - /* unsigned short */ W) \ - (C)->tab_width = ((W) & 0x0F) - -#define /* unsigned char */ config_get_tab_width(/* const config_t * */ C) \ - ((C)->tab_width) - -#define /* unsigned short */ config_setting_source_line( \ - /* const config_setting_t * */ S) \ - ((S)->line) - -#define /* const char */ config_setting_source_file( \ - /* const config_setting_t * */ S) \ - ((S)->file) - -#define /* const char * */ config_error_text(/* const config_t * */ C) \ - ((C)->error_text) - -#define /* const char * */ config_error_file(/* const config_t * */ C) \ - ((C)->error_file) - -#define /* int */ config_error_line(/* const config_t * */ C) \ - ((C)->error_line) - -#define /* config_error_t */ config_error_type(/* const config_t * */ C) \ - ((C)->error_type) - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* __libconfig_h */ diff --git a/makefile b/makefile index 6abbbdc..d5c3f5e 100644 --- a/makefile +++ b/makefile @@ -28,7 +28,7 @@ noi2c: linbpq linbpq: $(OBJS) - gcc $(OBJS) -Xlinker -Map=output.map -l:libminiupnpc.a -lcrypto -lrt -lm -lz -lpthread -lconfig -lpcap -o linbpq + gcc $(OBJS) -Xlinker -Map=output.map -l:libminiupnpc.a -lcrypto -lrt -lm -lz -lpthread -lconfig -lpcap -lasound -o linbpq sudo setcap "CAP_NET_ADMIN=ep CAP_NET_RAW=ep CAP_NET_BIND_SERVICE=ep" linbpq -include *.d diff --git a/makefile~ b/makefile~ index fdd6a38..6abbbdc 100644 --- a/makefile~ +++ b/makefile~ @@ -13,7 +13,7 @@ OBJS = pngwtran.o pngrtran.o pngset.o pngrio.o pngwio.o pngtrans.o pngrutil.o pn MailCommands.o MailDataDefs.o LinBPQ.o MailRouting.o MailTCP.o MBLRoutines.o md5.o Moncode.o \ NNTPRoutines.o RigControl.o TelnetV6.o WINMOR.o TNCCode.o UZ7HODrv.o WPRoutines.o \ SCSTrackeMulti.o SCSPactor.o SCSTracker.o HanksRT.o UIRoutines.o AGWAPI.o AGWMoncode.o \ - DRATS.o FreeDATA.o base64.o + DRATS.o FreeDATA.o base64.o Events.o # Configuration: @@ -28,7 +28,7 @@ noi2c: linbpq linbpq: $(OBJS) - gcc $(OBJS) -Xlinker -Map=output.map -l:libminiupnpc.a -lrt -lm -lz -lpthread -lconfig -lpcap -o linbpq + gcc $(OBJS) -Xlinker -Map=output.map -l:libminiupnpc.a -lcrypto -lrt -lm -lz -lpthread -lconfig -lpcap -o linbpq sudo setcap "CAP_NET_ADMIN=ep CAP_NET_RAW=ep CAP_NET_BIND_SERVICE=ep" linbpq -include *.d diff --git a/templatedefs.c b/templatedefs.c index bb2e495..a312d7f 100644 --- a/templatedefs.c +++ b/templatedefs.c @@ -1422,7 +1422,7 @@ char * ChatConfigtxt() "
Chat Configuration
\r\n" "
\r\n" + "style=\"align: center; border: 2px solid ; overflow: auto; text-align: center; position: relative; top: 10px; height: 600px; width: 700px; left: 96.5px;\">\r\n" "
\r\n" "

 Chat Server Params

\r\n" @@ -1431,10 +1431,13 @@ char * ChatConfigtxt() "Streams    \r\n" "  
\r\n" " 
\r\n" - " The Nodes to link to box defines which other Chat Nodes should be connected to, or from which " - "connections may be accepted. The format is ALIAS:CALL, eg BPQCHT:G8BPQ-4. Note these must be directly " - "connectable - ie in your NODES table.
\r\n" - "
\r\n" + "
The Nodes to link to box defines which other Chat Nodes should be connected to, or from which " + "connections may be accepted. The format is ALIAS:CALL, eg BPQCHT:G8BPQ-4. If the node is not directly " + "connectable (ie is not in your NODES table) you can add a connect script. This consists of a series of commands " + "seperared by |, eg NOTCHT:G8BPQ-4|C 3 GM8BPQ-9|CHAT" + + "

The Callsign of the Chat Node is not defined here - it is obtained from the bpq32.cfg APPLICATION line corresponding to the Chat Appl Number.
\r\n" + "
r\n" " 
\r\n" "
\r\n" " Map Position
\r\n" @@ -1449,7 +1452,7 @@ char * ChatConfigtxt() "
\r\n" "
\r\n" "\r\n" - "
\r\n" + "
\r\n" " \r\n" " \r\n" " \r\n" diff --git a/tncinfo.h b/tncinfo.h index ee03571..1e8d573 100644 --- a/tncinfo.h +++ b/tncinfo.h @@ -395,6 +395,9 @@ struct FreeDataINFO int arqstate; // 1 = Disc / 2 - connecting 3 - connected int TuningRange; // Must be 50, 100, 150, 200, 250 int LimitBandWidth; + int TXLevel; + int Explorer; // Enable reporting to Freedata Explorer + char SSIDList[256]; }; @@ -512,8 +515,6 @@ typedef struct TNCINFO BOOL TNCCONNECTING; // For FreeData BOOL TNCCONNECTED; - BOOL DAEMONCONNECTING; - BOOL DAEMONCONNECTED; char NodeCall[10]; // Call we listen for (PORTCALL or NODECALL char CurrentMYC[10]; // Save current call so we don't change it unnecessarily @@ -609,6 +610,7 @@ typedef struct TNCINFO char * InitPtr; // Next Command int ReinitState; // Reinit State Machine int ReinitCount; // Count for DED Recovery + int TermReinitCount; // Count for DED Term Mode Recovery BOOL TNCOK; // TNC is reponding int FramesOutstanding; // Frames Queued - used for flow control BOOL InternalCmd; // Last Command was generated internally