aboutsummaryrefslogtreecommitdiff
path: root/src/stable/protocoltelnet.cpp
diff options
context:
space:
mode:
authorMike Buland <eichlan@xagasoft.com>2012-03-25 20:00:08 +0000
committerMike Buland <eichlan@xagasoft.com>2012-03-25 20:00:08 +0000
commit469bbcf0701e1eb8a6670c23145b0da87357e178 (patch)
treeb5b062a16e46a6c5d3410b4e574cd0cc09057211 /src/stable/protocoltelnet.cpp
parentee1b79396076edc4e30aefb285fada03bb45e80d (diff)
downloadlibbu++-469bbcf0701e1eb8a6670c23145b0da87357e178.tar.gz
libbu++-469bbcf0701e1eb8a6670c23145b0da87357e178.tar.bz2
libbu++-469bbcf0701e1eb8a6670c23145b0da87357e178.tar.xz
libbu++-469bbcf0701e1eb8a6670c23145b0da87357e178.zip
Code is all reorganized. We're about ready to release. I should write up a
little explenation of the arrangement.
Diffstat (limited to 'src/stable/protocoltelnet.cpp')
-rw-r--r--src/stable/protocoltelnet.cpp620
1 files changed, 620 insertions, 0 deletions
diff --git a/src/stable/protocoltelnet.cpp b/src/stable/protocoltelnet.cpp
new file mode 100644
index 0000000..7e37cca
--- /dev/null
+++ b/src/stable/protocoltelnet.cpp
@@ -0,0 +1,620 @@
1/*
2 * Copyright (C) 2007-2011 Xagasoft, All rights reserved.
3 *
4 * This file is part of the libbu++ library and is released under the
5 * terms of the license contained in the file LICENSE.
6 */
7
8#include "bu/protocoltelnet.h"
9#include "bu/client.h"
10
11/* We apparently at least want defs for the lower 13, not sure we care about
12 * the rest of the chars, maybe escape.
13 */
14#define CH_NUL '\x00' /* NUL */
15#define CH_SOH '\x01' /* Start Of Heading */
16#define CH_STX '\x02' /* Start of Text */
17#define CH_ETX '\x03' /* End of Text */
18#define CH_EOT '\x04' /* End of transmission */
19#define CH_ENQ '\x05' /* Enquiery */
20#define CH_ACK '\x06' /* Acknowledge */
21#define CH_BEL '\x07' /* Bell */
22#define CH_BS '\x08' /* Backspace */
23#define CH_TAB '\x09' /* Horizontal Tab */
24#define CH_LF '\x0A' /* NL Line feed, new line */
25#define CH_VT '\x0B' /* Vertical Tab */
26#define CH_FF '\x0C' /* Form feed, new page */
27#define CH_CR '\x0D' /* Carriage return */
28#define CH_ESC '\x1B' /* Escape */
29#define CH_DEL '\x7F' /* Delete */
30
31#define CODE_SE '\xf0' /* End of subnegotiation params. */
32#define CODE_NOP '\xf1' /* No operation (keep-alive). */
33#define CODE_DM '\xf2' /* Datastream side of a Synch. */
34#define CODE_BRK '\xf3' /* Break character. */
35#define CODE_IP '\xf4' /* Interrupt Process character. */
36#define CODE_AO '\xf5' /* Abort Output character. */
37#define CODE_AYT '\xf6' /* Are You There? character. */
38#define CODE_EC '\xf7' /* Erase Character character. */
39#define CODE_EL '\xf8' /* Erase Line character. */
40#define CODE_GA '\xf9' /* Go Ahead signal. */
41#define CODE_SB '\xfa' /* Begin subnegotiation options. */
42#define CODE_WILL '\xfb' /* Desire to do something. */
43#define CODE_WONT '\xfc' /* Refuse to perform. */
44#define CODE_DO '\xfd' /* Request option. */
45#define CODE_DONT '\xfe' /* Demand a stop. */
46
47#define CODE_IAC '\xff' /* Interpret-As-Command. */
48
49#define OPT_BINARY '\x00' /* Binary mode (file transfers?). */
50#define OPT_ECHO '\x01' /* (local) Echo mode. */
51#define OPT_SUPGA '\x03' /* Suppress Go Ahead signals. */
52#define OPT_STATUS '\x05' /* Allow status messages. */
53#define OPT_TIMING '\x06' /* Place a timing mark in the code. */
54#define OPT_EXASCII '\x11' /* Extended ASCII. */
55#define OPT_LOGOUT '\x12' /* Logout. */
56#define OPT_TTYPE '\x18' /* Terminal Type. */
57#define OPT_NAWS '\x1f' /* Negotiate about window size. */
58#define OPT_TSPEED '\x20' /* Terminal Speed. */
59#define OPT_NEWENV '\x27' /* New Environment Option. */
60#define OPT_EXOPL '\xff' /* Can we, will we, handle extended options. */
61
62#ifndef __TELNET_DEBUG
63# define printCode( a ) (void)0
64# define printOpt( a ) (void)0
65#endif
66
67Bu::ProtocolTelnet::ProtocolTelnet() :
68 oBinary( *this, OPT_BINARY ),
69 oEcho( *this, OPT_ECHO ),
70 oNAWS( *this, OPT_NAWS ),
71 oSuppressGA(*this, OPT_SUPGA ),
72 bCanonical( true ),
73 bSubOpt( false )
74{
75}
76
77Bu::ProtocolTelnet::~ProtocolTelnet()
78{
79}
80
81void Bu::ProtocolTelnet::onNewConnection( Bu::Client *pClient )
82{
83 this->pClient = pClient;
84}
85
86void Bu::ProtocolTelnet::onNewData( Bu::Client *pClient )
87{
88 char bc;
89 int iLeft;
90 while( (iLeft = pClient->getInputSize()) )
91 {
92 if( bSubOpt )
93 {
94 pClient->peek( &bc, 1 );
95 if( bc == CODE_IAC )
96 {
97 if( iLeft <= 1 ) return;
98 char bc2;
99 printCode( CODE_IAC );
100 pClient->peek( &bc2, 1, 1 );
101 printCode( bc2 );
102 if( bc2 == CODE_SE )
103 {
104 bSubOpt = false;
105 onSubOpt();
106 }
107 else if( bc2 == CODE_IAC )
108 {
109 sSubBuf += CODE_IAC;
110 }
111 else
112 {
113 // Error of some sort.
114 }
115 pClient->seek( 1 );
116 }
117 else
118 {
119 sSubBuf += bc;
120 }
121 pClient->seek( 1 );
122 }
123 else
124 {
125 pClient->peek( &bc, 1 );
126 if( bc == CODE_IAC )
127 {
128 if( iLeft <= 1 ) return;
129 char bc2;
130 pClient->peek( &bc2, 1, 1 );
131 printCode( bc );
132 printCode( bc2 );
133
134 switch( bc2 )
135 {
136 case CODE_WILL:
137 if( iLeft <= 2 ) return;
138 {
139 char bc3;
140 pClient->peek( &bc3, 1, 2 );
141 pClient->seek( 1 );
142 printOpt( bc3 );
143 onWill( bc3 );
144 }
145 break;
146
147 case CODE_WONT:
148 if( iLeft <= 2 ) return;
149 {
150 char bc3;
151 pClient->peek( &bc3, 1, 2 );
152 pClient->seek( 1 );
153 printOpt( bc3 );
154 onWont( bc3 );
155 }
156 break;
157
158 case CODE_DO:
159 if( iLeft <= 2 ) return;
160 {
161 char bc3;
162 pClient->peek( &bc3, 1, 2 );
163 pClient->seek( 1 );
164 printOpt( bc3 );
165 onDo( bc3 );
166 }
167 break;
168
169 case CODE_DONT:
170 if( iLeft <= 2 ) return;
171 {
172 char bc3;
173 pClient->peek( &bc3, 1, 2 );
174 pClient->seek( 1 );
175 printOpt( bc3 );
176 onDont( bc3 );
177 }
178 break;
179
180 case CODE_SB:
181 if( iLeft <= 2 ) return;
182 {
183 pClient->peek( &cSubOpt, 1, 2 );
184 pClient->seek( 1 );
185 printOpt( cSubOpt );
186 bSubOpt = true;
187 }
188 break;
189
190 case CODE_IAC:
191 sDataBuf += CODE_IAC;
192 printCode( CODE_IAC );
193 break;
194 }
195 pClient->seek( 1 );
196#ifdef __TELNET_DEBUG
197 printf("\n");
198#endif
199 }
200 else if( bc == CODE_SB )
201 {
202 }
203 else
204 {
205 // This is where control code handling goes
206 // Also, possibly, character code conversion, although I'm not
207 // sure that really matters anymore, go ASCII/UTF-8
208 if( bCanonical )
209 {
210 if( bc < 0x20 || bc >= CH_DEL )
211 {
212 if( bc == CH_CR )
213 {
214 if( iLeft <= 1 ) return;
215 char bc2;
216 pClient->peek( &bc2, 1, 1 );
217 if( bc2 == CH_NUL || bc2 == CH_LF )
218 {
219 onCtlChar( bc );
220 gotLine( sDataBuf );
221 sDataBuf.clear();
222 }
223 pClient->seek( 1 );
224 }
225 else
226 {
227 onCtlChar( bc );
228 }
229 }
230 else
231 {
232 sDataBuf += bc;
233 if( oEcho.isLocalSet() )
234 {
235 pClient->write( &bc, 1 );
236#ifdef __TELNET_DEBUG
237 printf("%c", bc );
238 fflush( stdout );
239#endif
240 }
241 }
242 }
243 else
244 {
245 sDataBuf += bc;
246 if( oEcho.isLocalSet() )
247 {
248 pClient->write( &bc, 1 );
249 }
250 }
251 }
252 pClient->seek( 1 );
253 }
254 }
255
256 // It's true, this code will not be executed if we only have half of an
257 // IAC code or multibyte escape sequence or something, but then again, it
258 // shouldn't be called then, and really, shouldn't be, it'll be called soon
259 // enough, when we get the rest of that code.
260 if( !bCanonical )
261 {
262 gotData( sDataBuf );
263 }
264}
265
266void Bu::ProtocolTelnet::setCanonical( bool bCon )
267{
268 bCanonical = bCon;
269}
270
271bool Bu::ProtocolTelnet::isCanonical()
272{
273 return bCanonical;
274}
275
276void Bu::ProtocolTelnet::write( const Bu::String &sData )
277{
278 write( sData.getStr(), sData.getSize() );
279}
280
281void Bu::ProtocolTelnet::write( const char *pData, int iSize )
282{
283 int iLast = 0, j;
284 for( j = iLast; j < iSize; j++ )
285 {
286 if( pData[j] == '\n' )
287 {
288 if( j+1 >= iSize ||
289 (pData[j+1] != '\r' && pData[j+1] != '\0') )
290 {
291 pClient->write( pData+iLast, j-iLast );
292 pClient->write( "\n\r", 2 );
293 iLast = j+1;
294 }
295 else
296 {
297 j++;
298 }
299 }
300 }
301 if( j > iLast )
302 {
303 pClient->write( pData+iLast, iSize-iLast );
304 }
305 //pClient->write( pData, iSize );
306}
307
308void Bu::ProtocolTelnet::write( char cData )
309{
310 write( &cData, 1 );
311}
312
313void Bu::ProtocolTelnet::onWill( char cCode )
314{
315 try
316 {
317 Option *pOpt = hOpts[cCode];
318 if( pOpt->isRemoteEnabled() )
319 {
320 pOpt->fOpts |= Option::fRemoteIs;
321 char buf[3] = { CODE_IAC, CODE_DO, cCode };
322 pClient->write( buf, 3 );
323 }
324 else
325 {
326 char buf[3] = { CODE_IAC, CODE_DONT, cCode };
327 pClient->write( buf, 3 );
328 }
329
330 }
331 catch( Bu::HashException &e )
332 {
333 char buf[3] = { CODE_IAC, CODE_DONT, cCode };
334 pClient->write( buf, 3 );
335 }
336}
337
338void Bu::ProtocolTelnet::onWont( char cCode )
339{
340 try
341 {
342 Option *pOpt = hOpts[cCode];
343
344 pOpt->fOpts &= ~Option::fRemoteIs;
345 char buf[3] = { CODE_IAC, CODE_DONT, cCode };
346 pClient->write( buf, 3 );
347 }
348 catch( Bu::HashException &e )
349 {
350 char buf[3] = { CODE_IAC, CODE_DONT, cCode };
351 pClient->write( buf, 3 );
352 }
353}
354
355void Bu::ProtocolTelnet::onDo( char cCode )
356{
357 try
358 {
359 Option *pOpt = hOpts[cCode];
360 if( pOpt->isLocalEnabled() )
361 {
362 pOpt->fOpts |= Option::fLocalIs;
363 char buf[3] = { CODE_IAC, CODE_WILL, cCode };
364 pClient->write( buf, 3 );
365 }
366 else
367 {
368 char buf[3] = { CODE_IAC, CODE_WONT, cCode };
369 pClient->write( buf, 3 );
370 }
371
372 }
373 catch( Bu::HashException &e )
374 {
375 char buf[3] = { CODE_IAC, CODE_WONT, cCode };
376 pClient->write( buf, 3 );
377 }
378}
379
380void Bu::ProtocolTelnet::onDont( char cCode )
381{
382 try
383 {
384 Option *pOpt = hOpts[cCode];
385
386 pOpt->fOpts &= ~Option::fLocalIs;
387 char buf[3] = { CODE_IAC, CODE_DONT, cCode };
388 pClient->write( buf, 3 );
389 }
390 catch( Bu::HashException &e )
391 {
392 char buf[3] = { CODE_IAC, CODE_DONT, cCode };
393 pClient->write( buf, 3 );
394 }
395}
396
397void Bu::ProtocolTelnet::onSubOpt()
398{
399 switch( cSubOpt )
400 {
401 case OPT_NAWS:
402 {
403 uint16_t iWidth, iHeight;
404 ((char *)&iWidth)[1] = sSubBuf[0];
405 ((char *)&iWidth)[0] = sSubBuf[1];
406 ((char *)&iHeight)[1] = sSubBuf[2];
407 ((char *)&iHeight)[0] = sSubBuf[3];
408 onSubNAWS( iWidth, iHeight );
409 }
410 break;
411
412 default:
413 onSubUnknown( cSubOpt, sSubBuf );
414 break;
415 }
416
417 sSubBuf.clear();
418}
419
420void Bu::ProtocolTelnet::onCtlChar( char cChr )
421{
422#ifdef __TELNET_DEBUG
423 switch( cChr )
424 {
425 case CH_NUL: printf("NUL "); break;
426 case CH_SOH: printf("SOH "); break;
427 case CH_STX: printf("STX "); break;
428 case CH_ETX: printf("ETX "); break;
429 case CH_EOT: printf("EOT "); break;
430 case CH_ENQ: printf("ENQ "); break;
431 case CH_ACK: printf("ACK "); break;
432 case CH_BEL: printf("BEL "); break;
433 case CH_BS: printf("BS "); break;
434 case CH_TAB: printf("TAB "); break;
435 case CH_LF: printf("LF "); break;
436 case CH_VT: printf("VT "); break;
437 case CH_FF: printf("FF "); break;
438 case CH_CR: printf("CR "); break;
439 case CH_ESC: printf("ESC "); break;
440 case CH_DEL: printf("DEL "); break;
441 default: printf("!![%02x] ", cChr ); break;
442 }
443 fflush( stdout );
444#endif
445
446 switch( cChr )
447 {
448 case CH_DEL:
449 {
450 if( sDataBuf.getSize() > 0 )
451 {
452 sDataBuf.resize( sDataBuf.getSize()-1 );
453 char buf[3] = { CH_BS, ' ', CH_BS };
454 pClient->write( buf, 3 );
455 }
456 }
457 break;
458
459 }
460}
461
462#ifdef __TELNET_DEBUG
463void Bu::ProtocolTelnet::printCode( char cCode )
464{
465 switch( cCode )
466 {
467 case CODE_SE: printf("SE "); break;
468 case CODE_NOP: printf("NOP "); break;
469 case CODE_DM: printf("DM "); break;
470 case CODE_BRK: printf("BRK "); break;
471 case CODE_IP: printf("IP "); break;
472 case CODE_AO: printf("AO "); break;
473 case CODE_AYT: printf("AYT "); break;
474 case CODE_EC: printf("EC "); break;
475 case CODE_EL: printf("EL "); break;
476 case CODE_GA: printf("GA "); break;
477 case CODE_SB: printf("SB "); break;
478 case CODE_WILL: printf("WILL "); break;
479 case CODE_WONT: printf("WONT "); break;
480 case CODE_DO: printf("DO "); break;
481 case CODE_DONT: printf("DONT "); break;
482 case CODE_IAC: printf("IAC "); break;
483 default: printf("??%02x ", cCode ); break;
484 }
485 fflush( stdout );
486}
487
488void Bu::ProtocolTelnet::printOpt( char cOpt )
489{
490 switch( cOpt )
491 {
492 case OPT_BINARY: printf("BINARY "); break;
493 case OPT_ECHO: printf("ECHO "); break;
494 case OPT_SUPGA: printf("SUPGA "); break;
495 case OPT_STATUS: printf("STATUS "); break;
496 case OPT_TIMING: printf("TIMING "); break;
497 case OPT_EXASCII: printf("EXASCII "); break;
498 case OPT_LOGOUT: printf("LOGOUT "); break;
499 case OPT_TTYPE: printf("TTYPE "); break;
500 case OPT_NAWS: printf("NAWS "); break;
501 case OPT_TSPEED: printf("TSPEED "); break;
502 case OPT_NEWENV: printf("NEWENV "); break;
503 case OPT_EXOPL: printf("EXOPL "); break;
504 default: printf("??%02x ", cOpt); break;
505 }
506 fflush( stdout );
507}
508#endif
509
510Bu::ProtocolTelnet::Option::Option( Bu::ProtocolTelnet &rPT, char cCode ) :
511 rPT( rPT ),
512 fOpts( 0 ),
513 cCode( cCode )
514{
515 rPT.hOpts.insert( cCode, this );
516}
517
518Bu::ProtocolTelnet::Option::~Option()
519{
520}
521
522void Bu::ProtocolTelnet::Option::localEnable( bool bSet )
523{
524 if( bSet == (bool)(!(fOpts&fLocalCant)) ) return;
525
526 if( bSet )
527 fOpts &= ~fLocalCant;
528 else
529 fOpts |= fLocalCant;
530}
531
532void Bu::ProtocolTelnet::Option::localSet( bool bSet )
533{
534 if( bSet == (bool)(fOpts&fLocalIs) ) return;
535
536 char buf[3] = { CODE_IAC, 0, cCode };
537
538 if( bSet )
539 {
540 buf[1] = CODE_WILL;
541 rPT.pClient->write( buf, 3 );
542#ifdef __TELNET_DEBUG
543 printf("<= ");
544 rPT.printCode( buf[0] );
545 rPT.printCode( buf[1] );
546 rPT.printOpt( buf[2] );
547 printf("\n");
548#endif
549 }
550 else
551 {
552 buf[1] = CODE_WONT;
553 rPT.pClient->write( buf, 3 );
554#ifdef __TELNET_DEBUG
555 printf("<= ");
556 rPT.printCode( buf[0] );
557 rPT.printCode( buf[1] );
558 rPT.printOpt( buf[2] );
559 printf("\n");
560#endif
561 }
562}
563
564bool Bu::ProtocolTelnet::Option::isLocalEnabled()
565{
566 return (bool)(!(fOpts&fLocalCant));
567}
568
569bool Bu::ProtocolTelnet::Option::isLocalSet()
570{
571 return (bool)(fOpts&fLocalIs);
572}
573
574void Bu::ProtocolTelnet::Option::remoteEnable( bool /*bSet*/ )
575{
576 return;
577}
578
579void Bu::ProtocolTelnet::Option::remoteSet( bool bSet )
580{
581 //if( bSet == (bool)(fOpts&fRemoteIs) ) return;
582
583 char buf[3] = { CODE_IAC, 0, cCode };
584
585 if( bSet )
586 {
587 buf[1] = CODE_DO;
588 rPT.pClient->write( buf, 3 );
589#ifdef __TELNET_DEBUG
590 printf("<= ");
591 rPT.printCode( buf[0] );
592 rPT.printCode( buf[1] );
593 rPT.printOpt( buf[2] );
594 printf("\n");
595#endif
596 }
597 else
598 {
599 buf[1] = CODE_DONT;
600 rPT.pClient->write( buf, 3 );
601#ifdef __TELNET_DEBUG
602 printf("<= ");
603 rPT.printCode( buf[0] );
604 rPT.printCode( buf[1] );
605 rPT.printOpt( buf[2] );
606 printf("\n");
607#endif
608 }
609}
610
611bool Bu::ProtocolTelnet::Option::isRemoteEnabled()
612{
613 return (bool)(!(fOpts&fRemoteCant));
614}
615
616bool Bu::ProtocolTelnet::Option::isRemoteSet()
617{
618 return (bool)(fOpts&fRemoteIs);
619}
620