However, the CygTerm is built with the gcc compiler of the Cygwin package. So, the CygTerm program is implemented by using the gcc feature. The source code is the C language.
The main engine of Tera Term is implemented by using the C++ language. It is no problem that your programming skill is only C language because the code is C-like programming. However, the Microsoft Visual C++(VC++) supports the ANSI C standard(C89) and not support the C99 version. The C99-like feature is uniquely supported by VC++. The C99-like function adds the underscore(_) to the initial name of function in order to distinguish easily. For example, the _snprintf() of VC++ is different from the snprintf() of ANSI C(C99).
The "TTSSH" module uses the OpenSSL library to perform the cryptography processing. Is seems that the OpenSSL library has only the SSL(Secure Socket Layer) protocol function for web accessing, however that is wrong. The OpenSSL library supports basic cipher algorithm and the "TTSSH" module uses only basic function. In other words, the "TTSSH" module will not be almost affected when the OpenSSL library has the security hole.
The zlib library is used to compress the SSH packet. So, the packet compression is effective on lower network throughput like the dial-up connection, however the performance is not good on high-speed network. So, the packet compression function is disable by default.
The PuTTY is a terminal emulator for free, and defacto standard in world-wide. The Pageant program of the PuTTY package is the SSH authentication agent, and the TTSSH uses the PuTTY source code to support the Pageant authentication method.
| Module | Order |
|---|---|
| TTProxy | 0 |
| TTSSH | 2500 |
| TTX Kanji Menu | 5000 |
static TTXExports Exports = {
/* This must contain the size of the structure. See below for its usage. */
sizeof(TTXExports),
/* This is the load order number of this DLL. */
ORDER,
/* Now we just list the functions that we've implemented. */
TTXInit,
NULL, /* TTXGetUIHooks */
NULL, /* TTXGetSetupHooks */
NULL, /* TTXOpenTCP */
NULL, /* TTXCloseTCP */
NULL, /* TTXSetWinSize */
TTXModifyMenu,
TTXModifyPopupMenu,
TTXProcessCommand,
NULL, /* TTXEnd */
NULL /* TTXSetCommandLine */
};
Basically, the export function of the plug-in module should be designed not to interfere other plug-in modules. Also, when the plug-in module is called by Tera Term body, the module needs to check the request for own module.
| Function | Description |
|---|---|
| TTXBind | This function is called at first. The function sends the export function table to the Tera Term body. |
| TTXInit | This function is quickly called after the TTXBind() calling. The function received the global variables(ts and cv) of the Tera Term body, and initialized own plug-in module. |
| TTXGetUIHooks | This function can hook the dialog handle. The function is used to change the dialog interface of Tera term. The hook target is in the following: &SetupTerminal, &SetupWin, &SetupKeyboard, &SetupSerialPort, &SetupTCPIP, &GetHostName, &ChangeDirectory, &AboutDialog, &ChooseFontDlg, &SetupGeneral, &WindowWindow |
| TTXGetSetupHooks | This function can hook the setup routine. The hooked function should call the original function. When some plug-in module exists, each function is called in order. The hook target is in the following: &ReadIniFile, &WriteIniFile, &ReadKeyboardCnf, &CopyHostList, &AddHostToList, &ParseParam |
| TTXOpenTCP | This function is called on TCP connection, and is not called on Serial connection. Also, the function can hook the socket interface in the following: &Pclosesocket, &Pconnect, &Phtonl, &Phtons, &Pinet_addr, &Pioctlsocket, &Precv, &Pselect, &Psend, &Psetsockopt, &Psocket, &PWSAAsyncSelect, &PWSAAsyncGetHostByName, &PWSACancelAsyncRequest, &PWSAGetLastError |
| TTXCloseTCP | This function is called on TCP disconnection, and is not called on Serial connection. When below hooked interface exists, the function should restore an original interface. &Pclosesocket, &Pconnect, &Phtonl, &Phtons, &Pinet_addr, &Pioctlsocket, &Precv, &Pselect, &Psend, &Psetsockopt, &Psocket, &PWSAAsyncSelect, &PWSAAsyncGetHostByName, &PWSACancelAsyncRequest, &PWSAGetLastError |
| TTXSetWinSize | This function is called when the terminal screen is resized. |
| TTXModifyMenu | This function is called when the Tera Term menu is initialized. The function can insert new menu on the original menu list. |
| TTXModifyPopupMenu | This function is called when the Tera Term pop-up menu is initialized. The function can insert new pop-up menu on the original pop-up menu list. |
| TTXProcessCommand | This function is called when the Tera Term menu is executed. The function can process the plug-in module menu. |
| TTXEnd | This function is called when the Tera Term terminates. |
| TTXSetCommandLine | This function is called when the command line parameter is processed on new connection and duplicating connection. An original parameter of the plug-in module is processed. |
ts->ConfirmChangePaste = GetOnOff(Section, "ConfirmChangePaste", FName, TRUE);Also, the WriteIniFile()#ttset.c is implemented to write the file.
WriteOnOff(Section, "ConfirmChangePaste", FName, ts->ConfirmChangePaste);When the entry is set as a string, use the GetPrivateProfileString() and WritePrivateProfileString() Win32 API. However, when the entry is set as an integer, use the GetPrivateProfileInt() and WriteInt() Win32 API.
| Old | New |
|---|---|
| sprintf(), _snprintf() | _snprintf_s() |
| strcat(), strncat() | strncat_s() |
| strcpy(), strncpy() | strncpy_s() |
Example of using the strncpy_s() function is below shown. The second argument(numberOfElements) is specified with the buffer size including the terminating null(\0). The writing buffer has only three bytes, five bytes data specified by the third argument(strSource) truncates to two bytes. As a result, the buf[] stores the "he\0" string.
char buf[3]; strncpy_s(buf, sizeof(buf), "hello", _TRUNCATE);Next, example of using the strncat_s() function is shown. The first argument(strDest) should have terminating null to concatenate strings. The second argument(numberOfElements) of the strncpy_s() function is specified with the buffer size including terminating null. For below example, when the first code is executed, five bytes(four chars + null) is stored. When the second code is executed, two chars are only copied into the buffer because the buffer size has two bytes. Finally, the buffer stores the "TeraTe"(4 chars + 2 chars + null).
char str[7]; str[0] = '\0'; strncat_s(str, sizeof(str), "Tera", _TRUNCATE); strncat_s(str, sizeof(str), "Term", _TRUNCATE);Finally, the _snprintf_s() function uses. Confusingly, the _snprintf() does not use because terminating null may not be added into the buffer. Example of the _snprintf_s() function is below shown. The buf[] has the "ab\0".
char buf[3]; _snprintf_s(buf, sizeof(buf), _TRUNCATE, "abcdef");
static BOOL MySetLayeredWindowAttributes(HWND hwnd, COLORREF crKey, BYTE bAlpha, DWORD dwFlags)
{
typedef BOOL (WINAPI *func)(HWND,COLORREF,BYTE,DWORD);
static HMODULE g_hmodUser32 = NULL;
static func g_pSetLayeredWindowAttributes = NULL;
if (g_hmodUser32 == NULL) {
g_hmodUser32 = LoadLibrary("user32.dll");
if (g_hmodUser32 == NULL)
return FALSE;
g_pSetLayeredWindowAttributes =
(func)GetProcAddress(g_hmodUser32, "SetLayeredWindowAttributes");
}
if (g_pSetLayeredWindowAttributes == NULL)
return FALSE;
return g_pSetLayeredWindowAttributes(hwnd, crKey,
bAlpha, dwFlags);
}
However, it is too much like work to define a function prototype declaration manually. So, it eliminates complicated procedure by using DLL delay loading.
Currently, Tera Term can work on the Windows 95 with a method despite Tera Term is built by the Visual Studio 2005. Certainly, this is Microsoft unofficial method.
The binary program built by the Visual Studio 2005 links the IsDebuggerPresent function by default. The program can not work on the Windows 95 with link error because the function is added from the Windows 98.
So, this problem can be avoided to define dummy symbol of the IsDebuggerPresent function on the Windows 95. For details, refer to the "comapt_w95.h" header.
void OutputDebugPrintf(char *fmt, ...) {
char tmp[1024];
va_list arg;
va_start(arg, fmt);
_vsnprintf(tmp, sizeof(tmp), fmt, arg);
OutputDebugString(tmp);
}
#ifdef _DEBUG _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); #endifSo, an application program works on the virtual memory like as Windows. When the memory leak exists on terminating the program, the operation system frees the not freed memory.
| Generating point | Source file |
|---|---|
| Serial connection | CommStart()#commlib.c |
| TELNET keep-alive | TelStartKeepAliveThread()#telnet.c |
| IPv4/v6 socket creation | WSAAsyncGetAddrInfo()#WSAAsyncGetAddrInfo.c |
| Generating point | Source file |
|---|---|
| SSH keep-alive | start_ssh_heartbeat_thread()#ssh.c |
| SCP sending | SSH2_scp_tolocal()#ssh.c |
| SCP receiving | SSH2_scp_fromremote()#ssh.c |
#define WM_SEND_HEARTBEAT (WM_USER + 1)
static LRESULT CALLBACK telnet_heartbeat_dlg_proc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
switch (msg) {
case WM_INITDIALOG:
return FALSE;
case WM_SEND_HEARTBEAT:
TelSendNOP();
return TRUE;
break;
case WM_COMMAND:
break;
case WM_CLOSE:
return TRUE;
case WM_DESTROY:
return TRUE;
default:
return FALSE;
}
return TRUE;
}
static unsigned _stdcall TelKeepAliveThread(void *dummy) {
static int instance = 0;
if (instance > 0)
return 0;
instance++;
while (cv.Open && nop_interval > 0) {
if (time(NULL) >= cv.LastSendTime + nop_interval) {
SendMessage(keepalive_dialog, WM_SEND_HEARTBEAT, 0, 0);
}
Sleep(100);
}
instance--;
return 0;
}
void TelStartKeepAliveThread() {
unsigned tid;
if (ts.TelKeepAliveInterval > 0) {
nop_interval = ts.TelKeepAliveInterval;
keepalive_dialog = CreateDialog(hInst, MAKEINTRESOURCE(IDD_BROADCAST_DIALOG),
HVTWin, (DLGPROC)telnet_heartbeat_dlg_proc);
keepalive_thread = (HANDLE)_beginthreadex(NULL, 0, TelKeepAliveThread, NULL, 0, &tid);
if (keepalive_thread == (HANDLE)-1) {
nop_interval = 0;
}
}
}
The DDE and TCP communication are similar because the protocol connects between a server and a client with peer-to-peer and communicates. The application can use the DDE communication by using the DDEL(Dynamic Data Exchange Management Library) library like as the Win32 API.
| Type | Description |
|---|---|
| XTYP_ADVREQ | The DDE server sends this message to own in order to send the message from the DDE server to client. |
| XTYP_POKE | The DDE client sends the data to the DDE server. |
| XTYP_ADVSTART | The advise loop starts on the DDE server. |
| XTYP_ADVDATA | This message periodically sends the data to the DDE client. |
| XTYP_EXECUTE | This message sends a string to the DDE server for specifying the server. |
| Function | Description |
|---|---|
| DdeInitialize | This function initialized the DDE and registers the callback function. If the function succeeds, the instance returns. |
| DdeCreateStringHandle | This function creates a handle from a string. The handle is used for communications the server and client. |
| DdeNameService | This function registers the instance and the service name("TERATERM") on the DDE server. If the registration succeeds, the XTYP_REGISTER transaction is send to the DDE client. Also, this function uses for unregistration. |
| DdeCmpStringHandles | This function compares two string handles. |
| DdeClientTransaction | This function sends a transaction from client to server. The type of transactions can be specified with the XTYP_REQUEST, XTYP_EXECUTE, XTYP_ADVSTART, XTYP_POKE and so on. A timeout value that can wait until a server's ACK, and the value is almost specified with the 1000 milliseconds(1 second). However, the 5000 milliseconds(5 seconds) is specified when the Tera Term confirms the ACK. |
| DdeAccessData | This function retrieves a point of data from the DDE handle. If the pointer is not required, the DdeUnaccessData() must be called. |
| DdeCreateDataHandle | This function creates the DDE object and returns the handle. The handle is used for the DDE server's advise loop, also sending a data to the DDE client when the XTYP_REQUEST transaction is received. |
| DdeGetData | This function copies from the DDE object to th buffer. |
| DdeDisconnect | This function terminates the DDE communication. |
| DdePostAdvise | This function uses at the DDE server and sends the XTYP_ADVREQ transaction to own. |
SetTopic(); if (! InitDDE()) return; strncpy_s(Cmnd, sizeof(Cmnd),"TTPMACRO /D=", _TRUNCATE); strncat_s(Cmnd,sizeof(Cmnd),TopicName,_TRUNCATE);When the transaction is sent to th DDE sever from the DDE client, the DdeCallbackProc callback function executes. The callback function is registered when the DDE is initialized by the DdeInitialize().
ConvH = DdeConnect(Inst, Service, Topic, NULL);
if (ConvH == 0) return FALSE;
Linked = TRUE;
Cmd[0] = CmdSetHWnd;
w = HIWORD(HWin);
Word2HexStr(w,&(Cmd[1]));
w = LOWORD(HWin);
Word2HexStr(w,&(Cmd[5]));
DdeClientTransaction(Cmd,strlen(Cmd)+1,ConvH,0,
CF_OEMTEXT,XTYP_EXECUTE,1000,NULL);
DdeClientTransaction(NULL,0,ConvH,Item,
CF_OEMTEXT,XTYP_ADVSTART,1000,NULL);
if (((h = GetModuleHandle("kernel32.dll")) != NULL) &&
(GetProcAddress(h, "QueryDosDeviceA") != NULL) &&
(QueryDosDevice(NULL, devicesBuff, 65535) != 0)) {
p = devicesBuff;
while (*p != '\0') {
if (strncmp(p, "COM", 3) == 0 && p[3] != '\0') {
ComPortTable[comports++] = atoi(p+3);
if (comports >= ComPortMax)
break;
}
p += (strlen(p)+1);
}
static void ListupSerialPort(LPWORD ComPortTable, int comports, char **ComPortDesc, int ComPortMax)
{
GUID ClassGuid[1];
DWORD dwRequiredSize;
BOOL bRet;
HDEVINFO DeviceInfoSet = NULL;
SP_DEVINFO_DATA DeviceInfoData;
DWORD dwMemberIndex = 0;
int i;
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
bRet =
SetupDiClassGuidsFromName(_T("PORTS"), (LPGUID) & ClassGuid, 1,
&dwRequiredSize);
if (!bRet) {
goto cleanup;
}
DeviceInfoSet =
SetupDiGetClassDevs(&ClassGuid[0], NULL, NULL, DIGCF_PRESENT | DIGCF_PROFILE);
if (DeviceInfoSet) {
dwMemberIndex = 0;
while (SetupDiEnumDeviceInfo
(DeviceInfoSet, dwMemberIndex++, &DeviceInfoData)) {
TCHAR szFriendlyName[MAX_PATH];
TCHAR szPortName[MAX_PATH];
DWORD dwReqSize = 0;
DWORD dwPropType;
DWORD dwType = REG_SZ;
HKEY hKey = NULL;
bRet = SetupDiGetDeviceRegistryProperty(DeviceInfoSet,
&DeviceInfoData,
SPDRP_FRIENDLYNAME,
&dwPropType,
(LPBYTE)
szFriendlyName,
sizeof(szFriendlyName),
&dwReqSize);
hKey = SetupDiOpenDevRegKey(DeviceInfoSet,
&DeviceInfoData,
DICS_FLAG_GLOBAL,
0, DIREG_DEV, KEY_READ);
if (hKey) {
long lRet;
dwReqSize = sizeof(szPortName);
lRet = RegQueryValueEx(hKey,
_T("PortName"),
0,
&dwType,
(LPBYTE) & szPortName,
&dwReqSize);
RegCloseKey(hKey);
}
if (_strnicmp(szPortName, "COM", 3) == 0) { // Found COM port driver
int port = atoi(&szPortName[3]);
int i;
for (i = 0 ; i < comports ; i++) {
if (ComPortTable[i] == port) { // Confirm COM connection
ComPortDesc[i] = _strdup(szFriendlyName);
break;
}
}
}
}
}
cleanup:
SetupDiDestroyDeviceInfoList(DeviceInfoSet);
}