Sync with trunk (aka 'I want my virtualbox mouse integration too')
[reactos.git] / base / applications / network / telnet / src / tnclass.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 //Telnet Win32 : an ANSI telnet client.
3 //Copyright (C) 1998 Paul Brannan
4 //Copyright (C) 1998 I.Ioannou
5 //Copyright (C) 1997 Brad Johnson
6 //
7 //This program is free software; you can redistribute it and/or
8 //modify it under the terms of the GNU General Public License
9 //as published by the Free Software Foundation; either version 2
10 //of the License, or (at your option) any later version.
11 //
12 //This program is distributed in the hope that it will be useful,
13 //but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 //GNU General Public License for more details.
16 //
17 //You should have received a copy of the GNU General Public License
18 //along with this program; if not, write to the Free Software
19 //Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 //
21 //I.Ioannou
22 //roryt@hol.gr
23 //
24 ///////////////////////////////////////////////////////////////////////////
25
26 ///////////////////////////////////////////////////////////////////////////////
27 //
28 // Module: tnclass.cpp
29 //
30 // Contents: telnet object definition
31 //
32 // Product: telnet
33 //
34 // Revisions: August 30, 1998 Paul Brannan <pbranna@clemson.edu>
35 // July 12, 1998 Paul Brannan
36 // June 15, 1998 Paul Brannan
37 // May 14, 1998 Paul Brannan
38 // 5.April.1997 jbj@nounname.com
39 // 14.Sept.1996 jbj@nounname.com
40 // Version 2.0
41 //
42 ///////////////////////////////////////////////////////////////////////////////
43
44 #include <stdlib.h>
45 #include <string.h>
46 #include "tnclass.h"
47 #include "tnmisc.h"
48
49 // Mingw32 needs these (Paul Brannan 9/4/98)
50 #ifndef ICON_SMALL
51 #define ICON_SMALL 0
52 #endif
53 #ifndef ICON_BIG
54 #define ICON_BIG 1
55 #endif
56
57 // Ioannou Dec. 8, 1998
58 #ifdef __BORLANDC__
59 #ifndef WM_SETICON
60 #define WM_SETICON STM_SETICON
61 #endif
62 #endif
63
64 // DoInit() - performs initialization that is common to both the
65 // constructors (Paul Brannan 6/15/98)
66 void Telnet::DoInit() {
67 Socket = INVALID_SOCKET;
68 bConnected = 0;
69 bNetPaused = 1;
70 bNetFinished = 1;
71 bNetFinish = 0;
72 hThread = 0; // Sam Robertson 12/7/98
73 hProcess = 0;
74
75 WSADATA WsaData;
76
77 // Set the title
78 telSetConsoleTitle("No Connection");
79
80 // Change the icon
81 hConsoleWindow = TelnetGetConsoleWindow();
82 iconChange = SetIcon(hConsoleWindow, 0, &oldBIcon, &oldSIcon, ini.get_startdir());
83
84 if (WSAStartup(MAKEWORD(1, 1), &WsaData)) {
85 DWORD dwLastError = GetLastError();
86 printm(0, FALSE, MSG_ERROR, "WSAStartup()");
87 printm(0, TRUE, dwLastError);
88 bWinsockUp = 0;
89 return;
90 }
91 bWinsockUp = 1;
92
93 // Get keyfile (Paul Brannan 5/12/98)
94 const char *keyfile = ini.get_keyfile();
95
96 // This should be changed later to use the Tnerror routines
97 // This has been done (Paul Brannan 6/5/98)
98 if(LoadKeyMap( keyfile, ini.get_default_config()) != 1)
99 // printf("Error loading keymap.\n");
100 printm(0, FALSE, MSG_ERRKEYMAP);
101 }
102
103 Telnet::Telnet():
104 MapLoader(KeyTrans, Charmap),
105 Console(GetStdHandle(STD_OUTPUT_HANDLE)),
106 TelHandler(Network, Console, Parser),
107 ThreadParams(TelHandler),
108 Clipboard(TelnetGetConsoleWindow(), Network),
109 Mouse(Clipboard),
110 Scroller(Mouse, ini.get_scroll_size()),
111 Parser(Console, KeyTrans, Scroller, Network, Charmap) {
112 DoInit();
113 }
114
115 Telnet::Telnet(const char * szHost1, const char *strPort1):
116 MapLoader(KeyTrans, Charmap),
117 Console(GetStdHandle(STD_OUTPUT_HANDLE)),
118 TelHandler(Network, Console, Parser),
119 ThreadParams(TelHandler),
120 Clipboard(TelnetGetConsoleWindow(), Network),
121 Mouse(Clipboard),
122 Scroller(Mouse, ini.get_scroll_size()),
123 Parser(Console, KeyTrans, Scroller, Network, Charmap) {
124 DoInit();
125 Open( szHost1, strPort1);
126 }
127
128 Telnet::~Telnet(){
129 if (bWinsockUp){
130 if(bConnected) Close();
131 WSACleanup();
132 }
133
134 // Paul Brannan 8/10/98
135 if(iconChange) {
136 ResetIcon(hConsoleWindow, oldBIcon, oldSIcon);
137 }
138
139 }
140
141 // changed from char * to const char * (Paul Brannan 5/12/98)
142 int Telnet::LoadKeyMap(const char * file, const char * name){
143 // printf("Loading %s from %s.\n", name ,file);
144 printm(0, FALSE, MSG_KEYMAP, name, file);
145 return MapLoader.Load(file,name);
146 }
147
148 void Telnet::DisplayKeyMap(){ // display available keymaps
149 MapLoader.Display();
150 };
151
152 int Telnet::SwitchKeyMap(int to) { // switch to selected keymap
153 int ret = KeyTrans.SwitchTo(to);
154 switch(ret) {
155 case -1: printm(0, FALSE, MSG_KEYNOKEYMAPS); break;
156 case 0: printm(0, FALSE, MSG_KEYBADMAP); break;
157 case 1: printm(0, FALSE, MSG_KEYMAPSWITCHED); break;
158 }
159 return ret;
160 };
161
162
163 int Telnet::Open(const char *szHost1, const char *strPort1){
164 if (bWinsockUp && !bConnected){
165 telSetConsoleTitle(szHost1);
166
167 strncpy (szHost,szHost1, 127);
168 strncpy(strPort, strPort1, sizeof(strPort));
169
170 // Determine whether to pipe to an executable or use our own sockets
171 // (Paul Brannan March 18, 1999)
172 const char *netpipe;
173 if(*(netpipe=ini.get_netpipe())) {
174 PROCESS_INFORMATION pi;
175 HANDLE hInWrite, hOutRead, hErrRead;
176 if(!CreateHiddenConsoleProcess(netpipe, &pi, &hInWrite,
177 &hOutRead, &hErrRead)) {
178 printm(0, FALSE, MSG_ERRPIPE);
179 return TNNOCON;
180 }
181 Network.SetPipe(hOutRead, hInWrite);
182 hProcess = pi.hProcess;
183 } else {
184 Socket = Connect();
185 if (Socket == INVALID_SOCKET) {
186 printm(0, FALSE, GetLastError());
187 return TNNOCON;
188 }
189 Network.SetSocket(Socket);
190 SetLocalAddress(Socket);
191 }
192
193 bNetFinish = 0;
194 bConnected = 1;
195 ThreadParams.p.bNetPaused = &bNetPaused;
196 ThreadParams.p.bNetFinish = &bNetFinish;
197 ThreadParams.p.bNetFinished = &bNetFinished;
198 ThreadParams.p.hExit = CreateEvent(0, TRUE, FALSE, "");
199 ThreadParams.p.hPause = CreateEvent(0, FALSE, FALSE, "");
200 ThreadParams.p.hUnPause = CreateEvent(0, FALSE, FALSE, "");
201 DWORD idThread;
202
203 // Disable Ctrl-break (PB 5/14/98);
204 // Fixed (Thomas Briggs 8/17/98)
205 if(ini.get_disable_break() || ini.get_control_break_as_c())
206 SetConsoleCtrlHandler(ControlEventHandler, TRUE);
207
208 hThread = CreateThread(0, 0,
209 telProcessNetwork,
210 (LPVOID)&ThreadParams, 0, &idThread);
211 // This helps the display thread a little (Paul Brannan 8/3/98)
212 SetThreadPriority(hThread, THREAD_PRIORITY_ABOVE_NORMAL);
213 return Resume();
214 } else if(bWinsockUp && bConnected) {
215 printm (0, FALSE, MSG_ALREADYCONNECTED, szHost);
216 }
217
218 return TNNOCON; // cannot do winsock stuff or already connected
219 }
220
221 // There seems to be a bug with MSVC's optimization. This turns them off
222 // for these two functions.
223 // (Paul Brannan 5/14/98)
224 #ifdef _MSC_VER
225 #pragma optimize("", off)
226 #endif
227
228
229 int Telnet::Close() {
230 Console.sync();
231 switch(Network.get_net_type()) {
232 case TN_NETSOCKET:
233 if(Socket != INVALID_SOCKET) closesocket(Socket);
234 Socket = INVALID_SOCKET;
235 break;
236 case TN_NETPIPE:
237 if(hProcess != 0) {
238 TerminateProcess(hProcess, 0);
239 CloseHandle(hProcess);
240 hProcess = 0;
241 }
242 break;
243 }
244
245 // Enable Ctrl-break (PB 5/14/98);
246 // Ioannou : this must be FALSE
247 if(ini.get_disable_break()) SetConsoleCtrlHandler(NULL, FALSE);
248
249 if (hThread) CloseHandle(hThread); // Paul Brannan 8/11/98
250 hThread = NULL; // Daniel Straub 11/12/98
251
252 SetEvent(ThreadParams.p.hUnPause);
253 bNetFinish = 1;
254 while (!bNetFinished)
255 Sleep (0); // give up our time slice- this lets our connection thread
256 // finish itself, so we don't hang -crn@ozemail.com.au
257 telSetConsoleTitle("No Connection");
258 bConnected = 0;
259 return 1;
260 }
261
262 int Telnet::Resume(){
263 int i;
264 if (bConnected) {
265 Console.sync();
266 for(;;){
267 SetEvent(ThreadParams.p.hUnPause);
268 i = telProcessConsole(&ThreadParams.p, KeyTrans, Console,
269 Network, Mouse, Clipboard, hThread);
270 if (i) bConnected = 1;
271 else bConnected = 0;
272 ResetEvent(ThreadParams.p.hUnPause);
273 SetEvent(ThreadParams.p.hPause);
274 while (!bNetPaused)
275 Sleep (0); // give up our time slice- this lets our connection thread
276 // unpause itself, so we don't hang -crn@ozemail.com.au
277 switch (i){
278 case TNNOCON:
279 Close();
280 return TNDONE;
281 case TNPROMPT:
282 return TNPROMPT;
283 case TNSCROLLBACK:
284 Scroller.ScrollBack();
285 break;
286 case TNSPAWN:
287 NewProcess();
288 }
289 }
290 }
291 return TNNOCON;
292 }
293
294 // Turn optimization back on (Paul Brannan 5/12/98)
295 #ifdef _MSC_VER
296 #pragma optimize("", on)
297 #endif
298
299 // The scrollback functions have been moved to TScroll.cpp
300 // (Paul Brannan 6/15/98)
301 SOCKET Telnet::Connect()
302 {
303 SOCKET Socket1 = socket(AF_INET, SOCK_STREAM, 0);
304 SOCKADDR_IN SockAddr;
305 SockAddr.sin_family = AF_INET;
306 SockAddr.sin_addr.s_addr = inet_addr(szHost);
307
308 // determine the port correctly -crn@ozemail.com.au 15/12/98
309 SERVENT *sp;
310 sp = getservbyname (strPort, "tcp");
311 if (sp == NULL) {
312 if (isdigit (*(strPort)))
313 SockAddr.sin_port = htons(atoi(strPort));
314 else {
315 printm(0, FALSE, MSG_NOSERVICE, strPort);
316 return INVALID_SOCKET;
317 }
318 } else
319 SockAddr.sin_port = sp->s_port;
320 ///
321
322 // Were we given host name?
323 if (SockAddr.sin_addr.s_addr == INADDR_NONE) {
324
325 // Resolve host name to IP address.
326 printm(0, FALSE, MSG_RESOLVING, szHost);
327 hostent* pHostEnt = gethostbyname(szHost);
328 if (!pHostEnt)
329 return INVALID_SOCKET;
330 printit("\n");
331
332 SockAddr.sin_addr.s_addr = *(DWORD*)pHostEnt->h_addr;
333 }
334
335 // Print a message telling the user the IP we are connecting to
336 // (Paul Brannan 5/14/98)
337 char ss_b1[4], ss_b2[4], ss_b3[4], ss_b4[4], ss_b5[12];
338 itoa(SockAddr.sin_addr.S_un.S_un_b.s_b1, ss_b1, 10);
339 itoa(SockAddr.sin_addr.S_un.S_un_b.s_b2, ss_b2, 10);
340 itoa(SockAddr.sin_addr.S_un.S_un_b.s_b3, ss_b3, 10);
341 itoa(SockAddr.sin_addr.S_un.S_un_b.s_b4, ss_b4, 10);
342 itoa(ntohs(SockAddr.sin_port), ss_b5, 10);
343 printm(0, FALSE, MSG_TRYING, ss_b1, ss_b2, ss_b3, ss_b4, ss_b5);
344
345 if (connect(Socket1, (sockaddr*)&SockAddr, sizeof(SockAddr)))
346 return INVALID_SOCKET;
347
348 char esc[2];
349 esc [0] = ini.get_escape_key();
350 esc [1] = 0;
351 printm(0, FALSE, MSG_CONNECTED, szHost, esc);
352
353 return Socket1;
354 }
355
356 void Telnet::telSetConsoleTitle(const char * szHost1)
357 {
358 char szTitle[128] = "Telnet - ";
359 strcat(szTitle, szHost1);
360 if(ini.get_set_title()) SetConsoleTitle(szTitle);
361 }
362
363 void Telnet::NewProcess() {
364 char cmd_line[MAX_PATH*2];
365 PROCESS_INFORMATION pi;
366
367 strcpy(cmd_line, ini.get_startdir());
368 strcat(cmd_line, ini.get_exename()); // Thomas Briggs 12/7/98
369
370 if(!SpawnProcess(cmd_line, &pi)) printm(0, FALSE, MSG_NOSPAWN);
371 }
372
373 void Telnet::SetLocalAddress(SOCKET s) {
374 SOCKADDR_IN SockAddr;
375 int size = sizeof(SOCKADDR_IN);
376 memset(&SockAddr, 0, sizeof(SockAddr));
377 SockAddr.sin_family = AF_INET;
378
379 getsockname(Network.GetSocket(), (sockaddr*)&SockAddr, &size);
380 char ss_b1[4], ss_b2[4], ss_b3[4], ss_b4[4];
381 itoa(SockAddr.sin_addr.S_un.S_un_b.s_b1, ss_b1, 10);
382 itoa(SockAddr.sin_addr.S_un.S_un_b.s_b2, ss_b2, 10);
383 itoa(SockAddr.sin_addr.S_un.S_un_b.s_b3, ss_b3, 10);
384 itoa(SockAddr.sin_addr.S_un.S_un_b.s_b4, ss_b4, 10);
385
386 char addr[40];
387 strcpy(addr, ss_b1);
388 strcat(addr, ".");
389 strcat(addr, ss_b2);
390 strcat(addr, ".");
391 strcat(addr, ss_b3);
392 strcat(addr, ".");
393 strcat(addr, ss_b4);
394 strcat(addr, ":0.0");
395
396 Network.SetLocalAddress(addr);
397 }
398