Index: pjproject/branches/projects/cli/pjlib-util/src/pjlib-util/cli_telnet.c
===================================================================
--- pjproject/branches/projects/cli/pjlib-util/src/pjlib-util/cli_telnet.c	(revision 3232)
+++ pjproject/branches/projects/cli/pjlib-util/src/pjlib-util/cli_telnet.c	(revision 4299)
@@ -42,18 +42,202 @@
 #endif
 
-struct cli_telnet_sess
-{
-    pj_cli_sess         base;
-    pj_pool_t          *pool;
-    pj_activesock_t    *asock;
-    pj_bool_t           authorized;
-    pj_ioqueue_op_key_t op_key;
-    pj_mutex_t         *smutex;
-
-    char                rcmd[PJ_CLI_MAX_CMDBUF];
-    int                 len;
-    char                buf[CLI_TELNET_BUF_SIZE + MAX_CUT_MSG_LEN];
-    unsigned            buf_len;
+#define MAX_CLI_TELNET_OPTIONS 256
+
+/**
+ * This specify the state for the telnet option negotiation.
+ */
+enum cli_telnet_option_states 
+{    
+    OPT_DISABLE,		/* Option disable */
+    OPT_ENABLE,			/* Option enable */
+    OPT_EXPECT_DISABLE,		/* Already send disable req, expecting resp */
+    OPT_EXPECT_ENABLE,		/* Already send enable req, expecting resp */
+    OPT_EXPECT_DISABLE_REV,	/* Already send disable req, expecting resp, 
+				 * need to send enable req */
+    OPT_EXPECT_ENABLE_REV	/* Already send enable req, expecting resp,
+				 * need to send disable req */
 };
+
+/**
+ * This structure contains information for telnet session option negotiation.
+ * It contains the local/peer option config and the option negotiation state.
+ */
+typedef struct cli_telnet_sess_option
+{
+    /**
+     * Local setting for the option.
+     * Default: FALSE;
+     */
+    pj_bool_t local_is_enable;
+
+    /**
+     * Remote setting for the option.
+     * Default: FALSE;
+     */
+    pj_bool_t peer_is_enable;
+
+    /**
+     * Local state of the option negotiation.
+     */
+    enum cli_telnet_option_states local_state;
+
+    /**
+     * Remote state of the option negotiation.
+     */
+    enum cli_telnet_option_states peer_state;
+} cli_telnet_sess_option;
+
+/**
+ * This specify the state of input character parsing.
+ */
+typedef enum cmd_parse_state
+{
+    ST_NORMAL,
+    ST_CR,
+    ST_ESC,
+    ST_VT100,
+    ST_IAC,
+    ST_DO,
+    ST_DONT,
+    ST_WILL,
+    ST_WONT
+} cmd_parse_state;
+
+typedef enum cli_telnet_command
+{
+    SUBNEGO_END	    = 240,	/* End of subnegotiation parameters. */
+    NOP		    = 241,	/* No operation. */
+    DATA_MARK	    = 242,	/* Marker for NVT cleaning. */
+    BREAK	    = 243,	/* Indicates that the "break" key was hit. */
+    INT_PROCESS	    = 244,	/* Suspend, interrupt or abort the process. */
+    ABORT_OUTPUT    = 245,	/* Abort output, abort output stream. */
+    ARE_YOU_THERE   = 246,	/* Are you there. */
+    ERASE_CHAR	    = 247,	/* Erase character, erase the current char. */
+    ERASE_LINE	    = 248,	/* Erase line, erase the current line. */
+    GO_AHEAD	    = 249,	/* Go ahead, other end can transmit. */
+    SUBNEGO_BEGIN   = 250,	/* Subnegotiation begin. */
+    WILL	    = 251,	/* Accept the use of option. */
+    WONT	    = 252,	/* Refuse the use of option. */
+    DO		    = 253,	/* Request to use option. */
+    DONT	    = 254,	/* Request to not use option. */
+    IAC		    = 255	/* Interpret as command */
+} cli_telnet_command;
+
+enum cli_telnet_options
+{
+    TRANSMIT_BINARY	= 0,	/* Transmit Binary. */
+    ECHO		= 1,	/* Echo. */
+    RECONNECT		= 2,	/* Reconnection. */
+    SUPPRESS_GA		= 3,	/* Suppress Go Aheah. */
+    MESSAGE_SIZE_NEGO	= 4,	/* Approx Message Size Negotiation. */
+    STATUS		= 5,	/* Status. */
+    TIMING_MARK		= 6,	/* Timing Mark. */
+    RTCE_OPTION		= 7,	/* Remote Controlled Trans and Echo. */
+    OUTPUT_LINE_WIDTH	= 8,	/* Output Line Width. */
+    OUTPUT_PAGE_SIZE	= 9,	/* Output Page Size. */
+    CR_DISPOSITION	= 10,	/* Carriage-Return Disposition. */
+    HORI_TABSTOPS	= 11,	/* Horizontal Tabstops. */
+    HORI_TAB_DISPO	= 12,	/* Horizontal Tab Disposition. */
+    FF_DISP0		= 13,	/* Formfeed Disposition. */
+    VERT_TABSTOPS	= 14,	/* Vertical Tabstops. */
+    VERT_TAB_DISPO	= 15,	/* Vertical Tab Disposition. */
+    LF_DISP0		= 16,	/* Linefeed Disposition. */
+    EXT_ASCII		= 17, 	/* Extended ASCII. */
+    LOGOUT		= 18,	/* Logout. */
+    BYTE_MACRO		= 19,	/* Byte Macro. */
+    DE_TERMINAL		= 20,	/* Data Entry Terminal. */
+    SUPDUP_PROTO	= 21,	/* SUPDUP Protocol. */
+    SUPDUP_OUTPUT	= 22,	/* SUPDUP Output. */
+    SEND_LOC		= 23,	/* Send Location. */
+    TERM_TYPE		= 24,	/* Terminal Type. */
+    EOR			= 25,	/* End of Record. */
+    TACACS_UID		= 26,	/* TACACS User Identification. */
+    OUTPUT_MARKING	= 27,	/* Output Marking. */
+    TTYLOC		= 28,	/* Terminal Location Number. */
+    USE_3270_REGIME	= 29,	/* Telnet 3270 Regime. */
+    USE_X3_PAD		= 30,	/* X.3 PAD. */
+    WINDOW_SIZE		= 31,	/* Window Size. */
+    TERM_SPEED		= 32,	/* Terminal Speed. */
+    REM_FLOW_CONTROL	= 33,	/* Remote Flow Control. */
+    LINE_MODE		= 34,	/* Linemode. */
+    X_DISP_LOC		= 35,	/* X Display Location. */
+    ENVIRONMENT		= 36,	/* Environment. */
+    AUTH		= 37,	/* Authentication. */
+    ENCRYPTION		= 38, 	/* Encryption Option. */
+    NEW_ENVIRONMENT	= 39,	/* New Environment. */
+    TN_3270E		= 40,	/* TN3270E. */
+    XAUTH		= 41,	/* XAUTH. */
+    CHARSET		= 42,	/* CHARSET. */
+    REM_SERIAL_PORT	= 43,	/* Telnet Remote Serial Port. */
+    COM_PORT_CONTROL	= 44,	/* Com Port Control. */
+    SUPP_LOCAL_ECHO	= 45,	/* Telnet Suppress Local Echo. */
+    START_TLS		= 46,	/* Telnet Start TLS. */
+    KERMIT		= 47,	/* KERMIT. */
+    SEND_URL		= 48,	/* SEND-URL. */
+    FWD_X		= 49,	/* FORWARD_X. */
+    EXT_OPTIONS		= 255	/* Extended-Options-List */
+};
+
+enum terminal_cmd 
+{
+    TC_ESC		= 27,
+    TC_UP		= 65,
+    TC_DOWN		= 66,
+    TC_RIGHT		= 67,
+    TC_LEFT		= 68,
+    TC_END		= 70,
+    TC_HOME		= 72,    
+    TC_CTRL_C		= 3,
+    TC_CR		= 13,
+    TC_BS		= 8,
+    TC_TAB		= 9,
+    TC_QM		= 63,
+    TC_BELL		= 7,
+    TC_DEL		= 127
+};
+
+/**
+ * This structure contains the command line shown to the user.  
+ */
+typedef struct telnet_recv_buf {
+    /**
+     * Buffer containing the characters, NULL terminated.
+     */
+    unsigned char	    rbuf[PJ_CLI_MAX_CMDBUF];
+
+    /**
+     * Current length of the command line.
+     */
+    unsigned		    len;
+
+    /**
+     * Current cursor position.
+     */
+    unsigned		    cur_pos;
+} telnet_recv_buf;
+
+typedef struct cmd_history
+{
+    PJ_DECL_LIST_MEMBER(struct cmd_history);
+    pj_str_t command;
+} cmd_history;
+
+typedef struct cli_telnet_sess
+{
+    pj_cli_sess		    base;
+    pj_pool_t		    *pool;
+    pj_activesock_t	    *asock;
+    pj_bool_t		    authorized;
+    pj_ioqueue_op_key_t	    op_key;
+    pj_mutex_t		    *smutex;
+    cmd_parse_state	    parse_state;
+    cli_telnet_sess_option  telnet_option[MAX_CLI_TELNET_OPTIONS];
+    cmd_history		    *history;
+    cmd_history		    *active_history;
+
+    telnet_recv_buf	    *rcmd;
+    unsigned char	    buf[CLI_TELNET_BUF_SIZE + MAX_CUT_MSG_LEN];
+    unsigned		    buf_len;
+} cli_telnet_sess;
 
 struct cli_telnet_fe
@@ -71,4 +255,832 @@
 };
 
+/* Forward Declaration */
+static pj_status_t telnet_sess_send2(cli_telnet_sess *sess,
+                                     const unsigned char *str, int len);
+
+static pj_status_t telnet_sess_send(cli_telnet_sess *sess,
+                                    const pj_str_t *str);
+
+/**
+ * Return the number of characters between the current cursor position 
+ * to the end of line.
+ */
+static unsigned recv_buf_right_len(telnet_recv_buf *recv_buf)
+{
+    return (recv_buf->len - recv_buf->cur_pos);
+}
+
+static pj_bool_t recv_buf_insert(telnet_recv_buf *recv_buf, 
+				 unsigned char *data) 
+{    
+    if (recv_buf->len+1 >= PJ_CLI_MAX_CMDBUF) {
+	return PJ_FALSE;
+    } else {	
+	if (*data == '\t' || *data == '?' || *data == '\r') {
+	    /* Always insert to the end of line */
+	    recv_buf->rbuf[recv_buf->len] = *data;
+	} else {
+	    /* Insert based on the current cursor pos */
+	    unsigned cur_pos = recv_buf->cur_pos;
+	    unsigned rlen = recv_buf_right_len(recv_buf);	    
+	    if (rlen > 0) {		    
+		/* Shift right characters */
+		pj_memmove(&recv_buf->rbuf[cur_pos+1], 
+			   &recv_buf->rbuf[cur_pos], 
+			   rlen+1);
+	    } 
+	    recv_buf->rbuf[cur_pos] = *data;
+	}
+	++recv_buf->cur_pos;
+	++recv_buf->len;
+	recv_buf->rbuf[recv_buf->len] = 0;
+    }
+    return PJ_TRUE;
+}
+
+static pj_bool_t recv_buf_delete(telnet_recv_buf *recv_buf)
+{
+    if ((recv_buf->len == 0) || (recv_buf_right_len(recv_buf) == 0)) {
+	return PJ_FALSE;
+    } else {
+	unsigned rlen;
+	unsigned cur_pos = recv_buf->cur_pos;
+	rlen = recv_buf_right_len(recv_buf)-1;
+	pj_memmove(&recv_buf->rbuf[cur_pos], &recv_buf->rbuf[cur_pos+1], 
+		   rlen);
+	--recv_buf->cur_pos;
+	--recv_buf->len;
+	recv_buf->rbuf[recv_buf->len] = 0;
+    }
+    return PJ_TRUE;
+}
+
+static pj_bool_t recv_buf_backspace(telnet_recv_buf *recv_buf)
+{
+    if ((recv_buf->cur_pos == 0) || (recv_buf->len == 0)) {
+	return PJ_FALSE;
+    } else {
+	unsigned rlen = recv_buf_right_len(recv_buf);
+	if (rlen) {
+	    unsigned cur_pos = recv_buf->cur_pos;
+	    pj_memmove(&recv_buf->rbuf[cur_pos-1], &recv_buf->rbuf[cur_pos], 
+		       rlen);
+	}
+	--recv_buf->cur_pos;
+	--recv_buf->len;
+	recv_buf->rbuf[recv_buf->len] = 0;
+    }    
+    return PJ_TRUE;
+}
+
+static int compare_str(void *value, const pj_list_type *nd)
+{
+    cmd_history *node = (cmd_history*)nd;
+    return (pj_strcmp((pj_str_t *)value, &node->command));
+}
+
+static pj_status_t insert_history(cli_telnet_sess *sess, 				 
+				  char *cmd_val)
+{
+    cmd_history *in_history;
+    pj_str_t cmd;
+    cmd.ptr = cmd_val;
+    cmd.slen = pj_ansi_strlen(cmd_val)-1;
+
+    if (cmd.slen == 0)
+	return PJ_SUCCESS;
+
+    PJ_ASSERT_RETURN(sess, PJ_EINVAL);
+
+    /* Find matching history */
+    in_history = pj_list_search(sess->history, (void*)&cmd, compare_str);
+    if (!in_history) {
+	if (pj_list_size(sess->history) < PJ_CLI_MAX_CMD_HISTORY) {	    
+	    char *data_history;
+	    in_history = PJ_POOL_ZALLOC_T(sess->pool, cmd_history);
+	    pj_list_init(in_history);
+	    data_history = (char *)pj_pool_calloc(sess->pool, 
+			   sizeof(char), PJ_CLI_MAX_CMDBUF);
+	    in_history->command.ptr = data_history;
+	    in_history->command.slen = 0;
+	} else {
+	    /* Get the oldest history */
+	    in_history = sess->history->prev;
+	}	    
+    } else {
+	pj_list_insert_nodes_after(in_history->prev, in_history->next);
+    }
+    pj_strcpy(&in_history->command, pj_strtrim(&cmd));
+    pj_list_push_front(sess->history, in_history);
+    sess->active_history = sess->history;
+
+    return PJ_SUCCESS;
+}
+
+static pj_str_t* get_prev_history(cli_telnet_sess *sess, pj_bool_t is_forward)
+{
+    pj_str_t *retval;
+    pj_size_t history_size;
+    cmd_history *node;
+    cmd_history *root;
+
+    PJ_ASSERT_RETURN(sess, NULL);
+
+    node = sess->active_history;
+    root = sess->history;
+    history_size = pj_list_size(sess->history);
+
+    if (history_size == 0) {
+	return NULL;
+    } else {
+	if (is_forward) {	    
+	    node = (node->next==root)?node->next->next:node->next;	    
+	} else {
+	    node = (node->prev==root)?node->prev->prev:node->prev;	    
+	}
+	retval = &node->command;
+	sess->active_history = node;
+    }
+    return retval;
+}
+
+static pj_bool_t send_telnet_cmd(cli_telnet_sess *sess, 
+				 cli_telnet_command cmd, 
+				 unsigned char option)
+{       
+    unsigned char buf[3];
+    PJ_ASSERT_RETURN(sess, PJ_FALSE);
+
+    buf[0] = IAC;
+    buf[1] = cmd;
+    buf[2] = option;
+    telnet_sess_send2(sess, buf, 3);
+
+    return PJ_TRUE;
+}
+
+/**
+ * This method will handle sending telnet's ENABLE option negotiation.
+ * For local option: send WILL.
+ * For remote option: send DO.
+ * This method also handle the state transition of the ENABLE 
+ * negotiation process.
+ */
+static pj_bool_t send_enable_option(cli_telnet_sess *sess, 
+				    pj_bool_t is_local, 
+				    unsigned char option)
+{
+    cli_telnet_sess_option *sess_option;
+    enum cli_telnet_option_states *state;
+    PJ_ASSERT_RETURN(sess, PJ_FALSE);
+
+    sess_option = &sess->telnet_option[option];
+    state = is_local?(&sess_option->local_state):(&sess_option->peer_state);
+    switch (*state) {
+	case OPT_ENABLE:
+	    /* Ignore if already enabled */
+	    break;
+	case OPT_DISABLE:
+	    *state = OPT_EXPECT_ENABLE;
+	    send_telnet_cmd(sess, (is_local?WILL:DO), option);
+	    break;
+	case OPT_EXPECT_ENABLE:
+	    *state = OPT_DISABLE;
+	    break;
+	case OPT_EXPECT_DISABLE:
+	    *state = OPT_EXPECT_DISABLE_REV;
+	    break;
+	case OPT_EXPECT_ENABLE_REV:
+	    *state = OPT_EXPECT_ENABLE;
+	    break;
+	case OPT_EXPECT_DISABLE_REV:	    
+	    *state = OPT_DISABLE;
+	    break;
+	default:
+	    return PJ_FALSE;
+    }
+    return PJ_TRUE;
+}
+
+/**
+ * This method will handle sending telnet's DISABLE option negotiation.
+ * For local option: send WON'T.
+ * For remote option: send DON'T.
+ * This method also handle the state transition of the DISABLE 
+ * negotiation process. 
+ */
+static pj_bool_t send_disable_option(cli_telnet_sess *sess, 
+				     pj_bool_t is_local, 
+				     unsigned char option)
+{
+    cli_telnet_sess_option *sess_option;
+    enum cli_telnet_option_states *state;
+    PJ_ASSERT_RETURN(sess, PJ_FALSE);
+
+    sess_option = &sess->telnet_option[option];
+    state = is_local?(&sess_option->local_state):(&sess_option->peer_state);
+    switch (*state) {
+	case OPT_ENABLE:
+	    *state = OPT_EXPECT_DISABLE;
+	    send_telnet_cmd(sess, (is_local?WONT:DONT), option);
+	    break;
+	case OPT_DISABLE:
+	    /* Ignore if already disabled */
+	    break;
+	case OPT_EXPECT_ENABLE:
+	    *state = OPT_EXPECT_ENABLE_REV;
+	    break;
+	case OPT_EXPECT_DISABLE:
+	    *state = OPT_ENABLE;
+	    break;
+	case OPT_EXPECT_ENABLE_REV:	    
+	    *state = OPT_ENABLE;
+	    break;
+	case OPT_EXPECT_DISABLE_REV:
+	    *state = OPT_EXPECT_DISABLE;
+	    break;
+	default:
+	    return PJ_FALSE;
+    }
+    return PJ_TRUE;
+}
+
+static pj_bool_t send_cmd_do(cli_telnet_sess *sess, 
+			     unsigned char option)
+{
+    return send_enable_option(sess, PJ_FALSE, option);
+}
+
+static pj_bool_t send_cmd_dont(cli_telnet_sess *sess, 
+			       unsigned char option)
+{
+    return send_disable_option(sess, PJ_FALSE, option);
+}
+
+static pj_bool_t send_cmd_will(cli_telnet_sess *sess, 
+			       unsigned char option)
+{
+    return send_enable_option(sess, PJ_TRUE, option);
+}
+
+static pj_bool_t send_cmd_wont(cli_telnet_sess *sess, 
+			       unsigned char option)
+{
+    return send_disable_option(sess, PJ_TRUE, option);
+}
+
+/**
+ * This method will handle receiving telnet's ENABLE option negotiation.
+ * This method also handle the state transition of the ENABLE 
+ * negotiation process. 
+ */
+static pj_bool_t receive_enable_option(cli_telnet_sess *sess, 
+				       pj_bool_t is_local, 
+				       unsigned char option)
+{
+    cli_telnet_sess_option *sess_opt;
+    enum cli_telnet_option_states *state;
+    pj_bool_t opt_ena;
+    PJ_ASSERT_RETURN(sess, PJ_FALSE);
+
+    sess_opt = &sess->telnet_option[option];
+    state = is_local?(&sess_opt->local_state):(&sess_opt->peer_state);
+    opt_ena = is_local?sess_opt->local_is_enable:sess_opt->peer_is_enable;
+    switch (*state) {
+	case OPT_ENABLE:
+	    /* Ignore if already enabled */
+    	    break;
+	case OPT_DISABLE:
+	    if (opt_ena) {
+		*state = OPT_ENABLE;
+		send_telnet_cmd(sess, is_local?WILL:DO, option);
+	    } else {
+		send_telnet_cmd(sess, is_local?WONT:DONT, option);
+	    }
+	    break;
+	case OPT_EXPECT_ENABLE:	    
+	    *state = OPT_ENABLE;
+	    break;
+	case OPT_EXPECT_DISABLE:	    
+	    *state = OPT_DISABLE;
+	    break;
+	case OPT_EXPECT_ENABLE_REV:
+	    *state = OPT_EXPECT_DISABLE;
+	    send_telnet_cmd(sess, is_local?WONT:DONT, option);	    
+	    break;
+	case OPT_EXPECT_DISABLE_REV:
+	    *state = OPT_EXPECT_DISABLE;
+	    break;
+	default:
+	    return PJ_FALSE;
+    }
+    return PJ_TRUE;        
+}
+
+/**
+ * This method will handle receiving telnet's DISABLE option negotiation.
+ * This method also handle the state transition of the DISABLE 
+ * negotiation process. 
+ */
+static pj_bool_t receive_disable_option(cli_telnet_sess *sess, 
+					pj_bool_t is_local, 
+					unsigned char option)
+{
+    cli_telnet_sess_option *sess_opt;
+    enum cli_telnet_option_states *state;
+    pj_bool_t opt_ena;
+    PJ_ASSERT_RETURN(sess, PJ_FALSE);
+
+    sess_opt = &sess->telnet_option[option];
+    state = is_local?(&sess_opt->local_state):(&sess_opt->peer_state);
+    opt_ena = is_local?sess_opt->local_is_enable:sess_opt->peer_is_enable;
+    switch (*state) {
+	case OPT_ENABLE:
+	    /* Disabling option always need to be accepted */
+	    *state = OPT_DISABLE;
+	    send_telnet_cmd(sess, is_local?WONT:DONT, option);
+    	    break;
+	case OPT_DISABLE:
+	    /* Ignore if already enabled */
+	    break;
+	case OPT_EXPECT_ENABLE:	    
+	case OPT_EXPECT_DISABLE:
+	    *state = OPT_DISABLE;
+	    break;
+	case OPT_EXPECT_ENABLE_REV:
+	    *state = OPT_DISABLE;
+	    send_telnet_cmd(sess, is_local?WONT:DONT, option);	    
+	    break;
+	case OPT_EXPECT_DISABLE_REV:
+	    *state = OPT_EXPECT_ENABLE;
+	    send_telnet_cmd(sess, is_local?WILL:DO, option);
+	    break;
+	default:
+	    return PJ_FALSE;
+    }
+    return PJ_TRUE;       
+}
+
+static pj_bool_t receive_do(cli_telnet_sess *sess, unsigned char option)
+{
+    return receive_enable_option(sess, PJ_TRUE, option);
+}
+
+static pj_bool_t receive_dont(cli_telnet_sess *sess, unsigned char option)
+{
+    return receive_disable_option(sess, PJ_TRUE, option);
+}
+
+static pj_bool_t receive_will(cli_telnet_sess *sess, unsigned char option)
+{
+    return receive_enable_option(sess, PJ_FALSE, option);
+}
+
+static pj_bool_t receive_wont(cli_telnet_sess *sess, unsigned char option)
+{
+    return receive_disable_option(sess, PJ_FALSE, option);
+}
+
+static void set_local_option(cli_telnet_sess *sess, 
+			     unsigned char option, 
+			     pj_bool_t enable) 
+{
+    sess->telnet_option[option].local_is_enable = enable;
+}
+
+static void set_peer_option(cli_telnet_sess *sess, 
+			    unsigned char option, 
+			    pj_bool_t enable) 
+{
+    sess->telnet_option[option].peer_is_enable = enable;
+}
+
+static pj_bool_t is_local_option_state_ena(cli_telnet_sess *sess, 
+					   unsigned char option)
+{    
+    return (sess->telnet_option[option].local_state == OPT_ENABLE);
+}
+
+static pj_bool_t is_peer_option_state_ena(cli_telnet_sess *sess, 
+					  unsigned char option)
+{    
+    return (sess->telnet_option[option].peer_state == OPT_ENABLE);
+}
+
+static void send_return_key(cli_telnet_sess *sess) 
+{    
+    telnet_sess_send2(sess, (unsigned char*)"\r\n", 2);
+}
+
+static void send_bell(cli_telnet_sess *sess) {
+    unsigned char bell[1] = {0x07};
+    telnet_sess_send2(sess, &bell[0], 1);
+}
+
+static void send_prompt_str(cli_telnet_sess *sess)
+{
+    pj_str_t send_data;
+    char data_str[128];
+    struct cli_telnet_fe *fe = (struct cli_telnet_fe *)sess->base.fe;
+
+    send_data.ptr = &data_str[0];
+    send_data.slen = 0;
+    
+    pj_strcat(&send_data, &fe->cfg.prompt_str);
+
+    telnet_sess_send(sess, &send_data);
+}
+
+static void send_err_arg(cli_telnet_sess *sess, 
+			 const pj_cli_exec_info *info, 
+			 const pj_str_t *msg,
+			 pj_bool_t with_return,
+			 pj_bool_t with_last_cmd)
+{
+    pj_str_t send_data;
+    char data_str[256];
+    unsigned len;
+    unsigned i;
+    struct cli_telnet_fe *fe = (struct cli_telnet_fe *)sess->base.fe;
+
+    send_data.ptr = &data_str[0];
+    send_data.slen = 0;
+
+    if (with_return)
+	pj_strcat2(&send_data, "\r\n");
+
+    len = fe->cfg.prompt_str.slen + info->err_pos;
+
+    for (i=0;i<len;++i) {
+	pj_strcat2(&send_data, " ");
+    }
+    pj_strcat2(&send_data, "^");
+    pj_strcat2(&send_data, "\r\n");
+    pj_strcat(&send_data, msg);
+    pj_strcat(&send_data, &fe->cfg.prompt_str);
+    if (with_last_cmd)
+	pj_strcat2(&send_data, (char *)&sess->rcmd->rbuf[0]);
+
+    telnet_sess_send(sess, &send_data);
+}
+
+static void send_inv_arg(cli_telnet_sess *sess, 
+			 const pj_cli_exec_info *info,
+			 pj_bool_t with_return,
+			 pj_bool_t with_last_cmd)
+{
+    static const pj_str_t ERR_MSG = {"%Error : Invalid Arguments\r\n", 28};
+    send_err_arg(sess, info, &ERR_MSG, with_return, with_last_cmd);
+}
+
+static void send_too_many_arg(cli_telnet_sess *sess, 
+			      const pj_cli_exec_info *info,
+			      pj_bool_t with_return,
+			      pj_bool_t with_last_cmd)
+{
+    static const pj_str_t ERR_MSG = {"%Error : Too Many Arguments\r\n", 29};
+    send_err_arg(sess, info, &ERR_MSG, with_return, with_last_cmd);
+}
+
+static void send_ambi_arg(cli_telnet_sess *sess, 
+			  const pj_cli_exec_info *info,
+			  pj_bool_t with_return,
+			  pj_bool_t with_last_cmd)
+{
+    unsigned i;
+    pj_ssize_t j;
+    unsigned len;
+    pj_str_t send_data;
+    char data[1028];
+    struct cli_telnet_fe *fe = (struct cli_telnet_fe *)sess->base.fe;
+    const pj_cli_hint_info *hint = info->hint;
+    pj_bool_t is_process_sc = PJ_FALSE;
+    pj_bool_t valid_type = PJ_FALSE;
+    pj_ssize_t max_length = 0;
+    const pj_str_t sc_type = pj_str("SC");    
+    send_data.ptr = &data[0];
+    send_data.slen = 0;
+    
+    if (with_return)
+	pj_strcat2(&send_data, "\r\n");
+
+    len = fe->cfg.prompt_str.slen + info->err_pos;
+
+    for (i=0;i<len;++i) {
+	pj_strcat2(&send_data, " ");
+    }
+    pj_strcat2(&send_data, "^");
+    /* Get the max length of the command name */
+    for (i=0;i<info->hint_cnt;++i) {
+	if (hint[i].name.slen > max_length) {
+	    max_length = hint[i].name.slen;
+	}
+    }
+
+    for (i=0;i<info->hint_cnt;++i) {	
+	if ((&hint[i].type) && (hint[i].type.slen > 0)) {
+	    valid_type = PJ_TRUE;
+	    if (pj_stricmp(&hint[i].type, &sc_type) == 0) {
+		if (is_process_sc) {
+		    pj_strcat2(&send_data, ", ");
+		} else {
+		    pj_strcat2(&send_data, "\r\n\t Shorcut: ");
+		    is_process_sc = PJ_TRUE;
+		}
+		pj_strcat(&send_data, &hint[i].name);
+	    } else {
+		is_process_sc = PJ_FALSE;
+	    }
+	} else {	    
+	    valid_type = PJ_FALSE;
+	    is_process_sc = PJ_FALSE;
+	}
+
+	if (!is_process_sc) {
+	    pj_strcat2(&send_data, "\r\n\t");
+
+	    if (valid_type) {
+		pj_strcat2(&send_data, "<");
+		pj_strcat(&send_data, &hint[i].type);
+		pj_strcat2(&send_data, ">");	
+	    } else {
+		pj_strcat(&send_data, &hint[i].name);	    
+	    }
+    	
+	    if ((&hint[i].desc) && (hint[i].desc.slen > 0)) {
+		if (!valid_type) {
+		    for (j=0;j<(max_length-hint[i].name.slen);++j) {
+			pj_strcat2(&send_data, " ");
+		    }
+		}
+		pj_strcat2(&send_data, "\t");
+		pj_strcat(&send_data, &hint[i].desc);	    
+	    }
+	}
+    }   
+    pj_strcat2(&send_data, "\r\n");       
+    pj_strcat(&send_data, &fe->cfg.prompt_str);
+    if (with_last_cmd)
+	pj_strcat2(&send_data, (char *)&sess->rcmd->rbuf[0]);
+
+    telnet_sess_send(sess, &send_data);    
+}
+
+static void send_comp_arg(cli_telnet_sess *sess, 
+			  pj_cli_exec_info *info)
+{
+    pj_str_t send_data;
+    char data[128];
+
+    pj_strcat2(&info->hint[0].name, " ");
+
+    send_data.ptr = &data[0];
+    send_data.slen = 0;
+
+    pj_strcat(&send_data, &info->hint[0].name);        
+
+    telnet_sess_send(sess, &send_data);
+}
+
+static pj_bool_t handle_alfa_num(cli_telnet_sess *sess, unsigned char *data)
+{        
+    if (is_local_option_state_ena(sess, ECHO)) {
+	if (recv_buf_right_len(sess->rcmd) > 0) {
+	    /* Insert character */	    
+	    unsigned char echo[5] = {0x1b, 0x5b, 0x31, 0x40, 0x00};
+	    echo[4] = *data;
+	    telnet_sess_send2(sess, &echo[0], 5);
+	} else {
+	    /* Append character */
+	    telnet_sess_send2(sess, data, 1);
+	}
+	return PJ_TRUE;
+    } 
+    return PJ_FALSE;
+}
+
+static pj_bool_t handle_backspace(cli_telnet_sess *sess, unsigned char *data)
+{
+    unsigned rlen = recv_buf_right_len(sess->rcmd);
+    if (recv_buf_backspace(sess->rcmd)) {
+	if (rlen) {
+	    /* Cursor is not at the end of line */
+	    unsigned char echo[5] = {0x00, 0x1b, 0x5b, 0x31, 0x50};
+	    echo[0] = *data;
+	    telnet_sess_send2(sess, &echo[0], 5);
+	} else {
+	    const static unsigned char echo[3] = {0x08, 0x20, 0x08};	    
+	    telnet_sess_send2(sess, &echo[0], 3);
+	}
+	return PJ_TRUE;
+    }
+    return PJ_FALSE;
+}
+
+static pj_bool_t handle_tab(cli_telnet_sess *sess)
+{
+    pj_status_t status;
+    pj_bool_t retval = PJ_TRUE;
+    unsigned len;
+    
+    pj_pool_t *pool;
+    pj_cli_cmd_val *cmd_val;
+    pj_cli_exec_info info;
+    pool = pj_pool_create(sess->pool->factory, "handle_tab",
+			  PJ_CLI_TELNET_POOL_SIZE, PJ_CLI_TELNET_POOL_INC,
+			  NULL);
+
+    cmd_val = PJ_POOL_ZALLOC_T(pool, pj_cli_cmd_val);
+    
+    status = pj_cli_sess_parse(&sess->base, (char *)&sess->rcmd->rbuf, cmd_val, 
+			       pool, &info);    
+
+    len = pj_ansi_strlen((char *)&sess->rcmd->rbuf[0]);
+
+    switch (status) {
+    case PJ_CLI_EINVARG:
+	send_inv_arg(sess, &info, PJ_TRUE, PJ_TRUE);		
+	break;
+    case PJ_CLI_ETOOMANYARGS:
+	send_too_many_arg(sess, &info, PJ_TRUE, PJ_TRUE);
+	break;
+    case PJ_CLI_EMISSINGARG:
+    case PJ_CLI_EAMBIGUOUS:
+	send_ambi_arg(sess, &info, PJ_TRUE, PJ_TRUE);
+	break;
+    case PJ_SUCCESS:
+	if (len > sess->rcmd->cur_pos)
+	{
+	    /* Send the cursor to EOL */
+	    unsigned rlen = len - sess->rcmd->cur_pos+1;
+	    unsigned char *data_sent = &sess->rcmd->rbuf[sess->rcmd->cur_pos-1];
+	    telnet_sess_send2(sess, data_sent, rlen);
+	}
+	if (info.hint_cnt > 0) {	
+	    /* Complete command */
+	    send_comp_arg(sess, &info);
+
+	    pj_memcpy(&sess->rcmd->rbuf[len], 
+		      &info.hint[0].name.ptr[0], info.hint[0].name.slen);
+
+	    len += info.hint[0].name.slen;
+	    sess->rcmd->rbuf[len] = 0;		    
+	} else {
+	    retval = PJ_FALSE;
+	}
+	break;
+    }
+    sess->rcmd->len = len;
+    sess->rcmd->cur_pos = sess->rcmd->len;
+
+    pj_pool_release(pool);	
+    return retval; 
+}
+
+static pj_bool_t handle_return(cli_telnet_sess *sess)
+{
+    pj_status_t status;
+    pj_bool_t retval = PJ_TRUE;
+    
+    pj_pool_t *pool;    
+    pj_cli_exec_info info;    
+    
+    send_return_key(sess);
+    insert_history(sess, (char *)&sess->rcmd->rbuf);
+
+    pool = pj_pool_create(sess->pool->factory, "handle_return",
+			  PJ_CLI_TELNET_POOL_SIZE, PJ_CLI_TELNET_POOL_INC,
+			  NULL);    
+    
+    status = pj_cli_sess_exec(&sess->base, (char *)&sess->rcmd->rbuf, 
+			      pool, &info);    
+
+    switch (status) {
+    case PJ_CLI_EINVARG:
+	send_inv_arg(sess, &info, PJ_FALSE, PJ_FALSE);	
+	break;
+    case PJ_CLI_ETOOMANYARGS:
+	send_too_many_arg(sess, &info, PJ_FALSE, PJ_FALSE);
+	break;
+    case PJ_CLI_EAMBIGUOUS:
+    case PJ_CLI_EMISSINGARG:
+	send_ambi_arg(sess, &info, PJ_FALSE, PJ_FALSE);
+	break;
+    case PJ_CLI_EEXIT:
+	retval = PJ_FALSE;
+	break;
+    case PJ_SUCCESS:
+	send_prompt_str(sess);
+	break;
+    }    
+    if (retval) {
+	sess->rcmd->rbuf[0] = 0;
+	sess->rcmd->len = 0;
+	sess->rcmd->cur_pos = sess->rcmd->len;
+    }
+
+    pj_pool_release(pool);	
+    return retval; 
+}
+
+static pj_bool_t handle_right_key(cli_telnet_sess *sess)
+{
+    if (recv_buf_right_len(sess->rcmd)) {
+	unsigned char *data = &sess->rcmd->rbuf[sess->rcmd->cur_pos++];
+	telnet_sess_send2(sess, data, 1);		
+	return PJ_TRUE;
+    }
+    return PJ_FALSE;
+}
+
+static pj_bool_t handle_left_key(cli_telnet_sess *sess)
+{
+    const static unsigned char BACK_SPACE = 0x08;
+    if (sess->rcmd->cur_pos) {
+	telnet_sess_send2(sess, &BACK_SPACE, 1);
+	--sess->rcmd->cur_pos;
+	return PJ_TRUE;
+    }
+    return PJ_FALSE;
+}
+
+static pj_bool_t handle_up_down(cli_telnet_sess *sess, pj_bool_t is_up)
+{
+    pj_str_t *history;
+
+    PJ_ASSERT_RETURN(sess, PJ_FALSE);
+
+    history = get_prev_history(sess, is_up);
+    if (history) {
+	pj_str_t send_data;
+	char str[PJ_CLI_MAX_CMDBUF];
+	send_data.ptr = &str[0];
+	send_data.slen = 0;
+
+	if (sess->rcmd->cur_pos > 0) {
+	    pj_memset(send_data.ptr, 0x08, sess->rcmd->cur_pos);
+	    send_data.slen = sess->rcmd->cur_pos;
+	}
+
+	if (sess->rcmd->len > (unsigned)history->slen) {
+	    unsigned buf_len = sess->rcmd->len;
+	    pj_memset(&send_data.ptr[send_data.slen], 0x20, buf_len);
+	    send_data.slen += buf_len;
+	    pj_memset(&send_data.ptr[send_data.slen], 0x08, buf_len);
+	    send_data.slen += buf_len;
+	} 
+	/* Send data */
+	pj_strcat(&send_data, history);
+	telnet_sess_send(sess, &send_data);
+	pj_ansi_strncpy((char*)&sess->rcmd->rbuf, history->ptr, history->slen);
+	sess->rcmd->rbuf[history->slen] = 0;
+	sess->rcmd->len = history->slen;
+	sess->rcmd->cur_pos = sess->rcmd->len;
+	return PJ_TRUE;
+    }
+    return PJ_FALSE;
+}
+
+static pj_status_t process_vt100_cmd(cli_telnet_sess *sess, 
+				     unsigned char *cmd)
+{    
+    pj_status_t status = PJ_TRUE;
+    switch (*cmd) {
+	case TC_ESC:
+	    break;
+	case TC_UP:
+	    status = handle_up_down(sess, PJ_TRUE);
+	    break;
+	case TC_DOWN:
+	    status = handle_up_down(sess, PJ_FALSE);
+	    break;
+	case TC_RIGHT:
+	    status = handle_right_key(sess);
+	    break;
+	case TC_LEFT:
+	    status = handle_left_key(sess);
+	    break;
+	case TC_END:
+	    break;
+	case TC_HOME:
+	    break;
+	case TC_CTRL_C:
+	    break;
+	case TC_CR:
+	    break;
+	case TC_BS:
+	    break;
+	case TC_TAB:
+	    break;
+	case TC_QM:
+	    break;
+	case TC_BELL:
+	    break;
+	case TC_DEL:
+	    break;
+    };
+    return status;
+}
+
 PJ_DEF(void) pj_cli_telnet_cfg_default(pj_cli_telnet_cfg *param)
 {
@@ -80,7 +1092,6 @@
 }
 
-
 /* Send a message to a telnet session */
-static pj_status_t telnet_sess_send(struct cli_telnet_sess *sess,
+static pj_status_t telnet_sess_send(cli_telnet_sess *sess,
                                     const pj_str_t *str)
 {
@@ -110,8 +1121,8 @@
             pj_memmove(sess->buf + sess->buf_len, str->ptr, clen);
         if (clen < sz) {
-            pj_ansi_snprintf(sess->buf + CLI_TELNET_BUF_SIZE,
+            pj_ansi_snprintf((char *)sess->buf + CLI_TELNET_BUF_SIZE,
                              MAX_CUT_MSG_LEN, CUT_MSG);
             sess->buf_len = CLI_TELNET_BUF_SIZE +
-                            pj_ansi_strlen(sess->buf + CLI_TELNET_BUF_SIZE);
+                            pj_ansi_strlen((char *)sess->buf+CLI_TELNET_BUF_SIZE);
         } else
             sess->buf_len += clen;
@@ -126,6 +1137,6 @@
 }
 
-static pj_status_t telnet_sess_send2(struct cli_telnet_sess *sess,
-                                     const char *str, int len)
+static pj_status_t telnet_sess_send2(cli_telnet_sess *sess,
+                                     const unsigned char *str, int len)
 {
     pj_str_t s;
@@ -137,5 +1148,5 @@
 static void cli_telnet_sess_destroy(pj_cli_sess *sess)
 {
-    struct cli_telnet_sess *tsess = (struct cli_telnet_sess *)sess;
+    cli_telnet_sess *tsess = (cli_telnet_sess *)sess;
     pj_mutex_t *mutex = ((struct cli_telnet_fe *)sess->fe)->mutex;
 
@@ -161,9 +1172,9 @@
     sess = tfe->sess_head.next;
     while (sess != &tfe->sess_head) {
-        struct cli_telnet_sess *tsess = (struct cli_telnet_sess *)sess;
-
-        sess = sess->next;
-        if (tsess->base.log_level > level && tsess->authorized)
-            telnet_sess_send2(tsess, data, len);
+        cli_telnet_sess *tsess = (cli_telnet_sess *)sess;
+
+        sess = sess->next;        
+	if (tsess->base.log_level > level)
+            telnet_sess_send2(tsess, (unsigned char *)data, len);
     }
     
@@ -214,11 +1225,11 @@
 				          pj_ssize_t sent)
 {
-    struct cli_telnet_sess *sess = (struct cli_telnet_sess *)
-                                    pj_activesock_get_user_data(asock);
+    cli_telnet_sess *sess = (cli_telnet_sess *)
+			    pj_activesock_get_user_data(asock);
 
     PJ_UNUSED_ARG(op_key);
 
     if (sent <= 0) {
-        pj_cli_end_session(&sess->base);
+        pj_cli_sess_end_session(&sess->base);
         return PJ_FALSE;
     }
@@ -232,5 +1243,5 @@
         if (telnet_sess_send2(sess, sess->buf, len) != PJ_SUCCESS) {
             pj_mutex_unlock(sess->smutex);
-            pj_cli_end_session(&sess->base);
+            pj_cli_sess_end_session(&sess->base);
             return PJ_FALSE;
         }
@@ -248,8 +1259,9 @@
 			                  pj_size_t *remainder)
 {
-    struct cli_telnet_sess *sess = (struct cli_telnet_sess *)
-                                    pj_activesock_get_user_data(asock);
+    cli_telnet_sess *sess = (cli_telnet_sess *)
+                            pj_activesock_get_user_data(asock);
     struct cli_telnet_fe *tfe = (struct cli_telnet_fe *)sess->base.fe;
-    char *cdata = (char*)data;
+    unsigned char *cdata = (unsigned char*)data;    
+    pj_status_t is_valid = PJ_TRUE;
 
     PJ_UNUSED_ARG(size);
@@ -257,5 +1269,5 @@
 
     if (status != PJ_SUCCESS && status != PJ_EPENDING) {
-        pj_cli_end_session(&sess->base);
+        pj_cli_sess_end_session(&sess->base);
         return PJ_FALSE;
     }
@@ -266,62 +1278,87 @@
     pj_mutex_lock(sess->smutex);
 
-    if (*cdata == 8) {                          // Backspace
-        if (sess->len > 0)
-            sess->len--;
-        if (telnet_sess_send2(sess, " \b", 2) != PJ_SUCCESS)
-            goto on_exit;
-    } else {
-        if (sess->len < PJ_CLI_MAX_CMDBUF - 1)
-            sess->rcmd[sess->len++] = *cdata;
-        if (*cdata == '\n') {
-            /* Trim trailing newlines */
-            for (; sess->len > 0;) {
-                if (sess->rcmd[sess->len - 1] == '\n' ||
-                    sess->rcmd[sess->len - 1] == '\r')
-                    sess->rcmd[--sess->len] = 0;
-                else
-                    break;
-            }
-            if (sess->len > 0 && sess->rcmd[sess->len - 1] != 0)
-                sess->rcmd[sess->len++] = 0;
-
-            /* If a password is necessary, check whether the supplied password
-             * is correct.
-             */
-            if (!sess->authorized && 
-                ((struct cli_telnet_fe *)sess->base.fe)->cfg.passwd.slen > 0)
-            {
-                if (pj_strcmp2(&tfe->cfg.passwd, sess->rcmd)) {
-                    pj_str_t str = pj_str("Wrong password!\r\n");
-
-                    telnet_sess_send(sess, &str);
-                    goto on_exit;
-                } else {
-                    /* Password is correct, send welcome message */
-                    if (telnet_sess_send(sess, &tfe->cfg.welcome_msg)
-                        != PJ_SUCCESS)
-                    {
-                        goto on_exit;
-                    }
-                    sess->authorized = PJ_TRUE;
-                }
-            } else {
-                pj_status_t status;
-                
-                pj_mutex_unlock(sess->smutex);
-                status = pj_cli_exec(&sess->base, sess->rcmd, NULL);
-                if (status == PJ_CLI_EEXIT)
-                    return PJ_FALSE;
-                pj_mutex_lock(sess->smutex);
-            }
-            sess->len = 0;
-        } else if (!sess->authorized && 
-            ((struct cli_telnet_fe *)sess->base.fe)->cfg.passwd.slen > 0 &&
-            *cdata != '\r')
-        {
-            if (telnet_sess_send2(sess, "\b ", 2) != PJ_SUCCESS)
-                goto on_exit;
-        }
-
+    switch (sess->parse_state) {
+	case ST_CR:
+	    sess->parse_state = ST_NORMAL;
+	    if (*cdata == 0 || *cdata == '\n')				
+		pj_mutex_unlock(sess->smutex);
+		is_valid = handle_return(sess);
+		if (!is_valid)		    
+		    return PJ_FALSE;
+		pj_mutex_lock(sess->smutex);	
+		break;
+	case ST_NORMAL:
+	    if (*cdata == IAC) {
+		sess->parse_state = ST_IAC;
+	    } else if (*cdata == '\b') {
+		is_valid = handle_backspace(sess, cdata);
+	    } else if (*cdata == 27) {		    
+		sess->parse_state = ST_ESC;
+	    } else { 
+		if (recv_buf_insert(sess->rcmd, cdata)) {
+		    if (*cdata == '\r') {
+			sess->parse_state = ST_CR;
+		    } else if ((*cdata == '\t') || (*cdata == '?')) {
+			is_valid = handle_tab(sess);
+		    } else if (*cdata > 31 && *cdata < 127) {
+			is_valid = handle_alfa_num(sess, cdata);
+		    }
+		} else {
+		    is_valid = PJ_FALSE;
+		}
+	    }
+	    break;
+	case ST_ESC:
+	    if (*cdata == 91) {		
+		sess->parse_state = ST_VT100;
+	    } else {
+		sess->parse_state = ST_NORMAL;
+	    }
+	    break;
+	case ST_VT100:
+	    sess->parse_state = ST_NORMAL;
+	    is_valid = process_vt100_cmd(sess, cdata);
+	    break;
+	case ST_IAC:
+	    switch ((unsigned) *cdata) {
+		case DO:
+		    sess->parse_state = ST_DO;
+		    break;
+		case DONT:
+		    sess->parse_state = ST_DONT;
+		    break;
+		case WILL:
+		    sess->parse_state = ST_WILL;
+		    break;
+		case WONT:
+		    sess->parse_state = ST_WONT;
+		    break;
+		default:
+		    sess->parse_state = ST_NORMAL;
+		    break;
+	    }
+	    break;
+	case ST_DO:
+	    receive_do(sess, *cdata);
+	    sess->parse_state = ST_NORMAL;
+	    break;
+	case ST_DONT:
+	    receive_dont(sess, *cdata);
+	    sess->parse_state = ST_NORMAL;
+	    break;
+	case ST_WILL:
+	    receive_will(sess, *cdata);
+	    sess->parse_state = ST_NORMAL;
+	    break;
+	case ST_WONT:
+	    receive_wont(sess, *cdata);
+	    sess->parse_state = ST_NORMAL;
+	    break;
+	default:
+	    sess->parse_state = ST_NORMAL;
+	    break;
+    }
+    if (!is_valid) {
+	send_bell(sess);
     }
 
@@ -329,9 +1366,4 @@
 
     return PJ_TRUE;
-
-on_exit:
-    pj_mutex_unlock(sess->smutex);
-    pj_cli_end_session(&sess->base);
-    return PJ_FALSE;
 }
 
@@ -345,5 +1377,5 @@
     pj_status_t sstatus;
     pj_pool_t *pool;
-    struct cli_telnet_sess *sess;
+    cli_telnet_sess *sess;
     pj_activesock_cb asock_cb;
 
@@ -364,5 +1396,5 @@
     }
 
-    sess = PJ_POOL_ZALLOC_T(pool, struct cli_telnet_sess);
+    sess = PJ_POOL_ZALLOC_T(pool, cli_telnet_sess);
     sess->pool = pool;
     sess->base.fe = &fe->base;
@@ -373,4 +1405,8 @@
     asock_cb.on_data_read = &telnet_sess_on_data_read;
     asock_cb.on_data_sent = &telnet_sess_on_data_sent;
+    sess->rcmd = PJ_POOL_ZALLOC_T(pool, telnet_recv_buf);
+    sess->history = PJ_POOL_ZALLOC_T(pool, struct cmd_history);
+    pj_list_init(sess->history);
+    sess->active_history = sess->history;
 
     sstatus = pj_mutex_create_recursive(pool, "mutex_telnet_sess",
@@ -387,21 +1423,25 @@
     }
 
-    /* Prompt for password if required, otherwise directly send
-     * a welcome message.
-     */
-    if (fe->cfg.passwd.slen) {
-        pj_str_t pwd = pj_str("Password: ");
-        if (telnet_sess_send(sess, &pwd) != PJ_SUCCESS)
-            goto on_exit;
-    } else {
-        if (pj_strlen(&fe->cfg.welcome_msg)) {
-            if (telnet_sess_send(sess, &fe->cfg.welcome_msg) != PJ_SUCCESS)
-                goto on_exit;
-        } else {
-            if (telnet_sess_send2(sess, " \b", 2) != PJ_SUCCESS)
-                goto on_exit;
-        }
-        sess->authorized = PJ_TRUE;
-    }
+    pj_memset(sess->telnet_option, 0, sizeof(sess->telnet_option));
+    set_local_option(sess, TRANSMIT_BINARY, PJ_TRUE);
+    set_local_option(sess, STATUS, PJ_TRUE);
+    set_local_option(sess, SUPPRESS_GA, PJ_TRUE);
+    set_local_option(sess, TIMING_MARK, PJ_TRUE);
+    set_local_option(sess, TERM_SPEED, PJ_TRUE);
+    set_local_option(sess, TERM_TYPE, PJ_TRUE);
+
+    set_peer_option(sess, TRANSMIT_BINARY, PJ_TRUE);
+    set_peer_option(sess, SUPPRESS_GA, PJ_TRUE);
+    set_peer_option(sess, STATUS, PJ_TRUE);
+    set_peer_option(sess, TIMING_MARK, PJ_TRUE);
+    set_peer_option(sess, ECHO, PJ_TRUE);    
+
+    send_cmd_do(sess, SUPPRESS_GA);
+    send_cmd_will(sess, ECHO);
+    send_cmd_will(sess, STATUS);   
+    send_cmd_will(sess, SUPPRESS_GA);
+
+    /* Send prompt string */
+    telnet_sess_send(sess, &fe->cfg.prompt_str);
 
     /* Start reading for input from the new telnet session */
@@ -462,5 +1502,4 @@
     fe->base.type = PJ_CLI_TELNET_FRONT_END;
     fe->base.op->on_write_log = &cli_telnet_fe_write_log;
-//    fe->base.op->on_quit = &cli_telnet_fe_quit;
     fe->base.op->on_destroy = &cli_telnet_fe_destroy;
     fe->pool = pool;
@@ -497,4 +1536,14 @@
         PJ_LOG(3, (THIS_FILE, "CLI telnet daemon listening at port %d",
                fe->cfg.port));
+	
+	if (fe->cfg.prompt_str.slen == 0) {
+	    pj_str_t prompt_sign = {"> ", 2};
+	    char *prompt_data = pj_pool_alloc(fe->pool, 
+					      pj_gethostname()->slen+2);
+	    fe->cfg.prompt_str.ptr = prompt_data;
+
+	    pj_strcpy(&fe->cfg.prompt_str, pj_gethostname());
+	    pj_strcat(&fe->cfg.prompt_str, &prompt_sign);
+	}	
     } else {
         PJ_LOG(3, (THIS_FILE, "Failed binding the socket"));
