commit 983703190603caa30a8b1a65f70772dd5f9c09d6 Author: John Wiseman Date: Tue Aug 30 11:28:28 2022 +0100 First GIT Commit diff --git a/AGWCode.cpp b/AGWCode.cpp new file mode 100644 index 0000000..42f5f36 --- /dev/null +++ b/AGWCode.cpp @@ -0,0 +1,1546 @@ +// AGW Interface + +#include "QtTermTCP.h" +#include "TabDialog.h" +#include +#include +#include + +#ifndef WIN32 +#define strtok_s strtok_r +#endif + +#define UCHAR unsigned char +#define byte unsigned char + +myTcpSocket * AGWSock; + +extern QColor monRxText; +extern QColor monTxText; + +extern QColor outputText; +extern QColor EchoText; +extern QColor WarningText; + +extern QTabWidget *tabWidget; +extern QWidget * mythis; + +extern int AGWEnable; +extern int AGWMonEnable; +extern char AGWTermCall[12]; +extern char AGWBeaconDest[12]; +extern char AGWBeaconPath[80]; +extern int AGWBeaconInterval; +extern char AGWBeaconPorts[80]; +extern char AGWBeaconMsg[260]; + +extern char AGWHost[128]; +extern int AGWPortNum; +extern int AGWPaclen; + +extern char listenCText[4096]; +extern int ConnectBeep; + +extern QList _sessions; + +extern QLabel * Status1; +extern QLabel * Status2; +extern QLabel * Status3; +extern QLabel * Status4; + +extern QMenu *connectMenu; +extern QAction *discAction; +extern QAction *YAPPSend; + +extern QAction *actHost[17]; + +// Session Type Equates + +#define Term 1 +#define Mon 2 +#define Listen 4 + +extern int TermMode; + +#define Single 0 +#define MDI 1 +#define Tabbed 2 + +extern int singlemodeFormat; + +typedef struct AGWUser_t +{ + QTcpSocket *socket; + unsigned char data_in[8192]; + int data_in_len; + unsigned char AGW_frame_buf[512]; + int Monitor; + int Monitor_raw; + Ui_ListenSession * MonSess; // Window for Monitor info + +} AGWUser; + + +struct AGWHeader +{ + int Port; + unsigned char DataKind; + unsigned char filler2; + unsigned char PID; + unsigned char filler3; + char callfrom[10]; + char callto[10]; + int DataLength; + int reserved; +}; + +#define AGWHDDRRLEN sizeof(struct AGWHeader) + +typedef struct TAGWPort_t +{ + byte PID; + int port; + char corrcall[10]; + char mycall[10]; + int Active; + QTcpSocket * socket; + Ui_ListenSession * Sess; // Terminal Session + +} TAGWPort; + + +int AGWConnected = 0; +int AGWConnecting = 0; + +char * AGWPortList = NULL; + +#define max_sessions 8 + +TAGWPort AGWPort[max_sessions]; + +#define LSB 29 +#define MSB 30 +#define MON_ON '1' +#define MON_OFF '0' +#define MODE_OUR 0 +#define MODE_OTHER 1 +#define MODE_RETRY 2 + +// Don't think we will support more than one, but leave in option + +AGWUser *AGWUsers = NULL; // List of currently connected clients + +void AGW_add_socket(QTcpSocket * socket); +void AGW_Process_Input(AGWUser * AGW); +void Send_AGW_X_Frame(QTcpSocket* socket, char * CallFrom); +void Send_AGW_G_Frame(QTcpSocket* socket); +void Send_AGW_m_Frame(QTcpSocket* socket); + +Ui_ListenSession * FindFreeWindow(); +Ui_ListenSession * newWindow(QObject * parent, int Type, const char * Label = nullptr); +void AGW_frame_header(UCHAR * Msg, char AGWPort, char DataKind, unsigned char PID, const char * CallFrom, const char * CallTo, int Len); + + +void Debugprintf(const char * format, ...) +{ + char Mess[10000]; + va_list(arglist); + + va_start(arglist, format); + vsprintf(Mess, format, arglist); + qDebug() << Mess; + + return; +} + +int ConvToAX25(char * callsign, unsigned char * ax25call) +{ + int i; + + memset(ax25call, 0x40, 6); // in case short + ax25call[6] = 0x60; // default SSID + + for (i = 0; i < 7; i++) + { + if (callsign[i] == '-') + { + // + // process ssid and return + // + i = atoi(&callsign[i + 1]); + + if (i < 16) + { + ax25call[6] |= i << 1; + return (1); + } + return (0); + } + + if (callsign[i] == 0 || callsign[i] == 13 || callsign[i] == ' ' || callsign[i] == ',') + { + // + // End of call - no ssid + // + return (1); + } + + ax25call[i] = callsign[i] << 1; + } + + // + // Too many chars + // + + return (0); +} + + +int ConvFromAX25(unsigned char * incall, char * outcall) +{ + int in, out = 0; + unsigned char chr; + + memset(outcall, 0x20, 10); + + for (in = 0; in < 6; in++) + { + chr = incall[in]; + if (chr == 0x40) + break; + chr >>= 1; + outcall[out++] = chr; + } + + chr = incall[6]; // ssid + + if (chr == 0x42) + { + outcall[out++] = '-'; + outcall[out++] = 'T'; + return out; + } + + if (chr == 0x44) + { + outcall[out++] = '-'; + outcall[out++] = 'R'; + return out; + } + + chr >>= 1; + chr &= 15; + + if (chr > 0) + { + outcall[out++] = '-'; + if (chr > 9) + { + chr -= 10; + outcall[out++] = '1'; + } + chr += 48; + outcall[out++] = chr; + } + return (out); +} + +void doAGWBeacon() +{ + if (AGWBeaconDest[0] && AGWBeaconPorts[0]) + { + // Send as M or V depending on Digis + + UCHAR Msg[512]; + char ports[80]; + + char * ptr, * context; + int DataLen = (int)strlen(AGWBeaconMsg); + + strcpy(ports, AGWBeaconPorts); // strtok changes it + + + // Replace newlines with CR + + while ((ptr = strchr(AGWBeaconMsg, 10))) + *ptr = 13; + + ptr = strtok_s(ports, " ,", &context); + + if (AGWBeaconPath[0]) + { + + } + else + { + while (ptr) + { + AGW_frame_header(Msg, atoi(ptr) - 1, 'M', 240, AGWTermCall, AGWBeaconDest, DataLen); + + memcpy(&Msg[AGWHDDRRLEN], AGWBeaconMsg, DataLen); + DataLen += AGWHDDRRLEN; + AGWSock->write((char *)Msg, DataLen); + + ptr = strtok_s(NULL, " ,", &context); + } + } + } +} + +TAGWPort * get_free_port() +{ + int i = 0; + + while (i < max_sessions) + { + if (AGWPort[i].Active == 0) + return &AGWPort[i]; + + i++; + } + return nullptr; +} + +TAGWPort * findSession(int AGWChan, char * MyCall, char * OtherCall) +{ + int i = 0; + + TAGWPort * Sess = nullptr; + + while (i < max_sessions) + { + Sess = &AGWPort[i]; + + if (Sess->Active && Sess->port == AGWChan && strcmp(Sess->corrcall, OtherCall) == 0 && strcmp(Sess->mycall, MyCall) == 0) + return Sess; + + i++; + } + return nullptr; +} + + + +void QtTermTCP::AGWdisplayError(QAbstractSocket::SocketError socketError) +{ + switch (socketError) + { + case QAbstractSocket::RemoteHostClosedError: + break; + + case QAbstractSocket::HostNotFoundError: + QMessageBox::information(this, tr("QtTermTCP"), + tr("AGW host was not found. Please check the " + "host name and port settings.")); + + Status1->setText("AGW Connection Failed"); + + break; + + case QAbstractSocket::ConnectionRefusedError: + + Status1->setText("AGW Connection Refused"); + break; + + default: + + Status1->setText("AGW Connection Failed"); + } + + AGWConnecting = 0; + AGWConnected = 0; +} + +void QtTermTCP::AGWreadyRead() +{ + int Read; + unsigned char Buffer[4096]; + myTcpSocket* Socket = static_cast(QObject::sender()); + + // read the data from the socket + + Read = Socket->read((char *)Buffer, 4095); + + int AGWHeaderLen = sizeof(struct AGWHeader); + + AGWUser * AGW = NULL; + + AGW = AGWUsers; + + if (AGW == NULL) + return; + + memcpy(&AGW->data_in[AGW->data_in_len], Buffer, Read); + + AGW->data_in_len += Read; + + while (AGW->data_in_len >= AGWHeaderLen) // Make sure have at least header + { + struct AGWHeader * Hddr = (struct AGWHeader *)AGW->data_in; + + int AgwLen = Hddr->DataLength + AGWHeaderLen; + + if (AGW->data_in_len >= AgwLen) + { + // Have frame as well + + AGW_Process_Input(AGW); + + AGW->data_in_len -= AgwLen; + + memmove(AGW->data_in, &AGW->data_in[AgwLen], AGW->data_in_len); + } + else + return; // Wait for the data + } + +} + +void QtTermTCP::onAGWSocketStateChanged(QAbstractSocket::SocketState socketState) +{ + myTcpSocket* sender = static_cast(QObject::sender()); + Ui_ListenSession * Sess = (Ui_ListenSession *)sender->Sess; + int i; + + if (socketState == QAbstractSocket::UnconnectedState) + { + // Close any connections + + Status1->setText("AGW Disconnected"); + actHost[16]->setVisible(0); + + int i = 0; + + TAGWPort * AGW = nullptr; + + for (i = 0; i < max_sessions; i++) + { + AGW = &AGWPort[i]; + + if (AGW->Active && AGW->socket) + { + AGW->Active = 0; + AGW->socket = nullptr; + + Sess = AGW->Sess; + + if (Sess) + { + // Send Disconnected + + char Msg[] = "Disconnected\r"; + + WritetoOutputWindowEx(Sess, (unsigned char *)Msg, (int)strlen(Msg), + Sess->termWindow, &Sess->OutputSaveLen, Sess->OutputSave, WarningText); // Red + + if (TermMode == MDI) + { + if (Sess->SessionType == Mon) // Mon Only + Sess->setWindowTitle("Monitor Session Disconnected"); + else + Sess->setWindowTitle("Disconnected"); + } + else if (TermMode == Tabbed) + { + if (Sess->SessionType == Mon) // Mon Only + tabWidget->setTabText(Sess->Tab, "Monitor"); + else + { + char Label[16]; + sprintf(Label, "Sess %d", Sess->Tab + 1); + tabWidget->setTabText(Sess->Tab, Label); + } + } + else if (TermMode == Single) + { + if (Sess->SessionType == Mon) // Mon Only + mythis->setWindowTitle("Monitor Session Disconnected"); + else + mythis->setWindowTitle("Disconnected"); + } + + setMenus(false); + + // if Single Split or Monitor leave session allocated to AGW + + if (TermMode == Single && (Sess->SessionType & Mon)) + { + return; + } + + Sess->AGWSession = nullptr; + AGW->Sess = nullptr; + } + } + } + + // Free the monitor Window + + if (AGWUsers && AGWUsers->MonSess) + { + Sess = AGWUsers->MonSess; + + char Msg[] = "Disconnected\r"; + + WritetoOutputWindowEx(Sess, (unsigned char *)Msg, (int)strlen(Msg), + Sess->monWindow, &Sess->OutputSaveLen, Sess->OutputSave, WarningText); // Red + + if (TermMode == MDI) + Sess->setWindowTitle("Monitor Session Disconnected"); + + else if (TermMode == Tabbed) + tabWidget->setTabText(Sess->Tab, "Monitor"); + + Sess->AGWSession = nullptr; + AGWUsers->MonSess = nullptr; + } + + AGWConnected = 0; + } + else if (socketState == QAbstractSocket::ConnectedState) + { + AGWConnected = 1; + AGWConnecting = 0; + + Status1->setText("AGW Connected"); + + AGW_add_socket(sender); + + actHost[16]->setVisible(1); // Enable AGW Connect Line + + // Send X frame to register Term Call + + if (AGWTermCall[0]) + Send_AGW_X_Frame(sender, AGWTermCall); + + Send_AGW_G_Frame(sender); // Request Port List + + // Attach a Monitor Window if available + + Ui_ListenSession * Sess = NULL; + Ui_ListenSession * S; + + if (TermMode == MDI) + { + // See if an old session can be reused + + for (int i = 0; i < _sessions.size(); ++i) + { + S = _sessions.at(i); + + // for (Ui_ListenSession * S: _sessions) + // { + if ((S->SessionType == Mon) && S->clientSocket == NULL && S->AGWSession == NULL && S->KISSSession == NULL) + { + Sess = S; + break; + } + } + + // Create a window if none found, else reuse old + + if (Sess == NULL) + { + Sess = newWindow((QObject *)mythis, Mon, ""); + } + } + else if (TermMode == Tabbed) + { + // Tabbed - look for free session + + for (i = 8; i; i--) + { + S = _sessions.at(i); + + if (S->clientSocket == NULL && S->AGWSession == NULL) + { + Sess = S; + break; + } + } + } + else if (TermMode == Single && (singlemodeFormat & Mon)) + { + S = _sessions.at(0); + + if (S->clientSocket == NULL && S->AGWSession == NULL && S->KISSSession == NULL) + Sess = S; + + } + + if (Sess) + { + AGWUsers->MonSess = Sess; + Sess->AGWSession = sender; // Flag as in use + + if (TermMode == MDI) + Sess->setWindowTitle("AGW Monitor Window"); + else if (TermMode == Tabbed) + tabWidget->setTabText(Sess->Tab, "AGW Mon"); + else if (TermMode == Single) + mythis->setWindowTitle("AGW Monitor Window"); + + if (TermMode != Tabbed) // Not ideal, but AGW mon window is unlikely to be active window + { + discAction->setEnabled(false); + YAPPSend->setEnabled(false); + connectMenu->setEnabled(false); + } + + Send_AGW_m_Frame(sender); // Request Monitor Frames + } + } +} + + +void QtTermTCP::ConnecttoAGW() +{ + delete(AGWSock); + + AGWSock = new myTcpSocket(); + + connect(AGWSock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(AGWdisplayError(QAbstractSocket::SocketError))); + connect(AGWSock, SIGNAL(readyRead()), this, SLOT(AGWreadyRead())); + connect(AGWSock, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onAGWSocketStateChanged(QAbstractSocket::SocketState))); + + AGWSock->connectToHost(AGWHost, AGWPortNum); + + Status1->setText("AGW Connecting"); + + return; +} + +int AGWBeaconTimer = 0; + + +void QtTermTCP::AGWTimer() +{ + // Runs every 10 Seconds + + if (AGWConnected == 0 && AGWConnecting == 0) + { + AGWConnecting = true; + ConnecttoAGW(); + } + + if (AGWBeaconInterval) + { + AGWBeaconTimer++; + + if (AGWBeaconTimer >= AGWBeaconInterval * 6) + { + AGWBeaconTimer = 0; + + if (AGWConnected) + doAGWBeacon(); + } + } +} + + +void AGW_del_socket(void * socket) +{ + if (AGWUsers->socket == socket) + { + free(AGWUsers); + AGWUsers = nullptr; + } +} + + + +void AGW_add_socket(QTcpSocket * socket) +{ + AGWUser * User = (struct AGWUser_t *)malloc(sizeof(struct AGWUser_t)); // One Client + memset(User, 0, sizeof(struct AGWUser_t)); + + User->socket = socket; + + AGWUsers = User; +}; + +void AGWWindowClosing(Ui_ListenSession *Sess) +{ + if (AGWUsers && AGWUsers->MonSess == Sess) + AGWUsers->MonSess = NULL; +} + + + +void AGW_frame_header(UCHAR * Msg, char AGWPort, char DataKind, unsigned char PID, const char * CallFrom, const char * CallTo, int Len) +{ + struct AGWHeader * Hddr = (struct AGWHeader *)Msg; + memset(Hddr, 0, sizeof(struct AGWHeader)); + + Hddr->Port = AGWPort; + Hddr->DataLength = Len; + Hddr->DataKind = DataKind; + Hddr->PID = PID; + strcpy(Hddr->callfrom, CallFrom); + strcpy(Hddr->callto, CallTo); +}; + + +/* + +// AGW to APP frames + +UCHAR * AGW_R_Frame() +{ + UCHAR * Msg = AGW_frame_header(0, 'R', 0, "", "", 8); + +// stringAdd(Msg, (UCHAR *)AGWVersion, 8); + + return Msg; +}; +*/ + + +void Send_AGW_X_Frame(QTcpSocket* socket, char * CallFrom) +{ + UCHAR Msg[512]; + + AGW_frame_header(Msg, 0, 'X', 0, CallFrom, "", 0); + socket->write((char *)Msg, AGWHDDRRLEN); +} + +void Send_AGW_G_Frame(QTcpSocket* socket) +{ + UCHAR Msg[512]; + + AGW_frame_header(Msg, 0, 'G', 0, "", "", 0); + socket->write((char *)Msg, AGWHDDRRLEN); +} + +void Send_AGW_m_Frame(QTcpSocket* socket) +{ + UCHAR Msg[512]; + + AGW_frame_header(Msg, 0, 'm', 0, "", "", 0); + socket->write((char *)Msg, AGWHDDRRLEN); +} + +/* + + +UCHAR * AGW_Y_Frame(int port, char * CallFrom, char *CallTo, int frame_outstanding) +{ + UCHAR * Msg; + + Msg = AGW_frame_header(port, 'Y', 0, CallFrom, CallTo, 4); + + stringAdd(Msg, (UCHAR *)&frame_outstanding, 4); + return Msg; +} + + + +*/ + + +void Send_AGW_C_Frame(Ui_ListenSession * Sess, int Port, char * CallFrom, char * CallTo, char * Data, int DataLen) +{ + UCHAR Msg[512]; + char Title[64]; + + // We should allocate a AGW session record + + TAGWPort * AX25Sess; + + // FIrst check that we don't already have a session between these calla + + if (Port == -1) + return; + + Debugprintf("Send_AGW_C_Frame"); + + AX25Sess = findSession(Port, CallTo, CallFrom); + + if (AX25Sess) + { + // Seems this can be called twice + + if (Sess->AGWSession == nullptr) + QMessageBox::information(mythis, "QtTermTCP", "You already have a conenction to that call"); + + return; + } + + AX25Sess = get_free_port(); + + if (AX25Sess) + { + AX25Sess->Active = 1; + AX25Sess->port = Port; + AX25Sess->Sess = Sess; // Crosslink AGW and Term Sessions + AX25Sess->PID = 240; + + Sess->AGWSession = AX25Sess; + + strcpy(AX25Sess->mycall, CallTo); + strcpy(AX25Sess->corrcall, CallFrom); + + AX25Sess->socket = AGWSock; + + if (DataLen) // Digis so use 'v' frame + { + AGW_frame_header(Msg, Port, 'v', 240, CallFrom, CallTo, DataLen); + memcpy(&Msg[AGWHDDRRLEN], (UCHAR *)Data, DataLen); + } + else + { + AGW_frame_header(Msg, Port, 'C', 240, CallFrom, CallTo, DataLen); + } + + DataLen += AGWHDDRRLEN; + + AGWSock->write((char *)Msg, DataLen); + + sprintf(Title, "Connecting to %s", CallTo); + + if (TermMode == MDI) + Sess->setWindowTitle(Title); + else if (TermMode == Tabbed) + tabWidget->setTabText(Sess->Tab, "Connecting"); + else if (TermMode == Single) + mythis->setWindowTitle(Title); + + } +} + + +void Send_AGW_Ds_Frame(void * Sess) +{ + TAGWPort * AGW = (TAGWPort *)Sess; + UCHAR Msg[512]; + + AGW_frame_header(Msg, AGW->port, 'd', 240, AGW->corrcall, AGW->mycall, 0); + AGW->socket->write((char *)Msg, AGWHDDRRLEN); +} + + + +void Send_AGW_D_Frame(TAGWPort * AGW, UCHAR * Data, int DataLen) +{ + UCHAR Msg[512]; + + AGW_frame_header(Msg, AGW->port, 'D', AGW->PID, AGW->corrcall, AGW->mycall, DataLen); + + memcpy(&Msg[AGWHDDRRLEN], Data, DataLen); + + DataLen += AGWHDDRRLEN; + + AGW->socket->write((char *)Msg, DataLen); +} + + + +/* + +UCHAR * AGW_T_Frame(int port, char * CallFrom, char * CallTo, char * Data) +{ + UCHAR * Msg; + int DataLen; + + DataLen = strlen(Data); + + Msg = AGW_frame_header(port, 'T', 0, CallFrom, CallTo, DataLen); + + stringAdd(Msg, (byte *)Data, DataLen); + + return Msg; +} + +// Raw Monitor +UCHAR * AGW_K_Frame(int port, int PID, char * CallFrom, char * CallTo, UCHAR * Data, int DataLen) +{ + UCHAR Msg[512]; + + DataLen = Data->Length; + + AGW_frame_header(Msg, port, 'K', PID, CallFrom, CallTo, DataLen); + +// stringAdd(Msg, Data->Data, Data->Length); + +// freeString(Data); + + return Msg; +} + +// APP to AGW frames + +void on_AGW_P_frame(AGWUser * AGW) +{ + +} + +*/ + +void on_AGW_G_frame(unsigned char * Data) +{ + if (AGWPortList) + free(AGWPortList); + + AGWPortList = (char *)strdup((char *)Data); +}; + +/* + +void on_AGW_Ms_frame(AGWUser * AGW) +{ + AGW->Monitor ^= 1; // Flip State +} + + +void on_AGW_R_frame(AGWUser * AGW) +{ +// AGW_send_to_app(AGW->socket, AGW_R_Frame()); +} + + + +void on_AGW_Y_frame(void * socket, int snd_ch, char * CallFrom, char * CallTo) +{ + int frames; + TAGWPort * AX25Sess; + +// AX25Sess = get_user_port_by_calls(snd_ch, CallFrom, CallTo); + +// if (AX25Sess) +// { + //frames = AX25port[snd_ch][user_port].in_data_buf.Count; +// frames = AX25Sess->in_data_buf.Count + AX25Sess->I_frame_buf.Count; +// ; +// AGW_send_to_app(socket, AGW_Y_Frame(snd_ch, CallFrom, CallTo, frames)); +// } +} + +// UI Transmit + +void on_AGW_M_frame(int port, byte PID, char * CallFrom, char *CallTo, byte * Msg, int MsgLen) +{ + byte path[80]; + char Calls[80]; + UCHAR * Data = newString(); + + stringAdd(Data, Msg, MsgLen); + + sprintf(Calls, "%s,%s", CallTo, CallFrom); + + get_addr(Calls, 0, 0, path); + + Add(&all_frame_buf[port], + make_frame(Data, path, PID, 0, 0, U_FRM, U_UI, FALSE, SET_F, SET_C)); + +} + +*/ +void on_AGW_C_frame(AGWUser * AGW, struct AGWHeader * Frame, byte * Msg) +{ + int snd_ch = Frame->Port; + char * CallFrom = Frame->callfrom; + char * CallTo = Frame->callto; + char Title[64]; + int i = 0; + + TAGWPort * AX25Sess; + + // Connection received - may be incoming or response to our connect + + // See if incoming + + if (strstr((char *)Msg, (const char *)"CONNECTED To")) + { + // incoming call + + AX25Sess = get_free_port(); + + if (AX25Sess) + { + // Look for/create Terminal Window for connection + + Ui_ListenSession * Sess = NULL; + Ui_ListenSession * S; + + + if (TermMode == MDI) + { + // See if an old session can be reused + + for (int i = 0; i < _sessions.size(); ++i) + { + S = _sessions.at(i); + + // for (Ui_ListenSession * S: _sessions) + // { + if ((S->SessionType & Listen) && S->clientSocket == NULL) + { + Sess = S; + break; + } + } + + // Create a window if none found, else reuse old + + if (Sess == NULL) + { + Sess = newWindow((QObject *)mythis, Listen, ""); + } + } + else + { + // Single or Tabbed - look for free session + + + for (i = 0; i < _sessions.size(); ++i) + { + S = _sessions.at(i); + + if (S->clientSocket == NULL && S->AGWSession == NULL && S->KISSSession == NULL) + { + Sess = S; + break; + } + } + + if (Sess == NULL) + { + // Clear connection + + return; + } + } + + if (Sess) + { + sprintf(Title, "Connected to %s", CallFrom); + + if (TermMode == MDI) + { + Sess->setWindowTitle(Title); + } + else if (TermMode == Tabbed) + { + tabWidget->setTabText(i, CallFrom); + } + else if (TermMode == Single) + mythis->setWindowTitle(Title); + + AX25Sess->Active = 1; + AX25Sess->port = snd_ch; + AX25Sess->Sess = Sess; // Crosslink AGW and Term Sessions + AX25Sess->PID = 240;; + + Sess->AGWSession = AX25Sess; + + strcpy(AX25Sess->mycall, CallFrom); + strcpy(AX25Sess->corrcall, CallTo); + + AX25Sess->socket = AGW->socket; + + setMenus(true); + + if (ConnectBeep) + myBeep(); + + // Send CText if defined + + if (listenCText[0]) + Send_AGW_D_Frame(AX25Sess, (unsigned char *)listenCText, (int)strlen(listenCText)); + + // Send Message to Terminal + + char Msg[80]; + + sprintf(Msg, "Incoming Connect from %s\r\r", CallFrom); + + WritetoOutputWindow(Sess, (unsigned char *)Msg, (int)strlen(Msg)); + return; + } + } + } + + // Reply to out connect request - look for our connection + + AX25Sess = findSession(snd_ch, CallFrom, CallTo); + + if (AX25Sess) + { + Ui_ListenSession * Sess = AX25Sess->Sess; + + sprintf(Title, "Connected to %s", CallFrom); + + if (TermMode == MDI) + Sess->setWindowTitle(Title); + else if (TermMode == Tabbed) + tabWidget->setTabText(Sess->Tab, CallFrom); + else if (TermMode == Single) + mythis->setWindowTitle(Title); + + setMenus(true); + + int Len = sprintf(Title, "*** Connected to %s\r", CallFrom); + + ProcessReceivedData(Sess, (unsigned char *)Title, Len); + } + +} + + + +void on_AGW_D_frame(int snd_ch, char * CallFrom, char * CallTo, byte * Msg, int Len) +{ + TAGWPort * AX25Sess; + + AX25Sess = findSession(snd_ch, CallFrom, CallTo); + + if (AX25Sess) + { + ProcessReceivedData(AX25Sess->Sess, Msg, Len); + } +} + + +void on_AGW_Mon_frame(byte * Msg, int Len, char Type) +{ + if (AGWUsers && AGWUsers->MonSess && AGWUsers->MonSess->monWindow) + { + if (Type == 'T') + WritetoOutputWindowEx(AGWUsers->MonSess, Msg, Len, + AGWUsers->MonSess->monWindow, &AGWUsers->MonSess->OutputSaveLen, AGWUsers->MonSess->OutputSave, monTxText); // Red + else + WritetoOutputWindowEx(AGWUsers->MonSess, Msg, Len, + AGWUsers->MonSess->monWindow, &AGWUsers->MonSess->OutputSaveLen, AGWUsers->MonSess->OutputSave, monRxText); // Blue + } +} + + +void on_AGW_Ds_frame(int AGWChan, char * CallFrom, char * CallTo, struct AGWHeader * Frame) +{ + TAGWPort * AX25Sess; + char * Msg = (char *)Frame; + Msg = &Msg[36]; + + char Reply[128] = ""; + + memcpy(Reply, Msg, Frame->DataLength); + + // Disconnect Sessiom + + AX25Sess = findSession(AGWChan, CallFrom, CallTo); + + if (AX25Sess) + { + Ui_ListenSession * Sess = AX25Sess->Sess; + + WritetoOutputWindowEx(Sess, (unsigned char *)Reply, (int)strlen(Reply), + Sess->termWindow, &Sess->OutputSaveLen, Sess->OutputSave, WarningText); // Red + + if (TermMode == MDI) + { + if (Sess->SessionType == Mon) // Mon Only + Sess->setWindowTitle("Monitor Session Disconnected"); + else + Sess->setWindowTitle("Disconnected"); + } + else if (TermMode == Tabbed) + { + if (Sess->SessionType == Mon) // Mon Only + tabWidget->setTabText(Sess->Tab, "Monitor"); + else + { + char Label[16]; + sprintf(Label, "Sess %d", Sess->Tab + 1); + tabWidget->setTabText(Sess->Tab, Label); + } + } + else if (TermMode == Single) + { + if (Sess->SessionType == Mon) // Mon Only + mythis->setWindowTitle("Monitor Session Disconnected"); + else + mythis->setWindowTitle("Disconnected"); + } + + Sess->AGWSession = nullptr; + AX25Sess->Sess = nullptr; + AX25Sess->Active = 0; + + setMenus(false); + } + else + { + // Session not found + } +} + + +void AGW_AX25_data_in(void * Sess, UCHAR * data, int Len) +{ + TAGWPort * AX25Sess = (TAGWPort *)Sess; + int len = 250, sendlen; + UCHAR pkt[512]; + + while (Len) + { + if (Len > len) + sendlen = len; + else + sendlen = Len; + + memcpy(pkt, data, sendlen); + + Len -= sendlen; + + memmove(data, &data[sendlen], Len); + + Send_AGW_D_Frame(AX25Sess, pkt, sendlen); + } + +} + + +/* + +void AGW_frame_monitor(byte snd_ch, byte * path, UCHAR * data, byte pid, byte nr, byte ns, byte f_type, byte f_id, byte rpt, byte pf, byte cr, byte RX) +{ + char mon_frm[512]; + char AGW_path[256]; + UCHAR * AGW_data; + + const char * frm; + byte * datap = data->Data; + byte _data[512]; + byte * p_data = _data; + int _datalen; + + char agw_port; + char CallFrom[10], CallTo[10], Digi[80]; + + int i; + const char * ctrl; + int len; + + AGWUser * AGW; + + UNUSED(rpt); + + len = data->Length; + + // if (pid == 0xCF) + // data = parse_NETROM(data, f_id); + // IP parsing + // else if (pid == 0xCC) + // data = parse_IP(data); + // ARP parsing + // else if (pid == 0xCD) + // data = parse_ARP(data); + // + + if (len > 0) + { + for (i = 0; i < len; i++) + { + if (datap[i] > 31 || datap[i] == 13 || datap[i] == 9) + *(p_data++) = datap[i]; + } + } + + _datalen = p_data - _data; + + if (_datalen) + { + byte * ptr = _data; + i = 0; + + // remove successive cr or cr on end while (i < _datalen) + + while (i < _datalen) + { + if ((_data[i] == 13) && (_data[i + 1] = 13)) + i++; + else + *(ptr++) = _data[i++]; + } + + if (*(ptr - 1) == 13) + ptr--; + + *ptr = 0; + + _datalen = ptr - _data; + } + + agw_port = snd_ch; + + get_monitor_path(path, CallTo, CallFrom, Digi); + + ctrl = ""; + frm = ""; + + switch (f_id) + { + case I_I: + + frm = "I"; + if (cr == SET_C && pf == SET_P) + ctrl = " P"; + + break; + + case S_RR: + + frm = "RR"; + if (pf == SET_P) + ctrl = " P/F"; + + break; + + case S_RNR: + + frm = "RNR"; + if (pf == SET_P) + ctrl = " P/F"; + + break; + + case S_REJ: + + frm = "REJ"; + if (pf == SET_P) + ctrl = " P/F"; + + break; + + case U_SABM: + + frm = "SABM"; + if (cr == SET_C && pf == SET_P) + ctrl = " P"; + + break; + + case U_DISC: + + frm = "DISC"; + if (cr == SET_C && pf == SET_P) + ctrl = " P"; + break; + + case U_DM: + + frm = "DM"; + if ((cr == SET_R) && (pf == SET_P)) + ctrl = " F "; + break; + + case U_UA: + + frm = "UA"; + if ((cr == SET_R) && (pf == SET_P)) + ctrl = " F "; + + break; + + case U_FRMR: + + frm = "FRMR"; + if ((cr == SET_R) && (pf == SET_P)) + ctrl = " F "; + break; + + case U_UI: + + frm = "UI"; + if ((pf == SET_P)) + ctrl = " P/F"; + } + + if (Digi[0]) + sprintf(AGW_path, " %d:Fm %s To %s Via %s <%s", snd_ch + 1, CallFrom, CallTo, Digi, frm); + else + sprintf(AGW_path, " %d:Fm %s To %s <%s", snd_ch + 1, CallFrom, CallTo, frm); + + + switch (f_type) + { + case I_FRM: + + //mon_frm = AGW_path + ctrl + ' R' + inttostr(nr) + ' S' + inttostr(ns) + ' pid=' + dec2hex(pid) + ' Len=' + inttostr(len) + ' >' + time_now + #13 + _data + #13#13; + sprintf(mon_frm, "%s%s R%d S%d pid=%X Len=%d >[%s]\r%s\r", AGW_path, ctrl, nr, ns, pid, len, ShortDateTime(), _data); + + break; + + case U_FRM: + + if (f_id == U_UI) + { + sprintf(mon_frm, "%s pid=%X Len=%d >[%s]\r%s\r", AGW_path, pid, len, ShortDateTime(), _data); // "= AGW_path + ctrl + '>' + time_now + #13; + } + else if (f_id == U_FRMR) + { + // _data = copy(_data + #0#0#0, 1, 3); + // mon_frm = AGW_path + ctrl + '>' + time_now + #13 + inttohex((byte(data[1]) shl 16) or (byte(data[2]) shl 8) or byte(data[3]), 6) + #13#13; + } + else + sprintf(mon_frm, "%s%s>[%s]\r", AGW_path, ctrl, ShortDateTime()); // "= AGW_path + ctrl + '>' + time_now + #13; + + break; + + case S_FRM: + + // mon_frm = AGW_path + ctrl + ' R' + inttostr(nr) + ' >' + time_now + #13; + sprintf(mon_frm, "%s%s R%d>[%s]\r", AGW_path, ctrl, nr, ShortDateTime()); // "= AGW_path + ctrl + '>' + time_now + #13; + + break; + + } + + // stringAdd(mon_frm, "", 1); // Add 0 at the end of each frame + + // I think we send to all AGW sockets + + for (i = 0; i < AGWConCount; i++) + { + AGW = AGWUsers[i]; + + if (AGW->Monitor) + { + if (RX) + { + + + case f_id of + I_I : AGW_data = AGW_I_frame(agw_port,CallFrom,CallTo,mon_frm); + S_RR : AGW_data = AGW_S_frame(agw_port,CallFrom,CallTo,mon_frm); + S_RNR : AGW_data = AGW_S_frame(agw_port,CallFrom,CallTo,mon_frm); + S_REJ : AGW_data = AGW_S_frame(agw_port,CallFrom,CallTo,mon_frm); + U_SABM: AGW_data = AGW_S_frame(agw_port,CallFrom,CallTo,mon_frm); + U_DISC: AGW_data = AGW_S_frame(agw_port,CallFrom,CallTo,mon_frm); + U_DM : AGW_data = AGW_S_frame(agw_port,CallFrom,CallTo,mon_frm); + U_UA : AGW_data = AGW_S_frame(agw_port,CallFrom,CallTo,mon_frm); + U_FRMR: AGW_data = AGW_S_frame(agw_port,CallFrom,CallTo,mon_frm); + U_UI : AGW_data = AGW_U_frame(agw_port,CallFrom,CallTo,mon_frm); + + AGW_send_to_app(AGW->socket, AGW_data); + + + } + else + { + AGW_data = AGW_T_Frame(agw_port, CallFrom, CallTo, mon_frm); + AGW_send_to_app(AGW->socket, AGW_data); + } + } + } +} + +*/ + + + + +void AGW_Process_Input(AGWUser * AGW) +{ + struct AGWHeader * Frame = (struct AGWHeader *)AGW->data_in; + byte * Data = &AGW->data_in[36]; + + switch (Frame->DataKind) + { + case 'P': + +// on_AGW_P_frame(AGW); +// return; + + case 'X': + + // Registration response - no need to process + + return; + + case 'G': + + on_AGW_G_frame(Data); + return; +/* + + case 'x': + + on_AGW_Xs_frame(Frame->callfrom); + return; + + + + case 'm': + + on_AGW_Ms_frame(AGW); + return; + + case 'R': + + on_AGW_R_frame(AGW); + return; + + + // 'g': on_AGW_Gs_frame(AGW,Frame->Port); + // 'H': on_AGW_H_frame(AGW,Frame->Port); + // 'y': on_AGW_Ys_frame(AGW,Frame->Port); + + case 'Y': + on_AGW_Y_frame(AGW->socket, Frame->Port, Frame->callfrom, Frame->callto); + break; + + case 'M': + + on_AGW_M_frame(Frame->Port, Frame->PID, Frame->callfrom, Frame->callto, Data, Frame->DataLength); + break; +*/ + + case 'C': +// case 'v': // Call with digis + + on_AGW_C_frame(AGW, Frame, Data); + return; + + + case 'D': + + on_AGW_D_frame(Frame->Port, Frame->callfrom, Frame->callto, Data, Frame->DataLength); + return; + + case 'd': + + on_AGW_Ds_frame(Frame->Port, Frame->callfrom, Frame->callto, Frame); + return; + + case 'U': + case 'S': + case 'I': + case 'T': + + // Monitor Data + + if (AGWMonEnable) + on_AGW_Mon_frame(Data, Frame->DataLength, Frame->DataKind); + + return; + +/* + + // 'V': on_AGW_V_frame(AGW,Frame->Port,PID,CallFrom,CallTo,Data); + // 'c': on_AGW_Cs_frame(sAGWocket,Frame->Port,PID,CallFrom,CallTo); + + + case 'K': + + on_AGW_K_frame(Frame); + return; + + case 'k': + on_AGW_Ks_frame(AGW); + return; + */ + default: + Debugprintf("AGW %c", Frame->DataKind); + } +} diff --git a/AGWConnect.ui b/AGWConnect.ui new file mode 100644 index 0000000..c5e4506 --- /dev/null +++ b/AGWConnect.ui @@ -0,0 +1,148 @@ + + + AGWConnectDkg + + + + 0 + 0 + 500 + 400 + + + + Dialog + + + + + 10 + 10 + 480 + 380 + + + + + + + + + Call From + + + + + + + + + + Call To + + + + + + + true + + + QComboBox::NoInsert + + + + + + + Digis + + + + + + + + + + + + Radio Ports + + + + + + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + OK + + + + + + + Cancel + + + + + + + + + + + + okButton + clicked() + AGWConnectDkg + accept() + + + 278 + 253 + + + 96 + 254 + + + + + cancelButton + clicked() + AGWConnectDkg + reject() + + + 369 + 253 + + + 179 + 282 + + + + + diff --git a/AGWParams.ui b/AGWParams.ui new file mode 100644 index 0000000..7a5fc45 --- /dev/null +++ b/AGWParams.ui @@ -0,0 +1,395 @@ + + + AGWParams + + + + 0 + 0 + 562 + 491 + + + + Dialog + + + + + 120 + 50 + 113 + 20 + + + + + + + 12 + 83 + 537 + 215 + + + + Beacon Setup + + + + + 110 + 24 + 113 + 20 + + + + + + + 110 + 54 + 113 + 20 + + + + + + + 108 + 84 + 45 + 20 + + + + + + + 110 + 114 + 43 + 20 + + + + + + + 14 + 22 + 75 + 21 + + + + Destination + + + + + + 14 + 52 + 75 + 21 + + + + Digipeaters + + + + + + 14 + 82 + 75 + 21 + + + + Interval + + + + + + 14 + 112 + 75 + 21 + + + + Ports + + + + + + 176 + 82 + 75 + 22 + + + + Minutes + + + + + + 176 + 112 + 137 + 21 + + + + (Separate with commas) + + + + + + 14 + 144 + 75 + 21 + + + + Message + + + + + + 110 + 140 + 425 + 63 + + + + + + + 14 + 158 + 95 + 21 + + + + (max 256 chars) + + + + + + + 12 + 300 + 537 + 123 + + + + TNC Setup + + + + + 110 + 24 + 101 + 20 + + + + + + + 110 + 54 + 47 + 20 + + + + + + + 110 + 84 + 47 + 20 + + + + + + + 16 + 24 + 47 + 13 + + + + Host + + + + + + 16 + 54 + 47 + 13 + + + + Port + + + + + + 16 + 84 + 47 + 13 + + + + Paclen + + + + + + + 18 + 48 + 111 + 21 + + + + Terminal Callsign + + + + + + 180 + 440 + 95 + 23 + + + + OK + + + + + + 292 + 440 + 79 + 23 + + + + Cancel + + + + + + 152 + 16 + 23 + 25 + + + + Qt::RightToLeft + + + + + + + + + 20 + 18 + 135 + 21 + + + + Enable AGW Interface + + + + + + 255 + 18 + 216 + 21 + + + + Qt::RightToLeft + + + Enable AGW Monitor Window + + + + + + + cancelButton + clicked() + AGWParams + reject() + + + 369 + 253 + + + 179 + 282 + + + + + okButton + clicked() + AGWParams + accept() + + + 278 + 253 + + + 96 + 254 + + + + + diff --git a/ColourConfig.ui b/ColourConfig.ui new file mode 100644 index 0000000..d980e87 --- /dev/null +++ b/ColourConfig.ui @@ -0,0 +1,440 @@ + + + ColourDialog + + + + 0 + 0 + 342 + 407 + + + + Colour Selection + + + + + 66 + 350 + 199 + 33 + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + OK + + + + + + + Cancel + + + + + + + + + 216 + 15 + 75 + 23 + + + + + 75 + 0 + + + + + 75 + 16777215 + + + + + + + + + + 216 + 247 + 75 + 23 + + + + + 75 + 0 + + + + + 75 + 16777215 + + + + + + + + + + 216 + 276 + 75 + 23 + + + + + 75 + 0 + + + + + 75 + 16777215 + + + + Input + + + + + + 216 + 44 + 75 + 23 + + + + + 75 + 0 + + + + + 75 + 16777215 + + + + Normal + + + + + + 216 + 73 + 75 + 23 + + + + + 75 + 0 + + + + + 75 + 16777215 + + + + Echoed + + + + + + 216 + 102 + 75 + 23 + + + + + 75 + 0 + + + + + 75 + 16777215 + + + + Warning + + + + + + 21 + 16 + 187 + 16 + + + + Terminal Background + + + + + + 21 + 45 + 185 + 16 + + + + Normal Text + + + + + + 21 + 74 + 191 + 16 + + + + Echoed Text + + + + + + 21 + 103 + 185 + 16 + + + + Warning Text + + + + + + 21 + 248 + 189 + 16 + + + + Input Background + + + + + + 21 + 277 + 183 + 16 + + + + Input Text + + + + + + 216 + 189 + 75 + 23 + + + + + 75 + 0 + + + + + 75 + 16777215 + + + + RX Text + + + + + + 216 + 131 + 75 + 23 + + + + + 75 + 0 + + + + + 75 + 16777215 + + + + + + + + + + 216 + 218 + 75 + 23 + + + + + 75 + 0 + + + + + 75 + 16777215 + + + + Other Text + + + + + + 216 + 160 + 75 + 23 + + + + + 75 + 0 + + + + + 75 + 16777215 + + + + TX Text + + + + + + 21 + 132 + 187 + 16 + + + + Monitor Background + + + + + + 21 + 161 + 183 + 16 + + + + TX Text + + + + + + 21 + 190 + 187 + 16 + + + + RX Text + + + + + + 21 + 219 + 189 + 16 + + + + Other Text + + + + + + diff --git a/KISSConfig.ui b/KISSConfig.ui new file mode 100644 index 0000000..183778e --- /dev/null +++ b/KISSConfig.ui @@ -0,0 +1,381 @@ + + + KISSDialog + + + + 0 + 0 + 432 + 286 + + + + KISS Configuration + + + + + 40 + 238 + 351 + 33 + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + OK + + + + + + + Cancel + + + + + + + + + 10 + 96 + 401 + 57 + + + + TCP Setup + + + + + 90 + 20 + 111 + 22 + + + + + + + 330 + 21 + 47 + 22 + + + + + + + 16 + 24 + 47 + 13 + + + + Host + + + + + + 280 + 24 + 47 + 13 + + + + Port + + + + + + 85 + 63 + 47 + 22 + + + + + + + 20 + 66 + 47 + 13 + + + + Port + + + + + + + 0 + 46 + 411 + 51 + + + + Serial TNC + + + + + 150 + 20 + 111 + 22 + + + + + + + 20 + 23 + 131 + 16 + + + + Select Device + + + + + + 280 + 20 + 47 + 22 + + + + Speed + + + + + + 335 + 21 + 51 + 22 + + + + 19200 + + + + + + + 10 + 12 + 161 + 21 + + + + Enable KISS Interface + + + + + + 150 + 10 + 23 + 25 + + + + Qt::RightToLeft + + + + + + + + + 230 + 13 + 61 + 17 + + + + MYCALL + + + + + + 300 + 10 + 91 + 22 + + + + + + + 25 + 168 + 47 + 13 + + + + Paclen + + + + + + 100 + 165 + 47 + 22 + + + + + + + 265 + 168 + 66 + 16 + + + + MaxFrame + + + + + + 340 + 165 + 47 + 22 + + + + + + + 25 + 202 + 47 + 13 + + + + Frack + + + + + + 100 + 199 + 36 + 22 + + + + + + + 265 + 201 + 47 + 13 + + + + Retries + + + + + + 340 + 198 + 36 + 22 + + + + + + + + okButton + clicked() + KISSDialog + accept() + + + 278 + 253 + + + 96 + 254 + + + + + cancelButton + clicked() + KISSDialog + reject() + + + 369 + 253 + + + 179 + 282 + + + + + diff --git a/ListenPort.ui b/ListenPort.ui new file mode 100644 index 0000000..c46838a --- /dev/null +++ b/ListenPort.ui @@ -0,0 +1,140 @@ + + + ListenPort + + + + 0 + 0 + 445 + 654 + + + + Dialog + + + + + 0 + 0 + 431 + 301 + + + + + + + + + + 100 + 30 + + + + + 100 + 30 + + + + Port + + + + + + + + 0 + 30 + + + + + 100 + 30 + + + + + + + + + 0 + 30 + + + + + 16777215 + 30 + + + + CText + + + + + + + + 0 + 150 + + + + + 401 + 150 + + + + + + + + + + + + 0 + 50 + + + + + 16777215 + 50 + + + + Qt::RightToLeft + + + QDialogButtonBox::Save + + + + + + + Qt::LeftToRight + + + Enable Listen + + + + + + + + + + + diff --git a/QtSoundModem.ini b/QtSoundModem.ini new file mode 100644 index 0000000..85fc91e --- /dev/null +++ b/QtSoundModem.ini @@ -0,0 +1,99 @@ +[General] +HostParams0=127.0.0.1|8011|john|password|80001 1 1 0 1 0 0 1\r +HostParams1=|0||| +HostParams2=|0||| +HostParams3=|0||| +HostParams4=|0||| +HostParams5=|0||| +HostParams6=|0||| +HostParams7=|0||| +HostParams8=|0||| +HostParams9=|0||| +HostParams10=|0||| +HostParams11=|0||| +HostParams12=|0||| +HostParams13=|0||| +HostParams14=|0||| +HostParams15=|0||| +Split=50 +ChatMode=1 +Bells=1 +StripLF=1 +AlertBeep=1 +ConnectBeep=1 +CurrentHost=16 +YAPPPath= +listenPort=8015 +listenEnable=1 +listenCText= +convUTF8=0 +PTT=None +PTTBAUD=19200 +PTTMode=19200 +CATHex=1 +PTTOffString= +PTTOnString= +pttGPIOPin=17 +pttGPIOPinR=17 +CM108Addr=0xD8C:0x08 +HamLibPort=4532 +HamLibHost=127.0.0.1 +FLRigPort=12345 +FLRigHost=127.0.0.1 +AGWEnable=0 +AGWMonEnable=0 +AGWTermCall= +AGWBeaconDest= +AGWBeaconPath= +AGWBeaconInterval=0 +AGWBeaconPorts= +AGWBeaconText= +AGWHost=127.0.0.1 +AGWPort=8000 +AGWPaclen=80 +AGWToCalls=GM8BPQ-1, GM8BPQ-2, GM8BPQ, G8BPQ-2, G8B, G8BPQ-9, +KISSEnable=1 +MYCALL=GM8BPQ +KISSHost=127.0.0.1 +KISSPort=8102 +KISSSerialPort=COM54 +KISSBAUD=19200 +VARAEnable=0 +VARATermCall= +VARAHost=127.0.0.1 +VARAPort=8300 +VARAPath=C:\\VARA\\VARA.exe +VARAHostHF=127.0.0.1 +VARAPortHF=8300 +VARAPathHF=C:\\VARA\\VARA.exe +VARAHostFM=127.0.0.1 +VARAPortFM=8300 +VARAPathFM=C:\\VARA\\VARAFM.exe +VARAHostSAT=127.0.0.1 +VARAPortSAT=8300 +VARAPathSAT=C:\\VARA\\VARASAT.exe +VARA500=0 +VARA2300=1 +VARA2750=0 +VARAHF=1 +VARAFM=0 +VARASAT=0 +TabType=1 1 1 1 1 1 1 2 2 0 + +[AX25_A] +Retries=9 +Maxframe=6 +FrackTime=7 +IdleTime=180 +SlotTime=100 +Persist=128 +RespTime=1500 +TXFrmMode=1 +FrameCollector=6 +ExcludeCallsigns= +ExcludeAPRSFrmType= +KISSOptimization=0 +DynamicFrack=0 +BitRecovery=0 +IPOLL=80 +MyDigiCall= diff --git a/QtTermTCP.aps b/QtTermTCP.aps new file mode 100644 index 0000000..6fa53fb Binary files /dev/null and b/QtTermTCP.aps differ diff --git a/QtTermTCP.cpp b/QtTermTCP.cpp new file mode 100644 index 0000000..71c6b33 --- /dev/null +++ b/QtTermTCP.cpp @@ -0,0 +1,6146 @@ +// Qt Version of BPQTermTCP + +#define VersionString "0.0.0.55" + +// .12 Save font weight +// .13 Display incomplete lines (ie without CR) +// .14 Add YAPP and Listen Mode +// .15 Reuse windows in Listen Mode +// .17 MDI Version 7/1/20 +// .18 Fix inpout window losing focus when data arrives on other window +// .19 Fix Scrollback +// .20 WinXP compatibility changes +// .21 Save Window Positions +// .22 Open a window on first start +// .23 Add Tabbed display option +// .24 Fix crash when setting Monitor flags in Tabbed mode +// .25 Add AGW mode +// .26 Add sending CTRL/C CTRL/D and CTRL/Z) +// .27 Limit size of windows to 10000 lines +// Fix YAPP in Tabbed Mode +// .28 Add AGW Beacon +// .29 Fix allocating Listem sessions to tabs connected using AGW +// .30 Fix allocationd AGW Monitor Sessions +// .31 Fix output being written to the wrong place when window is scrolled back +// .32 Fix connect with digis +// .33 Add StripLF option +// .34 Improvements for Andriod +// Option to change Menu Fonts +// Fix receiving part lines when scrolled back +// .35 Fix PE if not in Tabbed Mode +// .36 Improved dialogs mainly for Android +// Try to make sure sessions are closed before exiting +// .37 Replace VT with LF (for pasting from BPQ32 Terminal Window) +// Dont show AGW status line if AGW interface is disabled +// Don't show Window Menu in Single Mode +// .38 Protect against connect to normal Telnet Port. +// Send CTEXT and Beep for inward AGW Connects +// Make sending Idle and Connect beeps configurable +// Change displayed Monitor flags when active window changed. +// Fix duplicate text on long lines +// .39 Add option to Convert non-utf8 charaters +// .40 Prevent crash if AGW monitor Window closed +// .41 Allow AGW Config and Connect dialogs to be resized with scrollbars +// Fix disabling connect flag on current session when AGW connects +// .42 Fix some bugs in AGW session handling +// .43 Include Ross KD5LPB's fixes for MAC +// .44 Ctrl/[ sends ESC (0x1b) +// .45 Add VARA support Nov 2021 +// .46 Fix dialog size on VARA setup +// .47 Move VARA Status indicator. +// Add FLRIG PTT for VARA +// .48 Check that YAPP Receive Directory has been set +// Save separate configs for VARA, VARAFM and VARASAT +// .49 Add BBS Monitor Dec 2021 +// .50 Make Serial port support optional for Android Version +// .51 Add KISS TNC Support May 2022 +// .52 Save partially typed lines on cursor up May 2022 +// .53 Add UI Mode for KISS Sessions May 2022 +// Fix saving KISS Paclen (b) +// Fix Keepalive (c) +// .54 Add option to change screen colours June 2022 +// .55 Build without optimisation + +#define _CRT_SECURE_NO_WARNINGS + +#define UNUSED(x) (void)(x) + +#define USESERIAL + +#include "QtTermTCP.h" +#include "TabDialog.h" +#include +#include +#include +#include +#include +#include "QTreeWidget" +#include +#include +#include +#include +#ifdef USESERIAL +#include +#include +#endif +#ifndef WIN32 +#define strtok_s strtok_r +#endif +#include +#include +#ifndef WIN32 +#include +#endif + +#include "ax25.h" + +#define MAXHOSTS 16 +#define MAXPORTS 32 + +#define UNREFERENCED_PARAMETER(P) (P) + +char Host[MAXHOSTS + 1][100] = { "" }; +int Port[MAXHOSTS + 1] = { 0 }; +char UserName[MAXHOSTS + 1][80] = { "" }; +char Password[MAXHOSTS + 1][80] = { "" }; +char MonParams[MAXHOSTS + 1][80] = { "" }; +int ListenPort = 8015; + +// Session Type Equates + +#define Term 1 +#define Mon 2 +#define Listen 4 + +// Presentation - Single Window, MDI or Tabbed + +int TermMode = 0; + +#define Single 0 +#define MDI 1 +#define Tabbed 2 + +int singlemodeFormat = Mon + Term; + +char monStyleSheet[128] = "background-color: rgb(0,255,255)"; +char termStyleSheet[128] = "background-color: rgb(255,0,255);"; +char inputStyleSheet[128] = "color: rgb(255, 0, 0); background-color: rgb(255,255,0);"; + + +QColor monBackground = qRgb(0, 255, 255); +QColor monRxText = qRgb(0, 0, 255); +QColor monTxText = qRgb(255, 0, 0); +QColor monOtherText = qRgb(0, 0, 0); + +QColor termBackground = qRgb(255, 0, 255); +QColor outputText = qRgb(0, 0, 0); +QColor EchoText = qRgb(0, 0, 255); +QColor WarningText = qRgb(255, 0, 0); +QColor inputBackground = qRgb(255, 255, 0); +QColor inputText = qRgb(0, 0, 255); + + +// There is something odd about this. It doesn't match BPQTERMTCP though it looks the same + +// Chat uses these (+ 10) +//{ 0, 4, 9, 11, 13, 16, 17, 42, 45, 50, 61, 64, 66, 72, 81, 84, 85, 86, 87, 89 }; + +// As we have a white background we need dark colours + +// 0 is black. 23 is normal output (blue) 81 is red (used by AGW Mon) +// for monitor in colour, host sends 17 for RX Text, 81 for TX Text but code converts to monRx/TxText qrgb values + +QRgb Colours[256] = { 0, + qRgb(0,0,0), qRgb(0,0,128), qRgb(0,0,192), qRgb(0,0,255), // 1 - 4 + qRgb(0,64,0), qRgb(0,64,128), qRgb(0,64,192), qRgb(0,64,255), // 5 - 8 + qRgb(0,128,0), qRgb(0,128,128), qRgb(0,128,192), qRgb(0,128,255), // 9 - 12 + qRgb(0,192,0), qRgb(0,192,128), qRgb(0,192,192), qRgb(0,192,255), // 13 - 16 + qRgb(0,255,0), qRgb(0,255,128), qRgb(0,255,192), qRgb(0,255,255), // 17 - 20 + + qRgb(64,0,0), qRgb(64,0,128), qRgb(64,0,192), qRgb(0,0,255), // 21 + qRgb(64,64,0), qRgb(64,64,128), qRgb(64,64,192), qRgb(64,64,255), + qRgb(64,128,0), qRgb(64,128,128), qRgb(64,128,192), qRgb(64,128,255), + qRgb(64,192,0), qRgb(64,192,128), qRgb(64,192,192), qRgb(64,192,255), + qRgb(64,255,0), qRgb(64,255,128), qRgb(64,255,192), qRgb(64,255,255), + + qRgb(128,0,0), qRgb(128,0,128), qRgb(128,0,192), qRgb(128,0,255), // 41 + qRgb(128,64,0), qRgb(128,64,128), qRgb(128,64,192), qRgb(128,64,255), + qRgb(128,128,0), qRgb(128,128,128), qRgb(128,128,192), qRgb(128,128,255), + qRgb(128,192,0), qRgb(128,192,128), qRgb(128,192,192), qRgb(128,192,255), + qRgb(128,255,0), qRgb(128,255,128), qRgb(128,255,192), qRgb(128,255,255), + + qRgb(192,0,0), qRgb(192,0,128), qRgb(192,0,192), qRgb(192,0,255), // 61 - 80 + qRgb(192,64,0), qRgb(192,64,128), qRgb(192,64,192), qRgb(192,64,255), + qRgb(192,128,0), qRgb(192,128,128), qRgb(192,128,192), qRgb(192,128,255), + qRgb(192,192,0), qRgb(192,192,128), qRgb(192,192,192), qRgb(192,192,255), + qRgb(192,255,0), qRgb(192,255,128), qRgb(192,255,192), qRgb(192,255,255), + + qRgb(255,0,0), qRgb(255,0,128), qRgb(255,0,192), qRgb(255,0,255), // 81 - 100 + qRgb(255,64,0), qRgb(255,64,128), qRgb(255,64,192), qRgb(255,64,255), + qRgb(255,128,0), qRgb(255,128,128), qRgb(255,128,192), qRgb(255,128,255), + qRgb(255,192,0), qRgb(255,192,128), qRgb(255,192,192), qRgb(255,192,255), + qRgb(255,255,0), qRgb(255,255,128), qRgb(255,255,192), qRgb(255,255,255) +}; + + + +int SavedHost = 0; // from config + +char * sessionList = NULL; // Saved sessions + +extern int ChatMode; +extern int Bells; +extern int StripLF; +extern int convUTF8; + +extern time_t LastWrite; +extern int AlertInterval; +extern int AlertBeep; +extern int AlertFreq; +extern int AlertDuration; +extern int ConnectBeep; + +// AGW Host Interface stuff + +int AGWEnable = 0; +int AGWMonEnable = 0; +char AGWTermCall[12] = ""; +char AGWBeaconDest[12] = ""; +char AGWBeaconPath[80] = ""; +int AGWBeaconInterval = 0; +char AGWBeaconPorts[80]; +char AGWBeaconMsg[260] = ""; + +char AGWHost[128] = "127.0.0.1"; +int AGWPortNum = 8000; +int AGWPaclen = 80; +extern char * AGWPortList; +extern myTcpSocket * AGWSock; + +QStringList AGWToCalls; + +// KISS Interface + +int KISSEnable = 0; +char SerialPort[80] = ""; +char KISSHost[128] = "127.0.0.1"; +int KISSPortNum = 1000; +int KISSBAUD = 19200; +char MYCALL[32]; + +int KISSMode = 0; // Connected (0) or UI (1) + +myTcpSocket * KISSSock; + +extern "C" void * KISSSockCopy[4]; + +int KISSConnected = 0; +int KISSConnecting = 0; + +Ui_ListenSession * KISSMonSess; + +QSerialPort * m_serial = nullptr; + +// VARA Interface + +int VARAEnable = 0; +char VARAHost[128] = "127.0.0.1"; +char VARAHostHF[128] = "127.0.0.1"; +char VARAHostFM[128] = "127.0.0.1"; +char VARAHostSAT[128] = "127.0.0.1"; +int VARAPortNum = 8000; +int VARAPortHF = 8000; +int VARAPortFM = 8000; +int VARAPortSAT = 8000; +char VARATermCall[12] = ""; +char VARAPath[256] = ""; +char VARAPathHF[256] = ""; +char VARAPathFM[256] = ""; +char VARAPathSAT[256] = ""; + +int VARA500 = 0; +int VARA2300 = 1; +int VARA2750 = 0; + +int VARAHF = 1; +int VARAFM = 0; +int VARASAT = 0; + +myTcpSocket * VARASock; +myTcpSocket * VARADataSock; + +int VARAConnected = 0; +int VARAConnecting = 0; + + +extern char YAPPPath[256]; + +void menuChecked(QAction * Act); + +// PTT Stuff + +#define PTTRTS 1 +#define PTTDTR 2 +#define PTTCAT 4 +#define PTTCM108 8 +#define PTTHAMLIB 16 +#define PTTFLRIG 32 + +#ifdef USESERIAL + +QSerialPort * hPTTDevice = 0; +#else + + +#endif +char PTTPort[80] = ""; // Port for Hardware PTT - may be same as control port. +int PTTBAUD = 19200; +int PTTMode = PTTRTS; // PTT Control Flags. + +char PTTOnString[128] = ""; +char PTTOffString[128] = ""; + +int CATHex = 1; + +unsigned char PTTOnCmd[64]; +int PTTOnCmdLen = 0; + +unsigned char PTTOffCmd[64]; +int PTTOffCmdLen = 0; + +int pttGPIOPin = 17; // Default +int pttGPIOPinR = 17; +bool pttGPIOInvert = false; +bool useGPIO = false; +bool gotGPIO = false; + +int HamLibPort = 4532; +char HamLibHost[32] = "192.168.1.14"; + +int FLRigPort = 12345; +char FLRigHost[32] = "127.0.0.1"; + + +char CM108Addr[80] = ""; + +int VID = 0; +int PID = 0; + +// CM108 Code + +char * CM108Device = NULL; + +QProcess *process = NULL; + +void GetSettings(); + +// These widgets defined here as they are accessed from outside the framework + +QLabel * Status1; +QLabel * Status2; +QLabel * Status3; +QLabel * Status4; + +QAction *actHost[19]; +QAction *actSetup[16]; + +QAction * TabSingle = NULL; +QAction * TabBoth = NULL; +QAction * TabMon = NULL; + +QMenu *monitorMenu; +QMenu * YAPPMenu; +QMenu *connectMenu; +QMenu *disconnectMenu; + +QAction *MonTX; +QAction *MonSup; +QAction *MonNodes; +QAction *MonUI; +QAction *MonColour; +QAction *MonPort[33]; +QAction *actChatMode; +QAction *actBells; +QAction *actStripLF; +QAction *actIntervalBeep; +QAction *actConnectBeep; +QAction *actAuto; +QAction *actnoConv; +QAction *actCP1251; +QAction *actCP1252; +QAction *actCP437; + +QAction *actFonts; +QAction *actmenuFont; +QAction *actColours; +QAction *actmyFonts; +QAction *YAPPSend; +QAction *YAPPSetRX; +QAction *singleAct; +QAction *singleAct2; +QAction *MDIAct; +QAction *tabbedAct; +QAction *discAction; +QFont * menufont; +QMenu *windowMenu; +QMenu *setupMenu; +QAction *ListenAction; + +QTabWidget *tabWidget; +QMdiArea *mdiArea; +QWidget * mythis; +QStatusBar * myStatusBar; + +QTcpServer * _server; + +QMenuBar *mymenuBar; +QToolBar *toolBar; +QToolButton * but1; +QToolButton * but2; +QToolButton * but3; +QToolButton * but4; +QToolButton * but5; + +QList _sessions; + +// Session Type Equates + +#define Term 1 +#define Mon 2 +#define Listen 4 + +int TabType[10] = { Term, Term + Mon, Term, Term, Term, Term, Mon, Mon, Mon }; + +int listenPort = 8015; +extern "C" int listenEnable; +char listenCText[4096] = ""; + +int ConfigHost = 0; + +int Split = 50; // Mon/Term size split + +int termX; + +bool Cascading = false; // Set to stop size being saved when cascading + +QMdiSubWindow * ActiveSubWindow = NULL; +Ui_ListenSession * ActiveSession = NULL; + +Ui_ListenSession * newWindow(QObject * parent, int Type, const char * Label = nullptr); +void Send_AGW_C_Frame(Ui_ListenSession * Sess, int Port, char * CallFrom, char * CallTo, char * Data, int DataLen); +void AGW_AX25_data_in(void * AX25Sess, unsigned char * data, int Len); +void AGWMonWindowClosing(Ui_ListenSession *Sess); +void AGWWindowClosing(Ui_ListenSession *Sess); +extern "C" void KISSDataReceived(void * socket, unsigned char * data, int length); +void closeSerialPort(); + +extern void initUTF8(); +int checkUTF8(unsigned char * Msg, int Len, unsigned char * out); + +#include +#include +#include + + + + +void EncodeSettingsLine(int n, char * String) +{ + sprintf(String, "%s|%d|%s|%s|%s", Host[n], Port[n], UserName[n], Password[n], MonParams[n]); + return; +} + +// This is used for placing discAction in the preference menu +#ifdef __APPLE__ + bool is_mac = true; +#else + bool is_mac = false; +#endif + +QString GetConfPath() +{ + std::string conf_path = "QtTermTCP.ini"; // Default conf file stored alongside application. + +#ifdef __APPLE__ + + // Get configuration path for MacOS. + + // This will usually be placed in /Users/USER/Library/Application Support/QtTermTCP + + std::string directory = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation).at(0).toStdString(); + + conf_path = directory + "/QtTermTCP.ini"; + + mkdir(directory.c_str(), 0775); + +#endif + + return QString::fromUtf8(conf_path.c_str()); + +} + + + +void DecodeSettingsLine(int n, char * String) +{ + char * Param = strdup(String); + char * Rest; + char * Save = Param; // for Free + + Rest = strlop(Param, '|'); + + if (Rest == NULL) + return; + + strcpy(Host[n], Param); + Param = Rest; + + Rest = strlop(Param, '|'); + Port[n] = atoi(Param); + Param = Rest; + + Rest = strlop(Param, '|'); + strcpy(UserName[n], Param); + Param = Rest; + + Rest = strlop(Param, '|'); + strcpy(Password[n], Param); + + strcpy(MonParams[n], Rest); + + + free(Save); + return; +} + +//#ifdef ANDROID +//#define inputheight 60 +//#else +#define inputheight 25 +//#endif + +extern "C" void SendtoAX25(void * conn, unsigned char * Msg, int Len); + +bool QtTermTCP::eventFilter(QObject* obj, QEvent *event) +{ + // See if from a Listening Session + + Ui_ListenSession * Sess; + + for (int i = 0; i < _sessions.size(); ++i) + { + Sess = _sessions.at(i); + +// Sess = _sessions.at(i); for (Ui_ListenSession * Sess : _sessions) +// { + if (Sess == NULL) + continue; + + if (Sess == obj) + { + if (event->type() == QEvent::Close) + { + // Window closing + + // This only closed mon sessinos + + if (Sess->AGWMonSession) +// AGWMonWindowClosing(Sess); + AGWWindowClosing(Sess); + + if (Sess->AGWSession) + AGWWindowClosing(Sess); + + if (Sess->clientSocket) + { + int loops = 100; + Sess->clientSocket->disconnectFromHost(); + while (Sess->clientSocket && loops-- && Sess->clientSocket->state() != QAbstractSocket::UnconnectedState) + QThread::msleep(10); + } + _sessions.removeOne(Sess); + return QMainWindow::eventFilter(obj, event); + } + + if (event->type() == QEvent::Resize) + { + QRect r = Sess->rect(); + + int H, Mid, monHeight, termHeight, Width, Border = 3; + + Width = r.width(); + H = r.height(); + + if (TermMode == Tabbed) + { + // H -= 20; + Width -= 7; + } + else if (TermMode == Single) + { + Width -= 7; +// H += 20; + } + + if (Sess->SessionType == Listen || Sess->SessionType == Term) + { + // Term and Input + + // Calc Positions of window + + termHeight = H - (inputheight + 3 * Border); + + Sess->termWindow->setGeometry(QRect(Border, Border, Width, termHeight)); + Sess->inputWindow->setGeometry(QRect(Border, H - (inputheight + Border), Width, inputheight)); + } + else if (Sess->SessionType == (Term | Mon)) + { + // All 3 + + // Calc Positions of window + + Mid = (H * Split) / 100; + + monHeight = Mid - Border; + termX = Mid; + termHeight = H - Mid - (inputheight + 3 * Border); + + Sess->monWindow->setGeometry(QRect(Border, Border, Width, monHeight)); + Sess->termWindow->setGeometry(QRect(Border, Mid + Border, Width, termHeight)); + Sess->inputWindow->setGeometry(QRect(Border, H - (inputheight + Border), Width, inputheight)); + } + else + { + // Should just be Mon only + + Sess->monWindow->setGeometry(QRect(Border, Border, Width, H - 2 * Border)); + } + + } + } + + if (Sess->inputWindow == obj) + { + if (event->type() == QEvent::KeyPress) + { + QKeyEvent* keyEvent = static_cast(event); + + int key = keyEvent->key(); + Qt::KeyboardModifiers modifier = keyEvent->modifiers(); + + if (modifier == Qt::ControlModifier) + { + char Msg[] = "\0\r"; + + if (key == Qt::Key_BracketLeft) + Msg[0] = 0x1b; + if (key == Qt::Key_Z) + Msg[0] = 0x1a; + else if (key == Qt::Key_C) + Msg[0] = 3; + else if (key == Qt::Key_D) + Msg[0] = 4; + + if (Msg[0]) + { + if (Sess->KISSSession) + { + // Send to ax.25 code + + SendtoAX25(Sess->KISSSession, (unsigned char *)Msg, (int)strlen(Msg)); + } + else if (Sess->AGWSession) + { + // Terminal is in AGWPE mode - send as AGW frame + + AGW_AX25_data_in(Sess->AGWSession, (unsigned char *)Msg, (int)strlen(Msg)); + + } + else if (Sess->clientSocket && Sess->clientSocket->state() == QAbstractSocket::ConnectedState) + { + Sess->clientSocket->write(Msg); + } + + return true; + } + } + + if (key == Qt::Key_Up) + { + // Scroll up + + if (Sess->KbdStack[Sess->StackIndex] == NULL) + return true; + + // If anything typed, stack part command + + QByteArray stringData = Sess->inputWindow->text().toUtf8(); + + if (stringData.length() && Sess->StackIndex == 0) + { + if (Sess->KbdStack[49]) + free(Sess->KbdStack[49]); + + for (int i = 48; i >= 0; i--) + { + Sess->KbdStack[i + 1] = Sess->KbdStack[i]; + } + + Sess->KbdStack[0] = qstrdup(stringData.data()); + Sess->StackIndex++; + } + + Sess->inputWindow->setText(Sess->KbdStack[Sess->StackIndex]); + Sess->inputWindow->cursorForward(strlen(Sess->KbdStack[Sess->StackIndex])); + + Sess->StackIndex++; + if (Sess->StackIndex == 50) + Sess->StackIndex = 49; + + return true; + } + else if (key == Qt::Key_Down) + { + // Scroll down + + if (Sess->StackIndex == 0) + { + Sess->inputWindow->setText(""); + return true; + } + + Sess->StackIndex--; + + if (Sess->StackIndex && Sess->KbdStack[Sess->StackIndex - 1]) + { + Sess->inputWindow->setText(Sess->KbdStack[Sess->StackIndex - 1]); + Sess->inputWindow->cursorForward(strlen(Sess->KbdStack[Sess->StackIndex - 1])); + } + else + Sess->inputWindow->setText(""); + + return true; + } + else if (key == Qt::Key_Return || key == Qt::Key_Enter) + { + LreturnPressed(Sess); + return true; + } + + return false; + } + + if (event->type() == QEvent::MouseButtonPress) + { + QMouseEvent *k = static_cast (event); + + // Paste on Right Click + + if (k->button() == Qt::RightButton) + { + Sess->inputWindow->paste(); + return true; + } + return QMainWindow::eventFilter(obj, event); + } + } + } + return QMainWindow::eventFilter(obj, event); +} + +QAction * setupMenuLine(QMenu * Menu, char * Label, QObject * parent, int State) +{ + QAction * Act = new QAction(Label, parent); + if (Menu) + Menu->addAction(Act); + + Act->setCheckable(true); + if (State) + Act->setChecked(true); + + parent->connect(Act, SIGNAL(triggered()), parent, SLOT(menuChecked())); + + Act->setFont(*menufont); + + return Act; +} + +// This now creates all window types - Term, Mon, Combined, Listen + +int xCount = 0; + +Ui_ListenSession * newWindow(QObject * parent, int Type, const char * Label) +{ + Ui_ListenSession * Sess = new(Ui_ListenSession); + + // Need to explicity initialise on Qt4 + + + Sess->termWindow = NULL; + Sess->monWindow = NULL; + Sess->inputWindow = NULL; + + for (int i = 0; i < 50; i++) + Sess->KbdStack[i] = NULL; + + Sess->StackIndex = 0; + Sess->InputMode = 0; + Sess->SlowTimer = 0; + Sess->MonData = 0; + Sess->OutputSaveLen = 0; + Sess->MonSaveLen = 0; + Sess->PortMonString[0] = 0; + Sess->portmask = 0; + Sess->portmask = 1; + Sess->mtxparam = 1; + Sess->mcomparam = 1; + Sess->monUI = 0; + Sess->MonitorNODES = 0; + Sess->MonitorColour = 1; + Sess->CurrentHost = 0; + + Sess->SessionType = Type; + Sess->clientSocket = NULL; + Sess->AGWSession = NULL; + Sess->AGWMonSession = NULL; + Sess->KISSSession = NULL; + Sess->KISSMode = 0; + + _sessions.push_back(Sess); + +// Sess->setObjectName(QString::fromUtf8("Sess")); + + if (TermMode == MDI) + { + Sess->sw = mdiArea->addSubWindow(Sess); +// Sess->installEventFilter(parent); + + Sess->sw->resize(800, 600); + } + else if (TermMode == Tabbed) + { + Sess->Tab = xCount++; + + if (Type == Mon) + tabWidget->addTab(Sess, "Monitor"); + else + tabWidget->addTab(Sess, Label); + } + + Sess->installEventFilter(parent); + + QSettings settings(GetConfPath(), QSettings::IniFormat); + +#ifdef ANDROID + QFont font = QFont(settings->value("FontFamily", "Driod Sans Mono").value(), + settings->value("PointSize", 12).toInt(), + settings->value("Weight", 50).toInt()); +#else + QFont font = QFont(settings.value("FontFamily", "Courier New").value(), + settings.value("PointSize", 10).toInt(), + settings.value("Weight", 50).toInt()); +#endif + + if ((Type & Mon)) + { + Sess->monWindow = new QTextEdit(Sess); + Sess->monWindow->setReadOnly(1); + Sess->monWindow->document()->setMaximumBlockCount(10000); + Sess->monWindow->setFont(font); + Sess->monWindow->setStyleSheet(monStyleSheet); + mythis->connect(Sess->monWindow, SIGNAL(selectionChanged()), parent, SLOT(onTEselectionChanged())); + } + + if ((Type & (Listen | Term))) + { + Sess->termWindow = new QTextEdit(Sess); + Sess->termWindow->setReadOnly(1); + Sess->termWindow->document()->setMaximumBlockCount(10000); + Sess->termWindow->setFont(font); + Sess->termWindow->setStyleSheet(termStyleSheet); + + mythis->connect(Sess->termWindow, SIGNAL(selectionChanged()), parent, SLOT(onTEselectionChanged())); + + Sess->inputWindow = new QLineEdit(Sess); + Sess->inputWindow->installEventFilter(parent); + Sess->inputWindow->setFont(font); + Sess->inputWindow->setStyleSheet(inputStyleSheet); + Sess->inputWindow->setContextMenuPolicy(Qt::PreventContextMenu); + + mythis->connect(Sess->inputWindow, SIGNAL(selectionChanged()), parent, SLOT(onLEselectionChanged())); + } + + if (Type == (Term | Mon)) + { + // Add Custom Menu to set Mon/Term Split with Right Click + + Sess->monWindow->setContextMenuPolicy(Qt::CustomContextMenu); + Sess->termWindow->setContextMenuPolicy(Qt::CustomContextMenu); + + + mythis->connect(Sess->monWindow, SIGNAL(customContextMenuRequested(const QPoint&)), + parent, SLOT(showContextMenuM(const QPoint &))); + + mythis->connect(Sess->termWindow, SIGNAL(customContextMenuRequested(const QPoint&)), + parent, SLOT(showContextMenuT(const QPoint &))); + } + + if (Sess->SessionType == Mon) // Mon Only + Sess->setWindowTitle("Monitor Session Disconnected"); + else if (Sess->SessionType == Listen) // Mon Only + Sess->setWindowTitle("Listen Window Disconnected"); + else + Sess->setWindowTitle("Disconnected"); + + Sess->installEventFilter(mythis); + + Sess->show(); + + int pos = (_sessions.size() - 1) * 20; + + if (TermMode == MDI) + { + Sess->sw->move(pos, pos); + } + + QSize Size(800, 602); // Not actually used, but Event constructor needs it + + QResizeEvent event(Size, Size); + + QApplication::sendEvent(Sess, &event); // Resize Widgets to fix Window + + return Sess; +} + + +QtTermTCP::QtTermTCP(QWidget *parent) : QMainWindow(parent) +{ + int i; + char Title[80]; + + // Get configuration path for MacOS. + std::string directory = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation).at(0).toStdString(); + std::string conf_path = directory + "/QtTermTCP.ini"; + +//mkdir(directory.c_str(), 0775); + + _server = new QTcpServer(); + + mythis = this; + + ui.setupUi(this); + + initUTF8(); + + mymenuBar = new QMenuBar(this); + mymenuBar->setGeometry(QRect(0, 0, 781, 26)); + setMenuBar(mymenuBar); + + toolBar = new QToolBar(this); + toolBar->setObjectName("mainToolbar"); + addToolBar(Qt::BottomToolBarArea, toolBar); + + QSettings mysettings(GetConfPath(), QSettings::IniFormat); + + restoreGeometry(mysettings.value("geometry").toByteArray()); + restoreState(mysettings.value("windowState").toByteArray()); + + GetSettings(); + + // Set background colours + + sprintf(monStyleSheet, "background-color: rgb(%d, %d, %d);", + monBackground.red(), monBackground.green(), monBackground.blue()); + + sprintf(termStyleSheet, "background-color: rgb(%d, %d, %d);", + termBackground.red(), termBackground.green(), termBackground.blue()); + + sprintf(inputStyleSheet, "color: rgb(%d, %d, %d); background-color: rgb(%d, %d, %d);", + inputText.red(), inputText.green(), inputText.blue(), + inputBackground.red(), inputBackground.green(), inputBackground.blue()); + + +#ifdef ANDROID + menufont = new QFont(mysettings.value("MFontFamily", "Driod Sans").value(), + mysettings.value("MPointSize", 12).toInt(), + mysettings.value("MWeight", 50).toInt()); +#else + menufont = new QFont(mysettings.value("MFontFamily", "Aerial").value(), + mysettings.value("MPointSize", 10).toInt(), + mysettings.value("MWeight", 50).toInt()); + +#endif + if (TermMode == MDI) + { + mdiArea = new QMdiArea(ui.centralWidget); + + mdiArea->setGeometry(QRect(0, 0, 771, 571)); + + mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + + connect(mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(xon_mdiArea_changed())); + + setCentralWidget(mdiArea); + } + else if (TermMode == Tabbed) + { + tabWidget = new QTabWidget(this); + + ui.verticalLayout->addWidget(tabWidget); + + tabWidget->setTabPosition(QTabWidget::South); + + newWindow(this, TabType[0], "Sess 1"); + newWindow(this, TabType[1], "Sess 2"); + newWindow(this, TabType[2], "Sess 3"); + newWindow(this, TabType[3], "Sess 4"); + newWindow(this, TabType[4], "Sess 5"); + newWindow(this, TabType[5], "Sess 6"); + newWindow(this, TabType[6], "Sess 7"); + newWindow(this, TabType[7], "Monitor"); + newWindow(this, TabType[8], "Monitor"); + + ActiveSession= _sessions.at(0); + + connect(tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabSelected(int))); + + tabWidget->setFont(*menufont); + + } + + sprintf(Title, "QtTermTCP Version %s", VersionString); + + this->setWindowTitle(Title); + +//#ifdef ANDROID +// mymymenuBar->setVisible(false); +//#endif + + if (TermMode == Single) + windowMenu = mymenuBar->addMenu(""); + else + windowMenu = mymenuBar->addMenu(tr("&Window")); + + connect(windowMenu, SIGNAL(aboutToShow()), this, SLOT(updateWindowMenu())); + windowMenu->setFont(*menufont); + + newTermAct = new QAction(tr("New Terminal Window"), this); + connect(newTermAct, SIGNAL(triggered()), this, SLOT(doNewTerm())); + + newMonAct = new QAction(tr("New Monitor Window"), this); + connect(newMonAct, SIGNAL(triggered()), this, SLOT(doNewMon())); + + newCombinedAct = new QAction(tr("New Combined Term/Mon Window"), this); + connect(newCombinedAct, SIGNAL(triggered()), this, SLOT(doNewCombined())); + + if (TermMode == MDI) + { + closeAct = new QAction(tr("Cl&ose"), this); + closeAct->setStatusTip(tr("Close the active window")); + connect(closeAct, SIGNAL(triggered()), mdiArea, SLOT(closeActiveSubWindow())); + + closeAllAct = new QAction(tr("Close &All"), this); + closeAllAct->setStatusTip(tr("Close all the windows")); + connect(closeAllAct, SIGNAL(triggered()), mdiArea, SLOT(closeAllSubWindows())); + + tileAct = new QAction(tr("&Tile"), this); + tileAct->setStatusTip(tr("Tile the windows")); + connect(tileAct, SIGNAL(triggered()), mdiArea, SLOT(tileSubWindows())); + + cascadeAct = new QAction(tr("&Cascade"), this); + cascadeAct->setStatusTip(tr("Cascade the windows")); + connect(cascadeAct, SIGNAL(triggered()), this, SLOT(doCascade())); + + nextAct = new QAction(tr("Ne&xt"), this); + nextAct->setShortcuts(QKeySequence::NextChild); + nextAct->setStatusTip(tr("Move the focus to the next window")); + connect(nextAct, SIGNAL(triggered()), mdiArea, SLOT(activateNextSubWindow())); + + previousAct = new QAction(tr("Pre&vious"), this); + previousAct->setShortcuts(QKeySequence::PreviousChild); + previousAct->setStatusTip(tr("Move the focus to the previous window")); + connect(previousAct, SIGNAL(triggered()), mdiArea, SLOT(activatePreviousSubWindow())); + } + + quitAction = new QAction(tr("&Quit"), this); + connect(quitAction, SIGNAL(triggered()), this, SLOT(doQuit())); + + windowMenuSeparatorAct = new QAction(this); + windowMenuSeparatorAct->setSeparator(true); + + updateWindowMenu(); + + connectMenu = mymenuBar->addMenu(tr("&Connect")); + + actHost[16] = new QAction("AGW Connect", this); + actHost[16]->setFont(*menufont); + actHost[16]->setVisible(0); + connectMenu->addAction(actHost[16]); + + connect(actHost[16], SIGNAL(triggered()), this, SLOT(Connect())); + + actHost[17] = new QAction("VARA Connect", this); + actHost[17]->setFont(*menufont); + actHost[17]->setVisible(0); + connectMenu->addAction(actHost[17]); + + connect(actHost[17], SIGNAL(triggered()), this, SLOT(Connect())); + + actHost[18] = new QAction("KISS Connect", this); + actHost[18]->setFont(*menufont); + actHost[18]->setVisible(KISSEnable); + actHost[18]->setEnabled(0); + connectMenu->addAction(actHost[18]); + + connect(actHost[18], SIGNAL(triggered()), this, SLOT(Connect())); + for (i = 0; i < MAXHOSTS; i++) + { + actHost[i] = new QAction(Host[i], this); + actHost[i]->setFont(*menufont); + connectMenu->addAction(actHost[i]); + connect(actHost[i], SIGNAL(triggered()), this, SLOT(Connect())); + } + + discAction = mymenuBar->addAction("&Disconnect"); + + // Place discAction in mac preferences menu, otherwise it doesn't appear + if (is_mac == true) { + QMenu * app_menu = mymenuBar->addMenu("App Menu"); + discAction->setMenuRole(QAction::ApplicationSpecificRole); + app_menu->addAction(discAction); + + } + + connect(discAction, SIGNAL(triggered()), this, SLOT(Disconnect())); + discAction->setEnabled(false); + + toolBar->setFont(*menufont); + +#ifndef ANDROID + toolBar->setVisible(false); +#endif + but4 = new QToolButton(); + but4->setPopupMode(QToolButton::InstantPopup); + but4->setText("Window"); + but4->addAction(windowMenu->menuAction()); + but4->setFont(*menufont); + toolBar->addWidget(but4); + + but5 = new QToolButton(); + but5->setPopupMode(QToolButton::InstantPopup); + but5->setText("Connect"); + but5->addAction(connectMenu->menuAction()); + but5->setFont(*menufont); + + toolBar->addWidget(but5); + + toolBar->addAction(discAction); + + setupMenu = mymenuBar->addMenu(tr("&Setup")); + hostsubMenu = setupMenu->addMenu("Hosts"); + hostsubMenu->setFont(*menufont); + + for (i = 0; i < MAXHOSTS; i++) + { + if (Host[i][0]) + actSetup[i] = new QAction(Host[i], this); + else + actSetup[i] = new QAction("New Host", this); + + hostsubMenu->addAction(actSetup[i]); + connect(actSetup[i], SIGNAL(triggered()), this, SLOT(SetupHosts())); + + actSetup[i]->setFont(*menufont); + } + + + // Setup Presentation Options + + setupMenu->addSeparator()->setText(tr("Presentation")); + + QActionGroup * termGroup = new QActionGroup(this); + + singleAct = setupMenuLine(nullptr, (char *)"Single Window (Term + Mon)", this, (TermMode == Single) && (singlemodeFormat == Term + Mon)); + singleAct2 = setupMenuLine(nullptr, (char *)"Single Window (Term only)", this, (TermMode == Single) && (singlemodeFormat == Term)); + MDIAct = setupMenuLine(nullptr, (char *)"MDI", this, TermMode == MDI); + tabbedAct = setupMenuLine(nullptr, (char *)"Tabbed", this, TermMode == Tabbed); + + termGroup->addAction(singleAct); + termGroup->addAction(singleAct2); + termGroup->addAction(MDIAct); + termGroup->addAction(tabbedAct); + + setupMenu->addAction(singleAct); + setupMenu->addAction(singleAct2); + setupMenu->addAction(MDIAct); + setupMenu->addAction(tabbedAct); + + setupMenu->addSeparator(); + + actFonts = new QAction("Terminal Font", this); + setupMenu->addAction(actFonts); + connect(actFonts, SIGNAL(triggered()), this, SLOT(doFonts())); + actFonts->setFont(*menufont); + + actmenuFont = new QAction("Menu Font", this); + setupMenu->addAction(actmenuFont); + connect(actmenuFont, SIGNAL(triggered()), this, SLOT(doMFonts())); + actmenuFont->setFont(*menufont); + + actColours = new QAction("Choose Colours", this); + setupMenu->addAction(actColours); + connect(actColours, SIGNAL(triggered()), this, SLOT(doColours())); + actColours->setFont(*menufont); + + AGWAction = new QAction("AGW Setup", this); + setupMenu->addAction(AGWAction); + connect(AGWAction, SIGNAL(triggered()), this, SLOT(AGWSlot())); + AGWAction->setFont(*menufont); + + VARAAction = new QAction("VARA Setup", this); + setupMenu->addAction(VARAAction); + connect(VARAAction, SIGNAL(triggered()), this, SLOT(VARASlot())); + VARAAction->setFont(*menufont); + + KISSAction = new QAction("KISS Setup", this); + setupMenu->addAction(KISSAction); + connect(KISSAction, SIGNAL(triggered()), this, SLOT(KISSSlot())); + KISSAction->setFont(*menufont); + + + actChatMode = setupMenuLine(setupMenu, (char *)"Chat Terminal Mode", this, ChatMode); + actBells = setupMenuLine(setupMenu, (char *)"Enable Bells", this, Bells); + actStripLF = setupMenuLine(setupMenu, (char *)"Strip Line Feeds", this, StripLF); + actIntervalBeep = setupMenuLine(setupMenu, (char *)"Beep after inactivity", this, AlertBeep); + actConnectBeep = setupMenuLine(setupMenu, (char *)"Beep on inbound connect", this, ConnectBeep); + + setupMenu->addAction(new QAction("Interpret non-UTF8 input as:", this)); + setupMenu->setFont(*menufont); + + QActionGroup * cpGroup = new QActionGroup(this); + + actnoConv = setupMenuLine(nullptr, (char *)" Don't Convert (assume is UTF-8)", this, convUTF8 == -1); + actAuto = setupMenuLine(nullptr, (char *)" Auto", this, convUTF8 == 0); + actCP1251 = setupMenuLine(nullptr, (char *)" CP1251 (Cyrillic)", this, convUTF8 == 1251); + actCP1252 = setupMenuLine(nullptr, (char *)" CP1252 (Western Europe)", this, convUTF8 == 1252); + actCP437 = setupMenuLine(nullptr, (char *)" CP437 (Windows Line Draw)", this, convUTF8 == 437); + + cpGroup->addAction(actnoConv); + cpGroup->addAction(actAuto); + cpGroup->addAction(actCP1251); + cpGroup->addAction(actCP1252); + cpGroup->addAction(actCP437); + + setupMenu->addAction(actnoConv); + setupMenu->addAction(actAuto); + setupMenu->addAction(actCP1251); + setupMenu->addAction(actCP1252); + setupMenu->addAction(actCP437); + + monitorMenu = mymenuBar->addMenu(tr("&Monitor")); + + MonTX = setupMenuLine(monitorMenu, (char *)"Monitor TX", this, 1); + MonSup = setupMenuLine(monitorMenu, (char *)"Monitor Supervisory", this, 1); + MonUI = setupMenuLine(monitorMenu, (char *)"Only Monitor UI Frames", this, 0); + MonNodes = setupMenuLine(monitorMenu, (char *)"Monitor NODES Broadcasts", this, 0); + MonColour = setupMenuLine(monitorMenu, (char *)"Enable Colour", this, 1); + + for (i = 0; i < MAXPORTS + 1; i++) + { + MonPort[i] = setupMenuLine(monitorMenu, (char *)"Port", this, 0); + MonPort[i]->setVisible(false); + } + + but1 = new QToolButton(); + but1->setPopupMode(QToolButton::InstantPopup); + but1->setText("Monitor"); + but1->addAction(monitorMenu->menuAction()); + toolBar->addWidget(but1); + + but2 = new QToolButton(); + but2->setPopupMode(QToolButton::InstantPopup); + but2->setText("Setup"); + but2->addAction(setupMenu->menuAction()); + toolBar->addWidget(but2); + + ListenAction = mymenuBar->addAction("&Listen"); + connect(ListenAction, SIGNAL(triggered()), this, SLOT(ListenSlot())); + + toolBar->addAction(ListenAction); + + YAPPMenu = mymenuBar->addMenu(tr("&YAPP")); + + YAPPSend = new QAction("Send File", this); + YAPPSetRX = new QAction("Set Receive Directory", this); + YAPPSend->setFont(*menufont); + YAPPSetRX->setFont(*menufont); + + YAPPMenu->addAction(YAPPSend); + YAPPMenu->addAction(YAPPSetRX); + YAPPSend->setEnabled(false); + + connect(YAPPSend, SIGNAL(triggered()), this, SLOT(doYAPPSend())); + connect(YAPPSetRX, SIGNAL(triggered()), this, SLOT(doYAPPSetRX())); + + + but3 = new QToolButton(); + but3->setPopupMode(QToolButton::InstantPopup); + but3->setText("YAPP"); + but3->addAction(YAPPMenu->menuAction()); + but3->setFont(*menufont); + toolBar->addWidget(but3); + + toolBar->setFont(*menufont); + + // Set up Status Bar + + setStyleSheet("QStatusBar{border-top: 1px outset black;} QStatusBar::item { border: 1px solid black; border-radius: 3px;}"); +// setStyleSheet("QStatusBar{border-top: 1px outset black;}"); + + + Status4 = new QLabel(""); + Status3 = new QLabel(" "); + Status2 = new QLabel(" "); + Status1 = new QLabel(" Disconnected "); + + Status1->setMinimumWidth(100); + Status2->setMinimumWidth(100); + Status3->setMinimumWidth(100); + Status4->setMinimumWidth(100); + + myStatusBar = statusBar(); + + statusBar()->addPermanentWidget(Status4); + statusBar()->addPermanentWidget(Status3); + statusBar()->addPermanentWidget(Status2); + statusBar()->addPermanentWidget(Status1); + + statusBar()->setVisible(AGWEnable | VARAEnable | KISSEnable); + // Restore saved sessions + + if (TermMode == Single) + { + Ui_ListenSession * Sess = newWindow(this, singlemodeFormat); + + ActiveSession = Sess; + ui.verticalLayout->addWidget(Sess); + + connectMenu->setEnabled(true); + discAction->setEnabled(false); + YAPPSend->setEnabled(false); + } + + if (TermMode == MDI) + { + int n = atoi(sessionList); + + char *p, *Context; + char delim[] = "|"; + + int type = 0, host = 0, topx, leftx, bottomx, rightx; + + p = strtok_s((char *)sessionList, delim, &Context); + + while (n--) + { + p = strtok_s(NULL, delim, &Context); + + sscanf(p, "%d,%d,%d,%d,%d,%d", &type, &host, &topx, &leftx, &bottomx, &rightx); + + Ui_ListenSession * Sess = newWindow(this, type); + + Sess->CurrentHost = host; + + QRect r(leftx, topx, rightx - leftx, bottomx - topx); + + Sess->sw->setGeometry(r); + Sess->sw->move(leftx, topx); + + } + } + + QTimer *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(MyTimerSlot())); + timer->start(10000); + + QTimer *timer2 = new QTimer(this); + connect(timer2, SIGNAL(timeout()), this, SLOT(KISSTimerSlot())); + timer2->start(100); + + // Run timer now to connect to AGW if configured + + MyTimerSlot(); + + if (listenEnable) + _server->listen(QHostAddress::Any, listenPort); + + connect(_server, SIGNAL(newConnection()), this, SLOT(onNewConnection())); + + setFonts(); + + if (VARAEnable) + OpenPTTPort(); +} + +void QtTermTCP::setFonts() +{ + windowMenu->menuAction()->setFont(*menufont); + connectMenu->menuAction()->setFont(*menufont); + setupMenu->menuAction()->setFont(*menufont); + monitorMenu->menuAction()->setFont(*menufont); + YAPPMenu->menuAction()->setFont(*menufont); + + if (tabWidget) + tabWidget->setFont(*menufont); + mymenuBar->setFont(*menufont); + toolBar->setFont(*menufont); + but1->setFont(*menufont); + but2->setFont(*menufont); + but3->setFont(*menufont); + but4->setFont(*menufont); + but5->setFont(*menufont); + discAction->setFont(*menufont); + ListenAction->setFont(*menufont); + + for (int i = 0; i < MAXHOSTS; i++) + { + actHost[i]->setFont(*menufont); + actSetup[i]->setFont(*menufont); + } + + actHost[16]->setFont(*menufont); // AGW Host +} + +void QtTermTCP::doQuit() +{ + if (_server->isListening()) + _server->close(); + + SaveSettings(); + + QCoreApplication::quit(); +} + +// "Copy on select" Code + + +void QtTermTCP::onTEselectionChanged() +{ + QTextEdit * x = static_cast(QObject::sender()); + x->copy(); +} + +void QtTermTCP::onLEselectionChanged() +{ + QLineEdit * x = static_cast(QObject::sender()); + x->copy(); +} + +int splitY; // Value when menu added + +void QtTermTCP::setSplit() +{ + QAction * sender = static_cast(QObject::sender()); + + QWidget * Parent = sender->parentWidget(); + + if (Parent) + Parent = Parent->parentWidget(); + + int y = Parent->rect().height() - 50; + + // y is height of whole windom. splitX is new split position + // So split = 100 * splitx/x + + Split = (splitY * 100) / y; + + if (Split < 10) + Split = 10; + else if (Split > 90) + Split = 90; + + QSize size(800, 602); + QResizeEvent event(size, size); + + eventFilter(Parent, &event); +} +void QtTermTCP::showContextMenuT(const QPoint &pt) // Term Window +{ + QTextEdit* sender = static_cast(QObject::sender()); + + QMenu *menu = sender->createStandardContextMenu(); + + QString style = "QMenu {border-radius:15px; background-color: white;margin: 2px; border: 1px solid rgb(58, 80, 116); color: rgb(58, 80, 116);}QMenu::separator {height: 2px;background: rgb(58, 80, 116);margin-left: 10px;margin-right: 5px;}"; + menu->setStyleSheet(style); + + + QAction * actSplit = new QAction("Set Monitor/Output Split", sender); + + menu->addAction(actSplit); + + splitY = pt.y() + termX; + + connect(actSplit, SIGNAL(triggered()), this, SLOT(setSplit())); + + menu->exec(sender->mapToGlobal(pt)); + delete menu; +} + +void QtTermTCP::showContextMenuM(const QPoint &pt) // Mon Window +{ + QTextEdit* sender = static_cast(QObject::sender()); + + QMenu *menu = sender->createStandardContextMenu(); + QString style = "QMenu {border-radius:15px; background-color: white;margin: 2px; border: 1px solid rgb(58, 80, 116); color: rgb(58, 80, 116);}"; + menu->setStyleSheet(style); + + QAction * actSplit = new QAction("Set Monitor/Output Split", sender); + + menu->addAction(actSplit); + + splitY = pt.y(); + + connect(actSplit, SIGNAL(triggered()), this, SLOT(setSplit())); + + menu->exec(sender->mapToGlobal(pt)); + delete menu; +} + +extern "C" void setMenus(int State) +{ + // Sets Connect, Disconnect and YAPP Send enable flags to match connection state + + if (State) + { + connectMenu->setEnabled(false); + discAction->setEnabled(true); + YAPPSend->setEnabled(true); + } + else + { + connectMenu->setEnabled(true); + discAction->setEnabled(false); + YAPPSend->setEnabled(false); + } +} + +extern "C" void SetSessLabel(Ui_ListenSession * Sess, char * label) +{ + if (TermMode == MDI) + Sess->setWindowTitle(label); + else if (TermMode == Tabbed) + tabWidget->setTabText(Sess->Tab, label); + else if (TermMode == Single) + mythis->setWindowTitle(label); +} + + +extern "C" void ClearSessLabel(Ui_ListenSession * Sess) +{ + if (TermMode == MDI) + { + if (Sess->SessionType == Mon) // Mon Only + Sess->setWindowTitle("Monitor Session Disconnected"); + else if (Sess->SessionType == Listen) // Mon Only + Sess->setWindowTitle("Listen Window Disconnected"); + else + Sess->setWindowTitle("Disconnected"); + } + else if (TermMode == Tabbed) + { + if (Sess->SessionType == Mon) // Mon Only + tabWidget->setTabText(Sess->Tab, "Monitor"); + else + { + char Label[16]; + sprintf(Label, "Sess %d", Sess->Tab + 1); + tabWidget->setTabText(Sess->Tab, Label); + } + } + else if (TermMode == Single) + { + if (Sess->SessionType == Mon) // Mon Only + mythis->setWindowTitle("Monitor Session Disconnected"); + else + mythis->setWindowTitle("Disconnected"); + } +} + +void QtTermTCP::tabSelected(int Current) +{ + Ui_ListenSession * Sess = NULL; + + if (Current < _sessions.size()) + { + Sess = _sessions.at(Current); + + if (Sess == nullptr) + return; + + ActiveSession = Sess; + + if (Sess->clientSocket || Sess->AGWSession || Sess->KISSSession || Sess->KISSMode) + { + connectMenu->setEnabled(false); + discAction->setEnabled(true); + YAPPSend->setEnabled(true); + } + else + { + connectMenu->setEnabled(true); + discAction->setEnabled(false); + YAPPSend->setEnabled(false); + } + + // If a monitor Window, change Monitor config settings + + if (Sess->PortMonString[0]) + { + char * ptr = (char *)malloc(1024); + memcpy(ptr, Sess->PortMonString, 1024); + + int NumberofPorts = atoi((char *)&ptr[2]); + char *p, *Context; + char msg[80]; + int portnum, m; + char delim[] = "|"; + + // Remove old Monitor menu + + for (int i = 0; i < 32; i++) + { + SetPortMonLine(i, (char *)"", 0, 0); // Set all hidden + } + + p = strtok_s((char *)&ptr[2], delim, &Context); + + while (NumberofPorts--) + { + p = strtok_s(NULL, delim, &Context); + if (p == NULL) + break; + + m = portnum = atoi(p); + + sprintf(msg, "Port %s", p); + + if (m == 0) + m = 33; + + if (Sess->portmask & (1ll << (m - 1))) + SetPortMonLine(portnum, msg, 1, 1); + else + SetPortMonLine(portnum, msg, 1, 0); + } + free(ptr); + + MonTX->setChecked(Sess->mtxparam); + MonSup->setChecked(Sess->mcomparam); + MonUI->setChecked(Sess->monUI); + MonNodes->setChecked(Sess->MonitorNODES); + MonColour->setChecked(Sess->MonitorColour); + } + return; + } +} + +void QtTermTCP::SetupHosts() +{ + QAction * Act = static_cast(QObject::sender()); + int i; + + for (i = 0; i < MAXHOSTS; i++) + { + if (Act == actSetup[i]) + break; + } + + if (i > 15) + return; + + ConfigHost = i; + + TabDialog tabdialog(0); + tabdialog.exec(); +} + + +void QtTermTCP::Connect() +{ + QMdiSubWindow * UI; + Ui_ListenSession * Sess = nullptr; + QAction * Act = static_cast(QObject::sender()); + + int i; + + for (i = 0; i < MAXHOSTS; i++) + { + if (Act == actHost[i]) + break; + } + + SavedHost = i; // Last used + + if (TermMode == MDI) + { + UI = mdiArea->activeSubWindow(); + + for (i = 0; i < _sessions.size(); ++i) + { + Sess = _sessions.at(i); + + if (Sess->sw == UI) + break; + } + + if (i == _sessions.size()) + return; + } + + else if (TermMode == Tabbed) + { + Sess = (Ui_ListenSession *)tabWidget->currentWidget(); + } + + else if (TermMode == Single) + Sess = _sessions.at(0); + + if ((Sess == nullptr || Sess->SessionType & Listen)) + return; + + Sess->CurrentHost = SavedHost; + + if (Act == actHost[16]) + { + // This runs the AGW Connection dialog + + Sess->CurrentHost = 16; // Iast used + AGWConnect dialog(0); + dialog.exec(); + return; + } + + + if (Act == actHost[17]) + { + // This runs the VARA Connection dialog + + Sess->CurrentHost = 17; // Iast used + VARADataSock->Sess = Sess; + VARASock->Sess = Sess; + + VARAConnect dialog(0); + dialog.exec(); + WritetoOutputWindow(Sess, (unsigned char *)"Connecting...\r",14); + + return; + } + + if (Act == actHost[18]) + { + // This runs the KISS Connection dialog + + Sess->CurrentHost = 18; // Iast used + + KISSConnect dialog(0); + dialog.exec(); + return; + } + + // Set Monitor Params for this host + + sscanf(MonParams[Sess->CurrentHost], "%llx %x %x %x %x %x", + &Sess->portmask, &Sess->mtxparam, &Sess->mcomparam, + &Sess->MonitorNODES, &Sess->MonitorColour, &Sess->monUI); + + MonTX->setChecked(Sess->mtxparam); + MonSup->setChecked(Sess->mcomparam); + MonUI->setChecked(Sess->monUI); + MonNodes->setChecked(Sess->MonitorNODES); + MonColour->setChecked(Sess->MonitorColour); + + // Remove old Monitor menu + + for (i = 0; i < 32; i++) + { + SetPortMonLine(i, (char *)"", 0, 0); // Set all hidden + } + + delete(Sess->clientSocket); + + Sess->clientSocket = new myTcpSocket(); + Sess->clientSocket->Sess = Sess; + + connect(Sess->clientSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(displayError(QAbstractSocket::SocketError))); + connect(Sess->clientSocket, SIGNAL(readyRead()), this, SLOT(readyRead())); + connect(Sess->clientSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onSocketStateChanged(QAbstractSocket::SocketState))); + + Sess->clientSocket->connectToHost(&Host[Sess->CurrentHost][0], Port[Sess->CurrentHost]); + return; +} + +extern "C" void rst_timer(TAX25Port * AX25Sess); +extern "C" void set_unlink(TAX25Port * AX25Sess, Byte * path); + +void QtTermTCP::Disconnect() +{ + QMdiSubWindow * UI; + Ui_ListenSession * Sess = nullptr; + + if (TermMode == MDI) + { + int i; + + UI = mdiArea->activeSubWindow(); + + for (i = 0; i < _sessions.size(); ++i) + { + Sess = _sessions.at(i); + + if (Sess->sw == UI) + break; + } + + if (i == _sessions.size()) + return; + } + + else if (TermMode == Tabbed) + { + Sess = (Ui_ListenSession *)tabWidget->currentWidget(); + } + + else if (TermMode == Single) + Sess = _sessions.at(0); + + // if AGW send a d frame else disconnect TCP session + + if (Sess->AGWSession) + Send_AGW_Ds_Frame(Sess->AGWSession); + else if (VARASock && VARASock->Sess == Sess) + VARASock->write("DISCONNECT\r"); + else if (Sess->KISSSession) + { + if (Sess->KISSMode == 0) + { + TAX25Port * Port = (TAX25Port *)Sess->KISSSession; + + rst_timer(Port); + set_unlink(Port, Port->Path); + } + else + { + // KISS UI Mode + + char Msg[128]; + int Len = sprintf(Msg, "Disconnected\r"); + + Sess->KISSMode = 0; + SendtoTerm(Sess, Msg, Len); + ClearSessLabel(Sess); + setMenus(0); + Sess->KISSSession = NULL; + } + } + else + + { + if (Sess && Sess->clientSocket) + Sess->clientSocket->disconnectFromHost(); + } + return; +} + + +void QtTermTCP::doYAPPSend() +{ + QFileDialog dialog(this); + QStringList fileNames; + dialog.setFileMode(QFileDialog::AnyFile); + + QMdiSubWindow * UI; + Ui_ListenSession * Sess = nullptr; + int i; + + if (TermMode == MDI) + { + UI = mdiArea->activeSubWindow(); + + for (i = 0; i < _sessions.size(); ++i) + { + Sess = _sessions.at(i); + + if (Sess->sw == UI) + break; + } + + if (i == _sessions.size()) + return; + } + + else if (TermMode == Tabbed) + { + Sess = (Ui_ListenSession *)tabWidget->currentWidget(); + } + + else if (TermMode == Single) + Sess = _sessions.at(0); + + if (Sess == nullptr) + return; + + if ((Sess->SessionType & Mon)) + { + // Turn off monitoring + + setTraceOff(Sess); + } + + if (dialog.exec()) + { + char FN[256]; + + fileNames = dialog.selectedFiles(); + if (fileNames[0].length() < 256) + { + strcpy(FN, fileNames[0].toUtf8()); + YAPPSendFile(Sess, FN); + } + } +} + +void QtTermTCP::doYAPPSetRX() +{ + QFileDialog dialog(this); + QStringList fileNames; + dialog.setFileMode(QFileDialog::Directory); + dialog.setDirectory(YAPPPath); + + if (dialog.exec()) + { + fileNames = dialog.selectedFiles(); + if (fileNames[0].length() < 256) + strcpy(YAPPPath, fileNames[0].toUtf8()); + + SaveSettings(); + } +} + +void QtTermTCP::menuChecked() +{ + QAction * Act = static_cast(QObject::sender()); + + int state = Act->isChecked(); + int newTermMode = TermMode; + int newSingleMode = singlemodeFormat; + + if (Act == TabSingle || Act == TabBoth || Act == TabMon) + { + // Tabbed Window format had changed + + int i = tabWidget->currentIndex(); + + if (Act == TabSingle) + TabType[i] = Term; + else if (Act == TabBoth) + TabType[i] = Term + Mon; + else + TabType[i] = Mon; + + QMessageBox msgBox; + msgBox.setText("Tab Types will change next time program is started."); + msgBox.exec(); + + } + + if (Act == singleAct || Act == singleAct2 || Act == MDIAct || Act == tabbedAct) + { + // Term Mode had changed + + if (singleAct->isChecked()) + { + newTermMode = Single; + newSingleMode = Mon + Term; + } + else if (singleAct2->isChecked()) + { + newTermMode = Single; + newSingleMode = Term; + } + else if (MDIAct->isChecked()) + newTermMode = MDI; + else if (tabbedAct->isChecked()) + newTermMode = Tabbed; + + if (newTermMode != TermMode || newSingleMode != singlemodeFormat) + { + QSettings settings(GetConfPath(), QSettings::IniFormat); + settings.setValue("TermMode", newTermMode); + settings.setValue("singlemodeFormat", newSingleMode); + QMessageBox msgBox; + msgBox.setText("Presentation Mode will change next time program is started."); + msgBox.exec(); + } + + return; + } + + + if (Act == MonTX) + ActiveSession->mtxparam = state; + else if (Act == MonSup) + ActiveSession->mcomparam = state; + else if (Act == MonUI) + ActiveSession->monUI = state; + else if (Act == MonNodes) + ActiveSession->MonitorNODES = state; + else if (Act == MonColour) + ActiveSession->MonitorColour = state; + else if (Act == actChatMode) + ChatMode = state; + else if (Act == actBells) + Bells = state; + else if (Act == actStripLF) + StripLF = state; + else if (Act == actIntervalBeep) + AlertBeep = state; + else if (Act == actConnectBeep) + ConnectBeep = state; + else if (Act == actnoConv) + convUTF8 = -1; + else if (Act == actAuto) + convUTF8 = 0; + else if (Act == actCP1251) + convUTF8 = 1251; + else if (Act == actCP1252) + convUTF8 = 1252; + else if (Act == actCP437) + convUTF8 = 437; + else + { + // look for port entry + + for (int i = 0; i < MAXPORTS; i++) + { + if (Act == MonPort[i]) + { + unsigned long long mmask; + + if (i == 0) // BBS Mon - use bit 32 + mmask = 1ll << 32; + else + mmask = 1ll << (i - 1); + + if (state) + ActiveSession->portmask |= mmask; + else + ActiveSession->portmask &= ~mmask; + break; + } + } + } + + // Get active Session +/* + + QMdiSubWindow *SW = mdiArea->activeSubWindow(); + + Ui_ListenSession * Sess; + + for (int i = 0; i < _sessions.size(); ++i) + { + Sess = _sessions.at(i); + +// for (Ui_ListenSession * Sess : _sessions) +// { + if (Sess->sw == SW) + { + */ + + if (ActiveSession->clientSocket && ActiveSession->SessionType & Mon) + SendTraceOptions(ActiveSession); + + return; +} + + + + +void QtTermTCP::LDisconnect(Ui_ListenSession * LUI) +{ + if (LUI->clientSocket) + LUI->clientSocket->disconnectFromHost(); +} + +extern QApplication * a; + +void QtTermTCP::LreturnPressed(Ui_ListenSession * Sess) +{ + QByteArray stringData = Sess->inputWindow->text().toUtf8(); + + // if multiline input (eg from copy/paste) replace LF with CR + + char * ptr; + char * Msgptr; + char * Msg; + + QScrollBar *scrollbar = Sess->termWindow->verticalScrollBar(); + bool scrollbarAtBottom = (scrollbar->value() >= (scrollbar->maximum() - 4)); + + if (scrollbarAtBottom) + Sess->termWindow->moveCursor(QTextCursor::End); // So we don't get blank lines + + // Stack it + + Sess->StackIndex = 0; + + if (Sess->KbdStack[49]) + free(Sess->KbdStack[49]); + + for (int i = 48; i >= 0; i--) + { + Sess->KbdStack[i + 1] = Sess->KbdStack[i]; + } + + Sess->KbdStack[0] = qstrdup(stringData.data()); + + stringData.append('\n'); + + Msgptr = stringData.data(); + + while ((ptr = strchr(Msgptr, 11))) // Replace VT with LF + *ptr++ = 10; + + LastWrite = time(NULL); // Stop initial beep + Sess->SlowTimer = 0; + + if (Sess->KISSMode == 1) + { + // UI session. Send as UI Message + + while ((ptr = strchr(Msgptr, '\n'))) + { + *ptr++ = 0; + + Msg = (char *)malloc(strlen(Msgptr) + 10); + strcpy(Msg, Msgptr); + strcat(Msg, "\r"); + + Send_UI(0, 0xF0, MYCALL, Sess->UIDEST, (unsigned char *)Msg, (int)strlen(Msg)); + + WritetoOutputWindowEx(Sess, (unsigned char *)Msg, (int)strlen(Msg), + Sess->termWindow, &Sess->OutputSaveLen, Sess->OutputSave, EchoText); // Black + + free(Msg); + + Msgptr = ptr; + } + } + else if (Sess->KISSSession) + { + // Send to ax.25 code + + while ((ptr = strchr(Msgptr, '\n'))) + { + *ptr++ = 0; + + Msg = (char *)malloc(strlen(Msgptr) + 10); + strcpy(Msg, Msgptr); + strcat(Msg, "\r"); + + SendtoAX25(Sess->KISSSession, (unsigned char *)Msg, (int)strlen(Msg)); + + WritetoOutputWindowEx(Sess, (unsigned char *)Msg, (int)strlen(Msg), + Sess->termWindow, &Sess->OutputSaveLen, Sess->OutputSave, EchoText); // Black + + free(Msg); + + Msgptr = ptr; + } + } + + else if (Sess->AGWSession) + { + // Terminal is in AGWPE mode - send as AGW frame + + while ((ptr = strchr(Msgptr, '\n'))) + { + *ptr++ = 0; + + Msg = (char *)malloc(strlen(Msgptr) + 10); + strcpy(Msg, Msgptr); + strcat(Msg, "\r"); + + AGW_AX25_data_in(Sess->AGWSession, (unsigned char *)Msg, (int)strlen(Msg)); + + WritetoOutputWindowEx(Sess, (unsigned char *)Msg, (int)strlen(Msg), + Sess->termWindow, &Sess->OutputSaveLen, Sess->OutputSave, EchoText); // Black + +// Sess->termWindow->insertPlainText(Msg); + + free(Msg); + + Msgptr = ptr; + } + } + + else if (VARASock && VARASock->Sess == Sess) + { + while ((ptr = strchr(Msgptr, '\n'))) + { + *ptr++ = 0; + + Msg = (char *)malloc(strlen(Msgptr) + 10); + strcpy(Msg, Msgptr); + strcat(Msg, "\r"); + + VARADataSock->write(Msg); + + WritetoOutputWindowEx(Sess, (unsigned char *)Msg, (int)strlen(Msg), + Sess->termWindow, &Sess->OutputSaveLen, Sess->OutputSave, EchoText); // Black + +// Sess->termWindow->insertPlainText(Msg); + + free(Msg); + + Msgptr = ptr; + } + + + } + else if (Sess->clientSocket && Sess->clientSocket->state() == QAbstractSocket::ConnectedState) + { + while ((ptr = strchr(Msgptr, '\n'))) + { + *ptr++ = 0; + + Msg = (char *)malloc(strlen(Msgptr) + 10); + strcpy(Msg, Msgptr); + strcat(Msg, "\r"); + + Sess->clientSocket->write(Msg); + + WritetoOutputWindowEx(Sess, (unsigned char *)Msg, (int)strlen(Msg), + Sess->termWindow, &Sess->OutputSaveLen, Sess->OutputSave, EchoText); // Black + +// Sess->termWindow->insertPlainText(Msg); + + free(Msg); + + Msgptr = ptr; + } + } + else + { + // Not Connected + + if (Sess->SessionType != Listen) + { + if (Sess->CurrentHost < MAXHOSTS) + { + // Last connect was TCP + + char Msg[] = "Connecting....\r"; + + WritetoOutputWindowEx(Sess, (unsigned char *)Msg, (int)strlen(Msg), + Sess->termWindow, &Sess->OutputSaveLen, Sess->OutputSave, WarningText); // Red + + delete(Sess->clientSocket); + + Sess->clientSocket = new myTcpSocket(); + Sess->clientSocket->Sess = Sess; + + connect(Sess->clientSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(displayError(QAbstractSocket::SocketError))); + connect(Sess->clientSocket, SIGNAL(readyRead()), this, SLOT(readyRead())); + connect(Sess->clientSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onSocketStateChanged(QAbstractSocket::SocketState))); + + Sess->clientSocket->connectToHost(&Host[Sess->CurrentHost][0], Port[Sess->CurrentHost]); + } + + else if (Sess->CurrentHost == 16) // AGW + { + // Invoke AGW connect dialog + + AGWConnect dialog(0); + dialog.exec(); + + WritetoOutputWindow(Sess, (unsigned char *)"Connecting...\r", 14); + return; + } + + else if (Sess->CurrentHost == 17) // VARA + { + // Invoke VARA connect dialog + + VARAConnect dialog(0); + dialog.exec(); + + WritetoOutputWindow(Sess, (unsigned char *)"Connecting...\r", 14); + return; + } + + else if (Sess->CurrentHost == 18) // KISS + { + // Do we send as UI or invoke kiss connect dialog ?? + + // Try Connect Dialog for now - may make a menu option + + KISSConnect dialog(0); + dialog.exec(); + + WritetoOutputWindow(Sess, (unsigned char *)"Connecting...\r", 14); + return; + } + } + else + { + char Msg[] = "Incoming Session - Can't Connect\r"; + + WritetoOutputWindowEx(Sess, (unsigned char *)Msg, (int)strlen(Msg), + Sess->termWindow, &Sess->OutputSaveLen, Sess->OutputSave, 81); // Red + + } + } + + if (scrollbarAtBottom) + Sess->termWindow->moveCursor(QTextCursor::End); + + Sess->inputWindow->setText(""); + a->inputMethod()->hide(); +} + + +void QtTermTCP::displayError(QAbstractSocket::SocketError socketError) +{ + myTcpSocket* sender = static_cast(QObject::sender()); + + switch (socketError) + { + case QAbstractSocket::RemoteHostClosedError: + break; + + case QAbstractSocket::HostNotFoundError: + QMessageBox::information(this, tr("QtTermTCP"), + tr("The host was not found. Please check the " + "host name and portsettings->")); + break; + + case QAbstractSocket::ConnectionRefusedError: + QMessageBox::information(this, tr("QtTermTCP"), + tr("The connection was refused by the peer.")); + break; + + default: + QMessageBox::information(this, tr("QtTermTCP"), + tr("The following error occurred: %1.") + .arg(sender->errorString())); + } + + connectMenu->setEnabled(true); +} + +void QtTermTCP::readyRead() +{ + int Read; + unsigned char Buffer[8192]; + myTcpSocket* Socket = static_cast(QObject::sender()); + + Ui_ListenSession * Sess = (Ui_ListenSession *)Socket->Sess; + + // read the data from the socket + + Read = Socket->read((char *)Buffer, 2047); + + while (Read > 0) + { +// if (InputMode == 'Y') // Yapp +// { +// QString myString = QString::fromUtf8((char*)Buffer, Read); +// QByteArray ptr = myString.toLocal8Bit(); +// memcpy(Buffer, ptr.data(), ptr.length()); +// Read = ptr.length(); +// } + + ProcessReceivedData(Sess, Buffer, Read); + + QString myString = QString::fromUtf8((char*)Buffer); +// qDebug() << myString; + Read = Socket->read((char *)Buffer, 2047); + } +} + +extern "C" int SocketSend(Ui_ListenSession * Sess, char * Buffer, int len) +{ + myTcpSocket *Socket = Sess->clientSocket; + + if (Socket && Socket->state() == QAbstractSocket::ConnectedState) + return Socket->write(Buffer, len); + + return 0; +} + +extern "C" int SocketFlush(Ui_ListenSession * Sess) +{ + if (Sess->AGWSession || Sess->KISSSession || (VARASock && VARASock->Sess == Sess)) + return 0; + + myTcpSocket* Socket = Sess->clientSocket; + + if (Socket && Socket->state() == QAbstractSocket::ConnectedState) + return Socket->flush(); + + return 0; +} + +extern "C" void mySleep(int ms) +{ + QThread::msleep(ms); +} + +extern "C" void SetPortMonLine(int i, char * Text, int visible, int enabled) +{ + MonPort[i]->setText(Text); + MonPort[i]->setVisible(visible); + MonPort[i]->setChecked(enabled); +} + +bool scrollbarAtBottom = 0; + +extern "C" void WritetoOutputWindowEx(Ui_ListenSession * Sess, unsigned char * Buffer, int len, QTextEdit * termWindow, int * OutputSaveLen, char * OutputSave, QColor Colour); + +extern "C" void WritetoOutputWindow(Ui_ListenSession * Sess, unsigned char * Buffer, int len) +{ + WritetoOutputWindowEx(Sess, Buffer, len, Sess->termWindow, &Sess->OutputSaveLen, Sess->OutputSave, outputText); +} + + +extern "C" void WritetoOutputWindowEx(Ui_ListenSession * Sess, unsigned char * Buffer, int len, QTextEdit * termWindow, int * OutputSaveLen, char * OutputSave, QColor Colour) +{ + unsigned char Copy[8192]; + unsigned char * ptr1, *ptr2; + unsigned char Line[8192]; + unsigned char out[8192]; + int outlen; + + int num; + + if (termWindow == NULL) + return; + + time_t NOW = time(NULL); + + // Beep if no output for a while + + if (AlertInterval && (NOW - LastWrite) > AlertInterval) + { + if (AlertBeep) + myBeep(); + } + + LastWrite = NOW; + + // Mustn't mess with original buffer + + memcpy(Copy, Buffer, len); + + Copy[len] = 0; + + ptr1 = Copy; + + const QTextCursor old_cursor = termWindow->textCursor(); + const int old_scrollbar_value = termWindow->verticalScrollBar()->value(); + const bool is_scrolled_down = old_scrollbar_value == termWindow->verticalScrollBar()->maximum(); + + if (*OutputSaveLen) + { + // Have part line - append to it + memcpy(&OutputSave[*OutputSaveLen], Copy, len); + *OutputSaveLen += len; + ptr1 = (unsigned char *)OutputSave; + len = *OutputSaveLen; + *OutputSaveLen = 0; + + // part line was written to screen so remove it + +// termWindow->setFocus(); + termWindow->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor); + termWindow->moveCursor(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor); + termWindow->moveCursor(QTextCursor::End, QTextCursor::KeepAnchor); + termWindow->textCursor().removeSelectedText(); + // termWindow->textCursor().deletePreviousChar(); + } + + // Move the cursor to the end of the document. + + termWindow->moveCursor(QTextCursor::End); + + // Insert the text at the position of the cursor (which is the end of the document). + +lineloop: + + if (len <= 0) + { + if (old_cursor.hasSelection() || !is_scrolled_down) + { + // The user has selected text or scrolled away from the bottom: maintain position. + termWindow->setTextCursor(old_cursor); + termWindow->verticalScrollBar()->setValue(old_scrollbar_value); + } + else + { + // The user hasn't selected any text and the scrollbar is at the bottom: scroll to the bottom. + termWindow->moveCursor(QTextCursor::End); + termWindow->verticalScrollBar()->setValue(termWindow->verticalScrollBar()->maximum()); + } + + return; + } + + + // copy text to control a line at a time + + ptr2 = (unsigned char *)memchr(ptr1, 13, len); + + if (ptr2 == 0) // No CR + { + if (len > 8000) + len = 8000; // Should never get lines this long + + memmove(OutputSave, ptr1, len); + *OutputSaveLen = len; + + // Write part line to screen + + memcpy(Line, ptr1, len); + Line[len] = 0; + + // I don't think I need to worry if UTF8 as will be rewritten when rest arrives + + if (Line[0] == 0x1b) // Colour Escape + { + if (Sess->MonitorColour) + termWindow->setTextColor(Colours[Line[1] - 10]); + + termWindow->textCursor().insertText(QString::fromUtf8((char*)&Line[2])); + } + else + { + termWindow->setTextColor(Colour); + termWindow->textCursor().insertText(QString::fromUtf8((char*)Line)); + } + +// *OutputSaveLen = 0; // Test if we need to delete part line + +// if (scrollbarAtBottom) +// termWindow->moveCursor(QTextCursor::End); + + if (old_cursor.hasSelection() || !is_scrolled_down) + { + // The user has selected text or scrolled away from the bottom: maintain position. + termWindow->setTextCursor(old_cursor); + termWindow->verticalScrollBar()->setValue(old_scrollbar_value); + } + else + { + // The user hasn't selected any text and the scrollbar is at the bottom: scroll to the bottom. + termWindow->moveCursor(QTextCursor::End); + termWindow->verticalScrollBar()->setValue(termWindow->verticalScrollBar()->maximum()); + } + return; + } + + *(ptr2++) = 0; + + if (Bells) + { + char * ptr; + + do { + ptr = (char *)memchr(ptr1, 7, len); + + if (ptr) + { + *(ptr) = 32; + myBeep(); + } + } while (ptr); + } + + num = ptr2 - ptr1 - 1; + + // if (LogMonitor) WriteMonitorLine(ptr1, ptr2 - ptr1); + + memcpy(Line, ptr1, num); + Line[num++] = 13; + Line[num] = 0; + + if (Line[0] == 0x1b) // Colour Escape + { + if (Sess->MonitorColour) + termWindow->setTextColor(Colours[Line[1] - 10]); + + outlen = checkUTF8(&Line[2], num - 2, out); + out[outlen] = 0; + termWindow->textCursor().insertText(QString::fromUtf8((char*)out)); +// termWindow->textCursor().insertText(QString::fromUtf8((char*)&Line[2])); + } + else + { + termWindow->setTextColor(Colour); + + outlen = checkUTF8(Line, num, out); + out[outlen] = 0; + termWindow->textCursor().insertText(QString::fromUtf8((char*)out)); +// termWindow->textCursor().insertText(QString::fromUtf8((char*)Line)); + } + + len -= (ptr2 - ptr1); + + ptr1 = ptr2; + + if ((len > 0) && StripLF) + { + if (*ptr1 == 0x0a) // Line Feed + { + ptr1++; + len--; + } + } + + + + goto lineloop; + +} + +extern "C" void WritetoMonWindow(Ui_ListenSession * Sess, unsigned char * Msg, int len) +{ + char * ptr1 = (char *)Msg, * ptr2; + char Line[512]; + int num; + +// QScrollBar *scrollbar = Sess->monWindow->verticalScrollBar(); +// bool scrollbarAtBottom = (scrollbar->value() >= (scrollbar->maximum() - 4)); + + if (Sess->monWindow == nullptr) + return; + + Sess->monWindow->setStyleSheet(monStyleSheet); + + const QTextCursor old_cursor = Sess->monWindow->textCursor(); + const int old_scrollbar_value = Sess->monWindow->verticalScrollBar()->value(); + const bool is_scrolled_down = old_scrollbar_value == Sess->monWindow->verticalScrollBar()->maximum(); + + // Move the cursor to the end of the document. + Sess->monWindow->moveCursor(QTextCursor::End); + + // Insert the text at the position of the cursor (which is the end of the document). + + + // Write a line at a time so we can action colour chars + +// Buffer[Len] = 0; + +// if (scrollbarAtBottom) +// Sess->monWindow->moveCursor(QTextCursor::End); + + if (Sess->MonSaveLen) + { + // Have part line - append to it + memcpy(&Sess->MonSave[Sess->MonSaveLen], Msg, len); + Sess->MonSaveLen += len; + ptr1 = Sess->MonSave; + len = Sess->MonSaveLen; + Sess->MonSaveLen = 0; + } + +lineloop: + + if (len <= 0) + { +// if (scrollbarAtBottom) +// Sess->monWindow->moveCursor(QTextCursor::End); + + + if (old_cursor.hasSelection() || !is_scrolled_down) + { + // The user has selected text or scrolled away from the bottom: maintain position. + Sess->monWindow->setTextCursor(old_cursor); + Sess->monWindow->verticalScrollBar()->setValue(old_scrollbar_value); + } + else + { + // The user hasn't selected any text and the scrollbar is at the bottom: scroll to the bottom. + Sess->monWindow->moveCursor(QTextCursor::End); + Sess->monWindow->verticalScrollBar()->setValue(Sess->monWindow->verticalScrollBar()->maximum()); + } + + return; + } + + // copy text to control a line at a time + + ptr2 = (char *)memchr(ptr1, 13, len); + + if (ptr2 == 0) // No CR + { + memmove(Sess->MonSave, ptr1, len); + Sess->MonSaveLen = len; + return; + } + + *(ptr2++) = 0; + +// if (LogMonitor) WriteMonitorLine(ptr1, ptr2 - ptr1); + + num = ptr2 - ptr1 - 1; + + memcpy(Line, ptr1, num); + Line[num++] = 13; + Line[num] = 0; + + if (Line[0] == 0x1b) // Colour Escape + { + if (Sess->MonitorColour) + { + if (Line[1] == 17) + Sess->monWindow->setTextColor(monRxText); + else + Sess->monWindow->setTextColor(monTxText); + } + else + Sess->monWindow->setTextColor(monOtherText); + + Sess->monWindow->textCursor().insertText(QString::fromUtf8((char*)&Line[2])); + } + else + { + Sess->monWindow->textCursor().insertText(QString::fromUtf8((char*)Line)); + } + len -= (ptr2 - ptr1); + + ptr1 = ptr2; + + goto lineloop; + +} + +int ProcessYAPPMessage(Ui_ListenSession * Sess, UCHAR * Msg, int Len); +void QueueMsg(Ui_ListenSession * Sess, char * Msg, int Len); + +// YAPP stuff + +#define SOH 1 +#define STX 2 +#define ETX 3 +#define EOT 4 +#define ENQ 5 +#define ACK 6 +#define DLE 0x10 +#define NAK 0x15 +#define CAN 0x18 + +extern "C" void SendtoTerm(Ui_ListenSession * Sess, char * Msg, int Len) +{ + Sess->SlowTimer = 0; + + if (Sess->InputMode == 'Y') // Yapp + { + ProcessYAPPMessage(Sess, (unsigned char *)Msg, Len); + return; + } + + // Could be a YAPP Header + + if (Len == 2 && Msg[0] == ENQ && Msg[1] == 1) // YAPP Send_Init + { + char YAPPRR[2]; + + // Turn off monitoring + + Sess->InputMode = 'Y'; + + YAPPRR[0] = ACK; + YAPPRR[1] = 1; + QueueMsg(Sess, YAPPRR, 2); + + return; + } + + WritetoOutputWindow(Sess, (unsigned char *)Msg, Len); + +} + +QSettings* settings; + +// This makes geting settings for more channels easier + +char Prefix[16] = "AX25_A"; + +void GetPortSettings(int Chan); + +QVariant getAX25Param(const char * key, QVariant Default) +{ + char fullKey[64]; + + sprintf(fullKey, "%s/%s", Prefix, key); + return settings->value(fullKey, Default); +} + +void getAX25Params(int chan) +{ + Prefix[5] = chan + 'A'; + GetPortSettings(chan); +} + + +void GetPortSettings(int Chan) +{ + maxframe[Chan] = getAX25Param("Maxframe", 4).toInt(); + fracks[Chan] = getAX25Param("Retries", 10).toInt(); + frack_time[Chan] = getAX25Param("FrackTime", 8).toInt(); + persist[Chan] = getAX25Param("Persist", 128).toInt(); + kisspaclen[Chan] = getAX25Param("Paclen", 128).toInt(); + idletime[Chan] = getAX25Param("IdleTime", 180).toInt(); + slottime[Chan] = getAX25Param("SlotTime", 100).toInt(); + resptime[Chan] = getAX25Param("RespTime", 1500).toInt(); + TXFrmMode[Chan] = getAX25Param("TXFrmMode", 1).toInt(); + max_frame_collector[Chan] = getAX25Param("FrameCollector", 6).toInt(); + //exclude_callsigns[Chan]= getAX25Param("ExcludeCallsigns/"); + //exclude_APRS_frm[Chan]= getAX25Param("ExcludeAPRSFrmType/"); + KISS_opt[Chan] = getAX25Param("KISSOptimization", false).toInt();; + dyn_frack[Chan] = getAX25Param("DynamicFrack", false).toInt();; + IPOLL[Chan] = getAX25Param("IPOLL", 80).toInt(); + +} + + +void GetSettings() +{ + QByteArray qb; + int i; + char Key[16]; + char Param[256]; + + settings = new QSettings(GetConfPath(), QSettings::IniFormat); + + // Get saved session definitions + + sessionList = strdup(settings->value("Sessions", "1|3, 0, 5, 5, 600, 800|").toString().toUtf8()); + + for (i = 0; i < MAXHOSTS; i++) + { + sprintf(Key, "HostParams%d", i); + + qb =settings->value(Key).toByteArray(); + + DecodeSettingsLine(i, qb.data()); + } + + Split =settings->value("Split", 50).toInt(); + ChatMode =settings->value("ChatMode", 1).toInt(); + Bells =settings->value("Bells", 1).toInt(); + StripLF =settings->value("StripLF", 1).toInt(); + AlertBeep =settings->value("AlertBeep", 1).toInt(); + ConnectBeep =settings->value("ConnectBeep", 1).toInt(); + SavedHost =settings->value("CurrentHost", 0).toInt(); + strcpy(YAPPPath,settings->value("YAPPPath", "").toString().toUtf8()); + + listenPort =settings->value("listenPort", 8015).toInt(); + listenEnable =settings->value("listenEnable", false).toBool(); + strcpy(listenCText,settings->value("listenCText", "").toString().toUtf8()); + + TermMode =settings->value("TermMode", 0).toInt(); + singlemodeFormat =settings->value("singlemodeFormat", Term + Mon).toInt(); + + AGWEnable =settings->value("AGWEnable", 0).toInt(); + AGWMonEnable =settings->value("AGWMonEnable", 0).toInt(); + strcpy(AGWTermCall,settings->value("AGWTermCall", "").toString().toUtf8()); + strcpy(AGWBeaconDest,settings->value("AGWBeaconDest", "").toString().toUtf8()); + strcpy(AGWBeaconPath,settings->value("AGWBeaconPath", "").toString().toUtf8()); + AGWBeaconInterval =settings->value("AGWBeaconInterval", 0).toInt(); + strcpy(AGWBeaconPorts,settings->value("AGWBeaconPorts", "").toString().toUtf8()); + strcpy(AGWBeaconMsg,settings->value("AGWBeaconText", "").toString().toUtf8()); + strcpy(AGWHost,settings->value("AGWHost", "127.0.0.1").toString().toUtf8()); + AGWPortNum =settings->value("AGWPort", 8000).toInt(); + AGWPaclen =settings->value("AGWPaclen", 80).toInt(); + AGWToCalls =settings->value("AGWToCalls","").toStringList(); + convUTF8 =settings->value("convUTF8", 0).toInt(); + + KISSEnable =settings->value("KISSEnable", 0).toInt(); + strcpy(MYCALL,settings->value("MYCALL", "").toString().toUtf8()); + strcpy(KISSHost,settings->value("KISSHost", "127.0.0.1").toString().toUtf8()); + KISSPortNum = settings->value("KISSPort", 8100).toInt(); + KISSMode = settings->value("KISSMode", 0).toInt(); + + strcpy(SerialPort,settings->value("KISSSerialPort", "None").toString().toUtf8()); + KISSBAUD =settings->value("KISSBAUD", 19200).toInt(); + getAX25Params(0); + + VARAEnable =settings->value("VARAEnable", 0).toInt(); + strcpy(VARAHost,settings->value("VARAHost", "127.0.0.1").toString().toUtf8()); + strcpy(VARAHostFM,settings->value("VARAHostFM", "127.0.0.1").toString().toUtf8()); + strcpy(VARAHostHF,settings->value("VARAHostHF", "127.0.0.1").toString().toUtf8()); + strcpy(VARAHostSAT,settings->value("VARAHostSAT", "127.0.0.1").toString().toUtf8()); + VARAPortNum =settings->value("VARAPort", 8300).toInt(); + VARAPortHF =settings->value("VARAPortHF", 8300).toInt(); + VARAPortFM =settings->value("VARAPortFM", 8300).toInt(); + VARAPortSAT =settings->value("VARAPortSAT", 8300).toInt(); + strcpy(VARAPath,settings->value("VARAPath", "C:\\VARA\\VARA.exe").toString().toUtf8()); + strcpy(VARAPathHF,settings->value("VARAPathHF", "C:\\VARA\\VARA.exe").toString().toUtf8()); + strcpy(VARAPathFM,settings->value("VARAPathFM", "C:\\VARA\\VARAFM.exe").toString().toUtf8()); + strcpy(VARAPathSAT,settings->value("VARAPathSAT", "C:\\VARA\\VARASAT.exe").toString().toUtf8()); + strcpy(VARATermCall,settings->value("VARATermCall", "").toString().toUtf8()); + VARA500 =settings->value("VARA500", 0).toInt(); + VARA2300 =settings->value("VARA2300", 1).toInt(); + VARA2750 =settings->value("VARA2750", 0).toInt(); + VARAHF =settings->value("VARAHF", 1).toInt(); + VARAFM =settings->value("VARAFM", 0).toInt(); + VARASAT =settings->value("VARASAT", 0).toInt(); + + strcpy(PTTPort,settings->value("PTT", "None").toString().toUtf8()); + PTTMode =settings->value("PTTMode", 19200).toInt(); + PTTBAUD =settings->value("PTTBAUD", 19200).toInt(); + CATHex =settings->value("CATHex", 1).toInt(); + + strcpy(PTTOnString,settings->value("PTTOnString", "").toString().toUtf8()); + strcpy(PTTOffString,settings->value("PTTOffString", "").toString().toUtf8()); + + pttGPIOPin =settings->value("pttGPIOPin", 17).toInt(); + pttGPIOPinR =settings->value("pttGPIOPinR", 17).toInt(); + +#ifdef WIN32 + strcpy(CM108Addr,settings->value("CM108Addr", "0xD8C:0x08").toString().toUtf8()); +#else + strcpy(CM108Addr,settings->value("CM108Addr", "/dev/hidraw0").toString().toUtf8()); +#endif + + HamLibPort =settings->value("HamLibPort", 4532).toInt(); + strcpy(HamLibHost,settings->value("HamLibHost", "127.0.0.1").toString().toUtf8()); + + FLRigPort =settings->value("FLRigPort", 12345).toInt(); + strcpy(FLRigHost,settings->value("FLRigHost", "127.0.0.1").toString().toUtf8()); + + + strcpy(Param,settings->value("TabType", "1 1 1 1 1 1 1 2 2").toString().toUtf8()); + sscanf(Param, "%d %d %d %d %d %d %d %d %d %d", + &TabType[0], &TabType[1], &TabType[2], &TabType[3], &TabType[4], + &TabType[5], &TabType[6], &TabType[7], &TabType[8], &TabType[9]); + + monBackground = settings->value("monBackground", QColor(255,255,255)).value(); + monRxText = settings->value("monRxText", QColor(0, 0, 255)).value(); + monTxText = settings->value("monTxText", QColor(255, 0, 0)).value(); + monOtherText = settings->value("monOtherText", QColor(0, 0, 0)).value(); + + termBackground = settings->value("termBackground", QColor(255, 255, 255)).value(); + outputText = settings->value("outputText", QColor(0, 0, 255)).value(); + EchoText = settings->value("EchoText", QColor(0, 0, 0)).value(); + WarningText = settings->value("WarningText", QColor(255, 0, 0)).value(); + + inputBackground = settings->value("inputBackground", QColor(255, 255, 255)).value(); + inputText = settings->value("inputText", QColor(0, 0, 0)).value(); + + delete(settings); +} + +void SavePortSettings(int Chan); + +void saveAX25Param(const char * key, QVariant Value) +{ + char fullKey[64]; + + sprintf(fullKey, "%s/%s", Prefix, key); + + settings->setValue(fullKey, Value); +} + +void saveAX25Params(int chan) +{ + Prefix[5] = chan + 'A'; + SavePortSettings(chan); +} + +void SavePortSettings(int Chan) +{ + saveAX25Param("Retries", fracks[Chan]); + saveAX25Param("Maxframe", maxframe[Chan]); + saveAX25Param("Paclen", kisspaclen[Chan]); + saveAX25Param("FrackTime", frack_time[Chan]); + saveAX25Param("IdleTime", idletime[Chan]); + saveAX25Param("SlotTime", slottime[Chan]); + saveAX25Param("Persist", persist[Chan]); + saveAX25Param("RespTime", resptime[Chan]); + saveAX25Param("TXFrmMode", TXFrmMode[Chan]); + saveAX25Param("FrameCollector", max_frame_collector[Chan]); + saveAX25Param("ExcludeCallsigns", exclude_callsigns[Chan]); + saveAX25Param("ExcludeAPRSFrmType", exclude_APRS_frm[Chan]); + saveAX25Param("KISSOptimization", KISS_opt[Chan]); + saveAX25Param("DynamicFrack", dyn_frack[Chan]); + saveAX25Param("BitRecovery", recovery[Chan]); + saveAX25Param("IPOLL", IPOLL[Chan]); + saveAX25Param("MyDigiCall", MyDigiCall[Chan]); +} + +extern "C" void SaveSettings() +{ + int i; + char Param[512]; + char Key[16]; + + settings = new QSettings(GetConfPath(), QSettings::IniFormat); + + for (i = 0; i < MAXHOSTS; i++) + { + sprintf(Key, "HostParams%d", i); + EncodeSettingsLine(i, Param); + settings->setValue(Key, Param); + } + + settings->setValue("Split", Split); + settings->setValue("ChatMode", ChatMode); + settings->setValue("Bells", Bells); + settings->setValue("StripLF", StripLF); + settings->setValue("AlertBeep", AlertBeep); + settings->setValue("ConnectBeep", ConnectBeep); + settings->setValue("CurrentHost", SavedHost); + + settings->setValue("YAPPPath", YAPPPath); + settings->setValue("listenPort", listenPort); + settings->setValue("listenEnable", listenEnable); + settings->setValue("listenCText", listenCText); + settings->setValue("convUTF8", convUTF8); + + settings->setValue("PTT", PTTPort); + settings->setValue("PTTBAUD", PTTBAUD); + settings->setValue("PTTMode", PTTMode); + + settings->setValue("CATHex", CATHex); + + settings->setValue("PTTOffString", PTTOffString); + settings->setValue("PTTOnString", PTTOnString); + + settings->setValue("pttGPIOPin", pttGPIOPin); + settings->setValue("pttGPIOPinR", pttGPIOPinR); + + settings->setValue("CM108Addr", CM108Addr); + settings->setValue("HamLibPort", HamLibPort); + settings->setValue("HamLibHost", HamLibHost); + settings->setValue("FLRigPort", FLRigPort); + settings->setValue("FLRigHost", FLRigHost); + + + // Save Sessions + + char SessionString[1024]; + int SessStringLen; + + SessStringLen = sprintf(SessionString, "%d|", _sessions.size()); + + if (TermMode == MDI) + { + for (int i = 0; i < _sessions.size(); ++i) + { + Ui_ListenSession * Sess = _sessions.at(i); + + QRect r = Sess->sw->geometry(); + + SessStringLen += sprintf(&SessionString[SessStringLen], + "%d, %d, %d, %d, %d, %d|", Sess->SessionType, Sess->CurrentHost, r.top(), r.left(), r.bottom(), r.right()); + } + + settings->setValue("Sessions", SessionString); + } + + settings->setValue("AGWEnable", AGWEnable); + settings->setValue("AGWMonEnable", AGWMonEnable); + settings->setValue("AGWTermCall", AGWTermCall); + settings->setValue("AGWBeaconDest", AGWBeaconDest); + settings->setValue("AGWBeaconPath", AGWBeaconPath); + settings->setValue("AGWBeaconInterval", AGWBeaconInterval); + settings->setValue("AGWBeaconPorts", AGWBeaconPorts); + settings->setValue("AGWBeaconText", AGWBeaconMsg); + settings->setValue("AGWHost", AGWHost); + settings->setValue("AGWPort", AGWPortNum); + settings->setValue("AGWPaclen", AGWPaclen); + settings->setValue("AGWToCalls", AGWToCalls); + + + settings->setValue("KISSEnable", KISSEnable); + settings->setValue("MYCALL", MYCALL); + settings->setValue("KISSHost", KISSHost); + settings->setValue("KISSMode", KISSMode); + settings->setValue("KISSPort", KISSPortNum); + settings->setValue("KISSSerialPort", SerialPort); + settings->setValue("KISSBAUD", KISSBAUD); + saveAX25Params(0); + + settings->setValue("VARAEnable", VARAEnable); + settings->setValue("VARATermCall", VARATermCall); + settings->setValue("VARAHost", VARAHost); + settings->setValue("VARAPort", VARAPortNum); + settings->setValue("VARAPath", VARAPath); + settings->setValue("VARAHostHF", VARAHostHF); + settings->setValue("VARAPortHF", VARAPortHF); + settings->setValue("VARAPathHF", VARAPathHF); + settings->setValue("VARAHostFM", VARAHostFM); + settings->setValue("VARAPortFM", VARAPortFM); + settings->setValue("VARAPathFM", VARAPathFM); + settings->setValue("VARAHostSAT", VARAHostSAT); + settings->setValue("VARAPortSAT", VARAPortSAT); + settings->setValue("VARAPathSAT", VARAPathSAT); + settings->setValue("VARA500", VARA500); + settings->setValue("VARA2300", VARA2300); + settings->setValue("VARA2750", VARA2750); + settings->setValue("VARAHF", VARAHF); + settings->setValue("VARAFM", VARAFM); + settings->setValue("VARASAT", VARASAT); + + + sprintf(Param, "%d %d %d %d %d %d %d %d %d %d", + TabType[0], TabType[1], TabType[2], TabType[3], TabType[4], TabType[5], TabType[6], TabType[7], TabType[8], TabType[9]); + + settings->setValue("TabType", Param); + + settings->setValue("monBackground", monBackground); + settings->setValue("monRxText", monRxText); + settings->setValue("monTxText", monTxText); + settings->setValue("monOtherText", monOtherText); + + settings->setValue("termBackground", termBackground); + settings->setValue("outputText", outputText); + settings->setValue("EchoText", EchoText); + settings->setValue("WarningText", WarningText); + + settings->setValue("inputBackground", inputBackground); + settings->setValue("inputText", inputText); + + settings->sync(); + delete(settings); +} + +#include + +void QtTermTCP::closeEvent(QCloseEvent *event) +{ + QMessageBox::StandardButton resBtn = QMessageBox::question(this, "QtTermTCP", + tr("Are you sure?\n"), + QMessageBox::Cancel | QMessageBox::No | QMessageBox::Yes, + QMessageBox::Yes); + if (resBtn != QMessageBox::Yes) { + event->ignore(); + } + else + { + event->accept(); +#ifdef USESERIAL + if (hPTTDevice) + hPTTDevice->close(); +#endif + if (process) + process->close(); + } +} + +QtTermTCP::~QtTermTCP() +{ + Ui_ListenSession * Sess; + + for (int i = 0; i < _sessions.size(); ++i) + { + Sess = _sessions.at(i); + + if (Sess->clientSocket) + { + int loops = 100; + Sess->clientSocket->disconnectFromHost(); + while (Sess->clientSocket && loops-- && Sess->clientSocket->state() != QAbstractSocket::UnconnectedState) + QThread::msleep(10); + } + + } + + if (AGWSock && AGWSock->ConnectedState == QAbstractSocket::ConnectedState) + { + int loops = 100; + AGWSock->disconnectFromHost(); + QThread::msleep(10); + while (AGWSock && loops-- && AGWSock->state() != QAbstractSocket::UnconnectedState) + QThread::msleep(10); + } + + if (_server->isListening()) + _server->close(); + + delete(_server); + + QSettings mysettings(GetConfPath(), QSettings::IniFormat); + mysettings.setValue("geometry", saveGeometry()); + mysettings.setValue("windowState", saveState()); + + SaveSettings(); +} + +extern "C" void timer_event(); + +void QtTermTCP::KISSTimerSlot() +{ + // Runs every 100 mS + + timer_event(); +} + +void QtTermTCP::MyTimerSlot() +{ + // Runs every 10 seconds + + Ui_ListenSession * Sess; + + for (int i = 0; i < _sessions.size(); ++i) + { + Sess = _sessions.at(i); + + // for (Ui_ListenSession * Sess : _sessions) + // { + if (Sess == NULL) + continue; + + if (!ChatMode) + continue; + + if (Sess->KISSSession || + (Sess->clientSocket && Sess->clientSocket->state() == QAbstractSocket::ConnectedState)) + { + Sess->SlowTimer++; + + if (Sess->SlowTimer > 54) // About 9 mins + { + unsigned char Msg[2] = ""; + + Sess->SlowTimer = 0; + + if (Sess->KISSSession) + { + TAX25Port * ax25 = (TAX25Port *)Sess->KISSSession; + + if (ax25->status == STAT_LINK) + SendtoAX25(Sess->KISSSession, Msg, 1); + } + else + Sess->clientSocket->write("\0", 1); + } + } + } + + if (AGWEnable) + AGWTimer(); + + if (VARAEnable) + VARATimer(); + + if (KISSEnable) + KISSTimer(); + +} + +extern "C" void myBeep() +{ + QApplication::beep(); +} + + +void QtTermTCP::ListenSlot() +{ + // This runs the Listen Configuration dialog + + ListenDialog * xx = new ListenDialog(); + xx->exec(); +} + +void QtTermTCP::AGWSlot() +{ + // This runs the AGW Configuration dialog + + AGWDialog dialog(0); + dialog.exec(); +} + +Ui_Dialog * Dev; + +static Ui_KISSDialog * KISS; +static Ui_ColourDialog * COLOURS; + +QDialog * deviceUI; + + +char NewPTTPort[80]; + +int newSoundMode = 0; +int oldSoundMode = 0; + +#ifdef USESERIAL +QList Ports = QSerialPortInfo::availablePorts(); +#endif + + +void QtTermTCP::KISSSlot() +{ + // This runs the KISS Configuration dialog + + KISS = new(Ui_KISSDialog); + + QDialog UI; + + KISS->setupUi(&UI); + + UI.setFont(*menufont); + + deviceUI = &UI; + KISS->KISSEnable->setChecked(KISSEnable); + KISS->MYCALL->setText(MYCALL); + +// connect(KISS->SerialPort, SIGNAL(currentIndexChanged(int)), this, SLOT(PTTPortChanged(int))); + + QStringList items; + +#ifdef USESERIAL + + for (const QSerialPortInfo &info : Ports) + { + items.append(info.portName()); + } + + items.sort(); + items.insert(0, "TCP"); + +#endif + + for (const QString &info : items) + { + KISS->SerialPort->addItem(info); + } + + KISS->SerialPort->setCurrentIndex(KISS->SerialPort->findText(SerialPort, Qt::MatchFixedString)); + KISS->Speed->setText(QString::number(KISSBAUD)); + KISS->Host->setText(KISSHost); + KISS->Port->setText(QString::number(KISSPortNum)); + + KISS->Paclen->setText(QString::number(kisspaclen[0])); + KISS->Maxframe->setText(QString::number(maxframe[0])); + KISS->Frack->setText(QString::number(frack_time[0])); + KISS->Retries->setText(QString::number(fracks[0])); + + QObject::connect(KISS->okButton, SIGNAL(clicked()), this, SLOT(KISSaccept())); + QObject::connect(KISS->cancelButton, SIGNAL(clicked()), this, SLOT(KISSreject())); + + UI.exec(); + +} + + +void QtTermTCP::KISSaccept() +{ + QVariant Q; + int OldEnable = KISSEnable; + char oldSerialPort[64]; + int OldPort = KISSPortNum; + char oldHost[128]; + + strcpy(oldSerialPort, SerialPort); + strcpy(oldHost, KISSHost); + + KISSEnable = KISS->KISSEnable->isChecked(); + actHost[18]->setVisible(KISSEnable); // Show KISS Connect Line + + strcpy(MYCALL, KISS->MYCALL->text().toUtf8().toUpper()); + + Q = KISS->Port->text(); + KISSPortNum = Q.toInt(); + + Q = KISS->Speed->text(); + KISSBAUD = Q.toInt(); + + strcpy(KISSHost, KISS->Host->text().toUtf8().toUpper()); + + Q = KISS->SerialPort->currentText(); + strcpy(SerialPort, Q.toString().toUtf8()); + + Q = KISS->Paclen->text(); + kisspaclen[0] = Q.toInt(); + Q = KISS->Maxframe->text(); + maxframe[0] = Q.toInt(); + Q = KISS->Frack->text(); + frack_time[0] = Q.toInt(); + Q = KISS->Retries->text(); + fracks[0] = Q.toInt(); + + myStatusBar->setVisible(AGWEnable | VARAEnable | KISSEnable); + + + if (KISSEnable != OldEnable || KISSPortNum != OldPort || + strcmp(oldHost, KISSHost) != 0 || + strcmp(oldSerialPort, SerialPort) != 0) + { + // (re)start connection + + if (OldEnable) + { + if (KISSEnable) + Status3->setText("KISS Closed"); + else + Status3->setText("KISS Disabled"); + + KISSConnected = 0; + + if (KISSSock && KISSSock->ConnectedState == QAbstractSocket::ConnectedState) + KISSSock->disconnectFromHost(); + else if (m_serial) + closeSerialPort(); + } + } + + delete(KISS); + SaveSettings(); + deviceUI->accept(); + +// QSize newSize(this->size()); +// QSize oldSize(this->size()); + +// QResizeEvent *myResizeEvent = new QResizeEvent(newSize, oldSize); + +// QCoreApplication::postEvent(this, myResizeEvent); +} + +void QtTermTCP::KISSreject() +{ + delete(KISS); + deviceUI->reject(); +} + + + + + +void QtTermTCP::VARASlot() +{ + // This runs the VARA Configuration dialog + + char valChar[80]; + + Dev = new(Ui_Dialog); + + QDialog UI; + + Dev->setupUi(&UI); + + UI.setFont(*menufont); + + deviceUI = &UI; + + Dev->VARAEnable->setChecked(VARAEnable); + Dev->TermCall->setText(VARATermCall); + + SetVARAParams(); + + connect(Dev->VARAHF, SIGNAL(toggled(bool)), this, SLOT(VARAHFChanged(bool))); + connect(Dev->VARAFM, SIGNAL(toggled(bool)), this, SLOT(VARAFMChanged(bool))); + connect(Dev->VARASAT, SIGNAL(toggled(bool)), this, SLOT(VARASATChanged(bool))); + + if (VARA500) + Dev->VARA500->setChecked(true); + else if (VARA2750) + Dev->VARA2750->setChecked(true); + else + Dev->VARA2300->setChecked(true); + + if (VARAHF) + Dev->VARAHF->setChecked(true); + else if (VARAFM) + Dev->VARAFM->setChecked(true); + else if (VARASAT) + Dev->VARASAT->setChecked(true); + + if (VARAHF == 0) + Dev->HFMode->setVisible(false); + + connect(Dev->CAT, SIGNAL(toggled(bool)), this, SLOT(CATChanged(bool))); + connect(Dev->PTTPort, SIGNAL(currentIndexChanged(int)), this, SLOT(PTTPortChanged(int))); + + if (PTTMode == PTTCAT) + Dev->CAT->setChecked(true); + else + Dev->RTSDTR->setChecked(true); + + if (CATHex) + Dev->CATHex->setChecked(true); + else + Dev->CATText->setChecked(true); + + sprintf(valChar, "%d", pttGPIOPin); + Dev->GPIOLeft->setText(valChar); + sprintf(valChar, "%d", pttGPIOPinR); + Dev->GPIORight->setText(valChar); + + Dev->VIDPID->setText(CM108Addr); + + + QStringList items; + +#ifdef USESERIAL + + for (const QSerialPortInfo &info : Ports) + { + items.append(info.portName()); + } + + items.sort(); + +#endif + + Dev->PTTPort->addItem("None"); + Dev->PTTPort->addItem("CM108"); + +#ifdef __ARM_ARCH + + // Dev->PTTPort->addItem("GPIO"); + +#endif + + Dev->PTTPort->addItem("FLRIG"); + Dev->PTTPort->addItem("HAMLIB"); + + for (const QString &info : items) + { + Dev->PTTPort->addItem(info); + } + + Dev->PTTPort->setCurrentIndex(Dev->PTTPort->findText(PTTPort, Qt::MatchFixedString)); + + PTTPortChanged(0); // Force reevaluation + + QObject::connect(Dev->okButton, SIGNAL(clicked()), this, SLOT(deviceaccept())); + QObject::connect(Dev->cancelButton, SIGNAL(clicked()), this, SLOT(devicereject())); + + UI.exec(); + +} + +extern QProcess *process; + +void ClosePTTPort(); + +void QtTermTCP::deviceaccept() +{ + QVariant Q; + + int OldEnable = VARAEnable; + int OldPort = VARAPortNum; + char oldHost[128]; + char oldPath[256]; + + strcpy(oldHost, VARAHost); + strcpy(oldPath, VARAPath); + + VARAEnable = Dev->VARAEnable->isChecked(); + + strcpy(VARATermCall, Dev->TermCall->text().toUtf8().toUpper()); + + Q = Dev->Port->text(); + + VARAPortNum = Q.toInt(); + strcpy(VARAHost, Dev->Host->text().toUtf8().toUpper()); + strcpy(VARAPath, Dev->Path->text().toUtf8()); + + VARA500 = Dev->VARA500->isChecked(); + VARA2300 = Dev->VARA2300->isChecked(); + VARA2750 = Dev->VARA2750->isChecked(); + + VARAHF = Dev->VARAHF->isChecked(); + VARAFM = Dev->VARAFM->isChecked(); + VARASAT = Dev->VARASAT->isChecked(); + + if (VARAHF) + { + strcpy(VARAHostHF, VARAHost); + strcpy(VARAPathHF, VARAPath); + VARAPortHF = VARAPortNum; + } + else if (VARAFM) + { + strcpy(VARAHostFM, VARAHost); + strcpy(VARAPathFM, VARAPath); + VARAPortFM = VARAPortNum; + } + else if (VARASAT) + { + strcpy(VARAHostSAT, VARAHost); + strcpy(VARAPathSAT, VARAPath); + VARAPortSAT = VARAPortNum; + } + + Q = Dev->PTTPort->currentText(); + strcpy(PTTPort, Q.toString().toUtf8()); + + if (Dev->CAT->isChecked()) + PTTMode = PTTCAT; + else + PTTMode = PTTRTS; + + if (Dev->CATHex->isChecked()) + CATHex = 1; + else + CATHex = 0; + + Q = Dev->PTTOn->text(); + strcpy(PTTOnString, Q.toString().toUtf8()); + Q = Dev->PTTOff->text(); + strcpy(PTTOffString, Q.toString().toUtf8()); + + Q = Dev->CATSpeed->text(); + PTTBAUD = Q.toInt(); + + Q = Dev->GPIOLeft->text(); + pttGPIOPin = Q.toInt(); + + Q = Dev->GPIORight->text(); + pttGPIOPinR = Q.toInt(); + + Q = Dev->VIDPID->text(); + + if (strcmp(PTTPort, "CM108") == 0) + strcpy(CM108Addr, Q.toString().toUtf8()); + else if (strcmp(PTTPort, "HAMLIB") == 0) + { + HamLibPort = Q.toInt(); + Q = Dev->PTTOn->text(); + strcpy(HamLibHost, Q.toString().toUtf8()); + } + + if (VARAEnable != OldEnable || VARAPortNum != OldPort || strcmp(oldHost, VARAHost) != 0) + { + // (re)start connection + + if (OldEnable && VARASock && VARASock->ConnectedState == QAbstractSocket::ConnectedState) + { + VARASock->disconnectFromHost(); + if (VARADataSock) + VARADataSock->disconnectFromHost(); + Status2->setText("VARA Disconnected"); + } + } + + if (process && process->state() == QProcess::Running) + if ((VARAEnable == 0 || strcmp(oldPath, VARAPath) != 0)) + process->close(); + + myStatusBar->setVisible(AGWEnable | VARAEnable | KISSEnable); + + ClosePTTPort(); + OpenPTTPort(); + + delete(Dev); + SaveSettings(); + deviceUI->accept(); + + QSize newSize(this->size()); + QSize oldSize(this->size()); + + QResizeEvent *myResizeEvent = new QResizeEvent(newSize, oldSize); + + QCoreApplication::postEvent(this, myResizeEvent); +} + +void QtTermTCP::devicereject() +{ + delete(Dev); + deviceUI->reject(); +} + +// This handles incoming connections + +void QtTermTCP::onNewConnection() +{ + myTcpSocket *clientSocket = (myTcpSocket *)_server->nextPendingConnection(); + + clientSocket->Sess = NULL; + + Ui_ListenSession * S; + Ui_ListenSession * Sess = NULL; + + char Title[512]; + int i = 0; + + QByteArray Host = clientSocket->peerAddress().toString().toUtf8(); + + if (TermMode == MDI) + { + // See if an old session can be reused + + for (int i = 0; i < _sessions.size(); ++i) + { + S = _sessions.at(i); + + // for (Ui_ListenSession * S: _sessions) + // { + if ((S->SessionType & Listen) && S->clientSocket == NULL) + { + Sess = S; + break; + } + } + + // Create a window if none found, else reuse old + + if (Sess == NULL) + { + Sess = newWindow(this, Listen); + } + + QByteArray Host = clientSocket->peerAddress().toString().toUtf8(); + } + else + { + // Single or Tabbed - look for free session + + for (i = 0; i < _sessions.size(); ++i) + { + S = _sessions.at(i); + + if (S->clientSocket == NULL && S->AGWSession == NULL && S->AGWMonSession == NULL && S->KISSSession == NULL) + { + Sess = S; + break; + } + } + + if (Sess == NULL) + { + // Clear connection + + clientSocket->disconnectFromHost(); + return; + } + } + + _sockets.push_back(clientSocket); + + clientSocket->Sess = Sess; + + // See if any data from host - first msg should be callsign + + clientSocket->waitForReadyRead(1000); + + QByteArray datas = clientSocket->readAll(); + + datas.chop(2); + datas.truncate(10); // Just in case! + + datas.append('\0'); + + sprintf(Title, "Inward Connect from %s:%d Call " + datas, + Host.data(), clientSocket->peerPort()); + + if (TermMode == MDI) + { + Sess->setWindowTitle(Title); + } + else if (TermMode == Tabbed) + { + tabWidget->setTabText(i, datas.data()); + } + else if (TermMode == Single) + this->setWindowTitle(Title); + + connect(clientSocket, SIGNAL(readyRead()), this, SLOT(readyRead())); + connect(clientSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onSocketStateChanged(QAbstractSocket::SocketState))); + + Sess->clientSocket = clientSocket; + + // We need to set Connect and Disconnect if the window is active + + if (TermMode == MDI && Sess->sw == ActiveSubWindow) + setMenus(true); + + if (TermMode == Tabbed && Sess->Tab == tabWidget->currentIndex()) + setMenus(true); + + if (TermMode == Single) + setMenus(true); // Single + + // Send CText if defined + + if (listenCText[0]) + Sess->clientSocket->write(listenCText); + + // Send Message to Terminal + + char Msg[80]; + + sprintf(Msg, "Listen Connect from %s\r\r", datas.data()); + + WritetoOutputWindow(Sess, (unsigned char *)Msg, (int)strlen(Msg)); + + if (ConnectBeep) + myBeep(); +} + +void QtTermTCP::onSocketStateChanged(QAbstractSocket::SocketState socketState) +{ + myTcpSocket* sender = static_cast(QObject::sender()); + Ui_ListenSession * Sess = (Ui_ListenSession *)sender->Sess; + + if (socketState == QAbstractSocket::UnconnectedState) + { + char Msg[] = "Disconnected\r"; + + WritetoOutputWindowEx(Sess, (unsigned char *)Msg, (int)strlen(Msg), + Sess->termWindow, &Sess->OutputSaveLen, Sess->OutputSave, WarningText); // Red + + if (TermMode == MDI) + { + if (Sess->SessionType == Mon) // Mon Only + Sess->setWindowTitle("Monitor Session Disconnected"); + else + Sess->setWindowTitle("Disconnected"); + } + else if (TermMode == Tabbed) + { + if (Sess->SessionType == Mon) // Mon Only + tabWidget->setTabText(Sess->Tab, "Monitor"); + else + { + char Label[16]; + sprintf(Label, "Sess %d", Sess->Tab + 1); + tabWidget->setTabText(Sess->Tab, Label); + } + } + else if (TermMode == Single) + { + if (Sess->AGWMonSession) + mythis->setWindowTitle("AGW Monitor Window"); + else + { + if (Sess->SessionType == Mon) // Mon Only + this->setWindowTitle("Monitor Session Disconnected"); + else + this->setWindowTitle("Disconnected"); + } + } + + Sess->PortMonString[0] = 0; + +// delete(Sess->clientSocket); + Sess->clientSocket = NULL; + + discAction->setEnabled(false); + + if ((Sess->SessionType & Listen)) + _sockets.removeOne(sender); + else + { + connectMenu->setEnabled(true); + YAPPSend->setEnabled(false); + } + } + else if (socketState == QAbstractSocket::ConnectedState) + { + char Signon[256]; + char Title[128]; + + // only seems to be triggered for outward connect + + sprintf(Signon, "%s\r%s\rBPQTERMTCP\r", UserName[Sess->CurrentHost], Password[Sess->CurrentHost]); + + Sess->clientSocket->write(Signon); + + discAction->setEnabled(true); + YAPPSend->setEnabled(true); + connectMenu->setEnabled(false); + + SendTraceOptions(Sess); + + Sess->InputMode = 0; + Sess->SlowTimer = 0; + Sess->MonData = 0; + Sess->OutputSaveLen = 0; // Clear any part line + Sess->MonSaveLen = 0; // Clear any part line + + if (Sess->SessionType == Mon) // Mon Only + sprintf(Title, "Monitor Session Connected to %s", Host[Sess->CurrentHost]); + else + sprintf(Title, "Connected to %s", Host[Sess->CurrentHost]); + + if (TermMode == MDI) + Sess->setWindowTitle(Title); + else if (TermMode == Tabbed) + tabWidget->setTabText(tabWidget->currentIndex(), Host[Sess->CurrentHost]); + else if (TermMode == Single) + this->setWindowTitle(Title); + } +} + +void QtTermTCP::updateWindowMenu() +{ + if (TermMode == MDI) + { + windowMenu->clear(); + windowMenu->addAction(newTermAct); + windowMenu->addAction(newMonAct); + windowMenu->addAction(newCombinedAct); + windowMenu->addSeparator(); + windowMenu->addAction(closeAct); + windowMenu->addAction(closeAllAct); + windowMenu->addSeparator(); + windowMenu->addAction(tileAct); + windowMenu->addAction(cascadeAct); + windowMenu->addSeparator(); + windowMenu->addAction(nextAct); + windowMenu->addAction(previousAct); + windowMenu->addAction(quitAction); + windowMenu->addAction(windowMenuSeparatorAct); + + QList windows = mdiArea->subWindowList(); + windowMenuSeparatorAct->setVisible(!windows.isEmpty()); + + Ui_ListenSession * Sess; + + for (int i = 0; i < _sessions.size(); ++i) + { + Sess = _sessions.at(i); + Sess->actActivate = windowMenu->addAction(Sess->sw->windowTitle()); + QAction::connect(Sess->actActivate, SIGNAL(triggered()), this, SLOT(actActivate())); + Sess->actActivate->setCheckable(true); + Sess->actActivate->setChecked(ActiveSubWindow == Sess->sw); + } + } + else if (TermMode == Tabbed) + { + windowMenu->clear(); + + Ui_ListenSession * Sess = (Ui_ListenSession *)tabWidget->currentWidget(); + + QActionGroup * termGroup = new QActionGroup(this); + + delete(TabSingle); + delete(TabBoth); + delete(TabMon); + + TabSingle = setupMenuLine(nullptr, (char *)"Terminal Only", this, (Sess->SessionType == Term)); + TabBoth = setupMenuLine(nullptr, (char *)"Terminal + Monitor", this, (Sess->SessionType == Term + Mon)); + TabMon = setupMenuLine(nullptr, (char *)"Monitor Only", this, (Sess->SessionType == Mon)); + + termGroup->addAction(TabSingle); + termGroup->addAction(TabBoth); + termGroup->addAction(TabMon); + + windowMenu->addAction(TabSingle); + windowMenu->addAction(TabBoth); + windowMenu->addAction(TabMon); + + } +} + +Ui_ListenSession::~Ui_ListenSession() +{ + if (this->clientSocket) + { + int loops = 100; + this->clientSocket->disconnectFromHost(); + while (loops-- && this->clientSocket->state() != QAbstractSocket::UnconnectedState) + QThread::msleep(10); + } +} + +extern "C" void setTraceOff(Ui_ListenSession * Sess) +{ + if ((Sess->SessionType & Mon) == 0) + return; // Not Monitor + + if (Sess->AGWMonSession) + return; + + char Buffer[80]; + int Len = sprintf(Buffer, "\\\\\\\\0 0 0 0 0 0 0 0\r"); + + SocketFlush(Sess); + SocketSend(Sess, Buffer, Len); + SocketFlush(Sess); +} + + +extern "C" void SendTraceOptions(Ui_ListenSession * Sess) +{ + if ((Sess->SessionType & Mon) == 0) + return; // Not Monitor + + if (Sess->AGWMonSession) + return; + + char Buffer[80]; + int Len = sprintf(Buffer, "\\\\\\\\%llx %x %x %x %x %x %x %x\r", Sess->portmask, Sess->mtxparam, Sess->mcomparam, + Sess->MonitorNODES, Sess->MonitorColour, Sess->monUI, 0, 1); + + strcpy(&MonParams[Sess->CurrentHost][0], &Buffer[4]); + SaveSettings(); + SocketFlush(Sess); + SocketSend(Sess, Buffer, Len); + SocketFlush(Sess); +} + +void QtTermTCP::doNewTerm() +{ + newWindow(this, Term); +} + +void QtTermTCP::doNewMon() +{ + newWindow(this, Mon); +} + +void QtTermTCP::doNewCombined() +{ + newWindow(this, Term + Mon); +} + +void QtTermTCP::doCascade() +{ + // Qt Cascade Minimizes windows so do it ourselves + + int x = 0, y = 0; + + Ui_ListenSession * Sess; + + for (int i = 0; i < _sessions.size(); ++i) + { + Sess = _sessions.at(i); + + Sess->sw->move(x, y); + x += 14; + y += 30; + } +} + +void QtTermTCP::actActivate() +{ + QAction * sender = static_cast(QObject::sender()); + + Ui_ListenSession * Sess; + + for (int i = 0; i < _sessions.size(); ++i) + { + Sess = _sessions.at(i); + + if (Sess->actActivate == sender) + { + mdiArea->setActiveSubWindow(Sess->sw); + return; + } + } +} + + +void QtTermTCP::xon_mdiArea_changed() +{ + // This is triggered when the Active MDI window changes + // and is used to enable/disable Connect, Disconnect and YAPP Send + + + QMdiSubWindow *SW = mdiArea->activeSubWindow(); + + // Dont waste time if not changed + + if (ActiveSubWindow == SW) + return; + + ActiveSubWindow = SW; + + Ui_ListenSession * Sess; + + for (int i = 0; i < _sessions.size(); ++i) + { + Sess = _sessions.at(i); + +// for (Ui_ListenSession * Sess : _sessions) +// { + if (Sess->sw == SW) + { + ActiveSession = Sess; + + if (Sess->clientSocket && Sess->clientSocket->state() == QAbstractSocket::ConnectedState) + { + discAction->setEnabled(true); + YAPPSend->setEnabled(true); + connectMenu->setEnabled(false); + } + else if (Sess->AGWMonSession) + { + // Connected AGW Monitor Session + + discAction->setEnabled(false); + YAPPSend->setEnabled(false); + connectMenu->setEnabled(false); + } + else if (Sess->AGWSession || Sess->KISSSession) + { + // Connected AGW or KISS Terminal Session + + discAction->setEnabled(true); + YAPPSend->setEnabled(true); + connectMenu->setEnabled(false); + } + else + { + // Not connected + + discAction->setEnabled(false); + YAPPSend->setEnabled(false); + + if ((Sess->SessionType & Listen)) // Listen Sessions can't connect + connectMenu->setEnabled(false); + else + connectMenu->setEnabled(true); + } + + // If a monitor Window, change Monitor config settings + + if (Sess->PortMonString[0]) + { + char * ptr = (char *)malloc(1024); + memcpy(ptr, Sess->PortMonString, 1024); + + int NumberofPorts = atoi((char *)&ptr[2]); + char *p, *Context; + char msg[80]; + int portnum; + char delim[] = "|"; + + // Remove old Monitor menu + + for (int i = 0; i < 32; i++) + { + SetPortMonLine(i, (char *)"", 0, 0); // Set all hidden + } + + p = strtok_s((char *)&ptr[2], delim, &Context); + + while (NumberofPorts--) + { + p = strtok_s(NULL, delim, &Context); + if (p == NULL) + break; + + portnum = atoi(p); + + sprintf(msg, "Port %s", p); + + if (portnum == 0) + portnum = 33; + + if (Sess->portmask & (1ll << (portnum - 1))) + SetPortMonLine(portnum, msg, 1, 1); + else + SetPortMonLine(portnum, msg, 1, 0); + } + free(ptr); + + MonTX->setChecked(Sess->mtxparam); + MonSup->setChecked(Sess->mcomparam); + MonUI->setChecked(Sess->monUI); + MonNodes->setChecked(Sess->MonitorNODES); + MonColour->setChecked(Sess->MonitorColour); + + } + + return; + } + } +} + +void QtTermTCP::doFonts() +{ + fontDialog * xx = new fontDialog(0); + xx->exec(); +} + +void QtTermTCP::doMFonts() +{ + fontDialog * xx = new fontDialog(1); + xx->exec(); +} + +QColor TempmonBackground = monBackground; +QColor TemptermBackground = termBackground; +QColor TempinputBackground = inputBackground; + +QColor TempmonRxText = monRxText; +QColor TempmonTxText = monTxText; +QColor TempmonOtherText = monOtherText; + +QColor TempoutputText = outputText; +QColor TempEchoText = EchoText; +QColor TempWarningText = WarningText; + + +QColor TempinputText = inputText; + + +void setDialogColours() +{ + char monStyle[128]; + + sprintf(monStyle, "color: rgb(%d, %d, %d); background-color: rgb(%d, %d, %d);", + TempmonOtherText.red(), TempmonOtherText.green(), TempmonOtherText.blue(), + TempmonBackground.red(), TempmonBackground.green(), TempmonBackground.blue()); + + COLOURS->MonitorBG->setStyleSheet(monStyle); + COLOURS->MonOther->setStyleSheet(monStyle); + + sprintf(monStyle, "color: rgb(%d, %d, %d); background-color: rgb(%d, %d, %d);", + TempmonTxText.red(), TempmonTxText.green(), TempmonTxText.blue(), + TempmonBackground.red(), TempmonBackground.green(), TempmonBackground.blue()); + COLOURS->MonTX->setStyleSheet(monStyle); + + sprintf(monStyle, "color: rgb(%d, %d, %d); background-color: rgb(%d, %d, %d);", + TempmonRxText.red(), TempmonRxText.green(), TempmonRxText.blue(), + TempmonBackground.red(), TempmonBackground.green(), TempmonBackground.blue()); + + COLOURS->MonRX->setStyleSheet(monStyle); + + sprintf(monStyle, "color: rgb(%d, %d, %d); background-color: rgb(%d, %d, %d);", + TempoutputText.red(), TempoutputText.green(), TempoutputText.blue(), + TemptermBackground.red(), TemptermBackground.green(), TemptermBackground.blue()); + + COLOURS->TermBG->setStyleSheet(monStyle); + COLOURS->TermNormal->setStyleSheet(monStyle); + + sprintf(monStyle, "color: rgb(%d, %d, %d); background-color: rgb(%d, %d, %d);", + TempEchoText.red(), TempEchoText.green(), TempEchoText.blue(), + TemptermBackground.red(), TemptermBackground.green(), TemptermBackground.blue()); + + COLOURS->Echoed->setStyleSheet(monStyle); + + sprintf(monStyle, "color: rgb(%d, %d, %d); background-color: rgb(%d, %d, %d);", + TempWarningText.red(), TempWarningText.green(), TempWarningText.blue(), + TemptermBackground.red(), TemptermBackground.green(), TemptermBackground.blue()); + + COLOURS->Warning->setStyleSheet(monStyle); + + sprintf(monStyle, "color: rgb(%d, %d, %d); background-color: rgb(%d, %d, %d);", + TempinputText.red(), TempinputText.green(), TempinputText.blue(), + TempinputBackground.red(), TempinputBackground.green(), TempinputBackground.blue()); + + COLOURS->InputBG->setStyleSheet(monStyle); + COLOURS->InputColour->setStyleSheet(monStyle); + +} + +void QtTermTCP::doColours() +{ + COLOURS = new(Ui_ColourDialog); + + QDialog UI; + + COLOURS->setupUi(&UI); + + UI.setFont(*menufont); + + deviceUI = &UI; + + TempmonBackground = monBackground; + TempmonRxText = monRxText; + TempmonTxText = monTxText; + TempmonOtherText = monOtherText; + + TemptermBackground = termBackground; + TempoutputText = outputText; + TempEchoText = EchoText; + TempWarningText = WarningText; + + TempinputBackground = inputBackground; + TempinputText = inputText; + + setDialogColours(); + + QObject::connect(COLOURS->MonitorBG, SIGNAL(clicked()), this, SLOT(ColourPressed())); + QObject::connect(COLOURS->TermBG, SIGNAL(clicked()), this, SLOT(ColourPressed())); + QObject::connect(COLOURS->InputBG, SIGNAL(clicked()), this, SLOT(ColourPressed())); + QObject::connect(COLOURS->MonRX, SIGNAL(clicked()), this, SLOT(ColourPressed())); + QObject::connect(COLOURS->MonTX, SIGNAL(clicked()), this, SLOT(ColourPressed())); + QObject::connect(COLOURS->MonOther, SIGNAL(clicked()), this, SLOT(ColourPressed())); + QObject::connect(COLOURS->TermNormal, SIGNAL(clicked()), this, SLOT(ColourPressed())); + QObject::connect(COLOURS->Echoed, SIGNAL(clicked()), this, SLOT(ColourPressed())); + QObject::connect(COLOURS->Warning, SIGNAL(clicked()), this, SLOT(ColourPressed())); + QObject::connect(COLOURS->InputColour, SIGNAL(clicked()), this, SLOT(ColourPressed())); + + QObject::connect(COLOURS->okButton, SIGNAL(clicked()), this, SLOT(Colouraccept())); + QObject::connect(COLOURS->cancelButton, SIGNAL(clicked()), this, SLOT(Colourreject())); + + UI.exec(); + +} + +void QtTermTCP::ColourPressed() +{ + char Name[32]; + + strcpy(Name, sender()->objectName().toUtf8()); + + if (strcmp(Name, "MonitorBG") == 0) + TempmonBackground = setColor(TempmonBackground); + + else if (strcmp(Name, "MonTX") == 0) + TempmonTxText = setColor(TempmonTxText); + + else if (strcmp(Name, "MonRX") == 0) + TempmonRxText = setColor(TempmonRxText); + + else if (strcmp(Name, "MonOther") == 0) + TempmonOtherText = setColor(TempmonOtherText); + + else if (strcmp(Name, "TermBG") == 0) + TemptermBackground = setColor(TemptermBackground); + + else if (strcmp(Name, "InputBG") == 0) + TempinputBackground = setColor(TempinputBackground); + + else if (strcmp(Name, "InputColour") == 0) + TempinputText = setColor(TempinputText); + + else if (strcmp(Name, "TermNormal") == 0) + TempoutputText = setColor(TempoutputText); + + else if (strcmp(Name, "Echoed") == 0) + TempEchoText = setColor(TempEchoText); + + else if (strcmp(Name, "Warning") == 0) + TempWarningText = setColor(TempWarningText); + + setDialogColours(); +} + + +void QtTermTCP::Colouraccept() +{ + monBackground = TempmonBackground; + monRxText = TempmonRxText; + monTxText = TempmonTxText; + monOtherText = TempmonOtherText; + + termBackground = TemptermBackground; + EchoText = TempEchoText; + WarningText = TempWarningText; + outputText = TempoutputText; + + inputBackground = TempinputBackground; + inputText = TempinputText; + + // Set background colour for new windows + + sprintf(monStyleSheet, "background-color: rgb(%d, %d, %d);", + monBackground.red(), monBackground.green(), monBackground.blue()); + + sprintf(termStyleSheet, "background-color: rgb(%d, %d, %d);", + termBackground.red(), termBackground.green(), termBackground.blue()); + + sprintf(inputStyleSheet, "color: rgb(%d, %d, %d); background-color: rgb(%d, %d, %d);", + inputText.red(), inputText.green(), inputText.blue(), + inputBackground.red(), inputBackground.green(), inputBackground.blue()); + + // Update existing windows + + for (int i = 0; i < _sessions.size(); ++i) + { + Ui_ListenSession * S = _sessions.at(i); + + if (S->monWindow) + S->monWindow->setStyleSheet(monStyleSheet); + + if (S->termWindow) + S->termWindow->setStyleSheet(termStyleSheet); + + if (S->inputWindow) + S->inputWindow->setStyleSheet(inputStyleSheet); + + } + + + delete(COLOURS); + + SaveSettings(); + deviceUI->accept(); +} + +void QtTermTCP::Colourreject() +{ + delete(COLOURS); + deviceUI->reject(); +} + + +void QtTermTCP::ConnecttoVARA() +{ + + delete(VARASock); + + VARASock = new myTcpSocket(); + + connect(VARASock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(VARAdisplayError(QAbstractSocket::SocketError))); + connect(VARASock, SIGNAL(readyRead()), this, SLOT(VARAreadyRead())); + connect(VARASock, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onVARASocketStateChanged(QAbstractSocket::SocketState))); + + VARASock->connectToHost(VARAHost, VARAPortNum); + + Status2->setText("VARA Control Connecting"); + + return; +} + + +void QtTermTCP::VARATimer() +{ + // Runs every 10 Seconds + + if (VARAConnected == 0 && VARAConnecting == 0) + { + if (process == nullptr || process->state() == QProcess::NotRunning) + { + if (VARAPath[0]) + { + process = new QProcess(this); + QString file = VARAPath; + process->start(file); + } + } + QThread::msleep(1000); + VARAConnecting = true; + ConnecttoVARA(); + } +} + + +void QtTermTCP::VARAdisplayError(QAbstractSocket::SocketError socketError) +{ + switch (socketError) + { + case QAbstractSocket::RemoteHostClosedError: + break; + + case QAbstractSocket::HostNotFoundError: + QMessageBox::information(this, tr("QtTermTCP"), + tr("VARA host was not found. Please check the " + "host name and portsettings->")); + + Status2->setText("VARA Connection Failed"); + + break; + + case QAbstractSocket::ConnectionRefusedError: + + Status2->setText("VARA Connection Refused"); + break; + + default: + + Status2->setText("VARA Connection Failed"); + } + + VARAConnecting = 0; + VARAConnected = 0; +} + +void QtTermTCP::VARAreadyRead() +{ + int Read; + char Buffer[4096]; + char * ptr; + char * Msg; + myTcpSocket* Socket = static_cast(QObject::sender()); + + // read the data from the socket + + Read = Socket->read((char *)Buffer, 4095); + + Buffer[Read] = 0; + + Msg = Buffer; + + ptr = strchr(Msg, 0x0d); + + while (ptr) + { + *ptr++ = 0; + + if (strcmp(Msg, "IAMALIVE") == 0) + { + } + else if (strcmp(Msg, "PTT ON") == 0) + { + RadioPTT(1); + } + else if (strcmp(Msg, "PTT OFF") == 0) + { + RadioPTT(0); + } + else if (strcmp(Msg, "PENDING") == 0) + { + } + else if (strcmp(Msg, "CANCELPENDING") == 0) + { + } + else if (strcmp(Msg, "OK") == 0) + { + } + else if (memcmp(Msg, "CONNECTED ", 10) == 0) + { + Ui_ListenSession * Sess = (Ui_ListenSession *)VARASock->Sess; + char Title[128] = ""; + char CallFrom[64] = ""; + char CallTo[64] = ""; + char Mode[64] = ""; + char Message[128]; + int n; + + + sscanf(&Msg[10], "%s %s %s", CallFrom, CallTo, Mode); + + if (Sess) + { + if (Mode[0]) + sprintf(Title, "Connected to %s %s Mode", CallTo, Mode); + else + sprintf(Title, "Connected to %s", CallTo); + + n = sprintf(Message, "%s\r\n", Title); + WritetoOutputWindow(Sess, (unsigned char *)Message, n); + + if (TermMode == MDI) + Sess->setWindowTitle(Title); + else if (TermMode == Tabbed) + tabWidget->setTabText(Sess->Tab, CallTo); + else if (TermMode == Single) + mythis->setWindowTitle(Title); + + setMenus(true); + } + else + { + // Incoming Call + + Ui_ListenSession * S; + int i = 0; + + if (TermMode == MDI) + { + // See if an old session can be reused + + for (int i = 0; i < _sessions.size(); ++i) + { + S = _sessions.at(i); + + // for (Ui_ListenSession * S: _sessions) + // { + if ((S->SessionType & Listen) && S->clientSocket == NULL) + { + Sess = S; + break; + } + } + + // Create a window if none found, else reuse old + + if (Sess == NULL) + { + Sess = newWindow(this, Listen); + } + } + else + { + // Single or Tabbed - look for free session + + for (i = 0; i < _sessions.size(); ++i) + { + S = _sessions.at(i); + + if (S->clientSocket == NULL && S->AGWSession == NULL && S->AGWMonSession == NULL && S->KISSSession == NULL) + { + Sess = S; + break; + } + } + } + + if (Sess == NULL) + { + // Clear connection + + VARASock->write("DISCONNECT\r"); + } + else + { + if (Mode[0]) + { + sprintf(Title, "Connected to %s Mode %s", CallFrom, Mode); + n = sprintf(Message, "Incoming Connected from %s %s Mode\r\n", CallFrom, Mode); + } + else + { + sprintf(Title, "Connected to %s", CallFrom); + n = sprintf(Message, "Incoming Connected from %s\r\n", CallFrom); + } + + WritetoOutputWindow(Sess, (unsigned char *)Message, n); + + VARASock->Sess = Sess; + VARADataSock->Sess = Sess; + + if (TermMode == MDI) + Sess->setWindowTitle(Title); + else if (TermMode == Tabbed) + tabWidget->setTabText(Sess->Tab, CallFrom); + else if (TermMode == Single) + mythis->setWindowTitle(Title); + + setMenus(true); + + } + } + } + else if (strcmp(Msg, "DISCONNECTED") == 0) + { + Ui_ListenSession * Sess = (Ui_ListenSession *)VARASock->Sess; + + if (Sess) + { + WritetoOutputWindow(Sess, (unsigned char *)"Disconnected\r\n", 14); + VARASock->Sess = 0; + VARADataSock->Sess = 0; + + if (TermMode == MDI) + { + if (Sess->SessionType == Mon) // Mon Only + Sess->setWindowTitle("Monitor Session Disconnected"); + else + Sess->setWindowTitle("Disconnected"); + } + else if (TermMode == Tabbed) + { + if (Sess->SessionType == Mon) // Mon Only + tabWidget->setTabText(Sess->Tab, "Monitor"); + else + { + char Label[16]; + sprintf(Label, "Sess %d", Sess->Tab + 1); + tabWidget->setTabText(Sess->Tab, Label); + } + } + else if (TermMode == Single) + { + if (Sess->AGWMonSession) + mythis->setWindowTitle("AGW Monitor Window"); + else + { + if (Sess->SessionType == Mon) // Mon Only + this->setWindowTitle("Monitor Session Disconnected"); + else + this->setWindowTitle("Disconnected"); + } + } + + setMenus(false); + } + } + + Msg = ptr; + + ptr = strchr(Msg, 0x0d); + } + + + +} + +void QtTermTCP::onVARASocketStateChanged(QAbstractSocket::SocketState socketState) +{ +// myTcpSocket* sender = static_cast(QObject::sender()); + + if (socketState == QAbstractSocket::UnconnectedState) + { + // Close any connections + + Status2->setText("VARA Disconnected"); + actHost[17]->setVisible(0); + + VARAConnecting = VARAConnected = 0; + } + else if (socketState == QAbstractSocket::ConnectedState) + { + // Connect Data Session. Leave Connecting till that completes + + VARADataSock = new myTcpSocket(); + + connect(VARADataSock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(VARADatadisplayError(QAbstractSocket::SocketError))); + connect(VARADataSock, SIGNAL(readyRead()), this, SLOT(VARADatareadyRead())); + connect(VARADataSock, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onVARADataSocketStateChanged(QAbstractSocket::SocketState))); + + VARADataSock->connectToHost(VARAHost, VARAPortNum + 1); + Status2->setText("VARA Data Connecting"); + } +} + + +void QtTermTCP::VARADatadisplayError(QAbstractSocket::SocketError socketError) +{ + switch (socketError) + { + case QAbstractSocket::RemoteHostClosedError: + break; + + case QAbstractSocket::HostNotFoundError: + QMessageBox::information(this, tr("QtTermTCP"), + tr("VARA host was not found. Please check the " + "host name and portsettings->")); + + Status2->setText("VARA Connection Failed"); + + break; + + case QAbstractSocket::ConnectionRefusedError: + + Status2->setText("VARA Connection Refused"); + break; + + default: + + Status2->setText("VARA Connection Failed"); + } + + VARAConnecting = 0; + VARAConnected = 0; +} + +void QtTermTCP::VARADatareadyRead() +{ + int Read; + unsigned char Buffer[4096]; + myTcpSocket* Socket = static_cast(QObject::sender()); + + Ui_ListenSession * Sess = (Ui_ListenSession *)Socket->Sess; + + // read the data from the socket + + Read = Socket->read((char *)Buffer, 2047); + + while (Read > 0) + { + // if (InputMode == 'Y') // Yapp + // { + // QString myString = QString::fromUtf8((char*)Buffer, Read); + // QByteArray ptr = myString.toLocal8Bit(); + // memcpy(Buffer, ptr.data(), ptr.length()); + // Read = ptr.length(); + // } + + ProcessReceivedData(Sess, Buffer, Read); + + QString myString = QString::fromUtf8((char*)Buffer); + // qDebug() << myString; + Read = Socket->read((char *)Buffer, 2047); + } +} + + +void QtTermTCP::onVARADataSocketStateChanged(QAbstractSocket::SocketState socketState) +{ +// myTcpSocket* sender = static_cast(QObject::sender()); + + if (socketState == QAbstractSocket::UnconnectedState) + { + // Close any connections + + Status2->setText("VARA Disconnected"); + actHost[17]->setVisible(0); + + VARAConnecting = VARAConnected = 0; + } + else if (socketState == QAbstractSocket::ConnectedState) + { + char MyCall[32]; + + VARAConnected = 1; + VARAConnecting = 0; + + Status2->setText("VARA Connected"); + + actHost[17]->setVisible(1); // Enable VARA Connect Line + + sprintf(MyCall, "MYCALL %s\r", VARATermCall); + VARASock->write(MyCall); + + if (VARA500) + VARASock->write("BW500\r"); + else if (VARA2300) + VARASock->write("BW2300\r"); + else if (VARA2750) + VARASock->write("BW2750\r"); + + VARASock->write("COMPRESSION FILES\r"); + + if (listenEnable) + VARASock->write("LISTEN ON\r"); + } +} + +// PTT Stuff + +#include "hidapi.h" + +// Serial Port Stuff + + +QTcpSocket * HAMLIBsock; +int HAMLIBConnected = 0; +int HAMLIBConnecting = 0; + +void QtTermTCP::HAMLIBdisplayError(QAbstractSocket::SocketError socketError) +{ + switch (socketError) + { + case QAbstractSocket::RemoteHostClosedError: + break; + + case QAbstractSocket::HostNotFoundError: + QMessageBox::information(nullptr, tr("QtSM"), + "HAMLIB host was not found. Please check the " + "host name and portsettings->"); + + break; + + case QAbstractSocket::ConnectionRefusedError: + + qDebug() << "HAMLIB Connection Refused"; + break; + + default: + + qDebug() << "HAMLIB Connection Failed"; + break; + + } + + HAMLIBConnecting = 0; + HAMLIBConnected = 0; +} + +void QtTermTCP::HAMLIBreadyRead() +{ + unsigned char Buffer[4096]; + QTcpSocket* Socket = static_cast(QObject::sender()); + + // read the data from the socket. Don't do anyhing with it at the moment + + Socket->read((char *)Buffer, 4095); +} + +void QtTermTCP::onHAMLIBSocketStateChanged(QAbstractSocket::SocketState socketState) +{ + if (socketState == QAbstractSocket::UnconnectedState) + { + // Close any connections + + HAMLIBConnected = 0; + HAMLIBConnecting = 0; + + // delete (HAMLIBsock); + // HAMLIBsock = 0; + + qDebug() << "HAMLIB Connection Closed"; + + } + else if (socketState == QAbstractSocket::ConnectedState) + { + HAMLIBConnected = 1; + HAMLIBConnecting = 0; + qDebug() << "HAMLIB Connected"; + } +} + + +void QtTermTCP::ConnecttoHAMLIB() +{ + delete(HAMLIBsock); + + HAMLIBConnected = 0; + HAMLIBConnecting = 1; + + HAMLIBsock = new QTcpSocket(); + + connect(HAMLIBsock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(HAMLIBdisplayError(QAbstractSocket::SocketError))); + connect(HAMLIBsock, SIGNAL(readyRead()), this, SLOT(HAMLIBreadyRead())); + connect(HAMLIBsock, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onHAMLIBSocketStateChanged(QAbstractSocket::SocketState))); + + HAMLIBsock->connectToHost(HamLibHost, HamLibPort); + + return; +} + +void QtTermTCP::HAMLIBSetPTT(int PTTState) +{ + char Msg[16]; + + if (HAMLIBsock == nullptr || HAMLIBsock->state() != QAbstractSocket::ConnectedState) + ConnecttoHAMLIB(); + + if (HAMLIBsock == nullptr || HAMLIBsock->state() != QAbstractSocket::ConnectedState) + return; + + sprintf(Msg, "T %d\r\n", PTTState); + HAMLIBsock->write(Msg); + + HAMLIBsock->waitForBytesWritten(3000); + + QByteArray datas = HAMLIBsock->readAll(); + + qDebug(datas.data()); +} + +QTcpSocket * FLRigsock; +int FLRigConnected = 0; +int FLRigConnecting = 0; + +void QtTermTCP::FLRigdisplayError(QAbstractSocket::SocketError socketError) +{ + switch (socketError) + { + case QAbstractSocket::RemoteHostClosedError: + break; + + case QAbstractSocket::HostNotFoundError: + QMessageBox::information(nullptr, tr("QtSM"), + "FLRig host was not found. Please check the " + "host name and portsettings->"); + + break; + + case QAbstractSocket::ConnectionRefusedError: + + qDebug() << "FLRig Connection Refused"; + break; + + default: + + qDebug() << "FLRig Connection Failed"; + break; + + } + + FLRigConnecting = 0; + FLRigConnected = 0; +} + +void QtTermTCP::FLRigreadyRead() +{ + unsigned char Buffer[4096]; + QTcpSocket* Socket = static_cast(QObject::sender()); + + // read the data from the socket. Don't do anyhing with it at the moment + + Socket->read((char *)Buffer, 4095); +} + +void QtTermTCP::onFLRigSocketStateChanged(QAbstractSocket::SocketState socketState) +{ + if (socketState == QAbstractSocket::UnconnectedState) + { + // Close any connections + + FLRigConnected = 0; + FLRigConnecting = 0; + + // delete (FLRigsock); + // FLRigsock = 0; + + qDebug() << "FLRig Connection Closed"; + + } + else if (socketState == QAbstractSocket::ConnectedState) + { + FLRigConnected = 1; + FLRigConnecting = 0; + qDebug() << "FLRig Connected"; + } +} + + +void QtTermTCP::ConnecttoFLRig() +{ + delete(FLRigsock); + + FLRigConnected = 0; + FLRigConnecting = 1; + + FLRigsock = new QTcpSocket(); + + connect(FLRigsock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(FLRigdisplayError(QAbstractSocket::SocketError))); + connect(FLRigsock, SIGNAL(readyRead()), this, SLOT(FLRigreadyRead())); + connect(FLRigsock, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onFLRigSocketStateChanged(QAbstractSocket::SocketState))); + + FLRigsock->connectToHost(FLRigHost, FLRigPort); + + return; +} + +static char MsgHddr[] = "POST /RPC2 HTTP/1.1\r\n" +"User-Agent: XMLRPC++ 0.8\r\n" +"Host: 127.0.0.1:7362\r\n" +"Content-Type: text/xml\r\n" +"Content-length: %d\r\n" +"\r\n%s"; + +static char Req[] = "\r\n" + "%s\r\n" + "%s" + "\r\n"; + +void QtTermTCP::FLRigSetPTT(int PTTState) +{ + int Len; + char ReqBuf[512]; + char SendBuff[512]; + char ValueString[256] = ""; + + sprintf(ValueString, "%d", PTTState); + + Len = sprintf(ReqBuf, Req, "rig.set_ptt", ValueString); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + + if (FLRigsock == nullptr || FLRigsock->state() != QAbstractSocket::ConnectedState) + ConnecttoFLRig(); + + if (FLRigsock == nullptr || FLRigsock->state() != QAbstractSocket::ConnectedState) + return; + + FLRigsock->write(SendBuff); + + FLRigsock->waitForBytesWritten(3000); + + QByteArray datas = FLRigsock->readAll(); + + qDebug(datas.data()); +} + + + +void QtTermTCP::CATChanged(bool State) +{ + UNUSED(State); + PTTPortChanged(0); +} + +void QtTermTCP::VARAHFChanged(bool State) +{ + Dev->HFMode->setVisible(State); + + if (State) + { + Dev->TNCInfo->setTitle("VARA HF Paramters"); + Dev->Host->setText(VARAHostHF); + Dev->Port->setText(QString::number(VARAPortHF)); + Dev->Path->setText(VARAPathHF); + } +} + +void QtTermTCP::VARAFMChanged(bool State) +{ + if (State) + { + Dev->TNCInfo->setTitle("VARA FM Paramters"); + Dev->Host->setText(VARAHostFM); + Dev->Port->setText(QString::number(VARAPortFM)); + Dev->Path->setText(VARAPathFM); + } +} + +void QtTermTCP::VARASATChanged(bool State) +{ + if (State) + { + Dev->TNCInfo->setTitle("VARA SAT Paramters"); + Dev->Host->setText(VARAHostSAT); + Dev->Port->setText(QString::number(VARAPortSAT)); + Dev->Path->setText(VARAPathSAT); + } +} + +void QtTermTCP::SetVARAParams() +{ + Dev->Host->setText(VARAHost); + Dev->Port->setText(QString::number(VARAPortNum)); + Dev->Path->setText(VARAPath); +} + +void QtTermTCP::PTTPortChanged(int Selected) +{ + UNUSED(Selected); + + QVariant Q = Dev->PTTPort->currentText(); + strcpy(NewPTTPort, Q.toString().toUtf8()); + + Dev->RTSDTR->setVisible(false); + Dev->CAT->setVisible(false); + + Dev->PTTOnLab->setVisible(false); + Dev->PTTOn->setVisible(false); + Dev->PTTOff->setVisible(false); + Dev->PTTOffLab->setVisible(false); + Dev->CATLabel->setVisible(false); + Dev->CATSpeed->setVisible(false); + Dev->CATHex->setVisible(false); + Dev->CATText->setVisible(false); + + Dev->GPIOLab->setVisible(false); + Dev->GPIOLeft->setVisible(false); + Dev->GPIORight->setVisible(false); + Dev->GPIOLab2->setVisible(false); + + Dev->CM108Label->setVisible(false); + Dev->VIDPID->setVisible(false); + + if (strcmp(NewPTTPort, "None") == 0) + { + } + else if (strcmp(NewPTTPort, "GPIO") == 0) + { + Dev->GPIOLab->setVisible(true); + Dev->GPIOLeft->setVisible(true); + } + + else if (strcmp(NewPTTPort, "CM108") == 0) + { + Dev->CM108Label->setVisible(true); + #ifdef WIN32 + Dev->CM108Label->setText("CM108 VID/PID"); + #else + Dev->CM108Label->setText("CM108 Device"); + #endif + Dev->VIDPID->setText(CM108Addr); + Dev->VIDPID->setVisible(true); + } + else if (strcmp(NewPTTPort, "HAMLIB") == 0) + { + Dev->CM108Label->setVisible(true); + Dev->CM108Label->setText("rigctrld Port"); + Dev->VIDPID->setText(QString::number(HamLibPort)); + Dev->VIDPID->setVisible(true); + Dev->PTTOnLab->setText("rigctrld Host"); + Dev->PTTOnLab->setVisible(true); + Dev->PTTOn->setText(HamLibHost); + Dev->PTTOn->setVisible(true); + } + else if (strcmp(NewPTTPort, "FLRIG") == 0) + { + Dev->CM108Label->setVisible(true); + Dev->CM108Label->setText("FLRig Port"); + Dev->VIDPID->setText(QString::number(FLRigPort)); + Dev->VIDPID->setVisible(true); + Dev->PTTOnLab->setText("FLRig Host"); + Dev->PTTOnLab->setVisible(true); + Dev->PTTOn->setText(FLRigHost); + Dev->PTTOn->setVisible(true); + } + + else + { + Dev->RTSDTR->setVisible(true); + Dev->CAT->setVisible(true); + + if (Dev->CAT->isChecked()) + { + Dev->CATHex->setVisible(true); + Dev->CATText->setVisible(true); + Dev->PTTOnLab->setVisible(true); + Dev->PTTOnLab->setText("PTT On String"); + Dev->PTTOn->setText(PTTOnString); + Dev->PTTOn->setVisible(true); + Dev->PTTOff->setVisible(true); + Dev->PTTOffLab->setVisible(true); + Dev->PTTOff->setVisible(true); + Dev->PTTOff->setText(PTTOffString); + Dev->CATLabel->setVisible(true); + Dev->CATSpeed->setVisible(true); + Dev->CATSpeed->setText(QString::number(PTTBAUD)); + } + } +} + + + +void DecodeCM108(char * ptr) +{ + // Called if Device Name or PTT = Param is CM108 + +#ifdef WIN32 + + // Next Param is VID and PID - 0xd8c:0x8 or Full device name + // On Windows device name is very long and difficult to find, so + // easier to use VID/PID, but allow device in case more than one needed + + char * next; + long VID = 0, PID = 0; + char product[256] = "Unknown"; + + struct hid_device_info *devs, *cur_dev; + const char *path_to_open = NULL; + hid_device *handle = NULL; + + if (strlen(ptr) > 16) + CM108Device = _strdup(ptr); + else + { + VID = strtol(ptr, &next, 0); + if (next) + PID = strtol(++next, &next, 0); + + // Look for Device + + devs = hid_enumerate((unsigned short)VID, (unsigned short)PID); + cur_dev = devs; + + while (cur_dev) + { + if (cur_dev->product_string) + wcstombs(product, cur_dev->product_string, 255); + + printf("HID Device %s VID %X PID %X", product, cur_dev->vendor_id, cur_dev->product_id); + if (cur_dev->vendor_id == VID && cur_dev->product_id == PID) + { + path_to_open = cur_dev->path; + break; + } + cur_dev = cur_dev->next; + } + + if (path_to_open) + { + handle = hid_open_path(path_to_open); + + if (handle) + { + hid_close(handle); + CM108Device = _strdup(path_to_open); + } + else + { + printf("Unable to open CM108 device %x %x", VID, PID); + } + } + else + printf("Couldn't find CM108 device %x %x", VID, PID); + + hid_free_enumeration(devs); + } +#else + + // Linux - Next Param HID Device, eg /dev/hidraw0 + + CM108Device = strdup(ptr); +#endif +} + + +void QtTermTCP::OpenPTTPort() +{ + PTTMode &= ~PTTCM108; + PTTMode &= ~PTTHAMLIB; + PTTMode &= ~PTTFLRIG; + + if (PTTPort[0] && strcmp(PTTPort, "None") != 0) + { + if (PTTMode == PTTCAT) + { + // convert config strings from Hex + + if (CATHex == 0) // Ascii Strings + { + strcpy((char *)PTTOffCmd, PTTOffString); + PTTOffCmdLen = strlen(PTTOffString); + + strcpy((char *)PTTOnCmd, PTTOnString); + PTTOnCmdLen = strlen(PTTOnString); + } + else + { + char * ptr1 = PTTOffString; + unsigned char * ptr2 = PTTOffCmd; + char c; + int val; + + while ((c = *(ptr1++))) + { + val = c - 0x30; + if (val > 15) val -= 7; + val <<= 4; + c = *(ptr1++) - 0x30; + if (c > 15) c -= 7; + val |= c; + *(ptr2++) = val; + } + + PTTOffCmdLen = ptr2 - PTTOffCmd; + + ptr1 = PTTOnString; + ptr2 = PTTOnCmd; + + while ((c = *(ptr1++))) + { + val = c - 0x30; + if (val > 15) val -= 7; + val <<= 4; + c = *(ptr1++) - 0x30; + if (c > 15) c -= 7; + val |= c; + *(ptr2++) = val; + } + + PTTOnCmdLen = ptr2 - PTTOnCmd; + } + } + + if (strcmp(PTTPort, "GPIO") == 0) + { + // Initialise GPIO for PTT if available + +#ifdef __ARM_ARCH + +// if (gpioInitialise() == 0) +// { +// printf("GPIO interface for PTT available\n"); +// gotGPIO = TRUE; + +// SetupGPIOPTT(); +// } +// else +// printf("Couldn't initialise GPIO interface for PTT\n"); +// +#else + printf("GPIO interface for PTT not available on this platform\n"); +#endif + + } + else if (strcmp(PTTPort, "CM108") == 0) + { + DecodeCM108(CM108Addr); + PTTMode |= PTTCM108; + } + + else if (strcmp(PTTPort, "HAMLIB") == 0) + { + PTTMode |= PTTHAMLIB; + HAMLIBSetPTT(0); // to open port + return; + } + + else if (strcmp(PTTPort, "FLRIG") == 0) + { + PTTMode |= PTTFLRIG; + FLRigSetPTT(0); // to open port + return; + } + + else // Serial Port + { +#ifdef USESERIAL + hPTTDevice = new QSerialPort(this); + hPTTDevice->setPortName(PTTPort); + hPTTDevice->setBaudRate(PTTBAUD ); + hPTTDevice->setDataBits(QSerialPort::Data8); + hPTTDevice->setParity(QSerialPort::NoParity); + hPTTDevice->setStopBits(QSerialPort::OneStop); + hPTTDevice->setFlowControl(QSerialPort::NoFlowControl); + if (hPTTDevice->open(QIODevice::ReadWrite)) + { + qDebug() << "PTT Port Opened"; + } + else + { + QMessageBox msgBox; + msgBox.setText("PTT COM Port Open Failed."); + msgBox.exec(); + qDebug() << "PTT Port Open failed"; + delete(hPTTDevice); + hPTTDevice = 0; + } +#endif + } + } +} + +void ClosePTTPort() +{ +#ifdef USESERIAL + if (hPTTDevice) + hPTTDevice->close(); + hPTTDevice = 0; +#endif +} + + +void CM108_set_ptt(int PTTState) +{ + unsigned char io[5]; + int n; + + io[0] = 0; + io[1] = 0; + io[2] = 1 << (3 - 1); + io[3] = PTTState << (3 - 1); + io[4] = 0; + + if (CM108Device == NULL) + return; + +#ifdef WIN32 + hid_device *handle; + + handle = hid_open_path(CM108Device); + + if (!handle) { + printf("unable to open device\n"); + return; + } + + n = hid_write(handle, io, 5); + if (n < 0) + { + printf("Unable to write()\n"); + printf("Error: %ls\n", hid_error(handle)); + } + + hid_close(handle); + +#else + + int fd; + + fd = open(CM108Device, O_WRONLY); + + if (fd == -1) + { + printf("Could not open %s for write, errno=%d\n", CM108Device, errno); + return; + } + + io[0] = 0; + io[1] = 0; + io[2] = 1 << (3 - 1); + io[3] = PTTState << (3 - 1); + io[4] = 0; + + n = write(fd, io, 5); + if (n != 5) + { + printf("Write to %s failed, n=%d, errno=%d\n", CM108Device, n, errno); + } + + close(fd); +#endif + return; + +} + + + +void QtTermTCP::RadioPTT(bool PTTState) +{ +#ifdef __ARM_ARCH + if (useGPIO) + { +// gpioWrite(pttGPIOPin, (pttGPIOInvert ? (1 - PTTState) : (PTTState))); +// return; + } + +#endif + + if ((PTTMode & PTTCM108)) + { + CM108_set_ptt(PTTState); + return; + } + + if ((PTTMode & PTTHAMLIB)) + { + HAMLIBSetPTT(PTTState); + return; + } + + if ((PTTMode & PTTFLRIG)) + { + FLRigSetPTT(PTTState); + return; + } + +#ifdef USESERIAL + + if (hPTTDevice == 0) + return; + + if ((PTTMode & PTTCAT)) + { + if (PTTState) + hPTTDevice->write((char *)PTTOnCmd, PTTOnCmdLen); + else + hPTTDevice->write((char *)PTTOffCmd, PTTOffCmdLen); + + hPTTDevice->flush(); +// hPTTDevice->error(); + return; + + } + + + if ((PTTMode & PTTRTS)) + { + int n = hPTTDevice->setRequestToSend(PTTState); + n = n; + } + +#endif + +} + + + +extern "C" void WriteDebugLog(char * Mess) +{ + qDebug() << Mess; +} + +void QtTermTCP::ConnecttoKISS() +{ + if (strcmp(SerialPort, "TCP") == 0) + { + delete(KISSSock); + + KISSSock = new myTcpSocket(); + KISSSockCopy[0] = (void *)KISSSock; + + + connect(KISSSock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(KISSdisplayError(QAbstractSocket::SocketError))); + connect(KISSSock, SIGNAL(readyRead()), this, SLOT(KISSreadyRead())); + connect(KISSSock, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onKISSSocketStateChanged(QAbstractSocket::SocketState))); + + KISSSock->connectToHost(KISSHost, KISSPortNum); + + Status3->setText("KISS Connecting"); + } + else + openSerialPort(); + + return; +} + + +void QtTermTCP::KISSTimer() +{ + // Runs every 10 Seconds + + if (KISSConnected == 0 && KISSConnecting == 0) + { + ConnecttoKISS(); + } + else + { + // Verify Serial port is still ok + + if (m_serial && KISSConnected) + { + m_serial->clearError(); + m_serial->isDataTerminalReady(); + + if (m_serial->error()) + { + Debugprintf("Serial Port Lost - isOpen %d Error %d", m_serial->isOpen(), m_serial->error()); + closeSerialPort(); + } + } + } +} + + + +void QtTermTCP::KISSdisplayError(QAbstractSocket::SocketError socketError) +{ + switch (socketError) + { + case QAbstractSocket::RemoteHostClosedError: + break; + + case QAbstractSocket::HostNotFoundError: + QMessageBox::information(this, tr("QtTermTCP"), + tr("KISS host was not found. Please check the " + "host name and portsettings->")); + + Status3->setText("KISS Connection Failed"); + + break; + + case QAbstractSocket::ConnectionRefusedError: + + Status3->setText("KISS Connection Refused"); + break; + + default: + + Status3->setText("KISS Connection Failed"); + } + + KISSConnecting = 0; + KISSConnected = 0; +} + + +extern "C" void KISSSendtoServer(myTcpSocket* Socket, char * Data, int Length) +{ + if (m_serial) + { + if (m_serial->isOpen()) + { + m_serial->clearError(); + + int n = m_serial->write(Data, Length); + + n = m_serial->flush(); + + if (m_serial->error()) + { + Debugprintf("Serial Flush Error - Requested = %d Actual %d Error %d", Length, n, m_serial->error()); + closeSerialPort(); + } + } + } + else if (Socket) + Socket->write(Data, Length); +} + + + +void QtTermTCP::KISSreadyRead() +{ + int Read; + unsigned char Buffer[4096]; myTcpSocket* Socket = static_cast(QObject::sender()); + + // read the data from the socket + + Read = Socket->read((char *)Buffer, 4095); + + KISSDataReceived(Socket, Buffer, Read); + +} + +extern "C" void KISS_del_socket(void * socket); +extern "C" void KISS_add_stream(void * Socket); + +void QtTermTCP::onKISSSocketStateChanged(QAbstractSocket::SocketState socketState) +{ + // myTcpSocket* sender = static_cast(QObject::sender()); + + QTcpSocket* sender = static_cast(QObject::sender()); + + if (socketState == QAbstractSocket::UnconnectedState) + { + // Close any connections + + Ui_ListenSession * Sess = NULL; + + Status3->setText("KISS Disconnected"); + actHost[18]->setEnabled(0); + + KISSConnecting = KISSConnected = 0; + + // Free the monitor Window + + if (KISSMonSess) + { + Sess = KISSMonSess; + + if (TermMode == MDI) + Sess->setWindowTitle("Monitor Session Disconnected"); + + else if (TermMode == Tabbed) + tabWidget->setTabText(Sess->Tab, "Monitor"); + + KISSMonSess = nullptr; + } + + KISS_del_socket(sender); + KISSSock = NULL; + } + else if (socketState == QAbstractSocket::ConnectedState) + { + int i; + + KISSConnected = 1; + KISSConnecting = 0; + + Status3->setText("KISS Connected"); + actHost[18]->setEnabled(1); // Enable KISS Connect Line + + KISS_add_stream(sender); + + // Attach a Monitor Window if available + + Ui_ListenSession * Sess = NULL; + Ui_ListenSession * S; + + if (TermMode == MDI) + { + // See if an old session can be reused + + for (int i = 0; i < _sessions.size(); ++i) + { + S = _sessions.at(i); + + // for (Ui_ListenSession * S: _sessions) + // { + if ((S->SessionType == Mon) && S->clientSocket == NULL && S->KISSSession == NULL) + { + Sess = S; + break; + } + } + + // Create a window if none found, else reuse old + + if (Sess == NULL) + { + Sess = newWindow((QObject *)mythis, Mon, ""); + } + } + else if (TermMode == Tabbed) + { + // Tabbed - look for free session + + for (i = 8; i; i--) + { + S = _sessions.at(i); + + if (S->clientSocket == NULL && S->KISSSession == NULL) + { + Sess = S; + break; + } + } + } + else if (TermMode == Single && (singlemodeFormat & Mon)) + { + S = _sessions.at(0); + + if (S->clientSocket == NULL && S->KISSSession == NULL) + Sess = S; + + } + + if (Sess) + { + KISSMonSess = Sess; // Flag as in use + + if (TermMode == MDI) + Sess->setWindowTitle("KISS Monitor Window"); + else if (TermMode == Tabbed) + tabWidget->setTabText(Sess->Tab, "KISS Mon"); + else if (TermMode == Single) + mythis->setWindowTitle("KISS Monitor Window"); + +// if (TermMode == Single) +// { +// discAction->setEnabled(false); +// YAPPSend->setEnabled(false); +// connectMenu->setEnabled(false); +// } + } + } +} + + +extern "C" char * frame_monitor(string * frame, char * code, bool tx_stat); +extern "C" char * ShortDateTime(); + +extern "C" void monitor_frame(int snd_ch, string * frame, char * code, int tx, int excluded) +{ + UNUSED(excluded); + UNUSED(snd_ch); + + int Len; + char Msg[1024]; + + if (tx) + sprintf(Msg, "\x1b\x10%s", frame_monitor(frame, code, tx)); + else + sprintf(Msg, "\x1b\x11%s", frame_monitor(frame, code, tx)); + + Len = strlen(Msg); + + if (Msg[Len - 1] != '\r') + { + Msg[Len++] = '\r'; + Msg[Len] = 0; + } + + if (KISSMonSess) + WritetoMonWindow(KISSMonSess, (unsigned char *)Msg, Len); + +} + +extern "C" Ui_ListenSession * ax25IncommingConnect(TAX25Port * AX25Sess) +{ + // Look for/create Terminal Window for connection + + Ui_ListenSession * Sess = NULL; + Ui_ListenSession * S; + char Title[80]; + int i = 0; + + if (TermMode == MDI) + { + // See if an old session can be reused + + for (int i = 0; i < _sessions.size(); ++i) + { + S = _sessions.at(i); + + if ((S->SessionType & Listen) && S->clientSocket == NULL && S->AGWSession == NULL && S->KISSSession == NULL) + { + Sess = S; + break; + } + } + + // Create a window if none found, else reuse old + + if (Sess == NULL) + { + Sess = newWindow((QObject *)mythis, Listen, ""); + } + } + else + { + // Single or Tabbed - look for free session + + + for (i = 0; i < _sessions.size(); ++i) + { + S = _sessions.at(i); + + if (S->clientSocket == NULL && S->AGWSession == NULL && S->KISSSession == NULL) + { + Sess = S; + break; + } + } + + if (Sess == NULL) + { + // Clear connection + + return NULL; + } + } + + if (Sess) + { + sprintf(Title, "Connected to %s", AX25Sess->corrcall); + + if (TermMode == MDI) + { + Sess->setWindowTitle(Title); + } + else if (TermMode == Tabbed) + { + tabWidget->setTabText(i, AX25Sess->corrcall); + } + else if (TermMode == Single) + mythis->setWindowTitle(Title); + + AX25Sess->port = 0; + AX25Sess->Sess = Sess; // Crosslink AGW and Term Sessions + AX25Sess->PID = 240;; + + Sess->KISSSession = AX25Sess; + + setMenus(true); + + if (ConnectBeep) + myBeep(); + + // Send CText if defined + + if (listenCText[0]) + SendtoAX25(Sess->KISSSession, (unsigned char *)listenCText, (int)strlen(listenCText)); + } + return Sess; +} + + +extern "C" void AX25_disc(TAX25Port * AX25Sess, Byte mode) +{ + char Msg[128]; + int Len = 0; + Ui_ListenSession * Sess = (Ui_ListenSession *)AX25Sess->Sess; + + if (AX25Sess->status == STAT_TRY_LINK) + { + // Connect failed + + Len = sprintf(Msg, "Connection to %s failed\r", AX25Sess->corrcall); + } + else + { + switch (mode) + { + case MODE_OTHER: + case MODE_OUR: + + Len = sprintf(Msg, "Disconnected from %s\r", AX25Sess->corrcall); + break; + + case MODE_RETRY: + + Len = sprintf(Msg, "Disconnected from %s - Retry count exceeded\r", AX25Sess->corrcall); + break; + + }; + } + + SendtoTerm(Sess, Msg, Len); + ClearSessLabel(Sess); + Sess->KISSSession = NULL; + AX25Sess->Sess = 0; + + setMenus(0); +}; + +int QtTermTCP::openSerialPort() +{ + if (m_serial && m_serial->isOpen()) + { + m_serial->close(); + } + + m_serial = nullptr; + m_serial = new QSerialPort(this); + + m_serial->setPortName(SerialPort); + m_serial->setBaudRate(KISSBAUD); + + if (m_serial->open(QIODevice::ReadWrite)) + { + int i; + connect(m_serial, &QSerialPort::readyRead, this, &QtTermTCP::readSerialData); +// connect(m_serial, &QSerialPort::errorOccurred, this, &QtTermTCP::handleError); + + KISSConnected = 1; + KISSConnecting = 0; + + Status3->setText("KISS Connected"); + actHost[18]->setEnabled(1); // Enable KISS Connect Line + + KISS_add_stream(m_serial); + + // Attach a Monitor Window if available + + Ui_ListenSession * Sess = NULL; + Ui_ListenSession * S; + + if (TermMode == MDI) + { + // See if an old session can be reused + + for (int i = 0; i < _sessions.size(); ++i) + { + S = _sessions.at(i); + + // for (Ui_ListenSession * S: _sessions) + // { + if ((S->SessionType == Mon) && S->clientSocket == NULL && S->KISSSession == NULL) + { + Sess = S; + break; + } + } + + // Create a window if none found, else reuse old + + if (Sess == NULL) + { + Sess = newWindow((QObject *)mythis, Mon, ""); + } + } + else if (TermMode == Tabbed) + { + // Tabbed - look for free session + + for (i = 8; i; i--) + { + S = _sessions.at(i); + + if (S->clientSocket == NULL && S->KISSSession == NULL) + { + Sess = S; + break; + } + } + } + else if (TermMode == Single && (singlemodeFormat & Mon)) + { + S = _sessions.at(0); + + if (S->clientSocket == NULL && S->KISSSession == NULL) + Sess = S; + + } + + if (Sess) + { + KISSMonSess = Sess; // Flag as in use + + if (TermMode == MDI) + Sess->setWindowTitle("KISS Monitor Window"); + else if (TermMode == Tabbed) + tabWidget->setTabText(Sess->Tab, "KISS Mon"); + else if (TermMode == Single) + mythis->setWindowTitle("KISS Monitor Window"); + + // if (TermMode == Single) + // { + // discAction->setEnabled(false); + // YAPPSend->setEnabled(false); + // connectMenu->setEnabled(false); + // } + } + + + + return 1; + } + else + { + Status3->setText("KISS Open Failed"); + KISSConnected = 0; + KISSConnecting = 0; + return 0; + } +} + + + +void closeSerialPort() +{ + if (m_serial && m_serial->isOpen()) + { + m_serial->close(); + m_serial = nullptr; + } + + m_serial = nullptr; + + KISSConnected = 0; + KISSConnecting = 0; + + Status3->setText("KISS Closed"); + actHost[18]->setEnabled(0); // Enable KISS Connect Line +} + +void QtTermTCP::readSerialData() +{ + int Read; + unsigned char Buffer[8192]; + + // read the data from the socket + + m_serial->clearError(); + + Read = m_serial->read((char *)Buffer, 2047); + + if (m_serial->error()) + { + Debugprintf("Serial Read Error - RC %d Error %d", Read, m_serial->error()); + closeSerialPort(); + return; + } + + + while (Read > 0) + { + KISSDataReceived(m_serial, Buffer, Read); + Read = m_serial->read((char *)Buffer, 2047); + } +} + +void QtTermTCP::handleError(QSerialPort::SerialPortError serialPortError) +{ + Debugprintf("Serial port Error %d", serialPortError); + closeSerialPort(); +} + +extern "C" void CheckUIFrame(unsigned char * path, string * data) +{ + // If we have KISS enabled and dest is UIDEST look for a KISS window in UI Mode + + if (KISSSock == 0) + return; + + char Dest[10]; + char From[10]; + + Dest[ConvFromAX25(path, Dest)] = 0; + From[ConvFromAX25(&path[7], From)] = 0; + + // ok, Find a Kiss Session with this Dest + + Ui_ListenSession * Sess = NULL; + + for (int i = 0; i < _sessions.size(); ++i) + { + Sess = _sessions.at(i); + + if (Sess->KISSMode == 1 && strcmp(Dest, Sess->UIDEST) == 0) + { + char Msg[512]; + int Len; + + data->Data[data->Length] = 0; + + Len = sprintf(Msg, "%s:%s", From, data->Data); + SendtoTerm(Sess, Msg, Len); + return; + } + } + +} + + +QColor QtTermTCP::setColor(QColor Colour) +{ + // const QColorDialog::ColorDialogOptions options = QFlag(colorDialogOptionsWidget->value()); + + + QColor col = Colour; + + QColorDialog dialog; + dialog.setCurrentColor(Colour); + dialog.setOption(QColorDialog::DontUseNativeDialog); + + if (dialog.exec() == QColorDialog::Accepted) + col = QVariant(dialog.currentColor()).toString(); + + +// const QColor color = QColorDialog::getColor(Qt::green, this, "Select Color", 0); + + return col; +} + + + + + + + diff --git a/QtTermTCP.h b/QtTermTCP.h new file mode 100644 index 0000000..dd93eec --- /dev/null +++ b/QtTermTCP.h @@ -0,0 +1,260 @@ +#pragma once + +#include +#include "ui_QtTermTCP.h" +//#include "ui_ListenPort.h" +//#include "ui_AGWParams.h" +//#include "ui_AGWConnect.h" +#include "ui_ColourConfig.h" +#include "ui_VARAConfig.h" +#include "ui_KISSConfig.h" +#include "QTextEdit" +#include "QSplitter" +#include "QLineEdit" +#include "QTcpSocket" +#include +#include +#include "QThread" +#include "QTcpServer" +#include "QMdiArea" +#include +#include "QMessageBox" +#include "QTimer" +#include "QSettings" +#include "QThread" +#include +#include +#include +#include +#include +#include +#include + + + +QT_BEGIN_NAMESPACE +class QComboBox; +class QLabel; +class QLineEdit; +class QPushButton; +class QTcpSocket; +class QNetworkSession; + +class myTcpSocket : public QTcpSocket +{ +public: + QWidget * Sess; +}; + + +class Ui_ListenSession : public QMainWindow +{ + Q_OBJECT + +public: + explicit Ui_ListenSession(QWidget *Parent = 0) : QMainWindow(Parent) {} + ~Ui_ListenSession(); + + int SessionType; // Type Mask - Term, Mon, Listen + int CurrentWidth; + int CurrentHeight; // Saved so can be restored after Cascade + + QTextEdit *termWindow; + QTextEdit *monWindow; + QLineEdit *inputWindow; + + myTcpSocket *clientSocket; + + QAction * actActivate; // From active Windows menu + + char * KbdStack[50]; + int StackIndex; + + QMdiSubWindow *sw; // The MdiSubwindow is the container for this session + + int InputMode; + int SlowTimer; + int MonData; + + int OutputSaveLen; + char OutputSave[16384]; + + int MonSaveLen; + char MonSave[4096]; + + char PortMonString[1024]; // 32 ports 32 Bytes + unsigned long long portmask; + int mtxparam; + int mcomparam; + int monUI; + int MonitorNODES; + int MonitorColour; + int CurrentHost; + int Tab; // Tab Index if Tabbed Mode + void * AGWSession; // Terinal sess - Need to cast to TAGWPort to use it + void * AGWMonSession; + void * KISSSession; + int KISSMode; // Connected or UI + char UIDEST[32]; + char UIPATH[128]; + +protected: + +private slots: + +private: + +}; + + +class QtTermTCP : public QMainWindow +{ + Q_OBJECT + +public: + QtTermTCP(QWidget *parent = NULL); + void closeEvent(QCloseEvent * event); + static void setFonts(); + ~QtTermTCP(); + +private slots: + void Disconnect(); + void doYAPPSend(); + void doYAPPSetRX(); + void menuChecked(); + void Connect(); + void displayError(QAbstractSocket::SocketError socketError); + void readyRead(); + + void LreturnPressed(Ui_ListenSession * LUI); + void LDisconnect(Ui_ListenSession * LUI); + void SetupHosts(); + void MyTimerSlot(); + void KISSTimerSlot(); + void ListenSlot(); + void AGWSlot(); + void VARASlot(); + void KISSSlot(); + void deviceaccept(); + void KISSaccept(); + void KISSreject(); + void devicereject(); + void showContextMenuM(const QPoint &pt); + void showContextMenuT(const QPoint &pt); + void doQuit(); + void onTEselectionChanged(); + void onLEselectionChanged(); + void setSplit(); + void onNewConnection(); + void onSocketStateChanged(QAbstractSocket::SocketState socketState); + void updateWindowMenu(); + void doNewTerm(); + void doNewMon(); + void doNewCombined(); + void doCascade(); + void actActivate(); + void xon_mdiArea_changed(); + void doFonts(); + void doMFonts(); + void ConnecttoVARA(); + void VARATimer(); + void AGWdisplayError(QAbstractSocket::SocketError socketError); + void AGWreadyRead(); + void onAGWSocketStateChanged(QAbstractSocket::SocketState socketState); + void VARAdisplayError(QAbstractSocket::SocketError socketError); + void VARAreadyRead(); + void onVARASocketStateChanged(QAbstractSocket::SocketState socketState); + void KISSdisplayError(QAbstractSocket::SocketError socketError); + void KISSreadyRead(); + void onKISSSocketStateChanged(QAbstractSocket::SocketState socketState); + int openSerialPort(); + void readSerialData(); + void handleError(QSerialPort::SerialPortError serialPortError); + void doColours(); + void ColourPressed(); + void Colouraccept(); + void Colourreject(); + QColor setColor(QColor Colour); + void VARADatadisplayError(QAbstractSocket::SocketError socketError); + void VARADatareadyRead(); + void onVARADataSocketStateChanged(QAbstractSocket::SocketState socketState); + void HAMLIBdisplayError(QAbstractSocket::SocketError socketError); + void HAMLIBreadyRead(); + void onHAMLIBSocketStateChanged(QAbstractSocket::SocketState socketState); + void ConnecttoHAMLIB(); + void HAMLIBSetPTT(int PTTState); + void FLRigdisplayError(QAbstractSocket::SocketError socketError); + void FLRigreadyRead(); + void onFLRigSocketStateChanged(QAbstractSocket::SocketState socketState); + void ConnecttoFLRig(); + void FLRigSetPTT(int PTTState); + void CATChanged(bool State); + void PTTPortChanged(int Selected); + void OpenPTTPort(); + void RadioPTT(bool PTTState); + void tabSelected(int); + void VARAHFChanged(bool state); + void VARAFMChanged(bool State); + void VARASATChanged(bool State); + void SetVARAParams(); + +protected: + bool eventFilter(QObject* obj, QEvent *event); + +private: + + void ConnecttoAGW(); + + void AGWTimer(); + + Ui::QtTermTCPClass ui; + + QMenu *hostsubMenu; + + QAction *closeAct; + QAction *closeAllAct; + QAction *tileAct; + QAction *cascadeAct; + QAction *nextAct; + QAction *previousAct; + QAction *windowMenuSeparatorAct; + QAction *newTermAct; + QAction *newMonAct; + QAction *newCombinedAct; + QAction *AGWAction; + QAction *VARAAction; + QAction *KISSAction; + QAction *quitAction; + + QList _sockets; + + QWidget *centralWidget; + void ConnecttoKISS(); + void KISSTimer(); +}; + +extern "C" +{ + void EncodeSettingsLine(int n, char * String); + void DecodeSettingsLine(int n, char * String); + void WritetoOutputWindow(Ui_ListenSession * Sess, unsigned char * Buffer, int Len); + void WritetoOutputWindowEx(Ui_ListenSession * Sess, unsigned char * Buffer, int len, QTextEdit * termWindow, int *OutputSaveLen, char * OutputSave, QColor Colour); + void WritetoMonWindow(Ui_ListenSession * Sess, unsigned char * Buffer, int Len); + void ProcessReceivedData(Ui_ListenSession * Sess, unsigned char * Buffer, int len); + void SendTraceOptions(Ui_ListenSession * LUI); + void setTraceOff(Ui_ListenSession * Sess); + void SetPortMonLine(int i, char * Text, int visible, int enabled); + void SaveSettings(); + void myBeep(); + void YAPPSendFile(Ui_ListenSession * Sess, char * FN); + int SocketSend(Ui_ListenSession * Sess, char * Buffer, int len); + void SendTraceOptions(Ui_ListenSession * Sess); + int SocketFlush(Ui_ListenSession * Sess); + extern void mySleep(int ms); + extern void setTraceOff(Ui_ListenSession * Sess); +} + + +char * strlop(char * buf, char delim); +extern "C" void setMenus(int State); +void Send_AGW_Ds_Frame(void * AGW); diff --git a/QtTermTCP.ico b/QtTermTCP.ico new file mode 100644 index 0000000..4a01e37 Binary files /dev/null and b/QtTermTCP.ico differ diff --git a/QtTermTCP.ini b/QtTermTCP.ini new file mode 100644 index 0000000..4544206 --- /dev/null +++ b/QtTermTCP.ini @@ -0,0 +1,116 @@ +[General] +HostParams0=127.0.0.1|8011|john|password| +geometry=@ByteArray(\x1\xd9\xd0\xcb\0\x3\0\0\0\0\0\xf9\0\0\0\x85\0\0\x4\x5\0\0\x3>\0\0\0\xf9\0\0\0\x85\0\0\x4\x5\0\0\x3>\0\0\0\0\0\0\0\0\x5\0\0\0\0\xf9\0\0\0\x85\0\0\x4\x5\0\0\x3>) +HostParams1=|0||| +windowState=@ByteArray(\0\0\0\xff\0\0\0\0\xfd\0\0\0\0\0\0\x3\r\0\0\x2z\0\0\0\x4\0\0\0\x4\0\0\0\b\0\0\0\b\xfc\0\0\0\x1\0\0\0\x3\0\0\0\x1\0\0\0\x16\0m\0\x61\0i\0n\0T\0o\0o\0l\0\x62\0\x61\0r\0\0\0\0\0\xff\xff\xff\xff\0\0\0\0\0\0\0\0) +HostParams2=|0||| +HostParams3=|0||| +HostParams4=|0||| +HostParams5=|0||| +HostParams6=|0||| +HostParams7=|0||| +HostParams8=|0||| +HostParams9=|0||| +HostParams10=|0||| +HostParams11=|0||| +HostParams12=|0||| +HostParams13=|0||| +HostParams14=|0||| +HostParams15=|0||| +Split=50 +ChatMode=1 +Bells=1 +StripLF=1 +AlertBeep=1 +ConnectBeep=1 +CurrentHost=0 +YAPPPath= +listenPort=8015 +listenEnable=1 +listenCText=Hello\r +convUTF8=0 +PTT=None +PTTBAUD=19200 +PTTMode=19200 +CATHex=1 +PTTOffString= +PTTOnString= +pttGPIOPin=17 +pttGPIOPinR=17 +CM108Addr=0xD8C:0x08 +HamLibPort=4532 +HamLibHost=127.0.0.1 +FLRigPort=12345 +FLRigHost=127.0.0.1 +AGWEnable=1 +AGWMonEnable=1 +AGWTermCall= +AGWBeaconDest= +AGWBeaconPath= +AGWBeaconInterval=0 +AGWBeaconPorts= +AGWBeaconText= +AGWHost=127.0.0.1 +AGWPort=8001 +AGWPaclen=80 +AGWToCalls= +KISSEnable=0 +MYCALL= +KISSHost=127.0.0.1 +KISSMode=0 +KISSPort=8100 +KISSSerialPort=None +KISSBAUD=19200 +VARAEnable=0 +VARATermCall= +VARAHost=127.0.0.1 +VARAPort=8300 +VARAPath=C:\\VARA\\VARA.exe +VARAHostHF=127.0.0.1 +VARAPortHF=8300 +VARAPathHF=C:\\VARA\\VARA.exe +VARAHostFM=127.0.0.1 +VARAPortFM=8300 +VARAPathFM=C:\\VARA\\VARAFM.exe +VARAHostSAT=127.0.0.1 +VARAPortSAT=8300 +VARAPathSAT=C:\\VARA\\VARASAT.exe +VARA500=0 +VARA2300=1 +VARA2750=0 +VARAHF=1 +VARAFM=0 +VARASAT=0 +TabType=1 1 1 1 1 1 1 2 2 0 +monBackground=@Variant(\0\0\0\x43\x1\xff\xff\0\0\0\0\0\0\0\0) +monRxText=@Variant(\0\0\0\x43\x1\xff\xff\0\0\xff\xff\0\0\0\0) +monTxText=@Variant(\0\0\0\x43\x1\xff\xff\xff\xff\0\0\0\0\0\0) +monOtherText=@Variant(\0\0\0\x43\x1\xff\xff\0\0\0\0\0\0\0\0) +termBackground=@Variant(\0\0\0\x43\x1\xff\xff\0\0\0\0\0\0\0\0) +outputText=@Variant(\0\0\0\x43\x1\xff\xff\0\0\xff\xff\0\0\0\0) +EchoText=@Variant(\0\0\0\x43\x1\xff\xff\xff\xff\0\0\xff\xff\0\0) +WarningText=@Variant(\0\0\0\x43\x1\xff\xff\xff\xff\0\0\0\0\0\0) +inputBackground=@Variant(\0\0\0\x43\x1\xff\xff\xff\xff\xff\xff\xff\xff\0\0) +inputText=@Variant(\0\0\0\x43\x1\xff\xff\0\0\0\0\0\0\0\0) +TermMode=2 +singlemodeFormat=3 +Sessions="1|4, 0, 3, 136, 599, 932|" + +[AX25_A] +Retries=10 +Maxframe=4 +Paclen=128 +FrackTime=8 +IdleTime=180 +SlotTime=100 +Persist=128 +RespTime=1500 +TXFrmMode=1 +FrameCollector=6 +ExcludeCallsigns= +ExcludeAPRSFrmType= +KISSOptimization=0 +DynamicFrack=0 +BitRecovery=0 +IPOLL=80 +MyDigiCall= diff --git a/QtTermTCP.pro b/QtTermTCP.pro new file mode 100644 index 0000000..e802688 --- /dev/null +++ b/QtTermTCP.pro @@ -0,0 +1,48 @@ + +QT += core gui +QT += network +QT += widgets +QT += serialport + + +TARGET = QtTermTCP +TEMPLATE = app + +# The following define makes your compiler emit warnings if you use +# any feature of Qt which as been marked as deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. + +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if you use deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x050000 # disables all the APIs deprecated before Qt 6.0.0 + + +SOURCES += main.cpp\ + QtTermTCP.cpp\ + TermTCPCommon.cpp\ + TabDialog.cpp \ + AGWCode.cpp \ + ax25.c \ + UZ7HOUtils.c \ + ax25_l2.c\ + utf8Routines.cpp + +HEADERS += QtTermTCP.h\ + TabDialog.h + +FORMS += QtTermTCP.ui\ + ListenPort.ui \ + AGWParams.ui \ + VARAConfig.ui \ + KISSConfig.ui \ + AGWConnect.ui + +RESOURCES += QtTermTCP.qrc + +RC_ICONS = QtTermTCP.ico + +QMAKE_LFLAGS += -no-pie diff --git a/QtTermTCP.pro.bak b/QtTermTCP.pro.bak new file mode 100644 index 0000000..d07cc3d --- /dev/null +++ b/QtTermTCP.pro.bak @@ -0,0 +1,38 @@ + +QT += core gui +QT += network +QT += widgets + + +TARGET = QtTermTCP +TEMPLATE = app + +# The following define makes your compiler emit warnings if you use +# any feature of Qt which as been marked as deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. + +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if you use deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x050000 # disables all the APIs deprecated before Qt 6.0.0 + + +SOURCES += main.cpp\ + QtTermTCP.cpp\ + TermTCPCommon.cpp\ + TabDialog.cpp + +HEADERS += QtTermTCP.h\ + TabDialog.h + +FORMS += QtTermTCP.ui\ + ListenPort.ui + +RESOURCES += QtTermTCP.qrc + +RC_ICONS = QtTermTCP.ico + +QMAKE_LFLAGS += -no-pie diff --git a/QtTermTCP.qrc b/QtTermTCP.qrc new file mode 100644 index 0000000..1e71b3b --- /dev/null +++ b/QtTermTCP.qrc @@ -0,0 +1,5 @@ + + + QtTermTCP.ico + + diff --git a/QtTermTCP.rc b/QtTermTCP.rc new file mode 100644 index 0000000..b75a88f Binary files /dev/null and b/QtTermTCP.rc differ diff --git a/QtTermTCP.sln b/QtTermTCP.sln new file mode 100644 index 0000000..01fdba7 --- /dev/null +++ b/QtTermTCP.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.102 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "QtTermTCP", "D:\BT Cloud\ActiveSource\QtTermTCP\QtTermTCP.vcxproj", "{B12702AD-ABFB-343A-A199-8E24837244A3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|x86.ActiveCfg = Debug|Win32 + {B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|x86.Build.0 = Debug|Win32 + {B12702AD-ABFB-343A-A199-8E24837244A3}.Release|x86.ActiveCfg = Release|Win32 + {B12702AD-ABFB-343A-A199-8E24837244A3}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {DD1F8FD5-5375-4EFC-BBEE-8E35ABA15C82} + EndGlobalSection +EndGlobal diff --git a/QtTermTCP.ui b/QtTermTCP.ui new file mode 100644 index 0000000..a2bfd8c --- /dev/null +++ b/QtTermTCP.ui @@ -0,0 +1,66 @@ + + + QtTermTCPClass + + + + 0 + 0 + 781 + 698 + + + + + Arial + 12 + + + + QtTermTCP + + + + :/QtTermTCP/QtTermTCP.ico:/QtTermTCP/QtTermTCP.ico + + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + 6 + + + + + + + + + Listen + + + + + Disconnect + + + + + + + + + diff --git a/QtTermTCP.vcxproj b/QtTermTCP.vcxproj new file mode 100644 index 0000000..4382cba --- /dev/null +++ b/QtTermTCP.vcxproj @@ -0,0 +1,267 @@ + + + + + Release + Win32 + + + Debug + Win32 + + + + {14F3B24E-473C-324E-A99D-3B679FCF5F67} + QtTermTCP + QtVS_v304 + 10.0.19041.0 + 10.0.19041.0 + $(MSBuildProjectDirectory)\QtMsBuild + + + + v141 + release\ + false + NotSet + Application + release\ + QtTermTCP + + + v141 + debug\ + false + NotSet + Application + debug\ + QtTermTCP + + + + + + + + + + + + + + + + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(SolutionDir)Intermed\$(Platform)\$(Configuration)\ + QtTermTCP + true + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(SolutionDir)Intermed\$(Platform)\$(Configuration)\ + QtTermTCP + true + false + + + msvc2017 + core;network;gui;widgets;serialport + + + msvc2017 + core;network;gui;widgets;serialport + + + + + + + GeneratedFiles\$(ConfigurationName);GeneratedFiles;.;release;/include;%(AdditionalIncludeDirectories) + -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) + $(IntDir) + false + None + 4577;4467;%(DisableSpecificWarnings) + Sync + $(IntDir) + MaxSpeed + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_DEPRECATED_WARNINGS;QT_DISABLE_DEPRECATED_BEFORE=0x050000;NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) + false + + + MultiThreadedDLL + true + true + Level3 + true + + + shell32.lib;setupapi.lib;%(AdditionalDependencies) + C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) + -no-pie "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) + true + false + true + false + true + $(OutDir)\QtTermTCP.exe + true + Windows + true + + + Unsigned + None + 0 + + + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_DEPRECATED_WARNINGS;QT_DISABLE_DEPRECATED_BEFORE=0x050000;NDEBUG;QT_NO_DEBUG;QT_WIDGETS_LIB;QT_GUI_LIB;QT_NETWORK_LIB;QT_SERIALPORT_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + + + msvc + ./$(Configuration)/moc_predefs.h + Moc'ing %(Identity)... + output + $(IntDir) + moc_%(Filename).cpp + + + QtTermTCP + default + Rcc'ing %(Identity)... + $(IntDir) + qrc_%(Filename).cpp + + + Uic'ing %(Identity)... + $(IntDir) + ui_%(Filename).h + + + + + GeneratedFiles\$(ConfigurationName);GeneratedFiles;.;debug;/include;%(AdditionalIncludeDirectories) + -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) + $(IntDir) + false + ProgramDatabase + 4577;4467;%(DisableSpecificWarnings) + Sync + $(intdir) + Disabled + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_DEPRECATED_WARNINGS;QT_DISABLE_DEPRECATED_BEFORE=0x050000;%(PreprocessorDefinitions) + false + MultiThreadedDebugDLL + true + true + Level3 + true + + + shell32.lib;setupapi.lib;%(AdditionalDependencies) + C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) + -no-pie "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) + true + true + true + $(OutDir)\QtTermTCP.exe + true + Windows + true + + + Unsigned + None + 0 + + + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_DEPRECATED_WARNINGS;QT_DISABLE_DEPRECATED_BEFORE=0x050000;QT_WIDGETS_LIB;QT_GUI_LIB;QT_NETWORK_LIB;QT_SERIALPORT_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) + + + msvc + ./$(Configuration)/moc_predefs.h + Moc'ing %(Identity)... + output + $(IntDir) + moc_%(Filename).cpp + + + QtTermTCP + default + Rcc'ing %(Identity)... + $(IntDir) + qrc_%(Filename).cpp + + + Uic'ing %(Identity)... + $(IntDir) + ui_%(Filename).h + + + + + + + + + + + + + + + + + + + + + + + Document + true + $(QTDIR)\mkspecs\features\data\dummy.cpp;%(AdditionalInputs) + cl -Bx"$(QTDIR)\bin\qmake.exe" -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -Zi -MDd -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -E $(QTDIR)\mkspecs\features\data\dummy.cpp 2>NUL >debug\moc_predefs.h + Generate moc_predefs.h + debug\moc_predefs.h;%(Outputs) + + + Document + $(QTDIR)\mkspecs\features\data\dummy.cpp;%(AdditionalInputs) + cl -Bx"$(QTDIR)\bin\qmake.exe" -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -O2 -MD -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -E $(QTDIR)\mkspecs\features\data\dummy.cpp 2>NUL >release\moc_predefs.h + Generate moc_predefs.h + release\moc_predefs.h;%(Outputs) + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/QtTermTCP.vcxproj.filters b/QtTermTCP.vcxproj.filters new file mode 100644 index 0000000..5347f44 --- /dev/null +++ b/QtTermTCP.vcxproj.filters @@ -0,0 +1,131 @@ + + + + + {99349809-55BA-4b9d-BF79-8FDBB0286EB3} + ui + false + + + {99349809-55BA-4b9d-BF79-8FDBB0286EB3} + ui + false + + + {71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11} + cpp;c;cxx;moc;h;def;odl;idl;res; + + + {71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11} + cpp;c;cxx;moc;h;def;odl;idl;res; + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} + qrc;* + false + + + {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} + qrc;* + false + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + + + Generated Files + + + Generated Files + + + + + Form Files + + + Form Files + + + Form Files + + + Form Files + + + Form Files + + + Form Files + + + Form Files + + + + + Resource Files + + + Resource Files + + + + + + \ No newline at end of file diff --git a/QtTermTCP.vcxproj.user b/QtTermTCP.vcxproj.user new file mode 100644 index 0000000..594ceec --- /dev/null +++ b/QtTermTCP.vcxproj.user @@ -0,0 +1,16 @@ + + + + + 2022-05-19T07:28:47.9186341Z + + + 2022-05-19T07:28:58.9302359Z + + + 2022-08-30T07:53:00.9638702Z + + + 2022-08-30T07:53:22.4120059Z + + \ No newline at end of file diff --git a/QtTermTCP52.zip b/QtTermTCP52.zip new file mode 100644 index 0000000..e36d5dc Binary files /dev/null and b/QtTermTCP52.zip differ diff --git a/QtTermTCP_resource.rc b/QtTermTCP_resource.rc new file mode 100644 index 0000000..e58cc35 --- /dev/null +++ b/QtTermTCP_resource.rc @@ -0,0 +1,37 @@ +#include + +IDI_ICON1 ICON DISCARDABLE "C:\\Users\\John\\OneDrive\\Dev\\Source\\QT\\QtTermTCP\\QtTermTCP.ico" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 0,0,0,0 + PRODUCTVERSION 0,0,0,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE 0x0L + BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "\0" + VALUE "FileDescription", "\0" + VALUE "FileVersion", "0.0.0.0\0" + VALUE "LegalCopyright", "\0" + VALUE "OriginalFilename", "QtTermTCP.exe\0" + VALUE "ProductName", "QtTermTCP\0" + VALUE "ProductVersion", "0.0.0.0\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 1200 + END + END +/* End of Version info */ + diff --git a/TCPHostConfig.ui b/TCPHostConfig.ui new file mode 100644 index 0000000..e4b706b --- /dev/null +++ b/TCPHostConfig.ui @@ -0,0 +1,85 @@ + + + Dialog + + + + 0 + 0 + 400 + 300 + + + + TCP Host Config + + + + QtTermTCP.icoQtTermTCP.ico + + + + + 30 + 240 + 341 + 32 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Save + + + true + + + + + + 184 + 44 + 104 + 23 + + + + + + + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/TabDialog.cpp b/TabDialog.cpp new file mode 100644 index 0000000..b627f30 --- /dev/null +++ b/TabDialog.cpp @@ -0,0 +1,1127 @@ + +#define _CRT_SECURE_NO_WARNINGS + +#include "TabDialog.h" +#include "QtTermTCP.h" +#include +#include "QSettings" +#include "QLineEdit" +#include "QTabWidget" +#include "QDialogButtonBox" +#include "QVBoxLayout" +#include "QLabel" +#include "QAction" +#include "QGroupBox" +#include "QPlainTextEdit" +#include "QCheckBox" +#include "QFormLayout" +#include "QScrollArea" + +#include "ax25.h" + +#ifndef WIN32 +#define strtok_s strtok_r +#endif + +extern "C" void SaveSettings(); + +extern int screenHeight; +extern int screenWidth; + +extern QList _sessions; +extern QTcpServer * _server; + +extern myTcpSocket * AGWSock; +extern myTcpSocket * KISSSock; +extern QLabel * Status1; +extern QFont * menufont; +extern QStatusBar * myStatusBar; + +QLineEdit *hostEdit; +QLineEdit *portEdit; +QLineEdit *userEdit; +QLineEdit *passEdit; + +extern QAction *actHost[17]; +extern QAction *actSetup[16]; + +extern int ConfigHost; + +#define MAXHOSTS 16 + +extern char Host[MAXHOSTS + 1][100]; +extern int Port[MAXHOSTS + 1]; +extern char UserName[MAXHOSTS + 1][80]; +extern char Password[MAXHOSTS + 1][80]; +extern char MYCALL[32]; + +QLineEdit *TermCall; +QGroupBox *groupBox; +QLineEdit *beaconDest; +QLineEdit *beaconPath; +QLineEdit *beaconInterval; +QLineEdit *beaconPorts; +QLabel *label_2; +QLabel *label_3; +QLabel *label_4; +QLabel *label_5; +QLabel *label_6; +QLabel *label_7; +QLabel *label_11; +QPlainTextEdit *beaconText; +QLabel *label_12; +QGroupBox *groupBox_2; +QLineEdit *AHost; +QLineEdit *APort; +QLineEdit *APath; +QLineEdit *Paclen; +QLabel *label_8; +QLabel *label_9; +QLabel *label_10; +QLabel *label; +QCheckBox *AAGWEnable; +QLabel *label_13; +QCheckBox *AAGWMonEnable; + +QLineEdit *AVARATermCall; +QLineEdit *AVARAHost; +QLineEdit *AVARAPort; +QCheckBox *AVARAEnable; + + +extern int AGWEnable; +extern int VARAEnable; +extern int KISSEnable; +extern int AGWMonEnable; +extern char AGWTermCall[12]; +extern char AGWBeaconDest[12]; +extern char AGWBeaconPath[80]; +extern int AGWBeaconInterval; +extern char AGWBeaconPorts[80]; +extern char AGWBeaconMsg[260]; + +extern char VARATermCall[12]; + +extern char AGWHost[128]; +extern int AGWPortNum; +extern int AGWPaclen; +extern char * AGWPortList; +extern myTcpSocket * AGWSock; +extern char * AGWPortList; +extern QStringList AGWToCalls; + +extern int KISSMode; + +extern Ui_ListenSession * ActiveSession; + +extern int TermMode; + +#define Single 0 +#define MDI 1 +#define Tabbed 2 + +extern int listenPort; +extern "C" int listenEnable; +extern char listenCText[4096]; + +void Send_AGW_C_Frame(Ui_ListenSession * Sess, int Port, char * CallFrom, char * CallTo, char * Data, int DataLen); +extern "C" void SetSessLabel(Ui_ListenSession * Sess, char * label); +extern "C" void SendtoTerm(Ui_ListenSession * Sess, char * Msg, int Len); + +QString GetConfPath(); + +QScrollArea *scrollArea; +QWidget *scrollAreaWidgetContents; + +bool myResize::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::Resize) + { + QResizeEvent *resizeEvent = static_cast(event); + QSize size = resizeEvent->size(); + int h = size.height(); + int w = size.width(); + + scrollArea->setGeometry(QRect(5, 5, w - 10, h - 10)); + return true; + } + return QObject::eventFilter(obj, event); +} + +AGWConnect::AGWConnect(QWidget *parent) : QDialog(parent) +{ + this->setFont(*menufont); + + setWindowTitle(tr("AGW Connection")); + + myResize *resize = new myResize(); + installEventFilter(resize); + + scrollArea = new QScrollArea(this); + scrollArea->setObjectName(QString::fromUtf8("scrollArea")); + scrollArea->setGeometry(QRect(5, 5, 562, 681)); + scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + scrollArea->setWidgetResizable(false); + scrollAreaWidgetContents = new QWidget(); + scrollAreaWidgetContents->setObjectName(QString::fromUtf8("scrollAreaWidgetContents")); + + QVBoxLayout *layout = new QVBoxLayout(); + layout->setContentsMargins(10, 10, 10, 10); + + setLayout(layout); + + QFormLayout *formLayout2 = new QFormLayout(); + layout->addLayout(formLayout2); + + wCallFrom = new QLineEdit(); + formLayout2->addRow(new QLabel("Call From"), wCallFrom); + + wCallTo = new QComboBox(); + wCallTo->setEditable(true); + wCallTo->setInsertPolicy(QComboBox::NoInsert); + + formLayout2->addRow(new QLabel("Call To"), wCallTo); + + Digis = new QLineEdit(); + formLayout2->addRow(new QLabel("Digis"), Digis); + + layout->addSpacing(2); + layout->addWidget(new QLabel("Radio Ports")); + + RadioPorts = new QListWidget(); + + layout->addWidget(RadioPorts); + + QString str; + int count; + + char * Context; + char * ptr; + + wCallFrom->setText(AGWTermCall); + + wCallTo->addItems(AGWToCalls); + + if (AGWPortList) + { + char * Temp = strdup(AGWPortList); // Need copy as strtok messes with it + + count = atoi(Temp); + + ptr = strtok_s(Temp, ";", &Context); + + for (int n = 0; n < count; n++) + { + ptr = strtok_s(NULL, ";", &Context); + new QListWidgetItem(ptr, RadioPorts); + } + + free(Temp); + + // calculate scrollarea height from count + + scrollAreaWidgetContents->setGeometry(QRect(0, 0, 400, 220 + 22 * count)); + this->resize(420, 240 + 22 * count); + + } + + RadioPorts->setFont(*menufont); + + buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + buttonBox->setFont(*menufont); + layout->addWidget(buttonBox); + + scrollAreaWidgetContents->setLayout(layout); + scrollArea->setWidget(scrollAreaWidgetContents); + + connect(buttonBox, SIGNAL(accepted()), this, SLOT(myaccept())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(myreject())); +} + +AGWConnect::~AGWConnect() +{ +} + +extern QAction *discAction; + +void AGWConnect::myaccept() +{ + QVariant Q; + + int port = RadioPorts->currentRow(); + char CallFrom[32]; + char CallTo[32]; + char Via[128]; + char DigiMsg[128] = ""; + int DigiLen = 0; + char * digiptr = &DigiMsg[1]; + + strcpy(CallFrom, wCallFrom->text().toUpper().toUtf8()); + strcpy(CallTo, wCallTo->currentText().toUpper().toUtf8()); + strcpy(Via, Digis->text().toUpper().toUtf8()); + + // if digis have to form block with byte count followed by n 10 byte calls + + if (Via[0]) + { + char * context; + char * ptr = strtok_s(Via, ", ", &context); + + while (ptr) + { + DigiMsg[0]++; + strcpy(digiptr, ptr); + digiptr += 10; + + ptr = strtok_s(NULL, ", ", &context); + } + DigiLen = digiptr - DigiMsg; + } + + // Add CallTo if not already in list + + if (AGWToCalls.contains(CallTo)) + AGWToCalls.removeOne(CallTo); + + AGWToCalls.insert(-1, CallTo); + + if (port == -1) + { + // Port not set. If connecting to SWITCH use any, otherwise tell user + + if (strcmp(CallTo, "SWITCH") == 0) + { + port = 0; + } + else + { + QMessageBox msgBox; + msgBox.setText("Select a port to call on"); + msgBox.exec(); + return; + } + } + + Send_AGW_C_Frame(ActiveSession, port, CallFrom, CallTo, DigiMsg, DigiLen); + + discAction->setEnabled(true); + + + AGWConnect::accept(); +} + +void AGWConnect::myreject() +{ + AGWConnect::reject(); +} + + +AGWDialog::AGWDialog(QWidget *parent) : QDialog(parent) +{ + this->setFont(*menufont); + + setWindowTitle(tr("TermTCP AGW Configuration")); + + myResize *resize = new myResize(); + installEventFilter(resize); + + + scrollArea = new QScrollArea(this); + scrollArea->setObjectName(QString::fromUtf8("scrollArea")); + scrollArea->setGeometry(QRect(5, 5, 562, 681)); + scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + scrollArea->setWidgetResizable(false); + scrollAreaWidgetContents = new QWidget(); + scrollAreaWidgetContents->setObjectName(QString::fromUtf8("scrollAreaWidgetContents")); + scrollAreaWidgetContents->setGeometry(QRect(0, 0, 552, 581)); + + this->resize(572, 601); + + QVBoxLayout *layout = new QVBoxLayout; + QHBoxLayout *hlayout = new QHBoxLayout; + + layout->addLayout(hlayout); + AAGWEnable = new QCheckBox("Enable AGW Interface"); + AAGWEnable->setLayoutDirection(Qt::LeftToRight); + + AAGWMonEnable = new QCheckBox("Enable Monitor"); + AAGWMonEnable->setGeometry(QRect(255, 18, 216, 21)); + AAGWMonEnable->setLayoutDirection(Qt::RightToLeft); + + hlayout->addWidget(AAGWEnable); + hlayout->addWidget(AAGWMonEnable); + + QFormLayout *flayout = new QFormLayout; + layout->addLayout(flayout); + + label = new QLabel("Terminal Call"); + TermCall = new QLineEdit(this); + + flayout->addRow(label, TermCall); + + layout->addWidget(new QLabel("Beacon Setup")); + + QFormLayout *flayout1 = new QFormLayout; + layout->addLayout(flayout1); + + label_2 = new QLabel("Destination"); + beaconDest = new QLineEdit(); + label_3 = new QLabel("Digipeaters"); + beaconPath = new QLineEdit(); + + flayout1->addRow(label_2, beaconDest); + flayout1->addRow(label_3, beaconPath); + + label_4 = new QLabel("Interval"); + beaconInterval = new QLineEdit(); + label_5 = new QLabel("Ports"); + beaconPorts = new QLineEdit(); + + flayout1->addRow(label_4, beaconInterval); + flayout1->addRow(label_5, beaconPorts); + +// label_6 = new QLabel("Minutes", groupBox); + +// label_7 = new QLabel("(Separate with commas)", groupBox); + label_11 = new QLabel("Message"); + beaconText = new QPlainTextEdit(); + + flayout1->addRow(label_11, beaconText); + + + +// label_12 = new QLabel("(max 256 chars)"); +// label_12->setGeometry(QRect(14, 158, 95, 21)); + + layout->addWidget(new QLabel("TNC Setup")); + + QFormLayout *flayout2 = new QFormLayout; + layout->addLayout(flayout2); + + AHost = new QLineEdit(); + APort = new QLineEdit(); + Paclen = new QLineEdit(); + label_8 = new QLabel("host"); + label_9 = new QLabel("Port"); + label_10 = new QLabel("Paclen "); + + flayout2->addRow(label_8, AHost); + flayout2->addRow(label_9, APort); + flayout2->addRow(label_10, Paclen); + + AAGWEnable->setChecked(AGWEnable); + AAGWMonEnable->setChecked(AGWMonEnable); + TermCall->setText(AGWTermCall); + beaconDest->setText(AGWBeaconDest); + beaconPath->setText(AGWBeaconPath); + beaconPorts->setText(AGWBeaconPorts); + beaconText->setPlainText(AGWBeaconMsg); + beaconInterval->setText(QString::number(AGWBeaconInterval)); + AHost->setText(AGWHost); + APort->setText(QString::number(AGWPortNum)); + Paclen->setText(QString::number(AGWPaclen)); + + buttonBox = new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Cancel, this); + buttonBox->setFont(*menufont); + layout->addWidget(buttonBox); + scrollAreaWidgetContents->setLayout(layout); + scrollArea->setWidget(scrollAreaWidgetContents); + + connect(buttonBox, SIGNAL(accepted()), this, SLOT(myaccept())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(myreject())); + +} + + +AGWDialog::~AGWDialog() +{ +} + + +KISSConnect::KISSConnect(QWidget *parent) : QDialog(parent) +{ + this->setFont(*menufont); + + setWindowTitle(tr("KISS Connection")); + + myResize *resize = new myResize(); + installEventFilter(resize); + + scrollArea = new QScrollArea(this); + scrollArea->setObjectName(QString::fromUtf8("scrollArea")); + scrollArea->setGeometry(QRect(5, 5, 250, 200)); + scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + scrollArea->setWidgetResizable(false); + scrollAreaWidgetContents = new QWidget(); + scrollAreaWidgetContents->setObjectName(QString::fromUtf8("scrollAreaWidgetContents")); + + // layout is top level. + // Add a Horizontal layout for Mode and Verical Layout for Call and Digis + + QVBoxLayout *layout = new QVBoxLayout(); + layout->setContentsMargins(10, 10, 10, 10); + setLayout(layout); + + QHBoxLayout *mylayout = new QHBoxLayout(); + + Connected = new QRadioButton("Sesion"); + UIMode = new QRadioButton("UI"); + + mylayout->addWidget(new QLabel("Connection Mode")); + mylayout->addWidget(Connected); + mylayout->addWidget(UIMode); + + if (KISSMode) // UI = 1 + UIMode->setChecked(1); + else + Connected->setChecked(1); + + QFormLayout *formLayout2 = new QFormLayout(); + layout->addLayout(mylayout); + layout->addSpacing(10); + layout->addLayout(formLayout2); + + wCallTo = new QComboBox(); + wCallTo->setEditable(true); + wCallTo->setInsertPolicy(QComboBox::NoInsert); + + formLayout2->addRow(new QLabel("Call To"), wCallTo); + + Digis = new QLineEdit(); + formLayout2->addRow(new QLabel("Digis"), Digis); + + layout->addSpacing(2); + + buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + buttonBox->setFont(*menufont); + layout->addWidget(buttonBox); + + scrollAreaWidgetContents->setLayout(layout); + scrollArea->setWidget(scrollAreaWidgetContents); + + wCallTo->addItems(AGWToCalls); + + connect(buttonBox, SIGNAL(accepted()), this, SLOT(myaccept())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(myreject())); +} + +extern "C" void * KISSConnectOut(void * Sess, char * CallFrom, char * CallTo, char * Digis, int Chan, void * Socket); + +KISSConnect::~KISSConnect() +{ +} + +extern QAction * YAPPSend; +extern QMenu * connectMenu; +extern QMenu * disconnectMenu; + +TAX25Port DummyPort; + +void KISSConnect::myaccept() +{ + QVariant Q; + + char CallTo[32]; + char Via[128]; + strcpy(CallTo, wCallTo->currentText().toUpper().toUtf8()); + strcpy(Via, Digis->text().toUpper().toUtf8()); + + KISSMode = UIMode->isChecked(); + + // Add CallTo if not already in list + + if (AGWToCalls.contains(CallTo)) + AGWToCalls.removeOne(CallTo); + + AGWToCalls.insert(-1, CallTo); + + ActiveSession->KISSMode = KISSMode; + + if (KISSMode == 0) + { + ActiveSession->KISSSession = KISSConnectOut(ActiveSession, MYCALL, CallTo, Via, 0, (void *)KISSSock); + WritetoOutputWindow(ActiveSession, (unsigned char *)"Connecting...\r", 14); + discAction->setEnabled(true); + } + else + { + // UI + + char Msg[128]; + int Len = 0; + + memset(&DummyPort, 0, sizeof(DummyPort)); + + ActiveSession->KISSSession = (void *)&DummyPort; // Dummy marker to show session in use + + strcpy(ActiveSession->UIDEST, CallTo); + strcpy(ActiveSession->UIPATH, Via); + + if (TermMode == Tabbed) + Len = sprintf(Msg, "UI %s", CallTo); + else + Len = sprintf(Msg, "UI Session with %s\r", CallTo); + + SetSessLabel(ActiveSession, Msg); + + Len = sprintf(Msg, "UI Session with %s\r", CallTo); + SendtoTerm(ActiveSession, Msg, Len); + connectMenu->setEnabled(false); + discAction->setEnabled(true); + YAPPSend->setEnabled(false); + } + + KISSConnect::accept(); +} + +void KISSConnect::myreject() +{ + KISSConnect::reject(); +} + + + +VARAConnect::VARAConnect(QWidget *parent) : QDialog(parent) +{ + this->setFont(*menufont); + + setWindowTitle(tr("VARA Connection")); + + myResize *resize = new myResize(); + installEventFilter(resize); + + scrollArea = new QScrollArea(this); + scrollArea->setObjectName(QString::fromUtf8("scrollArea")); + scrollArea->setGeometry(QRect(5, 5, 250, 150)); + scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + scrollArea->setWidgetResizable(false); + scrollAreaWidgetContents = new QWidget(); + scrollAreaWidgetContents->setObjectName(QString::fromUtf8("scrollAreaWidgetContents")); + + QVBoxLayout *layout = new QVBoxLayout(); + layout->setContentsMargins(10, 10, 10, 10); + + setLayout(layout); + + QFormLayout *formLayout2 = new QFormLayout(); + layout->addLayout(formLayout2); + + wCallFrom = new QLineEdit(); + formLayout2->addRow(new QLabel("Call From"), wCallFrom); + + wCallTo = new QComboBox(); + wCallTo->setEditable(true); + wCallTo->setInsertPolicy(QComboBox::NoInsert); + + formLayout2->addRow(new QLabel("Call To"), wCallTo); + + Digis = new QLineEdit(); + formLayout2->addRow(new QLabel("Digis"), Digis); + + layout->addSpacing(2); + + buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + buttonBox->setFont(*menufont); + layout->addWidget(buttonBox); + + scrollAreaWidgetContents->setLayout(layout); + scrollArea->setWidget(scrollAreaWidgetContents); + + wCallFrom->setText(VARATermCall); + wCallTo->addItems(AGWToCalls); + + connect(buttonBox, SIGNAL(accepted()), this, SLOT(myaccept())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(myreject())); +} + +VARAConnect::~VARAConnect() +{ +} + +extern myTcpSocket * VARASock; +extern myTcpSocket * VARADataSock; + +void VARAConnect::myaccept() +{ + QVariant Q; + + char CallFrom[32]; + char CallTo[32]; + char Via[128]; + char Msg[256]; + int Len; + + strcpy(CallFrom, wCallFrom->text().toUpper().toUtf8()); + strcpy(CallTo, wCallTo->currentText().toUpper().toUtf8()); + strcpy(Via, Digis->text().toUpper().toUtf8()); + +// if digis have to form block with byte count followed by n 10 byte calls + + +// Add CallTo if not already in list + + if (AGWToCalls.contains(CallTo)) + AGWToCalls.removeOne(CallTo); + + AGWToCalls.insert(-1, CallTo); + + + if (Via[0]) + Len = sprintf(Msg, "CONNECT %s %s VIA %s\r", CallFrom, CallTo, Via); + else + Len = sprintf(Msg, "CONNECT %s %s\r", CallFrom, CallTo); + + VARASock->write(Msg, Len); + + discAction->setEnabled(true); + VARAConnect::accept(); +} + +void VARAConnect::myreject() +{ + VARAConnect::reject(); +} + + +extern QProcess *process; + + + +TabDialog::TabDialog(QWidget *parent) : QDialog(parent) +{ + char portnum[10]; + + buttonBox = new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Cancel); + + connect(buttonBox, SIGNAL(accepted()), this, SLOT(myaccept())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(myreject())); + + QVBoxLayout *layout = new QVBoxLayout; + QLabel *hostLabel = new QLabel(tr("Host Name:")); + hostEdit = new QLineEdit(Host[ConfigHost]); + + QLabel *portLabel = new QLabel(tr("Port:")); + sprintf(portnum, "%d", Port[ConfigHost]); + portEdit = new QLineEdit(portnum); + + QLabel *userLabel = new QLabel(tr("User:")); + userEdit = new QLineEdit(UserName[ConfigHost]); + + QLabel *passLabel = new QLabel(tr("Password:")); + passEdit = new QLineEdit(Password[ConfigHost]); + + layout->addWidget(hostLabel); + layout->addWidget(hostEdit); + layout->addWidget(portLabel); + layout->addWidget(portEdit); + layout->addWidget(userLabel); + layout->addWidget(userEdit); + layout->addWidget(passLabel); + layout->addWidget(passEdit); + + layout->addStretch(1); + layout->addWidget(buttonBox); + setLayout(layout); + + setWindowTitle(tr("TermTCP Host Configuration")); +} + +void AGWDialog::myaccept() +{ + QVariant Q; + + int OldEnable = AGWEnable; + int OldPort = AGWPortNum; + char oldHost[128]; + strcpy(oldHost, AGWHost); + + // QString val = Sess->portNo->text();A + // QByteArray qb = val.toLatin1(); + // char * ptr = qb.data(); + + AGWEnable = AAGWEnable->isChecked(); + AGWMonEnable = AAGWMonEnable->isChecked(); + + strcpy(AGWTermCall, TermCall->text().toUtf8().toUpper()); + strcpy(AGWBeaconDest, beaconDest->text().toUtf8().toUpper()); + strcpy(AGWBeaconPath, beaconPath->text().toUtf8().toUpper()); + strcpy(AGWBeaconPorts, beaconPorts->text().toUtf8().toUpper()); + + if (beaconText->toPlainText().length() > 256) + { + QMessageBox msgBox; + msgBox.setText("Beacon Text Too Long"); + msgBox.exec(); + } + else + strcpy(AGWBeaconMsg, beaconText->toPlainText().toUtf8().toUpper()); + + Q = beaconInterval->text(); + AGWBeaconInterval = Q.toInt(); + + strcpy(AGWHost, AHost->text().toUtf8()); + + Q = APort->text(); + AGWPortNum = Q.toInt(); + + Q = Paclen->text(); + AGWPaclen = Q.toInt(); + + SaveSettings(); + + if (AGWEnable != OldEnable || AGWPortNum != OldPort || strcmp(oldHost, AGWHost) != 0) + { + // (re)start connection + + if (OldEnable && AGWSock && AGWSock->ConnectedState == QAbstractSocket::ConnectedState) + { + AGWSock->disconnectFromHost(); + Status1->setText("AGW Disconnected"); + } + // AGWTimer will reopen connection + } + + myStatusBar->setVisible(AGWEnable | VARAEnable | KISSEnable); + + AGWDialog::accept(); + +} + +void AGWDialog::myreject() +{ + AGWDialog::reject(); +} + +void TabDialog::myaccept() +{ + QString val = hostEdit->text(); + QByteArray qb = val.toLatin1(); + char * ptr = qb.data(); + strcpy(Host[ConfigHost], ptr); + + val = portEdit->text(); + qb = val.toLatin1(); + ptr = qb.data(); + Port[ConfigHost] = atoi(ptr); + + val = userEdit->text(); + qb = val.toLatin1(); + ptr = qb.data(); + strcpy(UserName[ConfigHost], ptr); + + val = passEdit->text(); + qb = val.toLatin1(); + ptr = qb.data(); + strcpy(Password[ConfigHost], ptr); + + actHost[ConfigHost]->setText(Host[ConfigHost]); + actSetup[ConfigHost]->setText(Host[ConfigHost]); + + SaveSettings(); + + + TabDialog::accept(); + +} + +void TabDialog::myreject() +{ + TabDialog::reject(); +} + +TabDialog::~TabDialog() +{ +} + +// Menu dialog + + +fontDialog::fontDialog(int Menu, QWidget *parent) : QDialog(parent) +{ + // Menu is set if setting Menufont, zero for setting terminal font. + + int i; + char valChar[16]; + + QString family; + int csize; + QFont::Weight weight; + +#ifdef ANDROID + this->resize((screenWidth * 7) / 8, 200); + this->setMaximumWidth((screenWidth * 7) / 8); +#endif + this->setFont(*menufont); + + Menuflag = Menu; + + if (Menu) + { + workingFont = *menufont; + + QFontInfo info(*menufont); + family = info.family(); + csize = info.pointSize(); + + setWindowTitle("Menu Font Dialog"); + } + else + { + // get current term font + + QSettings settings(GetConfPath(), QSettings::IniFormat); + +#ifdef ANDROID + family = settings.value("FontFamily", "Driod Sans Mono").toString(); + csize = settings.value("PointSize", 12).toInt(); + weight = (QFont::Weight)settings.value("Weight", 50).toInt(); +#else + family = settings.value("FontFamily", "Courier New").toString(); + csize = settings.value("PointSize", 10).toInt(); + weight = (QFont::Weight)settings.value("Weight", 50).toInt(); +#endif + + workingFont = QFont(family); + workingFont.setPointSize(csize); + workingFont.setWeight(weight); + + setWindowTitle("Terminal Font Dialog"); + } + + QVBoxLayout *layout = new QVBoxLayout(); + layout->setContentsMargins(10, 10, 10, 10); + + setLayout(layout); + + QHBoxLayout *hlayout = new QHBoxLayout(); + layout->addLayout(hlayout); + + font = new QFontComboBox(); + + if (Menu == 0) + font->setFontFilters(QFontComboBox::MonospacedFonts); + + font->setMaximumWidth((screenWidth * 5) / 8); + font->view()->setMaximumWidth((7 * screenWidth) / 8); + + style = new QComboBox(); + style->setMaximumWidth(screenWidth / 4); + size = new QComboBox(); + sample = new QTextEdit(); + sample->setText("ABCDabcd1234"); + sample->setFont(workingFont); + + hlayout->addWidget(font); + hlayout->addWidget(style); + hlayout->addWidget(size); + layout->addWidget(sample); + + QFontDatabase database; + + const QStringList styles = database.styles(family); + + const QList smoothSizes = database.smoothSizes(family, styles[0]); + + for (int points : smoothSizes) + size->addItem(QString::number(points)); + + for (QString xstyle : styles) + style->addItem(xstyle); + + i = font->findText(family, Qt::MatchExactly); + font->setCurrentIndex(i); + + sprintf(valChar, "%d", csize); + i = size->findText(valChar, Qt::MatchExactly); + size->setCurrentIndex(i); + + buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + buttonBox->setFont(*menufont); + layout->addWidget(buttonBox); + setLayout(layout); + + connect(buttonBox, SIGNAL(accepted()), this, SLOT(myaccept())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(myreject())); + connect(font, SIGNAL(currentFontChanged(QFont)), this, SLOT(fontchanged(QFont))); + connect(style, SIGNAL(currentIndexChanged(int)), this, SLOT(stylechanged())); + connect(size, SIGNAL(currentIndexChanged(int)), this, SLOT(sizechanged())); +} + +void fontDialog::fontchanged(QFont newfont) +{ + QFontDatabase database; + QString family = newfont.family(); + + workingFont = newfont; + + const QStringList styles = database.styles(family); + const QList smoothSizes = database.smoothSizes(family, styles[0]); + + size->clear(); + style->clear(); + + for (int points : smoothSizes) + size->addItem(QString::number(points)); + + for (QString xstyle : styles) + style->addItem(xstyle); + + sample->setFont(workingFont); +} + +void fontDialog::stylechanged() +{ + QFontDatabase database; + + QString family = font->currentFont().family(); + + bool italic = database.italic(family, style->currentText()); + int weight = database.weight(family, style->currentText()); + + if (weight < 0) + weight = 50; // Normal + + workingFont.setItalic(italic); + workingFont.setWeight((QFont::Weight) weight); + + sample->setFont(workingFont); +} + +void fontDialog::sizechanged() +{ + int newsize = size->currentText().toInt(); + workingFont.setPointSize(newsize); + sample->setFont(workingFont); +} +fontDialog::~fontDialog() +{ +} + +void fontDialog::myaccept() +{ + QSettings settings(GetConfPath(), QSettings::IniFormat); + + if (Menuflag) + { + delete menufont; + menufont = new QFont(workingFont); + + QtTermTCP::setFonts(); + + settings.setValue("MFontFamily", workingFont.family()); + settings.setValue("MPointSize", workingFont.pointSize()); + settings.setValue("MWeight", workingFont.weight()); + } + else + { + Ui_ListenSession * Sess; + + for (int i = 0; i < _sessions.size(); ++i) + { + Sess = _sessions.at(i); + + if (Sess->termWindow) + Sess->termWindow->setFont(workingFont); + + if (Sess->inputWindow) + Sess->inputWindow->setFont(workingFont); + + if (Sess->monWindow) + Sess->monWindow->setFont(workingFont); + } + + settings.setValue("FontFamily", workingFont.family()); + settings.setValue("PointSize", workingFont.pointSize()); + settings.setValue("Weight", workingFont.weight()); + } + + fontDialog::accept(); +} + +void fontDialog::myreject() +{ + fontDialog::reject(); +} + + + +ListenDialog::ListenDialog(QWidget *parent) : QDialog(parent) +{ +#ifdef ANDROID + this->resize((screenWidth * 3) / 4 , 500); +#endif + verticalLayout = new QVBoxLayout(); + verticalLayout->setContentsMargins(10, 10, 10, 10); + + setLayout(verticalLayout); + + Enabled = new QCheckBox(); + Enabled->setText(QString::fromUtf8("Enable Listen")); + Enabled->setLayoutDirection(Qt::LeftToRight); + + verticalLayout->addWidget(Enabled); + + formLayout = new QFormLayout(); + + portNo = new QLineEdit(); +// portNo->setMaximumSize(QSize(100, 30)); + + formLayout->addRow(new QLabel("Port"), portNo); + + CText = new QTextEdit(); + CText->setMinimumSize(QSize(0, 150)); + CText->setMaximumSize(QSize(401, 150)); + + formLayout->addRow(new QLabel("CText"), CText); + buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + buttonBox->setFont(*menufont); + verticalLayout->addWidget(buttonBox); + + connect(buttonBox, SIGNAL(accepted()), this, SLOT(myaccept())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(myreject())); + + verticalLayout->addLayout(formLayout); + verticalLayout->addWidget(buttonBox); + + portNo->setText(QString::number(listenPort)); + Enabled->setChecked(listenEnable); + CText->setText(listenCText); + +} + +ListenDialog::~ListenDialog() +{ +} + +void ListenDialog::myaccept() +{ + QString val = portNo->text(); + QByteArray qb = val.toLatin1(); + char * ptr = qb.data(); + + listenPort = atoi(ptr); + listenEnable = Enabled->isChecked(); + strcpy(listenCText, CText->toPlainText().toUtf8()); + + while ((ptr = strchr(listenCText, '\n'))) + *ptr = '\r'; + + if (_server->isListening()) + _server->close(); + + SaveSettings(); + + if (listenEnable) + _server->listen(QHostAddress::Any, listenPort); + + ListenDialog::accept(); +} + +void ListenDialog::myreject() +{ + ListenDialog::reject(); +} diff --git a/TabDialog.h b/TabDialog.h new file mode 100644 index 0000000..a7ac986 --- /dev/null +++ b/TabDialog.h @@ -0,0 +1,208 @@ + +#ifndef TABDIALOG_H +#define TABDIALOG_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class QDialogButtonBox; +class QFileInfo; +class QTabWidget; + +namespace Ui { +class TabDialog; +} + +class ListenDialog: public QDialog +{ + Q_OBJECT + +public: + explicit ListenDialog(QWidget *parent = 0); + ~ListenDialog(); + +private slots: + void myaccept(); + void myreject(); + +public: + QVBoxLayout *verticalLayout; + QFormLayout *formLayout; + QLineEdit *portNo; + QTextEdit *CText; + QVBoxLayout *verticalLayout_2; + QDialogButtonBox *buttonBox; + QCheckBox *Enabled; + + +}; + + + +class TabDialog : public QDialog +{ + Q_OBJECT + +public: + explicit TabDialog(QWidget *parent = 0); + ~TabDialog(); + +private slots: + void myaccept(); + void myreject(); + +private: + // Ui::TabDialog *ui; + QDialogButtonBox *buttonBox; +}; + +class AGWDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AGWDialog(QWidget *parent = 0); + ~AGWDialog(); + +public: + QPushButton *okButton; + QPushButton *cancelButton; + +private slots: + void myaccept(); + void myreject(); + +private: + // Ui::TabDialog *ui; + QDialogButtonBox *buttonBox; +}; + +class AGWConnect : public QDialog +{ + Q_OBJECT + +public: + explicit AGWConnect(QWidget *parent = 0); + ~AGWConnect(); + +public: + QLineEdit * wCallFrom; + QComboBox * wCallTo; + QLineEdit * Digis; + QListWidget * RadioPorts; + +private slots: + void myaccept(); + void myreject(); + +private: + // Ui::TabDialog *ui; + QDialogButtonBox *buttonBox; +}; + + + +class VARAConnect : public QDialog +{ + Q_OBJECT + +public: + explicit VARAConnect(QWidget *parent = 0); + ~VARAConnect(); + +public: + QLineEdit * wCallFrom; + QComboBox * wCallTo; + QLineEdit * Digis; + QListWidget * RadioPorts; + +private slots: + void myaccept(); + void myreject(); + +private: + // Ui::TabDialog *ui; + QDialogButtonBox *buttonBox; +}; + + +class KISSConnect : public QDialog +{ + Q_OBJECT + +public: + explicit KISSConnect(QWidget *parent = 0); + ~KISSConnect(); + +public: + QLineEdit * wCallFrom; + QComboBox * wCallTo; + QLineEdit * Digis; + QLineEdit * UIDest; + QListWidget * RadioPorts; + QHBoxLayout *mylayout; + QRadioButton * Connected; + QRadioButton * UIMode; + +private slots: + void myaccept(); + void myreject(); + +private: + // Ui::TabDialog *ui; + QDialogButtonBox *buttonBox; +}; + + + +class fontDialog : public QDialog +{ + Q_OBJECT + +public: + explicit fontDialog(int Menu, QWidget *parent = 0); + ~fontDialog(); + +public: + QFontComboBox *font; + QComboBox *style; + QComboBox *size; + QTextEdit * sample; + QFont workingFont; + int workingSize; + int Menuflag; // Set if menu font + +private slots: + void myaccept(); + void myreject(); + void fontchanged(QFont); + void stylechanged(); + void sizechanged(); + +private: + + QDialogButtonBox *buttonBox; +}; + +#endif + +class myResize : public QObject +{ + Q_OBJECT + +protected: + bool eventFilter(QObject *obj, QEvent *event) override; +}; + + + + diff --git a/TermTCPCommon.cpp b/TermTCPCommon.cpp new file mode 100644 index 0000000..217e122 --- /dev/null +++ b/TermTCPCommon.cpp @@ -0,0 +1,1091 @@ +#include +#include +#include +#include +#include + +#include "QtTermTCP.h" + +#define _CRT_SECURE_NO_WARNINGS + +#define TRUE 1 +#define FALSE 0 +#define UCHAR unsigned char +#define MAX_PATH 256 +#define WCHAR char + +#ifndef WIN32 +#define strtok_s strtok_r +#endif + + +typedef unsigned long DWORD; +typedef int BOOL; +typedef unsigned char BYTE; +typedef unsigned short WORD; + +#define MAXHOSTS 16 + +#define TCHAR char + +void QueueMsg(Ui_ListenSession * Sess, char * Msg, int Len); +int ProcessYAPPMessage(Ui_ListenSession * Sess, UCHAR * Msg, int Len); +void SetPortMonLine(int i, char * Text, int visible, int enabled); +void AGW_AX25_data_in(void * Sess, UCHAR * data, int Len); +int checkUTF8(unsigned char * Msg, int Len, unsigned char * out); + +int Bells = TRUE; +int StripLF = FALSE; +int LogMonitor = FALSE; +int LogOutput = FALSE; +int SendDisconnected = TRUE; +int ChatMode = TRUE; +int MonPorts = 1; +int ListenOn = FALSE; + +time_t LastWrite = 0xffffffff; +int AlertInterval = 300; +int AlertBeep = TRUE; +int AlertFreq = 600; +int AlertDuration = 250; +TCHAR AlertFileName[256] = { 0 }; +int ConnectBeep = TRUE; +int UseKeywords = TRUE; + +char KeyWordsName[MAX_PATH] = "Keywords.sys"; +char ** KeyWords = NULL; +int NumberofKeyWords = 0; + + + +// YAPP stuff + +#define SOH 1 +#define STX 2 +#define ETX 3 +#define EOT 4 +#define ENQ 5 +#define ACK 6 +#define DLE 0x10 +#define NAK 0x15 +#define CAN 0x18 + +#define YAPPTX 32768 // Sending YAPP file + +int MaxRXSize = 100000; +char BaseDir[256] = ""; + +unsigned char InputBuffer[1024]; + +char YAPPPath[MAX_PATH] = ""; // Path for saving YAPP Files + +int paclen = 128; + +int InputLen; // Data we have already = Offset of end of an incomplete packet; + +unsigned char * MailBuffer; // Yapp Message being received +int MailBufferSize; +int YAPPLen; // Bytes sent/received of YAPP Message +long YAPPDate; // Date for received file - if set enables YAPPC +char ARQFilename[200]; // Filename from YAPP Header + +unsigned char SavedData[8192]; // Max receive is 4096 is should never get more that 8k +int SaveLen = 0; + +void YAPPSendData(Ui_ListenSession * Sess); + + +char * strlop(char * buf, char delim) +{ + // Terminate buf at delim, and return rest of string + + char * ptr = strchr(buf, delim); + + if (ptr == NULL) return NULL; + + *(ptr)++ = 0; + + return ptr; +} + +int CheckKeyWord(char * Word, char * Msg) +{ + char * ptr1 = Msg, *ptr2; + int len = (int)strlen(Word); + + while (*ptr1) // Stop at end + { + ptr2 = strstr(ptr1, Word); + + if (ptr2 == NULL) + return FALSE; // OK + + // Only bad if it ia not part of a longer word + + if ((ptr2 == Msg) || !(isalpha(*(ptr2 - 1)))) // No alpha before + if (!(isalpha(*(ptr2 + len)))) // No alpha after + return TRUE; // Bad word + + // Keep searching + + ptr1 = ptr2 + len; + } + + return FALSE; // OK +} + +int CheckKeyWords(UCHAR * Msg, int len) +{ + char dupMsg[2048]; + int i; + + if (UseKeywords == 0 || NumberofKeyWords == 0) + return FALSE; + + memcpy(dupMsg, Msg, len); + dupMsg[len] = 0; + //_strlwr(dupMsg); + + for (i = 1; i <= NumberofKeyWords; i++) + { + if (CheckKeyWord(KeyWords[i], dupMsg)) + { +// Beep(660, 250); + return TRUE; // Alert + } + } + + return FALSE; // OK + +} + + +void ProcessReceivedData(Ui_ListenSession * Sess, unsigned char * Buffer, int len) +{ + int MonLen = 0; + unsigned char * ptr; + unsigned char * Buffptr; + unsigned char * FEptr = 0; + + if (Sess->InputMode == 'Y') // Yapp + { + ProcessYAPPMessage(Sess, Buffer, len); + return; + } + + // mbstowcs(Buffer, BufferB, len); + + // Look for MON delimiters (FF/FE) + + + Buffptr = Buffer; + + if (Sess->MonData) + { + // Already in MON State + + FEptr = (UCHAR *)memchr(Buffptr, 0xfe, len); + + if (!FEptr) + { + // no FE - so send all to monitor + + WritetoMonWindow(Sess, Buffer, len); + return; + } + + Sess->MonData = FALSE; + + MonLen = FEptr - Buffptr; // Mon Data, Excluding the FE + + WritetoMonWindow(Sess, Buffptr, MonLen); + + Buffptr = ++FEptr; // Char following FE + + if (++MonLen < len) + { + len -= MonLen; + goto MonLoop; // See if next in MON or Data + } + + // Nothing Left + + return; + } + +MonLoop: + + ptr = (UCHAR *)memchr(Buffptr, 0xff, len); + + if (ptr) + { + unsigned char telcmd[] = "\xff\xfb\x03\xff\xfb\x01"; + + // Try to trap connect to normal Telnet Port + + if (memcmp(ptr, telcmd, 6) == 0) + return; + + // Buffer contains Mon Data + + if (ptr > Buffptr) + { + // Some Normal Data before the FF + + int NormLen = ptr - Buffptr; // Before the FF + + if (NormLen == 1 && Buffptr[0] == 0) + { + // Keepalive + } + + else + { + CheckKeyWords(Buffptr, NormLen); + WritetoOutputWindow(Sess, Buffptr, NormLen); + } + + len -= NormLen; + Buffptr = ptr; + goto MonLoop; + } + + if (ptr[1] == 0xff) + { + // Port Definition String + + int NumberofPorts = atoi((char *)&ptr[2]); + char *p, *Context; + int i = 1; + TCHAR msg[80]; + int portnum; + char delim[] = "|"; + int m; + + // Save for changes of Window + + if (len < 1024) + memcpy(Sess->PortMonString, ptr, len); + + + // Remove old menu + + for (i = 0; i < 33; i++) + { + SetPortMonLine(i, (char *)"", 0, 0); + } + + p = strtok_s((char *)&ptr[2], delim, &Context); + + while (NumberofPorts--) + { + p = strtok_s(NULL, delim, &Context); + if (p == NULL) + break; + + m = portnum = atoi(p); + sprintf(msg, "Port %s", p); + + if (m == 0) + m = 33; + + if (Sess->portmask & (1ll << (m - 1))) + SetPortMonLine(portnum, msg, 1, 1); + else + SetPortMonLine(portnum, msg, 1, 0); + } + return; + } + + + Sess->MonData = 1; + + FEptr = (UCHAR *)memchr(Buffptr, 0xfe, len); + + if (FEptr) + { + Sess->MonData = 0; + + MonLen = FEptr + 1 - Buffptr; // MonLen includes FF and FE + + WritetoMonWindow(Sess, Buffptr + 1, MonLen - 2); + + len -= MonLen; + Buffptr += MonLen; // Char Following FE + + if (len <= 0) + { + return; + } + goto MonLoop; + } + else + { + // No FE, so rest of buffer is MON Data + + if (MonLen) + WritetoMonWindow(Sess, Buffptr + 1, MonLen - 1); + return; + } + } + + // No FF, so must be session data + + if (Sess->InputMode == 'Y') // Yapp + { + ProcessYAPPMessage(Sess, Buffer, len); + return; + } + + + if (len == 1 && Buffptr[0] == 0) + return; // Keepalive + + // Could be a YAPP Header + + if (len == 2 && Buffptr[0] == ENQ && Buffptr[1] == 1) // YAPP Send_Init + { + char YAPPRR[2]; + + // Turn off monitoring + + setTraceOff(Sess); + + Sess->InputMode = 'Y'; + + YAPPRR[0] = ACK; + YAPPRR[1] = 1; + + SocketFlush(Sess); // To give Monitor Msg time to be sent + mySleep(1000); + QueueMsg(Sess, YAPPRR, 2); + + return; + } + // Check UTF8 + { + CheckKeyWords(Buffptr, len); + WritetoOutputWindow(Sess, Buffptr, len); + } + Sess->SlowTimer = 0; + return; +} + +extern myTcpSocket * VARASock; +extern myTcpSocket * VARADataSock; +extern "C" void SendtoAX25(void * conn, unsigned char * Msg, int Len); + +int SendMsg(Ui_ListenSession * Sess, TCHAR * Buffer, int len) +{ + if (Sess->KISSSession) + { + // Send to ax.25 code + + SendtoAX25(Sess->KISSSession, (unsigned char *)Buffer, len); + return len; + } + else if (Sess->AGWSession) + { + // Terminal is in AGWPE mode - send as AGW frame + + AGW_AX25_data_in(Sess->AGWSession, (unsigned char *)Buffer, len); + return len; + } + else if (VARASock && VARASock->Sess == Sess) + { + VARADataSock->write(Buffer, len); + return len; + } + + return SocketSend(Sess, Buffer, len); +} + + + +void QueueMsg(Ui_ListenSession * Sess, char * Msg, int len) +{ + int Sent = SendMsg(Sess, Msg, len); + + if (Sent != len) + Sent = 0; +} + +int InnerProcessYAPPMessage(Ui_ListenSession * Sess, UCHAR * Msg, int Len); + +int ProcessYAPPMessage(Ui_ListenSession * Sess, UCHAR * Msg, int Len) +{ + // may have saved data + + memcpy(&SavedData[SaveLen], Msg, Len); + + SaveLen += Len; + + while (SaveLen && Sess->InputMode == 'Y') + { + int Used = InnerProcessYAPPMessage(Sess, SavedData, SaveLen); + + if (Used == 0) + return 0; // Waiting for more + + SaveLen -= Used; + + if (SaveLen) + memmove(SavedData, &SavedData[Used], SaveLen); + } + return 0; +} + +extern int VARAEnable; + +int InnerProcessYAPPMessage(Ui_ListenSession * Sess, UCHAR * Msg, int Len) +{ + int pktLen = Msg[1]; + char Reply[2] = { ACK }; + size_t NameLen, SizeLen, OptLen; + char * ptr; + int FileSize; + WCHAR MsgFile[MAX_PATH]; + FILE * hFile; + char Mess[255]; + int len; + UCHAR Buffer[2000]; + struct stat STAT; + + switch (Msg[0]) + { + case ENQ: // YAPP Send_Init + + // Shouldn't occur in session. Reset state and process + + if (MailBuffer) + { + free(MailBuffer); + MailBufferSize = 0; + MailBuffer = 0; + } + + Mess[0] = ACK; + Mess[1] = 1; + + Sess->InputMode = 'Y'; + QueueMsg(Sess, Mess, 2); + + // Turn off monitoring + + mySleep(1000); // To give YAPP Msg time to be sent + + setTraceOff(Sess); + + return Len; + + case SOH: + + // HD Send_Hdr SOH len (Filename) NUL (File Size in ASCII) NUL (Opt) + + // YAPPC has date/time in dos format + + if (Len < Msg[1] + 1) + return 0; // Wait till we have it all + + NameLen = strlen((char *)&Msg[2]); + strcpy(ARQFilename, (char *)&Msg[2]); + + ptr = (char *)&Msg[3 + NameLen]; + SizeLen = strlen(ptr); + FileSize = atoi(ptr); + + OptLen = pktLen - (NameLen + SizeLen + 2); + + YAPPDate = 0; + + if (OptLen >= 8) // We have a Date/Time for YAPPC + { + ptr = ptr + SizeLen + 1; + YAPPDate = strtol(ptr, NULL, 16); + } + + // Check Size + + if (FileSize > MaxRXSize && VARAEnable == 0) + { + Mess[0] = NAK; + Mess[1] = sprintf(&Mess[2], "File %s size %d larger than limit %d\r", ARQFilename, FileSize, MaxRXSize); + mySleep(1000); // To give YAPP Msg tome to be sent + QueueMsg(Sess, Mess, Mess[1] + 2); + + len = sprintf((char *)Buffer, "YAPP File %s size %d larger than limit %d\r", ARQFilename, FileSize, MaxRXSize); + WritetoOutputWindow(Sess, Buffer, len); + + Sess->InputMode = 0; + SendTraceOptions(Sess); + + return Len; + } + + // Check that Path is set + + if (YAPPPath[0] == 0) + { + Mess[0] = NAK; + Mess[1] = sprintf(&Mess[2], "%s", "YAPP Receive directory not set"); + mySleep(1000); // To give YAPP Msg time to be sent + QueueMsg(Sess, Mess, Mess[1] + 2); + len = sprintf((char *)Buffer, "YAPP File Receive Failed - YAPP Receive directory not set\r"); + WritetoOutputWindow(Sess, Buffer, len); + + Sess->InputMode = 0; + SendTraceOptions(Sess); + + return Len; + } + + // Make sure file does not exist + + sprintf(MsgFile, "%s/%s", YAPPPath, ARQFilename); + + if (stat(MsgFile, &STAT) == 0) + { + FileSize = STAT.st_size; + + Mess[0] = NAK; + Mess[1] = sprintf(&Mess[2], "%s", "File Already Exists"); + mySleep(1000); // To give YAPP Msg time to be sent + QueueMsg(Sess, Mess, Mess[1] + 2); + len = sprintf((char *)Buffer, "YAPP File Receive Failed - %s already exists\r", MsgFile); + WritetoOutputWindow(Sess, Buffer, len); + + Sess->InputMode = 0; + SendTraceOptions(Sess); + + return Len; + } + + + MailBufferSize = FileSize; + MailBuffer = (UCHAR *)malloc(FileSize); + YAPPLen = 0; + + if (YAPPDate) // If present use YAPPC + Reply[1] = ACK; //Receive_TPK + else + Reply[1] = 2; //Rcv_File + + QueueMsg(Sess, Reply, 2); + + len = sprintf((char *)Buffer, "YAPP Receving File %s size %d\r", ARQFilename, FileSize); + WritetoOutputWindow(Sess, Buffer, len); + + return Len; + + case STX: + + // Data Packet + + // Check we have it all + + if (YAPPDate) // If present use YAPPC so have checksum + { + if (pktLen > (Len - 3)) // -2 for header and checksum + return 0; // Wait for rest + } + else + { + if (pktLen > (Len - 2)) // -2 for header + return 0; // Wait for rest + } + + // Save data and remove from buffer + + // if YAPPC check checksum + + if (YAPPDate) + { + UCHAR Sum = 0; + int i; + UCHAR * uptr = &Msg[2]; + + i = pktLen; + + while (i--) + Sum += *(uptr++); + + if (Sum != *uptr) + { + // Checksum Error + + Mess[0] = CAN; + Mess[1] = sprintf(&Mess[2], "YAPPC Checksum Error"); + QueueMsg(Sess, Mess, Mess[1] + 2); + + len = sprintf((char *)Buffer, "YAPPC Checksum Error on file %s\r", MsgFile); + WritetoOutputWindow(Sess, Buffer, len); + + Sess->InputMode = 0; + SendTraceOptions(Sess); + return Len; + } + } + + if ((YAPPLen) + pktLen > MailBufferSize) + { + // Too Big ?? + + Mess[0] = CAN; + Mess[1] = sprintf(&Mess[2], "YAPP Too much data received"); + QueueMsg(Sess, Mess, Mess[1] + 2); + + len = sprintf((char *)Buffer, "YAPP Too much data received on file %s\r", MsgFile); + WritetoOutputWindow(Sess, Buffer, len); + + Sess->InputMode = 0; + SendTraceOptions(Sess); + return Len; + } + + + memcpy(&MailBuffer[YAPPLen], &Msg[2], pktLen); + YAPPLen += pktLen; + + if (YAPPDate) + ++pktLen; // Add Checksum + +// if (YAPPLen == MailBufferSize) +// pktLen = pktLen; + + return pktLen + 2; + + case ETX: + + // End Data + + if (YAPPLen == MailBufferSize) + { + // All received + + int Written = 0; + + sprintf(MsgFile, "%s/%s", YAPPPath, ARQFilename); + + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + Written = (int)fwrite(MailBuffer, 1, YAPPLen, hFile); + fclose(hFile); + + if (YAPPDate) + { +// struct tm TM; +// struct timeval times[2]; + /* + The MS-DOS date. The date is a packed value with the following format. + + cant use DosDateTimeToFileTime on Linux + + Bits Description + 0-4 Day of the month (1–31) + 5-8 Month (1 = January, 2 = February, and so on) + 9-15 Year offset from 1980 (add 1980 to get actual year) + wFatTime + The MS-DOS time. The time is a packed value with the following format. + Bits Description + 0-4 Second divided by 2 + 5-10 Minute (0–59) + 11-15 Hour (0–23 on a 24-hour clock) + */ + /* + + memset(&TM, 0, sizeof(TM)); + + TM.tm_sec = (YAPPDate & 0x1f) << 1; + TM.tm_min = ((YAPPDate >> 5) & 0x3f); + TM.tm_hour = ((YAPPDate >> 11) & 0x1f); + + TM.tm_mday = ((YAPPDate >> 16) & 0x1f); + TM.tm_mon = ((YAPPDate >> 21) & 0xf) - 1; + TM.tm_year = ((YAPPDate >> 25) & 0x7f) + 80; + + Debugprintf("%d %d %d %d %d %d", TM.tm_year, TM.tm_mon, TM.tm_mday, TM.tm_hour, TM.tm_min, TM.tm_sec); + + times[0].tv_sec = times[1].tv_sec = mktime(&TM); + times[0].tv_usec = times[1].tv_usec = 0; + */ + } + } + + + free(MailBuffer); + MailBufferSize = 0; + MailBuffer = 0; + + if (Written != YAPPLen) + { + Mess[0] = CAN; + Mess[1] = sprintf(&Mess[2], "Failed to save YAPP File"); + QueueMsg(Sess, Mess, Mess[1] + 2); + + len = sprintf((char *)Buffer, "Failed to save YAPP File %s\r", MsgFile); + WritetoOutputWindow(Sess, Buffer, len); + + Sess->InputMode = 0; + SendTraceOptions(Sess); + } + } + + Reply[1] = 3; //Ack_EOF + QueueMsg(Sess, Reply, 2); + + len = sprintf((char *)Buffer, "Reception of file %s complete\r", MsgFile); + WritetoOutputWindow(Sess, Buffer, len); + + return Len; + + case EOT: + + // End Session + + Reply[1] = 4; // Ack_EOT + QueueMsg(Sess, Reply, 2); + SocketFlush(Sess); + Sess->InputMode = 0; + + SendTraceOptions(Sess); + return Len; + + case CAN: + + // Abort + + Mess[0] = ACK; + Mess[1] = 5; // CAN Ack + QueueMsg(Sess, Mess, 2); + + if (MailBuffer) + { + free(MailBuffer); + MailBufferSize = 0; + MailBuffer = 0; + } + + // May have an error message + + len = Msg[1]; + + if (len) + { + len = sprintf((char *)Buffer, "YAPP Transfer cancelled - %s\r", &Msg[2]); + } + else + len = sprintf(Mess, "YAPP Transfer cancelled\r"); + + Sess->InputMode = 0; + SendTraceOptions(Sess); + + return Len; + + case ACK: + + switch (Msg[1]) + { + char * ptr; + + case 1: // Rcv_Rdy + + // HD Send_Hdr SOH len (Filename) NUL (File Size in ASCII) NUL (Opt) + + // Remote only needs filename so remove path + + ptr = ARQFilename; + + while (strchr(ptr, '/')) + ptr = strchr(ptr, '/') + 1; + + len = (int)strlen(ptr) + 3; + + strcpy(&Mess[2], ptr); + len += sprintf(&Mess[len], "%d", MailBufferSize); + len++; // include null +// len += sprintf(&Mess[len], "%8X", YAPPDate); +// len++; // include null + Mess[0] = SOH; + Mess[1] = len - 2; + + QueueMsg(Sess, Mess, len); + + return Len; + + case 2: + + YAPPDate = 0; // Switch to Normal (No Checksum) Mode + + case 6: // Send using YAPPC + + // Start sending message + + YAPPSendData(Sess); + return Len; + + case 3: + + // ACK EOF - Send EOT + + Mess[0] = EOT; + Mess[1] = 1; + QueueMsg(Sess, Mess, 2); + + return Len; + + case 4: + + // ACK EOT + + Sess->InputMode = 0; + SendTraceOptions(Sess); + + len = sprintf((char *)Buffer, "File transfer complete\r"); + WritetoOutputWindow(Sess, Buffer, len); + + + return Len; + + default: + return Len; + + } + + case NAK: + + // Either Reject or Restart + + // RE Resume NAK len R NULL (File size in ASCII) NULL + + if (Len > 2 && Msg[2] == 'R' && Msg[3] == 0) + { + int posn = atoi((char *)&Msg[4]); + + YAPPLen += posn; + MailBufferSize -= posn; + + YAPPSendData(Sess); + return Len; + + } + + // May have an error message + + len = Msg[1]; + + if (len) + { + char ws[256]; + + Msg[len + 2] = 0; + + strcpy(ws, (char *)&Msg[2]); + + len = sprintf((char *)Buffer, "File rejected - %s\r", ws); + } + else + len = sprintf((char *)Buffer, "File rejected\r"); + + WritetoOutputWindow(Sess, Buffer, len); + + + Sess->InputMode = 0; + SendTraceOptions(Sess); + + return Len; + + } + + len = sprintf((char *)Buffer, "Unexpected message during YAPP Transfer. Transfer cancelled\r"); + WritetoOutputWindow(Sess, Buffer, len); + + Sess->InputMode = 0; + SendTraceOptions(Sess); + + return Len; + +} + +void YAPPSendFile(Ui_ListenSession * Sess, WCHAR * FN) +{ + int FileSize = 0; + char MsgFile[MAX_PATH]; + FILE * hFile; + struct stat STAT; + UCHAR Buffer[2000]; + int Len; + + strcpy(MsgFile, FN); + + if (MsgFile[0] == 0) + { + Len = sprintf((char *)Buffer, "Filename missing\r"); + WritetoOutputWindow(Sess, Buffer, Len); + + SendTraceOptions(Sess); + + return; + } + + if (stat(MsgFile, &STAT) != -1) + { + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile) + { + char Mess[255]; +// time_t UnixTime = STAT.st_mtime; + +// FILETIME ft; +// long long ll; +// SYSTEMTIME st; +// WORD FatDate; +// WORD FatTime; +// struct tm TM; + + strcpy(ARQFilename, MsgFile); + + if (MailBuffer) + { + free(MailBuffer); + MailBufferSize = 0; + MailBuffer = 0; + } + + MailBuffer = (UCHAR *)malloc(FileSize); + MailBufferSize = FileSize; + YAPPLen = 0; + fread(MailBuffer, 1, FileSize, hFile); + + // Get Date and Time for YAPPC Mode + +/* The MS-DOS date. The date is a packed value with the following format. + + cant use DosDateTimeToFileTime on Linux + + Bits Description + 0-4 Day of the month (1–31) + 5-8 Month (1 = January, 2 = February, and so on) + 9-15 Year offset from 1980 (add 1980 to get actual year) + wFatTime + The MS-DOS time. The time is a packed value with the following format. + Bits Description + 0-4 Second divided by 2 + 5-10 Minute (0–59) + 11-15 Hour (0–23 on a 24-hour clock) + + memset(&TM, 0, sizeof(TM)); + + TM.tm_sec = (YAPPDate & 0x1f) << 1; + TM.tm_min = ((YAPPDate >> 5) & 0x3f); + TM.tm_hour = ((YAPPDate >> 11) & 0x1f); + + TM.tm_mday = ((YAPPDate >> 16) & 0x1f); + TM.tm_mon = ((YAPPDate >> 21) & 0xf) - 1; + TM.tm_year = ((YAPPDate >> 25) & 0x7f) + 80; + + +// Note that LONGLONG is a 64-bit value + + ll = Int32x32To64(UnixTime, 10000000) + 116444736000000000; + ft.dwLowDateTime = (DWORD)ll; + ll >>= 32; + ft.dwHighDateTime = (DWORD)ll; + + FileTimeToSystemTime(&ft, &st); + FileTimeToDosDateTime(&ft, &FatDate, &FatTime); + + YAPPDate = (FatDate << 16) + FatTime; + + memset(&TM, 0, sizeof(TM)); + + TM.tm_sec = (YAPPDate & 0x1f) << 1; + TM.tm_min = ((YAPPDate >> 5) & 0x3f); + TM.tm_hour = ((YAPPDate >> 11) & 0x1f); + + TM.tm_mday = ((YAPPDate >> 16) & 0x1f); + TM.tm_mon = ((YAPPDate >> 21) & 0xf) - 1; + TM.tm_year = ((YAPPDate >> 25) & 0x7f) + 80; +*/ + fclose(hFile); + + Mess[0] = ENQ; + Mess[1] = 1; + + QueueMsg(Sess, Mess, 2); + Sess->InputMode = 'Y'; + + Len = sprintf((char *)Buffer, "Sending File %s ...\r", FN); + WritetoOutputWindow(Sess, Buffer, Len); + + return; + } + } + + Len = sprintf((char *)Buffer, "File %s not found\r", FN); + WritetoOutputWindow(Sess, Buffer, Len); + +} + +void YAPPSendData(Ui_ListenSession * Sess) +{ + char Mess[258]; + + while (1) + { + int Left = MailBufferSize; + + if (Left == 0) + { + // Finished - send End Data + + Mess[0] = ETX; + Mess[1] = 1; + + QueueMsg(Sess, Mess, 2); + + break; + } + + if (Left > paclen - 3) // two bytes header and possible checksum + Left = paclen - 3; + + memcpy(&Mess[2], &MailBuffer[YAPPLen], Left); + + YAPPLen += Left; + MailBufferSize -= Left; + + // if YAPPC add checksum + + if (YAPPDate) + { + UCHAR Sum = 0; + int i; + UCHAR * uptr = (UCHAR *)&Mess[2]; + + i = Left; + + while (i--) + Sum += *(uptr++); + + *(uptr) = Sum; + + Mess[0] = STX; + Mess[1] = Left; + + QueueMsg(Sess, Mess, Left + 3); + } + else + { + Mess[0] = STX; + Mess[1] = Left; + + QueueMsg(Sess, Mess, Left + 2); + } + } +} + diff --git a/UZ7HOUtils.c b/UZ7HOUtils.c new file mode 100644 index 0000000..839bb64 --- /dev/null +++ b/UZ7HOUtils.c @@ -0,0 +1,320 @@ +/* +Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO + +This file is part of QtSoundModem + +QtSoundModem 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. + +QtSoundModem 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 QtSoundModem. If not, see http://www.gnu.org/licenses + +*/ + +// UZ7HO Soundmodem Port by John Wiseman G8BPQ +#include "ax25.h" + +// TStringlist And String emulation Functions + +// Dephi seems to mix starting counts at 0 or 1. I'll try making everything +// base zero. + +// Initialise a list + +void CreateStringList(TStringList * List) +{ + List->Count = 0; + List->Items = 0; +} + + +int Count(TStringList * List) +{ + return List->Count; +} + +string * newString() +{ + // Creates and Initialises a string + + UCHAR * ptr = malloc(sizeof(string)); // Malloc Data separately so it can be ralloc'ed + string * New = (string *)ptr; + New->Length = 0; + New->AllocatedLength = 256; + New->Data = malloc(256); + + return New; +} + +void initString(string * S) +{ + S->Length = 0; + S->AllocatedLength = 256; + S->Data = malloc(256); +} + +void initTStringList(TStringList* T) +{ + //string * New = newString(); + + T->Count = 0; + T->Items = NULL; + + //Add(T, New); +} + + + +TStringList * newTStringList() +{ + TStringList * T = (TStringList *) malloc(sizeof(TStringList)); + string * New = newString(); + + T->Count = 0; + T->Items = NULL; + + Add(T, New); + + return T; +} + + +void freeString(string * Msg) +{ + if (Msg->Data) + free(Msg->Data); + + free(Msg); +} + +string * Strings(TStringList * Q, int Index) +{ + if (Index >= Q->Count) + return NULL; + + return Q->Items[Index]; +} + +int Add(TStringList * Q, string * Entry) +{ + Q->Items = realloc(Q->Items,(Q->Count + 1) * sizeof(void *)); + Q->Items[Q->Count++] = Entry; + + return (Q->Count); +} + + +void mydelete(string * Source, int StartChar, int Count) +{ + //Description + //The Delete procedure deletes up to Count characters from the passed parameter Source string starting + //from position StartChar. + + if (StartChar > Source->Length) + return; + + int left = Source->Length - StartChar; + + if (Count > left) + Count = left; + + memmove(&Source->Data[StartChar], &Source->Data[StartChar + Count], left - Count); + + Source->Length -= Count; +} + + +void Delete(TStringList * Q, int Index) +{ + // Remove item at Index and move rest up list + // Index starts at zero + + if (Index >= Q->Count) + return; + + // We should free it, so user must duplicate msg if needed after delete + + freeString(Q->Items[Index]); + + Q->Count--; + + while (Index < Q->Count) + { + Q->Items[Index] = Q->Items[Index + 1]; + Index++; + } +} + +void setlength(string * Msg, int Count) +{ + // Set length, allocating more space if needed + + if (Count > Msg->AllocatedLength) + { + Msg->AllocatedLength = Count + 256; + Msg->Data = realloc(Msg->Data, Msg->AllocatedLength); + } + + Msg->Length = Count; +} + +string * stringAdd(string * Msg, UCHAR * Chars, int Count) +{ + // Add Chars to string + + if (Msg->Length + Count > Msg->AllocatedLength) + { + Msg->AllocatedLength += Count + 256; + Msg->Data = realloc(Msg->Data, Msg->AllocatedLength); + } + + memcpy(&Msg->Data[Msg->Length], Chars, Count); + Msg->Length += Count; + + return Msg; +} + +void Clear(TStringList * Q) +{ + int i = 0; + + if (Q->Items == NULL) + return; + + while (Q->Count) + { + freeString(Q->Items[i++]); + Q->Count--; + } + + free(Q->Items); + + Q->Items = NULL; +} + +// procedure move ( const SourcePointer; var DestinationPointer; CopyCount : Integer ) ; +// Description +// The move procedure is a badly named method of copying a section of memory from one place to another. + +// CopyCount bytes are copied from storage referenced by SourcePointer and written to DestinationPointer + +void move(UCHAR * SourcePointer, UCHAR * DestinationPointer, int CopyCount) +{ + memmove(DestinationPointer, SourcePointer, CopyCount); +} + +void fmove(float * SourcePointer, float * DestinationPointer, int CopyCount) +{ + memmove(DestinationPointer, SourcePointer, CopyCount); +} + + + +//Description +//The copy function has 2 forms. In the first, it creates a new string from part of an existing string. In the second, it creates a new array from part of an existing array. + +//1.String copy + +//The first character of a string has index = 1. + +//Up to Count characters are copied from the StartChar of the Source string to the returned string. +//Less than Count characters if the end of the Source string is encountered before Count characters have been copied. + + +string * copy(string * Source, int StartChar, int Count) +{ + string * NewString = newString(); + int end = StartChar + Count; + + if (end > Source->Length) + Count = Source->Length - StartChar; + + memcpy(NewString->Data, &Source->Data[StartChar], Count); + + NewString->Length = Count; + + return NewString; +} + +// Duplicate from > to + +void Assign(TStringList * to, TStringList * from) +{ + int i; + + Clear(to); + + if (from->Count == 0) + return; + + // Duplicate each item + + for (i = 0; i < from->Count; i++) + { + string * new = newString(); + + stringAdd(new, from->Items[i]->Data, from->Items[i]->Length); + Add(to, new); + } +} + +string * duplicateString(string * in) +{ + string * new = newString(); + + stringAdd(new, in->Data, in->Length); + + return new; +} + + +double pila(double x) +{ + //x : = frac(x); The frac function returns the fractional part of a floating point number. + + double whole; + double rem; + + rem = modf(x, &whole); // returns fraction, writes whole to whole + + if (rem != rem) + rem = 0; + + if (rem > 0.5) + rem = 1 - rem; + + return 2 * rem; +} + +boolean compareStrings(string * a, string * b) +{ + if (a->Length == b->Length && memcmp(a->Data, b->Data, a->Length) == 0) + return TRUE; + + return FALSE; +} + +// This looks for a string in a stringlist. Returns index if found, otherwise -1 + +int my_indexof(TStringList * l, string * s) +{ + int i; + + for (i = 0; i < l->Count; i++) + { + // Need to compare count and data - C doesn't allow struct compare + + if (l->Items[i]->Length == s->Length && memcmp(l->Items[i]->Data, s->Data, s->Length) == 0) + return i; + } + return -1; +} + + \ No newline at end of file diff --git a/VARA.ui b/VARA.ui new file mode 100644 index 0000000..c5e4506 --- /dev/null +++ b/VARA.ui @@ -0,0 +1,148 @@ + + + AGWConnectDkg + + + + 0 + 0 + 500 + 400 + + + + Dialog + + + + + 10 + 10 + 480 + 380 + + + + + + + + + Call From + + + + + + + + + + Call To + + + + + + + true + + + QComboBox::NoInsert + + + + + + + Digis + + + + + + + + + + + + Radio Ports + + + + + + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + OK + + + + + + + Cancel + + + + + + + + + + + + okButton + clicked() + AGWConnectDkg + accept() + + + 278 + 253 + + + 96 + 254 + + + + + cancelButton + clicked() + AGWConnectDkg + reject() + + + 369 + 253 + + + 179 + 282 + + + + + diff --git a/VARAConfig.ui b/VARAConfig.ui new file mode 100644 index 0000000..7992b6a --- /dev/null +++ b/VARAConfig.ui @@ -0,0 +1,566 @@ + + + Dialog + + + + 0 + 0 + 571 + 517 + + + + VARA Configuration + + + + + 170 + 460 + 211 + 33 + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + OK + + + + + + + Cancel + + + + + + + + + 10 + 120 + 537 + 123 + + + + TNC Setup + + + + + 130 + 22 + 101 + 22 + + + + + + + 130 + 54 + 47 + 22 + + + + + + + 130 + 90 + 391 + 22 + + + + + + + 16 + 24 + 47 + 13 + + + + Host + + + + + + 16 + 53 + 47 + 22 + + + + Port + + + + + + 16 + 89 + 47 + 22 + + + + Path + + + + + + + 8 + 280 + 541 + 181 + + + + PTT Port + + + + + 150 + 20 + 111 + 22 + + + + + + + 20 + 23 + 131 + 16 + + + + Select PTT Port + + + + + + 20 + 80 + 121 + 22 + + + + PTT On String + + + + + + 150 + 83 + 247 + 22 + + + + + + + 150 + 114 + 247 + 22 + + + + + + + 20 + 112 + 121 + 22 + + + + PTT Off String + + + + + + 20 + 52 + 111 + 16 + + + + GPIO Pin Left + + + + + + 150 + 50 + 25 + 20 + + + + + + + 310 + 50 + 25 + 20 + + + + + + + 165 + 50 + 83 + 18 + + + + GPIO Pin Right + + + + + + 18 + 50 + 121 + 18 + + + + CAT Port Speed + + + + + + 150 + 50 + 47 + 20 + + + + + + + 290 + 22 + 131 + 18 + + + + RTS/DTR + + + buttonGroup_2 + + + + + + 420 + 22 + 121 + 18 + + + + CAT + + + buttonGroup_2 + + + + + + 150 + 50 + 121 + 20 + + + + + + + 20 + 50 + 111 + 18 + + + + CM108 VID/PID + + + + + + 290 + 50 + 131 + 18 + + + + Hex Strings + + + buttonGroup + + + + + + 420 + 50 + 131 + 18 + + + + Text Strings + + + buttonGroup + + + + + + + 140 + 44 + 113 + 22 + + + + + + + 16 + 42 + 131 + 22 + + + + Terminal Callsign + + + + + + 18 + 12 + 161 + 21 + + + + Enable VARA Interface + + + + + + 180 + 10 + 23 + 25 + + + + Qt::RightToLeft + + + + + + + + + 270 + 10 + 131 + 101 + + + + VARA Mode + + + + + 10 + 20 + 121 + 20 + + + + VARA HF + + + true + + + buttonGroup_4 + + + + + + 10 + 43 + 121 + 20 + + + + VARA FM + + + buttonGroup_4 + + + + + + 10 + 66 + 121 + 20 + + + + VARA SAT + + + buttonGroup_4 + + + + + + + 410 + 10 + 131 + 101 + + + + VARA HF BW + + + + + 10 + 20 + 101 + 20 + + + + 500 Hz + + + buttonGroup_3 + + + + + + 10 + 43 + 101 + 20 + + + + 2300 Hz + + + true + + + buttonGroup_3 + + + + + + 10 + 66 + 101 + 21 + + + + 2750 Hz + + + buttonGroup_3 + + + + + + + + + + + + + diff --git a/ax25.c b/ax25.c new file mode 100644 index 0000000..c379b0a --- /dev/null +++ b/ax25.c @@ -0,0 +1,2987 @@ +/* +Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO + +This file is part of QtSoundModem + +QtSoundModem 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. + +QtSoundModem 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 QtSoundModem. If not, see http://www.gnu.org/licenses + +*/ + +// UZ7HO Soundmodem Port by John Wiseman G8BPQ + +#include "ax25.h" +#include + +#ifdef WIN32 + +__declspec(dllimport) unsigned short __stdcall htons(__in unsigned short hostshort); +__declspec(dllimport) unsigned short __stdcall ntohs(__in unsigned short hostshort); + +#else + +#define strtok_s strtok_r +#include +#endif + +void decode_frame(Byte * frame, int len, Byte * path, string * data, + Byte * pid, Byte * nr, Byte * ns, Byte * f_type, Byte * f_id, + Byte * rpt, Byte * pf, Byte * cr); + +void SetSessLabel(void * Sess, char * label); +void setMenus(int State); + +/* + +unit ax25; + +interface + +uses classes,sysutils,windows; + + procedure get_exclude_list(line: string; var list: TStringList); + procedure get_exclude_frm(line: string; var list: TStringList); + procedure get_monitor_path(path: string; var mycall,corrcall,digi: string); + procedure get_call(src_call: string; var call: string); + procedure get_call_fm_path(path: string; var callto,callfrom: string); + procedure set_corrcall(snd_ch,port: byte; path: string); + procedure set_mycall(snd_ch,port: byte; path: string); + procedure set_digi(snd_ch,port: byte; path: string); + procedure decode_frame(frame: string; var path,data: string; var pid,nr,ns,f_type,f_id: byte; var rpt,pf,cr: boolean); + procedure del_incoming_mycalls(src_call: string); + procedure del_incoming_mycalls_by_sock(socket: integer); + procedure ax25_info_init(snd_ch,port: byte); + procedure write_ax25_info(snd_ch,port: byte); + procedure clr_frm_win(snd_ch,port: byte); + procedure ax25_init; + procedure ax25_free; + function dec2hex(value: byte): string; + function get_corrcall(path: string): string; + function get_mycall(path: string): string; + function get_digi(path: string): string; + function is_excluded_call(snd_ch: byte; path: string): boolean; + function is_excluded_frm(snd_ch,f_id: byte; data: string): boolean; + function is_last_digi(path: string): boolean; + function is_digi(snd_ch,port: byte; path: string): boolean; + function is_corrcall(snd_ch,port: byte; path: string): boolean; + function is_mycall(snd_ch,port: byte; path: string): boolean; + function is_correct_path(path: string; pid: byte): boolean; + function direct_addr(path: string): string; + function reverse_addr(path: string): string; + function reverse_digi(path: string): string; + function number_digi(path: string): byte; + function info_pid(pid: byte): string; + function get_fcs(var data: string; len: word): word; + function set_addr(path: string; rpt,cr: boolean): string; + function set_ctrl(nr,ns,f_type,f_id: byte; pf: boolean): byte; + function make_frame(data,path: string; pid,nr,ns,f_type,f_id: byte; rpt,pf,cr: boolean): string; + function get_incoming_socket_by_call(src_call: string): integer; + function add_incoming_mycalls(socket: integer; src_call: string): boolean; + function in_list_incoming_mycall(path: string): boolean; + function get_UTC_time: string; + function parse_NETROM(data: string; f_id: byte): string; + function parse_IP(data: string): string; + function parse_ARP(data: string): string; + function add_raw_frames(snd_ch: byte; frame: string; var buf: TStringList): boolean; + function scrambler(in_buf: string): string; + function my_indexof(var l: TStringList; s: string): integer; + + +const + port_num=32; + PKT_ERR=17; //Minimum packet size, bytes + I_MAX=7; //Maximum number of packets + B_IDX_MAX=256; + ADDR_MAX_LEN=10; + FRAME_FLAG=126; + // Status flags + STAT_NO_LINK=0; + STAT_LINK=1; + STAT_CHK_LINK=2; + STAT_WAIT_ANS=3; + STAT_TRY_LINK=4; + STAT_TRY_UNLINK=5; + // Ñmd,Resp,Poll,Final,Digipeater flags + SET_P=TRUE; + SET_F=FALSE; + SET_C=TRUE; + SET_R=FALSE; + SET_NO_RPT=FALSE; + SET_RPT=TRUE; + // Frame ID flags + I_FRM=0; + S_FRM=1; + U_FRM=2; + I_I=0; + S_RR=1; + S_RNR=5; + S_REJ=9; + U_SABM=47; + U_DISC=67; + U_DM=15; + U_UA=99; + U_FRMR=135; + U_UI=3; + // PID flags + PID_X25=$01; // 00000001-CCIT X25 PLP + PID_SEGMENT=$08; // 00001000-Segmentation fragment + PID_TEXNET=$C3; // 11000011-TEXNET Datagram Protocol + PID_LQ=$C4; // 11001000-Link Quality Protocol + PID_APPLETALK=$CA; // 11001010-Appletalk + PID_APPLEARP=$CB; // 11001011-Appletalk ARP + PID_IP=$CC; // 11001100-ARPA Internet Protocol + PID_ARP=$CD; // 11001101-ARPA Address Resolution Protocol + PID_NET_ROM=$CF; // 11001111-NET/ROM +*/ + +#define ADDR_MAX_LEN 10 +#define PID_NO_L3 0xF0; // 11110000-No Level 3 Protocol + + +unsigned short CRCTable[256] = { + 0, 4489, 8978, 12955, 17956, 22445, 25910, 29887, + 35912, 40385, 44890, 48851, 51820, 56293, 59774, 63735, + 4225, 264, 13203, 8730, 22181, 18220, 30135, 25662, + 40137, 36160, 49115, 44626, 56045, 52068, 63999, 59510, + 8450, 12427, 528, 5017, 26406, 30383, 17460, 21949, + 44362, 48323, 36440, 40913, 60270, 64231, 51324, 55797, + 12675, 8202, 4753, 792, 30631, 26158, 21685, 17724, + 48587, 44098, 40665, 36688, 64495, 60006, 55549, 51572, + 16900, 21389, 24854, 28831, 1056, 5545, 10034, 14011, + 52812, 57285, 60766, 64727, 34920, 39393, 43898, 47859, + 21125, 17164, 29079, 24606, 5281, 1320, 14259, 9786, + 57037, 53060, 64991, 60502, 39145, 35168, 48123, 43634, + 25350, 29327, 16404, 20893, 9506, 13483, 1584, 6073, + 61262, 65223, 52316, 56789, 43370, 47331, 35448, 39921, + 29575, 25102, 20629, 16668, 13731, 9258, 5809, 1848, + 65487, 60998, 56541, 52564, 47595, 43106, 39673, 35696, + 33800, 38273, 42778, 46739, 49708, 54181, 57662, 61623, + 2112, 6601, 11090, 15067, 20068, 24557, 28022, 31999, + 38025, 34048, 47003, 42514, 53933, 49956, 61887, 57398, + 6337, 2376, 15315, 10842, 24293, 20332, 32247, 27774, + 42250, 46211, 34328, 38801, 58158, 62119, 49212, 53685, + 10562, 14539, 2640, 7129, 28518, 32495, 19572, 24061, + 46475, 41986, 38553, 34576, 62383, 57894, 53437, 49460, + 14787, 10314, 6865, 2904, 32743, 28270, 23797, 19836, + 50700, 55173, 58654, 62615, 32808, 37281, 41786, 45747, + 19012, 23501, 26966, 30943, 3168, 7657, 12146, 16123, + 54925, 50948, 62879, 58390, 37033, 33056, 46011, 41522, + 23237, 19276, 31191, 26718, 7393, 3432, 16371, 11898, + 59150, 63111, 50204, 54677, 41258, 45219, 33336, 37809, + 27462, 31439, 18516, 23005, 11618, 15595, 3696, 8185, + 63375, 58886, 54429, 50452, 45483, 40994, 37561, 33584, + 31687, 27214, 22741, 18780, 15843, 11370, 7921, 3960 }; + + +unsigned short pkt_raw_min_len = 8; +int stat_r_mem = 0; + +struct TKISSMode_t KISS; + + +TAX25Port AX25Port[4][port_num]; + +TStringList KISS_acked[4]; +TStringList KISS_iacked[4]; + +typedef struct registeredCalls_t +{ + UCHAR myCall[7]; // call in ax.25 + void * socket; + +} registeredCalls; + + +TStringList list_incoming_mycalls; // list strings containing a registered call + + +boolean busy = 0; +boolean dcd[5] = { 0 ,0 ,0, 0 }; + +boolean tx = 0; + +int stdtones = 0; +int fullduplex = 0; + +UCHAR diddles = 0; + +word MEMRecovery[5] = { 200,200,200,200 }; +int NonAX25[5] = { 0 }; + +boolean dyn_frack[4] = { FALSE,FALSE,FALSE,FALSE }; +Byte recovery[4] = { 0,0,0,0 }; +Byte users[4] = { 0,0,0,0 }; + +short txtail[5] = { 50, 50, 50, 50, 50 }; +short txdelay[5] = { 400, 400, 400, 400, 400 }; + +short modem_def[5] = { 1, 1, 1, 1, 1 }; + +int emph_db[5] = { 0, 0, 0, 0, 0 }; +UCHAR emph_all[5] = { 0, 0, 0, 0, 0 }; + +boolean KISS_opt[4] = { FALSE, FALSE, FALSE, FALSE }; +int resptime[4] = { 1500,1500,1500,1500 }; +int slottime[4] = { 100,100,100,100 }; +int persist[4] = { 100,100,100,100 }; +int kisspaclen[4] = { 128,128,128,128 }; +int fracks[4] = { 10,10,10,10 }; +int frack_time[4] = { 5,5,5,5 }; +int idletime[4] = { 180,180,180,180 }; +int redtime[4] = { 0,0,0,0 }; +int IPOLL[4] = { 30,30,30,30 }; +int maxframe[4] = { 4,4,4,4 }; +int TXFrmMode[4] = { 1,1,1,1 }; +int max_frame_collector[4] = { 6,6,6,6 }; + + +char MyDigiCall[4][512] = { "","","","" }; +char exclude_callsigns[4][512] = { "","","","" }; +char exclude_APRS_frm[4][512] = { "","","","" }; + +TStringList list_exclude_callsigns[4]; +TStringList list_exclude_APRS_frm[4]; +TStringList list_digi_callsigns[4]; + +Byte xData[256]; +Byte xEncoded[256]; +Byte xDecoded[256]; + +int frame_count = 0; +int single_frame_count = 0; + +/* + mydigi: string; + //// end of user params + addr: string[70]; + ctrl: byte; + pid: byte=PID_NO_L3; + fcs: word; + data: string; + frame: string; + +implementation + +uses ax25_l2,sm_main; + +*/ + +char * strlop(char * buf, char delim) +{ + // Terminate buf at delim, and return rest of string + + char * ptr = strchr(buf, delim); + + if (ptr == NULL) return NULL; + + *(ptr)++ = 0; + return ptr; +} + +void Debugprintf(const char * format, ...) +{ + char Mess[10000]; + va_list(arglist); + + va_start(arglist, format); + vsprintf(Mess, format, arglist); + WriteDebugLog(Mess); + + return; +} + + + +void AX25_conn(TAX25Port * AX25Sess, int snd_ch, Byte mode) +{ + UNUSED(snd_ch); + char Msg[128]; + int Len = 0; + + switch (mode) + { + case MODE_OTHER: + + Len = sprintf(Msg, "Incomming Connection from %s\r", AX25Sess->corrcall); + break; + + case MODE_OUR: + + Len = sprintf(Msg, "Connected To %s\r", AX25Sess->corrcall); + break; + + }; + + SendtoTerm(AX25Sess->Sess, Msg, Len); + SetSessLabel(AX25Sess->Sess, AX25Sess->corrcall); + setMenus(1); +} + +void send_data_buf(TAX25Port * AX25Sess, int nr); + +void SendtoAX25(void * conn, Byte * Msg, int Len) +{ + TAX25Port * AX25Sess; + AX25Sess = (TAX25Port * )conn; + + // Need to enforce PacLen + + if (AX25Sess) + { + int n; + + while (Len) + { + string * data = newString(); + + if (Len > kisspaclen[0]) + n = kisspaclen[0]; + else + n = Len; + + stringAdd(data, Msg, n); + Add(&AX25Sess->in_data_buf, data); + + Len -= n; + Msg += n; + } + send_data_buf(AX25Sess, AX25Sess->vs); + } +} + + + + + +void scrambler(UCHAR * in_buf, int Len) +{ + integer i; + word sreg; + Byte a = 0, k; + + sreg = 0x1ff; + + for (i = 0; i < Len; i++) + { + for (k = 0; k < 8; k++) + { + + // a: = (a shr 1) or (sreg and 1 shl 7); + + a = (a >> 1) | ((sreg & 1) << 7); + + // sreg: = (sreg shl 4 and $200) xor (sreg shl 8 and $200) or (sreg shr 1); + + + sreg = (((sreg << 4) & 0x200) ^ ((sreg << 8) & 0x200)) | (sreg >> 1); + } + in_buf[i] = in_buf[i] ^ a; + } +} + + +/* +function parse_ARP(data: string): string; + +function get_callsign(data: string): string; +var + i: integer; + s: string; + a: byte; +begin + s:=''; + if length(data)=7 then + begin + for i:=1 to 6 do + begin + a:=ord(data[i]) shr 1; + if (a in [$30..$39,$41..$5A]) then s:=s+chr(a); + end; + a:=ord(data[7]) shr 1 and 15; + if a>0 then s:=s+'-'+inttostr(a); + end + else + begin + if length(data)>0 then + begin + for i:=1 to length(data) do + if i=1 then s:=dec2hex(ord(data[i])) else s:=s+':'+dec2hex(ord(data[i])); + end; + end; + if s<>'' then s:=s+' '; + result:=s; +end; + +function get_IP(data: string): string; +var + i: integer; + s: string; +begin + s:=''; + if length(data)>0 then + begin + for i:=1 to length(data) do + if i=1 then s:=inttostr(ord(data[i])) else s:=s+'.'+inttostr(ord(data[i])); + end; + if s<>'' then s:=s+' '; + result:=s; +end; + +const + opcode: array [0..3] of string = ('ARP Request','ARP Response','RARP Request','RARP Response'); +var + oper: word; + hlen,plen: byte; + sha,spa,tha,tpa: string; + s: string; + i: word; +begin + s:=data; + if length(data)>7 then + begin + hlen:=ord(data[5]); + plen:=ord(data[6]); + oper:=(ord(data[7]) shl 8 or ord(data[8])) and 2; + i:=9; sha:=get_callsign(copy(data,i,hlen)); + i:=i+hlen; spa:=get_ip(copy(data,i,plen)); + i:=i+plen; tha:=get_callsign(copy(data,i,hlen)); + i:=i+hlen; tpa:=get_ip(copy(data,i,plen)); + s:=' [ARP] '+opcode[oper]+' from '+sha+spa+'to '+tha+tpa; + end; + result:=s; +end; + +function parse_NETROM(data: string; f_id: byte): string; + + function deshift_AX25(data: string): string; + var + i: byte; + call: string[6]; + ssid: string[2]; + begin + result:=''; + if length(data)<7 then exit; + for i:=1 to 7 do data[i]:=chr(ord(data[i]) shr 1); + call:=trim(copy(data,1,6)); + ssid:=trim(inttostr(ord(data[7]) and 15)); + if ssid='0' then result:=call else result:=call+'-'+ssid; + end; + + function con_req_info(data: string): string; + var + s_call: string; + d_call: string; + w: byte; + t_o: byte; + begin + result:=''; + if length(data)>14 then + begin + w:=ord(data[1]); + s_call:=deshift_AX25(copy(data,2,7)); + d_call:=deshift_AX25(copy(data,9,7)); + result:=' w='+inttostr(w)+' '+s_call+' at '+d_call; + end; + if length(data)>15 then + begin + t_o:=ord(data[16]); + result:=result+' t/o '+inttostr(t_o); + end; + end; + + function con_ack_info(data: string): string; + var + w: byte; + begin + result:=''; + if length(data)>0 then + begin + w:=ord(data[1]); + result:=' w='+inttostr(w); + end; + end; + +const + opcode_arr: array[0..7] of string = ('PE','CON REQ','CON ACK','DISC REQ','DISQ ACK','INFO','INFO ACK','RST'); +var + s: string; + netrom_header: string; + c_idx: byte; + c_ID: byte; + TX_nr: byte; + RX_nr: byte; + opcode: byte; + s_call: string; + s_node: string; + d_call: string; + d_node: string; + b_call: string; + r_s_nr: string; + opc_flags: string; + quality: byte; + ttl: byte; + hops: byte; + rtt: word; + inp3_nr_field: byte; + inp3_field_len: byte; + inp3_ext_fields: boolean; +begin + s:=data; + if length(data)>0 then + begin + if data[1]=#$FF then + begin + delete(data,1,1); + //Nodes broadcasting + if (f_id=U_UI) and (length(data)>5) then + begin + s_node:=copy(data,1,6); + delete(data,1,6); + s:='NODES broadcast from '+s_node+#13#10; + while length(data)>20 do + begin + d_call:=deshift_AX25(copy(data,1,7)); + d_node:=copy(data,8,6); + b_call:=deshift_AX25(copy(data,14,7)); + quality:=ord(data[21]); + delete(data,1,21); + s:=s+' '+d_node+':'+d_call+' via '+b_call+' Q='+inttostr(quality)+#13#10; + end; + end; + // INP3 RIF + if (f_id=I_I) and (length(data)>10) then + begin + s:='[INP3 RIF]'+#13#10; + while length(data)>10 do + begin + d_call:=deshift_AX25(copy(data,1,7)); + hops:=ord(data[8]); + rtt:=(ord(data[9]) shl 8) or ord(data[10]); + delete(data,1,10); + inp3_ext_fields:=TRUE; + inp3_nr_field:=0; + while (length(data)>0) and inp3_ext_fields do + begin + inp3_field_len:=ord(data[1]); + if inp3_field_len>0 then + begin + if (inp3_nr_field=0) and (length(data)>1) then + begin + if data[2]=#0 then d_call:=copy(data,3,inp3_field_len-2)+':'+d_call; // Copy alias + end; + delete(data,1,inp3_field_len); + inc(inp3_nr_field); + end + else inp3_ext_fields:=FALSE; + end; + delete(data,1,1); + s:=s+d_call+' hops='+inttostr(hops)+' rtt='+inttostr(rtt)+#13#10; + end; + end; + end + else + begin + // NETROM frames + if length(data)>19 then + begin + s_call:=deshift_AX25(copy(data,1,7)); + d_call:=deshift_AX25(copy(data,8,7)); + ttl:=ord(data[15]); + netrom_header:=copy(data,16,5); + delete(data,1,20); + c_idx:=ord(netrom_header[1]); + c_ID:=ord(netrom_header[2]); + TX_nr:=ord(netrom_header[3]); + RX_nr:=ord(netrom_header[4]); + opcode:=ord(netrom_header[5]); + // Opcode flags + opc_flags:=''; + if opcode and 128 = 128 then opc_flags:=opc_flags+' C'; + if opcode and 64 = 64 then opc_flags:=opc_flags+' N'; + // + s:=' [NETROM] '+s_call+' to '+d_call+' ttl='+inttostr(ttl)+' cct='+dec2hex(c_idx)+dec2hex(c_ID); + r_s_nr:=' S'+inttostr(TX_nr)+' R'+inttostr(RX_nr); + case (opcode and 7) of + 0 : s:=s+' <'+opcode_arr[opcode and 7]+r_s_nr+'>'+#13#10+data; + 1 : s:=s+' <'+opcode_arr[opcode and 7]+'>'+con_req_info(data); + 2 : s:=s+' <'+opcode_arr[opcode and 7]+'>'+con_ack_info(data)+' my cct='+dec2hex(TX_nr)+dec2hex(RX_nr); + 3 : s:=s+' <'+opcode_arr[opcode and 7]+'>'; + 4 : s:=s+' <'+opcode_arr[opcode and 7]+'>'; + 5 : s:=s+' <'+opcode_arr[opcode and 7]+r_s_nr+'>:'+#13#10+data; + 6 : s:=s+' <'+opcode_arr[opcode and 7]+' R'+inttostr(RX_nr)+'>'+opc_flags; + 7 : s:=s+' <'+opcode_arr[opcode and 7]+r_s_nr+'>'+#13#10+data; + end; + end; + end; + end; + result:=s; +end; + +function parse_IP(data: string): string; + + function parse_ICMP(var data: string): string; + var + ICMP_type: byte; + ICMP_code: byte; + s: string; + begin + result:=''; + if length(data)>3 then + begin + ICMP_type:=ord(data[1]); + ICMP_code:=ord(data[2]); + delete(data,1,4); + s:=' [ICMP] Type='+inttostr(ICMP_type)+' Code='+inttostr(ICMP_code)+#13#10; + result:=s; + end; + end; + + function parse_TCP(var data: string): string; + var + s: string; + src_port: string; + dest_port: string; + wnd: string; + ihl: word; + idl: word; + flags: byte; + seq: string; + ack: string; + s_flags: string; + s_idl: string; + begin + result:=''; + if length(data)>19 then + begin + src_port:=' src_port:'+inttostr((ord(data[1]) shl 8)+ord(data[2])); + dest_port:=' dest_port:'+inttostr((ord(data[3]) shl 8)+ord(data[4])); + seq:=' seq='+dec2hex(ord(data[5]))+dec2hex(ord(data[6]))+dec2hex(ord(data[7]))+dec2hex(ord(data[8])); + ack:=' ack='+dec2hex(ord(data[9]))+dec2hex(ord(data[10]))+dec2hex(ord(data[11]))+dec2hex(ord(data[12])); + ihl:=(ord(data[13]) shr 4)*4; + idl:=length(data)-ihl; + flags:=ord(data[14]); + wnd:=' wnd='+inttostr((ord(data[15]) shl 8)+ord(data[16])); + delete(data,1,ihl); + // + s_flags:=' '; + if (flags and 32)=32 then s_flags:=s_flags+'URG '; + if (flags and 16)=16 then s_flags:=s_flags+'ACK '; + if (flags and 8)=8 then s_flags:=s_flags+'PSH '; + if (flags and 4)=4 then s_flags:=s_flags+'RST '; + if (flags and 2)=2 then s_flags:=s_flags+'SYN '; + if (flags and 1)=1 then s_flags:=s_flags+'FIN '; + // + if idl>0 then s_idl:=' data='+inttostr(idl) else s_idl:=''; + if (flags and 16)<>16 then ack:=''; + // + s:=' [TCP]'+src_port+dest_port+seq+ack+wnd+s_idl+s_flags+#13#10; + result:=s; + end; + end; + + function parse_UDP(var data: string): string; + var + s: string; + src_port: string; + dest_port: string; + idl: word; + len: word; + s_idl: string; + begin + result:=''; + if length(data)>7 then + begin + src_port:=' src_port:'+inttostr((ord(data[1]) shl 8)+ord(data[2])); + dest_port:=' dest_port:'+inttostr((ord(data[3]) shl 8)+ord(data[4])); + len:=(ord(data[5]) shl 8)+ord(data[6]); + idl:=len-8; + delete(data,1,8); + // + if idl>0 then s_idl:=' data='+inttostr(idl) else s_idl:=''; + // + s:=' [UDP]'+src_port+dest_port+' len='+inttostr(len)+s_idl+#13#10; + result:=s; + end; + end; + +const + prot_idx=#1#6#17; + prot_name: array [1..3] of string = ('ICMP','TCP','UDP'); +var + s: string; + src_ip: string; + dest_ip: string; + s_prot: string; + len: string; + c_prot: char; + ttl: string; + offset: string; + ihl: byte; + p: byte; + fragment_offset: word; +begin + s:=data; + if length(data)>19 then + begin + ihl:=(ord(data[1]) and 15)*4; + len:=' len='+inttostr((ord(data[3]) shl 8)+ord(data[4])); + fragment_offset:=((ord(data[7]) shl 8)+ord(data[8])) shl 3; + ttl:=' ttl='+inttostr(ord(data[9])); + c_prot:=data[10]; + src_ip:=' Fm '+inttostr(ord(data[13]))+'.'+inttostr(ord(data[14]))+'.'+inttostr(ord(data[15]))+'.'+inttostr(ord(data[16])); + dest_ip:=' To '+inttostr(ord(data[17]))+'.'+inttostr(ord(data[18]))+'.'+inttostr(ord(data[19]))+'.'+inttostr(ord(data[20])); + delete(data,1,ihl); + // + p:=pos(c_prot,prot_idx); + if p>0 then s_prot:=' prot='+prot_name[p] else s_prot:=' prot=Type'+inttostr(ord(c_prot)); + if fragment_offset>0 then offset:=' offset='+inttostr(fragment_offset) else offset:=''; + s:=' [IP]'+src_ip+dest_ip+s_prot+ttl+len+offset+#13#10; + if fragment_offset=0 then + case p of + 1 : s:=s+parse_ICMP(data); + 2 : s:=s+parse_TCP(data); + 3 : s:=s+parse_UDP(data); + end; + s:=s+data; + end; + result:=s; +end; + +function get_UTC_time: string; +var + st: TSYSTEMTIME; + sec,hour,minute: string; +begin + GetSystemTime(st); + if st.wSecond<10 then sec:='0'+inttostr(st.wSecond) else sec:=inttostr(st.wSecond); + if st.wMinute<10 then minute:='0'+inttostr(st.wMinute) else minute:=inttostr(st.wMinute); + if st.wHour<10 then Hour:='0'+inttostr(st.wHour) else Hour:=inttostr(st.wHour); + result:=hour+':'+minute+':'+sec; +end; + +function dec2hex(value: byte): string; +const + hex='0123456789ABCDEF'; +var + lo,hi: byte; +begin + lo:=value and 15; + hi:=value shr 4; + result:=hex[hi+1]+hex[lo+1]; +end; + +*/ + +unsigned short get_fcs(UCHAR * Data, unsigned short len) +{ + unsigned short i; + unsigned short result; + + result = 0xFFFF; + + if (len == 0) + return result; + + for (i = 0; i < len; i++) + result = (result >> 8) ^ CRCTable[(result ^ Data[i]) & 0xff]; + + + result ^= 0xffff; + + return result; +} + + +unsigned short CRCTAB[256] = { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, +0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, +0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, +0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, +0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, +0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, +0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, +0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, +0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, +0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, +0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, +0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, +0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, +0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, +0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, +0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, +0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, +0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, +0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, +0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, +0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, +0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, +0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, +0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, +0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, +0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, +0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; + +unsigned short int compute_crc(unsigned char *buf, int len) +{ + unsigned short fcs = 0xffff; + int i; + + for (i = 0; i < len; i++) + fcs = (fcs >> 8) ^ CRCTAB[(fcs ^ buf[i]) & 0xff]; + + fcs ^= 0xffff; + + return fcs; +} + + + +int get_addr(char * Calls, UCHAR * AXCalls) +{ + // CONVERT CALL + OPTIONAL DIGI STRING (Comma separated) TO AX25, RETURN + // CONVERTED STRING IN AXCALLS. Return FALSE if invalied + + Byte * axptr = AXCalls; + char * ptr, *Context; + int n = 8; // Max digis + + memset(AXCalls, 0, 70); + + ptr = strtok_s(Calls, " ,", &Context); + + if (ptr == NULL) + return FALSE; + + // First field is Call + + if (ConvToAX25(ptr, axptr) == 0) + return FALSE; + + axptr += 7; + + ptr = strtok_s(NULL, " ,", &Context); + + if (ConvToAX25(ptr, axptr) == 0) + return FALSE; + + axptr += 7; + + ptr = strtok_s(NULL, " ,", &Context); + + while (ptr && n--) + { + // NEXT FIELD = COULD BE CALLSIGN, VIA, + + if (memcmp(ptr, "VIA", (int)strlen(ptr)) == 0) + { + } //skip via + else + { + // Convert next digi + + if (ConvToAX25(ptr, axptr) == 0) + return FALSE; + + axptr += 7; + } + + ptr = strtok_s(NULL, " ,", &Context); + } + + axptr[-1] |= 1; // Set end of address + + return axptr - AXCalls; +} + +Byte set_ctrl(Byte nr, Byte ns, Byte f_type, Byte f_id, boolean pf) +{ + Byte pf_bit, ctrl; + + ctrl = 0; + pf_bit = 0; + + if (pf) + pf_bit = 16; + + switch (f_type) + { + case I_FRM: + + ctrl = (nr << 5) + pf_bit + (ns << 1); + break; + + case S_FRM: + + ctrl = (nr << 5) + pf_bit + f_id; + break; + + case U_FRM: + + ctrl = f_id + pf_bit; + } + + return ctrl; +} + +string * make_frame(string * data, Byte * axaddr, Byte pid, Byte nr, Byte ns, Byte f_type, Byte f_id, boolean rpr, boolean pf, boolean cr) +{ + UNUSED(rpr); + + Byte ctrl; + + string * frame = newString(); + int addrlen; + Byte addr[80]; + + frame->Data[0] = 0; // Lower software expects a kiss control byte here + frame->Length = 1; + + ctrl = set_ctrl(nr, ns, f_type, f_id, pf); + + addrlen = strlen((char *)axaddr); + + memcpy(addr, axaddr, addrlen); + + if (cr) + addr[6] |= 0x80; // Set Command Bit + else + addr[13] |= 0x80; // Set Response Bit + + + switch (f_type) + { + case I_FRM: + + stringAdd(frame, addr, addrlen); + stringAdd(frame, (Byte *)&ctrl, 1); + stringAdd(frame, (Byte *)&pid, 1); + stringAdd(frame, data->Data, data->Length); + + break; + + + case S_FRM: + + stringAdd(frame, addr, addrlen); + stringAdd(frame, (Byte *)&ctrl, 1); + + break; + + case U_FRM: + + if (f_id == U_UI) + { + stringAdd(frame, addr, addrlen); + stringAdd(frame, (Byte *)&ctrl, 1); + stringAdd(frame, (Byte *)&pid, 1); + stringAdd(frame, data->Data, data->Length); + } + else if (f_id == U_FRMR) + { + stringAdd(frame, addr, addrlen); + stringAdd(frame, (Byte *)&ctrl, 1); + stringAdd(frame, data->Data, data->Length); + } + else + { + stringAdd(frame, addr, addrlen); + stringAdd(frame, (Byte *)&ctrl, 1); + } + } + + return frame; +} + + +int add_raw_frames(int snd_ch, string * frame, TStringList * buf) +{ + UNUSED(snd_ch); + + string *s_data = newString(); + Byte s_pid, s_nr, s_ns, s_f_type, s_f_id; + Byte s_rpt, s_cr, s_pf; + string *d_data = newString(); + Byte d_pid, d_nr, d_ns, d_f_type, d_f_id; + Byte d_rpt, d_cr, d_pf; + + Byte d_path[80]; + Byte s_path[80]; + + boolean found_I; + int i; + + unsigned char * framecontents; + int Length; + + boolean result = TRUE; + + // Have to be careful as at this point frames have KISS Header and maybe trailer + + if (buf->Count > 0) + { + // Check for duplicate. Ok to just compare as copy will have same header + + if (my_indexof(buf, frame) >= 0) + { +// Debugprintf("KISSOptimise discarding duplicate frame"); + return FALSE; + } + + // Need to adjust for KISS bytes + + // Normally one, but ackmode has 3 on front and sizeof(void *) on end + + framecontents = frame->Data; + Length = frame->Length; + + if ((framecontents[0] & 15) == 12) // Ackmode + { + framecontents += 3; + Length -= (3 + sizeof(void *)); + } + else + { + framecontents++; + Length--; + } + + decode_frame(framecontents, Length, s_path, s_data, &s_pid, &s_nr, &s_ns, &s_f_type, &s_f_id, &s_rpt, &s_pf, &s_cr); + + found_I = FALSE; + + // check for multiple RR (r) + + if (s_f_id == S_FRM && s_cr == SET_R) + { + for (i = 0; i < buf->Count; i++) + { + framecontents = buf->Items[i]->Data; + Length = buf->Items[i]->Length; + + if ((framecontents[0] & 15) == 12) // Ackmode + { + framecontents += 3; + Length -= (3 + sizeof(void *)); + } + else + { + framecontents++; + Length--; + } + + decode_frame(framecontents, Length, d_path, d_data, &d_pid, &d_nr, &d_ns, &d_f_type, &d_f_id, &d_rpt, &d_pf, &d_cr); + + if (d_f_id == S_FRM && d_cr == SET_R && strcmp((char *)s_path, (char *)d_path) == 0) + { + Delete(buf, i); + Debugprintf("KISSOptimise discarding unneeded RR(R%d)", d_nr); + + break; + } + } + } + + + // check for RR after I Frame + + if (s_f_id == S_FRM && s_cr == SET_C) + { + for (i = 0; i < buf->Count; i++) + { + framecontents = buf->Items[i]->Data; + Length = buf->Items[i]->Length; + + if ((framecontents[0] & 15) == 12) // Ackmode + { + framecontents += 3; + Length -= (3 + sizeof(void *)); + } + else + { + framecontents++; + Length--; + } + + decode_frame(framecontents, Length, d_path, d_data, &d_pid, &d_nr, &d_ns, &d_f_type, &d_f_id, &d_rpt, &d_pf, &d_cr); + + if (d_f_id == I_FRM && strcmp((char *)s_path, (char *)d_path) == 0) + { + found_I = TRUE; + break; + } + } + + if (found_I) + { + Debugprintf("KISSOptimise discarding unneeded RR(C %d) after I frame", s_nr); + result = FALSE; + } + } + + // check on I + + if (s_f_id == I_FRM) + { + for (i = 0; i < buf->Count; i++) + { + framecontents = buf->Items[i]->Data; + Length = buf->Items[i]->Length; + + if ((framecontents[0] & 15) == 12) // Ackmode + { + framecontents += 3; + Length -= (3 + sizeof(void *)); + } + else + { + framecontents++; + Length--; + } + + decode_frame(framecontents, Length, d_path, d_data, &d_pid, &d_nr, &d_ns, &d_f_type, &d_f_id, &d_rpt, &d_pf, &d_cr); + + if (strcmp((char *)s_path, (char *)d_path) == 0 && d_f_id == S_FRM && d_cr == SET_C) + { + Delete(buf, i); + Debugprintf("KISSOptimise discarding unneeded RR(C %d)", d_nr); + i--; // i was removed + } + } + } + } + + freeString(d_data); + freeString(s_data); + + return result; +} + +//////////////////////// Register incoming callsign //////////////////////////// + +// I think a call should only be registered on one socket (or we won't know where to send +// incoming calls + +boolean add_incoming_mycalls(void * socket, char * src_call) +{ + registeredCalls * reg = malloc(sizeof(struct registeredCalls_t)); + int i = 0; + + // Build a string containing Call and Socket + + ConvToAX25(src_call, reg->myCall); + reg->socket = socket; + + if (list_incoming_mycalls.Count > 0) + { + for (i = 0; i < list_incoming_mycalls.Count; i++) + { + registeredCalls * check = (registeredCalls *)list_incoming_mycalls.Items[i]; + + if (memcmp(check->myCall, reg->myCall, 7) == 0) + { + // Update socket + + check->socket = socket; + return FALSE; + } + } + } + + Add(&list_incoming_mycalls, (string *)reg); + return TRUE; +} + + + +void del_incoming_mycalls(char * src_call) +{ + int i = 0; + Byte axcall[7]; + registeredCalls * reg; + + ConvToAX25(src_call, axcall); + + while (i < list_incoming_mycalls.Count) + { + reg = (registeredCalls *)list_incoming_mycalls.Items[i]; + { + if (memcmp(reg->myCall, axcall, 7) == 0) + { + // cant use Delete as stringlist doesn't contain strings + + TStringList * Q = &list_incoming_mycalls; + int Index = i; + + free(Q->Items[Index]); + + Q->Count--; + + while (Index < Q->Count) + { + Q->Items[Index] = Q->Items[Index + 1]; + Index++; + } + + return; + } + } + i++; + } +} + + +void del_incoming_mycalls_by_sock(void * socket) +{ + int i = 0, snd_ch, port; + registeredCalls * reg; + + while (i < list_incoming_mycalls.Count) + { + reg = (registeredCalls *)list_incoming_mycalls.Items[i]; + { + if (reg->socket == socket) + { + // cant use Delete as stringlist doesn't contain strings + + TStringList * Q = &list_incoming_mycalls; + int Index = i; + + free(Q->Items[Index]); + + Q->Count--; + + while (Index < Q->Count) + { + Q->Items[Index] = Q->Items[Index + 1]; + Index++; + } + + //Delete(&list_incoming_mycalls, i); + } + else + i++; + } + } + + // Should clear all connections on socket + + for (snd_ch = 0; snd_ch < 4; snd_ch++) + { + for (port = 0; port < port_num; port++) + { + TAX25Port * AX25Sess = &AX25Port[snd_ch][port]; + + if (AX25Sess->socket == socket) + { + if (AX25Sess->status != STAT_NO_LINK) + { + // Shouldn't we send DM? -0 try it + + set_DM(snd_ch, AX25Sess->ReversePath); + + rst_timer(AX25Sess); + rst_values(AX25Sess); + + AX25Sess->status = STAT_NO_LINK; + } + AX25Sess->socket = 0; + } + } + } +} + + +/* +function get_incoming_socket_by_call(src_call: string): integer; +var + i: integer; + found: boolean; + socket: integer; + call,ssid: string; + a_call: array[0..1] of string; +begin + socket:=-1; + i:=0; + found:=FALSE; + try explode(a_call,'-',src_call,2); except end; + call:=trim(a_call[0]); + if a_call[1]<>'' then ssid:=a_call[1] else ssid:='0'; + if list_incoming_mycalls.Count>0 then + repeat + if (call+'-'+ssid)=list_incoming_mycalls.Strings[i] then + begin socket:=strtoint(list_incoming_mycalls_sock.Strings[i]); found:=TRUE; end; + inc(i); + until found or (i=list_incoming_mycalls.Count); + result:=socket; +end; +*/ + + + +void * in_list_incoming_mycall(Byte * path) +{ + // See if to call is in registered calls list + + int i = 0; + registeredCalls * check; // list_incoming_mycalls contains registeredCalls, not Strings + + while (i < list_incoming_mycalls.Count) + { + check = (registeredCalls *)list_incoming_mycalls.Items[i]; + + if (memcmp(check->myCall, path, 7) == 0) + return check->socket; + + i++; + } + + return NULL; +} + +/* +//////////////////////////////////////////////////////////////////////////////// + +function is_corrcall(snd_ch,port: byte; path: string): boolean; +var + call,ssid: string; +begin + call:=trim(copy(path,8,6)); + ssid:=copy(path,14,1); + if ssid<>'' then ssid:=inttostr((ord(ssid[1]) and 15)) else ssid:='0'; + call:=call+'-'+ssid; + if call=AX25Sess->corrcall then result:=TRUE else result:=FALSE; +end; + +function is_mycall(snd_ch,port: byte; path: string): boolean; +var + call,ssid: string; +begin + call:=trim(copy(path,1,6)); + ssid:=copy(path,7,1); + if ssid<>'' then ssid:=inttostr((ord(ssid[1]) and 15)) else ssid:='0'; + call:=call+'-'+ssid; + if call=AX25Sess->mycall then result:=TRUE else result:=FALSE; +end; + +function is_digi(snd_ch,port: byte; path: string): boolean; +var + digi,call,ssid: string; +begin + digi:=''; + if length(path)>14 then + begin + delete(path,1,14); + repeat + call:=trim(copy(path,1,6)); + ssid:=copy(path,7,1); + delete(path,1,7); + if ssid<>'' then ssid:=inttostr((ord(ssid[1]) and 15)) + else ssid:='0'; + if path<>'' then digi:=digi+call+'-'+ssid+',' + else digi:=digi+call+'-'+ssid; + until path=''; + end; + if digi=AX25Sess->digi then result:=TRUE else result:=FALSE; +end; +*/ + +// Check if laast digi used + +boolean is_last_digi(Byte *path) +{ + int len = strlen(path); + + if (len == 14) + return TRUE; + + if ((path[len - 1] & 128) == 128) + return TRUE; + + return FALSE; +} + + + +/* +function get_corrcall(path: string): string; +var + call,ssid: string; +begin + call:=trim(copy(path,8,6)); + ssid:=copy(path,14,1); + if ssid<>'' then ssid:=inttostr((ord(ssid[1]) and 15)) else ssid:='0'; + call:=call+'-'+ssid; + result:=call; +end; + +function get_mycall(path: string): string; +var + call,ssid: string; +begin + call:=trim(copy(path,1,6)); + ssid:=copy(path,7,1); + if ssid<>'' then ssid:=inttostr((ord(ssid[1]) and 15)) else ssid:='0'; + call:=call+'-'+ssid; + result:=call; +end; + +function get_digi(path: string): string; +var + digi,call,ssid: string; +begin + digi:=''; + if length(path)>14 then + begin + delete(path,1,14); + repeat + call:=trim(copy(path,1,6)); + ssid:=copy(path,7,1); + delete(path,1,7); + if ssid<>'' then ssid:=inttostr((ord(ssid[1]) and 15)) + else ssid:='0'; + if path<>'' then digi:=digi+call+'-'+ssid+',' + else digi:=digi+call+'-'+ssid; + until path=''; + end; + result:=digi; +end; +*/ + +boolean is_correct_path(Byte * path, Byte pid) +{ + Byte networks[] = { 6, 7, 8, 0xc4, 0xcc, 0xcd, 0xce, 0xcf, 0xf0 , 0 }; + int i; + + + if (pid == 0 || strchr(networks, pid)) + { + // Validate calls + + // I think checking bottom bit of first 13 bytes is enough + + for (i = 0; i < 13; i++) + { + if ((*(path) & 1)) + return FALSE; + + path++; + } + return TRUE; + } + return FALSE; +} + + +void get_exclude_list(char * line, TStringList * list) +{ + // Convert comma separated list of calls to ax25 format in list + + string axcall; + + char copy[512]; + + char * ptr, *Context; + + if (line[0] == 0) + return; + + strcpy(copy, line); // copy as strtok messes with it + strcat(copy, ","); + + axcall.Length = 8; + axcall.AllocatedLength = 8; + axcall.Data = malloc(8); + + memset(axcall.Data, 0, 8); + + ptr = strtok_s(copy, " ,", &Context); + + while (ptr) + { + if (ConvToAX25(ptr, axcall.Data) == 0) + return; + + Add(list, duplicateString(&axcall)); + + ptr = strtok_s(NULL, " ,", &Context); + } +} + + + +void get_exclude_frm(char * line, TStringList * list) +{ + UNUSED(line); + UNUSED(list); + /* + + s: string; + p: integer; + n: integer; +begin + list.Clear; + if line='' then exit; + repeat + p:=pos(',',line); + if p>0 then + begin + s:=trim(copy(line,1,p-1)); + if s<>'' then + begin + try n:=strtoint(s); except n:=-1; end; + if n in [0..255] then list.Add(chr(n)); + end; + delete(line,1,p); + end + else + begin + s:=trim(line); + if s<>'' then + begin + try n:=strtoint(s); except n:=-1; end; + if n in [0..255] then list.Add(chr(n)); + end; + end; + until p=0; +end; +*/ +} + +/* + +function is_excluded_call(snd_ch: byte; path: string): boolean; +var + excluded: boolean; + call: string; + ssid: string; +begin + excluded:=FALSE; + if (list_exclude_callsigns[snd_ch].Count>0) and (length(path)>13) then + begin + // Copy sender + call:=trim(copy(path,8,6)); + ssid:=copy(path,14,1); + if ssid<>'' then call:=call+'-'+inttostr((ord(ssid[1]) and 15)); + if list_exclude_callsigns[snd_ch].IndexOf(call)>-1 then excluded:=TRUE; + end; + result:=excluded; +end; + +function is_excluded_frm(snd_ch,f_id: byte; data: string): boolean; +var + excluded: boolean; +begin + excluded:=FALSE; + if list_exclude_APRS_frm[snd_ch].Count>0 then + if f_id=U_UI then + if length(data)>0 then + if list_exclude_APRS_frm[snd_ch].IndexOf(data[1])>=0 then excluded:=TRUE; + result:=excluded; +end; + +procedure set_corrcall(snd_ch,port: byte; path: string); +var + call,ssid: string; +begin + call:=trim(copy(path,8,6)); + ssid:=copy(path,14,1); + if ssid<>'' then ssid:=inttostr((ord(ssid[1]) and 15)) + else ssid:='0'; + AX25Sess->corrcall:=call+'-'+ssid; +end; + +procedure set_mycall(snd_ch,port: byte; path: string); +var + call,ssid: string; +begin + call:=trim(copy(path,1,6)); + ssid:=copy(path,7,1); + if ssid<>'' then ssid:=inttostr((ord(ssid[1]) and 15)) + else ssid:='0'; + AX25Sess->mycall:=call+'-'+ssid; +end; + +procedure set_digi(snd_ch,port: byte; path: string); +var + digi,call,ssid: string; +begin + digi:=''; + if length(path)>14 then + begin + delete(path,1,14); + repeat + call:=trim(copy(path,1,6)); + ssid:=copy(path,7,1); + delete(path,1,7); + if ssid<>'' then ssid:=inttostr((ord(ssid[1]) and 15)) + else ssid:='0'; + if path<>'' then digi:=digi+call+'-'+ssid+',' + else digi:=digi+call+'-'+ssid; + until path=''; + end; + AX25Sess->digi:=digi; +end; + +procedure get_call_fm_path(path: string; var callto,callfrom: string); +var + a_path: array [0..ADDR_MAX_LEN-1] of string; + i: byte; +begin + for i:=0 to ADDR_MAX_LEN-1 do a_path[i]:=''; + try explode(a_path,',',path,ADDR_MAX_LEN); except end; + callto:=a_path[0]; + callfrom:=a_path[1]; +end; + +procedure get_call(src_call: string; var call: string); +var + a_call: array[0..1] of string; + ssid: string; +begin + try explode(a_call,'-',src_call,2); except end; + call:=trim(a_call[0]); + if a_call[1]<>'' then ssid:=trim(a_call[1]) else ssid:='0'; + call:=call+'-'+ssid; +end; +*/ + +int number_digi(unsigned char * path) +{ + UNUSED(path); + int n = 0; + + // a_path: array [0..ADDR_MAX_LEN-3] of string; + // for i:=0 to ADDR_MAX_LEN-3 do a_path[i]:=''; + // try explode(a_path,',',path,ADDR_MAX_LEN-2); except end; + // for i:=0 to ADDR_MAX_LEN-3 do if a_path[i]<>'' then inc(n); + + return n; +} + + + +void get_monitor_path(Byte * path, char * mycall, char * corrcall, char * digi) +{ + Byte * digiptr = digi; + + digi[0] = 0; + + mycall[ConvFromAX25(path, mycall)] = 0; + path += 7; + corrcall[ConvFromAX25(path, corrcall)] = 0; + + while ((path[6] & 1) == 0) // End of call bit + { + if (digi != digiptr) + *(digi++) = ','; + + path += 7; + digi += ConvFromAX25(path, digi); + + if (((path[6] & 128) == 128)) // Digi'd + *(digi++) = '*'; + } + *digi = 0; +} + + +/* + +function reverse_digi(path: string): string; +var + digi: string; + a_path: array [0..ADDR_MAX_LEN-3] of string; + i: word; +begin + digi:=''; + for i:=0 to ADDR_MAX_LEN-3 do a_path[i]:=''; + try explode(a_path,',',path,ADDR_MAX_LEN-2); except end; + for i:=0 to ADDR_MAX_LEN-3 do + if a_path[i]<>'' then + begin + if digi='' then digi:=a_path[i]+digi + else digi:=a_path[i]+','+digi; + end; + result:=digi; +end; + +function direct_addr(path: string): string; +var + s,call,ssid: string; +begin + s:=''; + repeat + call:=copy(path,1,6); + delete(path,1,6); + ssid:=copy(path,1,1); + delete(path,1,1); + if ssid<>'' then ssid:=inttostr((ord(ssid[1]) and 15)); + if s='' then s:=call+'-'+ssid else s:=s+','+call+'-'+ssid; + until path=''; + result:=s; +end; +*/ + +void reverse_addr(Byte * path, Byte * revpath, int Len) +{ + Byte * ptr = path; + Byte * copy = revpath; + int endbit = Len - 1; + int numdigis = (Len - 14) / 7; + int i; + + if (Len < 14) + return; + + Byte digis[57]; // 8 * 7 + null terminator + memset(digis, 0, 57); + Byte * digiptr = digis + 49; // Last Digi + + // remove end of address bit + + path[endbit] &= 0xFE; + + // first reverse dest and origin + + memcpy(copy + 7, ptr, 7); + memcpy(copy, ptr + 7, 7); + + Len -= 14; + ptr += 14; + + for (i = 0; i < numdigis; i++) + { + memcpy(digiptr, ptr, 7); + ptr += 7; + digiptr -= 7; + } + + // Digiptr now points to new first digi + + memcpy(©[14], &digiptr[7], 7 * numdigis); + + path[endbit] |= 1; // restore original end bit + + copy[endbit++] |= 1; + copy[endbit] = 0; // Null terminate + + return; +} + + + +void decode_frame(Byte * frame, int len, Byte * path, string * data, + Byte * pid, Byte * nr, Byte * ns, Byte * f_type, Byte * f_id, + Byte * rpt, Byte * pf, Byte * cr) +{ + int i; + int addr_end; + Byte ctrl; + Byte * savepath = path; + + i = 0; + addr_end = FALSE; + + *cr = SET_R; + *pf = SET_F; + data->Length = 0; + ctrl = 0; + *pid = 0; + *nr = 0; + *ns = 0; + *f_type = 0; + *f_id = 0; + *rpt = FALSE; + + if ((frame[6] & 128) == 128 && (frame[13] & 128) == 0) + *cr = SET_C; + + while (len > i && i < ADDR_MAX_LEN * 7) + { + *path++ = frame[i]; + if ((frame[i] & 1) == 1) + { + addr_end = TRUE; + break; + } + i++; + } + + if (addr_end == 0) + return; + + // clear the c and r bits from address + + savepath[6] &= 0x7f; // Mask + savepath[13] &= 0x7f; // Mask + + *path = 0; // add null terminate + + i++; // Points to ctrl byte + + ctrl = frame[i]; + + if ((ctrl & 16) == 16) + *pf = SET_P; + + if ((ctrl & 1) == 0) // I frame + { + *f_type = I_FRM; + *f_id = I_I; + *nr = (ctrl >> 5); + *ns = (ctrl >> 1) & 7; + } + else + { + // Not I + + *f_type = U_FRM; + + *f_id = ctrl & 239; + + switch (ctrl & 15) + { + case S_RR: + case S_RNR: + case S_REJ: + case S_SREJ: + + *f_type = S_FRM; + } + + if (*f_type == S_FRM) + { + *f_id = ctrl & 15; + *nr = ctrl >> 5; + } + } + + + if (*f_id == I_I || *f_id == U_UI) + { + i++; + *pid = frame[i]; + i++; + if (len > i) + stringAdd(data, &frame[i], len - i); + } + else if (*f_id == U_FRMR) + { + *pid = 0; + i++; + if (len > i) + stringAdd(data, &frame[i], len - i); + } +} + +void ax25_info_init(TAX25Port * AX25Sess) +{ + AX25Sess->info.stat_s_pkt = 0; + AX25Sess->info.stat_s_byte = 0; + AX25Sess->info.stat_r_pkt = 0; + AX25Sess->info.stat_r_byte = 0; + AX25Sess->info.stat_r_fc = 0; + AX25Sess->info.stat_fec_count = 0; + AX25Sess->info.stat_l_r_byte = 0; + AX25Sess->info.stat_l_s_byte = 0; + AX25Sess->info.stat_begin_ses = 0; + AX25Sess->info.stat_end_ses = 0; +} + + +void clr_frm_win(TAX25Port * AX25Sess) +{ + int i; + + for (i = 0; i < 8; i++) + initString(&AX25Sess->frm_win[i]); +} + +void ax25_init() +{ + int snd_ch, port, i; + + for (i = 0; i < 4; i++) + { + initTStringList(&list_exclude_callsigns[i]); + initTStringList(&list_exclude_APRS_frm[i]); + initTStringList(&list_digi_callsigns[i]); + initTStringList(&KISS_acked[i]); + + get_exclude_list(MyDigiCall[i], &list_digi_callsigns[i]); + get_exclude_list(exclude_callsigns[i], &list_exclude_callsigns[i]); + get_exclude_frm(exclude_APRS_frm[i], &list_exclude_APRS_frm[i]); + + } + + initTStringList(&list_incoming_mycalls); +// initTStringList(&list_incoming_mycalls_sock); + + for (snd_ch = 0; snd_ch < 4; snd_ch++) + { + for (port = 0; port < port_num; port++) + { + TAX25Port * AX25Sess = &AX25Port[snd_ch][port]; + + AX25Sess->hi_vs = 0; + AX25Sess->vs = 0; + AX25Sess->vr = 0; + AX25Sess->PID = PID_NO_L3; + initTStringList(&AX25Sess->in_data_buf); + initString(&AX25Sess->out_data_buf); + AX25Sess->t1 = 0; + AX25Sess->t2 = 0; + AX25Sess->t3 = 0; + AX25Sess->i_lo = 0; + AX25Sess->i_hi = 0; + AX25Sess->n1 = 0; + AX25Sess->n2 = 0; + AX25Sess->status = 0; + AX25Sess->clk_frack = 0; + initTStringList(&AX25Sess->frame_buf); + initTStringList(&AX25Sess->I_frame_buf); + initTStringList(&AX25Sess->frm_collector); + AX25Sess->corrcall[0] = 0; + AX25Sess->mycall[0] = 0; + AX25Sess->digi[0] = 0; + AX25Sess->Path[0] = 0; + AX25Sess->kind[0] = 0; + AX25Sess->socket = NULL; + ax25_info_init(AX25Sess); + clr_frm_win(AX25Sess); + } + } +} +/* + +procedure ax25_free; +var + snd_ch,port,i: byte; +begin + for snd_ch:=1 to 4 do + for port:=0 to port_num-1 do + begin + AX25Sess->in_data_buf.Free; + AX25Sess->frame_buf.Free; + AX25Sess->I_frame_buf.Free; + AX25Sess->frm_collector.Free; + end; + for i:=1 to 4 do + begin + all_frame_buf[i].Free; + list_exclude_callsigns[i].Free; + list_exclude_APRS_frm[i].Free; + list_digi_callsigns[i].Free; + end; + list_incoming_mycalls.Free; + list_incoming_mycalls_sock.Free; +end; +*/ +void write_ax25_info(TAX25Port * AX25Sess) +{ + UNUSED(AX25Sess); +} + +/*var + new: boolean; + t: text; + s: string; + time_ses: tdatetime; + time_ses_sec: extended; + call,mycall,spkt,sbyte,rpkt,rbyte,rfc,tcps,rcps,acps,startses,timeses: string; +begin + if stat_log then + begin + time_ses:=AX25Sess->info.stat_end_ses-AX25Sess->info.stat_begin_ses; + time_ses_sec:=time_ses*86400; //âðåìÿ ñåññèè â ñåêóíäàõ + if time_ses_sec<1 then exit; + mycall:=copy(AX25Sess->mycall+' ',1,9); + call:=copy(AX25Sess->corrcall+' ',1,9); + spkt:=copy(inttostr(AX25Sess->info.stat_s_pkt)+' ',1,6); + sbyte:=copy(inttostr(AX25Sess->info.stat_s_byte)+' ',1,9); + rpkt:=copy(inttostr(AX25Sess->info.stat_r_pkt)+' ',1,6); + rbyte:=copy(inttostr(AX25Sess->info.stat_r_byte)+' ',1,9); + rfc:=copy(inttostr(AX25Sess->info.stat_r_fc)+' ',1,6); + tcps:=copy(inttostr(round(AX25Sess->info.stat_s_byte/time_ses_sec))+' ',1,5); + rcps:=copy(inttostr(round(AX25Sess->info.stat_r_byte/time_ses_sec))+' ',1,5); + acps:=copy(inttostr(round(AX25Sess->info.stat_s_byte/time_ses_sec+AX25Sess->info.stat_r_byte/time_ses_sec))+' ',1,5); + timeses:=FormatDateTime('hh:mm:ss',time_ses); + startses:=FormatDateTime('dd-mm-yy hh:mm:ss',AX25Sess->info.stat_begin_ses); + s:=mycall+' '+call+' '+spkt+' '+sbyte+' '+rpkt+' '+rbyte+' '+rfc+' '+tcps+' '+rcps+' '+acps+' '+startses+' '+timeses; + assignfile(t,'log.txt'); + if FileSearch('log.txt','')='' then new:=TRUE else new:=FALSE; + if new then + begin + rewrite(t); + writeln(t,'Mycall CorrCall TXPkt TXByte RXPkt RXByte FCPkt TXCPS RXCPS T.CPS Begin session SesTime'); + writeln(t,'-------- --------- ------ --------- ------ --------- ------ ----- ----- ----- ----------------- --------'); + end + else append(t); + if (AX25Sess->info.stat_s_byte>0) or (AX25Sess->info.stat_r_byte>0) then writeln(t,s); + closefile(t); + end; +end; + +end. +*/ + + +/* +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 +*/ + + + +// Monitor Code - from moncode.asm + + +#define CMDBIT 4 // CURRENT MESSAGE IS A COMMAND +#define RESP 2 // CURRENT MSG IS RESPONSE +#define VER1 1 // CURRENT MSG IS VERSION 1 + + +#define UI 3 +#define SABM 0x2F +#define DISC 0x43 +#define DM 0x0F +#define UA 0x63 +#define FRMR 0x87 +#define RR 1 +#define RNR 5 +#define REJ 9 + +#define PFBIT 0x10 // POLL/FINAL BIT IN CONTROL BYTE + +#define NETROM_PID 0xCF +#define IP_PID 0xCC +#define ARP_PID 0xCD + +#define NODES_SIG 0xFF + +/* + +DllExport int APIENTRY SetTraceOptions(int mask, int mtxparam, int mcomparam) +{ + + // Sets the tracing options for DecodeFrame. Mask is a bit + // mask of ports to monitor (ie 101 binary will monitor ports + // 1 and 3). MTX enables monitoring on transmitted frames. MCOM + // enables monitoring of protocol control frames (eg SABM, UA, RR), + // as well as info frames. + + MMASK = mask; + MTX = mtxparam; + MCOM = mcomparam; + + return (0); +} + +DllExport int APIENTRY SetTraceOptionsEx(int mask, int mtxparam, int mcomparam, int monUIOnly) +{ + + // Sets the tracing options for DecodeFrame. Mask is a bit + // mask of ports to monitor (ie 101 binary will monitor ports + // 1 and 3). MTX enables monitoring on transmitted frames. MCOM + // enables monitoring of protocol control frames (eg SABM, UA, RR), + // as well as info frames. + + + MMASK = mask; + MTX = mtxparam; + MCOM = mcomparam; + MUIONLY = monUIOnly; + + return 0; +} + +*/ + +#define USHORT unsigned short + +UCHAR MCOM = 1; +UCHAR MTX = 1; +ULONG MMASK = 0xF; +UCHAR MUIONLY = 0; + +#define SREJ 0x0D +#define SABME 0x6F +#define XID 0xAF +#define TEST 0xE3 + +#define L4BUSY 0x80 // BNA - DONT SEND ANY MORE +#define L4NAK 0x40 // NEGATIVE RESPONSE FLAG +#define L4MORE 0x20 // MORE DATA FOLLOWS - FRAGMENTATION FLAG + +#define L4CREQ 1 // CONNECT REQUEST +#define L4CACK 2 // CONNECT ACK +#define L4DREQ 3 // DISCONNECT REQUEST +#define L4DACK 4 // DISCONNECT ACK +#define L4INFO 5 // INFORMATION +#define L4IACK 6 // INFORMATION ACK + +//#pragma pack(1) +#pragma pack(push, 1) + +struct myin_addr { + union { + struct { unsigned char s_b1, s_b2, s_b3, s_b4; } S_un_b; + struct { unsigned short s_w1, s_w2; } S_un_w; + unsigned long addr; + }; +}; + + +typedef struct _IPMSG +{ + // FORMAT OF IP HEADER + // + // NOTE THESE FIELDS ARE STORED HI ORDER BYTE FIRST (NOT NORMAL 8086 FORMAT) + + UCHAR VERLEN; // 4 BITS VERSION, 4 BITS LENGTH + UCHAR TOS; // TYPE OF SERVICE + USHORT IPLENGTH; // DATAGRAM LENGTH + USHORT IPID; // IDENTIFICATION + USHORT FRAGWORD; // 3 BITS FLAGS, 13 BITS OFFSET + UCHAR IPTTL; + UCHAR IPPROTOCOL; // HIGHER LEVEL PROTOCOL + USHORT IPCHECKSUM; // HEADER CHECKSUM + struct myin_addr IPSOURCE; + struct myin_addr IPDEST; + + UCHAR Data; + +} IPMSG, *PIPMSG; + + +typedef struct _TCPMSG +{ + + // FORMAT OF TCP HEADER WITHIN AN IP DATAGRAM + + // NOTE THESE FIELDS ARE STORED HI ORDER BYTE FIRST (NOT NORMAL 8086 FORMAT) + + USHORT SOURCEPORT; + USHORT DESTPORT; + + ULONG SEQNUM; + ULONG ACKNUM; + + UCHAR TCPCONTROL; // 4 BITS DATA OFFSET 4 RESERVED + UCHAR TCPFLAGS; // (2 RESERVED) URG ACK PSH RST SYN FIN + + USHORT WINDOW; + USHORT CHECKSUM; + USHORT URGPTR; + + +} TCPMSG, *PTCPMSG; + +typedef struct _UDPMSG +{ + + // FORMAT OF UDP HEADER WITHIN AN IP DATAGRAM + + // NOTE THESE FIELDS ARE STORED HI ORDER BYTE FIRST (NOT NORMAL 8086 FORMAT) + + USHORT SOURCEPORT; + USHORT DESTPORT; + USHORT LENGTH; + USHORT CHECKSUM; + UCHAR UDPData[0]; + +} UDPMSG, *PUDPMSG; + +// ICMP MESSAGE STRUCTURE + +typedef struct _ICMPMSG +{ + // FORMAT OF ICMP HEADER WITHIN AN IP DATAGRAM + + // NOTE THESE FIELDS ARE STORED HI ORDER BYTE FIRST (NOT NORMAL 8086 FORMAT) + + UCHAR ICMPTYPE; + UCHAR ICMPCODE; + USHORT ICMPCHECKSUM; + + USHORT ICMPID; + USHORT ICMPSEQUENCE; + UCHAR ICMPData[0]; + +} ICMPMSG, *PICMPMSG; + + +typedef struct _L3MESSAGE +{ + // + // NETROM LEVEL 3 MESSAGE - WITHOUT L2 INFO + // + UCHAR L3SRCE[7]; // ORIGIN NODE + UCHAR L3DEST[7]; // DEST NODE + UCHAR L3TTL; // TX MONITOR FIELD - TO PREVENT MESSAGE GOING // ROUND THE NETWORK FOR EVER DUE TO ROUTING LOOP +// +// NETROM LEVEL 4 DATA +// + UCHAR L4INDEX; // TRANSPORT SESSION INDEX + UCHAR L4ID; // TRANSPORT SESSION ID + UCHAR L4TXNO; // TRANSMIT SEQUENCE NUMBER + UCHAR L4RXNO; // RECEIVE (ACK) SEQ NUMBER + UCHAR L4FLAGS; // FRAGMENTATION, ACK/NAK, FLOW CONTROL AND MSG TYPE BITS + + UCHAR L4DATA[236]; //DATA + +} L3MESSAGE, *PL3MESSAGE; + + +typedef struct _MESSAGE +{ + // BASIC LINK LEVEL MESSAGE BUFFER LAYOUT + + struct _MESSAGE * CHAIN; + + UCHAR PORT; + USHORT LENGTH; + + UCHAR DEST[7]; + UCHAR ORIGIN[7]; + + // MAY BE UP TO 56 BYTES OF DIGIS + + UCHAR CTL; + UCHAR PID; + + union + { /* array named screen */ + UCHAR L2DATA[256]; + struct _L3MESSAGE L3MSG; + }; + + +}MESSAGE, *PMESSAGE; + +//#pragma pack() +#pragma pack(pop) + +char * strlop(char * buf, char delim); +UCHAR * DisplayINP3RIF(UCHAR * ptr1, UCHAR * ptr2, unsigned int msglen); +char * DISPLAY_NETROM(MESSAGE * ADJBUFFER, UCHAR * Output, int MsgLen); +UCHAR * DISPLAYIPDATAGRAM(IPMSG * IP, UCHAR * Output, int MsgLen); +char * DISPLAYARPDATAGRAM(UCHAR * Datagram, UCHAR * Output); + +int CountBits(unsigned long in) +{ + int n = 0; + while (in) + { + if (in & 1) n++; + in >>= 1; + } + return n; +} + +BOOL ConvToAX25(char * callsign, unsigned char * ax25call) +{ + int i; + + memset(ax25call, 0x40, 6); // in case short + ax25call[6] = 0x60; // default SSID + + for (i = 0; i < 7; i++) + { + if (callsign[i] == '-') + { + // + // process ssid and return + // + i = atoi(&callsign[i + 1]); + + if (i < 16) + { + ax25call[6] |= i << 1; + return (TRUE); + } + return (FALSE); + } + + if (callsign[i] == 0 || callsign[i] == 13 || callsign[i] == ' ' || callsign[i] == ',') + { + // + // End of call - no ssid + // + return (TRUE); + } + + ax25call[i] = callsign[i] << 1; + } + + // + // Too many chars + // + + return (FALSE); +} + + +int ConvFromAX25(unsigned char * incall, char * outcall) +{ + int in, out = 0; + unsigned char chr; + + memset(outcall, 0x20, 10); + + for (in = 0; in < 6; in++) + { + chr = incall[in]; + if (chr == 0x40) + break; + chr >>= 1; + outcall[out++] = chr; + } + + chr = incall[6]; // ssid + + if (chr == 0x42) + { + outcall[out++] = '-'; + outcall[out++] = 'T'; + return out; + } + + if (chr == 0x44) + { + outcall[out++] = '-'; + outcall[out++] = 'R'; + return out; + } + + chr >>= 1; + chr &= 15; + + if (chr > 0) + { + outcall[out++] = '-'; + if (chr > 9) + { + chr -= 10; + outcall[out++] = '1'; + } + chr += 48; + outcall[out++] = chr; + } + return (out); +} + +TKISSMode ** KissConnections = NULL; +int KISSConCount = 0; + +#define FEND 0xc0 +#define FESC 0xDB +#define TFEND 0xDC +#define TFESC 0xDD +#define KISS_ACKMODE 0x0C +#define KISS_DATA 0 + +int KISS_encode(UCHAR * KISSBuffer, int port, string * frame, int TXMON); + +void KISS_init() +{ + int i; + + KISS.data_in = newString(); + + // initTStringList(KISS.socket); + + for (i = 0; i < 4; i++) + { + initTStringList(&KISS.buffer[i]); + } +} + +void ProcessKISSFrame(void * socket, UCHAR * Msg, int Len); + +void KISSDataReceived(void * socket, unsigned char * data, int length) +{ + int i; + unsigned char * ptr1, *ptr2; + int Length; + + TKISSMode * KISS = NULL; + + if (KISSConCount == 0) + return; + + for (i = 0; i < KISSConCount; i++) + { + if (KissConnections[i]->Socket == socket) + { + KISS = KissConnections[i]; + break; + } + } + + if (KISS == NULL) + return; + + stringAdd(KISS->data_in, data, length); + + if (KISS->data_in->Length > 10000) // Probably AGW Data on KISS Port + { + KISS->data_in->Length = 0; + return; + } + + ptr1 = KISS->data_in->Data; + Length = KISS->data_in->Length; + + + while ((ptr2 = memchr(ptr1, FEND, Length))) + { + int Len = (ptr2 - ptr1); + + if (Len == 0) + { + // Start of frame + + mydelete(KISS->data_in, 0, 1); + + ptr1 = KISS->data_in->Data; + Length = KISS->data_in->Length; + + continue; + } + + // Process Frame + + if (Len < 350) // Drop obviously corrupt frames + ProcessKISSFrame(socket, ptr1, Len); + + mydelete(KISS->data_in, 0, Len + 1); + + ptr1 = KISS->data_in->Data; + Length = KISS->data_in->Length; + + } +} + +void analiz_frame(int snd_ch, string * frame, void * socket, boolean fecflag); + + +void ProcessKISSFrame(void * socket, UCHAR * Msg, int Len) +{ + int n = Len; + UCHAR c; + int ESCFLAG = 0; + UCHAR * ptr1, *ptr2; + int Chan; + int Opcode; + string * TXMSG; + unsigned short CRC; + UCHAR CRCString[2]; + + ptr1 = ptr2 = Msg; + + while (n--) + { + c = *(ptr1++); + + if (ESCFLAG) + { + // + // FESC received - next should be TFESC or TFEND + + ESCFLAG = 0; + + if (c == TFESC) + c = FESC; + + if (c == TFEND) + c = FEND; + + } + else + { + switch (c) + { + case FEND: + + // + // Either start of message or message complete + // + + // npKISSINFO->MSGREADY = TRUE; + return; + + case FESC: + + ESCFLAG = 1; + continue; + + } + } + + // + // Ok, a normal char + // + + *(ptr2++) = c; + + } + Len = ptr2 - Msg; + + Chan = (Msg[0] >> 4); + Opcode = Msg[0] & 0x0f; + + if (Chan > 3) + return; + + switch (Opcode) + { + case KISS_ACKMODE: + + // How best to do ACKMODE?? I think pass whole frame including CMD and ack bytes to all_frame_buf + + // But ack should only be sent to client that sent the message - needs more thought! + + TXMSG = newString(); + stringAdd(TXMSG, &Msg[0], Len); // include Control + + CRC = get_fcs(&Msg[3], Len - 3); // exclude control and ack bytes + + CRCString[0] = CRC & 0xff; + CRCString[1] = CRC >> 8; + + stringAdd(TXMSG, CRCString, 2); + + // Ackmode needs to know where to send ack back to, so save socket on end of data + + stringAdd(TXMSG, (unsigned char *)&socket, sizeof(socket)); + + // if KISS Optimise see if frame is really needed + + if (!KISS_opt[Chan]) + Add(&KISS.buffer[Chan], TXMSG); + else + { + if (add_raw_frames(Chan, TXMSG, &KISS.buffer[Chan])) + Add(&KISS.buffer[Chan], TXMSG); + } + + + return; + + case KISS_DATA: + + TXMSG = newString(); + stringAdd(TXMSG, &Msg[1], Len - 1); // include Control + + analiz_frame(Chan, TXMSG, socket, 0); + + free(TXMSG); + return; + } + + // Still need to process kiss control frames +} + + +void KISS_add_stream(void * Socket) +{ + // Add a new connection. Called when QT accepts an incoming call} + + TKISSMode * KISS; + + KissConnections = realloc(KissConnections, (KISSConCount + 1) * sizeof(void *)); + + KISS = KissConnections[KISSConCount++] = malloc(sizeof(KISS)); + + KISS->Socket = Socket; + KISS->data_in = newString(); + +} + +void KISS_del_socket(void * socket) +{ + int i; + + TKISSMode * KISS = NULL; + + if (KISSConCount == 0) + return; + + for (i = 0; i < KISSConCount; i++) + { + if (KissConnections[i]->Socket == socket) + { + KISS = KissConnections[i]; + break; + } + } + + if (KISS == NULL) + return; + + // Need to remove entry and move others down + + KISSConCount--; + + while (i < KISSConCount) + { + KissConnections[i] = KissConnections[i + 1]; + i++; + } +} + +TAX25Port * get_free_port(int snd_ch); + +TAX25Port * KISSConnectOut(void * Sess, char * CallFrom, char * CallTo, char * Digis, int Chan, void * Socket) +{ + TAX25Port * AX25Sess = 0; + char path[128]; + Byte axpath[80]; + + AX25Sess = get_free_port(Chan); + + if (AX25Sess) + { + AX25Sess->snd_ch = Chan; + AX25Sess->Sess = Sess; + strcpy(AX25Sess->mycall, CallFrom); + strcpy(AX25Sess->corrcall, CallTo); + AX25Sess->PID = 0xf0; + + sprintf(path, "%s,%s", CallTo, CallFrom); + + if (Digis) + { + strcat(path, ","); + strcat(path, Digis); + } + + AX25Sess->digi[0] = 0; + + // rst_timer(snd_ch, free_port); + + strcpy(AX25Sess->kind, "Outgoing"); + AX25Sess->socket = Socket; + + AX25Sess->pathLen = get_addr(path, axpath); + + if (AX25Sess->pathLen == 0) + return AX25Sess; // Invalid Path + + strcpy((char *)AX25Sess->Path, (char *)axpath); + reverse_addr(axpath, AX25Sess->ReversePath, AX25Sess->pathLen); + + set_link(AX25Sess, AX25Sess->Path); + return AX25Sess; + } + return 0; +} + + + +// Monitor Code - from moncode.asm + + +#define CMDBIT 4 // CURRENT MESSAGE IS A COMMAND +#define RESP 2 // CURRENT MSG IS RESPONSE +#define VER1 1 // CURRENT MSG IS VERSION 1 + + +#define UI 3 +#define SABM 0x2F +#define DISC 0x43 +#define DM 0x0F +#define UA 0x63 +#define FRMR 0x87 +#define RR 1 +#define RNR 5 +#define REJ 9 + +#define SREJ 0x0D +#define SABME 0x6F +#define XID 0xAF +#define TEST 0xE3 + + +#define PFBIT 0x10 // POLL/FINAL BIT IN CONTROL BYTE + +#define NETROM_PID 0xCF +#define IP_PID 0xCC +#define ARP_PID 0xCD + +#define NODES_SIG 0xFF + +char ShortDT[] = "HH:MM:SS"; + +char * ShortDateTime() +{ + struct tm * tm; + time_t NOW = time(NULL); + + tm = gmtime(&NOW); + + sprintf(ShortDT, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec); + return ShortDT; +} + + + +char FrameData[1024] = ""; + +char * frame_monitor(string * frame, char * code, int tx_stat) +{ + char mon_frm[512]; + char Path[256]; + + char * frm = "???"; + Byte * datap; + Byte _data[512] = ""; + Byte * p_data = _data; + int _datalen; +; + char CallFrom[10], CallTo[10], Digi[80]; + + char TR = 'R'; + char codestr[16] = ""; + + integer i; + int len; + + + Byte pid, nr, ns, f_type, f_id; + Byte rpt, cr, pf; + Byte path[80]; + char c; + const char * p; + + string * data = newString(); + + if (code[0] && strlen(code) < 14) + sprintf(codestr, "[%s]", code); + + if (tx_stat) + TR = 'T'; + + if (tx_stat) // TX frame has control byte + + decode_frame(frame->Data +1 , frame->Length - 1, path, data, &pid, &nr, &ns, &f_type, &f_id, &rpt, &pf, &cr); + else + decode_frame(frame->Data, frame->Length, path, data, &pid, &nr, &ns, &f_type, &f_id, &rpt, &pf, &cr); + + datap = data->Data; + + len = data->Length; + + // if (pid == 0xCF) + // data = parse_NETROM(data, f_id); + // IP parsing + // else if (pid == 0xCC) + // data = parse_IP(data); + // ARP parsing + // else if (pid == 0xCD) + // data = parse_ARP(data); + // + + if (len > 0) + { + for (i = 0; i < len; i++) + { + if (datap[i] > 31 || datap[i] == 13 || datap[i] == 9) + *(p_data++) = datap[i]; + } + } + + _datalen = p_data - _data; + + if (_datalen) + { + Byte * ptr = _data; + i = 0; + + // remove successive cr or cr on end while (i < _datalen) + + while (i < _datalen) + { + if ((_data[i] == 13) && (_data[i + 1] == 13)) + i++; + else + *(ptr++) = _data[i++]; + } + + if (*(ptr - 1) == 13) + ptr--; + + *ptr = 0; + + _datalen = ptr - _data; + } + + get_monitor_path(path, CallTo, CallFrom, Digi); + + if (cr) + { + c = 'C'; + if (pf) + p = " P"; + else p = ""; + } + else + { + c = 'R'; + if (pf) + p = " F"; + else + p = ""; + } + + switch (f_id) + { + case I_I: + + frm = "I"; + break; + + case S_RR: + + frm = "RR"; + break; + + case S_RNR: + + frm = "RNR"; + break; + + case S_REJ: + + frm = "REJ"; + break; + + case S_SREJ: + + frm = "SREJ"; + break; + + case U_SABM: + + frm = "SABM"; + break; + + case SABME: + + frm = "SABME"; + break; + + case U_DISC: + + frm = "DISC"; + break; + + case U_DM: + + frm = "DM"; + break; + + case U_UA: + + frm = "UA"; + break; + + case U_FRMR: + + frm = "FRMR"; + break; + + case U_UI: + + frm = "UI"; + } + +// 07:29:42T G8BPQ - 2 > TEST Port = 20 < UI > : +// helllo +// 07:30: 8T G8BPQ - 2 > ID Port = 20 < UI C > : +// Network node(BPQ) + + if (Digi[0]) +// sprintf(Path, "Fm %s To %s Via %s <%s %c%s", CallFrom, CallTo, Digi, frm, c, p); + sprintf(Path, "%s%c %s>%s,%s <%s %c%s", ShortDateTime(), TR, CallFrom, CallTo, Digi, frm, c, p); + else +// sprintf(Path, "Fm %s To %s <%s %c %s", CallFrom, CallTo, frm, c, p); + sprintf(Path, "%s%c %s>%s <%s %c%s", ShortDateTime(), TR, CallFrom, CallTo, frm, c, p); + + + switch (f_type) + { + case I_FRM: + + //mon_frm = Path + ctrl + ' R' + inttostr(nr) + ' S' + inttostr(ns) + ' pid=' + dec2hex(pid) + ' Len=' + inttostr(len) + ' >' + time_now + #13 + _data + #13#13; + sprintf(mon_frm, "%s R%d S%d>%s\r%s\r", Path, nr, ns, codestr, _data); + + break; + + case U_FRM: + + if (f_id == U_UI) + { + sprintf(mon_frm, "%s>:\r%s\r", Path, _data); // "= Path + ctrl + '>' + time_now + #13; + } + else if (f_id == U_FRMR) + { + sprintf(mon_frm, "%s>%02x %02x %02x\r", Path, datap[0], datap[1], datap[2]); // "= Path + ctrl + '>' + time_now + #13; + } + else + sprintf(mon_frm, "%s>%s\r", Path, codestr); // "= Path + ctrl + '>' + time_now + #13; + + break; + + case S_FRM: + + // mon_frm = Path + ctrl + ' R' + inttostr(nr) + ' >' + time_now + #13; + sprintf(mon_frm, "%s R%d>%s\r", Path, nr, codestr); // "= Path + ctrl + '>' + time_now + #13; + + break; + + } + sprintf(FrameData, "%s", mon_frm); + + freeString(data); + return FrameData; +} + + + + + + + + diff --git a/ax25.h b/ax25.h new file mode 100644 index 0000000..8895eee --- /dev/null +++ b/ax25.h @@ -0,0 +1,304 @@ +// ax.25 code for QtTERMTCP KISS Mode + +// based on SoundModem/QtSoundModem ax.25 code + +// The underlying code allows for up to four KISS ports, but at the moment +// the config and user interface only supports 1 + +// The code supports KISS over a Serial port or TCP connection + +#include +#include +#include +#include +#include +#include + +#define UNUSED(x) (void)(x) + +#define single float +#define boolean int +#define Byte unsigned char // 0 to 255 +#define Word unsigned short // 0 to 65,535 +#define SmallInt short // -32,768 to 32,767 +#define LongWord unsigned int // 0 to 4,294,967,295 +// Int6 : Cardinal; // 0 to 4,294,967,295 +#define LongInt int // -2,147,483,648 to 2,147,483,647 +#define Integer int // -2,147,483,648 to 2,147,483,647 +//#define Int64 long long // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 + +//#define Byte unsigned char // 0 to 255 +#define word unsigned short // 0 to 65,535 +#define smallint short // -32,768 to 32,767 +#define longword unsigned int // 0 to 4,294,967,295 + // Int6 : Cardinal; // 0 to 4,294,967,295 +#define longint int // -2,147,483,648 to 2,147,483,647 +#define integer int // -2,147,483,648 to 2,147,483,647 + +typedef unsigned long ULONG; + +#define UCHAR unsigned char +#define UINT unsigned int +#define BOOL int +#define TRUE 1 +#define FALSE 0 + +#define FEND 0xc0 +#define FESC 0xDB +#define TFEND 0xDC +#define TFESC 0xDD + +#define port_num 32 // ?? Max AGW sessions +#define PKT_ERR 15 // Minimum packet size, bytes +#define I_MAX 7 // Maximum number of packets + + +// Status flags + +#define STAT_NO_LINK 0 +#define STAT_LINK 1 +#define STAT_CHK_LINK 2 +#define STAT_WAIT_ANS 3 +#define STAT_TRY_LINK 4 +#define STAT_TRY_UNLINK 5 + + + // Ñmd,Resp,Poll,Final,Digipeater flags +#define SET_P 1 +#define SET_F 0 +#define SET_C 1 +#define SET_R 0 +#define SET_NO_RPT 0 +#define SET_RPT 1 + // Frame ID flags +#define I_FRM 0 +#define S_FRM 1 +#define U_FRM 2 +#define I_I 0 +#define S_RR 1 +#define S_RNR 5 +#define S_REJ 9 +#define S_SREJ 0x0D +#define U_SABM 47 +#define U_DISC 67 +#define U_DM 15 +#define U_UA 99 +#define U_FRMR 135 +#define U_UI 3 + // PID flags +#define PID_X25 0x01 // 00000001-CCIT X25 PLP +#define PID_SEGMENT 0x08 // 00001000-Segmentation fragment +#define PID_TEXNET 0xC3 // 11000011-TEXNET Datagram Protocol +#define PID_LQ 0xC4 // 11001000-Link Quality Protocol +#define PID_APPLETALK 0xCA // 11001010-Appletalk +#define PID_APPLEARP 0xCB // 11001011-Appletalk ARP +#define PID_IP 0xCC // 11001100-ARPA Internet Protocol +#define PID_ARP 0xCD // 11001101-ARPA Address Resolution Protocol +#define PID_NET_ROM 0xCF // 11001111-NET/ROM + +#define FX25_LOAD 1 + +#define MODE_OUR 0 +#define MODE_OTHER 1 +#define MODE_RETRY 2 + +#define TIMER_FREE 0 +#define TIMER_BUSY 1 +#define TIMER_OFF 2 +#define TIMER_EVENT_ON 3 +#define TIMER_EVENT_OFF 4 + + +typedef struct string_T +{ + unsigned char * Data; + int Length; + int AllocatedLength; // A reasonable sized block is allocated at the start to speed up adding chars + +}string; + +typedef struct TStringList_T +{ + int Count; + string ** Items; + +} TStringList; + + +typedef struct AGWUser_t +{ + void *socket; + string * data_in; + TStringList AGW_frame_buf; + boolean Monitor; + boolean Monitor_raw; + boolean reportFreqAndModem; // Can report modem and frequency to host + +} AGWUser; + +typedef struct TAX25Info_t +{ + longint stat_s_pkt; + longint stat_s_byte; + longint stat_r_pkt; + longint stat_r_byte; + longint stat_r_fc; + longint stat_fec_count; + time_t stat_begin_ses; + time_t stat_end_ses; + longint stat_l_r_byte; + longint stat_l_s_byte; + +} TAX25Info; + +typedef struct TAX25Port_t +{ + Byte hi_vs; + Byte vs; + Byte vr; + Byte PID; + TStringList in_data_buf; + TStringList frm_collector; + string frm_win[8]; + string out_data_buf; + word t1; + word t2; + word t3; + Byte i_lo; + Byte i_hi; + word n1; + word n2; + word IPOLL_cnt; + TStringList frame_buf; //áóôåð êàäðîâ íà ïåðåäà÷ó + TStringList I_frame_buf; + Byte status; + word clk_frack; + char corrcall[10]; + char mycall[10]; + UCHAR digi[56]; + UCHAR Path[80]; // Path in ax25 format - added to save building it each time + UCHAR ReversePath[80]; + int snd_ch; // Simplifies parameter passing + int port; + int pathLen; + void * socket; + void * Sess; + char kind[16]; + TAX25Info info; +} TAX25Port; + +typedef struct TKISSMode_t +{ + string * data_in; + void * Socket; // Used as a key + + // Not sure what rest are used for. Seems to be one per channel + + TStringList buffer[4]; // Outgoing Frames + +} TKISSMode; + +// Dephi emulation functions + +string * Strings(TStringList * Q, int Index); +void Clear(TStringList * Q); +int Count(TStringList * List); + +string * newString(); +string * copy(string * Source, int StartChar, int Count); +TStringList * newTStringList(); + +void freeString(string * Msg); +void initString(string * S); +void initTStringList(TStringList* T); + +// Two delete() This is confusing!! +// Not really - one acts on String, other TStringList + +void Delete(TStringList * Q, int Index); +void mydelete(string * Source, int StartChar, int Count); +void move(UCHAR * SourcePointer, UCHAR * DestinationPointer, int CopyCount); +void fmove(float * SourcePointer, float * DestinationPointer, int CopyCount); +void setlength(string * Msg, int Count); // Set string length +string * stringAdd(string * Msg, UCHAR * Chars, int Count); // Extend string +void Assign(TStringList * to, TStringList * from); // Duplicate from to to +string * duplicateString(string * in); +int my_indexof(TStringList * l, string * s); +boolean compareStrings(string * a, string * b); +int Add(TStringList * Q, string * Entry); +void Debugprintf(const char * format, ...); +void ax25_info_init(TAX25Port * AX25Sess); +void clr_frm_win(TAX25Port * AX25Sess); +void decode_frame(Byte * frame, int len, Byte * path, string * data, + Byte * pid, Byte * nr, Byte * ns, Byte * f_type, Byte * f_id, + Byte * rpt, Byte * pf, Byte * cr); +#ifdef __cplusplus +extern "C" void KISSSendtoServer(myTcpSocket* Socket, char * Data, int Length); +extern "C" void monitor_frame(int snd_ch, string * frame, char * code, int tx, int excluded); +extern "C" void WriteDebugLog(char * Mess); +extern "C" void SendtoTerm(Ui_ListenSession * Sess, char * Msg, int Len); +extern "C" void ClearSessLabel(Ui_ListenSession * Sess); +extern "C" void rst_timer(TAX25Port * AX25Sess); +extern "C" void Send_UI(int port, Byte PID, char * CallFrom, char *CallTo, Byte * Msg, int MsgLen); +#else +void monitor_frame(int snd_ch, string * frame, char * code, int tx, int excluded); +void SendtoTerm(void * Sess, char * Msg, int Len); +void ClearSessLabel(void * Sess); +void WriteDebugLog(char * Mess); +void AX25_disc(TAX25Port * AX25Sess, Byte mode); +void rst_timer(TAX25Port * AX25Sess); +void Send_UI(int port, Byte PID, char * CallFrom, char *CallTo, Byte * Msg, int MsgLen); +#endif + +BOOL ConvToAX25(char * callsign, unsigned char * ax25call); +int ConvFromAX25(unsigned char * incall, char * outcall); +void reverse_addr(Byte * path, Byte * revpath, int Len); +void set_DM(int snd_ch, Byte * path); +void set_link(TAX25Port * AX25Sess, UCHAR * axpath); +boolean is_last_digi(Byte *path); +boolean is_correct_path(Byte * path, Byte pid); +int number_digi(unsigned char * path); +void AX25_conn(TAX25Port * AX25Sess, int snd_ch, Byte mode); +void write_ax25_info(TAX25Port * AX25Sess); +void rst_values(TAX25Port * AX25Sess); + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern boolean dyn_frack[4]; +extern Byte recovery[4]; +extern Byte users[4]; + +extern int resptime[4]; +extern int slottime[4]; +extern int persist[4]; +extern int kisspaclen[4]; +extern int fracks[4]; +extern int frack_time[4]; +extern int idletime[4]; +extern int redtime[4]; +extern int IPOLL[4]; +extern int maxframe[4]; +extern int TXFrmMode[4]; + +extern char MyDigiCall[4][512]; +extern char exclude_callsigns[4][512]; +extern char exclude_APRS_frm[4][512]; + +extern TStringList list_exclude_callsigns[4]; +extern TStringList list_exclude_APRS_frm[4]; +extern TStringList list_digi_callsigns[4]; + +extern int max_frame_collector[4]; +extern boolean KISS_opt[4]; + +extern TAX25Port AX25Port[4][port_num]; + +extern TStringList KISS_acked[]; +extern TStringList KISS_iacked[]; + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/ax25_l2.c b/ax25_l2.c new file mode 100644 index 0000000..60d587c --- /dev/null +++ b/ax25_l2.c @@ -0,0 +1,1726 @@ +/* +Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO + +This file is part of QtSoundModem + +QtSoundModem 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. + +QtSoundModem 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 QtSoundModem. If not, see http://www.gnu.org/licenses + +*/ + +// UZ7HO Soundmodem Port by John Wiseman G8BPQ + +#include "ax25.h" + +UCHAR TimerEvent = TIMER_EVENT_OFF; +extern int busy; +int listenEnable; +void * KISSSockCopy[4]; + +string * make_frame(string * data, Byte * path, Byte pid, Byte nr, Byte ns, Byte f_type, Byte f_id, boolean rpt, boolean pf, boolean cr); +void rst_t3(TAX25Port * AX25Sess); +void CheckUIFrame(unsigned char * path, string * data); +TAX25Port * get_user_port(int snd_ch, Byte * path); + +void inc_frack(TAX25Port * AX25Sess) +{ + AX25Sess->clk_frack++; +} + + +void rst_frack(TAX25Port * AX25Sess) +{ + AX25Sess->clk_frack = 0; +} + +void inc_t1(TAX25Port * AX25Sess) +{ + AX25Sess->t1++; +} + +void rst_t1(TAX25Port * AX25Sess) +{ + AX25Sess->t1 = 0; +} + +void inc_t3(TAX25Port * AX25Sess) +{ + AX25Sess->t3++; +} + +void rst_t3(TAX25Port * AX25Sess) +{ + AX25Sess->t3 = 0; +} + + +void rst_values(TAX25Port * AX25Sess) +{ + AX25Sess->IPOLL_cnt = 0; + AX25Sess->hi_vs = 0; + AX25Sess->vs = 0; + AX25Sess->vr = 0; + Clear(&AX25Sess->I_frame_buf); + Clear(&AX25Sess->in_data_buf); + Clear(&AX25Sess->frm_collector); + + ax25_info_init(AX25Sess); + clr_frm_win(AX25Sess); +} + + +void rst_timer(TAX25Port * AX25Sess) +{ + rst_frack(AX25Sess); + rst_t1(AX25Sess); + rst_t3(AX25Sess); +} + +void upd_i_lo(TAX25Port * AX25Sess, int n) //Update the counter of the first frame in the I-frame buffer +{ + AX25Sess->i_lo = n; +} + +void upd_i_hi(TAX25Port * AX25Sess, int n) //Update last frame counter in I-frame buffer +{ + AX25Sess->i_hi = n; +} + +void upd_vs(TAX25Port * AX25Sess, int n) //Update the counter of the next frame to transmit +{ + AX25Sess->vs = ++n & 7; +} + +void upd_vr(TAX25Port * AX25Sess, int n) //Refresh the counter of the next frame at the reception +{ + AX25Sess->vr = ++n & 7; +} + + +void Frame_Optimize(TAX25Port * AX25Sess, TStringList * buf) +{ + // I think this removes redundant frames from the TX Queue (eg repeated RR frames) + + string * frame; + Byte path[80]; + string * data = newString(); + + Byte pid, nr, ns, f_type, f_id, rpt, cr, pf; + boolean curr_req, optimize; + int i, k; + char need_frm[8] = ""; + int index = 0; + boolean PollRR; + boolean PollREJ; + + PollRR = FALSE; + PollREJ = FALSE; + curr_req = FALSE; + + // Check Poll RR and REJ frame + + i = 0; + + while (i < buf->Count && !PollREJ) + { + frame = Strings(buf, i); + // TX frame has kiss control on front + + decode_frame(frame->Data + 1, frame->Length - 1, path, data, &pid, &nr, &ns, &f_type, &f_id, &rpt, &pf, &cr); + + if (cr == SET_R && pf == SET_P) + { + if (f_id == S_REJ) + PollREJ = TRUE; + else if (f_id == S_RR && nr == AX25Sess->vr) + PollRR = TRUE; + } + i++; + } + + // Performance of the REJ Cards: Optional Rej Cards + + i = 0; + + while (i < buf->Count) + { + optimize = TRUE; + frame = Strings(buf, i); + decode_frame(frame->Data + 1, frame->Length - 1, path, data, &pid, &nr, &ns, &f_type, &f_id, &rpt, &pf, &cr); + + if (f_id == S_REJ && cr == SET_R) + { + if ((pf == SET_F && PollREJ) || nr != AX25Sess->vr) + { + Debugprintf("Optimizer dropping REJ nr %d vr %d pf %d PollREJ %d", nr, AX25Sess->vr, pf, PollREJ); + Delete(buf, i); + optimize = FALSE; + } + if (nr == AX25Sess->vr) + curr_req = TRUE; + } + if (optimize) + i++; + } + + // Performance Options + + i = 0; + + while (i < buf->Count) + { + need_frm[0] = 0; + index = 0; + k = AX25Sess->i_lo; + + while (k != AX25Sess->vs) + { + need_frm[index++] = k + 'A'; + k++; + k &= 7; + } + + optimize = TRUE; + + frame = Strings(buf, i); + + decode_frame(frame->Data +1 , frame->Length - 1, path, data, &pid, &nr, &ns, &f_type, &f_id, &rpt, &pf, &cr); + + if (f_id == S_RR) + { + // RR Cards Methods: Optional RR, F Cards + if (cr == SET_R) + { + if (nr != AX25Sess->vr || ((pf == SET_F) && PollRR) || curr_req) + { + Debugprintf("Optimizer dropping RR nr %d vr %d pf %d PollRR %d", nr, AX25Sess->vr, pf, PollRR); + + Delete(buf, i); + optimize = FALSE; + } + } + + + // RR Cards Methods : Optional RR, P Cards + if (cr == SET_C) + { + if (AX25Sess->status == STAT_LINK || AX25Sess->status == STAT_WAIT_ANS) + { + Debugprintf("Optimizer dropping RR nr %d vr %d pf %d PollRR %d", nr, AX25Sess->vr, pf, PollRR); + Delete(buf, i); + optimize = FALSE; + } + } + } + // I - Cards Methods : Options for I - Cards + else if (f_id == I_I) + { + if (strchr(need_frm, ns + 'A') == 0) + { + Delete(buf, i); + optimize = FALSE; + } + else + { + if (nr != AX25Sess->vr) + buf->Items[i] = make_frame(data, path, pid, AX25Sess->vr, ns, f_type, f_id, rpt, pf, cr); + } + } + + // SABM Applications + + if (f_id == U_SABM) + { + if (AX25Sess->status != STAT_TRY_LINK) + { + Delete(buf, i); + optimize = FALSE; + } + } + + if (optimize) + i++; + } +} + +int KISS_encode(UCHAR * KISSBuffer, int port, string * frame) +{ + // Encode frame + + UCHAR * ptr1 = frame->Data; + UCHAR TXCCC = 0; + int Len = frame->Length; + UCHAR * ptr2 = &KISSBuffer[2]; + UCHAR c; + + // TX Frame has control byte on front + + ptr1++; + Len--; + + KISSBuffer[0] = FEND; + KISSBuffer[1] = port << 4; + + TXCCC ^= KISSBuffer[1]; + + while (Len--) + { + c = *(ptr1++); + TXCCC ^= c; + + switch (c) + { + case FEND: + (*ptr2++) = FESC; + (*ptr2++) = TFEND; + break; + + case FESC: + + (*ptr2++) = FESC; + (*ptr2++) = TFESC; + break; + + // Drop through + + default: + + (*ptr2++) = c; + } + } + (*ptr2++) = FEND; + + return (int)(ptr2 - KISSBuffer); +} + +void KISSSendtoServer(void * Socket, char * Data, int Length); + +void add_pkt_buf(TAX25Port * AX25Sess, string * data) +{ +// boolean found = 0; +// int i = 0; + UCHAR KISSBuffer[512]; + int Length; + + // ? Don't we just send to TNC? + + Length = KISS_encode(KISSBuffer, 0, data); + + KISSSendtoServer(AX25Sess->socket, KISSBuffer, Length); + + monitor_frame(0, data, "", 1, 0); // Monitor + freeString(data); + + +// while (i < AX25Sess->frame_buf.Count && !found) +// { +// found = compareStrings(Strings(&AX25Sess->frame_buf, i++), data); +// } + +// if (found) +// freeString(data); +// else +// Add(&AX25Sess->frame_buf, data); +} + +void add_I_FRM(TAX25Port * AX25Sess) +{ + string * data; + int i; + + upd_i_lo(AX25Sess, AX25Sess->vs); + + while (AX25Sess->in_data_buf.Count > 0 && AX25Sess->I_frame_buf.Count != maxframe[AX25Sess->snd_ch]) + { + data = duplicateString(Strings(&AX25Sess->in_data_buf, 0)); + Delete(&AX25Sess->in_data_buf, 0); + Add(&AX25Sess->I_frame_buf, data); + } + if (AX25Sess->I_frame_buf.Count > 0) + { + for (i = 0; i < AX25Sess->I_frame_buf.Count; i++) + { + upd_i_hi(AX25Sess, AX25Sess->vs); + upd_vs(AX25Sess, AX25Sess->vs); + AX25Sess->hi_vs = AX25Sess->vs; // Last transmitted frame + } + } +} + + +void delete_I_FRM(TAX25Port * AX25Sess, int nr) +{ + int i; + + i = AX25Sess->i_lo; + + while (i != nr) + { + if (AX25Sess->I_frame_buf.Count > 0) + { + AX25Sess->info.stat_s_pkt++; + AX25Sess->info.stat_s_byte += Strings(&AX25Sess->I_frame_buf, 0)->Length; + Delete(&AX25Sess->I_frame_buf, 0); + } + + i++; + i &= 7; + } + upd_i_lo(AX25Sess, nr); +} + +void delete_I_FRM_port(TAX25Port * AX25Sess) +{ + string * frame; + char path[80] = ""; + string data= { 0 }; + + Byte pid, nr, ns, f_type, f_id, rpt, cr, pf; + boolean optimize; + int i = 0; + + while (i < AX25Sess->frame_buf.Count) + { + optimize = TRUE; + frame = Strings(&AX25Sess->frame_buf, i); + + decode_frame(frame->Data, frame->Length, path, &data, &pid, &nr, &ns, &f_type, &f_id, &rpt, &pf, &cr); + + if (f_id == I_I) + { + Delete(&AX25Sess->frame_buf, i); + optimize = FALSE; + } + if (optimize) + i++; + } +} + +void send_data_buf(TAX25Port * AX25Sess, int nr) +{ + int i; + boolean new_frames; + boolean PF_bit; + + if (AX25Sess->status != STAT_LINK) + return; + + AX25Sess->IPOLL_cnt = 0; + AX25Sess->vs = nr; + delete_I_FRM(AX25Sess, nr); // ?? free acked frames +// delete_I_FRM_port(AX25Sess); + + if (TXFrmMode[AX25Sess->snd_ch] == 1) + { + new_frames = FALSE; + + if (AX25Sess->I_frame_buf.Count < 2) + { + add_I_FRM(AX25Sess); + AX25Sess->status = STAT_LINK; + new_frames = TRUE; + } + + if (AX25Sess->I_frame_buf.Count > 0) + { + if (new_frames) + { + for (i = 0; i < AX25Sess->I_frame_buf.Count; i++) + { + if (i == AX25Sess->I_frame_buf.Count - 1) + PF_bit = SET_P; + else + PF_bit = SET_F; + + add_pkt_buf(AX25Sess, make_frame(Strings(&AX25Sess->I_frame_buf, i), AX25Sess->Path, AX25Sess->PID, AX25Sess->vr, ((AX25Sess->i_lo + i) & 7), I_FRM, I_I, FALSE, PF_bit, SET_C)); + } + } + if (!new_frames) + { + add_pkt_buf(AX25Sess, make_frame(Strings(&AX25Sess->I_frame_buf, 0), AX25Sess->Path, AX25Sess->PID, AX25Sess->vr, AX25Sess->i_lo, I_FRM, I_I, FALSE, SET_P, SET_C)); //SET_P + upd_vs(AX25Sess, AX25Sess->vs); + } + AX25Sess->status = STAT_WAIT_ANS; + rst_timer(AX25Sess); + } + } + + if (TXFrmMode[AX25Sess->snd_ch] == 0) + { + add_I_FRM(AX25Sess); + AX25Sess->status = STAT_LINK; + + if (AX25Sess->I_frame_buf.Count > 0) + { + for (i = 0; i < AX25Sess->I_frame_buf.Count; i++) + { + if (i == AX25Sess->I_frame_buf.Count - 1) + PF_bit = SET_P; + else + PF_bit = SET_F; + add_pkt_buf(AX25Sess, make_frame(Strings(&AX25Sess->I_frame_buf, i), AX25Sess->Path, AX25Sess->PID, AX25Sess->vr, ((AX25Sess->i_lo + i) & 7), I_FRM, I_I, FALSE, PF_bit, SET_C)); + } + AX25Sess->status = STAT_WAIT_ANS; + rst_timer(AX25Sess); + } + } +} + + +void send_data_buf_srej(TAX25Port * AX25Sess, int nr) +{ + // Similar to send_data_buf but only sends the requested I frame with P set + + int i = 0; + boolean new_frames; + boolean PF_bit; + + if (AX25Sess->status != STAT_LINK) + return; + + AX25Sess->IPOLL_cnt = 0; + AX25Sess->vs = nr; + delete_I_FRM(AX25Sess, nr); // ?? free acked frames + + new_frames = FALSE; + + add_I_FRM(AX25Sess); + AX25Sess->status = STAT_LINK; + new_frames = TRUE; + + if (AX25Sess->I_frame_buf.Count > 0) + { + if (new_frames) + { + PF_bit = SET_P; + + add_pkt_buf(AX25Sess, make_frame(Strings(&AX25Sess->I_frame_buf, i), AX25Sess->Path, AX25Sess->PID, AX25Sess->vr, ((AX25Sess->i_lo + i) & 7), I_FRM, I_I, FALSE, PF_bit, SET_C)); + } + else + { + + add_pkt_buf(AX25Sess, make_frame(Strings(&AX25Sess->I_frame_buf, 0), AX25Sess->Path, AX25Sess->PID, AX25Sess->vr, AX25Sess->i_lo, I_FRM, I_I, FALSE, SET_P, SET_C)); //SET_P + upd_vs(AX25Sess, AX25Sess->vs); + } + } + AX25Sess->status = STAT_WAIT_ANS; + rst_timer(AX25Sess); +} + +void write_frame_collector(TAX25Port * AX25Sess, int ns, string * data) +{ + Byte i; + char frm_ns; + string * frm; + boolean found ; + boolean need_frm; + + if (max_frame_collector[AX25Sess->snd_ch] < 1) + return; + + need_frm = FALSE; + i = 1; + do + { + if (ns == ((AX25Sess->vr + i) & 7)) + need_frm = TRUE; + + i++; + + } while (i <= max_frame_collector[AX25Sess->snd_ch] && !need_frm); + + if (need_frm) + { + frm_ns = ns; + found = FALSE; + i = 0; + + if (AX25Sess->frm_collector.Count > 0) + { + do + { + frm = Strings(&AX25Sess->frm_collector, i); + + if (frm_ns == frm->Data[0]) + found = TRUE; + i++; + } + while (!found && i != AX25Sess->frm_collector.Count); + + } + + if (!found) + { + string * frm = newString(); + + stringAdd(frm, (char *)&frm_ns, 1); + stringAdd(frm, data->Data, data->Length); + Add(&AX25Sess->frm_collector, frm); + } + } +} + +string * read_frame_collector(TAX25Port * AX25Sess, boolean fecflag) +{ + // Look for required frame no in saved frames + + string * frm; + string * data = newString(); + + int i = 0; + Byte frm_ns; + + while (i < AX25Sess->frm_collector.Count) + { + frm = duplicateString(Strings(&AX25Sess->frm_collector, i)); + + frm_ns = frm->Data[0]; + + if (frm_ns == AX25Sess->vr) + { + Delete(&AX25Sess->frm_collector, i); + + upd_vr(AX25Sess, AX25Sess->vr); + + mydelete(frm, 0, 1); // Remove vr from front + + stringAdd(data, frm->Data, frm->Length); + + AX25Sess->info.stat_r_pkt++; + AX25Sess->info.stat_r_fc++; + + if (fecflag) + AX25Sess->info.stat_fec_count++; + + AX25Sess->info.stat_r_byte += frm->Length; + AX25Sess->frm_win[frm_ns].Length = frm->Length; //Save the frame to the window buffer + AX25Sess->frm_win[frm_ns].Data = frm->Data; //Save the frame to the window buffer + } + + i++; + } + return data; +} + + +/////////////////////////// SET-FRAMES ////////////////////////////////// + +void set_chk_link(TAX25Port * AX25Sess, Byte * path) +{ + boolean b_IPOLL; + int len; + + AX25Sess->status = STAT_CHK_LINK; + + // if AX25Sess->digi<>'' then path:=path+','+reverse_digi(AX25Sess->digi); + + b_IPOLL = FALSE; + + if (AX25Sess->I_frame_buf.Count > 0 && AX25Sess->IPOLL_cnt < 2) + { + len = Strings(&AX25Sess->I_frame_buf, 0)->Length; + + if (len > 0 && len <= IPOLL[AX25Sess->snd_ch]) + { + + b_IPOLL = TRUE; + AX25Sess->IPOLL_cnt++; + } + } + if (b_IPOLL) + add_pkt_buf(AX25Sess, make_frame(Strings(&AX25Sess->I_frame_buf, 0), path, AX25Sess->PID, AX25Sess->vr, AX25Sess->i_lo, I_FRM, I_I, FALSE, SET_P, SET_C)); + else + add_pkt_buf(AX25Sess, make_frame(NULL, path, 0xF0, AX25Sess->vr, 0, S_FRM, S_RR, FALSE, SET_P, SET_C)); + + inc_frack(AX25Sess); +} + + + +// This seems to start a connection + +void set_link(TAX25Port * AX25Sess, UCHAR * axpath) +{ + if (AX25Sess->status != STAT_LINK) + { + string nullstring; + nullstring.Length = 0; + + rst_values(AX25Sess); + + AX25Sess->status = STAT_TRY_LINK; + + // if (AX25Sess->digi[0] != 0) + // path: = path + ',' + reverse_digi(AX25Sess->digi); + + add_pkt_buf(AX25Sess, make_frame(&nullstring, axpath, 0, 0, 0, U_FRM, U_SABM, SET_NO_RPT, SET_P, SET_C)); + + inc_frack(AX25Sess); + } +} + +#define MODE_OUR 0 + +void set_try_unlink(TAX25Port * AX25Sess, Byte * path) +{ + string nullstring; + nullstring.Length = 0; + + AX25Sess->status = STAT_TRY_UNLINK; + add_pkt_buf(AX25Sess, make_frame(&nullstring, path, 0, 0, 0, U_FRM, U_DISC, SET_NO_RPT, SET_P, SET_C)); + inc_frack(AX25Sess); +} + + +void set_unlink(TAX25Port * AX25Sess, Byte * path) +{ + if (AX25Sess->status == STAT_TRY_UNLINK + || AX25Sess->status == STAT_TRY_LINK + || AX25Sess->status == STAT_NO_LINK) + { + string nullstring; + nullstring.Length = 0; + + // if (AX25Sess->digi[0] != 0) + // path: = path + ',' + reverse_digi(AX25Sess->digi); + + AX25_disc(AX25Sess, MODE_OUR); + + if (AX25Sess->status != STAT_TRY_UNLINK) + add_pkt_buf(AX25Sess, make_frame(&nullstring, path, 0, 0, 0, U_FRM, U_DISC, SET_NO_RPT, SET_P, SET_C)); + + AX25Sess->info.stat_end_ses = time(NULL); + + write_ax25_info(AX25Sess); + rst_values(AX25Sess); + + memset(AX25Sess->corrcall, 0, 10); + memset(AX25Sess->mycall, 0, 10); + AX25Sess->digi[0] = 0; + AX25Sess->status = STAT_NO_LINK; + + } + else + set_try_unlink(AX25Sess, AX25Sess->Path); +} + +void set_FRMR(int snd_ch, Byte * path, unsigned char frameType) +{ + //We may not have a session when sending FRMR so reverse path and send + + Byte revpath[80]; + string * Data = newString(); + string * Frame; + + UCHAR KISSBuffer[512]; + int Length; + + Data->Data[0] = frameType; + Data->Data[1] = 0; + Data->Data[2] = 1; // Invalid CTL Byte + Data->Length = 3; + + reverse_addr(path, revpath, strlen(path)); + + Frame = make_frame(Data, revpath, 0, 0, 0, U_FRM, U_FRMR, FALSE, SET_P, SET_R); + + // ? Don't we just send to TNC? + + Length = KISS_encode(KISSBuffer, 0, Frame); + + KISSSendtoServer(KISSSockCopy[snd_ch], KISSBuffer, Length); + + monitor_frame(0, Frame, "", 1, 0); // Monitor + freeString(Frame); + freeString(Data); +} + +void set_DM(int snd_ch, Byte * path) +{ + //We may not have a session when sending DM so reverse path and send + + Byte revpath[80]; + + reverse_addr(path, revpath, strlen(path)); + + add_pkt_buf(&AX25Port[snd_ch][0], make_frame(NULL, revpath, 0, 0, 0, U_FRM,U_DM,FALSE,SET_P,SET_R)); +} + +/////////////////////////// S-FRAMES //////////////////////////////////// + + +void on_RR(TAX25Port * AX25Sess, Byte * path, int nr, int pf, int cr) +{ + char need_frame[16] = ""; + int index = 0; + + int i; + + if (AX25Sess->status == STAT_TRY_LINK) + return; + + if (AX25Sess->status == STAT_NO_LINK || AX25Sess->status == STAT_TRY_UNLINK) + { + if (cr == SET_C) + set_DM(AX25Sess->snd_ch, path); + + return; + } + + if (cr == SET_R) + { + // Determine which frames could get into the user’s frame buffer. + i = AX25Sess->vs; + + need_frame[index++] = i + '0'; + +// Debugprintf("RR Rxed vs = %d hi_vs = %d", AX25Sess->vs, AX25Sess->hi_vs); + while (i != AX25Sess->hi_vs) + { + i++; + i &= 7; + + need_frame[index++] = i + '0'; + if (index > 10) + { + Debugprintf("Index corrupt %d need_frame = %s", index, need_frame); + break; + } + } + + //Clear the received frames from the transmission buffer. + + if (AX25Sess->status == STAT_WAIT_ANS) + delete_I_FRM(AX25Sess, nr); + + // We restore the link if the number is valid + + if (AX25Sess->status == STAT_CHK_LINK || strchr(need_frame, nr + '0') != 0) + { + rst_timer(AX25Sess); + AX25Sess->status = STAT_LINK; + send_data_buf(AX25Sess, nr); + } + } + + if (cr == SET_C) + add_pkt_buf(AX25Sess, make_frame(NULL, path, 0, AX25Sess->vr, 0, S_FRM, S_RR, FALSE, pf, SET_R)); + + rst_t3(AX25Sess); +} + + +void on_RNR(TAX25Port * AX25Sess, Byte * path, int nr, int pf, int cr) +{ + UNUSED(pf); + + if (AX25Sess->status == STAT_TRY_LINK) + return; + + if (AX25Sess->status == STAT_NO_LINK || AX25Sess->status == STAT_TRY_UNLINK) + { + if (cr == SET_C) + set_DM(AX25Sess->snd_ch, path); + + return; + } + + if (cr == SET_R) + { + rst_timer(AX25Sess); + + if (AX25Sess->status == STAT_WAIT_ANS) + delete_I_FRM(AX25Sess, nr); + + AX25Sess->status = STAT_CHK_LINK; + } + + if (cr == SET_C) + add_pkt_buf(AX25Sess, make_frame(NULL, path, 0, AX25Sess->vr, 0, S_FRM, S_RR, FALSE, SET_P, SET_R)); + + rst_t3(AX25Sess); +} + + +void on_REJ(TAX25Port * AX25Sess, Byte * path, int nr, int pf, int cr) +{ + UNUSED(pf); + if (AX25Sess->status == STAT_TRY_LINK) + return; + + if (AX25Sess->status == STAT_NO_LINK || AX25Sess->status == STAT_TRY_UNLINK) + { + if (cr == SET_C) + set_DM(AX25Sess->snd_ch, path); + + return; + } + + if (cr == SET_R) + { + rst_timer(AX25Sess); + AX25Sess->status = STAT_LINK; + + send_data_buf(AX25Sess, nr); + } + + if (cr == SET_C) + add_pkt_buf(AX25Sess, make_frame(NULL, path, 0, AX25Sess->vr, 0, S_FRM, S_RR, FALSE, SET_P, SET_R)); +} + + +void on_SREJ(TAX25Port * AX25Sess, Byte * path, int nr, int pf, int cr) +{ + UNUSED(pf); + + if (AX25Sess->status == STAT_TRY_LINK) + return; + + if (AX25Sess->status == STAT_NO_LINK || AX25Sess->status == STAT_TRY_UNLINK) + { + if (cr == SET_C) + set_DM(AX25Sess->snd_ch, path); + + return; + } + + if (cr == SET_R) + { + rst_timer(AX25Sess); + AX25Sess->status = STAT_LINK; + + send_data_buf_srej(AX25Sess, nr); + } + + if (cr == SET_C) + add_pkt_buf(AX25Sess, make_frame(NULL, path, 0, AX25Sess->vr, 0, S_FRM, S_RR, FALSE, SET_P, SET_R)); +} +/////////////////////////// I-FRAMES //////////////////////////////////// + +void on_I(void * socket, TAX25Port * AX25Sess, int PID, Byte * path, string * data, int nr, int ns, int pf, int cr, boolean fecflag) +{ + UNUSED(cr); + UNUSED(socket); + string * collector_data = 0; + int i; + Byte need_frame[16] = ""; + int index = 0; + + + if (AX25Sess->status == STAT_TRY_LINK) + return; + + if (AX25Sess->status == STAT_NO_LINK || AX25Sess->status == STAT_TRY_UNLINK) + { + set_DM(0, path); + return; + } + + if (busy) + { + add_pkt_buf(AX25Sess, make_frame(NULL, path, PID, AX25Sess->vr, 0, S_FRM, S_RNR, FALSE, pf, SET_R)); + return; + } + + // Determine which frames could get into the user’s frame buffer. + + i = AX25Sess->vs; + + need_frame[index++] = i + '0'; + + // Debugprintf("I Rxed vs = %d hi_vs = %d", AX25Sess->vs, AX25Sess->hi_vs); + + while (i != AX25Sess->hi_vs) + { + i++; + i &= 7; + + need_frame[index++] = i + '0'; + if (index > 10) + { + Debugprintf("Index corrupt %d need_frame = %s", index, need_frame); + break; + } + } + + //Clear received frames from the transmission buffer. + + if (AX25Sess->status == STAT_WAIT_ANS) + delete_I_FRM(AX25Sess, nr); + + //We restore the link if the number is valid + + if (AX25Sess->status == STAT_CHK_LINK || strchr(need_frame, nr + '0') != 0) + { + //We restore the link if the number is valid + + rst_timer(AX25Sess); + AX25Sess->status = STAT_LINK; + send_data_buf(AX25Sess, nr); + } + + if (ns == AX25Sess->vr) + { + //If the expected frame, send RR, F + + AX25Sess->info.stat_r_pkt++; + AX25Sess->info.stat_r_byte += data->Length; + + if (fecflag) + AX25Sess->info.stat_fec_count++; + + upd_vr(AX25Sess, AX25Sess->vr); + + AX25Sess->frm_win[ns].Length = data->Length; //Save the frame to the window buffer + AX25Sess->frm_win[ns].Data = data->Data; //Save the frame to the window buffer + + collector_data = read_frame_collector(AX25Sess, fecflag); + + stringAdd(data, collector_data->Data, collector_data->Length); + + SendtoTerm(AX25Sess->Sess, (char *)data->Data, data->Length); + + // Andy's code queues RR immediately and uses frame optimiser to remove + // redundant RR (not P) frames. I can't do that so need a proper resptime + // system. Try setting an RRNeeded timer (t2). + + if (pf) + { + // Poll set, so respond immediately + + add_pkt_buf(AX25Sess, make_frame(NULL, path, 0, AX25Sess->vr, 0, S_FRM, S_RR, FALSE, pf, SET_R)); + AX25Sess->t2 = 0; + } + else + AX25Sess->t2 = resptime[0] / 100; // resptime is in mS + + freeString(collector_data); + } + else + { + // If the frame is not expected, send REJ, F + + if ((AX25Sess->frm_win[ns].Length != data->Length) && + memcmp(&AX25Sess->frm_win[ns].Data, data->Data, data->Length) != 0) + + write_frame_collector(AX25Sess, ns, data); + + add_pkt_buf(AX25Sess, make_frame(NULL, path, 0, AX25Sess->vr, 0, S_FRM, S_REJ, FALSE, pf, SET_R)); + } + rst_t3(AX25Sess); + +} +/////////////////////////// U-FRAMES //////////////////////////////////// + +void * ax25IncommingConnect(TAX25Port * AX25Sess); + +void on_SABM(void * socket, TAX25Port * AX25Sess) +{ + if (AX25Sess->status == STAT_TRY_UNLINK) + { + AX25Sess->info.stat_end_ses = time(NULL); + + write_ax25_info(AX25Sess); + rst_values(AX25Sess); + + memset(AX25Sess->corrcall, 0, 10); + memset(AX25Sess->mycall, 0, 10); + AX25Sess->digi[0] = 0; + + AX25_disc(AX25Sess, MODE_OTHER); + Clear(&AX25Sess->frame_buf); + + AX25Sess->status = STAT_NO_LINK; + } + + if (AX25Sess->status == STAT_TRY_LINK) + { + AX25_disc(AX25Sess, MODE_OTHER); + + rst_timer(AX25Sess); + rst_values(AX25Sess); + Clear(&AX25Sess->frame_buf); + AX25Sess->status = STAT_NO_LINK; + } + + if (AX25Sess->status != STAT_NO_LINK) + { + if ((strcmp(AX25Sess->kind, "Outgoing") == 0) || + AX25Sess->status == STAT_TRY_UNLINK || AX25Sess->info.stat_s_byte > 0 || + AX25Sess->info.stat_r_byte > 0 || AX25Sess->frm_collector.Count > 0) + { + AX25Sess->info.stat_end_ses = time(NULL); + AX25_disc(AX25Sess, MODE_OTHER); + write_ax25_info(AX25Sess); + rst_timer(AX25Sess); + rst_values(AX25Sess); + Clear(&AX25Sess->frame_buf); + AX25Sess->status = STAT_NO_LINK; + } + } + + if (AX25Sess->status == STAT_NO_LINK) + { + AX25Sess->vr = 0; + AX25Sess->vs = 0; + AX25Sess->hi_vs = 0; + + Clear(&AX25Sess->frm_collector); + clr_frm_win(AX25Sess); + AX25Sess->status = STAT_LINK; + AX25Sess->info.stat_begin_ses = time(NULL); + strcpy(AX25Sess->kind, "Incoming"); + AX25Sess->socket = socket; + + // Must send UA before any ctext + + add_pkt_buf(AX25Sess, make_frame(NULL, AX25Sess->Path, 0, 0, 0, U_FRM, U_UA, FALSE, SET_P, SET_R)); + + if (ax25IncommingConnect(AX25Sess)) // Attach to Terminal + AX25_conn(AX25Sess, AX25Sess->snd_ch, MODE_OTHER); + + return; + } + + add_pkt_buf(AX25Sess, make_frame(NULL, AX25Sess->Path, 0, 0, 0, U_FRM, U_UA, FALSE, SET_P, SET_R)); +} + +void on_DISC(TAX25Port * AX25Sess) +{ + if (AX25Sess->status != STAT_NO_LINK) + { + AX25Sess->info.stat_end_ses = time(NULL); + AX25_disc(AX25Sess, MODE_OTHER); + write_ax25_info(AX25Sess); + } + + if (AX25Sess->status == STAT_NO_LINK || AX25Sess->status == STAT_TRY_LINK) + set_DM(AX25Sess->snd_ch, AX25Sess->Path); + else + add_pkt_buf(AX25Sess, make_frame(NULL, AX25Sess->Path, 0, 0, 0, U_FRM, U_UA, FALSE, SET_P, SET_R)); + + rst_values(AX25Sess); + memset(AX25Sess->corrcall, 0, 10); + memset(AX25Sess->mycall, 0, 10); + AX25Sess->digi[0] = 0; + AX25Sess->status = STAT_NO_LINK; +} + +void on_DM(TAX25Port * AX25Sess) +{ + if (AX25Sess->status == STAT_TRY_LINK) + { + + char Msg[128]; + int Len; + + Len = sprintf(Msg, "*** Busy From %s\r", AX25Sess->corrcall); + SendtoTerm(AX25Sess->Sess, Msg, Len); + } + else if (AX25Sess->status != STAT_NO_LINK) + { + AX25Sess->info.stat_end_ses = time(NULL); + AX25_disc(AX25Sess, MODE_OTHER); + write_ax25_info(AX25Sess); + } + + rst_timer(AX25Sess); + rst_values(AX25Sess); + memset(AX25Sess->corrcall, 0, 10); + memset(AX25Sess->mycall, 0, 10); + AX25Sess->digi[0] = 0; + AX25Sess->status = STAT_NO_LINK; +} + + +void on_UA(TAX25Port * AX25Sess) +{ + switch (AX25Sess->status) + { + case STAT_TRY_LINK: + + AX25Sess->info.stat_begin_ses = time(NULL); + AX25Sess->status = STAT_LINK; + AX25_conn(AX25Sess, AX25Sess->snd_ch, MODE_OUR); + break; + + case STAT_TRY_UNLINK: + + AX25Sess->info.stat_end_ses = time(NULL); + AX25_disc(AX25Sess, MODE_OUR); + write_ax25_info(AX25Sess); + + rst_values(AX25Sess); + AX25Sess->status = STAT_NO_LINK; + memset(AX25Sess->corrcall, 0, 10); + memset(AX25Sess->mycall, 0, 10); + AX25Sess->digi[0] = 0; + break; + } + + rst_timer(AX25Sess); +} + +void on_UI(TAX25Port * AX25Sess, int pf, int cr) +{ + UNUSED(AX25Sess); + UNUSED(pf); + UNUSED(cr); + +} + +void on_FRMR(void * socket, TAX25Port * AX25Sess, Byte * path) +{ + if (AX25Sess->status != STAT_NO_LINK) + { + AX25Sess->info.stat_end_ses = time(NULL); + + AX25_disc(socket, MODE_OTHER); + write_ax25_info(AX25Sess); + } + + set_DM(AX25Sess->snd_ch, path); + + rst_values(AX25Sess); + memset(AX25Sess->corrcall, 0, 10); + memset(AX25Sess->mycall, 0, 10); + AX25Sess->digi[0] = 0; + AX25Sess->status = STAT_NO_LINK; +} + + + +void UpdateActiveConnects(int snd_ch) +{ + int port; + + users[snd_ch] = 0; + + for (port = 0; port < port_num; port++) + if (AX25Port[snd_ch][port].status != STAT_NO_LINK) + users[snd_ch]++; +} + + +void timer_event() +{ + int snd_ch, port; + single frack; + Byte active; + TAX25Port * AX25Sess; + + TimerEvent = TIMER_EVENT_OFF; + + for (snd_ch = 0; snd_ch < 4; snd_ch++) + { + //reset the slottime timer + if (dyn_frack[snd_ch]) + { + UpdateActiveConnects(snd_ch); + if (users[snd_ch] > 0) + active = users[snd_ch] - 1; + else + active = 0; + + frack = frack_time[snd_ch] + frack_time[snd_ch] * active * 0.5; + } + else + frack = frack_time[snd_ch]; + + // + for (port = 0; port < port_num; port++) + { + AX25Sess = &AX25Port[snd_ch][port]; + + if (AX25Sess->status == STAT_NO_LINK) + continue; + + if (AX25Sess->t2) + { + AX25Sess->t2--; + if (AX25Sess->t2 == 0) + { + // Expired - send RR (without P) + + add_pkt_buf(AX25Sess, make_frame(NULL, AX25Sess->Path, 0, AX25Sess->vr, 0, S_FRM, S_RR, FALSE, 0, SET_R)); + } + } + + if (AX25Sess->frame_buf.Count == 0) //when the transfer buffer is empty + inc_t1(AX25Sess); // we consider time of the timer of repeated requests + + // Wouldn't it make more sense to keep path in ax.25 struct? + + if (AX25Sess->t1 >= frack * 10 + (number_digi(AX25Sess->digi) * frack * 20)) + { + if (AX25Sess->clk_frack >= fracks[snd_ch]) + { + // This disconnects after retries expires + + rst_frack(AX25Sess); + set_unlink(AX25Sess, AX25Sess->Path); + } + + switch (AX25Sess->status) + { + case STAT_TRY_LINK: + + set_link(AX25Sess, AX25Sess->Path); + break; + + case STAT_TRY_UNLINK: + + set_try_unlink(AX25Sess, AX25Sess->Path); + break; + + case STAT_WAIT_ANS: + + set_chk_link(AX25Sess, AX25Sess->Path); + break; + + case STAT_CHK_LINK: + + set_chk_link(AX25Sess, AX25Sess->Path); + break; + } + + rst_t1(AX25Sess); + } + + + if (AX25Sess->t3 >= idletime[snd_ch] * 10) + { + set_chk_link(AX25Sess, AX25Sess->Path); + rst_t1(AX25Sess); + rst_t3(AX25Sess); + } + + if (AX25Sess->status == STAT_LINK) + inc_t3(AX25Sess); + + } + } + // KISS ACKMODE + //if (snd_status[snd_ch]<>SND_TX) and KISSServ then KISS_send_ack1(snd_ch); +} + +TAX25Port * get_free_port(int snd_ch) +{ + int i = 0; + + while (i < port_num) + { + if (AX25Port[snd_ch][i].status == STAT_NO_LINK) + return &AX25Port[snd_ch][i]; + + i++; + } + return FALSE; +} + + +TAX25Port * get_user_port(int snd_ch, Byte * path) + { + TAX25Port * AX25Sess = NULL; + + int i = 0; + + + while (i < port_num) + { + AX25Sess = &AX25Port[snd_ch][i]; + + if (AX25Sess->status != STAT_NO_LINK && memcmp(AX25Sess->ReversePath, path, AX25Sess->pathLen) == 0) + return AX25Sess; + + i++; + } + + return FALSE; +} + +boolean get_user_dupe(int snd_ch, Byte * path) +{ + int i = 0; + TAX25Port * AX25Sess; + + while (i < port_num) + { + AX25Sess = &AX25Port[snd_ch][i]; + + if (AX25Sess->status != STAT_NO_LINK && memcmp(AX25Sess->ReversePath, path, AX25Sess->pathLen) == 0) + return TRUE; + + i++; + } + + return FALSE; +} + +TAX25Port * get_user_port_by_calls(int snd_ch, char * CallFrom, char * CallTo) +{ + int i = 0; + TAX25Port * AX25Sess = NULL; + + while (i < port_num) + { + AX25Sess = &AX25Port[snd_ch][i]; + + if (AX25Sess->status != STAT_NO_LINK && + strcmp(CallFrom, AX25Sess->mycall) == 0 && strcmp(CallTo, AX25Sess->corrcall) == 0) + + return AX25Sess; + + i++; + } + + return NULL; +} + +void * get_sock_by_port(TAX25Port * AX25Sess) +{ + void * socket = (void *)-1; + + if (AX25Sess) + socket = AX25Sess->socket; + + return socket; +} + +void Digipeater(int snd_ch, string * frame) +{ + char call[16]; + Byte * addr = &frame->Data[7]; // Origin + string * frameCopy; + + int n = 8; // Max digis + + if (list_digi_callsigns[snd_ch].Count == 0) + return; + + // Look for first unused digi + + while ((addr[6] & 1) == 0 && n--) // until end of address + { + addr += 7; + + if ((addr[6] & 128) == 0) + { + // unused digi - is it addressed to us? + + memcpy(call, addr, 7); + call[6] &= 0x7E; // Mask end of call + + // See if in digi list + + int i; + + for (i = 0; i < list_digi_callsigns->Count; i++) + { + if (memcmp(list_digi_callsigns->Items[i]->Data, call, 7) == 0) + { + UCHAR KISSBuffer[512]; + int Length; + + // for us + + addr[6] |= 128; // set digi'ed + + // TX Frames need a KISS control on front + + frameCopy = newString(); + + frameCopy->Data[0] = 0; + frameCopy->Length = 1; + + stringAdd(frameCopy, frame->Data, frame->Length); // Exclude CRC + + addr[6] &= 0x7f; // clear digi'ed from original; + + // Send to TNC + + // ? Don't we just send to TNC? + + Length = KISS_encode(KISSBuffer, 0, frameCopy); + + KISSSendtoServer(KISSSockCopy[snd_ch], KISSBuffer, Length); + + monitor_frame(0, frameCopy, "", 1, 0); // Monitor + freeString(frameCopy); + + return; + } + } + } + } +} + +void analiz_frame(int snd_ch, string * frame, void * socket, boolean fecflag) +{ + Byte path[80]; + string * data = 0; + Byte pid, nr, ns, f_type, f_id, rpt, cr, pf; + Byte * ptr; + + int excluded = 0; + int len; + + TAX25Port * AX25Sess; + + // mod_icon_status = mod_rx; + + len = frame->Length; + + if (len < PKT_ERR) + return; + + data = newString(); + + decode_frame(frame->Data, frame->Length, path, data, &pid, &nr, &ns, &f_type, &f_id, &rpt, &pf, &cr); + + // if is_excluded_call(snd_ch,path) then excluded:=TRUE; + // if is_excluded_frm(snd_ch,f_id,data) then excluded:=TRUE; + + // CRC Collision Check + + if (!is_correct_path(path, pid)) + { + // Duff path - if Non-AX25 filter active log and discard + + } + + monitor_frame(snd_ch, frame, "", 0, excluded); // Monitor + + // Digipeat frame + + Digipeater(snd_ch, frame); + + if (!is_last_digi(path)) + { + freeString(data); + return; // Don't process if still unused digis + } + + // Clear reapeated bits from digi path + + ptr = &path[13]; + + while ((*ptr & 1) == 0) // end of address + { + ptr += 7; + *(ptr) &= 0x7f; // Clear digi'd bit + } + + // search for port of correspondent + + AX25Sess = get_user_port(snd_ch, path); + + // if not an active session, AX25Sess will be NULL + +// if (AX25Sess == NULL) +// socket = in_list_incoming_mycall(path); +// else +// socket = get_sock_by_port(AX25Sess); + + // link analysis + + if (AX25Sess == NULL) + { + if (f_id == U_UI) + { + CheckUIFrame(path, data); + freeString(data); + return; // Don't precess UI at the moment + } + + // No Session. If socket is set (so call is in incoming calls list) and SABM set up session + + if (listenEnable == 0) + { + set_DM(snd_ch, path); + freeString(data); + return; + } + + if (f_id != U_SABM) // Not SABM + { + // // send DM if P set + + if (cr == SET_C) + { + switch (f_id) + { + case U_DISC: + case S_RR: + case S_REJ: + case S_RNR: + case I_I: + + set_DM(snd_ch, path); + break; + + case U_UI: + break; + + default: + set_FRMR(snd_ch, path, f_id); + } + + + } + freeString(data); + return; + } + + // Must be SABM. See if it would duplicate an existing session (but could it - wouldn't that be found earlier ?? + + if (get_user_dupe(snd_ch, path)) // Not SABM or a duplicate call pair + { + freeString(data); + return; + } + + AX25Sess = get_free_port(snd_ch); + + if (AX25Sess == NULL) + { + // if there are no free ports for connection - reject + + Byte Rev[80]; + + reverse_addr(path, Rev, strlen(path)); + set_DM(snd_ch, Rev); + freeString(data); + return; + } + + // initialise new session + + AX25Sess->snd_ch = snd_ch; + + AX25Sess->corrcall[ConvFromAX25(&path[7], AX25Sess->corrcall)] = 0; + AX25Sess->mycall[ConvFromAX25(path, AX25Sess->mycall)] = 0; + AX25Sess->digi[0] = 0; + + // rst_timer(snd_ch, free_port); + + strcpy(AX25Sess->kind, "Incoming"); + AX25Sess->socket = socket; + + Debugprintf("incoming call socket = %x", socket); + + // I think we need to reverse the path + + AX25Sess->pathLen = strlen(path); + strcpy(AX25Sess->ReversePath, path); + reverse_addr(path, AX25Sess->Path, strlen(path)); + } + + // we process a packet on the necessary port + + memcpy(path, AX25Sess->Path, AX25Sess->pathLen); + + switch (f_id) + { + case I_I: + + on_I(socket, AX25Sess, pid, path, data, nr, ns, pf, cr, fecflag); + break; + + case S_RR: + + on_RR(AX25Sess, path, nr, pf, cr); + break; + + case S_RNR: + + on_RNR(AX25Sess, path, nr, pf, cr); + break; + + case S_REJ: + + on_REJ(AX25Sess, path, nr, pf, cr); + break; + + case S_SREJ: + + on_SREJ(AX25Sess, path, nr, pf, cr); + break; + + case U_SABM: + + on_SABM(socket, AX25Sess); + break; + + case U_DISC: + + on_DISC(AX25Sess); + break; + + case U_UA: + + on_UA(AX25Sess); + break; + + case U_DM: + + on_DM(AX25Sess); + break; + + case U_UI: + + on_UI(AX25Sess, pf, cr); + break; + + case U_FRMR: + + on_FRMR(socket, AX25Sess, path); + break; + } + freeString(data); +} + +int get_addr(char * Calls, UCHAR * AXCalls); + +void Send_UI(int port, Byte PID, char * CallFrom, char *CallTo, Byte * Msg, int MsgLen) +{ + Byte path[80]; + char Calls[80]; + string * Data = newString(); + string * Frame; + + UCHAR KISSBuffer[512]; + int Length; + + stringAdd(Data, Msg, MsgLen); + + sprintf(Calls, "%s,%s", CallTo, CallFrom); + + get_addr(Calls, path); + + Frame = make_frame(Data, path, PID, 0, 0, U_FRM, U_UI, FALSE, SET_F, SET_C); + + // ? Don't we just send to TNC? + + Length = KISS_encode(KISSBuffer, 0, Frame); + + KISSSendtoServer(KISSSockCopy[port], KISSBuffer, Length); + + monitor_frame(0, Frame, "", 1, 0); // Monitor + freeString(Frame); + +} + + + + diff --git a/hid.c b/hid.c new file mode 100644 index 0000000..88ab1bc --- /dev/null +++ b/hid.c @@ -0,0 +1,910 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 8/22/2009 + + Copyright 2009, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + http://github.com/signal11/hidapi . +********************************************************/ + +// Hacked about a bit for BPQ32. John Wiseman G8BPQ April 2018 + + + +#include + +#ifndef _NTDEF_ +typedef LONG NTSTATUS; +#endif + +#ifdef __MINGW32__ +#include +#include +#endif + +#ifdef __CYGWIN__ +#include +#define _wcsdup wcsdup +#endif + +//#define HIDAPI_USE_DDK + +#ifdef __cplusplus +extern "C" { +#endif + #include + #include + #ifdef HIDAPI_USE_DDK + #include + #endif + + // Copied from inc/ddk/hidclass.h, part of the Windows DDK. + #define HID_OUT_CTL_CODE(id) \ + CTL_CODE(FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS) + #define IOCTL_HID_GET_FEATURE HID_OUT_CTL_CODE(100) + +#ifdef __cplusplus +} // extern "C" +#endif + +#include +#include + + +#include "hidapi.h" + +#ifdef _MSC_VER + // Thanks Microsoft, but I know how to use strncpy(). + #pragma warning(disable:4996) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef HIDAPI_USE_DDK + // Since we're not building with the DDK, and the HID header + // files aren't part of the SDK, we have to define all this + // stuff here. In lookup_functions(), the function pointers + // defined below are set. + typedef struct _HIDD_ATTRIBUTES{ + ULONG Size; + USHORT VendorID; + USHORT ProductID; + USHORT VersionNumber; + } HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES; + + typedef USHORT USAGE; + typedef struct _HIDP_CAPS { + USAGE Usage; + USAGE UsagePage; + USHORT InputReportByteLength; + USHORT OutputReportByteLength; + USHORT FeatureReportByteLength; + USHORT Reserved[17]; + USHORT fields_not_used_by_hidapi[10]; + } HIDP_CAPS, *PHIDP_CAPS; + typedef char* HIDP_PREPARSED_DATA; + #define HIDP_STATUS_SUCCESS 0x0 + + typedef BOOLEAN(__stdcall *HidD_GetAttributes_)(HANDLE device, PHIDD_ATTRIBUTES attrib); + typedef BOOLEAN(__stdcall *HidD_GetSerialNumberString_)(HANDLE device, PVOID buffer, ULONG buffer_len); + typedef BOOLEAN(__stdcall *HidD_GetManufacturerString_)(HANDLE handle, PVOID buffer, ULONG buffer_len); + typedef BOOLEAN(__stdcall *HidD_GetProductString_)(HANDLE handle, PVOID buffer, ULONG buffer_len); + typedef BOOLEAN(__stdcall *HidD_SetFeature_)(HANDLE handle, PVOID data, ULONG length); + typedef BOOLEAN(__stdcall *HidD_GetFeature_)(HANDLE handle, PVOID data, ULONG length); + typedef BOOLEAN(__stdcall *HidD_GetIndexedString_)(HANDLE handle, ULONG string_index, PVOID buffer, ULONG buffer_len); + typedef BOOLEAN(__stdcall *HidD_GetPreparsedData_)(HANDLE handle, HIDP_PREPARSED_DATA **preparsed_data); + typedef BOOLEAN(__stdcall *HidD_FreePreparsedData_)(HIDP_PREPARSED_DATA *preparsed_data); + typedef BOOLEAN(__stdcall *HidP_GetCaps_)(HIDP_PREPARSED_DATA *preparsed_data, HIDP_CAPS *caps); + + static HidD_GetAttributes_ HidD_GetAttributes; + static HidD_GetSerialNumberString_ HidD_GetSerialNumberString; + static HidD_GetManufacturerString_ HidD_GetManufacturerString; + static HidD_GetProductString_ HidD_GetProductString; + static HidD_SetFeature_ HidD_SetFeature; + static HidD_GetFeature_ HidD_GetFeature; + static HidD_GetIndexedString_ HidD_GetIndexedString; + static HidD_GetPreparsedData_ HidD_GetPreparsedData; + static HidD_FreePreparsedData_ HidD_FreePreparsedData; + static HidP_GetCaps_ HidP_GetCaps; + + static HMODULE lib_handle = NULL; + static BOOLEAN initialized = FALSE; +#endif // HIDAPI_USE_DDK + +struct hid_device_ { + HANDLE device_handle; + BOOLEAN blocking; + int input_report_length; + void *last_error_str; + DWORD last_error_num; + BOOLEAN read_pending; + char *read_buf; + OVERLAPPED ol; +}; + +static hid_device *new_hid_device() +{ + hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device)); + dev->device_handle = INVALID_HANDLE_VALUE; + dev->blocking = TRUE; + dev->input_report_length = 0; + dev->last_error_str = NULL; + dev->last_error_num = 0; + dev->read_pending = FALSE; + dev->read_buf = NULL; + memset(&dev->ol, 0, sizeof(dev->ol)); + dev->ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*inital state f=nonsignaled*/, NULL); + + return dev; +} + + +static void register_error(hid_device *device, const char *op) +{ + WCHAR *ptr, *msg; + + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR)&msg, 0/*sz*/, + NULL); + + // Get rid of the CR and LF that FormatMessage() sticks at the + // end of the message. Thanks Microsoft! + ptr = msg; + while (*ptr) { + if (*ptr == '\r') { + *ptr = 0x0000; + break; + } + ptr++; + } + + // Store the message off in the Device entry so that + // the hid_error() function can pick it up. + LocalFree(device->last_error_str); + device->last_error_str = msg; +} + +#ifndef HIDAPI_USE_DDK +static int lookup_functions() +{ + lib_handle = LoadLibraryA("hid.dll"); + if (lib_handle) { +#define RESOLVE(x) x = (x##_)GetProcAddress(lib_handle, #x); if (!x) return -1; + RESOLVE(HidD_GetAttributes); + RESOLVE(HidD_GetSerialNumberString); + RESOLVE(HidD_GetManufacturerString); + RESOLVE(HidD_GetProductString); + RESOLVE(HidD_SetFeature); + RESOLVE(HidD_GetFeature); + RESOLVE(HidD_GetIndexedString); + RESOLVE(HidD_GetPreparsedData); + RESOLVE(HidD_FreePreparsedData); + RESOLVE(HidP_GetCaps); +#undef RESOLVE + } + else + return -1; + + return 0; +} +#endif + +static HANDLE open_device(const char *path) +{ + HANDLE handle; + + /* First, try to open with sharing mode turned off. This will make it so + that a HID device can only be opened once. This is to be consistent + with the behavior on the other platforms. */ + handle = CreateFileA(path, + GENERIC_WRITE |GENERIC_READ, + 0, /*share mode*/ + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED,//FILE_ATTRIBUTE_NORMAL, + 0); + + if (handle == INVALID_HANDLE_VALUE) { + /* Couldn't open the device. Some devices must be opened + with sharing enabled (even though they are only opened once), + so try it here. */ + handle = CreateFileA(path, + GENERIC_WRITE |GENERIC_READ, + FILE_SHARE_READ|FILE_SHARE_WRITE, /*share mode*/ + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED,//FILE_ATTRIBUTE_NORMAL, + 0); + } + + return handle; +} + +int HID_API_EXPORT hid_init(void) +{ +#ifndef HIDAPI_USE_DDK + if (!initialized) { + if (lookup_functions() < 0) { + hid_exit(); + return -1; + } + initialized = TRUE; + } +#endif + return 0; +} + +int HID_API_EXPORT hid_exit(void) +{ +#ifndef HIDAPI_USE_DDK + if (lib_handle) + FreeLibrary(lib_handle); + lib_handle = NULL; + initialized = FALSE; +#endif + return 0; +} + +struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id) +{ + BOOLEAN res; + struct hid_device_info *root = NULL; // return object + struct hid_device_info *cur_dev = NULL; + + // Windows objects for interacting with the driver. + GUID InterfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, {0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30} }; + SP_DEVINFO_DATA devinfo_data; + SP_DEVICE_INTERFACE_DATA device_interface_data; + SP_DEVICE_INTERFACE_DETAIL_DATA_A *device_interface_detail_data = NULL; + HDEVINFO device_info_set = INVALID_HANDLE_VALUE; + int device_index = 0; + + if (hid_init() < 0) + return NULL; + + // Initialize the Windows objects. + devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA); + device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + + // Get information for all the devices belonging to the HID class. + device_info_set = SetupDiGetClassDevsA(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + + // Iterate over each device in the HID class, looking for the right one. + + for (;;) { + HANDLE write_handle = INVALID_HANDLE_VALUE; + DWORD required_size = 0; + HIDD_ATTRIBUTES attrib; + + res = SetupDiEnumDeviceInterfaces(device_info_set, + NULL, + &InterfaceClassGuid, + device_index, + &device_interface_data); + + if (!res) { + // A return of FALSE from this function means that + // there are no more devices. + break; + } + + // Call with 0-sized detail size, and let the function + // tell us how long the detail struct needs to be. The + // size is put in &required_size. + res = SetupDiGetDeviceInterfaceDetailA(device_info_set, + &device_interface_data, + NULL, + 0, + &required_size, + NULL); + + // Allocate a long enough structure for device_interface_detail_data. + device_interface_detail_data = (SP_DEVICE_INTERFACE_DETAIL_DATA_A*) malloc(required_size); + device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); + + // Get the detailed data for this device. The detail data gives us + // the device path for this device, which is then passed into + // CreateFile() to get a handle to the device. + res = SetupDiGetDeviceInterfaceDetailA(device_info_set, + &device_interface_data, + device_interface_detail_data, + required_size, + NULL, + NULL); + + if (!res) { + //register_error(dev, "Unable to call SetupDiGetDeviceInterfaceDetail"); + // Continue to the next device. + goto cont; + } + + //wprintf(L"HandleName: %s\n", device_interface_detail_data->DevicePath); + + // Open a handle to the device + write_handle = open_device(device_interface_detail_data->DevicePath); + + // Check validity of write_handle. + if (write_handle == INVALID_HANDLE_VALUE) { + // Unable to open the device. + //register_error(dev, "CreateFile"); + goto cont_close; + } + + + // Get the Vendor ID and Product ID for this device. + attrib.Size = sizeof(HIDD_ATTRIBUTES); + HidD_GetAttributes(write_handle, &attrib); + //wprintf(L"Product/Vendor: %x %x\n", attrib.ProductID, attrib.VendorID); + + // Check the VID/PID to see if we should add this + // device to the enumeration list. + if ((vendor_id == 0x0 && product_id == 0x0) || + (attrib.VendorID == vendor_id && attrib.ProductID == product_id)) + { + #define WSTR_LEN 512 + const char *str; + struct hid_device_info *tmp; + HIDP_PREPARSED_DATA *pp_data = NULL; + HIDP_CAPS caps; + BOOLEAN res; + NTSTATUS nt_res; + wchar_t wstr[WSTR_LEN]; // TODO: Determine Size + int len; + + /* VID/PID match. Create the record. */ + tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info)); + if (cur_dev) { + cur_dev->next = tmp; + } + else { + root = tmp; + } + cur_dev = tmp; + + // Get the Usage Page and Usage for this device. + res = HidD_GetPreparsedData(write_handle, &pp_data); + if (res) { + nt_res = HidP_GetCaps(pp_data, &caps); + if (nt_res == HIDP_STATUS_SUCCESS) { + cur_dev->usage_page = caps.UsagePage; + cur_dev->usage = caps.Usage; + } + + HidD_FreePreparsedData(pp_data); + } + + /* Fill out the record */ + cur_dev->next = NULL; + str = device_interface_detail_data->DevicePath; + if (str) { + len = (int)strlen(str); + cur_dev->path = (char*) calloc(len+1, sizeof(char)); + strncpy(cur_dev->path, str, len+1); + cur_dev->path[len] = '\0'; + } + else + cur_dev->path = NULL; + + /* Serial Number */ + res = HidD_GetSerialNumberString(write_handle, wstr, sizeof(wstr)); + wstr[WSTR_LEN-1] = 0x0000; + if (res) { + cur_dev->serial_number = _wcsdup(wstr); + } + + /* Manufacturer String */ + res = HidD_GetManufacturerString(write_handle, wstr, sizeof(wstr)); + wstr[WSTR_LEN-1] = 0x0000; + if (res) { + cur_dev->manufacturer_string = _wcsdup(wstr); + } + + /* Product String */ + res = HidD_GetProductString(write_handle, wstr, sizeof(wstr)); + wstr[WSTR_LEN-1] = 0x0000; + if (res) { + cur_dev->product_string = _wcsdup(wstr); + } + + /* VID/PID */ + cur_dev->vendor_id = attrib.VendorID; + cur_dev->product_id = attrib.ProductID; + + /* Release Number */ + cur_dev->release_number = attrib.VersionNumber; + + /* Interface Number. It can sometimes be parsed out of the path + on Windows if a device has multiple interfaces. See + http://msdn.microsoft.com/en-us/windows/hardware/gg487473 or + search for "Hardware IDs for HID Devices" at MSDN. If it's not + in the path, it's set to -1. */ + cur_dev->interface_number = -1; + if (cur_dev->path) { + char *interface_component = strstr(cur_dev->path, "&mi_"); + if (interface_component) { + char *hex_str = interface_component + 4; + char *endptr = NULL; + cur_dev->interface_number = strtol(hex_str, &endptr, 16); + if (endptr == hex_str) { + /* The parsing failed. Set interface_number to -1. */ + cur_dev->interface_number = -1; + } + } + } + } + +cont_close: + CloseHandle(write_handle); +cont: + // We no longer need the detail data. It can be freed + free(device_interface_detail_data); + + device_index++; + + } + + // Close the device information handle. + SetupDiDestroyDeviceInfoList(device_info_set); + + return root; + +} + +void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs) +{ + // TODO: Merge this with the Linux version. This function is platform-independent. + struct hid_device_info *d = devs; + while (d) { + struct hid_device_info *next = d->next; + free(d->path); + free(d->serial_number); + free(d->manufacturer_string); + free(d->product_string); + free(d); + d = next; + } +} + + +HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, wchar_t *serial_number) +{ + // TODO: Merge this functions with the Linux version. This function should be platform independent. + struct hid_device_info *devs, *cur_dev; + const char *path_to_open = NULL; + hid_device *handle = NULL; + + devs = hid_enumerate(vendor_id, product_id); + cur_dev = devs; + while (cur_dev) { + if (cur_dev->vendor_id == vendor_id && + cur_dev->product_id == product_id) { + if (serial_number) { + if (wcscmp(serial_number, cur_dev->serial_number) == 0) { + path_to_open = cur_dev->path; + break; + } + } + else { + path_to_open = cur_dev->path; + break; + } + } + cur_dev = cur_dev->next; + } + + if (path_to_open) { + /* Open the device */ + handle = hid_open_path(path_to_open); + } + + hid_free_enumeration(devs); + + return handle; +} + +HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path) +{ + hid_device *dev; + HIDP_CAPS caps; + HIDP_PREPARSED_DATA *pp_data = NULL; + BOOLEAN res; + NTSTATUS nt_res; + + if (hid_init() < 0) { + return NULL; + } + + dev = new_hid_device(); + + // Open a handle to the device + dev->device_handle = open_device(path); + + // Check validity of write_handle. + if (dev->device_handle == INVALID_HANDLE_VALUE) { + // Unable to open the device. + register_error(dev, "CreateFile"); + goto err; + } + + // Get the Input Report length for the device. + res = HidD_GetPreparsedData(dev->device_handle, &pp_data); + if (!res) { + register_error(dev, "HidD_GetPreparsedData"); + goto err; + } + nt_res = HidP_GetCaps(pp_data, &caps); + if (nt_res != HIDP_STATUS_SUCCESS) { + register_error(dev, "HidP_GetCaps"); + goto err_pp_data; + } + dev->input_report_length = caps.InputReportByteLength; + HidD_FreePreparsedData(pp_data); + + dev->read_buf = (char*) malloc(dev->input_report_length); + + return dev; + +err_pp_data: + HidD_FreePreparsedData(pp_data); +err: + CloseHandle(dev->device_handle); + free(dev); + return NULL; +} + +int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length) +{ + DWORD bytes_written; + BOOLEAN res; + + OVERLAPPED ol; + memset(&ol, 0, sizeof(ol)); + + res = WriteFile(dev->device_handle, data, length, NULL, &ol); + + if (!res) { + if (GetLastError() != ERROR_IO_PENDING) { + // WriteFile() failed. Return error. + register_error(dev, "WriteFile"); + return -1; + } + } + + // Wait here until the write is done. This makes + // hid_write() synchronous. + res = GetOverlappedResult(dev->device_handle, &ol, &bytes_written, TRUE/*wait*/); + if (!res) { + // The Write operation failed. + register_error(dev, "WriteFile"); + return -1; + } + + return bytes_written; +} + +int HID_API_EXPORT HID_API_CALL hid_set_ptt(int state) +{ + int res; + hid_device *handle; + unsigned char buf[16]; + + handle = hid_open(0xd8c, 0x8, NULL); + if (!handle) { + printf("unable to open device\n"); + return 1; + } + + + // Toggle PTT + + buf[0] = 0; + buf[1] = 0; + buf[2]= 1 << (3 - 1); + buf[3] = state << (3 - 1); + buf[4] = 0; + + + res = hid_write(handle, buf, 5); + if (res < 0) + { + printf("Unable to write()\n"); + printf("Error: %ls\n", hid_error(handle)); + } + + hid_close(handle); + return res; +} + + +int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) +{ + DWORD bytes_read = 0; + BOOLEAN res; + + // Copy the handle for convenience. + HANDLE ev = dev->ol.hEvent; + + if (!dev->read_pending) { + // Start an Overlapped I/O read. + dev->read_pending = TRUE; + ResetEvent(ev); + res = ReadFile(dev->device_handle, dev->read_buf, dev->input_report_length, &bytes_read, &dev->ol); + + if (!res) { + if (GetLastError() != ERROR_IO_PENDING) { + // ReadFile() has failed. + // Clean up and return error. + CancelIo(dev->device_handle); + dev->read_pending = FALSE; + goto end_of_function; + } + } + } + + if (milliseconds >= 0) { + // See if there is any data yet. + res = WaitForSingleObject(ev, milliseconds); + if (res != WAIT_OBJECT_0) { + // There was no data this time. Return zero bytes available, + // but leave the Overlapped I/O running. + return 0; + } + } + + // Either WaitForSingleObject() told us that ReadFile has completed, or + // we are in non-blocking mode. Get the number of bytes read. The actual + // data has been copied to the data[] array which was passed to ReadFile(). + res = GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, TRUE/*wait*/); + + // Set pending back to false, even if GetOverlappedResult() returned error. + dev->read_pending = FALSE; + + if (res && bytes_read > 0) { + if (dev->read_buf[0] == 0x0) { + /* If report numbers aren't being used, but Windows sticks a report + number (0x0) on the beginning of the report anyway. To make this + work like the other platforms, and to make it work more like the + HID spec, we'll skip over this byte. */ + bytes_read--; + memcpy(data, dev->read_buf+1, length); + } + else { + /* Copy the whole buffer, report number and all. */ + memcpy(data, dev->read_buf, length); + } + } + +end_of_function: + if (!res) { + register_error(dev, "GetOverlappedResult"); + return -1; + } + + return bytes_read; +} + +int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length) +{ + return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0); +} + +int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock) +{ + dev->blocking = !nonblock; + return 0; /* Success */ +} + +int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) +{ + BOOLEAN res = HidD_SetFeature(dev->device_handle, (PVOID)data, length); + if (!res) { + register_error(dev, "HidD_SetFeature"); + return -1; + } + + return length; +} + + +int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) +{ + BOOLEAN res; +#if 0 + res = HidD_GetFeature(dev->device_handle, data, length); + if (!res) { + register_error(dev, "HidD_GetFeature"); + return -1; + } + return 0; /* HidD_GetFeature() doesn't give us an actual length, unfortunately */ +#else + DWORD bytes_returned; + + OVERLAPPED ol; + memset(&ol, 0, sizeof(ol)); + + res = DeviceIoControl(dev->device_handle, + IOCTL_HID_GET_FEATURE, + data, length, + data, length, + &bytes_returned, &ol); + + if (!res) { + if (GetLastError() != ERROR_IO_PENDING) { + // DeviceIoControl() failed. Return error. + register_error(dev, "Send Feature Report DeviceIoControl"); + return -1; + } + } + + // Wait here until the write is done. This makes + // hid_get_feature_report() synchronous. + res = GetOverlappedResult(dev->device_handle, &ol, &bytes_returned, TRUE/*wait*/); + if (!res) { + // The operation failed. + register_error(dev, "Send Feature Report GetOverLappedResult"); + return -1; + } + return bytes_returned; +#endif +} + +void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev) +{ + if (!dev) + return; + CancelIo(dev->device_handle); + CloseHandle(dev->ol.hEvent); + CloseHandle(dev->device_handle); + LocalFree(dev->last_error_str); + free(dev->read_buf); + free(dev); +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + BOOLEAN res; + + res = HidD_GetManufacturerString(dev->device_handle, string, 2 * maxlen); + if (!res) { + register_error(dev, "HidD_GetManufacturerString"); + return -1; + } + + return 0; +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + BOOLEAN res; + + res = HidD_GetProductString(dev->device_handle, string, 2 * maxlen); + if (!res) { + register_error(dev, "HidD_GetProductString"); + return -1; + } + + return 0; +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + BOOLEAN res; + + res = HidD_GetSerialNumberString(dev->device_handle, string, 2 * maxlen); + if (!res) { + register_error(dev, "HidD_GetSerialNumberString"); + return -1; + } + + return 0; +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) +{ + BOOLEAN res; + + res = HidD_GetIndexedString(dev->device_handle, string_index, string, 2 * maxlen); + if (!res) { + register_error(dev, "HidD_GetIndexedString"); + return -1; + } + + return 0; +} + + +HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) +{ + return (wchar_t*)dev->last_error_str; +} + + +//#define PICPGM +//#define S11 +#define P32 +#ifdef S11 + unsigned short VendorID = 0xa0a0; + unsigned short ProductID = 0x0001; +#endif + +#ifdef P32 + unsigned short VendorID = 0x04d8; + unsigned short ProductID = 0x3f; +#endif + + +#ifdef PICPGM + unsigned short VendorID = 0x04d8; + unsigned short ProductID = 0x0033; +#endif + + +#if 0 +int __cdecl main(int argc, char* argv[]) +{ + int res; + unsigned char buf[65]; + + UNREFERENCED_PARAMETER(argc); + UNREFERENCED_PARAMETER(argv); + + // Set up the command buffer. + memset(buf,0x00,sizeof(buf)); + buf[0] = 0; + buf[1] = 0x81; + + + // Open the device. + int handle = open(VendorID, ProductID, L"12345"); + if (handle < 0) + printf("unable to open device\n"); + + + // Toggle LED (cmd 0x80) + buf[1] = 0x80; + res = write(handle, buf, 65); + if (res < 0) + printf("Unable to write()\n"); + + // Request state (cmd 0x81) + buf[1] = 0x81; + write(handle, buf, 65); + if (res < 0) + printf("Unable to write() (2)\n"); + + // Read requested state + read(handle, buf, 65); + if (res < 0) + printf("Unable to read()\n"); + + // Print out the returned buffer. + for (int i = 0; i < 4; i++) + printf("buf[%d]: %d\n", i, buf[i]); + + return 0; +} +#endif + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/hidapi.h b/hidapi.h new file mode 100644 index 0000000..e8ad2ca --- /dev/null +++ b/hidapi.h @@ -0,0 +1,386 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 8/22/2009 + + Copyright 2009, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + http://github.com/signal11/hidapi . +********************************************************/ + +/** @file + * @defgroup API hidapi API + */ + +#ifndef HIDAPI_H__ +#define HIDAPI_H__ + +#include + +#ifdef _WIN32 +// #define HID_API_EXPORT __declspec(dllexport) // BPQ + #define HID_API_EXPORT + #define HID_API_CALL +#else + #define HID_API_EXPORT /**< API export macro */ + #define HID_API_CALL /**< API call macro */ +#endif + +#define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/ + +#ifdef __cplusplus +extern "C" { +#endif + struct hid_device_; + typedef struct hid_device_ hid_device; /**< opaque hidapi structure */ + + /** hidapi info structure */ + struct hid_device_info { + /** Platform-specific device path */ + char *path; + /** Device Vendor ID */ + unsigned short vendor_id; + /** Device Product ID */ + unsigned short product_id; + /** Serial Number */ + wchar_t *serial_number; + /** Device Release Number in binary-coded decimal, + also known as Device Version Number */ + unsigned short release_number; + /** Manufacturer String */ + wchar_t *manufacturer_string; + /** Product string */ + wchar_t *product_string; + /** Usage Page for this Device/Interface + (Windows/Mac only). */ + unsigned short usage_page; + /** Usage for this Device/Interface + (Windows/Mac only).*/ + unsigned short usage; + /** The USB interface which this logical device + represents. Valid on both Linux implementations + in all cases, and valid on the Windows implementation + only if the device contains more than one interface. */ + int interface_number; + + /** Pointer to the next device */ + struct hid_device_info *next; + }; + + + /** @brief Initialize the HIDAPI library. + + This function initializes the HIDAPI library. Calling it is not + strictly necessary, as it will be called automatically by + hid_enumerate() and any of the hid_open_*() functions if it is + needed. This function should be called at the beginning of + execution however, if there is a chance of HIDAPI handles + being opened by different threads simultaneously. + + @ingroup API + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_init(void); + + /** @brief Finalize the HIDAPI library. + + This function frees all of the static data associated with + HIDAPI. It should be called at the end of execution to avoid + memory leaks. + + @ingroup API + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_exit(void); + + /** @brief Enumerate the HID Devices. + + This function returns a linked list of all the HID devices + attached to the system which match vendor_id and product_id. + If @p vendor_id and @p product_id are both set to 0, then + all HID devices will be returned. + + @ingroup API + @param vendor_id The Vendor ID (VID) of the types of device + to open. + @param product_id The Product ID (PID) of the types of + device to open. + + @returns + This function returns a pointer to a linked list of type + struct #hid_device, containing information about the HID devices + attached to the system, or NULL in the case of failure. Free + this linked list by calling hid_free_enumeration(). + */ + struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id); + + /** @brief Free an enumeration Linked List + + This function frees a linked list created by hid_enumerate(). + + @ingroup API + @param devs Pointer to a list of struct_device returned from + hid_enumerate(). + */ + void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs); + + /** @brief Open a HID device using a Vendor ID (VID), Product ID + (PID) and optionally a serial number. + + If @p serial_number is NULL, the first device with the + specified VID and PID is opened. + + @ingroup API + @param vendor_id The Vendor ID (VID) of the device to open. + @param product_id The Product ID (PID) of the device to open. + @param serial_number The Serial Number of the device to open + (Optionally NULL). + + @returns + This function returns a pointer to a #hid_device object on + success or NULL on failure. + */ + HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, wchar_t *serial_number); + + /** @brief Open a HID device by its path name. + + The path name be determined by calling hid_enumerate(), or a + platform-specific path name can be used (eg: /dev/hidraw0 on + Linux). + + @ingroup API + @param path The path name of the device to open + + @returns + This function returns a pointer to a #hid_device object on + success or NULL on failure. + */ + HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path); + + /** @brief Write an Output report to a HID device. + + The first byte of @p data[] must contain the Report ID. For + devices which only support a single report, this must be set + to 0x0. The remaining bytes contain the report data. Since + the Report ID is mandatory, calls to hid_write() will always + contain one more byte than the report contains. For example, + if a hid report is 16 bytes long, 17 bytes must be passed to + hid_write(), the Report ID (or 0x0, for devices with a + single report), followed by the report data (16 bytes). In + this example, the length passed in would be 17. + + hid_write() will send the data on the first OUT endpoint, if + one exists. If it does not, it will send the data through + the Control Endpoint (Endpoint 0). + + @ingroup API + @param device A device handle returned from hid_open(). + @param data The data to send, including the report number as + the first byte. + @param length The length in bytes of the data to send. + + @returns + This function returns the actual number of bytes written and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length); + + /** @brief Read an Input report from a HID device with timeout. + + Input reports are returned + to the host through the INTERRUPT IN endpoint. The first byte will + contain the Report number if the device uses numbered reports. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data A buffer to put the read data into. + @param length The number of bytes to read. For devices with + multiple reports, make sure to read an extra byte for + the report number. + @param milliseconds timeout in milliseconds or -1 for blocking wait. + + @returns + This function returns the actual number of bytes read and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds); + + /** @brief Read an Input report from a HID device. + + Input reports are returned + to the host through the INTERRUPT IN endpoint. The first byte will + contain the Report number if the device uses numbered reports. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data A buffer to put the read data into. + @param length The number of bytes to read. For devices with + multiple reports, make sure to read an extra byte for + the report number. + + @returns + This function returns the actual number of bytes read and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length); + + /** @brief Set the device handle to be non-blocking. + + In non-blocking mode calls to hid_read() will return + immediately with a value of 0 if there is no data to be + read. In blocking mode, hid_read() will wait (block) until + there is data to read before returning. + + Nonblocking can be turned on and off at any time. + + @ingroup API + @param device A device handle returned from hid_open(). + @param nonblock enable or not the nonblocking reads + - 1 to enable nonblocking + - 0 to disable nonblocking. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock); + + /** @brief Send a Feature report to the device. + + Feature reports are sent over the Control endpoint as a + Set_Report transfer. The first byte of @p data[] must + contain the Report ID. For devices which only support a + single report, this must be set to 0x0. The remaining bytes + contain the report data. Since the Report ID is mandatory, + calls to hid_send_feature_report() will always contain one + more byte than the report contains. For example, if a hid + report is 16 bytes long, 17 bytes must be passed to + hid_send_feature_report(): the Report ID (or 0x0, for + devices which do not use numbered reports), followed by the + report data (16 bytes). In this example, the length passed + in would be 17. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data The data to send, including the report number as + the first byte. + @param length The length in bytes of the data to send, including + the report number. + + @returns + This function returns the actual number of bytes written and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length); + + /** @brief Get a feature report from a HID device. + + Make sure to set the first byte of @p data[] to the Report + ID of the report to be read. Make sure to allow space for + this extra byte in @p data[]. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data A buffer to put the read data into, including + the Report ID. Set the first byte of @p data[] to the + Report ID of the report to be read. + @param length The number of bytes to read, including an + extra byte for the report ID. The buffer can be longer + than the actual report. + + @returns + This function returns the number of bytes read and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length); + + /** @brief Close a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + */ + void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device); + + /** @brief Get The Manufacturer String from a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen); + + /** @brief Get The Product String from a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen); + + /** @brief Get The Serial Number String from a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen); + + /** @brief Get a string from a HID device, based on its string index. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string_index The index of the string to get. + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen); + + /** @brief Get a string describing the last error which occurred. + + @ingroup API + @param device A device handle returned from hid_open(). + + @returns + This function returns a string containing the last error + which occurred or NULL if none has occurred. + */ + HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device); + + int HID_API_EXPORT HID_API_CALL hid_set_ptt(int state); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/icon1.ico b/icon1.ico new file mode 100644 index 0000000..8aa98e5 Binary files /dev/null and b/icon1.ico differ diff --git a/icon2.ico b/icon2.ico new file mode 100644 index 0000000..5d06b9f Binary files /dev/null and b/icon2.ico differ diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..270cffe --- /dev/null +++ b/main.cpp @@ -0,0 +1,26 @@ +#include "QtTermTCP.h" +#include +#include + +QApplication * a; + +int screenHeight = 0; // used to control dialog sizes on Android +int screenWidth = 0; + +int main(int argc, char *argv[]) +{ + a = new QApplication (argc, argv); + + QSize r = a->screens()[0]->size(); + + screenHeight = r.height(); + screenWidth = r.width(); + + QtTermTCP w; + +//#ifdef ANDROID +// w.setWindowFlags(Qt::Window | Qt::CustomizeWindowHint); // Qt::FramelessWindowHint); +//#endif + w.show(); + return a->exec(); +} diff --git a/makeit b/makeit new file mode 100644 index 0000000..f008177 --- /dev/null +++ b/makeit @@ -0,0 +1,9 @@ +cp --preserve /mnt/Source/QT/QtTermTCP/*.cpp ./ +cp --preserve /mnt/Source/QT/QtTermTCP/*.c ./ +cp --preserve /mnt/Source/QT/QtTermTCP/*.h ./ +cp --preserve /mnt/Source/QT/QtTermTCP/*.ui ./ + +qmake +make -j4 +cp QtTermTCP /mnt/Source + diff --git a/ui_TCPHostConfig.h b/ui_TCPHostConfig.h new file mode 100644 index 0000000..1f232a7 --- /dev/null +++ b/ui_TCPHostConfig.h @@ -0,0 +1,65 @@ +/******************************************************************************** +** Form generated from reading UI file 'TCPHostConfig.ui' +** +** Created by: Qt User Interface Compiler version 5.14.2 +** +** WARNING! All changes made in this file will be lost when recompiling UI file! +********************************************************************************/ + +#ifndef UI_TCPHOSTCONFIG_H +#define UI_TCPHOSTCONFIG_H + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Ui_Dialog +{ +public: + QDialogButtonBox *buttonBox; + QTextEdit *Host; + + void setupUi(QDialog *Dialog) + { + if (Dialog->objectName().isEmpty()) + Dialog->setObjectName(QString::fromUtf8("Dialog")); + Dialog->resize(400, 300); + QIcon icon; + icon.addFile(QString::fromUtf8("QtTermTCP.ico"), QSize(), QIcon::Normal, QIcon::Off); + Dialog->setWindowIcon(icon); + buttonBox = new QDialogButtonBox(Dialog); + buttonBox->setObjectName(QString::fromUtf8("buttonBox")); + buttonBox->setGeometry(QRect(30, 240, 341, 32)); + buttonBox->setOrientation(Qt::Horizontal); + buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Save); + buttonBox->setCenterButtons(true); + Host = new QTextEdit(Dialog); + Host->setObjectName(QString::fromUtf8("Host")); + Host->setGeometry(QRect(184, 44, 104, 23)); + + retranslateUi(Dialog); + QObject::connect(buttonBox, SIGNAL(accepted()), Dialog, SLOT(accept())); + QObject::connect(buttonBox, SIGNAL(rejected()), Dialog, SLOT(reject())); + + QMetaObject::connectSlotsByName(Dialog); + } // setupUi + + void retranslateUi(QDialog *Dialog) + { + Dialog->setWindowTitle(QCoreApplication::translate("Dialog", "TCP Host Config", nullptr)); + } // retranslateUi + +}; + +namespace Ui { + class Dialog: public Ui_Dialog {}; +} // namespace Ui + +QT_END_NAMESPACE + +#endif // UI_TCPHOSTCONFIG_H diff --git a/utf8Routines.cpp b/utf8Routines.cpp new file mode 100644 index 0000000..59b1e23 --- /dev/null +++ b/utf8Routines.cpp @@ -0,0 +1,597 @@ +/* +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 +*/ + +// Routines to convert to and from UTF8 + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + +#define _CRT_SECURE_NO_DEPRECATE + + +#ifndef WIN32 + +#define VOID void +#define BOOL int +#define TRUE 1 +#define FALSE 0 + +#include + + +#else +#include +#endif + +int convUTF8 = 0; + +unsigned int CP437toUTF8Data[128] = { + 34755, 48323, 43459, 41667, + 42179, 41155, 42435, 42947, + 43715, 43971, 43203, 44995, + 44739, 44227, 33987, 34243, + 35267, 42691, 34499, 46275, // 90 + 46787, 45763, 48067, 47555, + 49091, 38595, 40131, 41666, + 41922, 42434, 10978018, 37574, + 41411, 44483, 46019, 47811, // A0 + 45507, 37315, 43714, 47810, + 49090, 9473250, 44226, 48578, + 48322, 41410, 43970, 48066, + 9541346, 9606882, 9672418, 8557794, //B0 + 10786018, 10589666, 10655202, 9868770, + 9803234, 10720738, 9541090, 9934306, + 10327522, 10261986, 10196450, 9475298, + 9737442, 11834594, 11310306, 10261730, //C0 + 8426722, 12358882, 10393058, 10458594, + 10130914, 9737698, 11113954, 10917346, + 10524130, 9475554, 11310562, 10982882, + 11048418, 10786274, 10851810, 10065378, //D0 + 9999842, 9606626, 9672162, 11245026, + 11179490, 9999586, 9213154, 8951522, + 8689378, 9213666, 9475810, 8427234, + 45518, 40899, 37838, 32975, // E0 + 41934, 33743, 46530, 33999, + 42702, 39118, 43470, 46286, + 10389730, 34511, 46542, 11110626, + 10586594, 45506, 10848738, 10783202, // F0 + 10521826, 10587362, 47043, 8948194, + 45250, 10062050, 47042, 10127586, + 12550626, 45762, 10524386, 41154, +}; +unsigned int CP437toUTF8DataLen[128] = { + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 3, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 3, 2, 2, + 2, 2, 2, 2, + 3, 3, 3, 3, + 3, 3, 3, 3, + 3, 3, 3, 3, + 3, 3, 3, 3, + 3, 3, 3, 3, + 3, 3, 3, 3, + 3, 3, 3, 3, + 3, 3, 3, 3, + 3, 3, 3, 3, + 3, 3, 3, 3, + 3, 3, 3, 3, + 3, 3, 3, 3, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 3, 2, 2, 3, + 3, 2, 3, 3, + 3, 3, 2, 3, + 2, 3, 2, 3, + 3, 2, 3, 2, +}; + +unsigned int CP1251toUTF8Data[128] = { + 33488, 33744, 10125538, 37841, + 10387682, 10911970, 10518754, 10584290, + 11305698, 11567330, 35280, 12157154, + 35536, 36048, 35792, 36816, + 37585, 9994466, 10060002, 10256610, + 10322146, 10649826, 9666786, 9732322, + 39106, 10650850, 39377, 12222690, + 39633, 40145, 39889, 40913, + 41154, 36560, 40657, 35024, + 42178, 37074, 42690, 42946, + 33232, 43458, 34000, 43970, + 44226, 44482, 44738, 34768, + 45250, 45506, 34512, 38609, + 37330, 46530, 46786, 47042, + 37329, 9864418, 38097, 48066, + 39121, 34256, 38353, 38865, + 37072, 37328, 37584, 37840, + 38096, 38352, 38608, 38864, + 39120, 39376, 39632, 39888, + 40144, 40400, 40656, 40912, + 41168, 41424, 41680, 41936, + 42192, 42448, 42704, 42960, + 43216, 43472, 43728, 43984, + 44240, 44496, 44752, 45008, + 45264, 45520, 45776, 46032, + 46288, 46544, 46800, 47056, + 47312, 47568, 47824, 48080, + 48336, 48592, 48848, 49104, + 32977, 33233, 33489, 33745, + 34001, 34257, 34513, 34769, + 35025, 35281, 35537, 35793, + 36049, 36305, 36561, 36817, +}; +unsigned int CP1251toUTF8DataLen[128] = { + 2, 2, 3, 2, + 3, 3, 3, 3, + 3, 3, 2, 3, + 2, 2, 2, 2, + 2, 3, 3, 3, + 3, 3, 3, 3, + 2, 3, 2, 3, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 3, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, +}; + + +unsigned int CP1252toUTF8Data[128] = { + 11305698, 33218, 10125538, 37574, + 10387682, 10911970, 10518754, 10584290, + 34507, 11567330, 41157, 12157154, + 37573, 36290, 48581, 36802, + 37058, 9994466, 10060002, 10256610, + 10322146, 10649826, 9666786, 9732322, + 40139, 10650850, 41413, 12222690, + 37829, 40386, 48837, 47301, + 41154, 41410, 41666, 41922, + 42178, 42434, 42690, 42946, + 43202, 43458, 43714, 43970, + 44226, 44482, 44738, 44994, + 45250, 45506, 45762, 46018, + 46274, 46530, 46786, 47042, + 47298, 47554, 47810, 48066, + 48322, 48578, 48834, 49090, + 32963, 33219, 33475, 33731, + 33987, 34243, 34499, 34755, + 35011, 35267, 35523, 35779, + 36035, 36291, 36547, 36803, + 37059, 37315, 37571, 37827, + 38083, 38339, 38595, 38851, + 39107, 39363, 39619, 39875, + 40131, 40387, 40643, 40899, + 41155, 41411, 41667, 41923, + 42179, 42435, 42691, 42947, + 43203, 43459, 43715, 43971, + 44227, 44483, 44739, 44995, + 45251, 45507, 45763, 46019, + 46275, 46531, 46787, 47043, + 47299, 47555, 47811, 48067, + 48323, 48579, 48835, 49091, +}; +unsigned int CP1252toUTF8DataLen[128] = { + 3, 2, 3, 2, + 3, 3, 3, 3, + 2, 3, 2, 3, + 2, 2, 2, 2, + 2, 3, 3, 3, + 3, 3, 3, 3, + 2, 3, 2, 3, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, +}; + +#ifdef __BIG_ENDIAN__ +BOOL initUTF8Done = FALSE; +#else +BOOL initUTF8Done = TRUE; +#endif + +VOID initUTF8() +{ + // Swap bytes of UTF-8 COde on Big-endian systems + + int n; + char temp[4]; + char rev[4]; + + if (initUTF8Done) + return; + + for (n = 0; n <128; n++) + { + memcpy(temp, &CP437toUTF8Data[n], 4); + rev[0] = temp[3]; + rev[1] = temp[2]; + rev[2] = temp[1]; + rev[3] = temp[0]; + + memcpy(&CP437toUTF8Data[n], rev, 4); + + memcpy(temp, &CP1251toUTF8Data[n], 4); + rev[0] = temp[3]; + rev[1] = temp[2]; + rev[2] = temp[1]; + rev[3] = temp[0]; + + memcpy(&CP1251toUTF8Data[n], rev, 4); + + memcpy(temp, &CP1252toUTF8Data[n], 4); + rev[0] = temp[3]; + rev[1] = temp[2]; + rev[2] = temp[1]; + rev[3] = temp[0]; + + memcpy(&CP1252toUTF8Data[n], rev, 4); + } + + initUTF8Done = TRUE; +} + + + +int Is8Bit(unsigned char *cpt, int len) +{ + int n; + + cpt--; + + for (n = 0; n < len; n++) + { + cpt++; + + if (*cpt > 127) + return TRUE; + } + + return FALSE; +} + + +int IsUTF8(unsigned char *ptr, int len) +{ + int n; + unsigned char * cpt = ptr; + + // This has to be a bit loose, as UTF8 sequences may split over packets + + memcpy(&ptr[len], "\x80\x80\x80", 3); // in case trailing bytes are in next packet + + // Don't check first 3 if could be part of sequence + + if ((*(cpt) & 0xC0) == 0x80) // Valid continuation + { + cpt++; + len--; + if ((*(cpt) & 0xC0) == 0x80) // Valid continuation + { + cpt++; + len--; + if ((*(cpt) & 0xC0) == 0x80) // Valid continuation + { + cpt++; + len--; + } + } + } + + cpt--; + + for (n = 0; n < len; n++) + { + cpt++; + + if (*cpt < 128) + continue; + + if ((*cpt & 0xF8) == 0xF0) + { // start of 4-byte sequence + if (((*(cpt + 1) & 0xC0) == 0x80) + && ((*(cpt + 2) & 0xC0) == 0x80) + && ((*(cpt + 3) & 0xC0) == 0x80)) + { + cpt += 3; + n += 3; + continue; + } + return FALSE; + } + else if ((*cpt & 0xF0) == 0xE0) + { // start of 3-byte sequence + if (((*(cpt + 1) & 0xC0) == 0x80) + && ((*(cpt + 2) & 0xC0) == 0x80)) + { + cpt += 2; + n += 2; + continue; + } + return FALSE; + } + else if ((*cpt & 0xE0) == 0xC0) + { // start of 2-byte sequence + if ((*(cpt + 1) & 0xC0) == 0x80) + { + cpt++; + n++; + continue; + } + return FALSE; + } + return FALSE; + } + + return TRUE; +} + +int WebIsUTF8(unsigned char *ptr, int len) +{ + int n; + unsigned char * cpt = ptr; + + // This is simpler than the Term version, as it only handles complete lines of text, so cant get split sequences + + cpt--; + + for (n = 0; n < len; n++) + { + cpt++; + + if (*cpt < 128) + continue; + + if ((*cpt & 0xF8) == 0xF0) + { // start of 4-byte sequence + if (((*(cpt + 1) & 0xC0) == 0x80) + && ((*(cpt + 2) & 0xC0) == 0x80) + && ((*(cpt + 3) & 0xC0) == 0x80)) + { + cpt += 3; + n += 3; + continue; + } + return FALSE; + } + else if ((*cpt & 0xF0) == 0xE0) + { // start of 3-byte sequence + if (((*(cpt + 1) & 0xC0) == 0x80) + && ((*(cpt + 2) & 0xC0) == 0x80)) + { + cpt += 2; + n += 2; + continue; + } + return FALSE; + } + else if ((*cpt & 0xE0) == 0xC0) + { // start of 2-byte sequence + if ((*(cpt + 1) & 0xC0) == 0x80) + { + cpt++; + n++; + continue; + } + return FALSE; + } + return FALSE; + } + + return TRUE; +} + + + +int Convert437toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF) +{ + unsigned char * ptr1 = MsgPtr; + unsigned char * ptr2 = UTF; + int n; + unsigned int c; + + for (n = 0; n < len; n++) + { + c = *(ptr1++); + + if (c < 128) + { + *(ptr2++) = c; + continue; + } + + memcpy(ptr2, &CP437toUTF8Data[c - 128], CP437toUTF8DataLen[c - 128]); + ptr2 += CP437toUTF8DataLen[c - 128]; + } + + return (int)(ptr2 - UTF); +} + +int Convert1251toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF) +{ + unsigned char * ptr1 = MsgPtr; + unsigned char * ptr2 = UTF; + int n; + unsigned int c; + + for (n = 0; n < len; n++) + { + c = *(ptr1++); + + if (c < 128) + { + *(ptr2++) = c; + continue; + } + + memcpy(ptr2, &CP1251toUTF8Data[c - 128], CP1251toUTF8DataLen[c - 128]); + ptr2 += CP1251toUTF8DataLen[c - 128]; + } + + return (int)(ptr2 - UTF); +} + +int Convert1252toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF) +{ + unsigned char * ptr1 = MsgPtr; + unsigned char * ptr2 = UTF; + int n; + unsigned int c; + + for (n = 0; n < len; n++) + { + c = *(ptr1++); + + if (c < 128) + { + *(ptr2++) = c; + continue; + } + + memcpy(ptr2, &CP1252toUTF8Data[c - 128], CP1252toUTF8DataLen[c - 128]); + ptr2 += CP1252toUTF8DataLen[c - 128]; + } + + return (int)(ptr2 - UTF); +} + +int TrytoGuessCode(unsigned char * Char, int Len) +{ + int Above127 = 0; + int LineDraw = 0; + int NumericAndSpaces = 0; + int n; + + for (n = 0; n < Len; n++) + { + if (Char[n] < 65) + { + NumericAndSpaces++; + } + else + { + if (Char[n] > 127) + { + Above127++; + if (Char[n] == 0xF8 || (Char[n] > 178 && Char[n] < 224)) + { + LineDraw++; + } + } + } + } + + if (Above127 == 0) // Doesn't really matter! + return 1252; + + if (Above127 == LineDraw) + return 437; // If only Line Draw chars, assume line draw + + // If mainly below 128, it is probably Latin if mainly above, probably Cyrillic + + if ((Len - (NumericAndSpaces + Above127)) < Above127) + return 1251; + else + return 1252; +} + +unsigned char outbuffer[16384]; // I don't think this needs to be thread safe + +int checkUTF8(unsigned char * in, int len, unsigned char * out) +{ + // We mustn't mess with input string + + unsigned char Msg[8192]; + int u, code = convUTF8; + + if (convUTF8 == -1 || !Is8Bit(in, len)) + { + // just copy to output + + memcpy(out, in, len); + return len; + } + + // Convert + + memcpy(Msg, in, len); + Msg[len] = 0; + + if (convUTF8 == 0) // Auto - Try to guess encoding + code = TrytoGuessCode(Msg, len); + + if (code == 437) + u = Convert437toUTF8(Msg, len, out); + else if (code == 1251) + u = Convert1251toUTF8(Msg, len, out); + else + u = Convert1252toUTF8(Msg, len, out); + + return u; + +} +