* #36737: switch back to svn repo, remove assert in sip_transaction.c
diff --git a/jni/pjproject-android/.svn/pristine/1a/1a4e4ad663e9f807427254b1a9cb0f56d0d4f003.svn-base b/jni/pjproject-android/.svn/pristine/1a/1a4e4ad663e9f807427254b1a9cb0f56d0d4f003.svn-base
new file mode 100644
index 0000000..f0124ea
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/1a/1a4e4ad663e9f807427254b1a9cb0f56d0d4f003.svn-base
@@ -0,0 +1,1934 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2010 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <pjlib-util/cli_imp.h>
+#include <pjlib-util/cli_telnet.h>
+#include <pj/activesock.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/except.h>
+#include <pjlib-util/errno.h>
+#include <pjlib-util/scanner.h>
+#include <pj/addr_resolv.h>
+#include <pj/compat/socket.h>
+
+#if (defined(PJ_WIN32) && PJ_WIN32!=0) || \
+    (defined(PJ_WIN64) && PJ_WIN64!=0) || \
+    (defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0)
+
+#define EADDRINUSE WSAEADDRINUSE 
+
+#endif
+
+#define CLI_TELNET_BUF_SIZE 256
+
+#define CUT_MSG "<..data truncated..>\r\n"
+#define MAX_CUT_MSG_LEN 25
+
+#if 1
+    /* Enable some tracing */
+    #define THIS_FILE   "cli_telnet.c"
+    #define TRACE_(arg)	PJ_LOG(3,arg)
+#else
+    #define TRACE_(arg)
+#endif
+
+#define MAX_CLI_TELNET_OPTIONS 256
+/** Maximum retry on Telnet Restart **/
+#define MAX_RETRY_ON_TELNET_RESTART 100
+/** Minimum number of millisecond to wait before retrying to re-bind on
+ * telnet restart **/
+#define MIN_WAIT_ON_TELNET_RESTART 20
+/** Maximum number of millisecod to wait before retrying to re-bind on
+ *  telnet restart **/
+#define MAX_WAIT_ON_TELNET_RESTART 1000
+
+/**
+ * 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. */
+    TERM_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 specify the state of output character parsing.
+ */
+typedef enum out_parse_state
+{
+    OP_NORMAL,
+    OP_TYPE,
+    OP_SHORTCUT,
+    OP_CHOICE
+} out_parse_state;
+
+/**
+ * This structure contains the command line shown to the user. 
+ * The telnet also needs to maintain and manage command cursor position. 
+ * Due to that reason, the insert/delete character process from buffer will
+ * consider its current cursor position.
+ */
+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;
+
+/**
+ * This structure contains the command history executed by user. 
+ * Besides storing the command history, it is necessary to be able
+ * to browse it.
+ */
+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;
+
+typedef struct cli_telnet_fe
+{
+    pj_cli_front_end        base;
+    pj_pool_t              *pool;
+    pj_cli_telnet_cfg       cfg;
+    pj_bool_t               own_ioqueue;
+    pj_cli_sess             sess_head;
+
+    pj_activesock_t	   *asock;
+    pj_thread_t            *worker_thread;
+    pj_bool_t               is_quitting;
+    pj_mutex_t             *mutex;    
+} cli_telnet_fe;
+
+/* 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);
+
+static pj_status_t telnet_start(cli_telnet_fe *fe);
+static pj_status_t telnet_restart(cli_telnet_fe *tfe);
+
+/**
+ * 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);
+}
+
+/**
+ * Insert character to the receive buffer. 
+ */
+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;
+}
+
+/**
+ * Delete character on the previous cursor position of the receive buffer.
+ */
+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;
+	    /* Shift left characters */
+	    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));
+}
+
+/**
+ * Insert the command to history. If the entered command is not on the list, 
+ * a new entry will be created. All entered command will be moved to 
+ * the first entry of the history. 
+ */
+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;
+}
+
+/**
+ * Get the next or previous history of the shown/active history. 
+ */
+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;
+}
+
+/*
+ * This method is used to send option negotiation command.
+ * The commands dealing with option negotiation are
+ * three byte sequences, the third byte being the code for the option
+ * referenced - (RFC-854).
+ */
+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;
+}
+
+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_will(cli_telnet_sess *sess, 
+			       unsigned char option)
+{
+    return send_enable_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_ASSERT_RETURN(sess, PJ_FALSE);
+
+    sess_opt = &sess->telnet_option[option];
+    state = is_local?(&sess_opt->local_state):(&sess_opt->peer_state);
+    
+    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 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) {
+    static const unsigned char bell = 0x07;
+    telnet_sess_send2(sess, &bell, 1);
+}
+
+static void send_prompt_str(cli_telnet_sess *sess)
+{
+    pj_str_t send_data;
+    char data_str[128];
+    cli_telnet_fe *fe = (cli_telnet_fe *)sess->base.fe;
+
+    send_data.ptr = data_str;
+    send_data.slen = 0;
+    
+    pj_strcat(&send_data, &fe->cfg.prompt_str);
+
+    telnet_sess_send(sess, &send_data);
+}
+
+/*
+ * This method is used to send error message to client, including
+ * the error position of the source command.
+ */
+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];
+    pj_size_t len;
+    unsigned i;
+    cli_telnet_fe *fe = (cli_telnet_fe *)sess->base.fe;
+
+    send_data.ptr = data_str;
+    send_data.slen = 0;
+
+    if (with_return)
+	pj_strcat2(&send_data, "\r\n");
+
+    len = fe->cfg.prompt_str.slen + info->err_pos;
+
+    /* Set the error pointer mark */
+    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);
+
+    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_hint_arg(cli_telnet_sess *sess, 
+			  pj_str_t *send_data, 
+			  const pj_str_t *desc,
+			  pj_ssize_t cmd_len,
+			  pj_ssize_t max_len)
+{
+    if ((desc) && (desc->slen > 0)) {
+	int j;
+
+	for (j=0;j<(max_len-cmd_len);++j) {
+	    pj_strcat2(send_data, " ");
+	}
+	pj_strcat2(send_data, "  ");
+	pj_strcat(send_data, desc);
+	telnet_sess_send(sess, send_data);
+	send_data->slen = 0;
+    }
+}
+
+/*
+ * This method is used to notify to the client that the entered command
+ * is ambiguous. It will show the matching command as the hint information.
+ */
+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_size_t len;
+    pj_str_t send_data;
+    char data[1028];
+    cli_telnet_fe *fe = (cli_telnet_fe *)sess->base.fe;
+    const pj_cli_hint_info *hint = info->hint;
+    out_parse_state parse_state = OP_NORMAL;
+    pj_ssize_t max_length = 0;
+    pj_ssize_t cmd_length = 0;
+    const pj_str_t *cmd_desc = 0;
+    static const pj_str_t sc_type = {"sc", 2};
+    static const pj_str_t choice_type = {"choice", 6};
+    send_data.ptr = data;
+    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].type) && (hint[i].type.slen > 0)) {	    
+	    if (pj_stricmp(&hint[i].type, &sc_type) == 0) {		
+		if ((i > 0) && (!pj_stricmp(&hint[i-1].desc, &hint[i].desc))) {
+		    cmd_length += (hint[i].name.slen + 3);
+		} else {
+		    cmd_length = hint[i].name.slen;
+		}		
+	    } else {
+		cmd_length = hint[i].name.slen;
+	    }
+	} else {	    	    
+	    cmd_length = hint[i].name.slen;
+	}
+
+	if (cmd_length > max_length) {
+	    max_length = cmd_length;
+	}
+    }
+    
+    cmd_length = 0;
+    /* Build hint information */
+    for (i=0;i<info->hint_cnt;++i) {			
+	if ((&hint[i].type) && (hint[i].type.slen > 0)) {	    
+	    if (pj_stricmp(&hint[i].type, &sc_type) == 0) {
+		parse_state = OP_SHORTCUT;
+	    } else if (pj_stricmp(&hint[i].type, &choice_type) == 0) {
+		parse_state = OP_CHOICE;
+	    } else {
+		parse_state = OP_TYPE;
+	    }
+	} else {	    	    
+	    parse_state = OP_NORMAL;
+	}
+	
+	if (parse_state != OP_SHORTCUT) {
+	    pj_strcat2(&send_data, "\r\n  ");
+	    cmd_length = hint[i].name.slen;
+	}	    
+	
+	switch (parse_state) {
+	case OP_CHOICE:
+	    /* Format : "[Choice Value]  description" */	    
+	    pj_strcat2(&send_data, "[");
+	    pj_strcat(&send_data, &hint[i].name);
+	    pj_strcat2(&send_data, "]");	
+	    break;
+	case OP_TYPE:
+	    /* Format : "<Argument Type>  description" */
+	    pj_strcat2(&send_data, "<");
+	    pj_strcat(&send_data, &hint[i].name);
+	    pj_strcat2(&send_data, ">");	
+	    break;
+	case OP_SHORTCUT:
+	    /* Format : "Command | sc |  description" */
+	    {		
+		cmd_length += hint[i].name.slen;
+		if ((i > 0) && (!pj_stricmp(&hint[i-1].desc, &hint[i].desc))) {
+		    pj_strcat2(&send_data, " | ");
+		    cmd_length += 3;		    
+		} else {
+		    pj_strcat2(&send_data, "\r\n  ");
+		}
+		pj_strcat(&send_data, &hint[i].name);	
+	    }
+	    break;
+	default:
+	    /* Command */
+	    pj_strcat(&send_data, &hint[i].name);
+	    cmd_desc = &hint[i].desc;	    
+	    break;
+	}
+    	
+	if ((parse_state == OP_TYPE) || (parse_state == OP_CHOICE) || 
+	    ((i+1) >= info->hint_cnt) ||
+	    (pj_strncmp(&hint[i].desc, &hint[i+1].desc, hint[i].desc.slen))) 
+	{
+	    /* Add description info */
+	    send_hint_arg(sess, &send_data, 
+			  &hint[i].desc, cmd_length, 
+			  max_length);
+
+	    cmd_length = 0;
+	}	
+    }  
+    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);
+
+    telnet_sess_send(sess, &send_data);    
+}
+
+/*
+ * This method is to send command completion of the entered command.
+ */
+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;
+    send_data.slen = 0;
+
+    pj_strcat(&send_data, &info->hint[0].name);        
+
+    telnet_sess_send(sess, &send_data);
+}
+
+/*
+ * This method is to process the alfa numeric character sent by client.
+ */
+static pj_bool_t handle_alfa_num(cli_telnet_sess *sess, unsigned char *data)
+{        
+    if (is_local_option_state_ena(sess, TERM_ECHO)) {
+	if (recv_buf_right_len(sess->rcmd) > 0) {
+	    /* Cursor is not at EOL, insert character */	    
+	    unsigned char echo[5] = {0x1b, 0x5b, 0x31, 0x40, 0x00};
+	    echo[4] = *data;
+	    telnet_sess_send2(sess, echo, 5);
+	} else {
+	    /* Append character */
+	    telnet_sess_send2(sess, data, 1);
+	}
+	return PJ_TRUE;
+    } 
+    return PJ_FALSE;
+}
+
+/*
+ * This method is to process the backspace character sent by client.
+ */
+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, move the characters 
+	     * after the cursor to left
+	     */
+	    unsigned char echo[5] = {0x00, 0x1b, 0x5b, 0x31, 0x50};
+	    echo[0] = *data;
+	    telnet_sess_send2(sess, echo, 5);
+	} else {
+	    const static unsigned char echo[3] = {0x08, 0x20, 0x08};	    
+	    telnet_sess_send2(sess, echo, 3);
+	}
+	return PJ_TRUE;
+    }
+    return PJ_FALSE;
+}
+
+/* 
+ * Syntax error handler for parser. 
+ */
+static void on_syntax_error(pj_scanner *scanner)
+{
+    PJ_UNUSED_ARG(scanner);
+    PJ_THROW(PJ_EINVAL);
+}
+
+/*
+ * This method is to process the backspace character sent by client.
+ */
+static pj_status_t get_last_token(pj_str_t *cmd, pj_str_t *str)
+{
+    pj_scanner scanner;
+    PJ_USE_EXCEPTION;
+    pj_scan_init(&scanner, cmd->ptr, cmd->slen, PJ_SCAN_AUTOSKIP_WS, 
+		 &on_syntax_error);
+    PJ_TRY {
+	while (!pj_scan_is_eof(&scanner)) {
+	    pj_scan_get_until_chr(&scanner, " \t\r\n", str);
+	}
+    }
+    PJ_CATCH_ANY {
+	pj_scan_fini(&scanner);	
+	return PJ_GET_EXCEPTION();
+    }
+    PJ_END;
+    return PJ_SUCCESS;
+}
+
+/*
+ * This method is to process the tab character sent by client.
+ */
+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 = (unsigned)pj_ansi_strlen((char *)sess->rcmd->rbuf);
+
+    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 */
+	    pj_str_t cmd = pj_str((char *)sess->rcmd->rbuf);
+	    pj_str_t last_token;
+
+	    if (get_last_token(&cmd, &last_token) == PJ_SUCCESS) {
+		/* Hint contains the match to the last command entered */
+		pj_str_t *hint_info = &info.hint[0].name;
+		pj_strtrim(&last_token);
+		if (hint_info->slen >= last_token.slen) {
+		    hint_info->slen -= last_token.slen;
+		    pj_memmove(hint_info->ptr, 
+			       &hint_info->ptr[last_token.slen], 
+			       hint_info->slen);		    
+		} 
+		send_comp_arg(sess, &info);
+
+		pj_memcpy(&sess->rcmd->rbuf[len], info.hint[0].name.ptr, 
+			  info.hint[0].name.slen);
+
+		len += (unsigned)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; 
+}
+
+/*
+ * This method is to process the return character sent by client.
+ */
+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; 
+}
+
+/*
+ * This method is to process the right key character sent by client.
+ */
+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;
+}
+
+/*
+ * This method is to process the left key character sent by client.
+ */
+static pj_bool_t handle_left_key(cli_telnet_sess *sess)
+{    
+    static const unsigned char move_cursor_left = 0x08;
+    if (sess->rcmd->cur_pos) {
+	telnet_sess_send2(sess, &move_cursor_left, 1);
+	--sess->rcmd->cur_pos;
+	return PJ_TRUE;
+    }
+    return PJ_FALSE;
+}
+
+/*
+ * This method is to process the up/down key character sent by client.
+ */
+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];
+	enum {
+	    MOVE_CURSOR_LEFT = 0x08,
+	    CLEAR_CHAR = 0x20
+	};
+	send_data.ptr = str;
+	send_data.slen = 0;
+
+	/* Move cursor position to the beginning of line */
+	if (sess->rcmd->cur_pos > 0) {
+	    pj_memset(send_data.ptr, MOVE_CURSOR_LEFT, sess->rcmd->cur_pos);
+	    send_data.slen = sess->rcmd->cur_pos;
+	}
+	
+	if (sess->rcmd->len > (unsigned)history->slen) {
+	    /* Clear the command currently shown*/
+	    unsigned buf_len = sess->rcmd->len;
+	    pj_memset(&send_data.ptr[send_data.slen], CLEAR_CHAR, buf_len);
+	    send_data.slen += buf_len;
+
+	    /* Move cursor position to the beginning of line */
+	    pj_memset(&send_data.ptr[send_data.slen], MOVE_CURSOR_LEFT, 
+		      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 = (unsigned)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)
+{
+    pj_assert(param);
+
+    pj_bzero(param, sizeof(*param));
+    param->port = PJ_CLI_TELNET_PORT;
+    param->log_level = PJ_CLI_TELNET_LOG_LEVEL;
+}
+
+/* 
+ * Send a message to a telnet session 
+ */
+static pj_status_t telnet_sess_send(cli_telnet_sess *sess,
+				    const pj_str_t *str)
+{
+    pj_ssize_t sz;
+    pj_status_t status = PJ_SUCCESS;
+
+    sz = str->slen;
+    if (!sz)
+        return PJ_SUCCESS;
+
+    pj_mutex_lock(sess->smutex);
+
+    if (sess->buf_len == 0)
+        status = pj_activesock_send(sess->asock, &sess->op_key, 
+                                    str->ptr, &sz, 0);
+    /* If we cannot send now, append it at the end of the buffer 
+     * to be sent later.
+     */
+    if (sess->buf_len > 0 || 
+        (status != PJ_SUCCESS && status != PJ_EPENDING))
+    {
+        int clen = (int)sz;
+
+        if (sess->buf_len + clen > CLI_TELNET_BUF_SIZE)
+            clen = CLI_TELNET_BUF_SIZE - sess->buf_len;
+        if (clen > 0)
+            pj_memmove(sess->buf + sess->buf_len, str->ptr, clen);
+        if (clen < sz) {
+            pj_ansi_snprintf((char *)sess->buf + CLI_TELNET_BUF_SIZE,
+                             MAX_CUT_MSG_LEN, CUT_MSG);
+            sess->buf_len = (unsigned)(CLI_TELNET_BUF_SIZE +
+                            pj_ansi_strlen((char *)sess->buf+
+				            CLI_TELNET_BUF_SIZE));
+        } else
+            sess->buf_len += clen;
+    } else if (status == PJ_SUCCESS && sz < str->slen) {
+        pj_mutex_unlock(sess->smutex);
+        return PJ_CLI_ETELNETLOST;
+    }
+
+    pj_mutex_unlock(sess->smutex);
+
+    return PJ_SUCCESS;
+}
+
+/* 
+ * Send a message to a telnet session with formatted text 
+ * (add single linefeed character with carriage return)
+ */
+static pj_status_t telnet_sess_send_with_format(cli_telnet_sess *sess,
+						const pj_str_t *str)
+{
+    pj_scanner scanner;
+    pj_str_t out_str;
+    static const pj_str_t CR_LF = {("\r\n"), 2};
+    int str_len = 0;
+    char *str_begin = 0;
+
+    PJ_USE_EXCEPTION;
+
+    pj_scan_init(&scanner, str->ptr, str->slen, 
+	         PJ_SCAN_AUTOSKIP_WS, &on_syntax_error);
+    
+    str_begin = scanner.begin;
+
+    PJ_TRY {
+	while (!pj_scan_is_eof(&scanner)) {	    	    
+	    pj_scan_get_until_ch(&scanner, '\n', &out_str);	    
+	    str_len = (int)(scanner.curptr - str_begin);
+	    if (*scanner.curptr == '\n') {		
+		if ((str_len > 1) && (out_str.ptr[str_len-2] == '\r')) 
+		{	    		    
+		    continue;
+		} else {		    
+		    int str_pos = (int)(str_begin - scanner.begin);
+
+		    if (str_len > 0) {
+			pj_str_t s;
+			pj_strset(&s, &str->ptr[str_pos], str_len);
+			telnet_sess_send(sess, &s);
+		    }
+		    telnet_sess_send(sess, &CR_LF);
+
+		    if (!pj_scan_is_eof(&scanner)) {
+			pj_scan_advance_n(&scanner, 1, PJ_TRUE);
+			str_begin = scanner.curptr;
+		    }		    
+		}
+	    } else {
+		pj_str_t s;
+		int str_pos = (int)(str_begin - scanner.begin);
+
+		pj_strset(&s, &str->ptr[str_pos], str_len);
+		telnet_sess_send(sess, &s);
+	    }
+	}
+    }
+    PJ_CATCH_ANY {
+	pj_scan_fini(&scanner);
+	return (PJ_GET_EXCEPTION());
+    }
+    PJ_END;
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t telnet_sess_send2(cli_telnet_sess *sess,
+                                     const unsigned char *str, int len)
+{
+    pj_str_t s;
+
+    pj_strset(&s, (char *)str, len);
+    return telnet_sess_send(sess, &s);
+}
+
+static void telnet_sess_destroy(pj_cli_sess *sess)
+{
+    cli_telnet_sess *tsess = (cli_telnet_sess *)sess;
+    pj_mutex_t *mutex = ((cli_telnet_fe *)sess->fe)->mutex;
+
+    pj_mutex_lock(mutex);
+    pj_list_erase(sess);
+    pj_mutex_unlock(mutex);
+
+    pj_mutex_lock(tsess->smutex);
+    pj_mutex_unlock(tsess->smutex);
+    pj_activesock_close(tsess->asock);
+    pj_mutex_destroy(tsess->smutex);    
+    pj_pool_release(tsess->pool);
+}
+
+static void telnet_fe_write_log(pj_cli_front_end *fe, int level,
+		                const char *data, pj_size_t len)
+{
+    cli_telnet_fe *tfe = (cli_telnet_fe *)fe;
+    pj_cli_sess *sess;    
+
+    pj_mutex_lock(tfe->mutex);
+
+    sess = tfe->sess_head.next;
+    while (sess != &tfe->sess_head) {
+        cli_telnet_sess *tsess = (cli_telnet_sess *)sess;
+
+        sess = sess->next;        
+	if (tsess->base.log_level > level) {
+	    pj_str_t s;
+
+	    pj_strset(&s, (char *)data, len);
+	    telnet_sess_send_with_format(tsess, &s);
+	}
+    }
+    
+    pj_mutex_unlock(tfe->mutex);
+}
+
+static void telnet_fe_destroy(pj_cli_front_end *fe)
+{
+    cli_telnet_fe *tfe = (cli_telnet_fe *)fe;
+    pj_cli_sess *sess;
+
+    tfe->is_quitting = PJ_TRUE;
+    if (tfe->worker_thread) {
+        pj_thread_join(tfe->worker_thread);
+    }
+
+    pj_mutex_lock(tfe->mutex);
+
+    /* Destroy all the sessions */
+    sess = tfe->sess_head.next;
+    while (sess != &tfe->sess_head) {
+        (*sess->op->destroy)(sess);
+        sess = tfe->sess_head.next;
+    }
+
+    pj_mutex_unlock(tfe->mutex);
+
+    pj_activesock_close(tfe->asock);
+    if (tfe->own_ioqueue)
+        pj_ioqueue_destroy(tfe->cfg.ioqueue);
+
+    if (tfe->worker_thread) {
+	pj_thread_destroy(tfe->worker_thread);
+	tfe->worker_thread = NULL;
+    }
+
+    pj_mutex_destroy(tfe->mutex);
+    pj_pool_release(tfe->pool);
+}
+
+static int poll_worker_thread(void *p)
+{
+    cli_telnet_fe *fe = (cli_telnet_fe *)p;
+
+    while (!fe->is_quitting) {
+	pj_time_val delay = {0, 50};
+        pj_ioqueue_poll(fe->cfg.ioqueue, &delay);
+    }
+
+    return 0;
+}
+
+static pj_bool_t telnet_sess_on_data_sent(pj_activesock_t *asock,
+ 				          pj_ioqueue_op_key_t *op_key,
+				          pj_ssize_t sent)
+{
+    cli_telnet_sess *sess = (cli_telnet_sess *)
+			    pj_activesock_get_user_data(asock);
+
+    PJ_UNUSED_ARG(op_key);    
+
+    if (sent <= 0) {
+	TRACE_((THIS_FILE, "Error On data send"));
+        pj_cli_sess_end_session(&sess->base);
+        return PJ_FALSE;
+    }
+
+    pj_mutex_lock(sess->smutex);
+
+    if (sess->buf_len) {
+        int len = sess->buf_len;
+
+        sess->buf_len = 0;
+        if (telnet_sess_send2(sess, sess->buf, len) != PJ_SUCCESS) {
+            pj_mutex_unlock(sess->smutex);
+            pj_cli_sess_end_session(&sess->base);
+            return PJ_FALSE;
+        }
+    }
+
+    pj_mutex_unlock(sess->smutex);
+
+    return PJ_TRUE;
+}
+
+static pj_bool_t telnet_sess_on_data_read(pj_activesock_t *asock,
+		                          void *data,
+			                  pj_size_t size,
+			                  pj_status_t status,
+			                  pj_size_t *remainder)
+{
+    cli_telnet_sess *sess = (cli_telnet_sess *)
+                            pj_activesock_get_user_data(asock);
+    cli_telnet_fe *tfe = (cli_telnet_fe *)sess->base.fe;
+    unsigned char *cdata = (unsigned char*)data;    
+    pj_status_t is_valid = PJ_TRUE;
+
+    PJ_UNUSED_ARG(size);
+    PJ_UNUSED_ARG(remainder);
+
+    if (tfe->is_quitting)
+        return PJ_FALSE;
+
+    if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+	TRACE_((THIS_FILE, "Error on data read %d", status));
+        return PJ_FALSE;
+    }    
+
+    pj_mutex_lock(sess->smutex);
+
+    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 == 127) {
+		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);
+    }
+
+    pj_mutex_unlock(sess->smutex);
+
+    return PJ_TRUE;
+}
+
+static pj_bool_t telnet_fe_on_accept(pj_activesock_t *asock,
+				     pj_sock_t newsock,
+				     const pj_sockaddr_t *src_addr,
+				     int src_addr_len,
+				     pj_status_t status)
+{
+    cli_telnet_fe *fe = (cli_telnet_fe *) pj_activesock_get_user_data(asock);
+
+    pj_status_t sstatus;
+    pj_pool_t *pool;
+    cli_telnet_sess *sess = NULL;
+    pj_activesock_cb asock_cb;
+
+    PJ_UNUSED_ARG(src_addr);
+    PJ_UNUSED_ARG(src_addr_len);
+
+    if (fe->is_quitting)
+        return PJ_FALSE;
+
+    if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+	TRACE_((THIS_FILE, "Error on data accept %d", status));
+	if (status == PJ_ESOCKETSTOP) 
+	    telnet_restart(fe);	    
+	
+        return PJ_FALSE;
+    }    
+
+    /* An incoming connection is accepted, create a new session */
+    pool = pj_pool_create(fe->pool->factory, "telnet_sess",
+                          PJ_CLI_TELNET_POOL_SIZE, PJ_CLI_TELNET_POOL_INC,
+                          NULL);
+    if (!pool) {
+        TRACE_((THIS_FILE, 
+                "Not enough memory to create a new telnet session"));
+        return PJ_TRUE;
+    }
+
+    sess = PJ_POOL_ZALLOC_T(pool, cli_telnet_sess);
+    sess->pool = pool;
+    sess->base.fe = &fe->base;
+    sess->base.log_level = fe->cfg.log_level;
+    sess->base.op = PJ_POOL_ZALLOC_T(pool, struct pj_cli_sess_op);
+    sess->base.op->destroy = &telnet_sess_destroy;
+    pj_bzero(&asock_cb, sizeof(asock_cb));
+    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",
+                                        &sess->smutex);
+    if (sstatus != PJ_SUCCESS)
+        goto on_exit;
+    
+    sstatus = pj_activesock_create(pool, newsock, pj_SOCK_STREAM(),
+                                   NULL, fe->cfg.ioqueue,
+			           &asock_cb, sess, &sess->asock);
+    if (sstatus != PJ_SUCCESS) {
+        TRACE_((THIS_FILE, "Failure creating active socket"));
+        goto on_exit;
+    }
+
+    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, TERM_ECHO, PJ_TRUE);
+
+    send_cmd_do(sess, SUPPRESS_GA);
+    send_cmd_will(sess, TERM_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 */
+    sstatus = pj_activesock_start_read(sess->asock, pool, 1, 0);
+    if (sstatus != PJ_SUCCESS) {
+        TRACE_((THIS_FILE, "Failure reading active socket"));
+        goto on_exit;
+    }
+
+    pj_ioqueue_op_key_init(&sess->op_key, sizeof(sess->op_key));
+    pj_mutex_lock(fe->mutex);
+    pj_list_push_back(&fe->sess_head, &sess->base);
+    pj_mutex_unlock(fe->mutex);
+
+    return PJ_TRUE;
+
+on_exit:
+    if (sess->asock)
+        pj_activesock_close(sess->asock);
+    else
+        pj_sock_close(newsock);
+    
+    if (sess->smutex)
+        pj_mutex_destroy(sess->smutex);
+
+    pj_pool_release(pool);
+
+    return PJ_TRUE;
+}
+
+PJ_DEF(pj_status_t) pj_cli_telnet_create(pj_cli_t *cli,
+					 pj_cli_telnet_cfg *param,
+					 pj_cli_front_end **p_fe)
+{
+    cli_telnet_fe *fe;
+    pj_pool_t *pool;
+    pj_status_t status;   
+
+    PJ_ASSERT_RETURN(cli, PJ_EINVAL);
+
+    pool = pj_pool_create(pj_cli_get_param(cli)->pf, "telnet_fe",
+                          PJ_CLI_TELNET_POOL_SIZE, PJ_CLI_TELNET_POOL_INC,
+                          NULL);
+    fe = PJ_POOL_ZALLOC_T(pool, cli_telnet_fe);
+    if (!fe)
+        return PJ_ENOMEM;
+	
+    fe->base.op = PJ_POOL_ZALLOC_T(pool, struct pj_cli_front_end_op);
+
+    if (!param)
+        pj_cli_telnet_cfg_default(&fe->cfg);
+    else
+        pj_memcpy(&fe->cfg, param, sizeof(*param));
+	
+    pj_list_init(&fe->sess_head);
+    fe->base.cli = cli;
+    fe->base.type = PJ_CLI_TELNET_FRONT_END;
+    fe->base.op->on_write_log = &telnet_fe_write_log;
+    fe->base.op->on_destroy = &telnet_fe_destroy;
+    fe->pool = pool;
+
+    if (!fe->cfg.ioqueue) {	
+        /* Create own ioqueue if application doesn't supply one */
+        status = pj_ioqueue_create(pool, 8, &fe->cfg.ioqueue);
+        if (status != PJ_SUCCESS)
+            goto on_exit;
+        fe->own_ioqueue = PJ_TRUE;
+    }
+
+    status = pj_mutex_create_recursive(pool, "mutex_telnet_fe", &fe->mutex);
+    if (status != PJ_SUCCESS)
+        goto on_exit;
+
+    /* Start telnet daemon */
+    status = telnet_start(fe);
+    if (status != PJ_SUCCESS)
+	goto on_exit;
+
+    pj_cli_register_front_end(cli, &fe->base);
+
+    if (p_fe)
+        *p_fe = &fe->base;
+
+    return PJ_SUCCESS;
+
+on_exit:
+    if (fe->own_ioqueue)
+        pj_ioqueue_destroy(fe->cfg.ioqueue);
+
+    if (fe->mutex)
+        pj_mutex_destroy(fe->mutex);
+
+    pj_pool_release(pool);
+    return status;
+}
+
+static pj_status_t telnet_start(cli_telnet_fe *fe)
+{
+    pj_sock_t sock = PJ_INVALID_SOCKET;
+    pj_activesock_cb asock_cb;
+    pj_sockaddr_in addr;
+    pj_status_t status;   
+    int val;
+    int restart_retry;
+    unsigned msec;
+
+    /* Start telnet daemon */
+    status = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &sock);
+
+    if (status != PJ_SUCCESS)
+        goto on_exit;
+
+    pj_sockaddr_in_init(&addr, NULL, fe->cfg.port);
+
+    val = 1;
+    status = pj_sock_setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+				&val, sizeof(val));
+
+    if (status != PJ_SUCCESS) {
+	PJ_LOG(3, (THIS_FILE, "Failed setting socket options"));
+    }
+
+    /* The loop is silly, but what else can we do? */
+    for (msec=MIN_WAIT_ON_TELNET_RESTART, restart_retry=0;
+	 restart_retry < MAX_RETRY_ON_TELNET_RESTART;
+	 ++restart_retry, msec=(msec<MAX_WAIT_ON_TELNET_RESTART?
+		          msec*2 : MAX_WAIT_ON_TELNET_RESTART))
+    {
+	status = pj_sock_bind(sock, &addr, sizeof(addr));
+	if (status != PJ_STATUS_FROM_OS(EADDRINUSE))
+	    break;
+	PJ_LOG(4,(THIS_FILE, "Address is still in use, retrying.."));
+	pj_thread_sleep(msec);
+    }    
+    
+    if (status == PJ_SUCCESS) {	
+	int addr_len = sizeof(addr);	
+
+	status = pj_sock_getsockname(sock, &addr, &addr_len);
+	if (status != PJ_SUCCESS)
+	    goto on_exit;
+	    
+        fe->cfg.port = pj_sockaddr_in_get_port(&addr);	
+
+	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"));
+        goto on_exit;
+    }
+
+    status = pj_sock_listen(sock, 4);
+    if (status != PJ_SUCCESS)
+        goto on_exit;
+
+    pj_bzero(&asock_cb, sizeof(asock_cb));
+    asock_cb.on_accept_complete2 = &telnet_fe_on_accept;
+    status = pj_activesock_create(fe->pool, sock, pj_SOCK_STREAM(),
+                                  NULL, fe->cfg.ioqueue,
+		                  &asock_cb, fe, &fe->asock);
+    if (status != PJ_SUCCESS)
+        goto on_exit;
+
+    status = pj_activesock_start_accept(fe->asock, fe->pool);
+    if (status != PJ_SUCCESS)
+        goto on_exit;
+
+    if (fe->own_ioqueue) {
+        /* Create our own worker thread */
+        status = pj_thread_create(fe->pool, "worker_telnet_fe",
+                                  &poll_worker_thread, fe, 0, 0,
+                                  &fe->worker_thread);
+        if (status != PJ_SUCCESS)
+            goto on_exit;
+    }
+
+    return PJ_SUCCESS;
+
+on_exit:
+    if (fe->cfg.on_started) {
+	(*fe->cfg.on_started)(status);
+    }
+
+    if (fe->asock)
+        pj_activesock_close(fe->asock);
+    else if (sock != PJ_INVALID_SOCKET)
+        pj_sock_close(sock);
+
+    if (fe->own_ioqueue)
+        pj_ioqueue_destroy(fe->cfg.ioqueue);
+
+    if (fe->mutex)
+        pj_mutex_destroy(fe->mutex);
+
+    pj_pool_release(fe->pool);
+    return status;
+}
+
+static pj_status_t telnet_restart(cli_telnet_fe *fe)
+{
+    pj_status_t status;
+    pj_cli_sess *sess;      
+
+    fe->is_quitting = PJ_TRUE;
+    if (fe->worker_thread) {	
+	pj_thread_join(fe->worker_thread);
+    }
+
+    pj_mutex_lock(fe->mutex);
+
+    /* Destroy all the sessions */
+    sess = fe->sess_head.next;
+    while (sess != &fe->sess_head) {
+	(*sess->op->destroy)(sess);
+	sess = fe->sess_head.next;
+    }
+
+    pj_mutex_unlock(fe->mutex);
+
+    /** Close existing activesock **/
+    status = pj_activesock_close(fe->asock);
+    if (status != PJ_SUCCESS)
+	goto on_exit;
+
+    if (fe->worker_thread) {
+	pj_thread_destroy(fe->worker_thread);
+	fe->worker_thread = NULL;
+    }
+
+    fe->is_quitting = PJ_FALSE;
+
+    /** Start Telnet **/
+    status = telnet_start(fe);
+    if (status == PJ_SUCCESS) {
+	if (fe->cfg.on_started) {
+	    (*fe->cfg.on_started)(status);
+	}
+	TRACE_((THIS_FILE, "Telnet Restarted"));
+    }
+on_exit:
+    return status;
+}
+
+PJ_DEF(pj_status_t) pj_cli_telnet_get_info(pj_cli_front_end *fe, 
+					   pj_cli_telnet_info *info)
+{
+    pj_sockaddr hostip;
+    pj_status_t status;
+    cli_telnet_fe *tfe = (cli_telnet_fe*) fe; 
+
+    PJ_ASSERT_RETURN(fe && (fe->type == PJ_CLI_TELNET_FRONT_END) && info, 
+		     PJ_EINVAL);
+
+    pj_strset(&info->ip_address, info->buf_, 0);
+
+    status = pj_gethostip(pj_AF_INET(), &hostip);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    pj_strcpy2(&info->ip_address, pj_inet_ntoa(hostip.ipv4.sin_addr));
+
+    info->port = tfe->cfg.port;
+
+    return PJ_SUCCESS;
+}