sync with trunk r47346
[reactos.git] / base / applications / network / telnet / src / ttelhndl.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: ttelhndl.cpp
29 //
30 // Contents: Telnet Handler
31 //
32 // Product: telnet
33 //
34 // Revisions: August 30, 1998 Paul Brannan <pbranna@clemson.edu>
35 // June 15, 1998 pbranna@clemson.edu (Paul Brannan)
36 //
37 // This is code originally from tnnet.cpp and ansiprsr.cpp
38 //
39 ///////////////////////////////////////////////////////////////////////////////
40
41 #include <string.h>
42 #include "ttelhndl.h"
43 #include "telnet.h"
44 #include "tnconfig.h"
45 #include "tparams.h"
46
47 int naws_string(char *buf, int width, int height);
48
49 // This helps make the code more readable (Paul Brannan 1/1/99)
50 #ifdef DEBUG_TELOPT
51 #define TELOPT_PRINTD(x) printit(x);
52 #define TELOPT_PRINTD2(x,n) { \
53 static char buf[20]; \
54 printit(s); \
55 printit(" "); \
56 itoa(d, buf, 10); \
57 printit(buf); \
58 printit("\n"); \
59 }
60 #else
61 #define TELOPT_PRINTD(x) ;
62 #define TELOPT_PRINTD2(x,n) ;
63 #endif
64
65 // A new print function for debugging (Paul Brannan 5/15/98)
66 #ifdef DEBUG_TELOPT
67 void TTelnetHandler::print_telopt(const char *s, int d) {
68 static char buf[20];
69 printit(s);
70 printit(" ");
71 itoa(d, buf, 10);
72 printit(buf);
73 printit("\n");
74 }
75 #endif
76
77 TTelnetHandler::TTelnetHandler(TNetwork &RefNetwork, TConsole &RefConsole,
78 TParser &RefParser):
79 Network(RefNetwork), Console(RefConsole), Parser(RefParser) {
80 init();
81
82 // Paul Brannan 9/13/98
83 dwBuffer = ini.get_buffer_size();
84 szBuffer = new char [dwBuffer];
85 Network.SetNawsFunc(NULL);
86 }
87
88 void TTelnetHandler::init() {
89 iTermSet = 0;
90 bInBinaryRx = 0;
91 bInBinaryTx = 0;
92 bInEchoTx = 0;
93 bInEchoRx = 0;
94 Network.set_local_echo(1);
95 }
96
97 TTelnetHandler::~TTelnetHandler() {
98 delete[] szBuffer;
99 }
100
101 int TTelnetHandler::escapeIAC(char *buf, int length){
102 // The size of buffer must be greater than 2 * length to ensure no memory
103 // out of bounds errors. The 0xff is escaped into 0xff 0xff.
104 char * temp;
105 temp = new char [length * 2];
106 int current=0;
107 for (int x=0; x < length; x++){
108 if (buf[x] == (signed char)IAC)
109 temp[current++]=(char)IAC;
110 temp[current++]=buf[x];
111 }
112 memcpy( buf, temp, current);
113 delete [] temp;
114 return current;
115 }
116
117 // This lets us get rid of all the printf's (Paul Brannan 5/15/98)
118 void TTelnetHandler::SendIAC(char c) {
119 static char buf[2] = {IAC};
120 buf[1] = c;
121 Network.WriteString(buf, 2);
122 }
123 void TTelnetHandler::SendIAC(char c1, char c2) {
124 static char buf[3] = {IAC};
125 buf[1] = c1; buf[2] = c2;
126 Network.WriteString(buf, 3);
127 }
128 void TTelnetHandler::SendIACParams(char c) {
129 static char buf[2];
130 buf[0] = c;
131 static int length = escapeIAC(buf, 1);
132 Network.WriteString(buf, length);
133 }
134 void TTelnetHandler::SendIACParams(char c1, char c2) {
135 static char buf[4];
136 buf[0] = c1; buf[1] = c2;
137 static int length = escapeIAC(buf, 2);
138 Network.WriteString(buf, length);
139 }
140
141 int naws_string(char *b, int width, int height) {
142 int l = 0;
143 unsigned char *buf = (unsigned char *)b;
144
145 union {
146 char szResponse[2];
147 int n;
148 };
149
150 buf[l++] = IAC;
151 buf[l++] = SB;
152 buf[l++] = TELOPT_NAWS;
153
154 n = width;
155 buf[l] = szResponse[1];
156 if(buf[l-1] == IAC) buf[l++] = IAC;
157 buf[l++] = szResponse[0];
158 if(buf[l-1] == IAC) buf[l++] = IAC;
159
160 n = height;
161 buf[l++] = szResponse[1];
162 if(buf[l-1] == IAC) buf[l++] = IAC;
163 buf[l++] = szResponse[0];
164 if(buf[l-1] == IAC) buf[l++] = IAC;
165
166 buf[l++] = IAC;
167 buf[l++] = SE;
168
169 return l;
170 }
171
172 // Ioannou 29 May 1998 : Something strange happens with
173 // Borland compiler at this point when it passes the arguments
174 // to SendIACParams. It always sends 80 lines to the server !!!
175 // There seems to be a bug with optimization (the disassemble shows
176 // that it uses an address plus 0xa than the right one).
177 // This turns them off for this point.
178 #ifdef __BORLANDC__
179 #pragma -O-
180 #endif
181
182 // Removed old printf code that was commented out to clean this function
183 // up a bit (Paul brannan 6/15/98)
184 char* TTelnetHandler::ParseIAC(char* pszBuffer, char* pszBufferEnd)
185 {
186 // int n,l;
187 // char szResponse[40];
188 // Ioannou 29 May 1998 : I prefer the union redefinitions
189 // than the typecasting (used with them from Pascal and Cobol :-) )
190 // FIX ME !!!! Shall we use the winsock routines instead ?
191
192 union {
193 char szResponse[2];
194 int n;
195 };
196
197 // Added support for user-defined term name (Paul Brannan 5/13/98)
198 #define LASTTERM 4
199 const char *pszTerms[] = {ini.get_term(), "ANSI","DEC-VT100","DEC-VT52","UNKNOWN"};
200 if(!iTermSet && (pszTerms[0] == 0 || *pszTerms[0] == 0)) iTermSet++;
201
202 if (pszBuffer + 2 < pszBufferEnd) {
203 switch ((unsigned char)pszBuffer[1]) {
204
205 ///////////////// DO ////////////////////
206 case DO:
207 {
208 switch (pszBuffer[2]){
209 case TELOPT_BINARY:
210 TELOPT_PRINTD("RCVD DO TELOPT_BINARY\n");
211 if (!bInBinaryRx){
212 SendIAC(WILL, TELOPT_BINARY);
213 bInBinaryRx = 1;
214 TELOPT_PRINTD("SENT WILL TELOPT_BINARY\n");
215 }
216 break;
217 case TELOPT_ECHO:
218 // we shouldn't echo for the server! (Paul Brannan 5/30/98)
219 TELOPT_PRINTD2("RCVD DO TELOPT_ECHO", pszBuffer[2]);
220 SendIAC(WONT, TELOPT_ECHO);
221 TELOPT_PRINTD("SENT WONT TELOPT_ECHO\n");
222 break;
223 case TELOPT_TTYPE:
224 TELOPT_PRINTD("RCVD DO TELOPT_TTYPE\n");
225 SendIAC(WILL, TELOPT_TTYPE);
226 TELOPT_PRINTD("SENT WILL TELOPT_TTYPE\n");
227 break;
228 case TELOPT_NAWS:
229 TELOPT_PRINTD("RCVD DO TELOPT_NAWS\n");
230 SendIAC(WILL, TELOPT_NAWS);
231 SendIAC(SB, TELOPT_NAWS);
232
233 Network.SetNawsFunc(naws_string);
234
235 n = Console.GetWidth();
236 SendIACParams(szResponse[1],szResponse [0]);
237
238 n = Console.GetHeight();
239 SendIACParams(szResponse[1],szResponse[0]);
240
241 SendIAC(SE);
242 TELOPT_PRINTD("SENT WILL TELOPT_NAWS\n");
243 break;
244 case TELOPT_XDISPLOC:
245 TELOPT_PRINTD("RCVD DO TELOPT_XDISPLOC\n");
246 SendIAC(WILL, TELOPT_XDISPLOC);
247 TELOPT_PRINTD("SENT WILL TELOPT_XDISPLOC\n");
248 printit("Retrieving IP...");
249 break;
250 default:
251 TELOPT_PRINTD2("RCVD DO", pszBuffer[2]);
252 SendIAC(WONT, pszBuffer[2]);
253 TELOPT_PRINTD2("SENT WONT", pszBuffer[2]);
254 break;
255 }
256 if (pszBuffer + 2 < pszBufferEnd)
257 pszBuffer += 3;
258 break;
259 }
260
261 ///////////////// WILL ////////////////////
262 case WILL:
263 {
264 switch ((unsigned char)pszBuffer[2]){
265 case TELOPT_BINARY:
266 TELOPT_PRINTD("RCVD WILL TELOPT_BINARY\n");
267 if (!bInBinaryTx){
268 SendIAC(DO, TELOPT_BINARY);
269 bInBinaryTx = 1;
270 TELOPT_PRINTD("SENT DO TELOPT_BINARY\n");
271 }
272 break;
273 case TELOPT_ECHO:
274 TELOPT_PRINTD2("RCVD WILL TELOPT_ECHO", pszBuffer[2]);
275 if(!bInEchoRx) {
276 SendIAC(DO, TELOPT_ECHO);
277 bInEchoRx = 1;
278 Network.set_local_echo(0); // Paul Brannan 8/25/98
279 if(iWillSGA) Network.set_line_mode(0);
280 TELOPT_PRINTD2("SENT DO TELOPT_ECHO", pszBuffer[2]);
281 if(Network.get_local_echo()) Network.set_line_mode(0);
282 }
283 break;
284
285 // Suppress Go Ahead (Paul Brannan 12/31/98)
286 case TELOPT_SGA:
287 TELOPT_PRINTD("RCVD WILL TELOPT_SGA\n");
288 if(!iWillSGA) {
289 SendIAC(DO, TELOPT_SGA);
290 if(bInEchoRx) Network.set_line_mode(0);
291 iWillSGA = 1;
292 TELOPT_PRINTD("SENT DO TELOPT_SGA\n");
293 }
294 break;
295
296 ////added 1/28/97
297 default:
298 TELOPT_PRINTD2("RCVD WILL", pszBuffer[2]);
299 SendIAC(DONT, pszBuffer[2]);
300 TELOPT_PRINTD2("SENT DONT", pszBuffer[2]);
301 break;
302 ////
303 }
304 if (pszBuffer + 2 < pszBufferEnd)
305 pszBuffer += 3;
306 break;
307 }
308
309 ///////////////// WONT ////////////////////
310 case WONT:
311 {
312 switch ((unsigned char)pszBuffer[2]){
313 case TELOPT_ECHO:
314 TELOPT_PRINTD("RCVD WONT TELOPT_ECHO\n");
315 if (bInEchoRx){
316 SendIAC(DONT, TELOPT_ECHO);
317 // bInBinaryRx = 0;
318 bInEchoRx = 0; // Paul Brannan 8/25/98
319 Network.set_local_echo(1);
320 Network.set_line_mode(0);
321 TELOPT_PRINTD("SENT DONT TELOPT_ECHO\n");
322 }
323 break;
324
325 // Suppress Go Ahead (Paul Brannan 12/31/98)
326 case TELOPT_SGA:
327 TELOPT_PRINTD("RCVD WONT TELOPT_SGA\n");
328 if(iWillSGA) {
329 SendIAC(DONT, TELOPT_SGA);
330 Network.set_line_mode(0);
331 iWillSGA = 0;
332 TELOPT_PRINTD("SENT DONT TELOPT_SGA\n");
333 }
334 break;
335
336 default:
337 TELOPT_PRINTD2("RCVD WONT", pszBuffer[2]);
338 break;
339 }
340 if (pszBuffer + 2 < pszBufferEnd)
341 pszBuffer += 3;
342 break;
343 }
344
345 ///////////////// DONT ////////////////////
346 case DONT:
347 {
348 switch ((unsigned char)pszBuffer[2]){
349 case TELOPT_ECHO:
350 TELOPT_PRINTD("RCVD DONT TELOPT_ECHO\n");
351 if (bInEchoTx){
352 SendIAC(WONT, TELOPT_ECHO);
353 bInEchoTx = 0;
354 TELOPT_PRINTD("SENT WONT TELOPT_ECHO\n");
355 }
356 break;
357 case TELOPT_NAWS:
358 TELOPT_PRINTD("RCVD DONT TELOPT_NAWS\n");
359 SendIAC(WONT, TELOPT_NAWS);
360 Network.SetNawsFunc(naws_string);
361 TELOPT_PRINTD("SENT WONT TELOPT_NAWS\n");
362 break;
363 default:
364 TELOPT_PRINTD2("RCVD DONT", pszBuffer[2]);
365 break;
366 }
367 if (pszBuffer + 2 < pszBufferEnd)
368 pszBuffer += 3;
369 break;
370 }
371
372 ///////////////// SB ////////////////////
373 case SB:
374 {
375 switch ((unsigned char)pszBuffer[2]){
376 case TELOPT_TTYPE:
377 if (pszBuffer + 5 < pszBufferEnd) {
378 TELOPT_PRINTD("RCVD SB TELOPT_TTYPE\n");
379 if (pszBuffer[3] == 1){
380 TELOPT_PRINTD("SENT SB TT");
381 TELOPT_PRINTD(pszTerms[iTermSet]);
382 TELOPT_PRINTD("\n");
383 SendIAC(SB, TELOPT_TTYPE);
384 SendIACParams(0);
385 Network.WriteString(pszTerms[iTermSet], strlen(pszTerms[iTermSet]));
386 SendIAC(SE);
387
388 if (iTermSet < LASTTERM )
389 iTermSet+=1;
390 }
391 if (pszBuffer + 5 < pszBufferEnd)
392 pszBuffer += 6;
393 }
394 break;
395 case TELOPT_XDISPLOC:
396 if(pszBuffer + 5 < pszBufferEnd) {
397 TELOPT_PRINTD("RCVD SB XDISPLOC\n");
398 SendIAC(SB, TELOPT_XDISPLOC);
399 TELOPT_PRINTD("SENT SB XDISPLOC");
400 SendIACParams(0);
401 if(Network.GetLocalAddress()) Network.WriteString(Network.GetLocalAddress(),
402 strlen(Network.GetLocalAddress()));
403 TELOPT_PRINTD(Network.GetLocalAddress());
404 TELOPT_PRINTD("\n");
405 SendIAC(SE);
406 if (pszBuffer + 5 < pszBufferEnd)
407 pszBuffer += 6;
408 }
409 break;
410 default: break;
411 }
412 break;
413 }
414 default:
415 pszBuffer += 2;
416 break;
417 }
418 }
419 return pszBuffer;
420 }
421
422 #ifdef __BORLANDC__
423 // bring bug optimazations
424 #pragma -O.
425 #endif
426
427 // This is the code from TANSIParser::ParseBuffer. It parses out IACs, and
428 // then calls TParser::ParseBuffer to do the terminal emulation.
429 // (Paul Brannan 6/15/98)
430 // Hopefully eliminating the unnecessary copying should speed things up a
431 // little. (Paul Brannan 6/28/98)
432 char* TTelnetHandler::ParseBuffer(char* pszBuffer, char* pszBufferEnd){
433 char *pszResult;
434 char *pszHead = pszBuffer;
435
436 if(Network.get_net_type() == TN_NETSOCKET) {
437 while (pszBuffer < pszBufferEnd) {
438 // if IAC then parse IAC
439 if((unsigned char) *pszBuffer == IAC) {
440
441 // check for escaped IAC
442 if((pszBufferEnd >= pszBuffer + 1) &&
443 (unsigned char)*(pszBuffer + 1) == IAC) {
444 // we move data at the front of the buffer to the end so
445 // that if we only have IACs we won't return pszBuffer
446 // even though we did parse something. Returning
447 // pszBuffer is an error condition.
448 memmove(pszHead + 1, pszHead, pszBuffer - pszHead);
449 pszBuffer+=2;
450 pszHead++;
451 }
452 // parse the IAC
453 else {
454 pszResult = ParseIAC(pszBuffer, pszBufferEnd);
455 if(pszBuffer == pszResult) return pszBuffer;
456 // see above regarding moving from front to end.
457 memmove(pszHead + (pszResult - pszBuffer), pszHead,
458 pszBuffer - pszHead);
459 pszHead += (pszResult - pszBuffer);
460 pszBuffer = pszResult;
461 }
462 }
463 // else copy char over to ANSI buffer
464 else {
465 pszBuffer++;
466 }
467 }
468
469 // Not a socket connection, so don't parse out IACs.
470 // (Paul Brannan 3/19/99)
471 } else {
472 pszBuffer = pszBufferEnd;
473 }
474
475 return(Parser.ParseBuffer(pszHead, pszBuffer));
476 }
477
478 // telProcessNetwork calls the member function TTelnetHandler::Go, since
479 // TTelnetHandler::Go is not a static function, and cannot be called with
480 // CreateThread(). (Paul Brannan 6/15/98)
481 DWORD WINAPI telProcessNetwork(LPVOID lpParameter) {
482 TelThreadParams *pParams = (TelThreadParams *)lpParameter;
483 return pParams->TelHandler.Go(&pParams->p);
484 }
485
486 // This function is what used to be telProcessNetwork (Paul Brannan 6/15/98)
487 DWORD TTelnetHandler::Go(LPVOID pvParams)
488 {
489 NetParams *pParams = (NetParams *)pvParams;
490
491 // No longer a need to copy pParams-> socket and create an instance
492 // of TANSIParser (Paul Brannan 6/15/98)
493
494 Console.sync(); // Sync with the parser so the cursor is positioned
495
496 Parser.Init(); // Reset the parser (Paul Brannan 9/19/98)
497 init(); // Turn on local echo (Paul Brannan 9/19/98)
498
499 *pParams->bNetFinished = 0;
500 char* pszHead = szBuffer;
501 char* pszTail = szBuffer;
502 while (!*pParams->bNetFinish) {
503 // Get data from Socket
504 *pParams->bNetPaused = 1; //Pause
505 int Result = Network.ReadString(pszTail, (szBuffer + dwBuffer) - pszTail);
506
507 // Speed up mouse by not going into loop (Paul Brannan 8/10/98)
508 // while(*pParams->bNetPause && !*pParams->bNetFinish) *pParams->bNetPaused = 1; //Pause
509 if(WaitForSingleObject(pParams->hPause, 0) == WAIT_OBJECT_0)
510 WaitForSingleObject(pParams->hUnPause, INFINITE);
511
512 *pParams->bNetPaused = 0; //UnPause
513
514 if (Result <= 0 || Result > dwBuffer ){
515 break;
516 }
517 pszTail += Result;
518
519 // Process the buffer
520 char* pszNewHead = pszHead;
521 do {
522 // Speed up mouse by not going into loop (Paul Brannan 8/10/98)
523 if(WaitForSingleObject(pParams->hPause, 0) == WAIT_OBJECT_0) {
524 *pParams->bNetPaused = 1;
525 WaitForSingleObject(pParams->hUnPause, INFINITE);
526 *pParams->bNetPaused = 0;
527 }
528
529 pszHead = pszNewHead;
530 pszNewHead = ParseBuffer(pszHead, pszTail); // Parse buffer
531 } while ((pszNewHead != pszHead) && (pszNewHead < pszTail) && !*pParams->bNetFinish);
532 pszHead = pszNewHead;
533
534 // When we reach the end of the buffer, move contents to the
535 // beginning of the buffer to get free space at the end.
536 if (pszTail == (szBuffer + dwBuffer)) {
537 memmove(szBuffer, pszHead, pszTail - pszHead);
538 pszTail = szBuffer + (pszTail - pszHead);
539 pszHead = szBuffer;
540 }
541 }
542 SetEvent(pParams->hExit);
543
544 printm(0, FALSE, MSG_TERMBYREM);
545 *pParams->bNetPaused = 1; //Pause
546 *pParams->bNetFinished = 1;
547 return 0;
548 }