Index: /pjproject/branches/projects/cli/pjlib-util/build/Makefile
===================================================================
--- /pjproject/branches/projects/cli/pjlib-util/build/Makefile	(revision 3230)
+++ /pjproject/branches/projects/cli/pjlib-util/build/Makefile	(revision 3231)
@@ -28,5 +28,5 @@
 export PJLIB_UTIL_SRCDIR = ../src/pjlib-util
 export PJLIB_UTIL_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \
-		base64.o cli.o cli_console.o crc32.o errno.o dns.o \
+		base64.o cli.o cli_console.o cli_telnet.o crc32.o errno.o dns.o \
 		dns_dump.o dns_server.o getopt.o hmac_md5.o hmac_sha1.o \
 		http_client.o md5.o pcap.o resolver.o scanner.o sha1.o \
Index: /pjproject/branches/projects/cli/pjlib-util/build/pjlib_util.vcproj
===================================================================
--- /pjproject/branches/projects/cli/pjlib-util/build/pjlib_util.vcproj	(revision 3230)
+++ /pjproject/branches/projects/cli/pjlib-util/build/pjlib_util.vcproj	(revision 3231)
@@ -2778,4 +2778,8 @@
 			</File>
 			<File
+				RelativePath="..\src\pjlib-util\cli_telnet.c"
+				>
+			</File>
+			<File
 				RelativePath="..\src\pjlib-util\crc32.c"
 				>
@@ -4735,4 +4739,8 @@
 			</File>
 			<File
+				RelativePath="..\include\pjlib-util\cli_telnet.h"
+				>
+			</File>
+			<File
 				RelativePath="..\include\pjlib-util\config.h"
 				>
Index: /pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/cli.h
===================================================================
--- /pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/cli.h	(revision 3230)
+++ /pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/cli.h	(revision 3231)
@@ -38,57 +38,4 @@
 
 /**
- * Maximum length of command buffer.
- */
-#ifndef PJ_CLI_MAX_CMDBUF
-#   define PJ_CLI_MAX_CMDBUF		120
-#endif
-
-/**
- * Maximum command arguments.
- */
-#ifndef PJ_CLI_MAX_ARGS
-#   define PJ_CLI_MAX_ARGS		8
-#endif
-
-/**
- * Maximum short name version (shortcuts) for a command.
- */
-#ifndef PJ_CLI_MAX_SHORTCUTS
-#   define PJ_CLI_MAX_SHORTCUTS		4
-#endif
-
-/*
- *  New error constants (note: to be placed in errno.h with new values)
- */
-/**
- * @hideinitializer
- * End the current session. This is a special error code returned by
- * pj_cli_exec() to indicate that "exit" or equivalent command has been
- * called to end the current session.
- */
-#define PJ_CLI_EEXIT        		-101
-/**
- * @hideinitializer
- * A required CLI argument is not specified.
- */
-#define PJ_CLI_EMISSINGARG    		-104
- /**
- * @hideinitializer
- * Too many CLI arguments.
- */
-#define PJ_CLI_ETOOMANYARGS    		-105
-/**
- * @hideinitializer
- * Invalid CLI argument. Typically this is caused by extra characters
- * specified in the command line which does not match any arguments.
- */
-#define PJ_CLI_EINVARG        		-106
-/**
- * @hideinitializer
- * CLI command with the specified name already exist.
- */
-#define PJ_CLI_EBADNAME        		-107
-
-/**
  * This opaque structure represents a CLI application. A CLI application is
  * the root placeholder of other CLI objects. In an application, one (and
@@ -146,14 +93,4 @@
      */
     pj_pool_factory *pf;
-
-    /**
-     * Specify whether only exact matching command will be executed. If
-     * PJ_FALSE, the framework will accept any unique abbreviations of
-     * the command. Please see the description of pj_cli_parse() function
-     * for more info.
-     *
-     * Default: PJ_FALSE
-     */
-    pj_bool_t exact_cmd;
 
 } pj_cli_cfg;
@@ -383,4 +320,18 @@
  */
 PJ_DECL(void) pj_cli_exec_info_default(pj_cli_exec_info *param);
+
+/**
+ * Write a log message to the CLI application. The CLI application
+ * will send the log message to all the registered front-ends.
+ *
+ * @param cli		The CLI application instance.
+ * @param level	        Verbosity level of this message message.
+ * @param buffer        The message itself.
+ * @param len 	        Length of this message.
+ */
+PJ_DECL(void) pj_cli_write_log(pj_cli_t *cli,
+                               int level,
+                               const char *buffer,
+                               int len);
 
 /**
@@ -498,13 +449,4 @@
  * registered in the CLI application.
  *
- * By default, a command may be matched by any shorter abbreviations of the
- * command that uniquely identify the command. For example, suppose two
- * commands "help" and "hold" are currently the only commands registered in
- * the CLI application. In this case, specifying "he" and "hel" would also
- * match "help" command, and similarly "ho" and "hol" would also match "hold"
- * command, but specifying "h" only would yield an error as it would match
- * more than one commands. This matching behavior can be turned off by
- * setting \a pj_cli_cfg.exact_cmd to PJ_TRUE.
- *
  * Zero or more arguments follow the command name. Arguments are separated by
  * one or more whitespaces. Argument may be placed inside a pair of quotes,
@@ -517,6 +459,6 @@
  *
  * The cmdline may be followed by an extra newline (LF or CR-LF characters),
- * which simply will be ignored. However any more characters following this
- * newline will cause an error to be returned.
+ * which will be removed by the function. However any more characters
+ * following this newline will cause an error to be returned.
  *
  * @param sess		The CLI session.
Index: /pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/cli_console.h
===================================================================
--- /pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/cli_console.h	(revision 3230)
+++ /pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/cli_console.h	(revision 3231)
@@ -86,5 +86,5 @@
  * instead of the standard gets() or fgets(), since unlike these functions,
  * this is able to end the blocking when pj_cli_quit() is called by other
- * session.
+ * session. This function requires a thread support.
  *
  * Note that this function would also remove the trailing newlines from the
Index: /pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/cli_telnet.h
===================================================================
--- /pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/cli_telnet.h	(revision 3231)
+++ /pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/cli_telnet.h	(revision 3231)
@@ -0,0 +1,113 @@
+/* $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 
+ */
+#ifndef __PJLIB_UTIL_CLI_TELNET_H__
+#define __PJLIB_UTIL_CLI_TELNET_H__
+
+/**
+ * @file cli_telnet.h
+ * @brief Command Line Interface Telnet Front End API
+ */
+
+#include <pjlib-util/cli_imp.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @ingroup PJLIB_UTIL_CLI_IMP
+ * @{
+ *
+ */
+
+
+/**
+ * This structure contains various options to instantiate the telnet daemon.
+ * Application must call pj_cli_telnet_cfg_default() to initialize
+ * this structure with its default values.
+ */
+typedef struct pj_cli_telnet_cfg
+{
+    /**
+     * Listening port number. The value may be 0 to let the system choose
+     * the first available port.
+     *
+     * Default value: PJ_CLI_TELNET_PORT
+     */
+    pj_uint16_t port;
+
+    /**
+     * Ioqueue instance to be used. If this field is NULL, an internal
+     * ioqueue and worker thread will be created.
+     */
+    pj_ioqueue_t *ioqueue;
+
+    /**
+     * Default log verbosity level for the session.
+     *
+     * Default value: PJ_CLI_TELNET_LOG_LEVEL
+     */
+    int log_level;
+
+    /**
+     * Specify a password to be asked to the end user to access the
+     * application.
+     *
+     * Default: empty (no password)
+     */
+    pj_str_t passwd;
+
+    /**
+     * Specify text message to be displayed to newly connected users.
+     *
+     * Default: empty
+     */
+    pj_str_t welcome_msg;
+
+} pj_cli_telnet_cfg;
+
+
+/**
+ * Initialize pj_cli_telnet_cfg with its default values.
+ *
+ * @param param		The structure to be initialized.
+ */
+PJ_DECL(void) pj_cli_telnet_cfg_default(pj_cli_telnet_cfg *param);
+
+
+/**
+ * Create, initialize, and start a telnet daemon for the application.
+ *
+ * @param cli		The CLI application instance.
+ * @param param		Optional parameters for creating the telnet daemon.
+ * 			If this value is NULL, default parameters will be used.
+ * @param p_fe		Optional pointer to receive the front-end instance
+ * 			of the telnet front-end just created.
+ *
+ * @return		PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_cli_telnet_create(pj_cli_t *cli,
+					  const pj_cli_telnet_cfg *param,
+					  pj_cli_front_end **p_fe);
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif /* __PJLIB_UTIL_CLI_TELNET_H__ */
Index: /pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/config.h
===================================================================
--- /pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/config.h	(revision 3230)
+++ /pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/config.h	(revision 3231)
@@ -267,4 +267,81 @@
 #endif
 
+/* **************************************************************************
+ * CLI configuration
+ */
+
+/**
+ * Initial pool size for CLI.
+ * Default: 1024 bytes
+ */
+#ifndef PJ_CLI_POOL_SIZE
+#   define PJ_CLI_POOL_SIZE    1024
+#endif
+
+/**
+ * Pool increment size for CLI.
+ * Default: 512 bytes
+ */
+#ifndef PJ_CLI_POOL_INC
+#   define PJ_CLI_POOL_INC     512
+#endif
+
+/**
+ * Maximum length of command buffer.
+ * Default: 120
+ */
+#ifndef PJ_CLI_MAX_CMDBUF
+#   define PJ_CLI_MAX_CMDBUF		120
+#endif
+
+/**
+ * Maximum command arguments.
+ * Default: 8
+ */
+#ifndef PJ_CLI_MAX_ARGS
+#   define PJ_CLI_MAX_ARGS		8
+#endif
+
+/**
+ * Maximum short name version (shortcuts) for a command.
+ * Default: 4
+ */
+#ifndef PJ_CLI_MAX_SHORTCUTS
+#   define PJ_CLI_MAX_SHORTCUTS		4
+#endif
+
+/**
+ * Initial pool size for console CLI.
+ * Default: 256 bytes
+ */
+#ifndef PJ_CLI_CONSOLE_POOL_SIZE
+#   define PJ_CLI_CONSOLE_POOL_SIZE    256
+#endif
+
+/**
+ * Pool increment size for console CLI.
+ * Default: 256 bytes
+ */
+#ifndef PJ_CLI_CONSOLE_POOL_INC
+#   define PJ_CLI_CONSOLE_POOL_INC     256
+#endif
+
+/**
+ * Initial pool size for telnet CLI.
+ * Default: 1024 bytes
+ */
+#ifndef PJ_CLI_TELNET_POOL_SIZE
+#   define PJ_CLI_TELNET_POOL_SIZE 1024
+#endif
+
+/**
+ * Pool increment size for telnet CLI.
+ * Default: 512 bytes
+ */
+#ifndef PJ_CLI_TELNET_POOL_INC
+#   define PJ_CLI_TELNET_POOL_INC  512
+#endif
+
+
 /**
  * @}
Index: /pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/errno.h
===================================================================
--- /pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/errno.h	(revision 3230)
+++ /pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/errno.h	(revision 3231)
@@ -387,4 +387,47 @@
 #define PJLIB_UTIL_EHTTPLOST	    (PJLIB_UTIL_ERRNO_START+155)/* 320155 */
 
+/************************************************************
+ * CLI ERROR
+ ***********************************************************/
+
+/**
+ * @hideinitializer
+ * End the current session. This is a special error code returned by
+ * pj_cli_exec() to indicate that "exit" or equivalent command has been
+ * called to end the current session.
+ */
+#define PJ_CLI_EEXIT        	    (PJLIB_UTIL_ERRNO_START+201)/* 320201 */
+/**
+ * @hideinitializer
+ * A required CLI argument is not specified.
+ */
+#define PJ_CLI_EMISSINGARG    	    (PJLIB_UTIL_ERRNO_START+202)/* 320202 */
+ /**
+ * @hideinitializer
+ * Too many CLI arguments.
+ */
+#define PJ_CLI_ETOOMANYARGS    	    (PJLIB_UTIL_ERRNO_START+203)/* 320203 */
+/**
+ * @hideinitializer
+ * Invalid CLI argument. Typically this is caused by extra characters
+ * specified in the command line which does not match any arguments.
+ */
+#define PJ_CLI_EINVARG        	    (PJLIB_UTIL_ERRNO_START+204)/* 320204 */
+/**
+ * @hideinitializer
+ * CLI command with the specified name already exist.
+ */
+#define PJ_CLI_EBADNAME        	    (PJLIB_UTIL_ERRNO_START+205)/* 320205 */
+/**
+ * @hideinitializer
+ * Invalid XML format for CLI command specification.
+ */
+#define PJ_CLI_EBADXML        	    (PJLIB_UTIL_ERRNO_START+206)/* 320206 */
+/**
+ * @hideinitializer
+ * Telnet connection lost.
+ */
+#define PJ_CLI_ETELNETLOST          (PJLIB_UTIL_ERRNO_START+211)/* 320211 */
+
 /**
  * @}
Index: /pjproject/branches/projects/cli/pjlib-util/src/pjlib-util/cli.c
===================================================================
--- /pjproject/branches/projects/cli/pjlib-util/src/pjlib-util/cli.c	(revision 3230)
+++ /pjproject/branches/projects/cli/pjlib-util/src/pjlib-util/cli.c	(revision 3231)
@@ -30,4 +30,9 @@
 #include <pjlib-util/xml.h>
 
+#define CMD_HASH_TABLE_SIZE 63	/* Hash table size */
+
+#define CLI_CMD_CHANGE_LOG  30000
+#define CLI_CMD_EXIT        30001
+
 #if 1
     /* Enable some tracing */
@@ -38,13 +43,11 @@
 #endif
 
-#define CMD_HASH_TABLE_SIZE 63		/* Hash table size */
-
 struct pj_cli_t
 {
-    pj_pool_t	       *pool;          /* Pool to allocate memory from */
+    pj_pool_t	       *pool;           /* Pool to allocate memory from */
     pj_cli_cfg          cfg;            /* CLI configuration */
     pj_cli_cmd_spec     root;           /* Root of command tree structure */
     pj_cli_front_end    fe_head;        /* List of front-ends */
-    pj_hash_table_t    *hash;          /* Command hash table */
+    pj_hash_table_t    *hash;           /* Command hash table */
 
     pj_bool_t           is_quitting;
@@ -68,4 +71,42 @@
 }
 
+PJ_DEF(void) pj_cli_write_log(pj_cli_t *cli,
+                              int level,
+                              const char *buffer,
+                              int len)
+{
+    struct pj_cli_front_end *fe;
+
+    pj_assert(cli);
+
+    fe = cli->fe_head.next;
+    while (fe != &cli->fe_head) {
+        if (fe->op && fe->op->on_write_log)
+            (*fe->op->on_write_log)(fe, level, buffer, len);
+        fe = fe->next;
+    }
+}
+
+/* Command handler */
+static pj_status_t cmd_handler(pj_cli_cmd_val *cval)
+{
+    unsigned level;
+
+    switch(cval->cmd->id) {
+    case CLI_CMD_CHANGE_LOG:
+        level = pj_strtoul(&cval->argv[1]);
+        if (!level && cval->argv[1].slen > 0 && (cval->argv[1].ptr[0] < '0' ||
+            cval->argv[1].ptr[0] > '9'))
+            return PJ_CLI_EINVARG;
+        cval->sess->log_level = level;
+        return PJ_SUCCESS;
+    case CLI_CMD_EXIT:
+        pj_cli_end_session(cval->sess);
+        return PJ_CLI_EEXIT;
+    default:
+        return PJ_SUCCESS;
+    }
+}
+
 PJ_DEF(pj_status_t) pj_cli_create(pj_cli_cfg *cfg,
                                   pj_cli_t **p_cli)
@@ -73,11 +114,22 @@
     pj_pool_t *pool;
     pj_cli_t *cli;
+    unsigned i;
+    char* cmd_xmls[] = {
+     "<CMD name='log' id='30000' sc='' desc='Change log level'>"
+     "  <ARGS>"
+     "    <ARG name='level' type='int' desc='Log level'/>"
+     "  </ARGS>"
+     "</CMD>",
+     "<CMD name='exit' id='30001' sc='' desc='Exit session'>"
+     "</CMD>",
+    };
 
     PJ_ASSERT_RETURN(cfg && cfg->pf && p_cli, PJ_EINVAL);
 
-    pool = pj_pool_create(cfg->pf, "cli", 1024, 1024, NULL);
+    pool = pj_pool_create(cfg->pf, "cli", PJ_CLI_POOL_SIZE, 
+                          PJ_CLI_POOL_INC, NULL);
+    if (!pool)
+        return PJ_ENOMEM;
     cli = PJ_POOL_ZALLOC_T(pool, struct pj_cli_t);
-    if (!cli)
-        return PJ_ENOMEM;
 
     pj_memcpy(&cli->cfg, cfg, sizeof(*cfg));
@@ -89,4 +141,13 @@
     cli->root.sub_cmd = PJ_POOL_ZALLOC_T(pool, pj_cli_cmd_spec);
     pj_list_init(cli->root.sub_cmd);
+
+    /* Register some standard commands. */
+    for (i = 0; i < sizeof(cmd_xmls)/sizeof(cmd_xmls[0]); i++) {
+        pj_str_t xml = pj_str(cmd_xmls[i]);
+
+        if (pj_cli_add_cmd_from_xml(cli, NULL, &xml, &cmd_handler, NULL) !=
+            PJ_SUCCESS)
+            TRACE_((THIS_FILE, "Failed to add command #%d", i));
+    }
 
     *p_cli = cli;
@@ -167,5 +228,5 @@
     pj_assert(sess);
 
-    if (sess->op->destroy)
+    if (sess->op && sess->op->destroy)
         (*sess->op->destroy)(sess);
 }
@@ -184,4 +245,6 @@
                                             pj_cli_cmd_spec *p_cmd)
 {
+    #define ERROR_(STATUS) \
+        do {status = STATUS; goto on_exit;} while(0)
     pj_pool_t *pool;
     pj_xml_node *root;
@@ -197,22 +260,17 @@
     /* Parse the xml */
     pool = pj_pool_create(cli->cfg.pf, "xml", 1024, 1024, NULL);
+    if (!pool)
+        return PJ_ENOMEM;
     root = pj_xml_parse(pool, xml->ptr, xml->slen);
     if (!root) {
 	TRACE_((THIS_FILE, "Error: unable to parse XML"));
-	status = PJ_EINVAL;
-        goto on_exit;
-    }
-
-    if (pj_stricmp2(&root->name, "CMD")) {
-	status = PJ_EINVAL;
-        goto on_exit;
-    }
+	ERROR_(PJ_CLI_EBADXML);
+    }
+
+    if (pj_stricmp2(&root->name, "CMD"))
+        ERROR_(PJ_EINVAL);
 
     /* Initialize the command spec */
     cmd = PJ_POOL_ZALLOC_T(cli->pool, struct pj_cli_cmd_spec);
-    if (!cmd) {
-        status = PJ_ENOMEM;
-        goto on_exit;
-    }
     
     /* Get the command attributes */
@@ -227,6 +285,5 @@
                             attr->value.slen, NULL))
             {
-                status = PJ_CLI_EBADNAME;
-                goto on_exit;
+                ERROR_(PJ_CLI_EBADNAME);
             }
 
@@ -267,6 +324,5 @@
             PJ_CATCH_ANY {
                 pj_scan_fini(&scanner);
-                status = PJ_GET_EXCEPTION();
-                goto on_exit;
+                ERROR_(PJ_GET_EXCEPTION());
             }
             PJ_END;
@@ -290,8 +346,6 @@
                     pj_cli_arg_spec *arg;
 
-                    if (cmd->arg_cnt >= PJ_CLI_MAX_ARGS) {
-                        status = PJ_CLI_ETOOMANYARGS;
-                        goto on_exit;
-                    }
+                    if (cmd->arg_cnt >= PJ_CLI_MAX_ARGS)
+                        ERROR_(PJ_CLI_ETOOMANYARGS);
                     arg = &args[cmd->arg_cnt];
                     pj_bzero(arg, sizeof(*arg));
@@ -323,16 +377,12 @@
     if (cmd->id == PJ_CLI_CMD_ID_GROUP) {
         /* Command group shouldn't have any shortcuts nor arguments */
-        if (!cmd->sc_cnt || !cmd->arg_cnt) {
-            status = PJ_EINVAL;
-            goto on_exit;
-        }
+        if (!cmd->sc_cnt || !cmd->arg_cnt)
+            ERROR_(PJ_EINVAL);
         cmd->sub_cmd = PJ_POOL_ALLOC_T(cli->pool, struct pj_cli_cmd_spec);
         pj_list_init(cmd->sub_cmd);
     }
 
-    if (!cmd->name.slen) {
-        status = PJ_CLI_EBADNAME;
-        goto on_exit;
-    }
+    if (!cmd->name.slen)
+        ERROR_(PJ_CLI_EBADNAME);
 
     if (cmd->arg_cnt) {
@@ -341,8 +391,4 @@
         cmd->arg = (pj_cli_arg_spec *)pj_pool_zalloc(cli->pool, cmd->arg_cnt *
                                                      sizeof(pj_cli_arg_spec));
-        if (!cmd->arg) {
-            status = PJ_ENOMEM;
-            goto on_exit;
-        }
         for (i = 0; i < cmd->arg_cnt; i++) {
             pj_strdup(cli->pool, &cmd->arg[i].name, &args[i].name);
@@ -357,12 +403,8 @@
         cmd->sc = (pj_str_t *)pj_pool_zalloc(cli->pool, cmd->sc_cnt *
                                              sizeof(pj_str_t));
-        if (!cmd->sc) {
-            status = PJ_ENOMEM;
-            goto on_exit;
-        }
         for (i = 0; i < cmd->sc_cnt; i++) {
             pj_strdup(cli->pool, &cmd->sc[i], &sc[i]);
-            pj_hash_set(cli->pool, cli->hash, cmd->sc[i].ptr, 
-                        cmd->sc[i].slen, 0, cmd);
+            pj_hash_set(cli->pool, cli->hash, sc[i].ptr, 
+                        sc[i].slen, 0, cmd);
         }
     }
@@ -384,4 +426,5 @@
 
     return status;
+#undef ERROR_
 }
 
@@ -406,4 +449,9 @@
     /* Parse the command line. */
     len = pj_ansi_strlen(cmdline);
+    if (len > 0 && cmdline[len - 1] == '\n') {
+        cmdline[--len] = 0;
+        if (len > 0 && cmdline[len - 1] == '\r')
+            cmdline[--len] = 0;
+    }
     pj_scan_init(&scanner, cmdline, len, PJ_SCAN_AUTOSKIP_WS, 
                  &on_syntax_error);
@@ -441,7 +489,5 @@
         
         if (!pj_scan_is_eof(&scanner)) {
-            pj_scan_get_newline(&scanner);
-            if (!pj_scan_is_eof(&scanner))
-                PJ_THROW(PJ_CLI_EINVARG);
+            PJ_THROW(PJ_CLI_EINVARG);
         }
     }
@@ -478,5 +524,4 @@
         info = &einfo;
     status = pj_cli_parse(sess, cmdline, &val, info);
-
     if (status != PJ_SUCCESS)
         return status;
Index: /pjproject/branches/projects/cli/pjlib-util/src/pjlib-util/cli_console.c
===================================================================
--- /pjproject/branches/projects/cli/pjlib-util/src/pjlib-util/cli_console.c	(revision 3230)
+++ /pjproject/branches/projects/cli/pjlib-util/src/pjlib-util/cli_console.c	(revision 3231)
@@ -22,4 +22,5 @@
 #include <pj/assert.h>
 #include <pj/errno.h>
+#include <pj/log.h>
 #include <pj/os.h>
 #include <pj/pool.h>
@@ -44,4 +45,14 @@
 };
 
+static void cli_console_write_log(pj_cli_front_end *fe, int level,
+		                  const char *data, int len)
+{
+    struct cli_console_fe * cfe = (struct cli_console_fe *)fe;
+
+    if (cfe->sess->log_level > level)
+        printf("%.*s", len, data);
+}
+
+
 static void cli_console_quit(pj_cli_front_end *fe, pj_cli_sess *req)
 {
@@ -65,4 +76,6 @@
     cli_console_quit(fe, NULL);
 
+    if (cfe->input_thread)
+        pj_thread_destroy(cfe->input_thread);
     pj_sem_destroy(cfe->thread_sem);
     pj_sem_destroy(cfe->input.sem);
@@ -90,9 +103,10 @@
 
     pool = pj_pool_create(pj_cli_get_param(cli)->pf, "console_fe",
-                          256, 256, NULL);
+                          PJ_CLI_CONSOLE_POOL_SIZE, PJ_CLI_CONSOLE_POOL_INC,
+                          NULL);
+    if (!pool)
+        return PJ_ENOMEM;
     sess = PJ_POOL_ZALLOC_T(pool, pj_cli_sess);
     fe = PJ_POOL_ZALLOC_T(pool, struct cli_console_fe);
-    if (!sess || !fe)
-        return PJ_ENOMEM;
 
     if (!param) {
@@ -102,7 +116,9 @@
     sess->fe = &fe->base;
     sess->log_level = param->log_level;
+    sess->op = PJ_POOL_ZALLOC_T(pool, struct pj_cli_sess_op);
+    fe->base.op = PJ_POOL_ZALLOC_T(pool, struct pj_cli_front_end_op);
     fe->base.cli = cli;
     fe->base.type = PJ_CLI_CONSOLE_FRONT_END;
-    fe->base.op = PJ_POOL_ZALLOC_T(pool, struct pj_cli_front_end_op);
+    fe->base.op->on_write_log = &cli_console_write_log;
     fe->base.op->on_quit = &cli_console_quit;
     fe->base.op->on_destroy = &cli_console_destroy;
@@ -128,5 +144,5 @@
         fgets(fe->input.buf, fe->input.maxlen, stdin);
         for (i = pj_ansi_strlen(fe->input.buf) - 1; i >= 0; i--) {
-            if (fe->input.buf[i] == '\n')
+            if (fe->input.buf[i] == '\n' || fe->input.buf[i] == '\r')
                 fe->input.buf[i] = 0;
             else
@@ -137,5 +153,4 @@
         pj_sem_wait(fe->thread_sem);
     }
-    pj_sem_post(fe->input.sem);
     fe->input_thread = NULL;
 
@@ -155,9 +170,10 @@
 
     if (!fe->input_thread) {
-        if (pj_thread_create(fe->pool, NULL, &readline_thread, fe,
-                             0, 0, &fe->input_thread) != PJ_SUCCESS)
-        {
-            return PJ_EUNKNOWN;
-        }
+        pj_status_t status;
+
+        status = pj_thread_create(fe->pool, NULL, &readline_thread, fe,
+                                  0, 0, &fe->input_thread);
+        if (status != PJ_SUCCESS)
+            return status;
     } else {
         /* Wake up readline thread */
@@ -166,5 +182,5 @@
 
     pj_sem_wait(fe->input.sem);
-    
-    return (fe->thread_quit ? PJ_CLI_EEXIT : PJ_SUCCESS);
+
+    return (pj_cli_is_quitting(fe->base.cli)? PJ_CLI_EEXIT : PJ_SUCCESS);
 }
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 3231)
+++ /pjproject/branches/projects/cli/pjlib-util/src/pjlib-util/cli_telnet.c	(revision 3231)
@@ -0,0 +1,536 @@
+/* $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 <pjlib-util/errno.h>
+
+#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
+
+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;
+};
+
+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;
+};
+
+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(struct 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(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);
+        } 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;
+}
+
+static pj_status_t telnet_sess_send2(struct cli_telnet_sess *sess,
+                                     const char *str, int len)
+{
+    pj_str_t s;
+
+    pj_strset(&s, (char *)str, len);
+    return telnet_sess_send(sess, &s);
+}
+
+static void cli_telnet_sess_destroy(pj_cli_sess *sess)
+{
+    struct cli_telnet_sess *tsess = (struct cli_telnet_sess *)sess;
+    pj_mutex_t *mutex = ((struct 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_mutex_destroy(tsess->smutex);
+    pj_activesock_close(tsess->asock);
+    pj_pool_release(tsess->pool);
+}
+
+static void cli_telnet_fe_write_log(pj_cli_front_end *fe, int level,
+		                    const char *data, int len)
+{
+    struct cli_telnet_fe * tfe = (struct cli_telnet_fe *)fe;
+    pj_cli_sess *sess;
+
+    pj_mutex_lock(tfe->mutex);
+
+    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);
+    }
+    
+    pj_mutex_unlock(tfe->mutex);
+}
+
+static void cli_telnet_fe_destroy(pj_cli_front_end *fe)
+{
+    struct cli_telnet_fe *tfe = (struct 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);
+    pj_mutex_destroy(tfe->mutex);
+    pj_pool_release(tfe->pool);
+}
+
+static int poll_worker_thread(void *p)
+{
+    struct cli_telnet_fe *fe = (struct 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)
+{
+    struct cli_telnet_sess *sess = (struct cli_telnet_sess *)
+                                    pj_activesock_get_user_data(asock);
+
+    PJ_UNUSED_ARG(op_key);
+
+    if (sent <= 0) {
+        pj_cli_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_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)
+{
+    struct cli_telnet_sess *sess = (struct 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;
+
+    PJ_UNUSED_ARG(size);
+    PJ_UNUSED_ARG(remainder);
+
+    if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+        pj_cli_end_session(&sess->base);
+        return PJ_FALSE;
+    }
+
+    if (tfe->is_quitting)
+        return PJ_FALSE;
+
+    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;
+        }
+
+    }
+
+    pj_mutex_unlock(sess->smutex);
+
+    return PJ_TRUE;
+
+on_exit:
+    pj_mutex_unlock(sess->smutex);
+    pj_cli_end_session(&sess->base);
+    return PJ_FALSE;
+}
+
+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)
+{
+    struct cli_telnet_fe *fe = (struct cli_telnet_fe *)
+                                pj_activesock_get_user_data(asock);
+    pj_status_t sstatus;
+    pj_pool_t *pool;
+    struct cli_telnet_sess *sess;
+    pj_activesock_cb asock_cb;
+
+    PJ_UNUSED_ARG(src_addr);
+    PJ_UNUSED_ARG(src_addr_len);
+
+    if (fe->is_quitting)
+        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, struct 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 = &cli_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;
+
+    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;
+    }
+
+    /* 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;
+    }
+
+    /* 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,
+					 const pj_cli_telnet_cfg *param,
+					 pj_cli_front_end **p_fe)
+{
+    struct cli_telnet_fe *fe;
+    pj_pool_t *pool;
+    pj_sock_t sock = PJ_INVALID_SOCKET;
+    pj_activesock_cb asock_cb;
+    pj_sockaddr addr;
+    pj_status_t sstatus;
+
+    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, struct 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 = &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;
+
+    if (!fe->cfg.ioqueue) {
+        /* Create own ioqueue if application doesn't supply one */
+        sstatus = pj_ioqueue_create(pool, 8, &fe->cfg.ioqueue);
+        if (sstatus != PJ_SUCCESS)
+            goto on_exit;
+        fe->own_ioqueue = PJ_TRUE;
+
+        /* Create our own worker thread */
+        sstatus = pj_thread_create(pool, "worker_telnet_fe",
+                                   &poll_worker_thread, fe, 0, 0,
+                                   &fe->worker_thread);
+        if (sstatus != PJ_SUCCESS)
+            goto on_exit;
+    }
+
+    sstatus = pj_mutex_create_recursive(pool, "mutex_telnet_fe", &fe->mutex);
+    if (sstatus != PJ_SUCCESS)
+        goto on_exit;
+
+    /* Start telnet daemon */
+    sstatus = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, 
+                             &sock);
+    if (sstatus != PJ_SUCCESS)
+        goto on_exit;
+
+    pj_sockaddr_init(pj_AF_INET(), &addr, NULL, fe->cfg.port);
+
+    sstatus = pj_sock_bind(sock, &addr, sizeof(addr));
+    if (sstatus != PJ_SUCCESS)
+        goto on_exit;
+
+    sstatus = pj_sock_listen(sock, 4);
+    if (sstatus != PJ_SUCCESS)
+        goto on_exit;
+
+    pj_bzero(&asock_cb, sizeof(asock_cb));
+    asock_cb.on_accept_complete = &telnet_fe_on_accept;
+    sstatus = pj_activesock_create(pool, sock, pj_SOCK_STREAM(),
+                                   NULL, fe->cfg.ioqueue,
+		                   &asock_cb, fe, &fe->asock);
+    if (sstatus != PJ_SUCCESS)
+        goto on_exit;
+
+    sstatus = pj_activesock_start_accept(fe->asock, pool);
+    if (sstatus != 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->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(pool);
+    return sstatus;
+}
Index: /pjproject/branches/projects/cli/pjlib-util/src/pjlib-util/errno.c
===================================================================
--- /pjproject/branches/projects/cli/pjlib-util/src/pjlib-util/errno.c	(revision 3230)
+++ /pjproject/branches/projects/cli/pjlib-util/src/pjlib-util/errno.c	(revision 3231)
@@ -96,4 +96,13 @@
     PJ_BUILD_ERR( PJLIB_UTIL_EHTTPINSBUF,	"Insufficient buffer"),
     PJ_BUILD_ERR( PJLIB_UTIL_EHTTPLOST,	        "Connection lost"),
+
+    /* CLI */
+    PJ_BUILD_ERR( PJ_CLI_EEXIT,	                "Exit current session"),
+    PJ_BUILD_ERR( PJ_CLI_EMISSINGARG,	        "Missing argument"),
+    PJ_BUILD_ERR( PJ_CLI_ETOOMANYARGS,	        "Too many arguments"),
+    PJ_BUILD_ERR( PJ_CLI_EINVARG,	        "Invalid argument"),
+    PJ_BUILD_ERR( PJ_CLI_EBADNAME,	        "Command name already exists"),
+    PJ_BUILD_ERR( PJ_CLI_EBADXML,	        "Invalid XML format"),
+    PJ_BUILD_ERR( PJ_CLI_ETELNETLOST,	        "Connection lost"),
 };
 #endif	/* PJ_HAS_ERROR_STRING */
Index: /pjproject/branches/projects/cli/pjsip-apps/build/Samples-vc.mak
===================================================================
--- /pjproject/branches/projects/cli/pjsip-apps/build/Samples-vc.mak	(revision 3230)
+++ /pjproject/branches/projects/cli/pjsip-apps/build/Samples-vc.mak	(revision 3231)
@@ -61,4 +61,5 @@
 SAMPLES = $(BINDIR)\auddemo.exe \
 	  $(BINDIR)\aectest.exe \
+	  $(BINDIR)\clidemo.exe \
 	  $(BINDIR)\confsample.exe \
 	  $(BINDIR)\confbench.exe \
Index: /pjproject/branches/projects/cli/pjsip-apps/build/Samples.mak
===================================================================
--- /pjproject/branches/projects/cli/pjsip-apps/build/Samples.mak	(revision 3230)
+++ /pjproject/branches/projects/cli/pjsip-apps/build/Samples.mak	(revision 3231)
@@ -16,4 +16,5 @@
 SAMPLES := auddemo \
 	   aectest \
+	   clidemo \
 	   confsample \
 	   encdec \
Index: /pjproject/branches/projects/cli/pjsip-apps/build/samples.vcproj
===================================================================
--- /pjproject/branches/projects/cli/pjsip-apps/build/samples.vcproj	(revision 3230)
+++ /pjproject/branches/projects/cli/pjsip-apps/build/samples.vcproj	(revision 3231)
@@ -1599,4 +1599,8 @@
 			</File>
 			<File
+				RelativePath="..\src\samples\clidemo.c"
+				>
+			</File>
+			<File
 				RelativePath="..\src\samples\confbench.c"
 				>
Index: /pjproject/branches/projects/cli/pjsip-apps/src/samples/clidemo.c
===================================================================
--- /pjproject/branches/projects/cli/pjsip-apps/src/samples/clidemo.c	(revision 3231)
+++ /pjproject/branches/projects/cli/pjsip-apps/src/samples/clidemo.c	(revision 3231)
@@ -0,0 +1,222 @@
+/* $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
+ */
+
+/*
+ * Sample CLI application
+ */
+#include <pjlib-util/cli.h>
+#include <pjlib-util/cli_imp.h>
+#include <pjlib-util/cli_console.h>
+#include <pjlib-util/cli_telnet.h>
+#include <pjlib-util/errno.h>
+#include <pjlib.h>
+
+#define THIS_FILE	"clidemo.c"
+
+struct cmd_xml_t {
+    char * xml;
+    pj_cli_cmd_handler handler;
+};
+
+/*
+ * Declaration of system specific main loop, which will be defined in
+ * a separate file.
+ */
+pj_status_t app_main(pj_cli_t *cli);
+
+#define print_msg(arg) \
+    do { \
+        unsigned d = pj_log_get_decor(); \
+        pj_log_set_decor(0); \
+        PJ_LOG(1, arg); \
+        pj_log_set_decor(d); \
+    } while (0)
+
+static pj_cli_t *cli = NULL;
+
+/* Handler for sayhello command */
+static pj_status_t sayhello(pj_cli_cmd_val *cval)
+{
+    print_msg(("", "Hello %.*s!\r\n", 
+              (int)cval->argv[1].slen, cval->argv[1].ptr));
+    return PJ_SUCCESS;
+}
+
+/* Handler for saybye command */
+static pj_status_t saybye(pj_cli_cmd_val *cval)
+{
+    print_msg(("", "Bye %.*s!\r\n",
+              (int)cval->argv[1].slen, cval->argv[1].ptr));
+    return PJ_SUCCESS;
+}
+
+/* Handler for say command */
+static pj_status_t say(pj_cli_cmd_val *cval)
+{
+    print_msg(("", "%.*s %.*s\r\n",
+              (int)cval->argv[1].slen, cval->argv[1].ptr,
+              (int)cval->argv[2].slen, cval->argv[2].ptr));
+    return PJ_SUCCESS;
+}
+
+static pj_status_t quit(pj_cli_cmd_val *cval)
+{
+    PJ_UNUSED_ARG(cval);
+    pj_cli_end_session(cval->sess);
+
+    return PJ_CLI_EEXIT;
+}
+
+static struct cmd_xml_t cmd_xmls[] = {
+    {"<CMD name='sayhello' id='1' sc='  ,h , ,, sh  ,' desc='Will say hello'>"
+     "  <ARGS>"
+     "    <ARG name='whom' type='text' desc='Whom to say hello to'/>"
+     "  </ARGS>"
+     "</CMD>",
+     &sayhello},
+    {"<CMD name='saybye' id='2' sc='b,sb' desc='Will say bye'>"
+     "  <ARGS>"
+     "    <ARG name='whom' type='text' desc='Whom to say bye to'/>"
+     "  </ARGS>"
+     "</CMD>",
+     &saybye},
+    {"<CMD name=' say ' id='3' sc='s' desc='Will say something'>"
+     "  <ARGS>"
+     "    <ARG name='msg' type='text' desc='Message to say'/>"
+     "    <ARG name='whom' type='text' desc='Whom to say to'/>"
+     "  </ARGS>"
+     "</CMD>",
+     &say},
+    {"<CMD name='quit' id='999' sc='q' desc='Quit the application'>"
+     "</CMD>",
+     &quit},
+};
+
+static void log_writer(int level, const char *buffer, int len)
+{
+    if (cli)
+        pj_cli_write_log(cli, level, buffer, len);
+}
+
+int main()
+{
+    pj_caching_pool cp;
+    pj_cli_cfg cli_cfg;
+    pj_cli_telnet_cfg tcfg;
+    pj_str_t xml;
+    pj_status_t status;
+    int i;
+
+    pj_init();
+    pj_caching_pool_init(&cp, NULL, 0);
+    pjlib_util_init();
+
+    pj_log_set_log_func(&log_writer);
+
+    /*
+     * Create CLI app.
+     */
+    pj_cli_cfg_default(&cli_cfg);
+    cli_cfg.pf = &cp.factory;
+    cli_cfg.name = pj_str("mycliapp");
+    cli_cfg.title = pj_str("My CLI Application");
+
+    status = pj_cli_create(&cli_cfg, &cli);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /*
+     * Register some commands.
+     */
+    for (i = 0; i < sizeof(cmd_xmls)/sizeof(cmd_xmls[0]); i++) {
+        xml = pj_str(cmd_xmls[i].xml);
+        status = pj_cli_add_cmd_from_xml(cli, NULL, &xml, 
+                                         cmd_xmls[i].handler, NULL);
+        if (status != PJ_SUCCESS)
+	    goto on_return;
+    }
+
+    /*
+     * Start telnet daemon
+     */
+    pj_cli_telnet_cfg_default(&tcfg);
+//    tcfg.passwd = pj_str("pjsip");
+    status = pj_cli_telnet_create(cli, &tcfg, NULL);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /*
+     * Run the system specific main loop.
+     */
+    status = app_main(cli);
+
+on_return:
+
+    /*
+     * Destroy
+     */
+    pj_cli_destroy(cli);
+    cli = NULL;
+    pj_caching_pool_destroy(&cp);
+    pj_shutdown();
+
+    return (status != PJ_SUCCESS ? 1 : 0);
+}
+
+
+/*xxxxxxxxxxxxxxxxxxxxxxxxxxxxx main_console.c xxxxxxxxxxxxxxxxxxxxxxxxxxxx */
+/*
+ * Simple implementation of app_main() for console targets
+ */
+pj_status_t app_main(pj_cli_t *cli)
+{
+    pj_status_t status;
+    pj_cli_sess *sess;
+
+    /*
+     * Create the console front end
+     */
+    status = pj_cli_console_create(cli, NULL, &sess, NULL);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /*
+     * Main loop.
+     */
+    for (;;) {
+	char cmdline[PJ_CLI_MAX_CMDBUF];
+        pj_status_t status;
+
+        status = pj_cli_console_readline(sess, cmdline, sizeof(cmdline));
+	if (status != PJ_SUCCESS)
+	    break;
+
+//        pj_ansi_strcpy(cmdline, "sayhello {Teluu Inc.}");
+	status = pj_cli_exec(sess, cmdline, NULL);
+	if (status == PJ_CLI_EEXIT) {
+	    /* exit is called */
+	    break;
+	} else if (status != PJ_SUCCESS) {
+	    /* Something wrong with the cmdline */
+	    PJ_PERROR(1,(THIS_FILE, status, "Exec error"));
+	}
+    }
+
+    return PJ_SUCCESS;
+}
