83147435e8e60da0f65acc5ad6b5b2263e7b5cdc
[senf.git] / senf / Utils / Termlib / Telnet.hh
1 // $Id$
2 //
3 // Copyright (C) 2008
4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 //
6 // The contents of this file are subject to the Fraunhofer FOKUS Public License
7 // Version 1.0 (the "License"); you may not use this file except in compliance
8 // with the License. You may obtain a copy of the License at 
9 // http://senf.berlios.de/license.html
10 //
11 // The Fraunhofer FOKUS Public License Version 1.0 is based on, 
12 // but modifies the Mozilla Public License Version 1.1.
13 // See the full license text for the amendments.
14 //
15 // Software distributed under the License is distributed on an "AS IS" basis, 
16 // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 
17 // for the specific language governing rights and limitations under the License.
18 //
19 // The Original Code is Fraunhofer FOKUS code.
20 //
21 // The Initial Developer of the Original Code is Fraunhofer-Gesellschaft e.V. 
22 // (registered association), Hansastraße 27 c, 80686 Munich, Germany.
23 // All Rights Reserved.
24 //
25 // Contributor(s):
26 //   Stefan Bund <g0dil@berlios.de>
27
28 /** \file
29     \brief Telnet public header */
30
31 #ifndef HH_SENF_Scheduler_Console_Telnet_
32 #define HH_SENF_Scheduler_Console_Telnet_ 1
33
34 // Custom includes
35 #include <vector>
36 #include <map>
37 #include <senf/Scheduler/Scheduler.hh>
38 #include <senf/Scheduler/ClockService.hh>
39
40 //#include "Telnet.mpp"
41 //-/////////////////////////////////////////////////////////////////////////////////////////////////
42
43 namespace senf {
44 namespace term {
45
46     /** \defgroup telnet_group Telnet protocol
47
48         The telnet protocol implementation is based on the relevant RFCs:
49
50         \li <a href="http://tools.ietf.org/html/rfc854">RFC 854</a> The Telnet protocol
51         \li <a href="http://tools.ietf.org/html/rfc854">RFC 855</a> Telnet option specifications
52
53         BaseTelnetProtocol provides the basic protocol implementation with generic telnet option
54         support. Several other classes provide additional support for further telnet options:
55
56         \li telnethandler::TerminalType provides support for the TERMINAL_TYPE option
57         \li telnethandler::NAWS provides support for the NAWS (Negotiation About Window Size) option
58
59         There are some symbolic constants for telnet option in the senf::term::telnetopt namespace.
60      */
61
62     /** \brief Telnet protocol implementation
63
64         This class implements the basic telnet protocol. It provides option parsing and negotiation
65         and implements the necessary character quoting.
66
67         This class is a base class. Several virtual function hooks are provided which will be called
68         by the protocol implementation.
69
70         The telnet protocol implementation is symmetric: At this level, there is no difference
71         between a telnet client and a telnet server.
72
73         This telnet protocol base class integrates with the SENF Scheduler, it relies on the
74         scheduler main loop to be active.
75
76         \section telnethandlers Telnet option handlers
77
78         To support complex telnet option which require subnegotiation, TelnetHandler's are
79         provided. A telnet handler is a class derived from TelnetHandler and then inherited into the
80         target telnet implementation class:
81
82         \code
83         class MyTelnet
84             : public senf::term::telnethandler::TerminalType,
85               public senf::term::telnethandler::NAWS
86         {
87             MyTelnet(Handle handle) : BaseTelnetProtocol(handle) {}
88
89             // ...
90         };
91         \endcode
92
93         It is very important, the BaseTelnetProtocol is \e always inherited by \c public \c virtual
94         inheritance. This allows the BaseTelnetProtocol constructor to be called by the most derived
95         class directly as above.
96
97         Each terminal handler may provide an additional API and/or additional virtual function
98         callbacks.
99
100         To implement a new terminal handler, derive from TelnetHandler and register the
101         handler. BaseTelnetProtocol will automatically request the corresponding option to be
102         enabled and will call the handlers \c v_init() member as soon as the peer as asserted the
103         option.
104
105         Whenever a subnegotiation for the registered handler is received, the handlers
106         \c v_handleOptionParameters() member is called.
107
108         \code
109         class MyTelnetHandler
110             : public senf::term::BaseTelnetProtocol::TelnetHandler
111         {
112         public:
113             // This constant is MANDATORY and gives the option code which this handler services
114             static option_type const OPTION_CODE = MY_OPTION_CODE;
115
116             void frobble() { // ... }
117
118         protected:
119             MyTelnetHandler() { registerHandler(this); }
120
121         private:
122             virtual void v_init()
123             {
124                 sendOptionParameters(OPTION_CODE, "my special subnegotiation");
125                 incrementRequestCounter();
126             }
127
128             virtual void v_handleOptionParameters(std::string const & data)
129             {
130                 if (data == "another special subnegotiation")
131                     decrementRequestCounter();
132             }
133         };
134         \endcode
135
136         \todo SYNCH handling
137
138         \ingroup telnet_group
139      */
140     class BaseTelnetProtocol
141     {
142     public:
143         static unsigned const DEFAULT_REQUEST_TIMEOUT_MS = 500u;
144
145         typedef ClientSocketHandle<senf::MakeSocketPolicy<
146             ConnectedCommunicationPolicy,
147             StreamFramingPolicy,
148             ReadablePolicy,
149             WriteablePolicy>::policy> Handle; ///< Type of socket handle required
150
151         typedef unsigned char option_type; ///< Type of telnet option numbers
152
153         struct TelnetHandler;
154
155         void write(std::string const & s); ///< Send string to peer
156                                         /**< The string will be correctly quoted and newline chars
157                                              will be sent as CR/LF pairs. */
158         void write(char c);             ///< Send single character to peer
159                                         /**< The character will be correctly quoted and newline
160                                              chars will be sent as CR/LF pairs. */
161
162         Handle handle();                ///< Get socket handle
163
164         void sendNOP();                 ///< Send NOP to peer
165         void sendBRK();                 ///< Send BReaK to peer
166         void sendIP();                  ///< Send InterruptProcess to peer
167         void sendAO();                  ///< Send AbortOutput to peer
168         void sendAYT();                 ///< Send AreYouThere to peer
169         void sendEC();                  ///< Send EraseCharacter to peer
170         void sendEL();                  ///< Send EraseLine to peer
171         void sendGA();                  ///< Send GoAhead to peer
172
173         void sendOptionParameters(option_type option, std::string const & data);
174                                         ///< Send extended option parameter to peer
175                                         /**< This will send \a data as extended option parameter of
176                                              option \a option to peer via a subnegotiation. */
177
178         void requestLocalOption(option_type option, bool enabled = true);
179                                         ///< Request option to be enabled here
180                                         /**< This will send a WILL \a option request to the peer */
181         void acceptLocalOption(option_type option, bool enabled = true);
182                                         ///< Accept a request for an option to be enabled here
183                                         /**< If the peer sends a DO \a option request, the request
184                                              will be granted */
185
186         void requestPeerOption(option_type option, bool enabled = true);
187                                         ///< Request peer to enable an option
188                                         /**< This will send a DO \a option request to the peer */
189         void acceptPeerOption(option_type option, bool enabled = true);
190                                         ///< Accept a request by the peer to enable an option
191                                         /**< If the peer sends a WILL \a option request, the request
192                                              will be ganted */
193
194         bool localOption(option_type option); ///< \c true, if \a option locally enabled
195         bool peerOption(option_type option); ///< \c true, if \a option enabled in peer
196
197     protected:
198         explicit BaseTelnetProtocol(Handle handle); ///< Construct telnet protocol handler
199         BaseTelnetProtocol();           ///< Provided for TelnetHandler mixins only
200
201         virtual ~BaseTelnetProtocol();
202
203         template <class Handler>
204         void registerHandler(Handler * h, bool request=true);
205                                         ///< Register a TelnetHandler
206
207         void incrementRequestCounter(); ///< Increment request counter
208                                         /**< This member may be called by a telnet handler to wait
209                                              for additional negotiations. It must be paired with a
210                                              corresponding decrementRequestCounter() call when that
211                                              negotiation is received. */
212         void decrementRequestCounter(); ///< Decrement request counter
213                                         /**< \see inrementRequestCounter() */
214         bool requestsPending();         ///< \c true, if there are pending requests
215
216 #ifndef DOXYGEN
217     private:
218 #endif
219         virtual void v_setupComplete() = 0; ///< Called, when no further requests are pending
220                                         /**< This callback will be called, when no further
221                                              negotiations are to be expected. The default
222                                              negotiation timeout is 500ms. If no reply is received
223                                              in this time, the request is abandoned and
224                                              v_setupComplete() is called.
225
226                                              mixin TelnetHandler's may additionally increment the
227                                              request counter to wait for specific
228                                              subnegotiations. v_setupComplete() will be called, when
229                                              all these negotiations are complete or have timed
230                                              out. */
231         virtual void v_charReceived(char c) = 0; ///< Called whenever a data character is received
232         virtual void v_eof() = 0;       ///< Called on input EOF
233
234         virtual void v_handleNOP();     ///< Called, when the peer sends a NOP
235         virtual void v_handleBRK();     ///< Called, when the peer sends a BReaK
236         virtual void v_handleIP();      ///< Called, when the peer sends an InterruptProcess
237         virtual void v_handleAO();      ///< Called, when the peer sends an AbortOutput
238         virtual void v_handleAYT();     ///< Called, when the peer sends an AreYouThere
239         virtual void v_handleEC();      ///< Called, when the peer sends an EraseCharacter
240         virtual void v_handleEL();      ///< Called, when the peer sends an EraseLine
241         virtual void v_handleGA();      ///< Called, when the peer sends a GoAhead
242
243     private:
244         void handleChar(char c);
245         void handleNormalChar(char c);
246         void handleCommand(char c);
247         void handleOption(char c);
248         void handleCR(char c);
249         void handleSBOption(char c);
250         void handleSBData(char c);
251         void handleSBIAC(char c);
252         void emit(char c);
253         void processCommand();
254         void transmit(char c);
255
256         void sendWILL(char option);
257         void sendWONT(char option);
258         void sendDO(char option);
259         void sendDONT(char option);
260
261         void readHandler(int state);
262         void writeHandler(int state);
263         void timeout();
264
265         enum Command {
266             CMD_NONE = 0,
267             CMD_SE = 240,
268             CMD_NOP = 241,
269             CMD_DM = 242,
270             CMD_BRK = 243,
271             CMD_IP = 244,
272             CMD_AO = 245,
273             CMD_AYT = 246,
274             CMD_EC = 247,
275             CMD_EL = 248,
276             CMD_GA = 249,
277             CMD_SB = 250,
278             CMD_WILL = 251,
279             CMD_WONT = 252,
280             CMD_DO = 253,
281             CMD_DONT = 254,
282             CMD_IAC = 255,
283         };
284
285         struct OptInfo
286         {
287             enum WantState { WANTED, ACCEPTED, DISABLED };
288             enum OptionState { NONE, REQUEST_SENT, ACKNOWLEDGED };
289
290             OptInfo();
291             OptInfo(bool local, option_type option);
292
293             //-////////////////////////////////////////////////////////////
294
295             bool const local;
296             option_type const option;
297
298             WantState wantState;
299             OptionState optionState;
300             bool enabled;
301
302         };
303
304         OptInfo & getOption(bool local, option_type option);
305         void request(OptInfo & info, bool enabled);
306         void response(OptInfo & info, bool enabled);
307
308         typedef std::map<std::pair<bool, option_type>, OptInfo> OptionsMap;
309         OptionsMap options_;
310
311         typedef std::map<option_type, TelnetHandler*> OptionHandlerMap;
312         OptionHandlerMap handlers_;
313
314         Handle handle_;
315
316         typedef std::vector<char> SendQueue;
317         SendQueue sendQueue_;
318
319         enum CharState { NORMAL, IAC_SEEN, EXPECT_OPTION, CR_SEEN,
320                          SB_OPTION, SB_DATA, SB_IAC_SEEN };
321         CharState charState_;
322
323         Command command_;
324         option_type option_;
325         std::string data_;
326
327         scheduler::FdEvent inputEvent_;
328         scheduler::FdEvent outputEvent_;
329
330         unsigned pendingRequests_;
331
332         ClockService::clock_type requestTimeout_;
333         scheduler::TimerEvent timeout_;
334
335         friend class TelnetHandler;
336     };
337
338     /** \brief Telnet handler base class
339
340         \see BaseTelnetProtocol
341      */
342     struct BaseTelnetProtocol::TelnetHandler
343         : public virtual BaseTelnetProtocol
344     {
345         virtual ~TelnetHandler();
346         virtual void v_init() = 0;      ///< Called, after option has been enabled
347         virtual void v_handleOptionParameters(std::string const & data) = 0;
348                                         ///< Called, whenever a subnegotiation is received
349     };
350
351     /** \brief Telnet option codes
352
353         See http://www.iana.org/assignments/telnet-options for a list of options
354
355         \ingroup telnet_group
356      */
357     namespace telnetopt { BaseTelnetProtocol::option_type const ECHO = 1u; }
358     namespace telnetopt { BaseTelnetProtocol::option_type const TRANSMIT_BINARY = 0u; }
359     namespace telnetopt { BaseTelnetProtocol::option_type const SUPPRESS_GO_AHEAD = 3u; }
360     namespace telnetopt { BaseTelnetProtocol::option_type const TERMINAL_TYPE = 24u; }
361     namespace telnetopt { BaseTelnetProtocol::option_type const NAWS = 31u; }
362     namespace telnetopt { BaseTelnetProtocol::option_type const LINEMODE = 34u; }
363
364 /** \brief Telnet handlers
365
366     \ingroup telnet_group */
367 namespace telnethandler {
368
369     /** \brief Implement TERMINAL_TYPE option
370
371         This telnet handler implements the TERMINAL_TYPE option. The handler automatically requests
372         the first terminal type during initialization. Further terminal types may then be requested
373         by calling nextTerminalType().
374
375         The last received terminal type will be returned by the terminalType() member.
376
377         This implementation only provides server support (querying the terminal type).
378
379         \see BaseTelnetProtocol for how to integrate this handler \n
380             <a href="http://tools.ietf.org/html/rfc1091">RFC 1091</a> Telnet Terminal-Type Option
381      */
382     class TerminalType
383         : public BaseTelnetProtocol::TelnetHandler
384     {
385     public:
386         static option_type const OPTION_CODE = telnetopt::TERMINAL_TYPE;
387
388         void nextTerminalType();        ///< Request another terminal type
389         std::string const & terminalType() const; ///< Return current terminal type
390
391     protected:
392         TerminalType();
393
394     private:
395         virtual void v_init();
396         virtual void v_handleOptionParameters(std::string const & data);
397
398         std::string type_;
399     };
400
401     /** \brief Implement NAWS (Negotiation About Window Size) option
402
403         This telnet handler implements the NAWS option. The client terminals window size will be
404         requested during initialization. The current window size may always be accessed using the
405         width() and height() members.
406
407         Whenever the window size is changed, the v_windowSizeChanged() function is called. This
408         function must be implemented in a derived class.
409
410         \see BaseTelnetProtocol for how to integrate this handler \n
411             <a href="http://tools.ietf.org/html/rfc1073">RFC 1073</a> Telnet Window Size Option
412      */
413     class NAWS
414         : public BaseTelnetProtocol::TelnetHandler
415     {
416     public:
417         static option_type const OPTION_CODE = telnetopt::NAWS;
418
419         unsigned width() const;         ///< Get current client window width
420         unsigned height() const;        ///< Get current client window height
421
422     protected:
423         NAWS();
424
425 #   ifndef DOXYGEN
426     private:
427 #   endif
428         virtual void v_windowSizeChanged() = 0; ///< Called, whenever window size changes
429                                         /**< This member is called for every window size change \e
430                                              after initialization. */
431
432     private:
433         virtual void v_init();
434         virtual void v_handleOptionParameters(std::string const & data);
435
436         unsigned width_;
437         unsigned height_;
438     };
439
440 }
441
442 }}
443
444 //-/////////////////////////////////////////////////////////////////////////////////////////////////
445 #include "Telnet.cci"
446
447 //#include "Telnet.ct"
448 #include "Telnet.cti"
449 #endif
450
451 \f
452 // Local Variables:
453 // mode: c++
454 // fill-column: 100
455 // comment-column: 40
456 // c-file-style: "senf"
457 // indent-tabs-mode: nil
458 // ispell-local-dictionary: "american"
459 // compile-command: "scons -u test"
460 // End: