[GLUE] Rsync SAMBA_3_2_0 SVN r25598 in order to create the v3-2-test branch.
[samba.git] / source / client / client.c
index d0bc01655ff852c282548f6cb8abc9d308e5f094..171d413b0ae546031058dc6485ff9fecf7126df6 100644 (file)
@@ -1,13 +1,14 @@
 /* 
    Unix SMB/CIFS implementation.
    SMB client
-   Copyright (C) Andrew Tridgell 1994-1998
-   Copyright (C) Simo Sorce 2001-2002
-   Copyright (C) Jelmer Vernooij 2003
+   Copyright (C) Andrew Tridgell          1994-1998
+   Copyright (C) Simo Sorce               2001-2002
+   Copyright (C) Jelmer Vernooij          2003
+   Copyright (C) Gerald (Jerry) Carter    2004
    
    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
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-#define NO_SYSLOG
-
 #include "includes.h"
-#include "../client/client_proto.h"
+#include "client/client_proto.h"
+#include "include/rpc_client.h"
 #ifndef REGISTER
 #define REGISTER 0
 #endif
 
-struct cli_state *cli;
+extern BOOL AllowDebugChange;
+extern BOOL override_logfile;
+extern char tar_type;
 extern BOOL in_client;
 static int port = 0;
 pstring cur_dir = "\\";
@@ -36,20 +37,22 @@ static pstring cd_path = "";
 static pstring service;
 static pstring desthost;
 static pstring username;
-static pstring password;
-static BOOL use_kerberos;
-static BOOL got_pass;
+static pstring calling_name;
 static BOOL grepable=False;
 static char *cmdstr = NULL;
 
 static int io_bufsize = 64512;
 
 static int name_type = 0x20;
-static int max_protocol = PROTOCOL_NT1;
+extern int max_protocol;
 
-static int process_tok(fstring tok);
+static int process_tok(pstring tok);
 static int cmd_help(void);
 
+static TALLOC_CTX *ctx;
+#define CREATE_ACCESS_READ READ_CONTROL_ACCESS
+static pstring cwd;
+
 /* 30 second timeout on most commands */
 #define CLIENT_TIMEOUT (30*1000)
 #define SHORT_TIMEOUT (5*1000)
@@ -61,7 +64,6 @@ time_t newer_than = 0;
 static int archive_level = 0;
 
 static BOOL translation = False;
-
 static BOOL have_ip;
 
 /* clitar bits insert */
@@ -73,9 +75,8 @@ extern BOOL tar_reset;
 
 static BOOL prompt = True;
 
-static int printmode = 1;
-
 static BOOL recurse = False;
+static BOOL showacls = False;
 BOOL lowercase = False;
 
 static struct in_addr dest_ip;
@@ -97,10 +98,12 @@ static unsigned int put_total_time_ms = 0;
 /* totals globals */
 static double dir_total;
 
-#define USENMB
+/* root cli_state connection */
+
+struct cli_state *cli;
 
-/* some forward declarations */
-static struct cli_state *do_connect(const char *server, const char *share);
+static char CLI_DIRSEP_CHAR = '\\';
+static char CLI_DIRSEP_STR[] = { '\\', '\0' };
 
 /****************************************************************************
  Write to a local file with CR/LF->LF translation if appropriate. Return the 
@@ -190,6 +193,10 @@ static void send_message(void)
                        msg[l] = c;   
                }
 
+               if ((total_len > 0) && (strlen(msg) == 0)) {
+                       break;
+               }
+
                if (!cli_message_text(cli, msg, l, grp_id)) {
                        d_printf("SMBsendtxt failed (%s)\n",cli_errstr(cli));
                        return;
@@ -216,9 +223,16 @@ static void send_message(void)
 static int do_dskattr(void)
 {
        int total, bsize, avail;
+       struct cli_state *targetcli;
+       pstring targetpath;
+
+       if ( !cli_resolve_path( "", cli, cur_dir, &targetcli, targetpath ) ) {
+               d_printf("Error in dskattr: %s\n", cli_errstr(cli));
+               return 1;
+       }
 
-       if (!cli_dskattr(cli, &bsize, &total, &avail)) {
-               d_printf("Error in dskattr: %s\n",cli_errstr(cli)); 
+       if (!cli_dskattr(targetcli, &bsize, &total, &avail)) {
+               d_printf("Error in dskattr: %s\n",cli_errstr(targetcli)); 
                return 1;
        }
 
@@ -248,34 +262,72 @@ static int do_cd(char *newdir)
        char *p = newdir;
        pstring saved_dir;
        pstring dname;
+       pstring targetpath;
+       struct cli_state *targetcli;
+       SMB_STRUCT_STAT sbuf;
+       uint32 attributes;
+       int ret = 1;
       
        dos_format(newdir);
 
-       /* Save the current directory in case the
-          new directory is invalid */
+       /* Save the current directory in case the new directory is invalid */
+
        pstrcpy(saved_dir, cur_dir);
-       if (*p == '\\')
+
+       if (*p == CLI_DIRSEP_CHAR) {
                pstrcpy(cur_dir,p);
-       else
+       } else {
                pstrcat(cur_dir,p);
-       if (*(cur_dir+strlen(cur_dir)-1) != '\\') {
-               pstrcat(cur_dir, "\\");
+               if ((cur_dir[0] != '\0') && (*(cur_dir+strlen(cur_dir)-1) != CLI_DIRSEP_CHAR)) {
+                       pstrcat(cur_dir, CLI_DIRSEP_STR);
+               }
+       }
+       
+       clean_name(cur_dir);
+       pstrcpy( dname, cur_dir );
+       
+       if ( !cli_resolve_path( "", cli, dname, &targetcli, targetpath ) ) {
+               d_printf("cd %s: %s\n", dname, cli_errstr(cli));
+               pstrcpy(cur_dir,saved_dir);
+               goto out;
+       }
+
+       if (strequal(targetpath,CLI_DIRSEP_STR )) {
+               return 0;
        }
-       dos_clean_name(cur_dir);
-       pstrcpy(dname,cur_dir);
-       pstrcat(cur_dir,"\\");
-       dos_clean_name(cur_dir);
+               
+       /* Use a trans2_qpathinfo to test directories for modern servers.
+          Except Win9x doesn't support the qpathinfo_basic() call..... */ 
        
-       if (!strequal(cur_dir,"\\")) {
-               if (!cli_chkpath(cli, dname)) {
-                       d_printf("cd %s: %s\n", dname, cli_errstr(cli));
+       if ( targetcli->protocol >  PROTOCOL_LANMAN2 && !targetcli->win95 ) {
+               if ( !cli_qpathinfo_basic( targetcli, targetpath, &sbuf, &attributes ) ) {
+                       d_printf("cd %s: %s\n", dname, cli_errstr(targetcli));
+                       pstrcpy(cur_dir,saved_dir);
+                       goto out;
+               }
+               
+               if ( !(attributes&FILE_ATTRIBUTE_DIRECTORY) ) {
+                       d_printf("cd %s: not a directory\n", dname);
+                       pstrcpy(cur_dir,saved_dir);
+                       goto out;
+               }               
+       } else {
+               pstrcat( targetpath, CLI_DIRSEP_STR );
+               clean_name( targetpath );
+               
+               if ( !cli_chkpath(targetcli, targetpath) ) {
+                       d_printf("cd %s: %s\n", dname, cli_errstr(targetcli));
                        pstrcpy(cur_dir,saved_dir);
+                       goto out;
                }
        }
+
+       ret = 0;
+
+out:
        
        pstrcpy(cd_path,cur_dir);
-
-       return 0;
+       return ret;
 }
 
 /****************************************************************************
@@ -284,9 +336,9 @@ static int do_cd(char *newdir)
 
 static int cmd_cd(void)
 {
-       fstring buf;
+       pstring buf;
        int rc = 0;
-
+               
        if (next_token_nr(NULL,buf,NULL,sizeof(buf)))
                rc = do_cd(buf);
        else
@@ -295,6 +347,19 @@ static int cmd_cd(void)
        return rc;
 }
 
+/****************************************************************************
+ Change directory.
+****************************************************************************/
+
+static int cmd_cd_oneup(void)
+{
+       pstring buf;
+
+       pstrcpy(buf, "..");
+       return do_cd(buf);
+}
+
+
 /*******************************************************************
  Decide if a file should be operated on.
 ********************************************************************/
@@ -310,7 +375,7 @@ static BOOL do_this_one(file_info *finfo)
                return False;
        }
 
-       if (newer_than && finfo->mtime < newer_than) {
+       if (newer_than && finfo->mtime_ts.tv_sec < newer_than) {
                DEBUG(3,("newer_than %s failed\n", finfo->name));
                return(False);
        }
@@ -330,13 +395,47 @@ static BOOL do_this_one(file_info *finfo)
 static void display_finfo(file_info *finfo)
 {
        if (do_this_one(finfo)) {
-               time_t t = finfo->mtime; /* the time is assumed to be passed as GMT */
-               d_printf("  %-30s%7.7s %8.0f  %s",
-                        finfo->name,
-                        attrib_string(finfo->mode),
-                        (double)finfo->size,
-                        asctime(LocalTime(&t)));
-               dir_total += finfo->size;
+               time_t t = finfo->mtime_ts.tv_sec; /* the time is assumed to be passed as GMT */
+               if (!showacls) {
+                       d_printf("  %-30s%7.7s %8.0f  %s",
+                                finfo->name,
+                                attrib_string(finfo->mode),
+                               (double)finfo->size,
+                               time_to_asc(t));
+                       dir_total += finfo->size;
+               } else {
+                       pstring afname;
+                       int fnum;
+
+                       /* skip if this is . or .. */
+                       if ( strequal(finfo->name,"..") || strequal(finfo->name,".") )
+                               return;
+                       /* create absolute filename for cli_nt_create() FIXME */
+                       pstrcpy( afname, cwd);
+                       pstrcat( afname, CLI_DIRSEP_STR);
+                       pstrcat( afname, finfo->name);
+                       /* print file meta date header */
+                       d_printf( "FILENAME:%s\n", afname);
+                       d_printf( "MODE:%s\n", attrib_string(finfo->mode));
+                       d_printf( "SIZE:%.0f\n", (double)finfo->size);
+                       d_printf( "MTIME:%s", time_to_asc(t));
+                       fnum = cli_nt_create(finfo->cli, afname, CREATE_ACCESS_READ);
+                       if (fnum == -1) {
+                               DEBUG( 0, ("display_finfo() Failed to open %s: %s\n",
+                                       afname,
+                                       cli_errstr( finfo->cli)));
+                       } else {
+                               SEC_DESC *sd = NULL;
+                               sd = cli_query_secdesc(finfo->cli, fnum, ctx);
+                               if (!sd) {
+                                       DEBUG( 0, ("display_finfo() failed to "
+                                               "get security descriptor: %s",
+                                               cli_errstr( finfo->cli)));
+                               } else {
+                                       display_sec_desc(sd);
+                               }
+                       }
+               }
        }
 }
 
@@ -387,7 +486,7 @@ static void init_do_list_queue(void)
 {
        reset_do_list_queue();
        do_list_queue_size = 1024;
-       do_list_queue = malloc(do_list_queue_size);
+       do_list_queue = (char *)SMB_MALLOC(do_list_queue_size);
        if (do_list_queue == 0) { 
                d_printf("malloc fail for size %d\n",
                         (int)do_list_queue_size);
@@ -403,7 +502,14 @@ static void adjust_do_list_queue(void)
         * If the starting point of the queue is more than half way through,
         * move everything toward the beginning.
         */
-       if (do_list_queue && (do_list_queue_start == do_list_queue_end)) {
+
+       if (do_list_queue == NULL) {
+               DEBUG(4,("do_list_queue is empty\n"));
+               do_list_queue_start = do_list_queue_end = 0;
+               return;
+       }
+               
+       if (do_list_queue_start == do_list_queue_end) {
                DEBUG(4,("do_list_queue is empty\n"));
                do_list_queue_start = do_list_queue_end = 0;
                *do_list_queue = '\0';
@@ -419,19 +525,17 @@ static void adjust_do_list_queue(void)
 
 static void add_to_do_list_queue(const char* entry)
 {
-       char *dlq;
        long new_end = do_list_queue_end + ((long)strlen(entry)) + 1;
        while (new_end > do_list_queue_size) {
                do_list_queue_size *= 2;
                DEBUG(4,("enlarging do_list_queue to %d\n",
                         (int)do_list_queue_size));
-               dlq = Realloc(do_list_queue, do_list_queue_size);
-               if (! dlq) {
+               do_list_queue = (char *)SMB_REALLOC(do_list_queue, do_list_queue_size);
+               if (! do_list_queue) {
                        d_printf("failure enlarging do_list_queue to %d bytes\n",
                                 (int)do_list_queue_size);
                        reset_do_list_queue();
                } else {
-                       do_list_queue = dlq;
                        memset(do_list_queue + do_list_queue_size / 2,
                               0, do_list_queue_size / 2);
                }
@@ -469,8 +573,16 @@ static int do_list_queue_empty(void)
  A helper for do_list.
 ****************************************************************************/
 
-static void do_list_helper(file_info *f, const char *mask, void *state)
+static void do_list_helper(const char *mntpoint, file_info *f, const char *mask, void *state)
 {
+       char *dir_end;
+
+       /* save the directory */
+       pstrcpy( f->dir, mask );
+       if ( (dir_end = strrchr( f->dir, CLI_DIRSEP_CHAR )) != NULL ) {
+               *dir_end = '\0';
+       }
+
        if (f->mode & aDIR) {
                if (do_list_dirs && do_this_one(f)) {
                        do_list_fn(f);
@@ -486,13 +598,15 @@ static void do_list_helper(file_info *f, const char *mask, void *state)
                                return;
                        }
 
-                       pstrcpy(mask2, mask);
-                       p = strrchr_m(mask2,'\\');
+                       pstrcpy(mask2, mntpoint);
+                       pstrcat(mask2, mask);
+                       p = strrchr_m(mask2,CLI_DIRSEP_CHAR);
                        if (!p)
                                return;
                        p[1] = 0;
                        pstrcat(mask2, f->name);
-                       pstrcat(mask2,"\\*");
+                       pstrcat(mask2,CLI_DIRSEP_STR);
+                       pstrcat(mask2,"*");
                        add_to_do_list_queue(mask2);
                }
                return;
@@ -510,6 +624,8 @@ static void do_list_helper(file_info *f, const char *mask, void *state)
 void do_list(const char *mask,uint16 attribute,void (*fn)(file_info *),BOOL rec, BOOL dirs)
 {
        static int in_do_list = 0;
+       struct cli_state *targetcli;
+       pstring targetpath;
 
        if (in_do_list && rec) {
                fprintf(stderr, "INTERNAL ERROR: do_list called recursively when the recursive flag is true\n");
@@ -536,28 +652,46 @@ void do_list(const char *mask,uint16 attribute,void (*fn)(file_info *),BOOL rec,
                         */
                        pstring head;
                        pstrcpy(head, do_list_queue_head());
-                       cli_list(cli, head, attribute, do_list_helper, NULL);
+                       
+                       /* check for dfs */
+                       
+                       if ( !cli_resolve_path( "", cli, head, &targetcli, targetpath ) ) {
+                               d_printf("do_list: [%s] %s\n", head, cli_errstr(cli));
+                               remove_do_list_queue_head();
+                               continue;
+                       }
+                       
+                       cli_list(targetcli, targetpath, attribute, do_list_helper, NULL);
                        remove_do_list_queue_head();
                        if ((! do_list_queue_empty()) && (fn == display_finfo)) {
                                char* next_file = do_list_queue_head();
                                char* save_ch = 0;
                                if ((strlen(next_file) >= 2) &&
                                    (next_file[strlen(next_file) - 1] == '*') &&
-                                   (next_file[strlen(next_file) - 2] == '\\')) {
+                                   (next_file[strlen(next_file) - 2] == CLI_DIRSEP_CHAR)) {
                                        save_ch = next_file +
                                                strlen(next_file) - 2;
                                        *save_ch = '\0';
+                                       if (showacls) /* cwd is only used if showacls is on */
+                                               pstrcpy( cwd, next_file);
                                }
-                               d_printf("\n%s\n",next_file);
+                               if (!showacls) /* don't disturbe the showacls output */
+                                       d_printf("\n%s\n",next_file);
                                if (save_ch) {
-                                       *save_ch = '\\';
+                                       *save_ch = CLI_DIRSEP_CHAR;
                                }
                        }
                }
        } else {
-               if (cli_list(cli, mask, attribute, do_list_helper, NULL) == -1) {
-                       d_printf("%s listing %s\n", cli_errstr(cli), mask);
+               /* check for dfs */
+                       
+               if ( cli_resolve_path( "", cli, mask, &targetcli, targetpath ) ) {
+                       if (cli_list(targetcli, targetpath, attribute, do_list_helper, NULL) == -1) 
+                               d_printf("%s listing %s\n", cli_errstr(targetcli), targetpath);
                }
+               else
+                       d_printf("do_list: [%s] %s\n", mask, cli_errstr(cli));
+               
        }
 
        in_do_list = 0;
@@ -572,29 +706,34 @@ static int cmd_dir(void)
 {
        uint16 attribute = aDIR | aSYSTEM | aHIDDEN;
        pstring mask;
-       fstring buf;
+       pstring buf;
        char *p=buf;
        int rc;
        
        dir_total = 0;
-       if (strcmp(cur_dir, "\\") != 0) {
+       if (strcmp(cur_dir, CLI_DIRSEP_STR) != 0) {
                pstrcpy(mask,cur_dir);
-               if(mask[strlen(mask)-1]!='\\')
-                       pstrcat(mask,"\\");
+               if ((mask[0] != '\0') && (mask[strlen(mask)-1]!=CLI_DIRSEP_CHAR))
+                       pstrcat(mask,CLI_DIRSEP_STR);
        } else {
-               *mask = '\0';
+               pstrcpy(mask, CLI_DIRSEP_STR);
        }
        
        if (next_token_nr(NULL,buf,NULL,sizeof(buf))) {
                dos_format(p);
-               if (*p == '\\')
-                       pstrcpy(mask,p);
+               if (*p == CLI_DIRSEP_CHAR)
+                       pstrcpy(mask,p + 1);
                else
                        pstrcat(mask,p);
        } else {
                pstrcat(mask,"*");
        }
 
+       if (showacls) {
+               /* cwd is only used if showacls is on */
+               pstrcpy(cwd, cur_dir);
+       }
+
        do_list(mask, attribute, display_finfo, recurse, True);
 
        rc = do_dskattr();
@@ -612,18 +751,18 @@ static int cmd_du(void)
 {
        uint16 attribute = aDIR | aSYSTEM | aHIDDEN;
        pstring mask;
-       fstring buf;
+       pstring buf;
        char *p=buf;
        int rc;
        
        dir_total = 0;
        pstrcpy(mask,cur_dir);
-       if(mask[strlen(mask)-1]!='\\')
-               pstrcat(mask,"\\");
+       if ((mask[0] != '\0') && (mask[strlen(mask)-1]!=CLI_DIRSEP_CHAR))
+               pstrcat(mask,CLI_DIRSEP_STR);
        
        if (next_token_nr(NULL,buf,NULL,sizeof(buf))) {
                dos_format(p);
-               if (*p == '\\')
+               if (*p == CLI_DIRSEP_CHAR)
                        pstrcpy(mask,p);
                else
                        pstrcat(mask,p);
@@ -640,6 +779,26 @@ static int cmd_du(void)
        return rc;
 }
 
+static int cmd_echo(void)
+{
+       fstring num;
+       pstring data;
+
+       if (!next_token_nr(NULL, num, NULL, sizeof(num))
+           || !next_token_nr(NULL, data, NULL, sizeof(data))) {
+               d_printf("echo <num> <data>\n");
+               return 1;
+       }
+
+       if (!cli_echo(cli, atoi(num), (uint8 *)data, strlen(data))) {
+               d_printf("echo failed: %s\n",
+                        nt_errstr(cli_get_nt_error(cli)));
+               return 1;
+       }
+
+       return 0;
+}
+
 /****************************************************************************
  Get a file from rname to lname
 ****************************************************************************/
@@ -652,18 +811,26 @@ static int do_get(char *rname, char *lname, BOOL reget)
        struct timeval tp_start;
        int read_size = io_bufsize;
        uint16 attr;
-       size_t size;
+       SMB_OFF_T size;
        off_t start = 0;
        off_t nread = 0;
        int rc = 0;
+       struct cli_state *targetcli;
+       pstring targetname;
 
-       GetTimeOfDay(&tp_start);
 
        if (lowercase) {
                strlower_m(lname);
        }
 
-       fnum = cli_open(cli, rname, O_RDONLY, DENY_NONE);
+       if ( !cli_resolve_path( "", cli, rname, &targetcli, targetname ) ) {
+               d_printf("Failed to open %s: %s\n", rname, cli_errstr(cli));
+               return 1;
+       }
+
+       GetTimeOfDay(&tp_start);
+       
+       fnum = cli_open(targetcli, targetname, O_RDONLY, DENY_NONE);
 
        if (fnum == -1) {
                d_printf("%s opening remote file %s\n",cli_errstr(cli),rname);
@@ -693,25 +860,25 @@ static int do_get(char *rname, char *lname, BOOL reget)
        }
 
 
-       if (!cli_qfileinfo(cli, fnum, 
+       if (!cli_qfileinfo(targetcli, fnum, 
                           &attr, &size, NULL, NULL, NULL, NULL, NULL) &&
-           !cli_getattrE(cli, fnum, 
+           !cli_getattrE(targetcli, fnum, 
                          &attr, &size, NULL, NULL, NULL)) {
-               d_printf("getattrib: %s\n",cli_errstr(cli));
+               d_printf("getattrib: %s\n",cli_errstr(targetcli));
                return 1;
        }
 
-       DEBUG(2,("getting file %s of size %.0f as %s ", 
+       DEBUG(1,("getting file %s of size %.0f as %s ", 
                 rname, (double)size, lname));
 
-       if(!(data = (char *)malloc(read_size))) { 
+       if(!(data = (char *)SMB_MALLOC(read_size))) { 
                d_printf("malloc fail for size %d\n", read_size);
-               cli_close(cli, fnum);
+               cli_close(targetcli, fnum);
                return 1;
        }
 
        while (1) {
-               int n = cli_read(cli, fnum, data, nread + start, read_size);
+               int n = cli_read(targetcli, fnum, data, nread + start, read_size);
 
                if (n <= 0)
                        break;
@@ -734,7 +901,7 @@ static int do_get(char *rname, char *lname, BOOL reget)
 
        SAFE_FREE(data);
        
-       if (!cli_close(cli, fnum)) {
+       if (!cli_close(targetcli, fnum)) {
                d_printf("Error %s closing remote file\n",cli_errstr(cli));
                rc = 1;
        }
@@ -758,7 +925,7 @@ static int do_get(char *rname, char *lname, BOOL reget)
                get_total_time_ms += this_time;
                get_total_size += nread;
                
-               DEBUG(2,("(%3.1f kb/s) (average %3.1f kb/s)\n",
+               DEBUG(1,("(%3.1f kb/s) (average %3.1f kb/s)\n",
                         nread / (1.024*this_time + 1.0e-4),
                         get_total_size / (1.024*get_total_time_ms)));
        }
@@ -777,7 +944,7 @@ static int cmd_get(void)
        char *p;
 
        pstrcpy(rname,cur_dir);
-       pstrcat(rname,"\\");
+       pstrcat(rname,CLI_DIRSEP_STR);
        
        p = rname + strlen(rname);
        
@@ -786,7 +953,7 @@ static int cmd_get(void)
                return 1;
        }
        pstrcpy(lname,p);
-       dos_clean_name(rname);
+       clean_name(rname);
        
        next_token_nr(NULL,lname,NULL,sizeof(lname));
        
@@ -833,7 +1000,7 @@ static void do_mget(file_info *finfo)
        pstrcpy(saved_curdir,cur_dir);
 
        pstrcat(cur_dir,finfo->name);
-       pstrcat(cur_dir,"\\");
+       pstrcat(cur_dir,CLI_DIRSEP_STR);
 
        unix_format(finfo->name);
        if (lowercase)
@@ -866,13 +1033,13 @@ static void do_mget(file_info *finfo)
 
 static int cmd_more(void)
 {
-       fstring rname,lname,pager_cmd;
+       pstring rname,lname,pager_cmd;
        char *pager;
        int fd;
        int rc = 0;
 
-       fstrcpy(rname,cur_dir);
-       fstrcat(rname,"\\");
+       pstrcpy(rname,cur_dir);
+       pstrcat(rname,CLI_DIRSEP_STR);
        
        slprintf(lname,sizeof(lname)-1, "%s/smbmore.XXXXXX",tmpdir());
        fd = smb_mkstemp(lname);
@@ -887,7 +1054,7 @@ static int cmd_more(void)
                unlink(lname);
                return 1;
        }
-       dos_clean_name(rname);
+       clean_name(rname);
 
        rc = do_get(rname, lname, False);
 
@@ -909,7 +1076,7 @@ static int cmd_mget(void)
 {
        uint16 attribute = aSYSTEM | aHIDDEN;
        pstring mget_mask;
-       fstring buf;
+       pstring buf;
        char *p=buf;
 
        *mget_mask = 0;
@@ -921,10 +1088,10 @@ static int cmd_mget(void)
 
        while (next_token_nr(NULL,p,NULL,sizeof(buf))) {
                pstrcpy(mget_mask,cur_dir);
-               if(mget_mask[strlen(mget_mask)-1]!='\\')
-                       pstrcat(mget_mask,"\\");
+               if ((mget_mask[0] != '\0') && (mget_mask[strlen(mget_mask)-1]!=CLI_DIRSEP_CHAR))
+                       pstrcat(mget_mask,CLI_DIRSEP_STR);
                
-               if (*p == '\\')
+               if (*p == CLI_DIRSEP_CHAR)
                        pstrcpy(mget_mask,p);
                else
                        pstrcat(mget_mask,p);
@@ -933,8 +1100,8 @@ static int cmd_mget(void)
 
        if (!*mget_mask) {
                pstrcpy(mget_mask,cur_dir);
-               if(mget_mask[strlen(mget_mask)-1]!='\\')
-                       pstrcat(mget_mask,"\\");
+               if(mget_mask[strlen(mget_mask)-1]!=CLI_DIRSEP_CHAR)
+                       pstrcat(mget_mask,CLI_DIRSEP_STR);
                pstrcat(mget_mask,"*");
                do_list(mget_mask, attribute,do_mget,False,True);
        }
@@ -948,9 +1115,17 @@ static int cmd_mget(void)
 
 static BOOL do_mkdir(char *name)
 {
-       if (!cli_mkdir(cli, name)) {
+       struct cli_state *targetcli;
+       pstring targetname;
+       
+       if ( !cli_resolve_path( "", cli, name, &targetcli, targetname ) ) {
+               d_printf("mkdir %s: %s\n", name, cli_errstr(cli));
+               return False;
+       }
+
+       if (!cli_mkdir(targetcli, targetname)) {
                d_printf("%s making remote directory %s\n",
-                        cli_errstr(cli),name);
+                        cli_errstr(targetcli),name);
                return(False);
        }
 
@@ -963,7 +1138,7 @@ static BOOL do_mkdir(char *name)
 
 static BOOL do_altname(char *name)
 {
-       fstring altname;
+       pstring altname;
        if (!NT_STATUS_IS_OK(cli_qpathinfo_alt_name(cli, name, altname))) {
                d_printf("%s getting alt name for %s\n",
                         cli_errstr(cli),name);
@@ -980,7 +1155,8 @@ static BOOL do_altname(char *name)
 
 static int cmd_quit(void)
 {
-       cli_shutdown(cli);
+       cli_cm_shutdown();
+       talloc_destroy( ctx);
        exit(0);
        /* NOTREACHED */
        return 0;
@@ -993,7 +1169,7 @@ static int cmd_quit(void)
 static int cmd_mkdir(void)
 {
        pstring mask;
-       fstring buf;
+       pstring buf;
        char *p=buf;
   
        pstrcpy(mask,cur_dir);
@@ -1008,17 +1184,23 @@ static int cmd_mkdir(void)
        if (recurse) {
                pstring ddir;
                pstring ddir2;
+               struct cli_state *targetcli;
+               pstring targetname;
                *ddir2 = 0;
                
-               pstrcpy(ddir,mask);
+               if ( !cli_resolve_path( "", cli, mask, &targetcli, targetname ) ) {
+                       return 1;
+               }
+
+               pstrcpy(ddir,targetname);
                trim_char(ddir,'.','\0');
                p = strtok(ddir,"/\\");
                while (p) {
                        pstrcat(ddir2,p);
-                       if (!cli_chkpath(cli, ddir2)) { 
+                       if (!cli_chkpath(targetcli, ddir2)) { 
                                do_mkdir(ddir2);
                        }
-                       pstrcat(ddir2,"\\");
+                       pstrcat(ddir2,CLI_DIRSEP_STR);
                        p = strtok(NULL,"/\\");
                }        
        } else {
@@ -1035,7 +1217,7 @@ static int cmd_mkdir(void)
 static int cmd_altname(void)
 {
        pstring name;
-       fstring buf;
+       pstring buf;
        char *p=buf;
   
        pstrcpy(name,cur_dir);
@@ -1059,30 +1241,37 @@ static int do_put(char *rname, char *lname, BOOL reput)
 {
        int fnum;
        XFILE *f;
-       size_t start = 0;
+       SMB_OFF_T start = 0;
        off_t nread = 0;
        char *buf = NULL;
        int maxwrite = io_bufsize;
        int rc = 0;
-       
        struct timeval tp_start;
+       struct cli_state *targetcli;
+       pstring targetname;
+       
+       if ( !cli_resolve_path( "", cli, rname, &targetcli, targetname ) ) {
+               d_printf("Failed to open %s: %s\n", rname, cli_errstr(cli));
+               return 1;
+       }
+       
        GetTimeOfDay(&tp_start);
 
        if (reput) {
-               fnum = cli_open(cli, rname, O_RDWR|O_CREAT, DENY_NONE);
+               fnum = cli_open(targetcli, targetname, O_RDWR|O_CREAT, DENY_NONE);
                if (fnum >= 0) {
-                       if (!cli_qfileinfo(cli, fnum, NULL, &start, NULL, NULL, NULL, NULL, NULL) &&
-                           !cli_getattrE(cli, fnum, NULL, &start, NULL, NULL, NULL)) {
+                       if (!cli_qfileinfo(targetcli, fnum, NULL, &start, NULL, NULL, NULL, NULL, NULL) &&
+                           !cli_getattrE(targetcli, fnum, NULL, &start, NULL, NULL, NULL)) {
                                d_printf("getattrib: %s\n",cli_errstr(cli));
                                return 1;
                        }
                }
        } else {
-               fnum = cli_open(cli, rname, O_RDWR|O_CREAT|O_TRUNC, DENY_NONE);
+               fnum = cli_open(targetcli, targetname, O_RDWR|O_CREAT|O_TRUNC, DENY_NONE);
        }
   
        if (fnum == -1) {
-               d_printf("%s opening remote file %s\n",cli_errstr(cli),rname);
+               d_printf("%s opening remote file %s\n",cli_errstr(targetcli),rname);
                return 1;
        }
 
@@ -1112,7 +1301,7 @@ static int do_put(char *rname, char *lname, BOOL reput)
        DEBUG(1,("putting file %s as %s ",lname,
                 rname));
   
-       buf = (char *)malloc(maxwrite);
+       buf = (char *)SMB_MALLOC(maxwrite);
        if (!buf) {
                d_printf("ERROR: Not enough memory!\n");
                return 1;
@@ -1130,7 +1319,7 @@ static int do_put(char *rname, char *lname, BOOL reput)
                        break;
                }
 
-               ret = cli_write(cli, fnum, 0, buf, nread + start, n);
+               ret = cli_write(targetcli, fnum, 0, buf, nread + start, n);
 
                if (n != ret) {
                        d_printf("Error writing file: %s\n", cli_errstr(cli));
@@ -1141,7 +1330,7 @@ static int do_put(char *rname, char *lname, BOOL reput)
                nread += n;
        }
 
-       if (!cli_close(cli, fnum)) {
+       if (!cli_close(targetcli, fnum)) {
                d_printf("%s closing remote file %s\n",cli_errstr(cli),rname);
                x_fclose(f);
                SAFE_FREE(buf);
@@ -1172,7 +1361,7 @@ static int do_put(char *rname, char *lname, BOOL reput)
        }
 
        if (f == x_stdin) {
-               cli_shutdown(cli);
+               cli_cm_shutdown();
                exit(0);
        }
        
@@ -1187,11 +1376,11 @@ static int cmd_put(void)
 {
        pstring lname;
        pstring rname;
-       fstring buf;
+       pstring buf;
        char *p=buf;
        
        pstrcpy(rname,cur_dir);
-       pstrcat(rname,"\\");
+       pstrcat(rname,CLI_DIRSEP_STR);
   
        if (!next_token_nr(NULL,p,NULL,sizeof(buf))) {
                d_printf("put <filename>\n");
@@ -1204,7 +1393,7 @@ static int cmd_put(void)
        else
                pstrcat(rname,lname);
        
-       dos_clean_name(rname);
+       clean_name(rname);
 
        {
                SMB_STRUCT_STAT st;
@@ -1234,15 +1423,15 @@ static struct file_list {
  Free a file_list structure.
 ****************************************************************************/
 
-static void free_file_list (struct file_list * list)
+static void free_file_list (struct file_list *list_head)
 {
-       struct file_list *tmp;
+       struct file_list *list, *next;
        
-       while (list) {
-               tmp = list;
-               DLIST_REMOVE(list, list);
-               SAFE_FREE(tmp->file_path);
-               SAFE_FREE(tmp);
+       for (list = list_head; list; list = next) {
+               next = list->next;
+               DLIST_REMOVE(list_head, list);
+               SAFE_FREE(list->file_path);
+               SAFE_FREE(list);
        }
 }
 
@@ -1284,7 +1473,7 @@ static int cmd_select(void)
 static int file_find(struct file_list **list, const char *directory, 
                      const char *expression, BOOL match)
 {
-       DIR *dir;
+       SMB_STRUCT_DIR *dir;
        struct file_list *entry;
         struct stat statbuf;
         int ret;
@@ -1292,7 +1481,7 @@ static int file_find(struct file_list **list, const char *directory,
        BOOL isdir;
        const char *dname;
 
-        dir = opendir(directory);
+        dir = sys_opendir(directory);
        if (!dir)
                return -1;
        
@@ -1321,14 +1510,14 @@ static int file_find(struct file_list **list, const char *directory,
                                
                                if (ret == -1) {
                                        SAFE_FREE(path);
-                                       closedir(dir);
+                                       sys_closedir(dir);
                                        return -1;
                                }
                        }
-                       entry = (struct file_list *) malloc(sizeof (struct file_list));
+                       entry = SMB_MALLOC_P(struct file_list);
                        if (!entry) {
                                d_printf("Out of memory in file_find\n");
-                               closedir(dir);
+                               sys_closedir(dir);
                                return -1;
                        }
                        entry->file_path = path;
@@ -1339,7 +1528,7 @@ static int file_find(struct file_list **list, const char *directory,
                }
         }
 
-       closedir(dir);
+       sys_closedir(dir);
        return 0;
 }
 
@@ -1349,7 +1538,7 @@ static int file_find(struct file_list **list, const char *directory,
 
 static int cmd_mput(void)
 {
-       fstring buf;
+       pstring buf;
        char *p=buf;
        
        while (next_token_nr(NULL,p,NULL,sizeof(buf))) {
@@ -1447,7 +1636,7 @@ static int do_cancel(int job)
 
 static int cmd_cancel(void)
 {
-       fstring buf;
+       pstring buf;
        int job; 
 
        if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
@@ -1518,14 +1707,13 @@ static void do_del(file_info *finfo)
 {
        pstring mask;
 
-       pstrcpy(mask,cur_dir);
-       pstrcat(mask,finfo->name);
+       pstr_sprintf( mask, "%s%c%s", finfo->dir, CLI_DIRSEP_CHAR, finfo->name );
 
        if (finfo->mode & aDIR) 
                return;
 
-       if (!cli_unlink(cli, mask)) {
-               d_printf("%s deleting remote file %s\n",cli_errstr(cli),mask);
+       if (!cli_unlink(finfo->cli, mask)) {
+               d_printf("%s deleting remote file %s\n",cli_errstr(finfo->cli),mask);
        }
 }
 
@@ -1536,7 +1724,7 @@ static void do_del(file_info *finfo)
 static int cmd_del(void)
 {
        pstring mask;
-       fstring buf;
+       pstring buf;
        uint16 attribute = aSYSTEM | aHIDDEN;
 
        if (recurse)
@@ -1556,167 +1744,813 @@ static int cmd_del(void)
 }
 
 /****************************************************************************
+ Wildcard delete some files.
 ****************************************************************************/
 
-static int cmd_open(void)
+static int cmd_wdel(void)
 {
        pstring mask;
-       fstring buf;
-       
-       pstrcpy(mask,cur_dir);
-       
+       pstring buf;
+       uint16 attribute;
+       struct cli_state *targetcli;
+       pstring targetname;
+
        if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
-               d_printf("open <filename>\n");
+               d_printf("wdel 0x<attrib> <wcard>\n");
+               return 1;
+       }
+
+       attribute = (uint16)strtol(buf, (char **)NULL, 16);
+
+       if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+               d_printf("wdel 0x<attrib> <wcard>\n");
                return 1;
        }
-       pstrcat(mask,buf);
 
-       cli_open(cli, mask, O_RDWR, DENY_ALL);
+       pstrcpy(mask,cur_dir);
+       pstrcat(mask,buf);
 
+       if ( !cli_resolve_path( "", cli, mask, &targetcli, targetname ) ) {
+               d_printf("cmd_wdel %s: %s\n", mask, cli_errstr(cli));
+               return 1;
+       }
+       
+       if (!cli_unlink_full(targetcli, targetname, attribute)) {
+               d_printf("%s deleting remote files %s\n",cli_errstr(targetcli),targetname);
+       }
        return 0;
 }
 
-
 /****************************************************************************
- Remove a directory.
 ****************************************************************************/
 
-static int cmd_rmdir(void)
+static int cmd_open(void)
 {
        pstring mask;
-       fstring buf;
-  
+       pstring buf;
+       struct cli_state *targetcli;
+       pstring targetname;
+       int fnum;
+
        pstrcpy(mask,cur_dir);
        
        if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
-               d_printf("rmdir <dirname>\n");
+               d_printf("open <filename>\n");
                return 1;
        }
        pstrcat(mask,buf);
 
-       if (!cli_rmdir(cli, mask)) {
-               d_printf("%s removing remote directory file %s\n",
-                        cli_errstr(cli),mask);
+       if ( !cli_resolve_path( "", cli, mask, &targetcli, targetname ) ) {
+               d_printf("open %s: %s\n", mask, cli_errstr(cli));
+               return 1;
        }
        
+       fnum = cli_nt_create(targetcli, targetname, FILE_READ_DATA|FILE_WRITE_DATA);
+       if (fnum == -1) {
+               fnum = cli_nt_create(targetcli, targetname, FILE_READ_DATA);
+               if (fnum != -1) {
+                       d_printf("open file %s: for read/write fnum %d\n", targetname, fnum);
+               } else {
+                       d_printf("Failed to open file %s. %s\n", targetname, cli_errstr(cli));
+               }
+       } else {
+               d_printf("open file %s: for read/write fnum %d\n", targetname, fnum);
+       }
+
        return 0;
 }
 
 /****************************************************************************
- UNIX hardlink.
 ****************************************************************************/
 
-static int cmd_link(void)
+static int cmd_posix_open(void)
 {
-       pstring src,dest;
-       fstring buf,buf2;
-  
-       if (!SERVER_HAS_UNIX_CIFS(cli)) {
-               d_printf("Server doesn't support UNIX CIFS calls.\n");
+       pstring mask;
+       pstring buf;
+       struct cli_state *targetcli;
+       pstring targetname;
+       mode_t mode;
+       int fnum;
+
+       pstrcpy(mask,cur_dir);
+       
+       if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+               d_printf("posix_open <filename> 0<mode>\n");
                return 1;
        }
-
-       pstrcpy(src,cur_dir);
-       pstrcpy(dest,cur_dir);
-  
-       if (!next_token_nr(NULL,buf,NULL,sizeof(buf)) || 
-           !next_token_nr(NULL,buf2,NULL, sizeof(buf2))) {
-               d_printf("link <src> <dest>\n");
+       pstrcat(mask,buf);
+       if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+               d_printf("posix_open <filename> 0<mode>\n");
                return 1;
        }
+       mode = (mode_t)strtol(buf, (char **)NULL, 8);
 
-       pstrcat(src,buf);
-       pstrcat(dest,buf2);
-
-       if (!cli_unix_hardlink(cli, src, dest)) {
-               d_printf("%s linking files (%s -> %s)\n", cli_errstr(cli), src, dest);
+       if (!cli_resolve_path( "", cli, mask, &targetcli, targetname )) {
+               d_printf("posix_open %s: %s\n", mask, cli_errstr(cli));
                return 1;
-       }  
+       }
+       
+       fnum = cli_posix_open(targetcli, targetname, O_CREAT|O_RDWR, mode);
+       if (fnum == -1) {
+               fnum = cli_posix_open(targetcli, targetname, O_CREAT|O_RDONLY, mode);
+               if (fnum != -1) {
+                       d_printf("posix_open file %s: for read/write fnum %d\n", targetname, fnum);
+               } else {
+                       d_printf("Failed to open file %s. %s\n", targetname, cli_errstr(cli));
+               }
+       } else {
+               d_printf("posix_open file %s: for read/write fnum %d\n", targetname, fnum);
+       }
 
        return 0;
 }
 
-/****************************************************************************
- UNIX symlink.
-****************************************************************************/
-
-static int cmd_symlink(void)
+static int cmd_posix_mkdir(void)
 {
-       pstring src,dest;
-       fstring buf,buf2;
-  
-       if (!SERVER_HAS_UNIX_CIFS(cli)) {
-               d_printf("Server doesn't support UNIX CIFS calls.\n");
-               return 1;
-       }
+       pstring mask;
+       pstring buf;
+       struct cli_state *targetcli;
+       pstring targetname;
+       mode_t mode;
+       int fnum;
 
-       pstrcpy(src,cur_dir);
-       pstrcpy(dest,cur_dir);
+       pstrcpy(mask,cur_dir);
        
-       if (!next_token_nr(NULL,buf,NULL,sizeof(buf)) || 
-           !next_token_nr(NULL,buf2,NULL, sizeof(buf2))) {
-               d_printf("symlink <src> <dest>\n");
+       if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+               d_printf("posix_mkdir <filename> 0<mode>\n");
                return 1;
        }
+       pstrcat(mask,buf);
+       if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+               d_printf("posix_mkdir <filename> 0<mode>\n");
+               return 1;
+       }
+       mode = (mode_t)strtol(buf, (char **)NULL, 8);
 
-       pstrcat(src,buf);
-       pstrcat(dest,buf2);
-
-       if (!cli_unix_symlink(cli, src, dest)) {
-               d_printf("%s symlinking files (%s -> %s)\n",
-                       cli_errstr(cli), src, dest);
+       if (!cli_resolve_path( "", cli, mask, &targetcli, targetname )) {
+               d_printf("posix_mkdir %s: %s\n", mask, cli_errstr(cli));
                return 1;
-       } 
+       }
+
+       fnum = cli_posix_mkdir(targetcli, targetname, mode);
+       if (fnum == -1) {
+               d_printf("Failed to open file %s. %s\n", targetname, cli_errstr(cli));
+       } else {
+               d_printf("posix_mkdir created directory %s\n", targetname);
+       }
 
        return 0;
 }
 
-/****************************************************************************
- UNIX chmod.
-****************************************************************************/
-
-static int cmd_chmod(void)
+static int cmd_posix_unlink(void)
 {
-       pstring src;
-       mode_t mode;
-       fstring buf, buf2;
-  
-       if (!SERVER_HAS_UNIX_CIFS(cli)) {
-               d_printf("Server doesn't support UNIX CIFS calls.\n");
-               return 1;
-       }
+       pstring mask;
+       pstring buf;
+       struct cli_state *targetcli;
+       pstring targetname;
 
-       pstrcpy(src,cur_dir);
+       pstrcpy(mask,cur_dir);
        
-       if (!next_token_nr(NULL,buf,NULL,sizeof(buf)) || 
-           !next_token_nr(NULL,buf2,NULL, sizeof(buf2))) {
-               d_printf("chmod mode file\n");
+       if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+               d_printf("posix_unlink <filename>\n");
                return 1;
        }
+       pstrcat(mask,buf);
 
-       mode = (mode_t)strtol(buf, NULL, 8);
-       pstrcat(src,buf2);
-
-       if (!cli_unix_chmod(cli, src, mode)) {
-               d_printf("%s chmod file %s 0%o\n",
-                       cli_errstr(cli), src, (unsigned int)mode);
+       if (!cli_resolve_path( "", cli, mask, &targetcli, targetname )) {
+               d_printf("posix_unlink %s: %s\n", mask, cli_errstr(cli));
                return 1;
-       } 
+       }
+       
+       if (!cli_posix_unlink(targetcli, targetname)) {
+               d_printf("Failed to unlink file %s. %s\n", targetname, cli_errstr(cli));
+       } else {
+               d_printf("posix_unlink deleted file %s\n", targetname);
+       }
 
        return 0;
 }
 
-/****************************************************************************
- UNIX chown.
+static int cmd_posix_rmdir(void)
+{
+       pstring mask;
+       pstring buf;
+       struct cli_state *targetcli;
+       pstring targetname;
+
+       pstrcpy(mask,cur_dir);
+       
+       if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+               d_printf("posix_rmdir <filename>\n");
+               return 1;
+       }
+       pstrcat(mask,buf);
+
+       if (!cli_resolve_path( "", cli, mask, &targetcli, targetname)) {
+               d_printf("posix_rmdir %s: %s\n", mask, cli_errstr(cli));
+               return 1;
+       }
+       
+       if (!cli_posix_rmdir(targetcli, targetname)) {
+               d_printf("Failed to unlink directory %s. %s\n", targetname, cli_errstr(cli));
+       } else {
+               d_printf("posix_rmdir deleted directory %s\n", targetname);
+       }
+
+       return 0;
+}
+
+static int cmd_close(void)
+{
+       fstring buf;
+       int fnum;
+
+       if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+               d_printf("close <fnum>\n");
+               return 1;
+       }
+
+       fnum = atoi(buf);
+       /* We really should use the targetcli here.... */
+       if (!cli_close(cli, fnum)) {
+               d_printf("close %d: %s\n", fnum, cli_errstr(cli));
+               return 1;
+       }
+       return 0;
+}
+
+static int cmd_posix(void)
+{
+       uint16 major, minor;
+       uint32 caplow, caphigh;
+       pstring caps;
+
+       if (!SERVER_HAS_UNIX_CIFS(cli)) {
+               d_printf("Server doesn't support UNIX CIFS extensions.\n");
+               return 1;
+       }
+
+       if (!cli_unix_extensions_version(cli, &major, &minor, &caplow, &caphigh)) {
+               d_printf("Can't get UNIX CIFS extensions version from server.\n");
+               return 1;
+       }
+
+       d_printf("Server supports CIFS extensions %u.%u\n", (unsigned int)major, (unsigned int)minor);
+
+       *caps = '\0';
+        if (caplow & CIFS_UNIX_FCNTL_LOCKS_CAP) {
+               pstrcat(caps, "locks ");
+       }
+        if (caplow & CIFS_UNIX_POSIX_ACLS_CAP) {
+               pstrcat(caps, "acls ");
+       }
+        if (caplow & CIFS_UNIX_XATTTR_CAP) {
+               pstrcat(caps, "eas ");
+       }
+        if (caplow & CIFS_UNIX_POSIX_PATHNAMES_CAP) {
+               pstrcat(caps, "pathnames ");
+       }
+        if (caplow & CIFS_UNIX_POSIX_PATH_OPERATIONS_CAP) {
+               pstrcat(caps, "posix_path_operations ");
+       }
+
+       if (strlen(caps) > 0 && caps[strlen(caps)-1] == ' ') {
+               caps[strlen(caps)-1] = '\0';
+       }
+
+       if (!cli_set_unix_extensions_capabilities(cli, major, minor, caplow, caphigh)) {
+               d_printf("Can't set UNIX CIFS extensions capabilities. %s.\n", cli_errstr(cli));
+               return 1;
+       }
+
+       d_printf("Selecting server supported CIFS capabilities %s\n", caps);
+
+       if (caplow & CIFS_UNIX_POSIX_PATHNAMES_CAP) {
+               CLI_DIRSEP_CHAR = '/';
+               *CLI_DIRSEP_STR = '/';
+               pstrcpy(cur_dir, CLI_DIRSEP_STR);
+       }
+
+       return 0;
+}
+
+static int cmd_lock(void)
+{
+       fstring buf;
+       SMB_BIG_UINT start, len;
+       enum brl_type lock_type;
+       int fnum;
+
+       if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+               d_printf("lock <fnum> [r|w] <hex-start> <hex-len>\n");
+               return 1;
+       }
+       fnum = atoi(buf);
+
+       if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+               d_printf("lock <fnum> [r|w] <hex-start> <hex-len>\n");
+               return 1;
+       }
+
+       if (*buf == 'r' || *buf == 'R') {
+               lock_type = READ_LOCK;
+       } else if (*buf == 'w' || *buf == 'W') {
+               lock_type = WRITE_LOCK;
+       } else {
+               d_printf("lock <fnum> [r|w] <hex-start> <hex-len>\n");
+               return 1;
+       }
+
+       if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+               d_printf("lock <fnum> [r|w] <hex-start> <hex-len>\n");
+               return 1;
+       }
+
+       start = (SMB_BIG_UINT)strtol(buf, (char **)NULL, 16);
+
+       if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+               d_printf("lock <fnum> [r|w] <hex-start> <hex-len>\n");
+               return 1;
+       }
+
+       len = (SMB_BIG_UINT)strtol(buf, (char **)NULL, 16);
+
+       if (!cli_posix_lock(cli, fnum, start, len, True, lock_type)) {
+               d_printf("lock failed %d: %s\n", fnum, cli_errstr(cli));
+       }
+
+       return 0;
+}
+
+static int cmd_unlock(void)
+{
+       fstring buf;
+       SMB_BIG_UINT start, len;
+       int fnum;
+
+       if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+               d_printf("unlock <fnum> <hex-start> <hex-len>\n");
+               return 1;
+       }
+       fnum = atoi(buf);
+
+       if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+               d_printf("unlock <fnum> <hex-start> <hex-len>\n");
+               return 1;
+       }
+
+       start = (SMB_BIG_UINT)strtol(buf, (char **)NULL, 16);
+
+       if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+               d_printf("unlock <fnum> <hex-start> <hex-len>\n");
+               return 1;
+       }
+
+       len = (SMB_BIG_UINT)strtol(buf, (char **)NULL, 16);
+
+       if (!cli_posix_unlock(cli, fnum, start, len)) {
+               d_printf("unlock failed %d: %s\n", fnum, cli_errstr(cli));
+       }
+
+       return 0;
+}
+
+
+/****************************************************************************
+ Remove a directory.
 ****************************************************************************/
 
-static int cmd_chown(void)
+static int cmd_rmdir(void)
+{
+       pstring mask;
+       pstring buf;
+       struct cli_state *targetcli;
+       pstring targetname;
+  
+       pstrcpy(mask,cur_dir);
+       
+       if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+               d_printf("rmdir <dirname>\n");
+               return 1;
+       }
+       pstrcat(mask,buf);
+
+       if ( !cli_resolve_path( "", cli, mask, &targetcli, targetname ) ) {
+               d_printf("rmdir %s: %s\n", mask, cli_errstr(cli));
+               return 1;
+       }
+       
+       if (!cli_rmdir(targetcli, targetname)) {
+               d_printf("%s removing remote directory file %s\n",
+                        cli_errstr(targetcli),mask);
+       }
+       
+       return 0;
+}
+
+/****************************************************************************
+ UNIX hardlink.
+****************************************************************************/
+
+static int cmd_link(void)
+{
+       pstring oldname,newname;
+       pstring buf,buf2;
+       struct cli_state *targetcli;
+       pstring targetname;
+  
+       pstrcpy(oldname,cur_dir);
+       pstrcpy(newname,cur_dir);
+  
+       if (!next_token_nr(NULL,buf,NULL,sizeof(buf)) || 
+           !next_token_nr(NULL,buf2,NULL, sizeof(buf2))) {
+               d_printf("link <oldname> <newname>\n");
+               return 1;
+       }
+
+       pstrcat(oldname,buf);
+       pstrcat(newname,buf2);
+
+       if ( !cli_resolve_path( "", cli, oldname, &targetcli, targetname ) ) {
+               d_printf("link %s: %s\n", oldname, cli_errstr(cli));
+               return 1;
+       }
+       
+       if (!SERVER_HAS_UNIX_CIFS(targetcli)) {
+               d_printf("Server doesn't support UNIX CIFS calls.\n");
+               return 1;
+       }
+       
+       if (!cli_unix_hardlink(targetcli, targetname, newname)) {
+               d_printf("%s linking files (%s -> %s)\n", cli_errstr(targetcli), newname, oldname);
+               return 1;
+       }  
+
+       return 0;
+}
+
+/****************************************************************************
+ UNIX symlink.
+****************************************************************************/
+
+static int cmd_symlink(void)
+{
+       pstring oldname,newname;
+       pstring buf,buf2;
+       struct cli_state *targetcli;
+       pstring targetname;
+  
+       if (!SERVER_HAS_UNIX_CIFS(cli)) {
+               d_printf("Server doesn't support UNIX CIFS calls.\n");
+               return 1;
+       }
+
+       pstrcpy(newname,cur_dir);
+       
+       if (!next_token_nr(NULL,buf,NULL,sizeof(buf)) || 
+           !next_token_nr(NULL,buf2,NULL, sizeof(buf2))) {
+               d_printf("symlink <oldname> <newname>\n");
+               return 1;
+       }
+
+       pstrcpy(oldname,buf);
+       pstrcat(newname,buf2);
+
+       if ( !cli_resolve_path( "", cli, oldname, &targetcli, targetname ) ) {
+               d_printf("link %s: %s\n", oldname, cli_errstr(cli));
+               return 1;
+       }
+
+       if (!cli_unix_symlink(targetcli, targetname, newname)) {
+               d_printf("%s symlinking files (%s -> %s)\n",
+                       cli_errstr(targetcli), newname, targetname);
+               return 1;
+       } 
+
+       return 0;
+}
+
+/****************************************************************************
+ UNIX chmod.
+****************************************************************************/
+
+static int cmd_chmod(void)
 {
        pstring src;
-       uid_t uid;
-       gid_t gid;
-       fstring buf, buf2, buf3;
+       mode_t mode;
+       pstring buf, buf2;
+       struct cli_state *targetcli;
+       pstring targetname;
   
+       pstrcpy(src,cur_dir);
+       
+       if (!next_token_nr(NULL,buf,NULL,sizeof(buf)) || 
+           !next_token_nr(NULL,buf2,NULL, sizeof(buf2))) {
+               d_printf("chmod mode file\n");
+               return 1;
+       }
+
+       mode = (mode_t)strtol(buf, NULL, 8);
+       pstrcat(src,buf2);
+
+       if ( !cli_resolve_path( "", cli, src, &targetcli, targetname ) ) {
+               d_printf("chmod %s: %s\n", src, cli_errstr(cli));
+               return 1;
+       }
+       
+       if (!SERVER_HAS_UNIX_CIFS(targetcli)) {
+               d_printf("Server doesn't support UNIX CIFS calls.\n");
+               return 1;
+       }
+       
+       if (!cli_unix_chmod(targetcli, targetname, mode)) {
+               d_printf("%s chmod file %s 0%o\n",
+                       cli_errstr(targetcli), src, (unsigned int)mode);
+               return 1;
+       } 
+
+       return 0;
+}
+
+static const char *filetype_to_str(mode_t mode)
+{
+       if (S_ISREG(mode)) {
+               return "regular file";
+       } else if (S_ISDIR(mode)) {
+               return "directory";
+       } else 
+#ifdef S_ISCHR
+       if (S_ISCHR(mode)) {
+               return "character device";
+       } else
+#endif
+#ifdef S_ISBLK
+       if (S_ISBLK(mode)) {
+               return "block device";
+       } else
+#endif
+#ifdef S_ISFIFO
+       if (S_ISFIFO(mode)) {
+               return "fifo";
+       } else
+#endif
+#ifdef S_ISLNK
+       if (S_ISLNK(mode)) {
+               return "symbolic link";
+       } else
+#endif
+#ifdef S_ISSOCK
+       if (S_ISSOCK(mode)) {
+               return "socket";
+       } else
+#endif
+       return "";
+}
+
+static char rwx_to_str(mode_t m, mode_t bt, char ret)
+{
+       if (m & bt) {
+               return ret;
+       } else {
+               return '-';
+       }
+}
+
+static char *unix_mode_to_str(char *s, mode_t m)
+{
+       char *p = s;
+       const char *str = filetype_to_str(m);
+
+       switch(str[0]) {
+               case 'd':
+                       *p++ = 'd';
+                       break;
+               case 'c':
+                       *p++ = 'c';
+                       break;
+               case 'b':
+                       *p++ = 'b';
+                       break;
+               case 'f':
+                       *p++ = 'p';
+                       break;
+               case 's':
+                       *p++ = str[1] == 'y' ? 'l' : 's';
+                       break;
+               case 'r':
+               default:
+                       *p++ = '-';
+                       break;
+       }
+       *p++ = rwx_to_str(m, S_IRUSR, 'r');
+       *p++ = rwx_to_str(m, S_IWUSR, 'w');
+       *p++ = rwx_to_str(m, S_IXUSR, 'x');
+       *p++ = rwx_to_str(m, S_IRGRP, 'r');
+       *p++ = rwx_to_str(m, S_IWGRP, 'w');
+       *p++ = rwx_to_str(m, S_IXGRP, 'x');
+       *p++ = rwx_to_str(m, S_IROTH, 'r');
+       *p++ = rwx_to_str(m, S_IWOTH, 'w');
+       *p++ = rwx_to_str(m, S_IXOTH, 'x');
+       *p++ = '\0';
+       return s;
+}
+
+/****************************************************************************
+ Utility function for UNIX getfacl.
+****************************************************************************/
+
+static char *perms_to_string(fstring permstr, unsigned char perms)
+{
+       fstrcpy(permstr, "---");
+       if (perms & SMB_POSIX_ACL_READ) {
+               permstr[0] = 'r';
+       }
+       if (perms & SMB_POSIX_ACL_WRITE) {
+               permstr[1] = 'w';
+       }
+       if (perms & SMB_POSIX_ACL_EXECUTE) {
+               permstr[2] = 'x';
+       }
+       return permstr;
+}
+
+/****************************************************************************
+ UNIX getfacl.
+****************************************************************************/
+
+static int cmd_getfacl(void)
+{
+       pstring src, name;
+       uint16 major, minor;
+       uint32 caplow, caphigh;
+       char *retbuf = NULL;
+       size_t rb_size = 0;
+       SMB_STRUCT_STAT sbuf;
+       uint16 num_file_acls = 0;
+       uint16 num_dir_acls = 0;
+       uint16 i;
+       struct cli_state *targetcli;
+       pstring targetname;
+       pstrcpy(src,cur_dir);
+       
+       if (!next_token_nr(NULL,name,NULL,sizeof(name))) {
+               d_printf("stat file\n");
+               return 1;
+       }
+
+       pstrcat(src,name);
+       
+       if ( !cli_resolve_path( "", cli, src, &targetcli, targetname ) ) {
+               d_printf("stat %s: %s\n", src, cli_errstr(cli));
+               return 1;
+       }
+       
+       if (!SERVER_HAS_UNIX_CIFS(targetcli)) {
+               d_printf("Server doesn't support UNIX CIFS calls.\n");
+               return 1;
+       }
+       
+       if (!cli_unix_extensions_version(targetcli, &major, &minor, &caplow, &caphigh)) {
+               d_printf("Can't get UNIX CIFS version from server.\n");
+               return 1;
+       }
+
+       if (!(caplow & CIFS_UNIX_POSIX_ACLS_CAP)) {
+               d_printf("This server supports UNIX extensions but doesn't support POSIX ACLs.\n");
+               return 1;
+       }
+
+       if (!cli_unix_stat(targetcli, targetname, &sbuf)) {
+               d_printf("%s getfacl doing a stat on file %s\n",
+                       cli_errstr(targetcli), src);
+               return 1;
+       } 
+
+       if (!cli_unix_getfacl(targetcli, targetname, &rb_size, &retbuf)) {
+               d_printf("%s getfacl file %s\n",
+                       cli_errstr(targetcli), src);
+               return 1;
+       } 
+
+       /* ToDo : Print out the ACL values. */
+       if (SVAL(retbuf,0) != SMB_POSIX_ACL_VERSION || rb_size < 6) {
+               d_printf("getfacl file %s, unknown POSIX acl version %u.\n",
+                       src, (unsigned int)CVAL(retbuf,0) );
+               SAFE_FREE(retbuf);
+               return 1;
+       }
+
+       num_file_acls = SVAL(retbuf,2);
+       num_dir_acls = SVAL(retbuf,4);
+       if (rb_size != SMB_POSIX_ACL_HEADER_SIZE + SMB_POSIX_ACL_ENTRY_SIZE*(num_file_acls+num_dir_acls)) {
+               d_printf("getfacl file %s, incorrect POSIX acl buffer size (should be %u, was %u).\n",
+                       src,
+                       (unsigned int)(SMB_POSIX_ACL_HEADER_SIZE + SMB_POSIX_ACL_ENTRY_SIZE*(num_file_acls+num_dir_acls)),
+                       (unsigned int)rb_size);
+
+               SAFE_FREE(retbuf);
+               return 1;
+       }
+
+       d_printf("# file: %s\n", src);
+       d_printf("# owner: %u\n# group: %u\n", (unsigned int)sbuf.st_uid, (unsigned int)sbuf.st_gid);
+
+       if (num_file_acls == 0 && num_dir_acls == 0) {
+               d_printf("No acls found.\n");
+       }
+
+       for (i = 0; i < num_file_acls; i++) {
+               uint32 uorg;
+               fstring permstring;
+               unsigned char tagtype = CVAL(retbuf, SMB_POSIX_ACL_HEADER_SIZE+(i*SMB_POSIX_ACL_ENTRY_SIZE));
+               unsigned char perms = CVAL(retbuf, SMB_POSIX_ACL_HEADER_SIZE+(i*SMB_POSIX_ACL_ENTRY_SIZE)+1);
+
+               switch(tagtype) {
+                       case SMB_POSIX_ACL_USER_OBJ:
+                               d_printf("user::");
+                               break;
+                       case SMB_POSIX_ACL_USER:
+                               uorg = IVAL(retbuf,SMB_POSIX_ACL_HEADER_SIZE+(i*SMB_POSIX_ACL_ENTRY_SIZE)+2);
+                               d_printf("user:%u:", uorg);
+                               break;
+                       case SMB_POSIX_ACL_GROUP_OBJ:
+                               d_printf("group::");
+                               break;
+                       case SMB_POSIX_ACL_GROUP:
+                               uorg = IVAL(retbuf,SMB_POSIX_ACL_HEADER_SIZE+(i*SMB_POSIX_ACL_ENTRY_SIZE)+2);
+                               d_printf("group:%u", uorg);
+                               break;
+                       case SMB_POSIX_ACL_MASK:
+                               d_printf("mask::");
+                               break;
+                       case SMB_POSIX_ACL_OTHER:
+                               d_printf("other::");
+                               break;
+                       default:
+                               d_printf("getfacl file %s, incorrect POSIX acl tagtype (%u).\n",
+                                       src, (unsigned int)tagtype );
+                               SAFE_FREE(retbuf);
+                               return 1;
+               }
+
+               d_printf("%s\n", perms_to_string(permstring, perms));
+       }
+
+       for (i = 0; i < num_dir_acls; i++) {
+               uint32 uorg;
+               fstring permstring;
+               unsigned char tagtype = CVAL(retbuf, SMB_POSIX_ACL_HEADER_SIZE+((i+num_file_acls)*SMB_POSIX_ACL_ENTRY_SIZE));
+               unsigned char perms = CVAL(retbuf, SMB_POSIX_ACL_HEADER_SIZE+((i+num_file_acls)*SMB_POSIX_ACL_ENTRY_SIZE)+1);
+
+               switch(tagtype) {
+                       case SMB_POSIX_ACL_USER_OBJ:
+                               d_printf("default:user::");
+                               break;
+                       case SMB_POSIX_ACL_USER:
+                               uorg = IVAL(retbuf,SMB_POSIX_ACL_HEADER_SIZE+((i+num_file_acls)*SMB_POSIX_ACL_ENTRY_SIZE)+2);
+                               d_printf("default:user:%u:", uorg);
+                               break;
+                       case SMB_POSIX_ACL_GROUP_OBJ:
+                               d_printf("default:group::");
+                               break;
+                       case SMB_POSIX_ACL_GROUP:
+                               uorg = IVAL(retbuf,SMB_POSIX_ACL_HEADER_SIZE+((i+num_file_acls)*SMB_POSIX_ACL_ENTRY_SIZE)+2);
+                               d_printf("default:group:%u", uorg);
+                               break;
+                       case SMB_POSIX_ACL_MASK:
+                               d_printf("default:mask::");
+                               break;
+                       case SMB_POSIX_ACL_OTHER:
+                               d_printf("default:other::");
+                               break;
+                       default:
+                               d_printf("getfacl file %s, incorrect POSIX acl tagtype (%u).\n",
+                                       src, (unsigned int)tagtype );
+                               SAFE_FREE(retbuf);
+                               return 1;
+               }
+
+               d_printf("%s\n", perms_to_string(permstring, perms));
+       }
+
+       SAFE_FREE(retbuf);
+       return 0;
+}
+
+/****************************************************************************
+ UNIX stat.
+****************************************************************************/
+
+static int cmd_stat(void)
+{
+       pstring src, name;
+       fstring mode_str;
+       SMB_STRUCT_STAT sbuf;
+       struct cli_state *targetcli;
+       struct tm *lt;
+       pstring targetname;
        if (!SERVER_HAS_UNIX_CIFS(cli)) {
                d_printf("Server doesn't support UNIX CIFS calls.\n");
                return 1;
@@ -1724,6 +2558,94 @@ static int cmd_chown(void)
 
        pstrcpy(src,cur_dir);
        
+       if (!next_token_nr(NULL,name,NULL,sizeof(name))) {
+               d_printf("stat file\n");
+               return 1;
+       }
+
+       pstrcat(src,name);
+
+       
+       if ( !cli_resolve_path( "", cli, src, &targetcli, targetname ) ) {
+               d_printf("stat %s: %s\n", src, cli_errstr(cli));
+               return 1;
+       }
+       
+       if (!cli_unix_stat(targetcli, targetname, &sbuf)) {
+               d_printf("%s stat file %s\n",
+                       cli_errstr(targetcli), src);
+               return 1;
+       } 
+
+       /* Print out the stat values. */
+       d_printf("File: %s\n", src);
+       d_printf("Size: %-12.0f\tBlocks: %u\t%s\n",
+               (double)sbuf.st_size,
+               (unsigned int)sbuf.st_blocks,
+               filetype_to_str(sbuf.st_mode));
+
+#if defined(S_ISCHR) && defined(S_ISBLK)
+       if (S_ISCHR(sbuf.st_mode) || S_ISBLK(sbuf.st_mode)) {
+               d_printf("Inode: %.0f\tLinks: %u\tDevice type: %u,%u\n",
+                       (double)sbuf.st_ino,
+                       (unsigned int)sbuf.st_nlink,
+                       unix_dev_major(sbuf.st_rdev),
+                       unix_dev_minor(sbuf.st_rdev));
+       } else 
+#endif
+               d_printf("Inode: %.0f\tLinks: %u\n",
+                       (double)sbuf.st_ino,
+                       (unsigned int)sbuf.st_nlink);
+
+       d_printf("Access: (0%03o/%s)\tUid: %u\tGid: %u\n",
+               ((int)sbuf.st_mode & 0777),
+               unix_mode_to_str(mode_str, sbuf.st_mode),
+               (unsigned int)sbuf.st_uid, 
+               (unsigned int)sbuf.st_gid);
+
+       lt = localtime(&sbuf.st_atime);
+       if (lt) {
+               strftime(mode_str, sizeof(mode_str), "%Y-%m-%d %T %z", lt);
+       } else {
+               fstrcpy(mode_str, "unknown");
+       }
+       d_printf("Access: %s\n", mode_str);
+
+       lt = localtime(&sbuf.st_mtime);
+       if (lt) {
+               strftime(mode_str, sizeof(mode_str), "%Y-%m-%d %T %z", lt);
+       } else {
+               fstrcpy(mode_str, "unknown");
+       }
+       d_printf("Modify: %s\n", mode_str);
+
+       lt = localtime(&sbuf.st_ctime);
+       if (lt) {
+               strftime(mode_str, sizeof(mode_str), "%Y-%m-%d %T %z", lt);
+       } else {
+               fstrcpy(mode_str, "unknown");
+       }
+       d_printf("Change: %s\n", mode_str);
+       
+       return 0;
+}
+
+
+/****************************************************************************
+ UNIX chown.
+****************************************************************************/
+
+static int cmd_chown(void)
+{
+       pstring src;
+       uid_t uid;
+       gid_t gid;
+       pstring buf, buf2, buf3;
+       struct cli_state *targetcli;
+       pstring targetname;
+  
+       pstrcpy(src,cur_dir);
+       
        if (!next_token_nr(NULL,buf,NULL,sizeof(buf)) || 
            !next_token_nr(NULL,buf2,NULL, sizeof(buf2)) ||
            !next_token_nr(NULL,buf3,NULL, sizeof(buf3))) {
@@ -1735,9 +2657,19 @@ static int cmd_chown(void)
        gid = (gid_t)atoi(buf2);
        pstrcat(src,buf3);
 
-       if (!cli_unix_chown(cli, src, uid, gid)) {
+       if ( !cli_resolve_path( "", cli, src, &targetcli, targetname ) ) {
+               d_printf("chown %s: %s\n", src, cli_errstr(cli));
+               return 1;
+       }
+
+       if (!SERVER_HAS_UNIX_CIFS(targetcli)) {
+               d_printf("Server doesn't support UNIX CIFS calls.\n");
+               return 1;
+       }
+       
+       if (!cli_unix_chown(targetcli, targetname, uid, gid)) {
                d_printf("%s chown file %s uid=%d, gid=%d\n",
-                       cli_errstr(cli), src, (int)uid, (int)gid);
+                       cli_errstr(targetcli), src, (int)uid, (int)gid);
                return 1;
        } 
 
@@ -1745,65 +2677,109 @@ static int cmd_chown(void)
 }
 
 /****************************************************************************
- Rename some file.
+ Rename some file.
+****************************************************************************/
+
+static int cmd_rename(void)
+{
+       pstring src,dest;
+       pstring buf,buf2;
+       struct cli_state *targetcli;
+       pstring targetsrc;
+       pstring targetdest;
+  
+       pstrcpy(src,cur_dir);
+       pstrcpy(dest,cur_dir);
+       
+       if (!next_token_nr(NULL,buf,NULL,sizeof(buf)) || 
+           !next_token_nr(NULL,buf2,NULL, sizeof(buf2))) {
+               d_printf("rename <src> <dest>\n");
+               return 1;
+       }
+
+       pstrcat(src,buf);
+       pstrcat(dest,buf2);
+
+       if ( !cli_resolve_path( "", cli, src, &targetcli, targetsrc ) ) {
+               d_printf("rename %s: %s\n", src, cli_errstr(cli));
+               return 1;
+       }
+
+       if ( !cli_resolve_path( "", cli, dest, &targetcli, targetdest ) ) {
+               d_printf("rename %s: %s\n", dest, cli_errstr(cli));
+               return 1;
+       }
+
+       if (!cli_rename(targetcli, targetsrc, targetdest)) {
+               d_printf("%s renaming files %s -> %s \n",
+                       cli_errstr(targetcli),
+                       targetsrc,
+                       targetdest);
+               return 1;
+       }
+       
+       return 0;
+}
+
+/****************************************************************************
+ Print the volume name.
 ****************************************************************************/
 
-static int cmd_rename(void)
+static int cmd_volume(void)
 {
-       pstring src,dest;
-       fstring buf,buf2;
+       fstring volname;
+       uint32 serial_num;
+       time_t create_date;
   
-       pstrcpy(src,cur_dir);
-       pstrcpy(dest,cur_dir);
-       
-       if (!next_token_nr(NULL,buf,NULL,sizeof(buf)) || 
-           !next_token_nr(NULL,buf2,NULL, sizeof(buf2))) {
-               d_printf("rename <src> <dest>\n");
-               return 1;
-       }
-
-       pstrcat(src,buf);
-       pstrcat(dest,buf2);
-
-       if (!cli_rename(cli, src, dest)) {
-               d_printf("%s renaming files\n",cli_errstr(cli));
+       if (!cli_get_fs_volume_info(cli, volname, &serial_num, &create_date)) {
+               d_printf("Errr %s getting volume info\n",cli_errstr(cli));
                return 1;
        }
        
+       d_printf("Volume: |%s| serial number 0x%x\n", volname, (unsigned int)serial_num);
        return 0;
 }
 
-#if 0
- This will become a hard link call. JRA.
 /****************************************************************************
Rename some file using the NT call.
Hard link files using the NT call.
 ****************************************************************************/
 
-static int cmd_ntrename(void)
+static int cmd_hardlink(void)
 {
        pstring src,dest;
-       fstring buf,buf2;
+       pstring buf,buf2;
+       struct cli_state *targetcli;
+       pstring targetname;
   
        pstrcpy(src,cur_dir);
        pstrcpy(dest,cur_dir);
        
        if (!next_token_nr(NULL,buf,NULL,sizeof(buf)) || 
            !next_token_nr(NULL,buf2,NULL, sizeof(buf2))) {
-               d_printf("ntrename <src> <dest>\n");
+               d_printf("hardlink <src> <dest>\n");
                return 1;
        }
 
        pstrcat(src,buf);
        pstrcat(dest,buf2);
 
-       if (!cli_ntrename(cli, src, dest)) {
-               d_printf("%s doing an NT rename of files\n",cli_errstr(cli));
+       if ( !cli_resolve_path( "", cli, src, &targetcli, targetname ) ) {
+               d_printf("hardlink %s: %s\n", src, cli_errstr(cli));
+               return 1;
+       }
+       
+       if (!SERVER_HAS_UNIX_CIFS(targetcli)) {
+               d_printf("Server doesn't support UNIX CIFS calls.\n");
+               return 1;
+       }
+       
+       if (!cli_nt_hardlink(targetcli, targetname, dest)) {
+               d_printf("%s doing an NT hard link of files\n",cli_errstr(targetcli));
                return 1;
        }
        
        return 0;
 }
-#endif
 
 /****************************************************************************
  Toggle the prompt flag.
@@ -1823,7 +2799,7 @@ static int cmd_prompt(void)
 
 static int cmd_newer(void)
 {
-       fstring buf;
+       pstring buf;
        BOOL ok;
        SMB_STRUCT_STAT sbuf;
 
@@ -1831,7 +2807,7 @@ static int cmd_newer(void)
        if (ok && (sys_stat(buf,&sbuf) == 0)) {
                newer_than = sbuf.st_mtime;
                DEBUG(1,("Getting files newer than %s",
-                        asctime(LocalTime(&newer_than))));
+                        time_to_asc(newer_than)));
        } else {
                newer_than = 0;
        }
@@ -1850,7 +2826,7 @@ static int cmd_newer(void)
 
 static int cmd_archive(void)
 {
-       fstring buf;
+       pstring buf;
 
        if (next_token_nr(NULL,buf,NULL,sizeof(buf))) {
                archive_level = atoi(buf);
@@ -1873,63 +2849,60 @@ static int cmd_lowercase(void)
 }
 
 /****************************************************************************
- Toggle the recurse flag.
+ Toggle the case sensitive flag.
 ****************************************************************************/
 
-static int cmd_recurse(void)
+static int cmd_setcase(void)
 {
-       recurse = !recurse;
-       DEBUG(2,("directory recursion is now %s\n",recurse?"on":"off"));
+       BOOL orig_case_sensitive = cli_set_case_sensitive(cli, False);
+
+       cli_set_case_sensitive(cli, !orig_case_sensitive);
+       DEBUG(2,("filename case sensitivity is now %s\n",!orig_case_sensitive ?
+               "on":"off"));
 
        return 0;
 }
 
 /****************************************************************************
- Toggle the translate flag.
+ Toggle the showacls flag.
 ****************************************************************************/
 
-static int cmd_translate(void)
+static int cmd_showacls(void)
 {
-       translation = !translation;
-       DEBUG(2,("CR/LF<->LF and print text translation now %s\n",
-                translation?"on":"off"));
+       showacls = !showacls;
+       DEBUG(2,("showacls is now %s\n",showacls?"on":"off"));
+
+       if (!ctx && showacls)
+               ctx = talloc_init("smbclient:showacls");
+               if (!ctx) {
+                       DEBUG( 0, ("cmd_showacls() out of memory.  talloc_init() failed.\n"));
+       }
 
        return 0;
 }
 
+
 /****************************************************************************
Do a printmode command.
Toggle the recurse flag.
 ****************************************************************************/
 
-static int cmd_printmode(void)
+static int cmd_recurse(void)
 {
-       fstring buf;
-       fstring mode;
+       recurse = !recurse;
+       DEBUG(2,("directory recursion is now %s\n",recurse?"on":"off"));
 
-       if (next_token_nr(NULL,buf,NULL,sizeof(buf))) {
-               if (strequal(buf,"text")) {
-                       printmode = 0;      
-               } else {
-                       if (strequal(buf,"graphics"))
-                               printmode = 1;
-                       else
-                               printmode = atoi(buf);
-               }
-       }
+       return 0;
+}
 
-       switch(printmode) {
-               case 0: 
-                       fstrcpy(mode,"text");
-                       break;
-               case 1: 
-                       fstrcpy(mode,"graphics");
-                       break;
-               default: 
-                       slprintf(mode,sizeof(mode)-1,"%d",printmode);
-                       break;
-       }
-       
-       DEBUG(2,("the printmode is now %s\n",mode));
+/****************************************************************************
+ Toggle the translate flag.
+****************************************************************************/
+
+static int cmd_translate(void)
+{
+       translation = !translation;
+       DEBUG(2,("CR/LF<->LF and print text translation now %s\n",
+                translation?"on":"off"));
 
        return 0;
 }
@@ -1940,7 +2913,7 @@ static int cmd_printmode(void)
 
 static int cmd_lcd(void)
 {
-       fstring buf;
+       pstring buf;
        pstring d;
        
        if (next_token_nr(NULL,buf,NULL,sizeof(buf)))
@@ -1961,7 +2934,7 @@ static int cmd_reget(void)
        char *p;
 
        pstrcpy(remote_name, cur_dir);
-       pstrcat(remote_name, "\\");
+       pstrcat(remote_name, CLI_DIRSEP_STR);
        
        p = remote_name + strlen(remote_name);
        
@@ -1970,7 +2943,7 @@ static int cmd_reget(void)
                return 1;
        }
        pstrcpy(local_name, p);
-       dos_clean_name(remote_name);
+       clean_name(remote_name);
        
        next_token_nr(NULL, local_name, NULL, sizeof(local_name));
        
@@ -1985,12 +2958,12 @@ static int cmd_reput(void)
 {
        pstring local_name;
        pstring remote_name;
-       fstring buf;
+       pstring buf;
        char *p = buf;
        SMB_STRUCT_STAT st;
        
        pstrcpy(remote_name, cur_dir);
-       pstrcat(remote_name, "\\");
+       pstrcat(remote_name, CLI_DIRSEP_STR);
   
        if (!next_token_nr(NULL, p, NULL, sizeof(buf))) {
                d_printf("reput <filename>\n");
@@ -2008,7 +2981,7 @@ static int cmd_reput(void)
        else
                pstrcat(remote_name, local_name);
        
-       dos_clean_name(remote_name);
+       clean_name(remote_name);
 
        return do_put(remote_name, local_name, True);
 }
@@ -2024,7 +2997,7 @@ static void browse_fn(const char *name, uint32 m,
 
         *typestr=0;
 
-        switch (m)
+        switch (m & 7)
         {
           case STYPE_DISKTREE:
             fstrcpy(typestr,"Disk"); break;
@@ -2046,6 +3019,57 @@ static void browse_fn(const char *name, uint32 m,
        }
 }
 
+static BOOL browse_host_rpc(BOOL sort)
+{
+       NTSTATUS status;
+       struct rpc_pipe_client *pipe_hnd;
+       TALLOC_CTX *mem_ctx;
+       ENUM_HND enum_hnd;
+       WERROR werr;
+       SRV_SHARE_INFO_CTR ctr;
+       int i;
+
+       mem_ctx = talloc_new(NULL);
+       if (mem_ctx == NULL) {
+               DEBUG(0, ("talloc_new failed\n"));
+               return False;
+       }
+
+       init_enum_hnd(&enum_hnd, 0);
+
+       pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SRVSVC, &status);
+
+       if (pipe_hnd == NULL) {
+               DEBUG(10, ("Could not connect to srvsvc pipe: %s\n",
+                          nt_errstr(status)));
+               TALLOC_FREE(mem_ctx);
+               return False;
+       }
+
+       werr = rpccli_srvsvc_net_share_enum(pipe_hnd, mem_ctx, 1, &ctr,
+                                           0xffffffff, &enum_hnd);
+
+       if (!W_ERROR_IS_OK(werr)) {
+               TALLOC_FREE(mem_ctx);
+               cli_rpc_pipe_close(pipe_hnd);
+               return False;
+       }
+
+       for (i=0; i<ctr.num_entries; i++) {
+               SRV_SHARE_INFO_1 *info = &ctr.share.info1[i];
+               char *name, *comment;
+               name = rpcstr_pull_unistr2_talloc(
+                       mem_ctx, &info->info_1_str.uni_netname);
+               comment = rpcstr_pull_unistr2_talloc(
+                       mem_ctx, &info->info_1_str.uni_remark);
+               browse_fn(name, info->info_1.type, comment, NULL);
+       }
+
+       TALLOC_FREE(mem_ctx);
+       cli_rpc_pipe_close(pipe_hnd);
+       return True;
+}
+
 /****************************************************************************
  Try and browse available connections on a host.
 ****************************************************************************/
@@ -2058,6 +3082,10 @@ static BOOL browse_host(BOOL sort)
                d_printf("\t---------       ----      -------\n");
        }
 
+       if (browse_host_rpc(sort)) {
+               return True;
+       }
+
        if((ret = cli_RNetShareEnum(cli, browse_fn, NULL)) == -1)
                d_printf("Error returning browse list: %s\n", cli_errstr(cli));
 
@@ -2085,6 +3113,8 @@ static void server_fn(const char *name, uint32 m,
 
 static BOOL list_servers(const char *wk_grp)
 {
+       fstring state;
+
        if (!cli->server_domain)
                return False;
 
@@ -2092,16 +3122,18 @@ static BOOL list_servers(const char *wk_grp)
                d_printf("\n\tServer               Comment\n");
                d_printf("\t---------            -------\n");
        };
+       fstrcpy( state, "Server" );
        cli_NetServerEnum(cli, cli->server_domain, SV_TYPE_ALL, server_fn,
-                         "Server");
+                         state);
 
        if (!grepable) {
                d_printf("\n\tWorkgroup            Master\n");
                d_printf("\t---------            -------\n");
        }; 
 
+       fstrcpy( state, "Workgroup" );
        cli_NetServerEnum(cli, cli->server_domain, SV_TYPE_DOMAIN_ENUM,
-                         server_fn, "Workgroup");
+                         server_fn, state);
        return True;
 }
 
@@ -2129,7 +3161,7 @@ static int cmd_vuid(void)
 static int cmd_logon(void)
 {
        pstring l_username, l_password;
-       fstring buf,buf2;
+       pstring buf,buf2;
   
        if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
                d_printf("logon <username> [<password>]\n");
@@ -2138,20 +3170,19 @@ static int cmd_logon(void)
 
        pstrcpy(l_username, buf);
 
-       if (!next_token_nr(NULL,buf2,NULL,sizeof(buf))) {
+       if (!next_token_nr(NULL,buf2,NULL,sizeof(buf))) 
+       {
                char *pass = getpass("Password: ");
-               if (pass) {
+               if (pass) 
                        pstrcpy(l_password, pass);
-                       got_pass = 1;
-               }
-       } else {
+       } 
+       else
                pstrcpy(l_password, buf2);
-       }
 
-       if (!cli_session_setup(cli, l_username, 
-                              l_password, strlen(l_password),
-                              l_password, strlen(l_password),
-                              lp_workgroup())) {
+       if (!NT_STATUS_IS_OK(cli_session_setup(cli, l_username, 
+                                              l_password, strlen(l_password),
+                                              l_password, strlen(l_password),
+                                              lp_workgroup()))) {
                d_printf("session setup failed: %s\n", cli_errstr(cli));
                return -1;
        }
@@ -2160,6 +3191,36 @@ static int cmd_logon(void)
        return 0;
 }
 
+
+/****************************************************************************
+ list active connections
+****************************************************************************/
+
+static int cmd_list_connect(void)
+{
+       cli_cm_display();
+
+       return 0;
+}
+
+/****************************************************************************
+ display the current active client connection
+****************************************************************************/
+
+static int cmd_show_connect( void )
+{
+       struct cli_state *targetcli;
+       pstring targetpath;
+       
+       if ( !cli_resolve_path( "", cli, cur_dir, &targetcli, targetpath ) ) {
+               d_printf("showconnect %s: %s\n", cur_dir, cli_errstr(cli));
+               return 1;
+       }
+       
+       d_printf("//%s/%s\n", targetcli->desthost, targetcli->share);
+       return 0;
+}
+
 /* Some constants for completing filename arguments */
 
 #define COMPL_NONE        0          /* No completions */
@@ -2183,20 +3244,27 @@ static struct
   {"archive",cmd_archive,"<level>\n0=ignore archive bit\n1=only get archive files\n2=only get archive files and reset archive bit\n3=get all files and reset archive bit",{COMPL_NONE,COMPL_NONE}},
   {"blocksize",cmd_block,"blocksize <number> (default 20)",{COMPL_NONE,COMPL_NONE}},
   {"cancel",cmd_cancel,"<jobid> cancel a print queue entry",{COMPL_NONE,COMPL_NONE}},
+  {"case_sensitive",cmd_setcase,"toggle the case sensitive flag to server",{COMPL_NONE,COMPL_NONE}},
   {"cd",cmd_cd,"[directory] change/report the remote directory",{COMPL_REMOTE,COMPL_NONE}},
   {"chmod",cmd_chmod,"<src> <mode> chmod a file using UNIX permission",{COMPL_REMOTE,COMPL_REMOTE}},
   {"chown",cmd_chown,"<src> <uid> <gid> chown a file using UNIX uids and gids",{COMPL_REMOTE,COMPL_REMOTE}},
+  {"close",cmd_close,"<fid> close a file given a fid",{COMPL_REMOTE,COMPL_REMOTE}},
   {"del",cmd_del,"<mask> delete all matching files",{COMPL_REMOTE,COMPL_NONE}},
   {"dir",cmd_dir,"<mask> list the contents of the current directory",{COMPL_REMOTE,COMPL_NONE}},
   {"du",cmd_du,"<mask> computes the total size of the current directory",{COMPL_REMOTE,COMPL_NONE}},
+  {"echo",cmd_echo,"ping the server",{COMPL_NONE,COMPL_NONE}},
   {"exit",cmd_quit,"logoff the server",{COMPL_NONE,COMPL_NONE}},
   {"get",cmd_get,"<remote name> [local name] get a file",{COMPL_REMOTE,COMPL_LOCAL}},
+  {"getfacl",cmd_getfacl,"<file name> get the POSIX ACL on a file (UNIX extensions only)",{COMPL_REMOTE,COMPL_LOCAL}},
+  {"hardlink",cmd_hardlink,"<src> <dest> create a Windows hard link",{COMPL_REMOTE,COMPL_REMOTE}},
   {"help",cmd_help,"[command] give help on a command",{COMPL_NONE,COMPL_NONE}},
   {"history",cmd_history,"displays the command history",{COMPL_NONE,COMPL_NONE}},
   {"lcd",cmd_lcd,"[directory] change/report the local current working directory",{COMPL_LOCAL,COMPL_NONE}},
-  {"link",cmd_link,"<src> <dest> create a UNIX hard link",{COMPL_REMOTE,COMPL_REMOTE}},
+  {"link",cmd_link,"<oldname> <newname> create a UNIX hard link",{COMPL_REMOTE,COMPL_REMOTE}},
+  {"lock",cmd_lock,"lock <fnum> [r|w] <hex-start> <hex-len> : set a POSIX lock",{COMPL_REMOTE,COMPL_REMOTE}},
   {"lowercase",cmd_lowercase,"toggle lowercasing of filenames for get",{COMPL_NONE,COMPL_NONE}},  
   {"ls",cmd_dir,"<mask> list the contents of the current directory",{COMPL_REMOTE,COMPL_NONE}},
+  {"l",cmd_dir,"<mask> list the contents of the current directory",{COMPL_REMOTE,COMPL_NONE}},
   {"mask",cmd_select,"<mask> mask all filenames against this",{COMPL_REMOTE,COMPL_NONE}},
   {"md",cmd_mkdir,"<directory> make a directory",{COMPL_NONE,COMPL_NONE}},
   {"mget",cmd_mget,"<mask> get all the matching files",{COMPL_REMOTE,COMPL_NONE}},
@@ -2204,13 +3272,13 @@ static struct
   {"more",cmd_more,"<remote name> view a remote file with your pager",{COMPL_REMOTE,COMPL_NONE}},  
   {"mput",cmd_mput,"<mask> put all matching files",{COMPL_REMOTE,COMPL_NONE}},
   {"newer",cmd_newer,"<file> only mget files newer than the specified local file",{COMPL_LOCAL,COMPL_NONE}},
-#if 0
-  /* This call will eventually morph into a hard link call. JRA */
-  {"ntrename",cmd_ntrename,"<src> <dest> NT rename some files",{COMPL_REMOTE,COMPL_REMOTE}},
-#endif
   {"open",cmd_open,"<mask> open a file",{COMPL_REMOTE,COMPL_NONE}},
+  {"posix", cmd_posix, "turn on all POSIX capabilities", {COMPL_REMOTE,COMPL_NONE}},
+  {"posix_open",cmd_posix_open,"<name> 0<mode> open_flags mode open a file using POSIX interface",{COMPL_REMOTE,COMPL_NONE}},
+  {"posix_mkdir",cmd_posix_mkdir,"<name> 0<mode> creates a directory using POSIX interface",{COMPL_REMOTE,COMPL_NONE}},
+  {"posix_rmdir",cmd_posix_rmdir,"<name> removes a directory using POSIX interface",{COMPL_REMOTE,COMPL_NONE}},
+  {"posix_unlink",cmd_posix_unlink,"<name> removes a file using POSIX interface",{COMPL_REMOTE,COMPL_NONE}},
   {"print",cmd_print,"<file name> print a file",{COMPL_NONE,COMPL_NONE}},
-  {"printmode",cmd_printmode,"<graphics or text> set the print mode",{COMPL_NONE,COMPL_NONE}},
   {"prompt",cmd_prompt,"toggle prompting for filenames for mget and mput",{COMPL_NONE,COMPL_NONE}},  
   {"put",cmd_put,"<local name> [remote name] put a file",{COMPL_LOCAL,COMPL_REMOTE}},
   {"pwd",cmd_pwd,"show current remote directory (same as 'cd' with no args)",{COMPL_NONE,COMPL_NONE}},
@@ -2224,14 +3292,22 @@ static struct
   {"reput",cmd_reput,"<local name> [remote name] put a file restarting at end of remote file",{COMPL_LOCAL,COMPL_REMOTE}},
   {"rm",cmd_del,"<mask> delete all matching files",{COMPL_REMOTE,COMPL_NONE}},
   {"rmdir",cmd_rmdir,"<directory> remove a directory",{COMPL_NONE,COMPL_NONE}},
+  {"showacls",cmd_showacls,"toggle if ACLs are shown or not",{COMPL_NONE,COMPL_NONE}},  
   {"setmode",cmd_setmode,"filename <setmode string> change modes of file",{COMPL_REMOTE,COMPL_NONE}},
-  {"symlink",cmd_symlink,"<src> <dest> create a UNIX symlink",{COMPL_REMOTE,COMPL_REMOTE}},
+  {"stat",cmd_stat,"filename Do a UNIX extensions stat call on a file",{COMPL_REMOTE,COMPL_REMOTE}},
+  {"symlink",cmd_symlink,"<oldname> <newname> create a UNIX symlink",{COMPL_REMOTE,COMPL_REMOTE}},
   {"tar",cmd_tar,"tar <c|x>[IXFqbgNan] current directory to/from <file name>",{COMPL_NONE,COMPL_NONE}},
   {"tarmode",cmd_tarmode,"<full|inc|reset|noreset> tar's behaviour towards archive bits",{COMPL_NONE,COMPL_NONE}},
   {"translate",cmd_translate,"toggle text translation for printing",{COMPL_NONE,COMPL_NONE}},
+  {"unlock",cmd_unlock,"unlock <fnum> <hex-start> <hex-len> : remove a POSIX lock",{COMPL_REMOTE,COMPL_REMOTE}},
+  {"volume",cmd_volume,"print the volume name",{COMPL_NONE,COMPL_NONE}},
   {"vuid",cmd_vuid,"change current vuid",{COMPL_NONE,COMPL_NONE}},
+  {"wdel",cmd_wdel,"<attrib> <mask> wildcard delete all matching files",{COMPL_REMOTE,COMPL_NONE}},
   {"logon",cmd_logon,"establish new logon",{COMPL_NONE,COMPL_NONE}},
-  
+  {"listconnect",cmd_list_connect,"list open connections",{COMPL_NONE,COMPL_NONE}},
+  {"showconnect",cmd_show_connect,"display the current active connection",{COMPL_NONE,COMPL_NONE}},
+  {"..",cmd_cd_oneup,"change the remote directory (up one level)",{COMPL_REMOTE,COMPL_NONE}},
+
   /* Yes, this must be here, see crh's comment above. */
   {"!",NULL,"run a shell command on the local system",{COMPL_NONE,COMPL_NONE}},
   {NULL,NULL,NULL,{COMPL_NONE,COMPL_NONE}}
@@ -2242,7 +3318,7 @@ static struct
  abbreviations.
 ******************************************************************/
 
-static int process_tok(fstring tok)
+static int process_tok(pstring tok)
 {
        int i = 0, matches = 0;
        int cmd=0;
@@ -2275,7 +3351,7 @@ static int process_tok(fstring tok)
 static int cmd_help(void)
 {
        int i=0,j;
-       fstring buf;
+       pstring buf;
        
        if (next_token_nr(NULL,buf,NULL,sizeof(buf))) {
                if ((i = process_tok(buf)) >= 0)
@@ -2305,14 +3381,14 @@ static int process_command_string(char *cmd)
        /* establish the connection if not already */
        
        if (!cli) {
-               cli = do_connect(desthost, service);
+               cli = cli_cm_open(desthost, service, True);
                if (!cli)
                        return 0;
        }
        
        while (cmd[0] != '\0')    {
                char *p;
-               fstring tok;
+               pstring tok;
                int i;
                
                if ((p = strchr_m(cmd, ';')) == 0) {
@@ -2353,13 +3429,13 @@ typedef struct {
        int len;
 } completion_remote_t;
 
-static void completion_remote_filter(file_info *f, const char *mask, void *state)
+static void completion_remote_filter(const char *mnt, file_info *f, const char *mask, void *state)
 {
        completion_remote_t *info = (completion_remote_t *)state;
 
        if ((info->count < MAX_COMPLETIONS - 1) && (strncmp(info->text, f->name, info->len) == 0) && (strcmp(f->name, ".") != 0) && (strcmp(f->name, "..") != 0)) {
                if ((info->dirmask[0] == 0) && !(f->mode & aDIR))
-                       info->matches[info->count] = strdup(f->name);
+                       info->matches[info->count] = SMB_STRDUP(f->name);
                else {
                        pstring tmp;
 
@@ -2370,7 +3446,7 @@ static void completion_remote_filter(file_info *f, const char *mask, void *state
                        pstrcat(tmp, f->name);
                        if (f->mode & aDIR)
                                pstrcat(tmp, "/");
-                       info->matches[info->count] = strdup(tmp);
+                       info->matches[info->count] = SMB_STRDUP(tmp);
                }
                if (info->matches[info->count] == NULL)
                        return;
@@ -2398,16 +3474,29 @@ static char **remote_completion(const char *text, int len)
        info.text = text;
        info.len = len;
                
-       if (len >= PATH_MAX)
+       if (len >= MIN(PATH_MAX,sizeof(pstring))) {
                return(NULL);
+       }
+
+       info.matches = SMB_MALLOC_ARRAY(char *,MAX_COMPLETIONS);
+       if (!info.matches) {
+               return NULL;
+       }
 
-       info.matches = (char **)malloc(sizeof(info.matches[0])*MAX_COMPLETIONS);
-       if (!info.matches) return NULL;
+       /*
+        * We're leaving matches[0] free to fill it later with the text to
+        * display: Either the one single match or the longest common subset
+        * of the matches.
+        */
        info.matches[0] = NULL;
+       info.count = 1;
 
-       for (i = len-1; i >= 0; i--)
-               if ((text[i] == '/') || (text[i] == '\\'))
+       for (i = len-1; i >= 0; i--) {
+               if ((text[i] == '/') || (text[i] == CLI_DIRSEP_CHAR)) {
                        break;
+               }
+       }
+
        info.text = text+i+1;
        info.samelen = info.len = len-i-1;
 
@@ -2415,21 +3504,43 @@ static char **remote_completion(const char *text, int len)
                strncpy(info.dirmask, text, i+1);
                info.dirmask[i+1] = 0;
                pstr_sprintf(dirmask, "%s%*s*", cur_dir, i-1, text);
-       } else
+       } else {
                pstr_sprintf(dirmask, "%s*", cur_dir);
+       }
 
        if (cli_list(cli, dirmask, aDIR | aSYSTEM | aHIDDEN, completion_remote_filter, &info) < 0)
                goto cleanup;
 
-       if (info.count == 2)
-               info.matches[0] = strdup(info.matches[1]);
-       else {
-               info.matches[0] = malloc(info.samelen+1);
-               if (!info.matches[0])
-                       goto cleanup;
-               strncpy(info.matches[0], info.matches[1], info.samelen);
-               info.matches[0][info.samelen] = 0;
+       if (info.count == 1) {
+
+               /*
+                * No matches at all, NULL indicates there is nothing
+                */
+
+               SAFE_FREE(info.matches[0]);
+               SAFE_FREE(info.matches);
+               return NULL;
        }
+
+       if (info.count == 2) {
+
+               /*
+                * Exactly one match in matches[1], indicate this is the one
+                * in matches[0].
+                */
+
+               info.matches[0] = info.matches[1];
+               info.matches[1] = NULL;
+               info.count -= 1;
+               return info.matches;
+       }
+
+       /*
+        * We got more than one possible match, set the result to the maximum
+        * common subset
+        */
+
+       info.matches[0] = SMB_STRNDUP(info.matches[1], info.samelen);
        info.matches[info.count] = NULL;
        return info.matches;
 
@@ -2456,10 +3567,13 @@ static char **completion_fn(const char *text, int start, int end)
                sp = strchr(buf, ' ');
                if (sp == NULL)
                        return NULL;
-               
-               for (i = 0; commands[i].name; i++)
-                       if ((strncmp(commands[i].name, text, sp - buf) == 0) && (commands[i].name[sp - buf] == 0))
+
+               for (i = 0; commands[i].name; i++) {
+                       if ((strncmp(commands[i].name, buf, sp - buf) == 0) &&
+                           (commands[i].name[sp - buf] == 0)) {
                                break;
+                       }
+               }
                if (commands[i].name == NULL)
                        return NULL;
 
@@ -2477,16 +3591,18 @@ static char **completion_fn(const char *text, int start, int end)
                        return NULL;
        } else {
                char **matches;
-               int i, len, samelen, count=1;
+               int i, len, samelen = 0, count=1;
 
-               matches = (char **)malloc(sizeof(matches[0])*MAX_COMPLETIONS);
-               if (!matches) return NULL;
+               matches = SMB_MALLOC_ARRAY(char *, MAX_COMPLETIONS);
+               if (!matches) {
+                       return NULL;
+               }
                matches[0] = NULL;
 
                len = strlen(text);
                for (i=0;commands[i].fn && count < MAX_COMPLETIONS-1;i++) {
                        if (strncmp(text, commands[i].name, len) == 0) {
-                               matches[count] = strdup(commands[i].name);
+                               matches[count] = SMB_STRDUP(commands[i].name);
                                if (!matches[count])
                                        goto cleanup;
                                if (count == 1)
@@ -2503,10 +3619,10 @@ static char **completion_fn(const char *text, int start, int end)
                case 1:
                        goto cleanup;
                case 2:
-                       matches[0] = strdup(matches[1]);
+                       matches[0] = SMB_STRDUP(matches[1]);
                        break;
                default:
-                       matches[0] = malloc(samelen+1);
+                       matches[0] = (char *)SMB_MALLOC(samelen+1);
                        if (!matches[0])
                                goto cleanup;
                        strncpy(matches[0], matches[1], samelen);
@@ -2559,24 +3675,34 @@ static void readline_callback(void)
           session keepalives and then drop them here.
        */
        if (FD_ISSET(cli->fd,&fds)) {
-               receive_smb(cli->fd,cli->inbuf,0);
+               if (!receive_smb(cli->fd,cli->inbuf,0)) {
+                       DEBUG(0, ("Read from server failed, maybe it closed the "
+                               "connection\n"));
+                       return;
+               }
                goto again;
        }
       
-       cli_chkpath(cli, "\\");
+       /* Ping the server to keep the connection alive using SMBecho. */
+       {
+               unsigned char garbage[16];
+               memset(garbage, 0xf0, sizeof(garbage));
+               cli_echo(cli, 1, garbage, sizeof(garbage));
+       }
 }
 
 /****************************************************************************
  Process commands on stdin.
 ****************************************************************************/
 
-static void process_stdin(void)
+static int process_stdin(void)
 {
        const char *ptr;
+       int rc = 0;
 
        while (1) {
-               fstring tok;
-               fstring the_prompt;
+               pstring tok;
+               pstring the_prompt;
                char *cline;
                pstring line;
                int i;
@@ -2600,130 +3726,14 @@ static void process_stdin(void)
                if (!next_token_nr(&ptr,tok,NULL,sizeof(tok))) continue;
 
                if ((i = process_tok(tok)) >= 0) {
-                       commands[i].fn();
+                       rc = commands[i].fn();
                } else if (i == -2) {
                        d_printf("%s: command abbreviation ambiguous\n",tok);
                } else {
                        d_printf("%s: command not found\n",tok);
                }
        }
-}
-
-/***************************************************** 
- Return a connection to a server.
-*******************************************************/
-
-static struct cli_state *do_connect(const char *server, const char *share)
-{
-       struct cli_state *c;
-       struct nmb_name called, calling;
-       const char *server_n;
-       struct in_addr ip;
-       fstring servicename;
-       char *sharename;
-       
-       /* make a copy so we don't modify the global string 'service' */
-       fstrcpy(servicename, share);
-       sharename = servicename;
-       if (*sharename == '\\') {
-               server = sharename+2;
-               sharename = strchr_m(server,'\\');
-               if (!sharename) return NULL;
-               *sharename = 0;
-               sharename++;
-       }
-
-       server_n = server;
-       
-       zero_ip(&ip);
-
-       make_nmb_name(&calling, global_myname(), 0x0);
-       make_nmb_name(&called , server, name_type);
-
- again:
-       zero_ip(&ip);
-       if (have_ip) ip = dest_ip;
-
-       /* have to open a new connection */
-       if (!(c=cli_initialise(NULL)) || (cli_set_port(c, port) != port) ||
-           !cli_connect(c, server_n, &ip)) {
-               d_printf("Connection to %s failed\n", server_n);
-               return NULL;
-       }
-
-       c->protocol = max_protocol;
-       c->use_kerberos = use_kerberos;
-       cli_setup_signing_state(c, cmdline_auth_info.signing_state);
-               
-
-       if (!cli_session_request(c, &calling, &called)) {
-               char *p;
-               d_printf("session request to %s failed (%s)\n", 
-                        called.name, cli_errstr(c));
-               cli_shutdown(c);
-               if ((p=strchr_m(called.name, '.'))) {
-                       *p = 0;
-                       goto again;
-               }
-               if (strcmp(called.name, "*SMBSERVER")) {
-                       make_nmb_name(&called , "*SMBSERVER", 0x20);
-                       goto again;
-               }
-               return NULL;
-       }
-
-       DEBUG(4,(" session request ok\n"));
-
-       if (!cli_negprot(c)) {
-               d_printf("protocol negotiation failed\n");
-               cli_shutdown(c);
-               return NULL;
-       }
-
-       if (!got_pass) {
-               char *pass = getpass("Password: ");
-               if (pass) {
-                       pstrcpy(password, pass);
-                       got_pass = 1;
-               }
-       }
-
-       if (!cli_session_setup(c, username, 
-                              password, strlen(password),
-                              password, strlen(password),
-                              lp_workgroup())) {
-               /* if a password was not supplied then try again with a null username */
-               if (password[0] || !username[0] || use_kerberos ||
-                   !cli_session_setup(c, "", "", 0, "", 0, lp_workgroup())) { 
-                       d_printf("session setup failed: %s\n", cli_errstr(c));
-                       if (NT_STATUS_V(cli_nt_error(c)) == 
-                           NT_STATUS_V(NT_STATUS_MORE_PROCESSING_REQUIRED))
-                               d_printf("did you forget to run kinit?\n");
-                       cli_shutdown(c);
-                       return NULL;
-               }
-               d_printf("Anonymous login successful\n");
-       }
-
-       if (*c->server_domain) {
-               DEBUG(1,("Domain=[%s] OS=[%s] Server=[%s]\n",
-                       c->server_domain,c->server_os,c->server_type));
-       } else if (*c->server_os || *c->server_type){
-               DEBUG(1,("OS=[%s] Server=[%s]\n",
-                        c->server_os,c->server_type));
-       }               
-       DEBUG(4,(" session setup ok\n"));
-
-       if (!cli_send_tconX(c, sharename, "?????",
-                           password, strlen(password)+1)) {
-               d_printf("tree connect failed: %s\n", cli_errstr(c));
-               cli_shutdown(c);
-               return NULL;
-       }
-
-       DEBUG(4,(" tconx ok\n"));
-
-       return c;
+       return rc;
 }
 
 /****************************************************************************
@@ -2734,12 +3744,18 @@ static int process(char *base_directory)
 {
        int rc = 0;
 
-       cli = do_connect(desthost, service);
+       cli = cli_cm_open(desthost, service, True);
        if (!cli) {
                return 1;
        }
 
-       if (*base_directory) do_cd(base_directory);
+       if (*base_directory) {
+               rc = do_cd(base_directory);
+               if (rc) {
+                       cli_cm_shutdown();
+                       return rc;
+               }
+       }
        
        if (cmdstr) {
                rc = process_command_string(cmdstr);
@@ -2747,7 +3763,7 @@ static int process(char *base_directory)
                process_stdin();
        }
   
-       cli_shutdown(cli);
+       cli_cm_shutdown();
        return rc;
 }
 
@@ -2757,7 +3773,7 @@ static int process(char *base_directory)
 
 static int do_host_query(char *query_host)
 {
-       cli = do_connect(query_host, "IPC$");
+       cli = cli_cm_open(query_host, "IPC$", True);
        if (!cli)
                return 1;
 
@@ -2768,9 +3784,9 @@ static int do_host_query(char *query_host)
                /* Workgroups simply don't make sense over anything
                   else but port 139... */
 
-               cli_shutdown(cli);
-               port = 139;
-               cli = do_connect(query_host, "IPC$");
+               cli_cm_shutdown();
+               cli_cm_set_port( 139 );
+               cli = cli_cm_open(query_host, "IPC$", True);
        }
 
        if (cli == NULL) {
@@ -2780,12 +3796,11 @@ static int do_host_query(char *query_host)
 
        list_servers(lp_workgroup());
 
-       cli_shutdown(cli);
+       cli_cm_shutdown();
        
        return(0);
 }
 
-
 /****************************************************************************
  Handle a tar operation.
 ****************************************************************************/
@@ -2796,18 +3811,24 @@ static int do_tar_op(char *base_directory)
 
        /* do we already have a connection? */
        if (!cli) {
-               cli = do_connect(desthost, service);    
+               cli = cli_cm_open(desthost, service, True);
                if (!cli)
                        return 1;
        }
 
        recurse=True;
 
-       if (*base_directory) do_cd(base_directory);
+       if (*base_directory)  {
+               ret = do_cd(base_directory);
+               if (ret) {
+                       cli_cm_shutdown();
+                       return ret;
+               }
+       }
        
        ret=process_tar();
 
-       cli_shutdown(cli);
+       cli_cm_shutdown();
 
        return(ret);
 }
@@ -2822,8 +3843,10 @@ static int do_message_op(void)
        struct nmb_name called, calling;
        fstring server_name;
        char name_type_hex[10];
+       int msg_port;
+       NTSTATUS status;
 
-       make_nmb_name(&calling, global_myname(), 0x0);
+       make_nmb_name(&calling, calling_name, 0x0);
        make_nmb_name(&called , desthost, name_type);
 
        fstrcpy(server_name, desthost);
@@ -2831,66 +3854,55 @@ static int do_message_op(void)
        fstrcat(server_name, name_type_hex);
 
         zero_ip(&ip);
-       if (have_ip) ip = dest_ip;
+       if (have_ip) 
+               ip = dest_ip;
+
+       /* we can only do messages over port 139 (to windows clients at least) */
 
-       if (!(cli=cli_initialise(NULL)) || (cli_set_port(cli, port) != port) ||
-           !cli_connect(cli, server_name, &ip)) {
+       msg_port = port ? port : 139;
+
+       if (!(cli=cli_initialise()) || (cli_set_port(cli, msg_port) != msg_port)) {
                d_printf("Connection to %s failed\n", desthost);
                return 1;
        }
 
+       status = cli_connect(cli, server_name, &ip);
+       if (!NT_STATUS_IS_OK(status)) {
+               d_printf("Connection to %s failed. Error %s\n", desthost, nt_errstr(status));
+               return 1;
+       }
+
        if (!cli_session_request(cli, &calling, &called)) {
                d_printf("session request failed\n");
-               cli_shutdown(cli);
+               cli_cm_shutdown();
                return 1;
        }
 
        send_message();
-       cli_shutdown(cli);
+       cli_cm_shutdown();
 
        return 0;
 }
 
 
-/**
- * Process "-L hostname" option.
- *
- * We don't actually do anything yet -- we just stash the name in a
- * global variable and do the query when all options have been read.
- **/
-
-static void remember_query_host(const char *arg,
-                               pstring query_host)
-{
-       char *slash;
-       
-       while (*arg == '\\' || *arg == '/')
-               arg++;
-       pstrcpy(query_host, arg);
-       if ((slash = strchr(query_host, '/'))
-           || (slash = strchr(query_host, '\\'))) {
-               *slash = 0;
-       }
-}
-
 /****************************************************************************
   main program
 ****************************************************************************/
 
  int main(int argc,char *argv[])
 {
-       extern BOOL AllowDebugChange;
-       fstring base_directory;
+       pstring base_directory;
        int opt;
        pstring query_host;
        BOOL message = False;
-       extern char tar_type;
        pstring term_code;
        static const char *new_name_resolve_order = NULL;
        poptContext pc;
        char *p;
        int rc = 0;
        fstring new_workgroup;
+       BOOL tar_opt = False;
+       BOOL service_opt = False;
        struct poptOption long_options[] = {
                POPT_AUTOHELP
 
@@ -2913,6 +3925,7 @@ static void remember_query_host(const char *arg,
                POPT_TABLEEND
        };
        
+       load_case_tables();
 
 #ifdef KANJI
        pstrcpy(term_code, KANJI);
@@ -2927,20 +3940,44 @@ static void remember_query_host(const char *arg,
           not it was set by a command line option */
           
        set_global_myworkgroup( "" );
+       set_global_myname( "" );
 
         /* set default debug level to 0 regardless of what smb.conf sets */
        setup_logging( "smbclient", True );
        DEBUGLEVEL_CLASS[DBGC_ALL] = 1;
-       dbf = x_stderr;
-       x_setbuf( x_stderr, NULL );
+       if ((dbf = x_fdup(x_stderr))) {
+               x_setbuf( dbf, NULL );
+       }
 
-       pc = poptGetContext("smbclient", argc, (const char **) argv, long_options, 
-                               POPT_CONTEXT_KEEP_FIRST);
+       /* skip argv(0) */
+       pc = poptGetContext("smbclient", argc, (const char **) argv, long_options, 0);
        poptSetOtherOptionHelp(pc, "service <password>");
 
        in_client = True;   /* Make sure that we tell lp_load we are */
 
        while ((opt = poptGetNextOpt(pc)) != -1) {
+
+               /* if the tar option has been called previouslt, now we need to eat out the leftovers */
+               /* I see no other way to keep things sane --SSS */
+               if (tar_opt == True) {
+                       while (poptPeekArg(pc)) {
+                               poptGetArg(pc);
+                       }
+                       tar_opt = False;
+               }
+
+               /* if the service has not yet been specified lets see if it is available in the popt stack */
+               if (!service_opt && poptPeekArg(pc)) {
+                       pstrcpy(service, poptGetArg(pc));
+                       service_opt = True;
+               }
+
+               /* if the service has already been retrieved then check if we have also a password */
+               if (service_opt && (!cmdline_auth_info.got_pass) && poptPeekArg(pc)) {
+                       pstrcpy(cmdline_auth_info.password, poptGetArg(pc));
+                       cmdline_auth_info.got_pass = True;
+               }
+       
                switch (opt) {
                case 'M':
                        /* Messages are sent to NetBIOS name type 0x3
@@ -2948,9 +3985,10 @@ static void remember_query_host(const char *arg,
                         * to port 139 instead of port 445. srl,crh
                         */
                        name_type = 0x03; 
+                       cli_cm_set_dest_name_type( name_type );
                        pstrcpy(desthost,poptGetOptArg(pc));
-                       if( 0 == port )
-                               port = 139;
+                       if( !port )
+                               cli_cm_set_port( 139 );
                        message = True;
                        break;
                case 'I':
@@ -2959,15 +3997,20 @@ static void remember_query_host(const char *arg,
                                if (is_zero_ip(dest_ip))
                                        exit(1);
                                have_ip = True;
+
+                               cli_cm_set_dest_ip( dest_ip );
                        }
                        break;
                case 'E':
+                       if (dbf) {
+                               x_fclose(dbf);
+                       }
                        dbf = x_stderr;
                        display_set_stderr();
                        break;
 
                case 'L':
-                       remember_query_host(poptGetOptArg(pc), query_host);
+                       pstrcpy(query_host, poptGetOptArg(pc));
                        break;
                case 't':
                        pstrcpy(term_code, poptGetOptArg(pc));
@@ -2979,26 +4022,22 @@ static void remember_query_host(const char *arg,
                        /* We must use old option processing for this. Find the
                         * position of the -T option in the raw argv[]. */
                        {
-                               int i, optnum;
+                               int i;
                                for (i = 1; i < argc; i++) {
                                        if (strncmp("-T", argv[i],2)==0)
                                                break;
                                }
                                i++;
-                               if (!(optnum = tar_parseargs(argc, argv, poptGetOptArg(pc), i))) {
+                               if (!tar_parseargs(argc, argv, poptGetOptArg(pc), i)) {
                                        poptPrintUsage(pc, stderr, 0);
                                        exit(1);
                                }
-                               /* Now we must eat (optnum - i) options - they have
-                                * been processed by tar_parseargs().
-                                */
-                               optnum -= i;
-                               for (i = 0; i < optnum; i++)
-                                       poptGetOptArg(pc);
                        }
+                       /* this must be the last option, mark we have parsed it so that we know we have */
+                       tar_opt = True;
                        break;
                case 'D':
-                       fstrcpy(base_directory,poptGetOptArg(pc));
+                       pstrcpy(base_directory,poptGetOptArg(pc));
                        break;
                case 'g':
                        grepable=True;
@@ -3006,8 +4045,31 @@ static void remember_query_host(const char *arg,
                }
        }
 
-       poptGetArg(pc);
+       /* We may still have some leftovers after the last popt option has been called */
+       if (tar_opt == True) {
+               while (poptPeekArg(pc)) {
+                       poptGetArg(pc);
+               }
+               tar_opt = False;
+       }
+
+       /* if the service has not yet been specified lets see if it is available in the popt stack */
+       if (!service_opt && poptPeekArg(pc)) {
+               pstrcpy(service, poptGetArg(pc));
+               service_opt = True;
+       }
+
+       /* if the service has already been retrieved then check if we have also a password */
+       if (service_opt && (!cmdline_auth_info.got_pass) && poptPeekArg(pc)) {
+               pstrcpy(cmdline_auth_info.password, poptGetArg(pc));
+               cmdline_auth_info.got_pass = True;
+       }
        
+       /* check for the -P option */
+
+       if ( port != 0 )
+               cli_cm_set_port( port );
+
        /*
         * Don't load debug level from smb.conf. It should be
         * set by cmdline arg or remain default (0)
@@ -3016,38 +4078,40 @@ static void remember_query_host(const char *arg,
        
        /* save the workgroup...
        
-          FIXME!! do we need to do tyhis for other options as well 
+          FIXME!! do we need to do this for other options as well 
           (or maybe a generic way to keep lp_load() from overwriting 
           everything)?  */
        
-       fstrcpy( new_workgroup, lp_workgroup() );               
+       fstrcpy( new_workgroup, lp_workgroup() );
+       pstrcpy( calling_name, global_myname() );
        
-       if (!lp_load(dyn_CONFIGFILE,True,False,False)) {
+       if ( override_logfile )
+               setup_logging( lp_logfile(), False );
+       
+       if (!lp_load(dyn_CONFIGFILE,True,False,False,True)) {
                fprintf(stderr, "%s: Can't load %s - run testparm to debug it\n",
                        argv[0], dyn_CONFIGFILE);
        }
        
        load_interfaces();
-       
-       if ( strlen(new_workgroup) != 0 )
-               set_global_myworkgroup( new_workgroup );
 
-       if(poptPeekArg(pc)) {
-               pstrcpy(service,poptGetArg(pc));  
+       if (service_opt) {
                /* Convert any '/' characters in the service name to '\' characters */
                string_replace(service, '/','\\');
-
                if (count_chars(service,'\\') < 3) {
                        d_printf("\n%s: Not enough '\\' characters in service\n",service);
                        poptPrintUsage(pc, stderr, 0);
                        exit(1);
                }
        }
+       
+       if ( strlen(new_workgroup) != 0 )
+               set_global_myworkgroup( new_workgroup );
 
-       if (poptPeekArg(pc) && !cmdline_auth_info.got_pass) { 
-               cmdline_auth_info.got_pass = True;
-               pstrcpy(cmdline_auth_info.password,poptGetArg(pc));  
-       }
+       if ( strlen(calling_name) != 0 )
+               set_global_myname( calling_name );
+       else
+               pstrcpy( calling_name, global_myname() );
 
        init_names();
 
@@ -3061,11 +4125,10 @@ static void remember_query_host(const char *arg,
 
        poptFreeContext(pc);
 
-       pstrcpy(username, cmdline_auth_info.username);
-       pstrcpy(password, cmdline_auth_info.password);
+       /* store the username an password for dfs support */
 
-       use_kerberos = cmdline_auth_info.use_kerberos;
-       got_pass = cmdline_auth_info.got_pass;
+       cli_cm_set_credentials( &cmdline_auth_info );
+       pstrcpy(username, cmdline_auth_info.username);
 
        DEBUG(3,("Client started (version %s).\n", SAMBA_VERSION_STRING));
 
@@ -3075,14 +4138,26 @@ static void remember_query_host(const char *arg,
                return do_tar_op(base_directory);
        }
 
-       if ((p=strchr_m(query_host,'#'))) {
-               *p = 0;
-               p++;
-               sscanf(p, "%x", &name_type);
-       }
-  
        if (*query_host) {
-               return do_host_query(query_host);
+               char *qhost = query_host;
+               char *slash;
+
+               while (*qhost == '\\' || *qhost == '/')
+                       qhost++;
+
+               if ((slash = strchr_m(qhost, '/'))
+                   || (slash = strchr_m(qhost, '\\'))) {
+                       *slash = 0;
+               }
+
+               if ((p=strchr_m(qhost, '#'))) {
+                       *p = 0;
+                       p++;
+                       sscanf(p, "%x", &name_type);
+                       cli_cm_set_dest_name_type( name_type );
+               }
+
+               return do_host_query(qhost);
        }
 
        if (message) {
@@ -3093,5 +4168,6 @@ static void remember_query_host(const char *arg,
                return 1;
        }
 
+       talloc_destroy( ctx);
        return rc;
 }