/* telnet.hh - implementation of the telnet protocol
 * Copyright 2003 Bas Wijnen <wijnen@debian.org>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef SHEVEK_TELNET_HH
#define SHEVEK_TELNET_HH

#include "socket.hh"

namespace shevek
{
  /// Input and output filters for shevek::socket to make them telnet sockets.
  class telnet : public socket
  {
    // These are ideal for an enum, in a sense, but the values are really char constants, and C++ doesn't allow that for enums. So the choice is to cast the enum value to a char, or to not use an enum. I don't like casts.
    // Command constants
    static char const SE = '\xf0';
    static char const NOP = '\xf1';
    static char const MARK = '\xf2';
    static char const BREAK = '\xf3';
    static char const IP = '\xf4';
    static char const AO = '\xf5';
    static char const AYT = '\xf6';
    static char const EC = '\xf7';
    static char const EL = '\xf8';
    static char const GA = '\xf9';
    static char const SB = '\xfa';
    static char const WILL = '\xfb';
    static char const WONT = '\xfc';
    static char const DO = '\xfd';
    static char const DONT = '\xfe';
    static char const IAC = '\xff';
    // Option constants.
    static char const BINARY = '\x00';
    static char const ECHO = '\x01';
    static char const SUPPRESS_GA = '\x03';
    static char const STATUS = '\x05';
    static char const TIMING_MARK = '\x06';
    static char const EXOPL = '\xff';
    enum option_idx {
      BINARY_IDX = 0,
      ECHO_IDX = 1,
      SUPPRESS_GA_IDX = 2,
      STATUS_IDX = 3,
      TIMING_MARK_IDX = 4,
      EXOPL_IDX = 5
    };      
    struct option_t;
    typedef void (telnet::*action)(option_t *opt);
    struct option_t
    {
      char type;
      action will, wont, doo, dont;
      bool here, there, not_both;
    };
    static option_t options[6];
    option_t nop_option;
    option_t *s_find (char opt);
    void nop (option_t *);
    void nopwill (option_t *opt);
    void nopdo (option_t *opt);
    void l_will (option_t *opt);
    void l_wont (option_t *opt);
    void l_do (option_t *opt);
    void l_dont (option_t *opt);
    void will_check (option_t *opt);
    void do_check (option_t *opt);
    void wont_check (option_t *opt);
    void dont_check (option_t *opt);
    void l_do_sub (std::string const &data, std::string::size_type &pos);
    bool m_ignore;
    void l_in_filter (std::string &data);
    void l_out_filter (std::string &data);
    std::string m_am_here, m_inbuffer;
  protected:
    /// Derived classes have their own create function and may call the constructor.
    telnet (Glib::RefPtr <Glib::MainContext> main);
  public:
    /// Create a new telnet socket.
    static Glib::RefPtr <telnet> create (Glib::RefPtr <Glib::MainContext> main = Glib::MainContext::get_default () );
    /// Return the string that is used in reply to ARE_YOU_THERE requests.
    /** This can also be used to change the reply.
     */
    std::string &you_there ();
  };
}

#endif
