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