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