aboutsummaryrefslogtreecommitdiff
path: root/src/stable/protocoltelnet.h
blob: 04062be0216de97e38055706c9ef5c30dbfaaee6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
/*
 * Copyright (C) 2007-2023 Xagasoft, All rights reserved.
 *
 * This file is part of the libbu++ library and is released under the
 * terms of the license contained in the file LICENSE.
 */

#ifndef BU_PROTOCOL_TELNET_H
#define BU_PROTOCOL_TELNET_H

#include "bu/protocol.h"
#include "bu/hash.h"
#include "bu/string.h"
#include "bu/mutex.h"

// #define __TELNET_DEBUG

namespace Bu
{
    /**
     * Telnet Protocol handler.  This attempts to provide useful and general
     * support for most of the most commonly used Telnet extensions in a simple
     * and easy to use way.  The Option variables control the settings that can
     * be used on the line, and control which virtual "callbacks" will be called
     * when different events happen.
     *
     * To setup initial values and to disable any options you wish override the
     * onNewConnection function in your own class, like this:
     *@code
     class MyTelnet : public Bu::ProtocolTelnet
     {
     public:
        ...

        virtual void onNewConnection( class Bu::Client *pClient )
        {
            // Call the parent class' onNewConnection to get everything all
            // set up.
            Bu::ProtocolTelnet::onNewConnection( pClient );

            // These functions disable the option to send files via telnet,
            // disabling the remote option means that we won't accept this
            // option (binary data being sent to us) from the client.
            //
            // Disabling the local option means that the client cannot ask us
            // to send them binary data.
            oBinary.enableRemote( false );
            oBinary.enableLocal( false );

            // This requests that the client send us window size updates
            // whenever the size of their window changes, and an initial set to
            // boot.
            //
            // To see if this option is set later, try oNAWS.isRemoteSet(), but
            // wait a little while, asking immediatly will always return false,
            // since the remote side has yet to receive our request.
            oNAWS.remoteSet();
        }
    }
     @endcode
     *
     *@ingroup Serving
     */
    class ProtocolTelnet : public Protocol
    {
    public:
        ProtocolTelnet();
        virtual ~ProtocolTelnet();

        /**
         * If you override this function in a child class, make sure to call
         * this version of it as the very first thing that you do, before you
         * set any options.  See the example in the class docs.
         */
        virtual void onNewConnection( class Bu::Client *pClient );

        /**
         * You should never override this function unless you really, really
         * know what you're doing.  If you want to get data after each line
         * entered (in canonical mode) or after any data arrives (non canonical
         * mode) then override the gotLine and gotData functions, respectively.
         */
        virtual void onNewData( class Bu::Client *pClient );

        /**
         * Override this function to be notified of lines being submitted by
         * the client.  This function is only called in canonical mode, after
         * all edits are performed on the data.  In this mode weather you use
         * the line or not, the data will be cleared from the buffer when this
         * function returns, any changes made to the buffer will be destroyed.
         */
        virtual void gotLine( Bu::String & /*sLine*/ ){};

        /**
         * Override this function to be notified of any new data that comes in
         * from the client.  This function is only called in non-canonical mode,
         * and includes all raw data minus telnet control codes and ansi
         * escape sequences.  In this mode control of the buffer is up to the
         * child class in this function, the buffer will never be cleared unless
         * it happens in this function's override.
         */
        virtual void gotData( Bu::String & /*sData*/ ){};

        /**
         * Using this function to enable or disable canonical mode only affects
         * the way the data is processed and which virtual functions are called
         * during processing.  It does not affect options set locally or
         * remotely.  Setting this to false will enable char-at-a-time mode,
         * effectively disabling internal line-editing code.  Characters
         * such as backspace that are detected will not be handled and will be
         * sent to the user override.  The subclass will also be notified every
         * time new data is available, not just whole lines.
         *
         * When set to true (the default), line editing control codes will be
         * interpreted and used, and the subclass will only be notified when
         * complete lines are available in the buffer.
         */
        void setCanonical( bool bCon=true );
        bool isCanonical();

        void setEcho( bool bOpt=true );
        bool isEchoEnabled();

        void write( const Bu::String &sData );
        void write( const char *pData, int iSize );
        void write( char cData );
        void flush();
        void close();

        const Bu::String &getBuffer() { return sDataBuf; }
        void setBuffer( const Bu::String &sNewBuffer );
        void appendBuffer( const Bu::String &sNewData );

    public:
        /**
         * If you wish to know the current dimensions of the client window,
         * override this function, it will be called whenever the size changes.
         */
        virtual void onSubNAWS( uint16_t /*iWidth*/, uint16_t /*iHeight*/ ){};

        /**
         * This function is called whenever an unknown sub negotiation option is
         * sent over the line.  This doesn't mean that it's malformatted, it
         * just means that this class doesn't support that option yet, but you
         * can handle it yourself if you'd like.  Feel free to change the
         * sSubBuf, it will be cleared as soon as this function returns anyway.
         */
        virtual void onSubUnknown( char /*cSubOpt*/,
            Bu::String & /*sSubBuf*/ ){};

    private:
        /**
         * Represents a basic telnet option, either on or off, no parameters.
         * Each Option can negotiate effectively on it's own, and has two
         * parameters in each of two classes.  Both local and remote can be
         * enabled/disabled and set/unset.  Enabled represents the ability to
         * set the option, disabling an option should also unset it.  Set or
         * unset represent wether the option is being used, if it is allowed.
         */
        class Option
        {
            friend class Bu::ProtocolTelnet;
        private:
            Option( ProtocolTelnet &rPT, char cCode );
            virtual ~Option();

        public:
            void localEnable( bool bSet=true );
            void localSet( bool bSet=true );
            
            bool isLocalEnabled();
            bool isLocalSet();
            
            void remoteEnable( bool bSet=true );
            void remoteSet( bool bSet=true );

            bool isRemoteEnabled();
            bool isRemoteSet();

        private:
            enum
            {
                fLocalCant  = 0x01, /**< Local can't/won't allow option. */
                fLocalIs    = 0x02, /**< Local is using option. */
                fRemoteCant = 0x04, /**< Remote can't/won't allow option. */
                fRemoteIs   = 0x08  /**< Remote is using option. */
            };

            ProtocolTelnet &rPT;
            char fOpts;
            char cCode;
        };
        friend class Bu::ProtocolTelnet::Option;

        Hash<char, Option *> hOpts;

    public:
        Option oBinary;
        Option oEcho;
        Option oNAWS;
        Option oSuppressGA;

    private:
        void onWill( char cCode );
        void onWont( char cCode );
        void onDo( char cCode );
        void onDont( char cCode );
        void onSubOpt();
        void onCtlChar( char cChr );

#ifdef __TELNET_DEBUG
        void printCode( char cCode );
        void printOpt( char cOpt );
#endif

    private:
        Mutex mRead;
        Mutex mWrite;
        Client *pClient;

        Bu::String sDataBuf;    /**< Buffer for regular line data. */
        Bu::String sSubBuf; /**< Buffer for subnegotiation data. */
        char cSubOpt;   /**< Which suboption are we processing. */

        bool bCanonical;    /**< Are we canonicalizing incoming data? */
        bool bEcho;         /**< Should chars be echoed? */
        bool bSubOpt;       /**< Are we processing a suboption right now? */
    };
}

#endif