r23784: use the GPLv3 boilerplate as recommended by the FSF and the license text
[tprouty/samba.git] / source / libsmb / libsmbclient.c
index 3dec0c92b4860e40c6a31b3361c3a97277b2efd2..90cde9100af4d33ca01ba7ea26bdcb53ee3a8e22 100644 (file)
@@ -5,11 +5,11 @@
    Copyright (C) Richard Sharpe 2000, 2002
    Copyright (C) John Terpstra 2000
    Copyright (C) Tom Jansen (Ninja ISD) 2002 
-   Copyright (C) Derrell Lipman 2003
+   Copyright (C) Derrell Lipman 2003, 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/>.
 */
 
 #include "includes.h"
 
 #include "include/libsmb_internal.h"
 
+struct smbc_dirent *smbc_readdir_ctx(SMBCCTX *context, SMBCFILE *dir);
+struct smbc_dir_list *smbc_check_dir_ent(struct smbc_dir_list *list, 
+                                        struct smbc_dirent *dirent);
+
+/*
+ * DOS Attribute values (used internally)
+ */
+typedef struct DOS_ATTR_DESC {
+       int mode;
+       SMB_OFF_T size;
+       time_t create_time;
+       time_t access_time;
+       time_t write_time;
+       time_t change_time;
+       SMB_INO_T inode;
+} DOS_ATTR_DESC;
+
+
 /*
  * Internal flags for extended attributes
  */
@@ -65,6 +82,35 @@ static int DLIST_CONTAINS(SMBCFILE * list, SMBCFILE *p) {
        return False;
 }
 
+/*
+ * Find an lsa pipe handle associated with a cli struct.
+ */
+static struct rpc_pipe_client *
+find_lsa_pipe_hnd(struct cli_state *ipc_cli)
+{
+       struct rpc_pipe_client *pipe_hnd;
+
+       for (pipe_hnd = ipc_cli->pipe_list;
+             pipe_hnd;
+             pipe_hnd = pipe_hnd->next) {
+            
+               if (pipe_hnd->pipe_idx == PI_LSARPC) {
+                       return pipe_hnd;
+               }
+       }
+
+       return NULL;
+}
+
+static int
+smbc_close_ctx(SMBCCTX *context,
+               SMBCFILE *file);
+static off_t
+smbc_lseek_ctx(SMBCCTX *context,
+               SMBCFILE *file,
+               off_t offset,
+               int whence);
+
 extern BOOL in_client;
 
 /*
@@ -84,73 +130,104 @@ hex2int( unsigned int _char )
     return -1;
 }
 
-static void 
-decode_urlpart(char *segment, size_t sizeof_segment)
+/*
+ * smbc_urldecode()
+ *
+ * Convert strings of %xx to their single character equivalent.  Each 'x' must
+ * be a valid hexadecimal digit, or that % sequence is left undecoded.
+ *
+ * dest may, but need not be, the same pointer as src.
+ *
+ * Returns the number of % sequences which could not be converted due to lack
+ * of two following hexadecimal digits.
+ */
+int
+smbc_urldecode(char *dest, char * src, size_t max_dest_len)
+{
+        int old_length = strlen(src);
+        int i = 0;
+        int err_count = 0;
+        pstring temp;
+        char * p;
+
+        if ( old_length == 0 ) {
+                return 0;
+        }
+
+        p = temp;
+        while ( i < old_length ) {
+                unsigned char character = src[ i++ ];
+
+                if (character == '%') {
+                        int a = i+1 < old_length ? hex2int( src[i] ) : -1;
+                        int b = i+1 < old_length ? hex2int( src[i+1] ) : -1;
+
+                        /* Replace valid sequence */
+                        if (a != -1 && b != -1) {
+
+                                /* Replace valid %xx sequence with %dd */
+                                character = (a * 16) + b;
+
+                                if (character == '\0') {
+                                        break; /* Stop at %00 */
+                                }
+
+                                i += 2;
+                        } else {
+
+                                err_count++;
+                        }
+                }
+
+                *p++ = character;
+        }
+
+        *p = '\0';
+
+        strncpy(dest, temp, max_dest_len - 1);
+        dest[max_dest_len - 1] = '\0';
+
+        return err_count;
+}
+
+/*
+ * smbc_urlencode()
+ *
+ * Convert any characters not specifically allowed in a URL into their %xx
+ * equivalent.
+ *
+ * Returns the remaining buffer length.
+ */
+int
+smbc_urlencode(char * dest, char * src, int max_dest_len)
 {
-    int old_length = strlen(segment);
-    int new_length = 0;
-    int new_length2 = 0;
-    int i = 0;
-    pstring new_segment;
-    char *new_usegment = 0;
-
-    if ( !old_length ) {
-       return;
-    }
-
-    /* make a copy of the old one */
-    new_usegment = (char*)malloc( old_length * 3 + 1 );
-
-    while( i < old_length ) {
-       int bReencode = False;
-       unsigned char character = segment[ i++ ];
-       if ((character <= ' ') || (character > 127))
-           bReencode = True;
-
-       new_usegment [ new_length2++ ] = character;
-       if (character == '%' ) {
-           int a = i+1 < old_length ? hex2int( segment[i] ) : -1;
-           int b = i+1 < old_length ? hex2int( segment[i+1] ) : -1;
-           if ((a == -1) || (b == -1)) { /* Only replace if sequence is valid */
-               /* Contains stray %, make sure to re-encode! */
-               bReencode = True;
-           } else {
-               /* Valid %xx sequence */
-               character = a * 16 + b; /* Replace with value of %dd */
-               if (!character)
-                   break; /* Stop at %00 */
-
-               new_usegment [ new_length2++ ] = (unsigned char) segment[i++];
-               new_usegment [ new_length2++ ] = (unsigned char) segment[i++];
-           }
-       }
-       if (bReencode) {
-           unsigned int c = character / 16;
-           new_length2--;
-           new_usegment [ new_length2++ ] = '%';
-
-           c += (c > 9) ? ('A' - 10) : '0';
-           new_usegment[ new_length2++ ] = c;
-
-           c = character % 16;
-           c += (c > 9) ? ('A' - 10) : '0';
-           new_usegment[ new_length2++ ] = c;
-       }
-
-       new_segment [ new_length++ ] = character;
-    }
-    new_segment [ new_length ] = 0;
-
-    free(new_usegment);
-
-    /* realloc it with unix charset */
-    pull_utf8_allocate(&new_usegment, new_segment);
-
-    /* this assumes (very safely) that removing %aa sequences
-       only shortens the string */
-    strncpy(segment, new_usegment, sizeof_segment);
-
-    free(new_usegment);
+        char hex[] = "0123456789ABCDEF";
+
+        for (; *src != '\0' && max_dest_len >= 3; src++) {
+
+                if ((*src < '0' &&
+                     *src != '-' &&
+                     *src != '.') ||
+                    (*src > '9' &&
+                     *src < 'A') ||
+                    (*src > 'Z' &&
+                     *src < 'a' &&
+                     *src != '_') ||
+                    (*src > 'z')) {
+                        *dest++ = '%';
+                        *dest++ = hex[(*src >> 4) & 0x0f];
+                        *dest++ = hex[*src & 0x0f];
+                        max_dest_len -= 3;
+                } else {
+                        *dest++ = *src;
+                        max_dest_len--;
+                }
+        }
+
+        *dest++ = '\0';
+        max_dest_len--;
+        
+        return max_dest_len;
 }
 
 /*
@@ -158,48 +235,36 @@ decode_urlpart(char *segment, size_t sizeof_segment)
  *
  * The general format of an SMB URI is explain in Christopher Hertel's CIFS
  * book, at http://ubiqx.org/cifs/Appendix-D.html.  We accept a subset of the
- * general format ("smb:" only; we do not look for "cifs:"), and expand on
- * what he calls "context", herein called "options" to avoid conflict with the
- * SMBCCTX context used throughout this library.  We add the "mb" keyword
- * which applies as follows:
+ * general format ("smb:" only; we do not look for "cifs:").
  *
  *
  * We accept:
- *  smb://[[[domain;]user[:password@]]server[/share[/path[/file]]]][?options]
+ *  smb://[[[domain;]user[:password]@]server[/share[/path[/file]]]][?options]
  *
  * Meaning of URLs:
  *
- * smb://           show all workgroups known by the first master browser found
- * smb://?mb=.any   same as smb:// (i.e. without any options)
- *
- * smb://?mb=.all   show all workgroups known by every master browser found.
- *                  Why might you want this?  In an "appliance" application
- *                  where the workgroup/domain being used on the local network
- *                  is not known ahead of time, but where one wanted to
- *                  provide network services via samba, a unique workgroup
- *                  could be used.  However, when the appliance is first
- *                  started, the local samba instance's master browser has not
- *                  synchronized with the other master browser(s) on the
- *                  network (and might not synchronize for 12 minutes) and
- *                  therefore is not aware of the workgroup/ domain names
- *                  available on the network.  This option may be used to
- *                  overcome the problem of a libsmbclient application
- *                  arbitrarily selecting the local (still ignorant) master
- *                  browser to obtain its list of workgroups/domains and
- *                  getting back a practically emmpty list.  By requesting
- *                  the list of workgroups/domains from each found master
- *                  browser on the local network, a complete list of
- *                  workgroups/domains can be built.
+ * smb://           Show all workgroups.
  *
- * smb://?mb=name   NOT YET IMPLEMENTED -- show all workgroups known by the
- *                  master browser whose name is "name"
+ *                  The method of locating the list of workgroups varies
+ *                  depending upon the setting of the context variable
+ *                  context->options.browse_max_lmb_count.  This value
+ *                  determine the maximum number of local master browsers to
+ *                  query for the list of workgroups.  In order to ensure that
+ *                  a complete list of workgroups is obtained, all master
+ *                  browsers must be queried, but if there are many
+ *                  workgroups, the time spent querying can begin to add up.
+ *                  For small networks (not many workgroups), it is suggested
+ *                  that this variable be set to 0, indicating query all local
+ *                  master browsers.  When the network has many workgroups, a
+ *                  reasonable setting for this variable might be around 3.
  *
  * smb://name/      if name<1D> or name<1B> exists, list servers in
  *                  workgroup, else, if name<20> exists, list all shares
  *                  for server ...
  *
- * If "options" are provided, this function returns the entire option list as
- * a string, for later parsing by the caller.
+ * If "options" are provided, this function returns the entire option list as a
+ * string, for later parsing by the caller.  Note that currently, no options
+ * are supported.
  */
 
 static const char *smbc_prefix = "smb:";
@@ -207,6 +272,7 @@ static const char *smbc_prefix = "smb:";
 static int
 smbc_parse_path(SMBCCTX *context,
                 const char *fname,
+                char *workgroup, int workgroup_len,
                 char *server, int server_len,
                 char *share, int share_len,
                 char *path, int path_len,
@@ -221,6 +287,16 @@ smbc_parse_path(SMBCCTX *context,
        int len;
 
        server[0] = share[0] = path[0] = user[0] = password[0] = (char)0;
+
+        /*
+         * Assume we wont find an authentication domain to parse, so default
+         * to the workgroup in the provided context.
+         */
+        if (workgroup != NULL) {
+                strncpy(workgroup, context->workgroup, workgroup_len - 1);
+                workgroup[workgroup_len - 1] = '\0';
+        }
+
         if (options != NULL && options_len > 0) {
                 options[0] = (char)0;
         }
@@ -246,7 +322,7 @@ smbc_parse_path(SMBCCTX *context,
        p += 2;  /* Skip the double slash */
 
         /* See if any options were specified */
-        if ( (q = strrchr(p, '?')) != NULL ) {
+        if ((q = strrchr(p, '?')) != NULL ) {
                 /* There are options.  Null terminate here and point to them */
                 *q++ = '\0';
                 
@@ -262,11 +338,15 @@ smbc_parse_path(SMBCCTX *context,
            goto decoding;
 
        if (*p == '/') {
+               int wl = strlen(context->workgroup);
+
+               if (wl > 16) {
+                       wl = 16;
+               }
 
-               strncpy(server, context->workgroup, 
-                       (strlen(context->workgroup) < 16)?strlen(context->workgroup):16);
+               strncpy(server, context->workgroup, wl);
+                server[wl] = '\0';
                return 0;
-               
        }
 
        /*
@@ -283,19 +363,19 @@ smbc_parse_path(SMBCCTX *context,
                pstring username, passwd, domain;
                const char *u = userinfo;
 
-               next_token(&p, userinfo, "@", sizeof(fstring));
+               next_token_no_ltrim(&p, userinfo, "@", sizeof(fstring));
 
                username[0] = passwd[0] = domain[0] = 0;
 
                if (strchr_m(u, ';')) {
       
-                       next_token(&u, domain, ";", sizeof(fstring));
+                       next_token_no_ltrim(&u, domain, ";", sizeof(fstring));
 
                }
 
                if (strchr_m(u, ':')) {
 
-                       next_token(&u, username, ":", sizeof(fstring));
+                       next_token_no_ltrim(&u, username, ":", sizeof(fstring));
 
                        pstrcpy(passwd, u);
 
@@ -306,11 +386,20 @@ smbc_parse_path(SMBCCTX *context,
 
                }
 
-               if (username[0])
-                       strncpy(user, username, user_len);  /* FIXME, domain */
+                if (domain[0] && workgroup) {
+                        strncpy(workgroup, domain, workgroup_len - 1);
+                        workgroup[workgroup_len - 1] = '\0';
+                }
+
+               if (username[0]) {
+                       strncpy(user, username, user_len - 1);
+                        user[user_len - 1] = '\0';
+                }
 
-               if (passwd[0])
-                       strncpy(password, passwd, password_len);
+               if (passwd[0]) {
+                       strncpy(password, passwd, password_len - 1);
+                        password[password_len - 1] = '\0';
+                }
 
        }
 
@@ -328,16 +417,24 @@ smbc_parse_path(SMBCCTX *context,
 
        }
 
-        safe_strcpy(path, p, path_len - 1);
+        /*
+         * Prepend a leading slash if there's a file path, as required by
+         * NetApp filers.
+         */
+        *path = '\0';
+        if (*p != '\0') {
+                *path = '/';
+                safe_strcpy(path + 1, p, path_len - 2);
+        }
 
        all_string_sub(path, "/", "\\", 0);
 
  decoding:
-       decode_urlpart(path, path_len);
-       decode_urlpart(server, server_len);
-       decode_urlpart(share, share_len);
-       decode_urlpart(user, user_len);
-       decode_urlpart(password, password_len);
+       (void) smbc_urldecode(path, path, path_len);
+       (void) smbc_urldecode(server, server, server_len);
+       (void) smbc_urldecode(share, share, share_len);
+       (void) smbc_urldecode(user, user, user_len);
+       (void) smbc_urldecode(password, password, password_len);
 
        return 0;
 }
@@ -345,41 +442,29 @@ smbc_parse_path(SMBCCTX *context,
 /*
  * Verify that the options specified in a URL are valid
  */
-static int smbc_check_options(char *server, char *share, char *path, char *options)
+static int
+smbc_check_options(char *server,
+                   char *share,
+                   char *path,
+                   char *options)
 {
-        DEBUG(4, ("smbc_check_options(): server='%s' share='%s' path='%s' options='%s'\n", server, share, path, options));
+        DEBUG(4, ("smbc_check_options(): server='%s' share='%s' "
+                  "path='%s' options='%s'\n",
+                  server, share, path, options));
 
         /* No options at all is always ok */
         if (! *options) return 0;
 
-        /*
-         * For right now, we only support a very few options possibilities.
-         * No options are supported if server, share, or path are not empty.
-         * If all are empty, then we support the following two choices right
-         * now:
-         *
-         *   mb=.any
-         *   mb=.all
-         */
-        if ((*server || *share || *path) && *options) {
-                /* Invalid: options provided with server, share, or path */
-                DEBUG(1, ("Found unsupported options (%s) with non-empty server, share, or path\n", options));
-                return -1;
-        }
-
-        if (strcmp(options, "mb=.any") != 0 &&
-            strcmp(options, "mb=.all") != 0) {
-                DEBUG(1, ("Found unsupported options (%s)\n", options));
-                return -1;
-        }
-
-        return 0;
+        /* Currently, we don't support any options. */
+        return -1;
 }
 
 /*
  * Convert an SMB error into a UNIX error ...
  */
-static int smbc_errno(SMBCCTX *context, struct cli_state *c)
+static int
+smbc_errno(SMBCCTX *context,
+           struct cli_state *c)
 {
        int ret = cli_errno(c);
        
@@ -404,16 +489,40 @@ static int smbc_errno(SMBCCTX *context, struct cli_state *c)
 }
 
 /* 
- * Check a server_fd.
+ * Check a server for being alive and well.
  * returns 0 if the server is in shape. Returns 1 on error 
  * 
  * Also useable outside libsmbclient to enable external cache
  * to do some checks too.
  */
-int smbc_check_server(SMBCCTX * context, SMBCSRV * server) 
+static int
+smbc_check_server(SMBCCTX * context,
+                  SMBCSRV * server) 
 {
-       if ( send_keepalive(server->cli.fd) == False )
-               return 1;
+        socklen_t size;
+        struct sockaddr addr;
+
+        /*
+         * Although the use of port 139 is not a guarantee that we're using
+         * netbios, we assume so.  We don't want to send a keepalive packet if
+         * not netbios because it's not valid, and Vista, at least,
+         * disconnects the client on such a request.
+         */
+        if (server->cli->port == 139) {
+                /* Assuming netbios.  Send a keepalive packet */
+                if ( send_keepalive(server->cli->fd) == False ) {
+                        return 1;
+                }
+        } else {
+                /*
+                 * Assuming not netbios.  Try a different method to detect if
+                 * the connection is still alive.
+                 */
+                size = sizeof(addr);
+                if (getpeername(server->cli->fd, &addr, &size) == -1) {
+                        return 1;
+                }
+        }
 
        /* connection is ok */
        return 0;
@@ -425,7 +534,9 @@ int smbc_check_server(SMBCCTX * context, SMBCSRV * server)
  * 
  * Also useable outside libsmbclient
  */
-int smbc_remove_unused_server(SMBCCTX * context, SMBCSRV * srv)
+int
+smbc_remove_unused_server(SMBCCTX * context,
+                          SMBCSRV * srv)
 {
        SMBCFILE * file;
 
@@ -438,7 +549,8 @@ int smbc_remove_unused_server(SMBCCTX * context, SMBCSRV * srv)
        for (file = context->internal->_files; file; file=file->next) {
                if (file->srv == srv) {
                        /* Still used */
-                       DEBUG(3, ("smbc_remove_usused_server: %p still used by %p.\n", 
+                       DEBUG(3, ("smbc_remove_usused_server: "
+                                  "%p still used by %p.\n", 
                                  srv, file));
                        return 1;
                }
@@ -446,23 +558,25 @@ int smbc_remove_unused_server(SMBCCTX * context, SMBCSRV * srv)
 
        DLIST_REMOVE(context->internal->_servers, srv);
 
-       cli_shutdown(&srv->cli);
+       cli_shutdown(srv->cli);
+       srv->cli = NULL;
 
        DEBUG(3, ("smbc_remove_usused_server: %p removed.\n", srv));
 
        context->callbacks.remove_cached_srv_fn(context, srv);
-       
-       SAFE_FREE(srv);
+
+        SAFE_FREE(srv);
        
        return 0;
 }
 
-SMBCSRV *find_server(SMBCCTX *context,
-                     const char *server,
-                     const char *share,
-                     fstring workgroup,
-                     fstring username,
-                     fstring password)
+static SMBCSRV *
+find_server(SMBCCTX *context,
+            const char *server,
+            const char *share,
+            fstring workgroup,
+            fstring username,
+            fstring password)
 {
         SMBCSRV *srv;
         int auth_called = 0;
@@ -471,12 +585,23 @@ SMBCSRV *find_server(SMBCCTX *context,
 
        srv = context->callbacks.get_cached_srv_fn(context, server, share, 
                                                   workgroup, username);
-       
+
        if (!auth_called && !srv && (!username[0] || !password[0])) {
-               context->callbacks.auth_fn(server, share,
-                                           workgroup, sizeof(fstring),
-                                           username, sizeof(fstring),
-                                           password, sizeof(fstring));
+                if (context->internal->_auth_fn_with_context != NULL) {
+                         context->internal->_auth_fn_with_context(
+                                context,
+                                server, share,
+                                workgroup, sizeof(fstring),
+                                username, sizeof(fstring),
+                                password, sizeof(fstring));
+                } else {
+                        context->callbacks.auth_fn(
+                                server, share,
+                                workgroup, sizeof(fstring),
+                                username, sizeof(fstring),
+                                password, sizeof(fstring));
+                }
+
                /*
                  * However, smbc_auth_fn may have picked up info relating to
                  * an existing connection, so try for an existing connection
@@ -512,6 +637,7 @@ SMBCSRV *find_server(SMBCCTX *context,
                          */
                        goto check_server_cache; 
                }
+
                return srv;
        }
 
@@ -529,19 +655,27 @@ SMBCSRV *find_server(SMBCCTX *context,
  * info we need, unless the username and password were passed in.
  */
 
-SMBCSRV *smbc_server(SMBCCTX *context,
-                    const char *server, const char *share, 
-                    fstring workgroup, fstring username, 
-                    fstring password)
+static SMBCSRV *
+smbc_server(SMBCCTX *context,
+            BOOL connect_if_not_found,
+            const char *server,
+            const char *share, 
+            fstring workgroup,
+            fstring username, 
+            fstring password)
 {
        SMBCSRV *srv=NULL;
-       struct cli_state c;
+       struct cli_state *c;
        struct nmb_name called, calling;
        const char *server_n = server;
        pstring ipenv;
        struct in_addr ip;
        int tried_reverse = 0;
-  
+        int port_try_first;
+        int port_try_next;
+        const char *username_used;
+       NTSTATUS status;
+
        zero_ip(&ip);
        ZERO_STRUCT(c);
 
@@ -550,27 +684,81 @@ SMBCSRV *smbc_server(SMBCCTX *context,
                return NULL;
        }
 
+        /* Look for a cached connection */
         srv = find_server(context, server, share,
                           workgroup, username, password);
-        if (srv)
+        
+        /*
+         * If we found a connection and we're only allowed one share per
+         * server...
+         */
+        if (srv && *share != '\0' && context->options.one_share_per_server) {
+
+                /*
+                 * ... then if there's no current connection to the share,
+                 * connect to it.  find_server(), or rather the function
+                 * pointed to by context->callbacks.get_cached_srv_fn which
+                 * was called by find_server(), will have issued a tree
+                 * disconnect if the requested share is not the same as the
+                 * one that was already connected.
+                 */
+                if (srv->cli->cnum == (uint16) -1) {
+                        /* Ensure we have accurate auth info */
+                        if (context->internal->_auth_fn_with_context != NULL) {
+                                context->internal->_auth_fn_with_context(
+                                        context,
+                                        server, share,
+                                        workgroup, sizeof(fstring),
+                                        username, sizeof(fstring),
+                                        password, sizeof(fstring));
+                        } else {
+                                context->callbacks.auth_fn(
+                                        server, share,
+                                        workgroup, sizeof(fstring),
+                                        username, sizeof(fstring),
+                                        password, sizeof(fstring));
+                        }
+
+                        if (! cli_send_tconX(srv->cli, share, "?????",
+                                             password, strlen(password)+1)) {
+                        
+                                errno = smbc_errno(context, srv->cli);
+                                cli_shutdown(srv->cli);
+                               srv->cli = NULL;
+                                context->callbacks.remove_cached_srv_fn(context,
+                                                                        srv);
+                                srv = NULL;
+                        }
+
+                        /*
+                         * Regenerate the dev value since it's based on both
+                         * server and share
+                         */
+                        if (srv) {
+                                srv->dev = (dev_t)(str_checksum(server) ^
+                                                   str_checksum(share));
+                        }
+                }
+        }
+        
+        /* If we have a connection... */
+        if (srv) {
+
+                /* ... then we're done here.  Give 'em what they came for. */
                 return srv;
+        }
+
+        /* If we're not asked to connect when a connection doesn't exist... */
+        if (! connect_if_not_found) {
+                /* ... then we're done here. */
+                return NULL;
+        }
 
        make_nmb_name(&calling, context->netbios_name, 0x0);
        make_nmb_name(&called , server, 0x20);
 
        DEBUG(4,("smbc_server: server_n=[%s] server=[%s]\n", server_n, server));
   
-#if 0 /* djl: obsolete code?  neither group nor p is used beyond here */
-       if ((p=strchr_m(server_n,'#')) && 
-           (strcmp(p+1,"1D")==0 || strcmp(p+1,"01")==0)) {
-    
-               fstrcpy(group, server_n);
-               p = strchr_m(group,'#');
-               *p = 0;
-               
-       }
-#endif
-
        DEBUG(4,(" -> server_n=[%s] server=[%s]\n", server_n, server));
 
  again:
@@ -579,88 +767,116 @@ SMBCSRV *smbc_server(SMBCCTX *context,
        zero_ip(&ip);
 
        /* have to open a new connection */
-       if (!cli_initialise(&c)) {
+       if ((c = cli_initialise()) == NULL) {
                errno = ENOMEM;
                return NULL;
        }
 
-       c.timeout = context->timeout;
+       if (context->flags & SMB_CTX_FLAG_USE_KERBEROS) {
+               c->use_kerberos = True;
+       }
+       if (context->flags & SMB_CTX_FLAG_FALLBACK_AFTER_KERBEROS) {
+               c->fallback_after_kerberos = True;
+       }
 
-        /* Force use of port 139 for first try, so browse lists can work */
-        c.port = 139;
+       c->timeout = context->timeout;
 
-       if (!cli_connect(&c, server_n, &ip)) {
-                /*
-                 * Port 139 connection failed.  Try port 445 to handle
-                 * connections to newer (e.g. XP) hosts with NetBIOS disabled.
-                 */
-                c.port = 445;
-                if (!cli_connect(&c, server_n, &ip)) {
-                       cli_shutdown(&c);
-                        errno = ENETUNREACH;
-                        return NULL;
-                }
-       }
+        /*
+         * Force use of port 139 for first try if share is $IPC, empty, or
+         * null, so browse lists can work
+         */
+        if (share == NULL || *share == '\0' || strcmp(share, "IPC$") == 0) {
+                port_try_first = 139;
+                port_try_next = 445;
+        } else {
+                port_try_first = 445;
+                port_try_next = 139;
+        }
+
+        c->port = port_try_first;
+
+       status = cli_connect(c, server_n, &ip);
+       if (!NT_STATUS_IS_OK(status)) {
+
+                /* First connection attempt failed.  Try alternate port. */
+                c->port = port_try_next;
+
+                status = cli_connect(c, server_n, &ip);
+               if (!NT_STATUS_IS_OK(status)) {
+                       cli_shutdown(c);
+                       errno = ETIMEDOUT;
+                       return NULL;
+               }
+       }
 
-       if (!cli_session_request(&c, &calling, &called)) {
-               cli_shutdown(&c);
+       if (!cli_session_request(c, &calling, &called)) {
+               cli_shutdown(c);
                if (strcmp(called.name, "*SMBSERVER")) {
                        make_nmb_name(&called , "*SMBSERVER", 0x20);
                        goto again;
-               }
-               else {  /* Try one more time, but ensure we don't loop */
-
-                 /* Only try this if server is an IP address ... */
+               } else {  /* Try one more time, but ensure we don't loop */
 
-                 if (is_ipaddress(server) && !tried_reverse) {
-                   fstring remote_name;
-                   struct in_addr rem_ip;
+                       /* Only try this if server is an IP address ... */
 
-                   if ((rem_ip.s_addr=inet_addr(server)) == INADDR_NONE) {
-                     DEBUG(4, ("Could not convert IP address %s to struct in_addr\n", server));
-                     errno = ENOENT;
-                     return NULL;
-                   }
+                       if (is_ipaddress(server) && !tried_reverse) {
+                               fstring remote_name;
+                               struct in_addr rem_ip;
 
-                   tried_reverse++; /* Yuck */
-
-                   if (name_status_find("*", 0, 0, rem_ip, remote_name)) {
-                     make_nmb_name(&called, remote_name, 0x20);
-                     goto again;
-                   }
+                               if ((rem_ip.s_addr=inet_addr(server)) == INADDR_NONE) {
+                                       DEBUG(4, ("Could not convert IP address "
+                                               "%s to struct in_addr\n", server));
+                                       errno = ETIMEDOUT;
+                                       return NULL;
+                               }
 
+                               tried_reverse++; /* Yuck */
 
-                 }
+                               if (name_status_find("*", 0, 0, rem_ip, remote_name)) {
+                                       make_nmb_name(&called, remote_name, 0x20);
+                                       goto again;
+                               }
+                       }
                }
-               errno = ENOENT;
+               errno = ETIMEDOUT;
                return NULL;
        }
   
        DEBUG(4,(" session request ok\n"));
   
-       if (!cli_negprot(&c)) {
-               cli_shutdown(&c);
-               errno = ENOENT;
+       if (!cli_negprot(c)) {
+               cli_shutdown(c);
+               errno = ETIMEDOUT;
                return NULL;
        }
 
-       if (!cli_session_setup(&c, username, 
-                              password, strlen(password),
-                              password, strlen(password),
-                              workgroup) &&
-           /* try an anonymous login if it failed */
-           !cli_session_setup(&c, "", "", 1,"", 0, workgroup)) {
-               cli_shutdown(&c);
-               errno = EPERM;
-               return NULL;
+        username_used = username;
+
+       if (!NT_STATUS_IS_OK(cli_session_setup(c, username_used, 
+                                              password, strlen(password),
+                                              password, strlen(password),
+                                              workgroup))) {
+                
+                /* Failed.  Try an anonymous login, if allowed by flags. */
+                username_used = "";
+
+                if ((context->flags & SMBCCTX_FLAG_NO_AUTO_ANONYMOUS_LOGON) ||
+                     !NT_STATUS_IS_OK(cli_session_setup(c, username_used,
+                                                       password, 1,
+                                                       password, 0,
+                                                       workgroup))) {
+
+                        cli_shutdown(c);
+                        errno = EPERM;
+                        return NULL;
+                }
        }
 
        DEBUG(4,(" session setup ok\n"));
 
-       if (!cli_send_tconX(&c, share, "?????",
+       if (!cli_send_tconX(c, share, "?????",
                            password, strlen(password)+1)) {
-               errno = smbc_errno(context, &c);
-               cli_shutdown(&c);
+               errno = smbc_errno(context, c);
+               cli_shutdown(c);
                return NULL;
        }
   
@@ -668,10 +884,10 @@ SMBCSRV *smbc_server(SMBCCTX *context,
   
        /*
         * Ok, we have got a nice connection
-        * Let's find a free server_fd 
+        * Let's allocate a server structure.
         */
 
-       srv = (SMBCSRV *)malloc(sizeof(*srv));
+       srv = SMB_MALLOC_P(SMBCSRV);
        if (!srv) {
                errno = ENOMEM;
                goto failed;
@@ -680,6 +896,9 @@ SMBCSRV *smbc_server(SMBCCTX *context,
        ZERO_STRUCTP(srv);
        srv->cli = c;
        srv->dev = (dev_t)(str_checksum(server) ^ str_checksum(share));
+        srv->no_pathinfo = False;
+        srv->no_pathinfo2 = False;
+        srv->no_nt_session = False;
 
        /* now add it to the cache (internal or external)  */
        /* Let the cache function set errno if it wants to */
@@ -701,8 +920,10 @@ SMBCSRV *smbc_server(SMBCCTX *context,
        return srv;
 
  failed:
-       cli_shutdown(&c);
-       if (!srv) return NULL;
+       cli_shutdown(c);
+       if (!srv) {
+               return NULL;
+       }
   
        SAFE_FREE(srv);
        return NULL;
@@ -712,40 +933,61 @@ SMBCSRV *smbc_server(SMBCCTX *context,
  * Connect to a server for getting/setting attributes, possibly on an existing
  * connection.  This works similarly to smbc_server().
  */
-SMBCSRV *smbc_attr_server(SMBCCTX *context,
-                          const char *server, const char *share, 
-                          fstring workgroup,
-                          fstring username, fstring password,
-                          POLICY_HND *pol)
+static SMBCSRV *
+smbc_attr_server(SMBCCTX *context,
+                 const char *server,
+                 const char *share, 
+                 fstring workgroup,
+                 fstring username,
+                 fstring password,
+                 POLICY_HND *pol)
 {
+        int flags;
         struct in_addr ip;
        struct cli_state *ipc_cli;
+       struct rpc_pipe_client *pipe_hnd;
         NTSTATUS nt_status;
        SMBCSRV *ipc_srv=NULL;
 
         /*
          * See if we've already created this special connection.  Reference
-         * our "special" share name 'IPC$$'.
+         * our "special" share name '*IPC$', which is an impossible real share
+         * name due to the leading asterisk.
          */
-        ipc_srv = find_server(context, server, "IPC$$",
+        ipc_srv = find_server(context, server, "*IPC$",
                               workgroup, username, password);
         if (!ipc_srv) {
 
                 /* We didn't find a cached connection.  Get the password */
                 if (*password == '\0') {
                         /* ... then retrieve it now. */
-                        context->callbacks.auth_fn(server, share,
-                                                   workgroup, sizeof(fstring),
-                                                   username, sizeof(fstring),
-                                                   password, sizeof(fstring));
+                        if (context->internal->_auth_fn_with_context != NULL) {
+                                context->internal->_auth_fn_with_context(
+                                        context,
+                                        server, share,
+                                        workgroup, sizeof(fstring),
+                                        username, sizeof(fstring),
+                                        password, sizeof(fstring));
+                        } else {
+                                context->callbacks.auth_fn(
+                                        server, share,
+                                        workgroup, sizeof(fstring),
+                                        username, sizeof(fstring),
+                                        password, sizeof(fstring));
+                        }
                 }
         
+                flags = 0;
+                if (context->flags & SMB_CTX_FLAG_USE_KERBEROS) {
+                        flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
+                }
+
                 zero_ip(&ip);
                 nt_status = cli_full_connection(&ipc_cli,
                                                 global_myname(), server, 
                                                 &ip, 0, "IPC$", "?????",  
                                                 username, workgroup,
-                                                password, 0,
+                                                password, flags,
                                                 Undefined, NULL);
                 if (! NT_STATUS_IS_OK(nt_status)) {
                         DEBUG(1,("cli_full_connection failed! (%s)\n",
@@ -754,29 +996,7 @@ SMBCSRV *smbc_attr_server(SMBCCTX *context,
                         return NULL;
                 }
 
-                if (!cli_nt_session_open(ipc_cli, PI_LSARPC)) {
-                        DEBUG(1, ("cli_nt_session_open fail!\n"));
-                        errno = ENOTSUP;
-                        cli_shutdown(ipc_cli);
-                        return NULL;
-                }
-
-                /* Some systems don't support SEC_RIGHTS_MAXIMUM_ALLOWED,
-                   but NT sends 0x2000000 so we might as well do it too. */
-        
-                nt_status = cli_lsa_open_policy(ipc_cli,
-                                                ipc_cli->mem_ctx,
-                                                True, 
-                                                GENERIC_EXECUTE_ACCESS,
-                                                pol);
-        
-                if (!NT_STATUS_IS_OK(nt_status)) {
-                        errno = smbc_errno(context, ipc_cli);
-                        cli_shutdown(ipc_cli);
-                        return NULL;
-                }
-
-                ipc_srv = (SMBCSRV *)malloc(sizeof(*ipc_srv));
+                ipc_srv = SMB_MALLOC_P(SMBCSRV);
                 if (!ipc_srv) {
                         errno = ENOMEM;
                         cli_shutdown(ipc_cli);
@@ -784,23 +1004,53 @@ SMBCSRV *smbc_attr_server(SMBCCTX *context,
                 }
 
                 ZERO_STRUCTP(ipc_srv);
-                ipc_srv->cli = *ipc_cli;
+                ipc_srv->cli = ipc_cli;
+
+                if (pol) {
+                        pipe_hnd = cli_rpc_pipe_open_noauth(ipc_srv->cli,
+                                                            PI_LSARPC,
+                                                            &nt_status);
+                        if (!pipe_hnd) {
+                                DEBUG(1, ("cli_nt_session_open fail!\n"));
+                                errno = ENOTSUP;
+                                cli_shutdown(ipc_srv->cli);
+                                free(ipc_srv);
+                                return NULL;
+                        }
 
-                free(ipc_cli);
+                        /*
+                         * Some systems don't support
+                         * SEC_RIGHTS_MAXIMUM_ALLOWED, but NT sends 0x2000000
+                         * so we might as well do it too.
+                         */
+        
+                        nt_status = rpccli_lsa_open_policy(
+                                pipe_hnd,
+                                ipc_srv->cli->mem_ctx,
+                                True, 
+                                GENERIC_EXECUTE_ACCESS,
+                                pol);
+        
+                        if (!NT_STATUS_IS_OK(nt_status)) {
+                                errno = smbc_errno(context, ipc_srv->cli);
+                                cli_shutdown(ipc_srv->cli);
+                                return NULL;
+                        }
+                }
 
                 /* now add it to the cache (internal or external) */
 
                 errno = 0;      /* let cache function set errno if it likes */
                 if (context->callbacks.add_cached_srv_fn(context, ipc_srv,
                                                          server,
-                                                         "IPC$$",
+                                                         "*IPC$",
                                                          workgroup,
                                                          username)) {
                         DEBUG(3, (" Failed to add server to cache\n"));
                         if (errno == 0) {
                                 errno = ENOMEM;
                         }
-                        cli_shutdown(&ipc_srv->cli);
+                        cli_shutdown(ipc_srv->cli);
                         free(ipc_srv);
                         return NULL;
                 }
@@ -815,10 +1065,16 @@ SMBCSRV *smbc_attr_server(SMBCCTX *context,
  * Routine to open() a file ...
  */
 
-static SMBCFILE *smbc_open_ctx(SMBCCTX *context, const char *fname, int flags, mode_t mode)
+static SMBCFILE *
+smbc_open_ctx(SMBCCTX *context,
+              const char *fname,
+              int flags,
+              mode_t mode)
 {
        fstring server, share, user, password, workgroup;
        pstring path;
+        pstring targetpath;
+       struct cli_state *targetcli;
        SMBCSRV *srv   = NULL;
        SMBCFILE *file = NULL;
        int fd;
@@ -839,6 +1095,7 @@ static SMBCFILE *smbc_open_ctx(SMBCCTX *context, const char *fname, int flags, m
        }
 
        if (smbc_parse_path(context, fname,
+                            workgroup, sizeof(workgroup),
                             server, sizeof(server),
                             share, sizeof(share),
                             path, sizeof(path),
@@ -851,9 +1108,8 @@ static SMBCFILE *smbc_open_ctx(SMBCCTX *context, const char *fname, int flags, m
 
        if (user[0] == (char)0) fstrcpy(user, context->user);
 
-       fstrcpy(workgroup, context->workgroup);
-
-       srv = smbc_server(context, server, share, workgroup, user, password);
+       srv = smbc_server(context, True,
+                          server, share, workgroup, user, password);
 
        if (!srv) {
 
@@ -871,7 +1127,7 @@ static SMBCFILE *smbc_open_ctx(SMBCCTX *context, const char *fname, int flags, m
        }
        else {
          
-               file = malloc(sizeof(SMBCFILE));
+               file = SMB_MALLOC_P(SMBCFILE);
 
                if (!file) {
 
@@ -882,12 +1138,22 @@ static SMBCFILE *smbc_open_ctx(SMBCCTX *context, const char *fname, int flags, m
 
                ZERO_STRUCTP(file);
 
-               if ((fd = cli_open(&srv->cli, path, flags, DENY_NONE)) < 0) {
+               /*d_printf(">>>open: resolving %s\n", path);*/
+               if (!cli_resolve_path( "", srv->cli, path, &targetcli, targetpath))
+               {
+                       d_printf("Could not resolve %s\n", path);
+                       SAFE_FREE(file);
+                       return NULL;
+               }
+               /*d_printf(">>>open: resolved %s as %s\n", path, targetpath);*/
+               
+               if ((fd = cli_open(targetcli, targetpath, flags,
+                                   context->internal->_share_mode)) < 0) {
 
                        /* Handle the error ... */
 
                        SAFE_FREE(file);
-                       errno = smbc_errno(context, &srv->cli);
+                       errno = smbc_errno(context, targetcli);
                        return NULL;
 
                }
@@ -895,12 +1161,43 @@ static SMBCFILE *smbc_open_ctx(SMBCCTX *context, const char *fname, int flags, m
                /* Fill in file struct */
 
                file->cli_fd  = fd;
-               file->fname   = strdup(fname);
+               file->fname   = SMB_STRDUP(fname);
                file->srv     = srv;
                file->offset  = 0;
                file->file    = True;
 
                DLIST_ADD(context->internal->_files, file);
+
+                /*
+                 * If the file was opened in O_APPEND mode, all write
+                 * operations should be appended to the file.  To do that,
+                 * though, using this protocol, would require a getattrE()
+                 * call for each and every write, to determine where the end
+                 * of the file is. (There does not appear to be an append flag
+                 * in the protocol.)  Rather than add all of that overhead of
+                 * retrieving the current end-of-file offset prior to each
+                 * write operation, we'll assume that most append operations
+                 * will continuously write, so we'll just set the offset to
+                 * the end of the file now and hope that's adequate.
+                 *
+                 * Note to self: If this proves inadequate, and O_APPEND
+                 * should, in some cases, be forced for each write, add a
+                 * field in the context options structure, for
+                 * "strict_append_mode" which would select between the current
+                 * behavior (if FALSE) or issuing a getattrE() prior to each
+                 * write and forcing the write to the end of the file (if
+                 * TRUE).  Adding that capability will likely require adding
+                 * an "append" flag into the _SMBCFILE structure to track
+                 * whether a file was opened in O_APPEND mode.  -- djl
+                 */
+                if (flags & O_APPEND) {
+                        if (smbc_lseek_ctx(context, file, 0, SEEK_END) < 0) {
+                                (void) smbc_close_ctx(context, file);
+                                errno = ENXIO;
+                                return NULL;
+                        }
+                }
+
                return file;
 
        }
@@ -910,7 +1207,7 @@ static SMBCFILE *smbc_open_ctx(SMBCCTX *context, const char *fname, int flags, m
        if (fd == -1) {
                int eno = 0;
 
-               eno = smbc_errno(context, &srv->cli);
+               eno = smbc_errno(context, srv->cli);
                file = context->opendir(context, fname);
                if (!file) errno = eno;
                return file;
@@ -928,7 +1225,10 @@ static SMBCFILE *smbc_open_ctx(SMBCCTX *context, const char *fname, int flags, m
 
 static int creat_bits = O_WRONLY | O_CREAT | O_TRUNC; /* FIXME: Do we need this */
 
-static SMBCFILE *smbc_creat_ctx(SMBCCTX *context, const char *path, mode_t mode)
+static SMBCFILE *
+smbc_creat_ctx(SMBCCTX *context,
+               const char *path,
+               mode_t mode)
 {
 
        if (!context || !context->internal ||
@@ -946,9 +1246,27 @@ static SMBCFILE *smbc_creat_ctx(SMBCCTX *context, const char *path, mode_t mode)
  * Routine to read() a file ...
  */
 
-static ssize_t smbc_read_ctx(SMBCCTX *context, SMBCFILE *file, void *buf, size_t count)
+static ssize_t
+smbc_read_ctx(SMBCCTX *context,
+              SMBCFILE *file,
+              void *buf,
+              size_t count)
 {
        int ret;
+       fstring server, share, user, password;
+       pstring path, targetpath;
+       struct cli_state *targetcli;
+
+        /*
+         * offset:
+         *
+         * Compiler bug (possibly) -- gcc (GCC) 3.3.5 (Debian 1:3.3.5-2) --
+         * appears to pass file->offset (which is type off_t) differently than
+         * a local variable of type off_t.  Using local variable "offset" in
+         * the call to cli_read() instead of file->offset fixes a problem
+         * retrieving data at an offset greater than 4GB.
+         */
+        off_t offset;
 
        if (!context || !context->internal ||
            !context->internal->_initialized) {
@@ -967,6 +1285,8 @@ static ssize_t smbc_read_ctx(SMBCCTX *context, SMBCFILE *file, void *buf, size_t
 
        }
 
+       offset = file->offset;
+
        /* Check that the buffer exists ... */
 
        if (buf == NULL) {
@@ -976,11 +1296,33 @@ static ssize_t smbc_read_ctx(SMBCCTX *context, SMBCFILE *file, void *buf, size_t
 
        }
 
-       ret = cli_read(&file->srv->cli, file->cli_fd, buf, file->offset, count);
+       /*d_printf(">>>read: parsing %s\n", file->fname);*/
+       if (smbc_parse_path(context, file->fname,
+                            NULL, 0,
+                            server, sizeof(server),
+                            share, sizeof(share),
+                            path, sizeof(path),
+                            user, sizeof(user),
+                            password, sizeof(password),
+                            NULL, 0)) {
+                errno = EINVAL;
+                return -1;
+        }
+       
+       /*d_printf(">>>read: resolving %s\n", path);*/
+       if (!cli_resolve_path("", file->srv->cli, path,
+                              &targetcli, targetpath))
+       {
+               d_printf("Could not resolve %s\n", path);
+               return -1;
+       }
+       /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
+       
+       ret = cli_read(targetcli, file->cli_fd, (char *)buf, offset, count);
 
        if (ret < 0) {
 
-               errno = smbc_errno(context, &file->srv->cli);
+               errno = smbc_errno(context, targetcli);
                return -1;
 
        }
@@ -997,10 +1339,20 @@ static ssize_t smbc_read_ctx(SMBCCTX *context, SMBCFILE *file, void *buf, size_t
  * Routine to write() a file ...
  */
 
-static ssize_t smbc_write_ctx(SMBCCTX *context, SMBCFILE *file, void *buf, size_t count)
+static ssize_t
+smbc_write_ctx(SMBCCTX *context,
+               SMBCFILE *file,
+               void *buf,
+               size_t count)
 {
        int ret;
+        off_t offset;
+       fstring server, share, user, password;
+       pstring path, targetpath;
+       struct cli_state *targetcli;
 
+       /* First check all pointers before dereferencing them */
+       
        if (!context || !context->internal ||
            !context->internal->_initialized) {
 
@@ -1025,11 +1377,36 @@ static ssize_t smbc_write_ctx(SMBCCTX *context, SMBCFILE *file, void *buf, size_
 
        }
 
-       ret = cli_write(&file->srv->cli, file->cli_fd, 0, buf, file->offset, count);
+        offset = file->offset; /* See "offset" comment in smbc_read_ctx() */
+
+       /*d_printf(">>>write: parsing %s\n", file->fname);*/
+       if (smbc_parse_path(context, file->fname,
+                            NULL, 0,
+                            server, sizeof(server),
+                            share, sizeof(share),
+                            path, sizeof(path),
+                            user, sizeof(user),
+                            password, sizeof(password),
+                            NULL, 0)) {
+                errno = EINVAL;
+                return -1;
+        }
+       
+       /*d_printf(">>>write: resolving %s\n", path);*/
+       if (!cli_resolve_path("", file->srv->cli, path,
+                              &targetcli, targetpath))
+       {
+               d_printf("Could not resolve %s\n", path);
+               return -1;
+       }
+       /*d_printf(">>>write: resolved path as %s\n", targetpath);*/
+
+
+       ret = cli_write(targetcli, file->cli_fd, 0, (char *)buf, offset, count);
 
        if (ret <= 0) {
 
-               errno = smbc_errno(context, &file->srv->cli);
+               errno = smbc_errno(context, targetcli);
                return -1;
 
        }
@@ -1043,9 +1420,14 @@ static ssize_t smbc_write_ctx(SMBCCTX *context, SMBCFILE *file, void *buf, size_
  * Routine to close() a file ...
  */
 
-static int smbc_close_ctx(SMBCCTX *context, SMBCFILE *file)
+static int
+smbc_close_ctx(SMBCCTX *context,
+               SMBCFILE *file)
 {
         SMBCSRV *srv; 
+       fstring server, share, user, password;
+       pstring path, targetpath;
+       struct cli_state *targetcli;
 
        if (!context || !context->internal ||
            !context->internal->_initialized) {
@@ -1069,13 +1451,35 @@ static int smbc_close_ctx(SMBCCTX *context, SMBCFILE *file)
 
        }
 
-       if (!cli_close(&file->srv->cli, file->cli_fd)) {
+       /*d_printf(">>>close: parsing %s\n", file->fname);*/
+       if (smbc_parse_path(context, file->fname,
+                            NULL, 0,
+                            server, sizeof(server),
+                            share, sizeof(share),
+                            path, sizeof(path),
+                            user, sizeof(user),
+                            password, sizeof(password),
+                            NULL, 0)) {
+                errno = EINVAL;
+                return -1;
+        }
+       
+       /*d_printf(">>>close: resolving %s\n", path);*/
+       if (!cli_resolve_path("", file->srv->cli, path,
+                              &targetcli, targetpath))
+       {
+               d_printf("Could not resolve %s\n", path);
+               return -1;
+       }
+       /*d_printf(">>>close: resolved path as %s\n", targetpath);*/
+
+       if (!cli_close(targetcli, file->cli_fd)) {
 
                DEBUG(3, ("cli_close failed on %s. purging server.\n", 
                          file->fname));
                /* Deallocate slot and remove the server 
                 * from the server cache if unused */
-               errno = smbc_errno(context, &file->srv->cli);  
+               errno = smbc_errno(context, targetcli);
                srv = file->srv;
                DLIST_REMOVE(context->internal->_files, file);
                SAFE_FREE(file->fname);
@@ -1097,11 +1501,22 @@ static int smbc_close_ctx(SMBCCTX *context, SMBCFILE *file)
  * Get info from an SMB server on a file. Use a qpathinfo call first
  * and if that fails, use getatr, as Win95 sometimes refuses qpathinfo
  */
-static BOOL smbc_getatr(SMBCCTX * context, SMBCSRV *srv, char *path, 
-                uint16 *mode, size_t *size, 
-                time_t *c_time, time_t *a_time, time_t *m_time,
-                SMB_INO_T *ino)
+static BOOL
+smbc_getatr(SMBCCTX * context,
+            SMBCSRV *srv,
+            char *path, 
+            uint16 *mode,
+            SMB_OFF_T *size, 
+            struct timespec *create_time_ts,
+            struct timespec *access_time_ts,
+            struct timespec *write_time_ts,
+            struct timespec *change_time_ts,
+            SMB_INO_T *ino)
 {
+       pstring fixedpath;
+       pstring targetpath;
+       struct cli_state *targetcli;
+       time_t write_time;
 
        if (!context || !context->internal ||
            !context->internal->_initialized) {
@@ -1111,20 +1526,61 @@ static BOOL smbc_getatr(SMBCCTX * context, SMBCSRV *srv, char *path,
  
        }
 
+       /* path fixup for . and .. */
+       if (strequal(path, ".") || strequal(path, ".."))
+               pstrcpy(fixedpath, "\\");
+       else
+       {
+               pstrcpy(fixedpath, path);
+               trim_string(fixedpath, NULL, "\\..");
+               trim_string(fixedpath, NULL, "\\.");
+       }
        DEBUG(4,("smbc_getatr: sending qpathinfo\n"));
   
+       if (!cli_resolve_path( "", srv->cli, fixedpath, &targetcli, targetpath))
+       {
+               d_printf("Couldn't resolve %s\n", path);
+               return False;
+       }
+       
        if (!srv->no_pathinfo2 &&
-           cli_qpathinfo2(&srv->cli, path, c_time, a_time, m_time, NULL,
-                          size, mode, ino)) return True;
+            cli_qpathinfo2(targetcli, targetpath,
+                           create_time_ts,
+                           access_time_ts,
+                           write_time_ts,
+                           change_time_ts,
+                           size, mode, ino)) {
+            return True;
+        }
 
        /* if this is NT then don't bother with the getatr */
-       if (srv->cli.capabilities & CAP_NT_SMBS) {
+       if (targetcli->capabilities & CAP_NT_SMBS) {
                 errno = EPERM;
                 return False;
         }
 
-       if (cli_getatr(&srv->cli, path, mode, size, m_time)) {
-               a_time = c_time = m_time;
+       if (cli_getatr(targetcli, targetpath, mode, size, &write_time)) {
+
+                struct timespec w_time_ts;
+
+                w_time_ts = convert_time_t_to_timespec(write_time);
+
+                if (write_time_ts != NULL) {
+                       *write_time_ts = w_time_ts;
+                }
+
+                if (create_time_ts != NULL) {
+                        *create_time_ts = w_time_ts;
+                }
+                
+                if (access_time_ts != NULL) {
+                        *access_time_ts = w_time_ts;
+                }
+                
+                if (change_time_ts != NULL) {
+                        *change_time_ts = w_time_ts;
+                }
+
                srv->no_pathinfo2 = True;
                return True;
        }
@@ -1135,13 +1591,99 @@ static BOOL smbc_getatr(SMBCCTX * context, SMBCSRV *srv, char *path,
 }
 
 /*
- * Routine to unlink() a file
+ * Set file info on an SMB server.  Use setpathinfo call first.  If that
+ * fails, use setattrE..
+ *
+ * Access and modification time parameters are always used and must be
+ * provided.  Create time, if zero, will be determined from the actual create
+ * time of the file.  If non-zero, the create time will be set as well.
+ *
+ * "mode" (attributes) parameter may be set to -1 if it is not to be set.
  */
+static BOOL
+smbc_setatr(SMBCCTX * context, SMBCSRV *srv, char *path, 
+            time_t create_time,
+            time_t access_time,
+            time_t write_time,
+            time_t change_time,
+            uint16 mode)
+{
+        int fd;
+        int ret;
+
+        /*
+         * First, try setpathinfo (if qpathinfo succeeded), for it is the
+         * modern function for "new code" to be using, and it works given a
+         * filename rather than requiring that the file be opened to have its
+         * attributes manipulated.
+         */
+        if (srv->no_pathinfo ||
+            ! cli_setpathinfo(srv->cli, path,
+                              create_time,
+                              access_time,
+                              write_time,
+                              change_time,
+                              mode)) {
+
+                /*
+                 * setpathinfo is not supported; go to plan B. 
+                 *
+                 * cli_setatr() does not work on win98, and it also doesn't
+                 * support setting the access time (only the modification
+                 * time), so in all cases, we open the specified file and use
+                 * cli_setattrE() which should work on all OS versions, and
+                 * supports both times.
+                 */
+
+                /* Don't try {q,set}pathinfo() again, with this server */
+                srv->no_pathinfo = True;
+
+                /* Open the file */
+                if ((fd = cli_open(srv->cli, path, O_RDWR, DENY_NONE)) < 0) {
+
+                        errno = smbc_errno(context, srv->cli);
+                        return -1;
+                }
+
+                /* Set the new attributes */
+                ret = cli_setattrE(srv->cli, fd,
+                                   change_time,
+                                   access_time,
+                                   write_time);
+
+                /* Close the file */
+                cli_close(srv->cli, fd);
 
-static int smbc_unlink_ctx(SMBCCTX *context, const char *fname)
+                /*
+                 * Unfortunately, setattrE() doesn't have a provision for
+                 * setting the access mode (attributes).  We'll have to try
+                 * cli_setatr() for that, and with only this parameter, it
+                 * seems to work on win98.
+                 */
+                if (ret && mode != (uint16) -1) {
+                        ret = cli_setatr(srv->cli, path, mode, 0);
+                }
+
+                if (! ret) {
+                        errno = smbc_errno(context, srv->cli);
+                        return False;
+                }
+        }
+
+        return True;
+}
+
+ /*
+  * Routine to unlink() a file
+  */
+
+static int
+smbc_unlink_ctx(SMBCCTX *context,
+                const char *fname)
 {
        fstring server, share, user, password, workgroup;
-       pstring path;
+       pstring path, targetpath;
+       struct cli_state *targetcli;
        SMBCSRV *srv = NULL;
 
        if (!context || !context->internal ||
@@ -1160,6 +1702,7 @@ static int smbc_unlink_ctx(SMBCCTX *context, const char *fname)
        }
 
        if (smbc_parse_path(context, fname,
+                            workgroup, sizeof(workgroup),
                             server, sizeof(server),
                             share, sizeof(share),
                             path, sizeof(path),
@@ -1172,9 +1715,8 @@ static int smbc_unlink_ctx(SMBCCTX *context, const char *fname)
 
        if (user[0] == (char)0) fstrcpy(user, context->user);
 
-       fstrcpy(workgroup, context->workgroup);
-
-       srv = smbc_server(context, server, share, workgroup, user, password);
+       srv = smbc_server(context, True,
+                          server, share, workgroup, user, password);
 
        if (!srv) {
 
@@ -1182,40 +1724,38 @@ static int smbc_unlink_ctx(SMBCCTX *context, const char *fname)
 
        }
 
-       /*  if (strncmp(srv->cli.dev, "LPT", 3) == 0) {
-
-    int job = smbc_stat_printjob(srv, path, NULL, NULL);
-    if (job == -1) {
-
-      return -1;
-
-    }
-    if ((err = cli_printjob_del(&srv->cli, job)) != 0) {
-
-    
-      return -1;
-
-    }
-    } else */
+       /*d_printf(">>>unlink: resolving %s\n", path);*/
+       if (!cli_resolve_path( "", srv->cli, path, &targetcli, targetpath))
+       {
+               d_printf("Could not resolve %s\n", path);
+               return -1;
+       }
+       /*d_printf(">>>unlink: resolved path as %s\n", targetpath);*/
 
-       if (!cli_unlink(&srv->cli, path)) {
+       if (!cli_unlink(targetcli, targetpath)) {
 
-               errno = smbc_errno(context, &srv->cli);
+               errno = smbc_errno(context, targetcli);
 
                if (errno == EACCES) { /* Check if the file is a directory */
 
                        int saverr = errno;
-                       size_t size = 0;
+                       SMB_OFF_T size = 0;
                        uint16 mode = 0;
-                       time_t m_time = 0, a_time = 0, c_time = 0;
+                       struct timespec write_time_ts;
+                        struct timespec access_time_ts;
+                        struct timespec change_time_ts;
                        SMB_INO_T ino = 0;
 
                        if (!smbc_getatr(context, srv, path, &mode, &size,
-                                        &c_time, &a_time, &m_time, &ino)) {
+                                        NULL,
+                                         &access_time_ts,
+                                         &write_time_ts,
+                                         &change_time_ts,
+                                         &ino)) {
 
                                /* Hmmm, bad error ... What? */
 
-                               errno = smbc_errno(context, &srv->cli);
+                               errno = smbc_errno(context, targetcli);
                                return -1;
 
                        }
@@ -1241,11 +1781,27 @@ static int smbc_unlink_ctx(SMBCCTX *context, const char *fname)
  * Routine to rename() a file
  */
 
-static int smbc_rename_ctx(SMBCCTX *ocontext, const char *oname, 
-                          SMBCCTX *ncontext, const char *nname)
+static int
+smbc_rename_ctx(SMBCCTX *ocontext,
+                const char *oname, 
+                SMBCCTX *ncontext,
+                const char *nname)
 {
-       fstring server1, share1, server2, share2, user1, user2, password1, password2, workgroup;
-       pstring path1, path2;
+       fstring server1;
+        fstring share1;
+        fstring server2;
+        fstring share2;
+        fstring user1;
+        fstring user2;
+        fstring password1;
+        fstring password2;
+        fstring workgroup;
+       pstring path1;
+        pstring path2;
+        pstring targetpath1;
+        pstring targetpath2;
+       struct cli_state *targetcli1;
+        struct cli_state *targetcli2;
        SMBCSRV *srv = NULL;
 
        if (!ocontext || !ncontext || 
@@ -1268,6 +1824,7 @@ static int smbc_rename_ctx(SMBCCTX *ocontext, const char *oname,
        DEBUG(4, ("smbc_rename(%s,%s)\n", oname, nname));
 
        smbc_parse_path(ocontext, oname,
+                        workgroup, sizeof(workgroup),
                         server1, sizeof(server1),
                         share1, sizeof(share1),
                         path1, sizeof(path1),
@@ -1278,6 +1835,7 @@ static int smbc_rename_ctx(SMBCCTX *ocontext, const char *oname,
        if (user1[0] == (char)0) fstrcpy(user1, ocontext->user);
 
        smbc_parse_path(ncontext, nname,
+                        NULL, 0,
                         server2, sizeof(server2),
                         share2, sizeof(share2),
                         path2, sizeof(path2),
@@ -1297,21 +1855,44 @@ static int smbc_rename_ctx(SMBCCTX *ocontext, const char *oname,
 
        }
 
-       fstrcpy(workgroup, ocontext->workgroup);
-       /* HELP !!! Which workgroup should I use ? Or are they always the same -- Tom */ 
-       srv = smbc_server(ocontext, server1, share1, workgroup, user1, password1);
+       srv = smbc_server(ocontext, True,
+                          server1, share1, workgroup, user1, password1);
        if (!srv) {
 
                return -1;
 
        }
 
-       if (!cli_rename(&srv->cli, path1, path2)) {
-               int eno = smbc_errno(ocontext, &srv->cli);
+       /*d_printf(">>>rename: resolving %s\n", path1);*/
+       if (!cli_resolve_path( "", srv->cli, path1, &targetcli1, targetpath1))
+       {
+               d_printf("Could not resolve %s\n", path1);
+               return -1;
+       }
+       /*d_printf(">>>rename: resolved path as %s\n", targetpath1);*/
+       /*d_printf(">>>rename: resolving %s\n", path2);*/
+       if (!cli_resolve_path( "", srv->cli, path2, &targetcli2, targetpath2))
+       {
+               d_printf("Could not resolve %s\n", path2);
+               return -1;
+       }
+       /*d_printf(">>>rename: resolved path as %s\n", targetpath2);*/
+       
+       if (strcmp(targetcli1->desthost, targetcli2->desthost) ||
+            strcmp(targetcli1->share, targetcli2->share))
+       {
+               /* can't rename across file systems */
+               
+               errno = EXDEV;
+               return -1;
+       }
+
+       if (!cli_rename(targetcli1, targetpath1, targetpath2)) {
+               int eno = smbc_errno(ocontext, targetcli1);
 
                if (eno != EEXIST ||
-                   !cli_unlink(&srv->cli, path2) ||
-                   !cli_rename(&srv->cli, path1, path2)) {
+                   !cli_unlink(targetcli1, targetpath2) ||
+                   !cli_rename(targetcli1, targetpath1, targetpath2)) {
 
                        errno = eno;
                        return -1;
@@ -1327,9 +1908,16 @@ static int smbc_rename_ctx(SMBCCTX *ocontext, const char *oname,
  * A routine to lseek() a file
  */
 
-static off_t smbc_lseek_ctx(SMBCCTX *context, SMBCFILE *file, off_t offset, int whence)
+static off_t
+smbc_lseek_ctx(SMBCCTX *context,
+               SMBCFILE *file,
+               off_t offset,
+               int whence)
 {
-       size_t size;
+       SMB_OFF_T size;
+       fstring server, share, user, password;
+       pstring path, targetpath;
+       struct cli_state *targetcli;
 
        if (!context || !context->internal ||
            !context->internal->_initialized) {
@@ -1363,12 +1951,35 @@ static off_t smbc_lseek_ctx(SMBCCTX *context, SMBCFILE *file, off_t offset, int
                break;
 
        case SEEK_END:
-               if (!cli_qfileinfo(&file->srv->cli, file->cli_fd, NULL, &size, NULL, NULL,
-                                  NULL, NULL, NULL)) 
+               /*d_printf(">>>lseek: parsing %s\n", file->fname);*/
+               if (smbc_parse_path(context, file->fname,
+                                    NULL, 0,
+                                    server, sizeof(server),
+                                    share, sizeof(share),
+                                    path, sizeof(path),
+                                    user, sizeof(user),
+                                    password, sizeof(password),
+                                    NULL, 0)) {
+                        
+                                       errno = EINVAL;
+                                       return -1;
+                       }
+               
+               /*d_printf(">>>lseek: resolving %s\n", path);*/
+               if (!cli_resolve_path("", file->srv->cli, path,
+                                      &targetcli, targetpath))
+               {
+                       d_printf("Could not resolve %s\n", path);
+                       return -1;
+               }
+               /*d_printf(">>>lseek: resolved path as %s\n", targetpath);*/
+               
+               if (!cli_qfileinfo(targetcli, file->cli_fd, NULL,
+                                   &size, NULL, NULL, NULL, NULL, NULL)) 
                {
-                   SMB_BIG_UINT b_size = size;
-                   if (!cli_getattrE(&file->srv->cli, file->cli_fd, NULL, &b_size, NULL, NULL,
-                                     NULL)) 
+                   SMB_OFF_T b_size = size;
+                       if (!cli_getattrE(targetcli, file->cli_fd,
+                                          NULL, &b_size, NULL, NULL, NULL)) 
                    {
                        errno = EINVAL;
                        return -1;
@@ -1392,8 +2003,9 @@ static off_t smbc_lseek_ctx(SMBCCTX *context, SMBCFILE *file, off_t offset, int
  * Generate an inode number from file name for those things that need it
  */
 
-static
-ino_t smbc_inode(SMBCCTX *context, const char *name)
+static ino_t
+smbc_inode(SMBCCTX *context,
+           const char *name)
 {
 
        if (!context || !context->internal ||
@@ -1414,8 +2026,12 @@ ino_t smbc_inode(SMBCCTX *context, const char *name)
  * fstat below.
  */
 
-static
-int smbc_setup_stat(SMBCCTX *context, struct stat *st, char *fname, size_t size, int mode)
+static int
+smbc_setup_stat(SMBCCTX *context,
+                struct stat *st,
+                char *fname,
+                SMB_OFF_T size,
+                int mode)
 {
        
        st->st_mode = 0;
@@ -1459,13 +2075,22 @@ int smbc_setup_stat(SMBCCTX *context, struct stat *st, char *fname, size_t size,
  * Routine to stat a file given a name
  */
 
-static int smbc_stat_ctx(SMBCCTX *context, const char *fname, struct stat *st)
+static int
+smbc_stat_ctx(SMBCCTX *context,
+              const char *fname,
+              struct stat *st)
 {
        SMBCSRV *srv;
-       fstring server, share, user, password, workgroup;
+       fstring server;
+        fstring share;
+        fstring user;
+        fstring password;
+        fstring workgroup;
        pstring path;
-       time_t m_time = 0, a_time = 0, c_time = 0;
-       size_t size = 0;
+       struct timespec write_time_ts;
+        struct timespec access_time_ts;
+        struct timespec change_time_ts;
+       SMB_OFF_T size = 0;
        uint16 mode = 0;
        SMB_INO_T ino = 0;
 
@@ -1487,6 +2112,7 @@ static int smbc_stat_ctx(SMBCCTX *context, const char *fname, struct stat *st)
        DEBUG(4, ("smbc_stat(%s)\n", fname));
 
        if (smbc_parse_path(context, fname,
+                            workgroup, sizeof(workgroup),
                             server, sizeof(server),
                             share, sizeof(share),
                             path, sizeof(path),
@@ -1499,18 +2125,21 @@ static int smbc_stat_ctx(SMBCCTX *context, const char *fname, struct stat *st)
 
        if (user[0] == (char)0) fstrcpy(user, context->user);
 
-       fstrcpy(workgroup, context->workgroup);
-
-       srv = smbc_server(context, server, share, workgroup, user, password);
+       srv = smbc_server(context, True,
+                          server, share, workgroup, user, password);
 
        if (!srv) {
                return -1;  /* errno set by smbc_server */
        }
 
        if (!smbc_getatr(context, srv, path, &mode, &size, 
-                        &c_time, &a_time, &m_time, &ino)) {
+                        NULL,
+                         &access_time_ts,
+                         &write_time_ts,
+                         &change_time_ts,
+                         &ino)) {
 
-               errno = smbc_errno(context, &srv->cli);
+               errno = smbc_errno(context, srv->cli);
                return -1;
                
        }
@@ -1519,9 +2148,9 @@ static int smbc_stat_ctx(SMBCCTX *context, const char *fname, struct stat *st)
 
        smbc_setup_stat(context, st, path, size, mode);
 
-       st->st_atime = a_time;
-       st->st_ctime = c_time;
-       st->st_mtime = m_time;
+       set_atimespec(st, access_time_ts);
+       set_ctimespec(st, change_time_ts);
+       set_mtimespec(st, write_time_ts);
        st->st_dev   = srv->dev;
 
        return 0;
@@ -1532,11 +2161,23 @@ static int smbc_stat_ctx(SMBCCTX *context, const char *fname, struct stat *st)
  * Routine to stat a file given an fd
  */
 
-static int smbc_fstat_ctx(SMBCCTX *context, SMBCFILE *file, struct stat *st)
+static int
+smbc_fstat_ctx(SMBCCTX *context,
+               SMBCFILE *file,
+               struct stat *st)
 {
-       time_t c_time, a_time, m_time;
-       size_t size;
+       struct timespec change_time_ts;
+        struct timespec access_time_ts;
+        struct timespec write_time_ts;
+       SMB_OFF_T size;
        uint16 mode;
+       fstring server;
+        fstring share;
+        fstring user;
+        fstring password;
+       pstring path;
+        pstring targetpath;
+       struct cli_state *targetcli;
        SMB_INO_T ino = 0;
 
        if (!context || !context->internal ||
@@ -1560,26 +2201,56 @@ static int smbc_fstat_ctx(SMBCCTX *context, SMBCFILE *file, struct stat *st)
 
        }
 
-       if (!cli_qfileinfo(&file->srv->cli, file->cli_fd,
-                          &mode, &size, &c_time, &a_time, &m_time, NULL, &ino)) {
-           SMB_BIG_UINT b_size = size;
-           if (!cli_getattrE(&file->srv->cli, file->cli_fd,
-                         &mode, &b_size, &c_time, &a_time, &m_time)) {
-
-               errno = EINVAL;
+       /*d_printf(">>>fstat: parsing %s\n", file->fname);*/
+       if (smbc_parse_path(context, file->fname,
+                            NULL, 0,
+                            server, sizeof(server),
+                            share, sizeof(share),
+                            path, sizeof(path),
+                            user, sizeof(user),
+                            password, sizeof(password),
+                            NULL, 0)) {
+                errno = EINVAL;
+                return -1;
+        }
+       
+       /*d_printf(">>>fstat: resolving %s\n", path);*/
+       if (!cli_resolve_path("", file->srv->cli, path,
+                              &targetcli, targetpath))
+       {
+               d_printf("Could not resolve %s\n", path);
                return -1;
-           } else
-               size = b_size;
+       }
+       /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
+
+       if (!cli_qfileinfo(targetcli, file->cli_fd, &mode, &size,
+                           NULL,
+                           &access_time_ts,
+                           &write_time_ts,
+                           &change_time_ts,
+                           &ino)) {
+
+               time_t change_time, access_time, write_time;
+
+               if (!cli_getattrE(targetcli, file->cli_fd, &mode, &size,
+                               &change_time, &access_time, &write_time)) {
+
+                       errno = EINVAL;
+                       return -1;
+               }
 
+               change_time_ts = convert_time_t_to_timespec(change_time);
+               access_time_ts = convert_time_t_to_timespec(access_time);
+               write_time_ts = convert_time_t_to_timespec(write_time);
        }
 
        st->st_ino = ino;
 
        smbc_setup_stat(context, st, file->fname, size, mode);
 
-       st->st_atime = a_time;
-       st->st_ctime = c_time;
-       st->st_mtime = m_time;
+       set_atimespec(st, access_time_ts);
+       set_ctimespec(st, change_time_ts);
+       set_mtimespec(st, write_time_ts);
        st->st_dev = file->srv->dev;
 
        return 0;
@@ -1591,7 +2262,8 @@ static int smbc_fstat_ctx(SMBCCTX *context, SMBCFILE *file, struct stat *st)
  * We accept the URL syntax explained in smbc_parse_path(), above.
  */
 
-static void smbc_remove_dir(SMBCFILE *dir)
+static void
+smbc_remove_dir(SMBCFILE *dir)
 {
        struct smbc_dir_list *d,*f;
 
@@ -1609,27 +2281,25 @@ static void smbc_remove_dir(SMBCFILE *dir)
 
 }
 
-static int add_dirent(SMBCFILE *dir, const char *name, const char *comment, uint32 type)
+static int
+add_dirent(SMBCFILE *dir,
+           const char *name,
+           const char *comment,
+           uint32 type)
 {
        struct smbc_dirent *dirent;
        int size;
-       char *u_name = NULL, *u_comment = NULL;
-       size_t u_name_len = 0, u_comment_len = 0;
-
-       if (name)
-           u_name_len = push_utf8_allocate(&u_name, name);
-       if (comment)
-           u_comment_len = push_utf8_allocate(&u_comment, comment);
+        int name_length = (name == NULL ? 0 : strlen(name));
+        int comment_len = (comment == NULL ? 0 : strlen(comment));
 
        /*
         * Allocate space for the dirent, which must be increased by the 
-        * size of the name and the comment and 1 for the null on the comment.
-        * The null on the name is already accounted for.
+        * size of the name and the comment and 1 each for the null terminator.
         */
 
-       size = sizeof(struct smbc_dirent) + u_name_len + u_comment_len + 1;
+       size = sizeof(struct smbc_dirent) + name_length + comment_len + 2;
     
-       dirent = malloc(size);
+       dirent = (struct smbc_dirent *)SMB_MALLOC(size);
 
        if (!dirent) {
 
@@ -1642,7 +2312,7 @@ static int add_dirent(SMBCFILE *dir, const char *name, const char *comment, uint
 
        if (dir->dir_list == NULL) {
 
-               dir->dir_list = malloc(sizeof(struct smbc_dir_list));
+               dir->dir_list = SMB_MALLOC_P(struct smbc_dir_list);
                if (!dir->dir_list) {
 
                        SAFE_FREE(dirent);
@@ -1656,7 +2326,7 @@ static int add_dirent(SMBCFILE *dir, const char *name, const char *comment, uint
        }
        else {
 
-               dir->dir_end->next = malloc(sizeof(struct smbc_dir_list));
+               dir->dir_end->next = SMB_MALLOC_P(struct smbc_dir_list);
                
                if (!dir->dir_end->next) {
                        
@@ -1674,24 +2344,28 @@ static int add_dirent(SMBCFILE *dir, const char *name, const char *comment, uint
        dir->dir_end->dirent = dirent;
        
        dirent->smbc_type = type;
-       dirent->namelen = u_name_len;
-       dirent->commentlen = u_comment_len;
+       dirent->namelen = name_length;
+       dirent->commentlen = comment_len;
        dirent->dirlen = size;
   
-       strncpy(dirent->name, (u_name?u_name:""), dirent->namelen + 1);
-
+        /*
+         * dirent->namelen + 1 includes the null (no null termination needed)
+         * Ditto for dirent->commentlen.
+         * The space for the two null bytes was allocated.
+         */
+       strncpy(dirent->name, (name?name:""), dirent->namelen + 1);
        dirent->comment = (char *)(&dirent->name + dirent->namelen + 1);
-       strncpy(dirent->comment, (u_comment?u_comment:""), dirent->commentlen + 1);
+       strncpy(dirent->comment, (comment?comment:""), dirent->commentlen + 1);
        
-       SAFE_FREE(u_comment);
-       SAFE_FREE(u_name);
-
        return 0;
 
 }
 
 static void
-list_unique_wg_fn(const char *name, uint32 type, const char *comment, void *state)
+list_unique_wg_fn(const char *name,
+                  uint32 type,
+                  const char *comment,
+                  void *state)
 {
        SMBCFILE *dir = (SMBCFILE *)state;
         struct smbc_dir_list *dir_list;
@@ -1724,6 +2398,7 @@ list_unique_wg_fn(const char *name, uint32 type, const char *comment, void *stat
                         /* Found the end of the list.  Remove it. */
                         dir->dir_end = dir_list;
                         free(dir_list->next);
+                        free(dirent);
                         dir_list->next = NULL;
                         break;
                 }
@@ -1731,17 +2406,31 @@ list_unique_wg_fn(const char *name, uint32 type, const char *comment, void *stat
 }
 
 static void
-list_fn(const char *name, uint32 type, const char *comment, void *state)
+list_fn(const char *name,
+        uint32 type,
+        const char *comment,
+        void *state)
 {
        SMBCFILE *dir = (SMBCFILE *)state;
        int dirent_type;
 
-       /* We need to process the type a little ... */
-
+       /*
+         * We need to process the type a little ...
+         *
+         * Disk share     = 0x00000000
+         * Print share    = 0x00000001
+         * Comms share    = 0x00000002 (obsolete?)
+         * IPC$ share     = 0x00000003 
+         *
+         * administrative shares:
+         * ADMIN$, IPC$, C$, D$, E$ ...  are type |= 0x80000000
+         */
+        
        if (dir->dir_type == SMBC_FILE_SHARE) {
                
                switch (type) {
-               case 0: /* Directory tree */
+                case 0 | 0x80000000:
+               case 0:
                        dirent_type = SMBC_FILE_SHARE;
                        break;
 
@@ -1753,6 +2442,7 @@ list_fn(const char *name, uint32 type, const char *comment, void *state)
                        dirent_type = SMBC_COMMS_SHARE;
                        break;
 
+                case 3 | 0x80000000:
                case 3:
                        dirent_type = SMBC_IPC_SHARE;
                        break;
@@ -1762,7 +2452,9 @@ list_fn(const char *name, uint32 type, const char *comment, void *state)
                        break;
                }
        }
-       else dirent_type = dir->dir_type;
+       else {
+                dirent_type = dir->dir_type;
+        }
 
        if (add_dirent(dir, name, comment, dirent_type) < 0) {
 
@@ -1773,7 +2465,10 @@ list_fn(const char *name, uint32 type, const char *comment, void *state)
 }
 
 static void
-dir_list_fn(file_info *finfo, const char *mask, void *state)
+dir_list_fn(const char *mnt,
+            file_info *finfo,
+            const char *mask,
+            void *state)
 {
 
        if (add_dirent((SMBCFILE *)state, finfo->name, "", 
@@ -1787,30 +2482,109 @@ dir_list_fn(file_info *finfo, const char *mask, void *state)
 
 }
 
-static SMBCFILE *smbc_opendir_ctx(SMBCCTX *context, const char *fname)
+static int
+net_share_enum_rpc(struct cli_state *cli,
+                   void (*fn)(const char *name,
+                              uint32 type,
+                              const char *comment,
+                              void *state),
+                   void *state)
+{
+        int i;
+       NTSTATUS result;
+       uint32 enum_hnd;
+        uint32 info_level = 1;
+       uint32 preferred_len = 0xffffffff;
+       struct srvsvc_NetShareCtr1 ctr1;
+       union srvsvc_NetShareCtr ctr;
+        void *mem_ctx;
+       struct rpc_pipe_client *pipe_hnd;
+       uint32 numentries;
+        NTSTATUS nt_status;
+
+        /* Open the server service pipe */
+        pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SRVSVC, &nt_status);
+        if (!pipe_hnd) {
+                DEBUG(1, ("net_share_enum_rpc pipe open fail!\n"));
+                return -1;
+        }
+
+        /* Allocate a context for parsing and for the entries in "ctr" */
+        mem_ctx = talloc_init("libsmbclient: net_share_enum_rpc");
+        if (mem_ctx == NULL) {
+                DEBUG(0, ("out of memory for net_share_enum_rpc!\n"));
+                cli_rpc_pipe_close(pipe_hnd);
+                return -1; 
+        }
+
+       ZERO_STRUCT(ctr1);
+       ctr.ctr1 = &ctr1;
+
+        /* Issue the NetShareEnum RPC call and retrieve the response */
+       enum_hnd = 0;
+       result = rpccli_srvsvc_NetShareEnum(pipe_hnd, mem_ctx, NULL,
+                                           &info_level, &ctr, preferred_len,
+                                           &numentries, &enum_hnd);
+
+        /* Was it successful? */
+       if (!NT_STATUS_IS_OK(result) || numentries == 0) {
+                /*  Nope.  Go clean up. */
+               goto done;
+        }
+
+        /* For each returned entry... */
+        for (i = 0; i < numentries; i++) {
+
+                /* Add this share to the list */
+                (*fn)(ctr.ctr1->array[i].name, 
+                                         ctr.ctr1->array[i].type, 
+                                         ctr.ctr1->array[i].comment, state);
+        }
+
+done:
+        /* Close the server service pipe */
+        cli_rpc_pipe_close(pipe_hnd);
+
+        /* Free all memory which was allocated for this request */
+        TALLOC_FREE(mem_ctx);
+
+        /* Tell 'em if it worked */
+        return NT_STATUS_IS_OK(result) ? 0 : -1;
+}
+
+
+
+static SMBCFILE *
+smbc_opendir_ctx(SMBCCTX *context,
+                 const char *fname)
 {
+        int saved_errno;
        fstring server, share, user, password, options;
        pstring workgroup;
        pstring path;
+        uint16 mode;
+        char *p;
        SMBCSRV *srv  = NULL;
        SMBCFILE *dir = NULL;
+        struct _smbc_callbacks *cb;
        struct in_addr rem_ip;
 
        if (!context || !context->internal ||
            !context->internal->_initialized) {
                DEBUG(4, ("no valid context\n"));
-               errno = EINVAL;
+               errno = EINVAL + 8192;
                return NULL;
 
        }
 
        if (!fname) {
                DEBUG(4, ("no valid fname\n"));
-               errno = EINVAL;
+               errno = EINVAL + 8193;
                return NULL;
        }
 
        if (smbc_parse_path(context, fname,
+                            workgroup, sizeof(workgroup),
                             server, sizeof(server),
                             share, sizeof(share),
                             path, sizeof(path),
@@ -1818,24 +2592,24 @@ static SMBCFILE *smbc_opendir_ctx(SMBCCTX *context, const char *fname)
                             password, sizeof(password),
                             options, sizeof(options))) {
                DEBUG(4, ("no valid path\n"));
-               errno = EINVAL;
+               errno = EINVAL + 8194;
                return NULL;
        }
 
-       DEBUG(4, ("parsed path: fname='%s' server='%s' share='%s' path='%s' options='%s'\n", fname, server, share, path, options));
+       DEBUG(4, ("parsed path: fname='%s' server='%s' share='%s' "
+                  "path='%s' options='%s'\n",
+                  fname, server, share, path, options));
 
         /* Ensure the options are valid */
         if (smbc_check_options(server, share, path, options)) {
                 DEBUG(4, ("unacceptable options (%s)\n", options));
-                errno = EINVAL;
+                errno = EINVAL + 8195;
                 return NULL;
         }
 
        if (user[0] == (char)0) fstrcpy(user, context->user);
 
-       pstrcpy(workgroup, context->workgroup);
-
-       dir = malloc(sizeof(*dir));
+       dir = SMB_MALLOC_P(SMBCFILE);
 
        if (!dir) {
 
@@ -1847,114 +2621,17 @@ static SMBCFILE *smbc_opendir_ctx(SMBCCTX *context, const char *fname)
        ZERO_STRUCTP(dir);
 
        dir->cli_fd   = 0;
-       dir->fname    = strdup(fname);
+       dir->fname    = SMB_STRDUP(fname);
        dir->srv      = NULL;
        dir->offset   = 0;
        dir->file     = False;
        dir->dir_list = dir->dir_next = dir->dir_end = NULL;
 
-       if (server[0] == (char)0 &&
-            (! *options || strcmp(options, "mb=.any") == 0)) {
-                struct in_addr server_ip;
-               if (share[0] != (char)0 || path[0] != (char)0) {
-
-                       errno = EINVAL;
-                       if (dir) {
-                               SAFE_FREE(dir->fname);
-                               SAFE_FREE(dir);
-                       }
-                       return NULL;
-               }
-
-               /*
-                 * We have server and share and path empty ... so list the
-                 * workgroups first try to get the LMB for our workgroup, and
-                 * if that fails, try the DMB
-                 */
-
-               pstrcpy(workgroup, lp_workgroup());
-
-               if (!find_master_ip(workgroup, &server_ip)) {
-                   struct user_auth_info u_info;
-                   struct cli_state *cli;
-
-                   DEBUG(4, ("Unable to find master browser for workgroup %s\n", 
-                             workgroup));
-
-                   /* find the name of the server ... */
-                   pstrcpy(u_info.username, user);
-                   pstrcpy(u_info.password, password);
-
-                   if (!(cli = get_ipc_connect_master_ip_bcast(workgroup, &u_info))) {
-                       DEBUG(4, ("Unable to find master browser by "
-                                 "broadcast\n"));
-                       errno = ENOENT;
-                       return NULL;
-                   }
-
-                   fstrcpy(server, cli->desthost);
-
-                   cli_shutdown(cli);
-               } else {
-                    /*
-                     * Do a name status query to find out the name of the
-                     * master browser.  We use <01><02>__MSBROWSE__<02>#01 if
-                     * *#00 fails because a domain master browser will not
-                     * respond to a wildcard query (or, at least, an NT4
-                     * server acting as the domain master browser will not).
-                     *
-                     * We might be able to use ONLY the query on MSBROWSE, but
-                     * that's not yet been tested with all Windows versions,
-                     * so until it is, leave the original wildcard query as
-                     * the first choice and fall back to MSBROWSE if the
-                     * wildcard query fails.
-                     */
-                   if (!name_status_find("*", 0, 0x20, server_ip, server) &&
-                        !name_status_find(MSBROWSE, 1, 0x1b, server_ip, server)) {
-                       errno = ENOENT;
-                       return NULL;
-                   }
-               }       
-
-               DEBUG(4, ("using workgroup %s %s\n", workgroup, server));
-
-                /*
-                 * Get a connection to IPC$ on the server if we do not already
-                 * have one
-                 */
-                
-               srv = smbc_server(context, server, "IPC$", workgroup, user, password);
-                if (!srv) {
-                    
-                    if (dir) {
-                        SAFE_FREE(dir->fname);
-                        SAFE_FREE(dir);
-                    }
-                    return NULL;
-                }
-                
-               dir->srv = srv;
-               dir->dir_type = SMBC_WORKGROUP;
-
-               /* Now, list the stuff ... */
-
-               if (!cli_NetServerEnum(&srv->cli, workgroup, SV_TYPE_DOMAIN_ENUM, list_fn,
-                                      (void *)dir)) {
-
-                        DEBUG(1, ("Could not enumerate domains using '%s'\n", workgroup));
-                       if (dir) {
-                               SAFE_FREE(dir->fname);
-                               SAFE_FREE(dir);
-                       }
-
-                       return NULL;
-
-               }
-        } else if (server[0] == (char)0 &&
-                   (! *options || strcmp(options, "mb=.all") == 0)) {
+       if (server[0] == (char)0) {
 
                 int i;
                 int count;
+                int max_lmb_count;
                 struct ip_service *ip_list;
                 struct ip_service server_addr;
                 struct user_auth_info u_info;
@@ -1962,7 +2639,7 @@ static SMBCFILE *smbc_opendir_ctx(SMBCCTX *context, const char *fname)
 
                if (share[0] != (char)0 || path[0] != (char)0) {
 
-                       errno = EINVAL;
+                       errno = EINVAL + 8196;
                        if (dir) {
                                SAFE_FREE(dir->fname);
                                SAFE_FREE(dir);
@@ -1970,6 +2647,11 @@ static SMBCFILE *smbc_opendir_ctx(SMBCCTX *context, const char *fname)
                        return NULL;
                }
 
+                /* Determine how many local master browsers to query */
+                max_lmb_count = (context->options.browse_max_lmb_count == 0
+                                 ? INT_MAX
+                                 : context->options.browse_max_lmb_count);
+
                 pstrcpy(u_info.username, user);
                 pstrcpy(u_info.password, password);
 
@@ -1982,9 +2664,17 @@ static SMBCFILE *smbc_opendir_ctx(SMBCCTX *context, const char *fname)
                  * a single master browser.
                  */
 
+                ip_list = NULL;
                 if (!name_resolve_bcast(MSBROWSE, 1, &ip_list, &count)) {
+
+                        SAFE_FREE(ip_list);
+
                         if (!find_master_ip(workgroup, &server_addr.ip)) {
 
+                               if (dir) {
+                                       SAFE_FREE(dir->fname);
+                                       SAFE_FREE(dir);
+                               }
                                 errno = ENOENT;
                                 return NULL;
                         }
@@ -1993,11 +2683,13 @@ static SMBCFILE *smbc_opendir_ctx(SMBCCTX *context, const char *fname)
                         count = 1;
                 }
 
-                for (i = 0; i < count; i++) {
-                        DEBUG(99, ("Found master browser %s\n", inet_ntoa(ip_list[i].ip)));
+                for (i = 0; i < count && i < max_lmb_count; i++) {
+                        DEBUG(99, ("Found master browser %d of %d: %s\n",
+                                   i+1, MAX(count, max_lmb_count),
+                                   inet_ntoa(ip_list[i].ip)));
                         
-                        cli = get_ipc_connect_master_ip(&ip_list[i], workgroup, &u_info);
-
+                        cli = get_ipc_connect_master_ip(&ip_list[i],
+                                                        workgroup, &u_info);
                        /* cli == NULL is the master browser refused to talk or 
                           could not be found */
                        if ( !cli )
@@ -2006,7 +2698,8 @@ static SMBCFILE *smbc_opendir_ctx(SMBCCTX *context, const char *fname)
                         fstrcpy(server, cli->desthost);
                         cli_shutdown(cli);
 
-                        DEBUG(4, ("using workgroup %s %s\n", workgroup, server));
+                        DEBUG(4, ("using workgroup %s %s\n",
+                                  workgroup, server));
 
                         /*
                          * For each returned master browser IP address, get a
@@ -2015,15 +2708,10 @@ static SMBCFILE *smbc_opendir_ctx(SMBCCTX *context, const char *fname)
                          * workgroups/domains that it knows about.
                          */
                 
-                        srv = smbc_server(context, server,
-                                          "IPC$", workgroup, user, password);
+                        srv = smbc_server(context, True, server, "IPC$",
+                                          workgroup, user, password);
                         if (!srv) {
-                                
-                                if (dir) {
-                                        SAFE_FREE(dir->fname);
-                                        SAFE_FREE(dir);
-                                }
-                                return NULL;
+                                continue;
                         }
                 
                         dir->srv = srv;
@@ -2031,28 +2719,26 @@ static SMBCFILE *smbc_opendir_ctx(SMBCCTX *context, const char *fname)
 
                         /* Now, list the stuff ... */
                         
-                        if (!cli_NetServerEnum(&srv->cli, workgroup, SV_TYPE_DOMAIN_ENUM, list_unique_wg_fn,
+                        if (!cli_NetServerEnum(srv->cli,
+                                               workgroup,
+                                               SV_TYPE_DOMAIN_ENUM,
+                                               list_unique_wg_fn,
                                                (void *)dir)) {
-                                
-                                if (dir) {
-                                        SAFE_FREE(dir->fname);
-                                        SAFE_FREE(dir);
-                                }
-                                
-                                return NULL;
-                                
+                                continue;
                         }
                 }
+
+                SAFE_FREE(ip_list);
         } else { 
                 /*
                  * Server not an empty string ... Check the rest and see what
                  * gives
                  */
-               if (share[0] == (char)0) {
-
-                       if (path[0] != (char)0) { /* Should not have empty share with path */
+               if (*share == '\0') {
+                       if (*path != '\0') {
 
-                               errno = EINVAL;
+                                /* Should not have empty share with path */
+                               errno = EINVAL + 8197;
                                if (dir) {
                                        SAFE_FREE(dir->fname);
                                        SAFE_FREE(dir);
@@ -2061,12 +2747,32 @@ static SMBCFILE *smbc_opendir_ctx(SMBCCTX *context, const char *fname)
        
                        }
 
-                       /* Check to see if <server><1D>, <server><1B>, or <server><20> translates */
-                       /* However, we check to see if <server> is an IP address first */
+                       /*
+                         * We don't know if <server> is really a server name
+                         * or is a workgroup/domain name.  If we already have
+                         * a server structure for it, we'll use it.
+                         * Otherwise, check to see if <server><1D>,
+                         * <server><1B>, or <server><20> translates.  We check
+                         * to see if <server> is an IP address first.
+                         */
+
+                        /*
+                         * See if we have an existing server.  Do not
+                         * establish a connection if one does not already
+                         * exist.
+                         */
+                        srv = smbc_server(context, False, server, "IPC$",
+                                          workgroup, user, password);
+
+                        /*
+                         * If no existing server and not an IP addr, look for
+                         * LMB or DMB
+                         */
+                       if (!srv &&
+                            !is_ipaddress(server) &&
+                           (resolve_name(server, &rem_ip, 0x1d) ||   /* LMB */
+                             resolve_name(server, &rem_ip, 0x1b) )) { /* DMB */
 
-                       if (!is_ipaddress(server) &&  /* Not an IP addr so check next */
-                           (resolve_name(server, &rem_ip, 0x1d) ||   /* Found LMB */
-                                    resolve_name(server, &rem_ip, 0x1b) )) { /* Found DMB */
                                fstring buserver;
 
                                dir->dir_type = SMBC_SERVER;
@@ -2074,22 +2780,28 @@ static SMBCFILE *smbc_opendir_ctx(SMBCCTX *context, const char *fname)
                                /*
                                 * Get the backup list ...
                                 */
+                               if (!name_status_find(server, 0, 0,
+                                                      rem_ip, buserver)) {
 
-
-                               if (!name_status_find(server, 0, 0, rem_ip, buserver)) {
-
-                                       DEBUG(0, ("Could not get name of local/domain master browser for server %s\n", server));
-                                       errno = EPERM;  /* FIXME, is this correct */
+                                        DEBUG(0, ("Could not get name of "
+                                                  "local/domain master browser "
+                                                  "for server %s\n", server));
+                                       if (dir) {
+                                               SAFE_FREE(dir->fname);
+                                               SAFE_FREE(dir);
+                                       }
+                                       errno = EPERM;
                                        return NULL;
 
                                }
 
                                /*
-                                * Get a connection to IPC$ on the server if we do not already have one
-                                */
-
-                               srv = smbc_server(context, buserver, "IPC$", workgroup, user, password);
-
+                                 * Get a connection to IPC$ on the server if
+                                 * we do not already have one
+                                 */
+                               srv = smbc_server(context, True,
+                                                  buserver, "IPC$",
+                                                  workgroup, user, password);
                                if (!srv) {
                                        DEBUG(0, ("got no contact to IPC$\n"));
                                        if (dir) {
@@ -2103,8 +2815,8 @@ static SMBCFILE *smbc_opendir_ctx(SMBCCTX *context, const char *fname)
                                dir->srv = srv;
 
                                /* Now, list the servers ... */
-
-                               if (!cli_NetServerEnum(&srv->cli, server, 0x0000FFFE, list_fn,
+                               if (!cli_NetServerEnum(srv->cli, server,
+                                                       0x0000FFFE, list_fn,
                                                       (void *)dir)) {
 
                                        if (dir) {
@@ -2112,94 +2824,156 @@ static SMBCFILE *smbc_opendir_ctx(SMBCCTX *context, const char *fname)
                                                SAFE_FREE(dir);
                                        }
                                        return NULL;
-                                       
                                }
-                       }
-                       else {
+                       } else if (srv ||
+                                   (resolve_name(server, &rem_ip, 0x20))) {
+                                
+                                /* If we hadn't found the server, get one now */
+                                if (!srv) {
+                                        srv = smbc_server(context, True,
+                                                          server, "IPC$",
+                                                          workgroup,
+                                                          user, password);
+                                }
 
-                               if (resolve_name(server, &rem_ip, 0x20)) {
+                                if (!srv) {
+                                        if (dir) {
+                                                SAFE_FREE(dir->fname);
+                                                SAFE_FREE(dir);
+                                        }
+                                        return NULL;
 
-                                       /* Now, list the shares ... */
+                                }
 
-                                       dir->dir_type = SMBC_FILE_SHARE;
+                                dir->dir_type = SMBC_FILE_SHARE;
+                                dir->srv = srv;
+
+                                /* List the shares ... */
+
+                                if (net_share_enum_rpc(
+                                            srv->cli,
+                                            list_fn,
+                                            (void *) dir) < 0 &&
+                                    cli_RNetShareEnum(
+                                            srv->cli,
+                                            list_fn, 
+                                            (void *)dir) < 0) {
+                                                
+                                        errno = cli_errno(srv->cli);
+                                        if (dir) {
+                                                SAFE_FREE(dir->fname);
+                                                SAFE_FREE(dir);
+                                        }
+                                        return NULL;
 
-                                       srv = smbc_server(context, server, "IPC$", workgroup, user, password);
+                                }
+                        } else {
+                                /* Neither the workgroup nor server exists */
+                                errno = ECONNREFUSED;   
+                                if (dir) {
+                                        SAFE_FREE(dir->fname);
+                                        SAFE_FREE(dir);
+                                }
+                                return NULL;
+                       }
 
-                                       if (!srv) {
+               }
+               else {
+                        /*
+                         * The server and share are specified ... work from
+                         * there ...
+                         */
+                       pstring targetpath;
+                       struct cli_state *targetcli;
 
-                                               if (dir) {
-                                                       SAFE_FREE(dir->fname);
-                                                       SAFE_FREE(dir);
-                                               }
-                                               return NULL;
+                       /* We connect to the server and list the directory */
+                       dir->dir_type = SMBC_FILE_SHARE;
 
-                                       }
+                       srv = smbc_server(context, True, server, share,
+                                          workgroup, user, password);
 
-                                       dir->srv = srv;
+                       if (!srv) {
 
-                                       /* Now, list the servers ... */
+                               if (dir) {
+                                       SAFE_FREE(dir->fname);
+                                       SAFE_FREE(dir);
+                               }
+                               return NULL;
 
-                                       if (cli_RNetShareEnum(&srv->cli, list_fn, 
-                                                             (void *)dir) < 0) {
+                       }
 
-                                               errno = cli_errno(&srv->cli);
-                                               if (dir) {
-                                                       SAFE_FREE(dir->fname);
-                                                       SAFE_FREE(dir);
-                                               }
-                                               return NULL;
+                       dir->srv = srv;
 
-                                       }
+                       /* Now, list the files ... */
 
-                               }
-                               else {
-
-                                       errno = ECONNREFUSED;   /* Neither the workgroup nor server exists */
-                                       if (dir) {
-                                               SAFE_FREE(dir->fname);
-                                               SAFE_FREE(dir);
-                                       }
-                                       return NULL;
-
-                               }
-
-                       }
-
-               }
-               else { /* The server and share are specified ... work from there ... */
-
-                       /* Well, we connect to the server and list the directory */
-
-                       dir->dir_type = SMBC_FILE_SHARE;
-
-                       srv = smbc_server(context, server, share, workgroup, user, password);
-
-                       if (!srv) {
+                        p = path + strlen(path);
+                       pstrcat(path, "\\*");
 
+                       if (!cli_resolve_path("", srv->cli, path,
+                                              &targetcli, targetpath))
+                       {
+                               d_printf("Could not resolve %s\n", path);
                                if (dir) {
                                        SAFE_FREE(dir->fname);
                                        SAFE_FREE(dir);
                                }
                                return NULL;
-
                        }
-
-                       dir->srv = srv;
-
-                       /* Now, list the files ... */
-
-                       pstrcat(path, "\\*");
-
-                       if (cli_list(&srv->cli, path, aDIR | aSYSTEM | aHIDDEN, dir_list_fn, 
-                                    (void *)dir) < 0) {
+                       
+                       if (cli_list(targetcli, targetpath,
+                                     aDIR | aSYSTEM | aHIDDEN,
+                                     dir_list_fn, (void *)dir) < 0) {
 
                                if (dir) {
                                        SAFE_FREE(dir->fname);
                                        SAFE_FREE(dir);
                                }
-                               errno = smbc_errno(context, &srv->cli);
-                               return NULL;
+                               saved_errno = smbc_errno(context, targetcli);
+
+                                if (saved_errno == EINVAL) {
+                                    /*
+                                     * See if they asked to opendir something
+                                     * other than a directory.  If so, the
+                                     * converted error value we got would have
+                                     * been EINVAL rather than ENOTDIR.
+                                     */
+                                    *p = '\0'; /* restore original path */
+
+                                    if (smbc_getatr(context, srv, path,
+                                                    &mode, NULL,
+                                                    NULL, NULL, NULL, NULL,
+                                                    NULL) &&
+                                        ! IS_DOS_DIR(mode)) {
+
+                                        /* It is.  Correct the error value */
+                                        saved_errno = ENOTDIR;
+                                    }
+                                }
 
+                                /*
+                                 * If there was an error and the server is no
+                                 * good any more...
+                                 */
+                                cb = &context->callbacks;
+                                if (cli_is_error(targetcli) &&
+                                    cb->check_server_fn(context, srv)) {
+
+                                    /* ... then remove it. */
+                                    if (cb->remove_unused_server_fn(context,
+                                                                    srv)) { 
+                                        /*
+                                         * We could not remove the server
+                                         * completely, remove it from the
+                                         * cache so we will not get it
+                                         * again. It will be removed when the
+                                         * last file/dir is closed.
+                                         */
+                                        cb->remove_cached_srv_fn(context, srv);
+                                    }
+                                }
+
+                                errno = saved_errno;
+                               return NULL;
                        }
                }
 
@@ -2214,7 +2988,9 @@ static SMBCFILE *smbc_opendir_ctx(SMBCCTX *context, const char *fname)
  * Routine to close a directory
  */
 
-static int smbc_closedir_ctx(SMBCCTX *context, SMBCFILE *dir)
+static int
+smbc_closedir_ctx(SMBCCTX *context,
+                  SMBCFILE *dir)
 {
 
         if (!context || !context->internal ||
@@ -2246,12 +3022,51 @@ static int smbc_closedir_ctx(SMBCCTX *context, SMBCFILE *dir)
 
 }
 
+static void
+smbc_readdir_internal(SMBCCTX * context,
+                      struct smbc_dirent *dest,
+                      struct smbc_dirent *src,
+                      int max_namebuf_len)
+{
+        if (context->options.urlencode_readdir_entries) {
+
+                /* url-encode the name.  get back remaining buffer space */
+                max_namebuf_len =
+                        smbc_urlencode(dest->name, src->name, max_namebuf_len);
+
+                /* We now know the name length */
+                dest->namelen = strlen(dest->name);
+
+                /* Save the pointer to the beginning of the comment */
+                dest->comment = dest->name + dest->namelen + 1;
+
+                /* Copy the comment */
+                strncpy(dest->comment, src->comment, max_namebuf_len - 1);
+                dest->comment[max_namebuf_len - 1] = '\0';
+
+                /* Save other fields */
+                dest->smbc_type = src->smbc_type;
+                dest->commentlen = strlen(dest->comment);
+                dest->dirlen = ((dest->comment + dest->commentlen + 1) -
+                                (char *) dest);
+        } else {
+
+                /* No encoding.  Just copy the entry as is. */
+                memcpy(dest, src, src->dirlen);
+                dest->comment = (char *)(&dest->name + src->namelen + 1);
+        }
+        
+}
+
 /*
  * Routine to get a directory entry
  */
 
-struct smbc_dirent *smbc_readdir_ctx(SMBCCTX *context, SMBCFILE *dir)
+struct smbc_dirent *
+smbc_readdir_ctx(SMBCCTX *context,
+                 SMBCFILE *dir)
 {
+        int maxlen;
        struct smbc_dirent *dirp, *dirent;
 
        /* Check that all is ok first ... */
@@ -2284,37 +3099,41 @@ struct smbc_dirent *smbc_readdir_ctx(SMBCCTX *context, SMBCFILE *dir)
        if (!dir->dir_next) {
                return NULL;
         }
-       else {
 
-               dirent = dir->dir_next->dirent;
-               if (!dirent) {
+        dirent = dir->dir_next->dirent;
+        if (!dirent) {
 
-                       errno = ENOENT;
-                       return NULL;
+                errno = ENOENT;
+                return NULL;
 
-               }
+        }
 
-               /* Hmmm, do I even need to copy it? */
+        dirp = (struct smbc_dirent *)context->internal->_dirent;
+        maxlen = (sizeof(context->internal->_dirent) -
+                  sizeof(struct smbc_dirent));
 
-               memcpy(context->internal->_dirent, dirent, dirent->dirlen); /* Copy the dirent */
-               dirp = (struct smbc_dirent *)context->internal->_dirent;
-               dirp->comment = (char *)(&dirp->name + dirent->namelen + 1);
-               dir->dir_next = dir->dir_next->next;
+        smbc_readdir_internal(context, dirp, dirent, maxlen);
 
-               return (struct smbc_dirent *)context->internal->_dirent;
-       }
+        dir->dir_next = dir->dir_next->next;
 
+        return dirp;
 }
 
 /*
  * Routine to get directory entries
  */
 
-static int smbc_getdents_ctx(SMBCCTX *context, SMBCFILE *dir, struct smbc_dirent *dirp, int count)
+static int
+smbc_getdents_ctx(SMBCCTX *context,
+                  SMBCFILE *dir,
+                  struct smbc_dirent *dirp,
+                  int count)
 {
-       struct smbc_dir_list *dirlist;
-       int rem = count, reqd;
+       int rem = count;
+        int reqd;
+        int maxlen;
        char *ndir = (char *)dirp;
+       struct smbc_dir_list *dirlist;
 
        /* Check that all is ok first ... */
 
@@ -2356,8 +3175,15 @@ static int smbc_getdents_ctx(SMBCCTX *context, SMBCFILE *dir, struct smbc_dirent
 
                }
 
-               if (rem < (reqd = (sizeof(struct smbc_dirent) + dirlist->dirent->namelen + 
-                                  dirlist->dirent->commentlen + 1))) {
+                /* Do urlencoding of next entry, if so selected */
+                dirent = (struct smbc_dirent *)context->internal->_dirent;
+                maxlen = (sizeof(context->internal->_dirent) -
+                          sizeof(struct smbc_dirent));
+                smbc_readdir_internal(context, dirent, dirlist->dirent, maxlen);
+
+                reqd = dirent->dirlen;
+
+               if (rem < reqd) {
 
                        if (rem < count) { /* We managed to copy something */
 
@@ -2374,12 +3200,12 @@ static int smbc_getdents_ctx(SMBCCTX *context, SMBCFILE *dir, struct smbc_dirent
 
                }
 
-               dirent = dirlist->dirent;
-
                memcpy(ndir, dirent, reqd); /* Copy the data in ... */
     
                ((struct smbc_dirent *)ndir)->comment = 
-                       (char *)(&((struct smbc_dirent *)ndir)->name + dirent->namelen + 1);
+                       (char *)(&((struct smbc_dirent *)ndir)->name +
+                                 dirent->namelen +
+                                 1);
 
                ndir += reqd;
 
@@ -2399,11 +3225,19 @@ static int smbc_getdents_ctx(SMBCCTX *context, SMBCFILE *dir, struct smbc_dirent
  * Routine to create a directory ...
  */
 
-static int smbc_mkdir_ctx(SMBCCTX *context, const char *fname, mode_t mode)
+static int
+smbc_mkdir_ctx(SMBCCTX *context,
+               const char *fname,
+               mode_t mode)
 {
        SMBCSRV *srv;
-       fstring server, share, user, password, workgroup;
-       pstring path;
+       fstring server;
+        fstring share;
+        fstring user;
+        fstring password;
+        fstring workgroup;
+       pstring path, targetpath;
+       struct cli_state *targetcli;
 
        if (!context || !context->internal || 
            !context->internal->_initialized) {
@@ -2423,6 +3257,7 @@ static int smbc_mkdir_ctx(SMBCCTX *context, const char *fname, mode_t mode)
        DEBUG(4, ("smbc_mkdir(%s)\n", fname));
 
        if (smbc_parse_path(context, fname,
+                            workgroup, sizeof(workgroup),
                             server, sizeof(server),
                             share, sizeof(share),
                             path, sizeof(path),
@@ -2435,9 +3270,8 @@ static int smbc_mkdir_ctx(SMBCCTX *context, const char *fname, mode_t mode)
 
        if (user[0] == (char)0) fstrcpy(user, context->user);
 
-       fstrcpy(workgroup, context->workgroup);
-
-       srv = smbc_server(context, server, share, workgroup, user, password);
+       srv = smbc_server(context, True,
+                          server, share, workgroup, user, password);
 
        if (!srv) {
 
@@ -2445,30 +3279,17 @@ static int smbc_mkdir_ctx(SMBCCTX *context, const char *fname, mode_t mode)
 
        }
 
-       /* if (strncmp(srv->cli.dev, "IPC", 3) == 0) {
-
-          mode = aDIR | aRONLY;
-
-          }
-          else if (strncmp(srv->cli.dev, "LPT", 3) == 0) {
-
-          if (strcmp(path, "\\") == 0) {
-
-          mode = aDIR | aRONLY;
-
-          }
-          else {
-
-          mode = aRONLY;
-          smbc_stat_printjob(srv, path, &size, &m_time);
-          c_time = a_time = m_time;
-
-          }
-          else { */
+       /*d_printf(">>>mkdir: resolving %s\n", path);*/
+       if (!cli_resolve_path( "", srv->cli, path, &targetcli, targetpath))
+       {
+               d_printf("Could not resolve %s\n", path);
+               return -1;
+       }
+       /*d_printf(">>>mkdir: resolved path as %s\n", targetpath);*/
 
-       if (!cli_mkdir(&srv->cli, path)) {
+       if (!cli_mkdir(targetcli, targetpath)) {
 
-               errno = smbc_errno(context, &srv->cli);
+               errno = smbc_errno(context, targetcli);
                return -1;
 
        } 
@@ -2483,23 +3304,36 @@ static int smbc_mkdir_ctx(SMBCCTX *context, const char *fname, mode_t mode)
 
 static int smbc_rmdir_dirempty = True;
 
-static void rmdir_list_fn(file_info *finfo, const char *mask, void *state)
+static void
+rmdir_list_fn(const char *mnt,
+              file_info *finfo,
+              const char *mask,
+              void *state)
 {
-
-       if (strncmp(finfo->name, ".", 1) != 0 && strncmp(finfo->name, "..", 2) != 0)
+       if (strncmp(finfo->name, ".", 1) != 0 &&
+            strncmp(finfo->name, "..", 2) != 0) {
+                
                smbc_rmdir_dirempty = False;
-
+        }
 }
 
 /*
  * Routine to remove a directory
  */
 
-static int smbc_rmdir_ctx(SMBCCTX *context, const char *fname)
+static int
+smbc_rmdir_ctx(SMBCCTX *context,
+               const char *fname)
 {
        SMBCSRV *srv;
-       fstring server, share, user, password, workgroup;
+       fstring server;
+        fstring share;
+        fstring user;
+        fstring password;
+        fstring workgroup;
        pstring path;
+        pstring targetpath;
+       struct cli_state *targetcli;
 
        if (!context || !context->internal || 
            !context->internal->_initialized) {
@@ -2519,6 +3353,7 @@ static int smbc_rmdir_ctx(SMBCCTX *context, const char *fname)
        DEBUG(4, ("smbc_rmdir(%s)\n", fname));
 
        if (smbc_parse_path(context, fname,
+                            workgroup, sizeof(workgroup),
                             server, sizeof(server),
                             share, sizeof(share),
                             path, sizeof(path),
@@ -2532,9 +3367,8 @@ static int smbc_rmdir_ctx(SMBCCTX *context, const char *fname)
 
        if (user[0] == (char)0) fstrcpy(user, context->user);
 
-       fstrcpy(workgroup, context->workgroup);
-
-       srv = smbc_server(context, server, share, workgroup, user, password);
+       srv = smbc_server(context, True,
+                          server, share, workgroup, user, password);
 
        if (!srv) {
 
@@ -2542,47 +3376,37 @@ static int smbc_rmdir_ctx(SMBCCTX *context, const char *fname)
 
        }
 
-       /* if (strncmp(srv->cli.dev, "IPC", 3) == 0) {
-
-          mode = aDIR | aRONLY;
-
-          }
-          else if (strncmp(srv->cli.dev, "LPT", 3) == 0) {
-
-          if (strcmp(path, "\\") == 0) {
-
-          mode = aDIR | aRONLY;
-
-          }
-          else {
+       /*d_printf(">>>rmdir: resolving %s\n", path);*/
+       if (!cli_resolve_path( "", srv->cli, path, &targetcli, targetpath))
+       {
+               d_printf("Could not resolve %s\n", path);
+               return -1;
+       }
+       /*d_printf(">>>rmdir: resolved path as %s\n", targetpath);*/
 
-          mode = aRONLY;
-          smbc_stat_printjob(srv, path, &size, &m_time);
-          c_time = a_time = m_time;
-          
-          }
-          else { */
 
-       if (!cli_rmdir(&srv->cli, path)) {
+       if (!cli_rmdir(targetcli, targetpath)) {
 
-               errno = smbc_errno(context, &srv->cli);
+               errno = smbc_errno(context, targetcli);
 
                if (errno == EACCES) {  /* Check if the dir empty or not */
 
-                       pstring lpath; /* Local storage to avoid buffer overflows */
+                        /* Local storage to avoid buffer overflows */
+                       pstring lpath; 
 
                        smbc_rmdir_dirempty = True;  /* Make this so ... */
 
-                       pstrcpy(lpath, path);
+                       pstrcpy(lpath, targetpath);
                        pstrcat(lpath, "\\*");
 
-                       if (cli_list(&srv->cli, lpath, aDIR | aSYSTEM | aHIDDEN, rmdir_list_fn,
-                                    NULL) < 0) {
+                       if (cli_list(targetcli, lpath,
+                                     aDIR | aSYSTEM | aHIDDEN,
+                                     rmdir_list_fn, NULL) < 0) {
 
                                /* Fix errno to ignore latest error ... */
-
-                               DEBUG(5, ("smbc_rmdir: cli_list returned an error: %d\n", 
-                                         smbc_errno(context, &srv->cli)));
+                               DEBUG(5, ("smbc_rmdir: "
+                                          "cli_list returned an error: %d\n", 
+                                         smbc_errno(context, targetcli)));
                                errno = EACCES;
 
                        }
@@ -2606,7 +3430,9 @@ static int smbc_rmdir_ctx(SMBCCTX *context, const char *fname)
  * Routine to return the current directory position
  */
 
-static off_t smbc_telldir_ctx(SMBCCTX *context, SMBCFILE *dir)
+static off_t
+smbc_telldir_ctx(SMBCCTX *context,
+                 SMBCFILE *dir)
 {
        off_t ret_val; /* Squash warnings about cast */
 
@@ -2635,7 +3461,7 @@ static off_t smbc_telldir_ctx(SMBCCTX *context, SMBCFILE *dir)
        /*
         * We return the pointer here as the offset
         */
-       ret_val = (int)dir->dir_next;
+       ret_val = (off_t)(long)dir->dir_next;
        return ret_val;
 
 }
@@ -2644,8 +3470,9 @@ static off_t smbc_telldir_ctx(SMBCCTX *context, SMBCFILE *dir)
  * A routine to run down the list and see if the entry is OK
  */
 
-struct smbc_dir_list *smbc_check_dir_ent(struct smbc_dir_list *list, 
-                                        struct smbc_dirent *dirent)
+struct smbc_dir_list *
+smbc_check_dir_ent(struct smbc_dir_list *list, 
+                   struct smbc_dirent *dirent)
 {
 
        /* Run down the list looking for what we want */
@@ -2674,7 +3501,10 @@ struct smbc_dir_list *smbc_check_dir_ent(struct smbc_dir_list *list,
  * Routine to seek on a directory
  */
 
-static int smbc_lseekdir_ctx(SMBCCTX *context, SMBCFILE *dir, off_t offset)
+static int
+smbc_lseekdir_ctx(SMBCCTX *context,
+                  SMBCFILE *dir,
+                  off_t offset)
 {
        long int l_offset = offset;  /* Handle problems of size */
        struct smbc_dirent *dirent = (struct smbc_dirent *)l_offset;
@@ -2724,7 +3554,10 @@ static int smbc_lseekdir_ctx(SMBCCTX *context, SMBCFILE *dir, off_t offset)
  * Routine to fstat a dir
  */
 
-static int smbc_fstatdir_ctx(SMBCCTX *context, SMBCFILE *dir, struct stat *st)
+static int
+smbc_fstatdir_ctx(SMBCCTX *context,
+                  SMBCFILE *dir,
+                  struct stat *st)
 {
 
        if (!context || !context->internal || 
@@ -2741,10 +3574,17 @@ static int smbc_fstatdir_ctx(SMBCCTX *context, SMBCFILE *dir, struct stat *st)
 
 }
 
-int smbc_chmod_ctx(SMBCCTX *context, const char *fname, mode_t newmode)
+static int
+smbc_chmod_ctx(SMBCCTX *context,
+               const char *fname,
+               mode_t newmode)
 {
         SMBCSRV *srv;
-       fstring server, share, user, password, workgroup;
+       fstring server;
+        fstring share;
+        fstring user;
+        fstring password;
+        fstring workgroup;
        pstring path;
        uint16 mode;
 
@@ -2766,6 +3606,7 @@ int smbc_chmod_ctx(SMBCCTX *context, const char *fname, mode_t newmode)
        DEBUG(4, ("smbc_chmod(%s, 0%3o)\n", fname, newmode));
 
        if (smbc_parse_path(context, fname,
+                            workgroup, sizeof(workgroup),
                             server, sizeof(server),
                             share, sizeof(share),
                             path, sizeof(path),
@@ -2778,9 +3619,8 @@ int smbc_chmod_ctx(SMBCCTX *context, const char *fname, mode_t newmode)
 
        if (user[0] == (char)0) fstrcpy(user, context->user);
 
-       fstrcpy(workgroup, context->workgroup);
-
-       srv = smbc_server(context, server, share, workgroup, user, password);
+       srv = smbc_server(context, True,
+                          server, share, workgroup, user, password);
 
        if (!srv) {
                return -1;  /* errno set by smbc_server */
@@ -2793,21 +3633,28 @@ int smbc_chmod_ctx(SMBCCTX *context, const char *fname, mode_t newmode)
        if ((newmode & S_IXGRP) && lp_map_system(-1)) mode |= aSYSTEM;
        if ((newmode & S_IXOTH) && lp_map_hidden(-1)) mode |= aHIDDEN;
 
-       if (!cli_setatr(&srv->cli, path, mode, 0)) {
-               errno = smbc_errno(context, &srv->cli);
+       if (!cli_setatr(srv->cli, path, mode, 0)) {
+               errno = smbc_errno(context, srv->cli);
                return -1;
        }
        
         return 0;
 }
 
-int smbc_utimes_ctx(SMBCCTX *context, const char *fname, struct timeval *tbuf)
+static int
+smbc_utimes_ctx(SMBCCTX *context,
+                const char *fname,
+                struct timeval *tbuf)
 {
         SMBCSRV *srv;
-       fstring server, share, user, password, workgroup;
+       fstring server;
+        fstring share;
+        fstring user;
+        fstring password;
+        fstring workgroup;
        pstring path;
-       uint16 mode;
-        time_t t = (tbuf == NULL ? time(NULL) : tbuf->tv_sec);
+        time_t access_time;
+        time_t write_time;
 
        if (!context || !context->internal ||
            !context->internal->_initialized) {
@@ -2824,9 +3671,37 @@ int smbc_utimes_ctx(SMBCCTX *context, const char *fname, struct timeval *tbuf)
 
        }
   
-       DEBUG(4, ("smbc_utimes(%s, [%s])\n", fname, ctime(&t)));
+        if (tbuf == NULL) {
+                access_time = write_time = time(NULL);
+        } else {
+                access_time = tbuf[0].tv_sec;
+                write_time = tbuf[1].tv_sec;
+        }
+
+        if (DEBUGLVL(4)) 
+        {
+                char *p;
+                char atimebuf[32];
+                char mtimebuf[32];
+
+                strncpy(atimebuf, ctime(&access_time), sizeof(atimebuf) - 1);
+                atimebuf[sizeof(atimebuf) - 1] = '\0';
+                if ((p = strchr(atimebuf, '\n')) != NULL) {
+                        *p = '\0';
+                }
+
+                strncpy(mtimebuf, ctime(&write_time), sizeof(mtimebuf) - 1);
+                mtimebuf[sizeof(mtimebuf) - 1] = '\0';
+                if ((p = strchr(mtimebuf, '\n')) != NULL) {
+                        *p = '\0';
+                }
+
+                dbgtext("smbc_utimes(%s, atime = %s mtime = %s)\n",
+                        fname, atimebuf, mtimebuf);
+        }
 
        if (smbc_parse_path(context, fname,
+                            workgroup, sizeof(workgroup),
                             server, sizeof(server),
                             share, sizeof(share),
                             path, sizeof(path),
@@ -2839,30 +3714,19 @@ int smbc_utimes_ctx(SMBCCTX *context, const char *fname, struct timeval *tbuf)
 
        if (user[0] == (char)0) fstrcpy(user, context->user);
 
-       fstrcpy(workgroup, context->workgroup);
-
-       srv = smbc_server(context, server, share, workgroup, user, password);
+       srv = smbc_server(context, True,
+                          server, share, workgroup, user, password);
 
        if (!srv) {
-               return -1;  /* errno set by smbc_server */
-       }
-
-       if (!smbc_getatr(context, srv, path,
-                         &mode, NULL,
-                         NULL, NULL, NULL,
-                         NULL)) {
-                return -1;
+               return -1;      /* errno set by smbc_server */
        }
 
-       if (!cli_setatr(&srv->cli, path, mode, t)) {
-               /* some servers always refuse directory changes */
-               if (!(mode & aDIR)) {
-                       errno = smbc_errno(context, &srv->cli);
-                        return -1;
-               }
-       }
+        if (!smbc_setatr(context, srv, path,
+                         0, access_time, write_time, 0, 0)) {
+                return -1;      /* errno set by smbc_setatr */
+        }
 
-       return 0;
+        return 0;
 }
 
 
@@ -2871,7 +3735,9 @@ int smbc_utimes_ctx(SMBCCTX *context, const char *fname, struct timeval *tbuf)
    computer running Windows NT 5.0" if denied ACEs do not appear before
    allowed ACEs. */
 
-static int ace_compare(SEC_ACE *ace1, SEC_ACE *ace2)
+static int
+ace_compare(SEC_ACE *ace1,
+            SEC_ACE *ace2)
 {
        if (sec_ace_equal(ace1, ace2)) 
                return 0;
@@ -2885,8 +3751,8 @@ static int ace_compare(SEC_ACE *ace1, SEC_ACE *ace2)
        if (ace1->flags != ace2->flags) 
                return ace1->flags - ace2->flags;
 
-       if (ace1->info.mask != ace2->info.mask) 
-               return ace1->info.mask - ace2->info.mask;
+       if (ace1->access_mask != ace2->access_mask) 
+               return ace1->access_mask - ace2->access_mask;
 
        if (ace1->size != ace2->size) 
                return ace1->size - ace2->size;
@@ -2895,18 +3761,20 @@ static int ace_compare(SEC_ACE *ace1, SEC_ACE *ace2)
 }
 
 
-static void sort_acl(SEC_ACL *the_acl)
+static void
+sort_acl(SEC_ACL *the_acl)
 {
        uint32 i;
        if (!the_acl) return;
 
-       qsort(the_acl->ace, the_acl->num_aces, sizeof(the_acl->ace[0]), QSORT_CAST ace_compare);
+       qsort(the_acl->aces, the_acl->num_aces, sizeof(the_acl->aces[0]),
+              QSORT_CAST ace_compare);
 
        for (i=1;i<the_acl->num_aces;) {
-               if (sec_ace_equal(&the_acl->ace[i-1], &the_acl->ace[i])) {
+               if (sec_ace_equal(&the_acl->aces[i-1], &the_acl->aces[i])) {
                        int j;
                        for (j=i; j<the_acl->num_aces-1; j++) {
-                               the_acl->ace[j] = the_acl->ace[j+1];
+                               the_acl->aces[j] = the_acl->aces[j+1];
                        }
                        the_acl->num_aces--;
                } else {
@@ -2916,23 +3784,30 @@ static void sort_acl(SEC_ACL *the_acl)
 }
 
 /* convert a SID to a string, either numeric or username/group */
-static void convert_sid_to_string(struct cli_state *ipc_cli,
-                                  POLICY_HND *pol,
-                                  fstring str,
-                                  BOOL numeric,
-                                  DOM_SID *sid)
+static void
+convert_sid_to_string(struct cli_state *ipc_cli,
+                      POLICY_HND *pol,
+                      fstring str,
+                      BOOL numeric,
+                      DOM_SID *sid)
 {
        char **domains = NULL;
        char **names = NULL;
-       uint32 *types = NULL;
-
+       enum lsa_SidType *types = NULL;
+       struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli);
        sid_to_string(str, sid);
 
-        if (numeric) return;     /* no lookup desired */
-        
+       if (numeric) {
+               return;     /* no lookup desired */
+       }
+       
+       if (!pipe_hnd) {
+               return;
+       }
        /* Ask LSA to convert the sid to a name */
 
-       if (!NT_STATUS_IS_OK(cli_lsa_lookup_sids(ipc_cli, ipc_cli->mem_ctx,  
+       if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_sids(pipe_hnd, ipc_cli->mem_ctx,  
                                                 pol, 1, sid, &domains, 
                                                 &names, &types)) ||
            !domains || !domains[0] || !names || !names[0]) {
@@ -2947,15 +3822,21 @@ static void convert_sid_to_string(struct cli_state *ipc_cli,
 }
 
 /* convert a string to a SID, either numeric or username/group */
-static BOOL convert_string_to_sid(struct cli_state *ipc_cli,
-                                  POLICY_HND *pol,
-                                  BOOL numeric,
-                                  DOM_SID *sid,
-                                  const char *str)
+static BOOL
+convert_string_to_sid(struct cli_state *ipc_cli,
+                      POLICY_HND *pol,
+                      BOOL numeric,
+                      DOM_SID *sid,
+                      const char *str)
 {
-       uint32 *types = NULL;
+       enum lsa_SidType *types = NULL;
        DOM_SID *sids = NULL;
        BOOL result = True;
+       struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli);
+
+       if (!pipe_hnd) {
+               return False;
+       }
 
         if (numeric) {
                 if (strncmp(str, "S-", 2) == 0) {
@@ -2966,8 +3847,8 @@ static BOOL convert_string_to_sid(struct cli_state *ipc_cli,
                 goto done;
         }
 
-       if (!NT_STATUS_IS_OK(cli_lsa_lookup_names(ipc_cli, ipc_cli->mem_ctx, 
-                                                 pol, 1, &str, &sids, 
+       if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_names(pipe_hnd, ipc_cli->mem_ctx, 
+                                                 pol, 1, &str, NULL, 1, &sids, 
                                                  &types))) {
                result = False;
                goto done;
@@ -2981,16 +3862,19 @@ static BOOL convert_string_to_sid(struct cli_state *ipc_cli,
 
 
 /* parse an ACE in the same format as print_ace() */
-static BOOL parse_ace(struct cli_state *ipc_cli,
-                      POLICY_HND *pol,
-                      SEC_ACE *ace,
-                      BOOL numeric,
-                      char *str)
+static BOOL
+parse_ace(struct cli_state *ipc_cli,
+          POLICY_HND *pol,
+          SEC_ACE *ace,
+          BOOL numeric,
+          char *str)
 {
        char *p;
        const char *cp;
        fstring tok;
-       unsigned atype, aflags, amask;
+       unsigned int atype;
+        unsigned int aflags;
+        unsigned int amask;
        DOM_SID sid;
        SEC_ACCESS mask;
        const struct perm_value *v;
@@ -3095,43 +3979,52 @@ static BOOL parse_ace(struct cli_state *ipc_cli,
        }
 
  done:
-       mask.mask = amask;
+       mask = amask;
        init_sec_ace(ace, &sid, atype, mask, aflags);
        return True;
 }
 
 /* add an ACE to a list of ACEs in a SEC_ACL */
-static BOOL add_ace(SEC_ACL **the_acl, SEC_ACE *ace, TALLOC_CTX *ctx)
+static BOOL
+add_ace(SEC_ACL **the_acl,
+        SEC_ACE *ace,
+        TALLOC_CTX *ctx)
 {
-       SEC_ACL *new;
+       SEC_ACL *newacl;
        SEC_ACE *aces;
+
        if (! *the_acl) {
                (*the_acl) = make_sec_acl(ctx, 3, 1, ace);
                return True;
        }
 
-       aces = calloc(1+(*the_acl)->num_aces,sizeof(SEC_ACE));
-       memcpy(aces, (*the_acl)->ace, (*the_acl)->num_aces * sizeof(SEC_ACE));
+       if ((aces = SMB_CALLOC_ARRAY(SEC_ACE, 1+(*the_acl)->num_aces)) == NULL) {
+               return False;
+       }
+       memcpy(aces, (*the_acl)->aces, (*the_acl)->num_aces * sizeof(SEC_ACE));
        memcpy(aces+(*the_acl)->num_aces, ace, sizeof(SEC_ACE));
-       new = make_sec_acl(ctx,(*the_acl)->revision,1+(*the_acl)->num_aces, aces);
+       newacl = make_sec_acl(ctx, (*the_acl)->revision,
+                              1+(*the_acl)->num_aces, aces);
        SAFE_FREE(aces);
-       (*the_acl) = new;
+       (*the_acl) = newacl;
        return True;
 }
 
 
 /* parse a ascii version of a security descriptor */
-static SEC_DESC *sec_desc_parse(TALLOC_CTX *ctx,
-                                struct cli_state *ipc_cli,
-                                POLICY_HND *pol,
-                                BOOL numeric,
-                                char *str)
+static SEC_DESC *
+sec_desc_parse(TALLOC_CTX *ctx,
+               struct cli_state *ipc_cli,
+               POLICY_HND *pol,
+               BOOL numeric,
+               char *str)
 {
        const char *p = str;
        fstring tok;
-       SEC_DESC *ret;
+       SEC_DESC *ret = NULL;
        size_t sd_size;
-       DOM_SID *grp_sid=NULL, *owner_sid=NULL;
+       DOM_SID *grp_sid=NULL;
+        DOM_SID *owner_sid=NULL;
        SEC_ACL *dacl=NULL;
        int revision=1;
 
@@ -3143,49 +4036,65 @@ static SEC_DESC *sec_desc_parse(TALLOC_CTX *ctx,
                }
 
                if (StrnCaseCmp(tok,"OWNER:", 6) == 0) {
-                       owner_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID));
+                       if (owner_sid) {
+                               DEBUG(5, ("OWNER specified more than once!\n"));
+                               goto done;
+                       }
+                       owner_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
                        if (!owner_sid ||
                            !convert_string_to_sid(ipc_cli, pol,
                                                    numeric,
                                                    owner_sid, tok+6)) {
                                DEBUG(5, ("Failed to parse owner sid\n"));
-                               return NULL;
+                               goto done;
                        }
                        continue;
                }
 
                if (StrnCaseCmp(tok,"OWNER+:", 7) == 0) {
-                       owner_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID));
+                       if (owner_sid) {
+                               DEBUG(5, ("OWNER specified more than once!\n"));
+                               goto done;
+                       }
+                       owner_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
                        if (!owner_sid ||
                            !convert_string_to_sid(ipc_cli, pol,
                                                    False,
                                                    owner_sid, tok+7)) {
                                DEBUG(5, ("Failed to parse owner sid\n"));
-                               return NULL;
+                               goto done;
                        }
                        continue;
                }
 
                if (StrnCaseCmp(tok,"GROUP:", 6) == 0) {
-                       grp_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID));
+                       if (grp_sid) {
+                               DEBUG(5, ("GROUP specified more than once!\n"));
+                               goto done;
+                       }
+                       grp_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
                        if (!grp_sid ||
                            !convert_string_to_sid(ipc_cli, pol,
                                                    numeric,
                                                    grp_sid, tok+6)) {
                                DEBUG(5, ("Failed to parse group sid\n"));
-                               return NULL;
+                               goto done;
                        }
                        continue;
                }
 
                if (StrnCaseCmp(tok,"GROUP+:", 7) == 0) {
-                       grp_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID));
+                       if (grp_sid) {
+                               DEBUG(5, ("GROUP specified more than once!\n"));
+                               goto done;
+                       }
+                       grp_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
                        if (!grp_sid ||
                            !convert_string_to_sid(ipc_cli, pol,
                                                    False,
                                                    grp_sid, tok+6)) {
                                DEBUG(5, ("Failed to parse group sid\n"));
-                               return NULL;
+                               goto done;
                        }
                        continue;
                }
@@ -3194,11 +4103,11 @@ static SEC_DESC *sec_desc_parse(TALLOC_CTX *ctx,
                        SEC_ACE ace;
                        if (!parse_ace(ipc_cli, pol, &ace, numeric, tok+4)) {
                                DEBUG(5, ("Failed to parse ACL %s\n", tok));
-                               return NULL;
+                               goto done;
                        }
                        if(!add_ace(&dacl, &ace, ctx)) {
                                DEBUG(5, ("Failed to add ACL %s\n", tok));
-                               return NULL;
+                               goto done;
                        }
                        continue;
                }
@@ -3207,22 +4116,23 @@ static SEC_DESC *sec_desc_parse(TALLOC_CTX *ctx,
                        SEC_ACE ace;
                        if (!parse_ace(ipc_cli, pol, &ace, False, tok+5)) {
                                DEBUG(5, ("Failed to parse ACL %s\n", tok));
-                               return NULL;
+                               goto done;
                        }
                        if(!add_ace(&dacl, &ace, ctx)) {
                                DEBUG(5, ("Failed to add ACL %s\n", tok));
-                               return NULL;
+                               goto done;
                        }
                        continue;
                }
 
                DEBUG(5, ("Failed to parse security descriptor\n"));
-               return NULL;
+               goto done;
        }
 
        ret = make_sec_desc(ctx, revision, SEC_DESC_SELF_RELATIVE, 
                            owner_sid, grp_sid, NULL, dacl, &sd_size);
 
+  done:
        SAFE_FREE(grp_sid);
        SAFE_FREE(owner_sid);
 
@@ -3230,224 +4140,912 @@ static SEC_DESC *sec_desc_parse(TALLOC_CTX *ctx,
 }
 
 
+/* Obtain the current dos attributes */
+static DOS_ATTR_DESC *
+dos_attr_query(SMBCCTX *context,
+               TALLOC_CTX *ctx,
+               const char *filename,
+               SMBCSRV *srv)
+{
+        struct timespec create_time_ts;
+        struct timespec write_time_ts;
+        struct timespec access_time_ts;
+        struct timespec change_time_ts;
+        SMB_OFF_T size = 0;
+        uint16 mode = 0;
+       SMB_INO_T inode = 0;
+        DOS_ATTR_DESC *ret;
+    
+        ret = TALLOC_P(ctx, DOS_ATTR_DESC);
+        if (!ret) {
+                errno = ENOMEM;
+                return NULL;
+        }
+
+        /* Obtain the DOS attributes */
+        if (!smbc_getatr(context, srv, CONST_DISCARD(char *, filename),
+                         &mode, &size, 
+                         &create_time_ts,
+                         &access_time_ts,
+                         &write_time_ts,
+                         &change_time_ts, 
+                         &inode)) {
+        
+                errno = smbc_errno(context, srv->cli);
+                DEBUG(5, ("dos_attr_query Failed to query old attributes\n"));
+                return NULL;
+        
+        }
+                
+        ret->mode = mode;
+        ret->size = size;
+        ret->create_time = convert_timespec_to_time_t(create_time_ts);
+        ret->access_time = convert_timespec_to_time_t(access_time_ts);
+        ret->write_time = convert_timespec_to_time_t(write_time_ts);
+        ret->change_time = convert_timespec_to_time_t(change_time_ts);
+        ret->inode = inode;
+
+        return ret;
+}
+
+
+/* parse a ascii version of a security descriptor */
+static void
+dos_attr_parse(SMBCCTX *context,
+               DOS_ATTR_DESC *dad,
+               SMBCSRV *srv,
+               char *str)
+{
+        int n;
+        const char *p = str;
+       fstring tok;
+        struct {
+                const char * create_time_attr;
+                const char * access_time_attr;
+                const char * write_time_attr;
+                const char * change_time_attr;
+        } attr_strings;
+
+        /* Determine whether to use old-style or new-style attribute names */
+        if (context->internal->_full_time_names) {
+                /* new-style names */
+                attr_strings.create_time_attr = "CREATE_TIME";
+                attr_strings.access_time_attr = "ACCESS_TIME";
+                attr_strings.write_time_attr = "WRITE_TIME";
+                attr_strings.change_time_attr = "CHANGE_TIME";
+        } else {
+                /* old-style names */
+                attr_strings.create_time_attr = NULL;
+                attr_strings.access_time_attr = "A_TIME";
+                attr_strings.write_time_attr = "M_TIME";
+                attr_strings.change_time_attr = "C_TIME";
+        }
+
+        /* if this is to set the entire ACL... */
+        if (*str == '*') {
+                /* ... then increment past the first colon if there is one */
+                if ((p = strchr(str, ':')) != NULL) {
+                        ++p;
+                } else {
+                        p = str;
+                }
+        }
+
+       while (next_token(&p, tok, "\t,\r\n", sizeof(tok))) {
+
+               if (StrnCaseCmp(tok, "MODE:", 5) == 0) {
+                       dad->mode = strtol(tok+5, NULL, 16);
+                       continue;
+               }
+
+               if (StrnCaseCmp(tok, "SIZE:", 5) == 0) {
+                        dad->size = (SMB_OFF_T)atof(tok+5);
+                       continue;
+               }
+
+                n = strlen(attr_strings.access_time_attr);
+                if (StrnCaseCmp(tok, attr_strings.access_time_attr, n) == 0) {
+                        dad->access_time = (time_t)strtol(tok+n+1, NULL, 10);
+                       continue;
+               }
+
+                n = strlen(attr_strings.change_time_attr);
+                if (StrnCaseCmp(tok, attr_strings.change_time_attr, n) == 0) {
+                        dad->change_time = (time_t)strtol(tok+n+1, NULL, 10);
+                       continue;
+               }
+
+                n = strlen(attr_strings.write_time_attr);
+                if (StrnCaseCmp(tok, attr_strings.write_time_attr, n) == 0) {
+                        dad->write_time = (time_t)strtol(tok+n+1, NULL, 10);
+                       continue;
+               }
+
+               if (attr_strings.create_time_attr != NULL) {
+                       n = strlen(attr_strings.create_time_attr);
+                       if (StrnCaseCmp(tok, attr_strings.create_time_attr,
+                                       n) == 0) {
+                               dad->create_time = (time_t)strtol(tok+n+1,
+                                                                 NULL, 10);
+                               continue;
+                       }
+               }
+
+               if (StrnCaseCmp(tok, "INODE:", 6) == 0) {
+                        dad->inode = (SMB_INO_T)atof(tok+6);
+                       continue;
+               }
+       }
+}
+
 /***************************************************** 
-retrieve the acls for a file
+ Retrieve the acls for a file.
 *******************************************************/
-static int cacl_get(TALLOC_CTX *ctx, struct cli_state *cli,
-                    struct cli_state *ipc_cli, POLICY_HND *pol,
-                    char *filename, char *name, char *buf, int bufsize)
+
+static int
+cacl_get(SMBCCTX *context,
+         TALLOC_CTX *ctx,
+         SMBCSRV *srv,
+         struct cli_state *ipc_cli,
+         POLICY_HND *pol,
+         char *filename,
+         char *attr_name,
+         char *buf,
+         int bufsize)
 {
        uint32 i;
         int n = 0;
         int n_used;
         BOOL all;
+        BOOL all_nt;
+        BOOL all_nt_acls;
+        BOOL all_dos;
+        BOOL some_nt;
+        BOOL some_dos;
+        BOOL exclude_nt_revision = False;
+        BOOL exclude_nt_owner = False;
+        BOOL exclude_nt_group = False;
+        BOOL exclude_nt_acl = False;
+        BOOL exclude_dos_mode = False;
+        BOOL exclude_dos_size = False;
+        BOOL exclude_dos_create_time = False;
+        BOOL exclude_dos_access_time = False;
+        BOOL exclude_dos_write_time = False;
+        BOOL exclude_dos_change_time = False;
+        BOOL exclude_dos_inode = False;
         BOOL numeric = True;
         BOOL determine_size = (bufsize == 0);
        int fnum = -1;
        SEC_DESC *sd;
        fstring sidstr;
+        fstring name_sandbox;
+        char *name;
+        char *pExclude;
         char *p;
+        struct timespec create_time_ts;
+       struct timespec write_time_ts;
+        struct timespec access_time_ts;
+        struct timespec change_time_ts;
+       time_t create_time = (time_t)0;
+       time_t write_time = (time_t)0;
+        time_t access_time = (time_t)0;
+        time_t change_time = (time_t)0;
+       SMB_OFF_T size = 0;
+       uint16 mode = 0;
+       SMB_INO_T ino = 0;
+        struct cli_state *cli = srv->cli;
+        struct {
+                const char * create_time_attr;
+                const char * access_time_attr;
+                const char * write_time_attr;
+                const char * change_time_attr;
+        } attr_strings;
+        struct {
+                const char * create_time_attr;
+                const char * access_time_attr;
+                const char * write_time_attr;
+                const char * change_time_attr;
+        } excl_attr_strings;
+
+        /* Determine whether to use old-style or new-style attribute names */
+        if (context->internal->_full_time_names) {
+                /* new-style names */
+                attr_strings.create_time_attr = "CREATE_TIME";
+                attr_strings.access_time_attr = "ACCESS_TIME";
+                attr_strings.write_time_attr = "WRITE_TIME";
+                attr_strings.change_time_attr = "CHANGE_TIME";
+
+                excl_attr_strings.create_time_attr = "CREATE_TIME";
+                excl_attr_strings.access_time_attr = "ACCESS_TIME";
+                excl_attr_strings.write_time_attr = "WRITE_TIME";
+                excl_attr_strings.change_time_attr = "CHANGE_TIME";
+        } else {
+                /* old-style names */
+                attr_strings.create_time_attr = NULL;
+                attr_strings.access_time_attr = "A_TIME";
+                attr_strings.write_time_attr = "M_TIME";
+                attr_strings.change_time_attr = "C_TIME";
+
+                excl_attr_strings.create_time_attr = NULL;
+                excl_attr_strings.access_time_attr = "dos_attr.A_TIME";
+                excl_attr_strings.write_time_attr = "dos_attr.M_TIME";
+                excl_attr_strings.change_time_attr = "dos_attr.C_TIME";
+        }
 
-       fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
-
-       if (fnum == -1) {
-                DEBUG(5, ("cacl_get failed to open %s: %s\n",
-                          filename, cli_errstr(cli)));
-                errno = 0;
-               return -1;
-       }
+        /* Copy name so we can strip off exclusions (if any are specified) */
+        strncpy(name_sandbox, attr_name, sizeof(name_sandbox) - 1);
 
-       sd = cli_query_secdesc(cli, fnum, ctx);
+        /* Ensure name is null terminated */
+        name_sandbox[sizeof(name_sandbox) - 1] = '\0';
 
-       if (!sd) {
-                DEBUG(5, ("cacl_get Failed to query old descriptor\n"));
-                errno = 0;
-               return -1;
-       }
+        /* Play in the sandbox */
+        name = name_sandbox;
 
-       cli_close(cli, fnum);
+        /* If there are any exclusions, point to them and mask them from name */
+        if ((pExclude = strchr(name, '!')) != NULL)
+        {
+                *pExclude++ = '\0';
+        }
 
-        all = (*name == '*');
+        all = (StrnCaseCmp(name, "system.*", 8) == 0);
+        all_nt = (StrnCaseCmp(name, "system.nt_sec_desc.*", 20) == 0);
+        all_nt_acls = (StrnCaseCmp(name, "system.nt_sec_desc.acl.*", 24) == 0);
+        all_dos = (StrnCaseCmp(name, "system.dos_attr.*", 17) == 0);
+        some_nt = (StrnCaseCmp(name, "system.nt_sec_desc.", 19) == 0);
+        some_dos = (StrnCaseCmp(name, "system.dos_attr.", 16) == 0);
         numeric = (* (name + strlen(name) - 1) != '+');
 
+        /* Look for exclusions from "all" requests */
+        if (all || all_nt || all_dos) {
+
+                /* Exclusions are delimited by '!' */
+                for (;
+                     pExclude != NULL;
+                     pExclude = (p == NULL ? NULL : p + 1)) {
+
+                /* Find end of this exclusion name */
+                if ((p = strchr(pExclude, '!')) != NULL)
+                {
+                    *p = '\0';
+                }
+
+                /* Which exclusion name is this? */
+                if (StrCaseCmp(pExclude, "nt_sec_desc.revision") == 0) {
+                    exclude_nt_revision = True;
+                }
+                else if (StrCaseCmp(pExclude, "nt_sec_desc.owner") == 0) {
+                    exclude_nt_owner = True;
+                }
+                else if (StrCaseCmp(pExclude, "nt_sec_desc.group") == 0) {
+                    exclude_nt_group = True;
+                }
+                else if (StrCaseCmp(pExclude, "nt_sec_desc.acl") == 0) {
+                    exclude_nt_acl = True;
+                }
+                else if (StrCaseCmp(pExclude, "dos_attr.mode") == 0) {
+                    exclude_dos_mode = True;
+                }
+                else if (StrCaseCmp(pExclude, "dos_attr.size") == 0) {
+                    exclude_dos_size = True;
+                }
+                else if (excl_attr_strings.create_time_attr != NULL &&
+                         StrCaseCmp(pExclude,
+                                    excl_attr_strings.change_time_attr) == 0) {
+                    exclude_dos_create_time = True;
+                }
+                else if (StrCaseCmp(pExclude,
+                                    excl_attr_strings.access_time_attr) == 0) {
+                    exclude_dos_access_time = True;
+                }
+                else if (StrCaseCmp(pExclude,
+                                    excl_attr_strings.write_time_attr) == 0) {
+                    exclude_dos_write_time = True;
+                }
+                else if (StrCaseCmp(pExclude,
+                                    excl_attr_strings.change_time_attr) == 0) {
+                    exclude_dos_change_time = True;
+                }
+                else if (StrCaseCmp(pExclude, "dos_attr.inode") == 0) {
+                    exclude_dos_inode = True;
+                }
+                else {
+                    DEBUG(5, ("cacl_get received unknown exclusion: %s\n",
+                              pExclude));
+                    errno = ENOATTR;
+                    return -1;
+                }
+            }
+        }
+
         n_used = 0;
 
-        if (all) {
-                if (determine_size) {
-                        p = talloc_asprintf(ctx,
-                                            "REVISION:%d", sd->revision);
-                        if (!p) {
-                                errno = ENOMEM;
+        /*
+         * If we are (possibly) talking to an NT or new system and some NT
+         * attributes have been requested...
+         */
+        if (ipc_cli && (all || some_nt || all_nt_acls)) {
+                /* Point to the portion after "system.nt_sec_desc." */
+                name += 19;     /* if (all) this will be invalid but unused */
+
+                /* ... then obtain any NT attributes which were requested */
+                fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
+
+                if (fnum == -1) {
+                        DEBUG(5, ("cacl_get failed to open %s: %s\n",
+                                  filename, cli_errstr(cli)));
+                        errno = 0;
+                        return -1;
+                }
+
+                sd = cli_query_secdesc(cli, fnum, ctx);
+
+                if (!sd) {
+                        DEBUG(5,
+                              ("cacl_get Failed to query old descriptor\n"));
+                        errno = 0;
+                        return -1;
+                }
+
+                cli_close(cli, fnum);
+
+                if (! exclude_nt_revision) {
+                        if (all || all_nt) {
+                                if (determine_size) {
+                                        p = talloc_asprintf(ctx,
+                                                            "REVISION:%d",
+                                                            sd->revision);
+                                        if (!p) {
+                                                errno = ENOMEM;
+                                                return -1;
+                                        }
+                                        n = strlen(p);
+                                } else {
+                                        n = snprintf(buf, bufsize,
+                                                     "REVISION:%d",
+                                                     sd->revision);
+                                }
+                        } else if (StrCaseCmp(name, "revision") == 0) {
+                                if (determine_size) {
+                                        p = talloc_asprintf(ctx, "%d",
+                                                            sd->revision);
+                                        if (!p) {
+                                                errno = ENOMEM;
+                                                return -1;
+                                        }
+                                        n = strlen(p);
+                                } else {
+                                        n = snprintf(buf, bufsize, "%d",
+                                                     sd->revision);
+                                }
+                        }
+        
+                        if (!determine_size && n > bufsize) {
+                                errno = ERANGE;
                                 return -1;
                         }
-                        n = strlen(p);
-                } else {
-                        n = snprintf(buf, bufsize,
-                                     "REVISION:%d", sd->revision);
+                        buf += n;
+                        n_used += n;
+                        bufsize -= n;
                 }
-        } else if (StrCaseCmp(name, "revision") == 0) {
-                if (determine_size) {
-                        p = talloc_asprintf(ctx, "%d", sd->revision);
-                        if (!p) {
-                                errno = ENOMEM;
+
+                if (! exclude_nt_owner) {
+                        /* Get owner and group sid */
+                        if (sd->owner_sid) {
+                                convert_sid_to_string(ipc_cli, pol,
+                                                      sidstr,
+                                                      numeric,
+                                                      sd->owner_sid);
+                        } else {
+                                fstrcpy(sidstr, "");
+                        }
+
+                        if (all || all_nt) {
+                                if (determine_size) {
+                                        p = talloc_asprintf(ctx, ",OWNER:%s",
+                                                            sidstr);
+                                        if (!p) {
+                                                errno = ENOMEM;
+                                                return -1;
+                                        }
+                                        n = strlen(p);
+                                } else if (sidstr[0] != '\0') {
+                                        n = snprintf(buf, bufsize,
+                                                     ",OWNER:%s", sidstr);
+                                }
+                        } else if (StrnCaseCmp(name, "owner", 5) == 0) {
+                                if (determine_size) {
+                                        p = talloc_asprintf(ctx, "%s", sidstr);
+                                        if (!p) {
+                                                errno = ENOMEM;
+                                                return -1;
+                                        }
+                                        n = strlen(p);
+                                } else {
+                                        n = snprintf(buf, bufsize, "%s",
+                                                     sidstr);
+                                }
+                        }
+
+                        if (!determine_size && n > bufsize) {
+                                errno = ERANGE;
+                                return -1;
+                        }
+                        buf += n;
+                        n_used += n;
+                        bufsize -= n;
+                }
+
+                if (! exclude_nt_group) {
+                        if (sd->group_sid) {
+                                convert_sid_to_string(ipc_cli, pol,
+                                                      sidstr, numeric,
+                                                      sd->group_sid);
+                        } else {
+                                fstrcpy(sidstr, "");
+                        }
+
+                        if (all || all_nt) {
+                                if (determine_size) {
+                                        p = talloc_asprintf(ctx, ",GROUP:%s",
+                                                            sidstr);
+                                        if (!p) {
+                                                errno = ENOMEM;
+                                                return -1;
+                                        }
+                                        n = strlen(p);
+                                } else if (sidstr[0] != '\0') {
+                                        n = snprintf(buf, bufsize,
+                                                     ",GROUP:%s", sidstr);
+                                }
+                        } else if (StrnCaseCmp(name, "group", 5) == 0) {
+                                if (determine_size) {
+                                        p = talloc_asprintf(ctx, "%s", sidstr);
+                                        if (!p) {
+                                                errno = ENOMEM;
+                                                return -1;
+                                        }
+                                        n = strlen(p);
+                                } else {
+                                        n = snprintf(buf, bufsize,
+                                                     "%s", sidstr);
+                                }
+                        }
+
+                        if (!determine_size && n > bufsize) {
+                                errno = ERANGE;
+                                return -1;
+                        }
+                        buf += n;
+                        n_used += n;
+                        bufsize -= n;
+                }
+
+                if (! exclude_nt_acl) {
+                        /* Add aces to value buffer  */
+                        for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
+
+                                SEC_ACE *ace = &sd->dacl->aces[i];
+                                convert_sid_to_string(ipc_cli, pol,
+                                                      sidstr, numeric,
+                                                      &ace->trustee);
+
+                                if (all || all_nt) {
+                                        if (determine_size) {
+                                                p = talloc_asprintf(
+                                                        ctx, 
+                                                        ",ACL:"
+                                                        "%s:%d/%d/0x%08x", 
+                                                        sidstr,
+                                                        ace->type,
+                                                        ace->flags,
+                                                        ace->access_mask);
+                                                if (!p) {
+                                                        errno = ENOMEM;
+                                                        return -1;
+                                                }
+                                                n = strlen(p);
+                                        } else {
+                                                n = snprintf(
+                                                        buf, bufsize,
+                                                        ",ACL:%s:%d/%d/0x%08x", 
+                                                        sidstr,
+                                                        ace->type,
+                                                        ace->flags,
+                                                        ace->access_mask);
+                                        }
+                                } else if ((StrnCaseCmp(name, "acl", 3) == 0 &&
+                                            StrCaseCmp(name+3, sidstr) == 0) ||
+                                           (StrnCaseCmp(name, "acl+", 4) == 0 &&
+                                            StrCaseCmp(name+4, sidstr) == 0)) {
+                                        if (determine_size) {
+                                                p = talloc_asprintf(
+                                                        ctx, 
+                                                        "%d/%d/0x%08x", 
+                                                        ace->type,
+                                                        ace->flags,
+                                                        ace->access_mask);
+                                                if (!p) {
+                                                        errno = ENOMEM;
+                                                        return -1;
+                                                }
+                                                n = strlen(p);
+                                        } else {
+                                                n = snprintf(buf, bufsize,
+                                                             "%d/%d/0x%08x", 
+                                                             ace->type,
+                                                             ace->flags,
+                                                             ace->access_mask);
+                                        }
+                                } else if (all_nt_acls) {
+                                        if (determine_size) {
+                                                p = talloc_asprintf(
+                                                        ctx, 
+                                                        "%s%s:%d/%d/0x%08x",
+                                                        i ? "," : "",
+                                                        sidstr,
+                                                        ace->type,
+                                                        ace->flags,
+                                                        ace->access_mask);
+                                                if (!p) {
+                                                        errno = ENOMEM;
+                                                        return -1;
+                                                }
+                                                n = strlen(p);
+                                        } else {
+                                                n = snprintf(buf, bufsize,
+                                                             "%s%s:%d/%d/0x%08x",
+                                                             i ? "," : "",
+                                                             sidstr,
+                                                             ace->type,
+                                                             ace->flags,
+                                                             ace->access_mask);
+                                        }
+                                }
+                                if (!determine_size && n > bufsize) {
+                                        errno = ERANGE;
+                                        return -1;
+                                }
+                                buf += n;
+                                n_used += n;
+                                bufsize -= n;
+                        }
+                }
+
+                /* Restore name pointer to its original value */
+                name -= 19;
+        }
+
+        if (all || some_dos) {
+                /* Point to the portion after "system.dos_attr." */
+                name += 16;     /* if (all) this will be invalid but unused */
+
+                /* Obtain the DOS attributes */
+                if (!smbc_getatr(context, srv, filename, &mode, &size, 
+                                 &create_time_ts,
+                                 &access_time_ts,
+                                 &write_time_ts,
+                                 &change_time_ts,
+                                 &ino)) {
+                        
+                        errno = smbc_errno(context, srv->cli);
+                        return -1;
+                        
+                }
+
+                create_time = convert_timespec_to_time_t(create_time_ts);
+                access_time = convert_timespec_to_time_t(access_time_ts);
+                write_time = convert_timespec_to_time_t(write_time_ts);
+                change_time = convert_timespec_to_time_t(change_time_ts);
+
+                if (! exclude_dos_mode) {
+                        if (all || all_dos) {
+                                if (determine_size) {
+                                        p = talloc_asprintf(ctx,
+                                                            "%sMODE:0x%x",
+                                                            (ipc_cli &&
+                                                             (all || some_nt)
+                                                             ? ","
+                                                             : ""),
+                                                            mode);
+                                        if (!p) {
+                                                errno = ENOMEM;
+                                                return -1;
+                                        }
+                                        n = strlen(p);
+                                } else {
+                                        n = snprintf(buf, bufsize,
+                                                     "%sMODE:0x%x",
+                                                     (ipc_cli &&
+                                                      (all || some_nt)
+                                                      ? ","
+                                                      : ""),
+                                                     mode);
+                                }
+                        } else if (StrCaseCmp(name, "mode") == 0) {
+                                if (determine_size) {
+                                        p = talloc_asprintf(ctx, "0x%x", mode);
+                                        if (!p) {
+                                                errno = ENOMEM;
+                                                return -1;
+                                        }
+                                        n = strlen(p);
+                                } else {
+                                        n = snprintf(buf, bufsize,
+                                                     "0x%x", mode);
+                                }
+                        }
+        
+                        if (!determine_size && n > bufsize) {
+                                errno = ERANGE;
+                                return -1;
+                        }
+                        buf += n;
+                        n_used += n;
+                        bufsize -= n;
+                }
+
+                if (! exclude_dos_size) {
+                        if (all || all_dos) {
+                                if (determine_size) {
+                                        p = talloc_asprintf(
+                                                ctx,
+                                                ",SIZE:%.0f",
+                                                (double)size);
+                                        if (!p) {
+                                                errno = ENOMEM;
+                                                return -1;
+                                        }
+                                        n = strlen(p);
+                                } else {
+                                        n = snprintf(buf, bufsize,
+                                                     ",SIZE:%.0f",
+                                                     (double)size);
+                                }
+                        } else if (StrCaseCmp(name, "size") == 0) {
+                                if (determine_size) {
+                                        p = talloc_asprintf(
+                                                ctx,
+                                                "%.0f",
+                                                (double)size);
+                                        if (!p) {
+                                                errno = ENOMEM;
+                                                return -1;
+                                        }
+                                        n = strlen(p);
+                                } else {
+                                        n = snprintf(buf, bufsize,
+                                                     "%.0f",
+                                                     (double)size);
+                                }
+                        }
+        
+                        if (!determine_size && n > bufsize) {
+                                errno = ERANGE;
                                 return -1;
                         }
-                        n = strlen(p);
-                } else {
-                        n = snprintf(buf, bufsize, "%d", sd->revision);
+                        buf += n;
+                        n_used += n;
+                        bufsize -= n;
                 }
-        }
-        
-        if (!determine_size && n > bufsize) {
-                errno = ERANGE;
-                return -1;
-        }
-        buf += n;
-        n_used += n;
-        bufsize -= n;
-
-       /* Get owner and group sid */
-
-       if (sd->owner_sid) {
-                convert_sid_to_string(ipc_cli, pol,
-                                      sidstr, numeric, sd->owner_sid);
-       } else {
-               fstrcpy(sidstr, "");
-       }
 
-        if (all) {
-                if (determine_size) {
-                        p = talloc_asprintf(ctx, ",OWNER:%s", sidstr);
-                        if (!p) {
-                                errno = ENOMEM;
+                if (! exclude_dos_create_time &&
+                    attr_strings.create_time_attr != NULL) {
+                        if (all || all_dos) {
+                                if (determine_size) {
+                                        p = talloc_asprintf(ctx,
+                                                            ",%s:%lu",
+                                                            attr_strings.create_time_attr,
+                                                            create_time);
+                                        if (!p) {
+                                                errno = ENOMEM;
+                                                return -1;
+                                        }
+                                        n = strlen(p);
+                                } else {
+                                        n = snprintf(buf, bufsize,
+                                                     ",%s:%lu",
+                                                     attr_strings.create_time_attr,
+                                                     create_time);
+                                }
+                        } else if (StrCaseCmp(name, attr_strings.create_time_attr) == 0) {
+                                if (determine_size) {
+                                        p = talloc_asprintf(ctx, "%lu", create_time);
+                                        if (!p) {
+                                                errno = ENOMEM;
+                                                return -1;
+                                        }
+                                        n = strlen(p);
+                                } else {
+                                        n = snprintf(buf, bufsize,
+                                                     "%lu", create_time);
+                                }
+                        }
+        
+                        if (!determine_size && n > bufsize) {
+                                errno = ERANGE;
                                 return -1;
                         }
-                        n = strlen(p);
-                } else {
-                        n = snprintf(buf, bufsize, ",OWNER:%s", sidstr);
+                        buf += n;
+                        n_used += n;
+                        bufsize -= n;
                 }
-        } else if (StrnCaseCmp(name, "owner", 5) == 0) {
-                if (determine_size) {
-                        p = talloc_asprintf(ctx, "%s", sidstr);
-                        if (!p) {
-                                errno = ENOMEM;
+
+                if (! exclude_dos_access_time) {
+                        if (all || all_dos) {
+                                if (determine_size) {
+                                        p = talloc_asprintf(ctx,
+                                                            ",%s:%lu",
+                                                            attr_strings.access_time_attr,
+                                                            access_time);
+                                        if (!p) {
+                                                errno = ENOMEM;
+                                                return -1;
+                                        }
+                                        n = strlen(p);
+                                } else {
+                                        n = snprintf(buf, bufsize,
+                                                     ",%s:%lu",
+                                                     attr_strings.access_time_attr,
+                                                     access_time);
+                                }
+                        } else if (StrCaseCmp(name, attr_strings.access_time_attr) == 0) {
+                                if (determine_size) {
+                                        p = talloc_asprintf(ctx, "%lu", access_time);
+                                        if (!p) {
+                                                errno = ENOMEM;
+                                                return -1;
+                                        }
+                                        n = strlen(p);
+                                } else {
+                                        n = snprintf(buf, bufsize,
+                                                     "%lu", access_time);
+                                }
+                        }
+        
+                        if (!determine_size && n > bufsize) {
+                                errno = ERANGE;
                                 return -1;
                         }
-                        n = strlen(p);
-                } else {
-                        n = snprintf(buf, bufsize, "%s", sidstr);
+                        buf += n;
+                        n_used += n;
+                        bufsize -= n;
                 }
-        }
-
-        if (!determine_size && n > bufsize) {
-                errno = ERANGE;
-                return -1;
-        }
-        buf += n;
-        n_used += n;
-        bufsize -= n;
 
-       if (sd->grp_sid) {
-               convert_sid_to_string(ipc_cli, pol,
-                                      sidstr, numeric, sd->grp_sid);
-       } else {
-               fstrcpy(sidstr, "");
-       }
-
-        if (all) {
-                if (determine_size) {
-                        p = talloc_asprintf(ctx, ",GROUP:%s", sidstr);
-                        if (!p) {
-                                errno = ENOMEM;
+                if (! exclude_dos_write_time) {
+                        if (all || all_dos) {
+                                if (determine_size) {
+                                        p = talloc_asprintf(ctx,
+                                                            ",%s:%lu",
+                                                            attr_strings.write_time_attr,
+                                                            write_time);
+                                        if (!p) {
+                                                errno = ENOMEM;
+                                                return -1;
+                                        }
+                                        n = strlen(p);
+                                } else {
+                                        n = snprintf(buf, bufsize,
+                                                     ",%s:%lu",
+                                                     attr_strings.write_time_attr,
+                                                     write_time);
+                                }
+                        } else if (StrCaseCmp(name, attr_strings.write_time_attr) == 0) {
+                                if (determine_size) {
+                                        p = talloc_asprintf(ctx, "%lu", write_time);
+                                        if (!p) {
+                                                errno = ENOMEM;
+                                                return -1;
+                                        }
+                                        n = strlen(p);
+                                } else {
+                                        n = snprintf(buf, bufsize,
+                                                     "%lu", write_time);
+                                }
+                        }
+        
+                        if (!determine_size && n > bufsize) {
+                                errno = ERANGE;
                                 return -1;
                         }
-                        n = strlen(p);
-                } else {
-                        n = snprintf(buf, bufsize, ",GROUP:%s", sidstr);
+                        buf += n;
+                        n_used += n;
+                        bufsize -= n;
                 }
-        } else if (StrnCaseCmp(name, "group", 5) == 0) {
-                if (determine_size) {
-                        p = talloc_asprintf(ctx, "%s", sidstr);
-                        if (!p) {
-                                errno = ENOMEM;
+
+                if (! exclude_dos_change_time) {
+                        if (all || all_dos) {
+                                if (determine_size) {
+                                        p = talloc_asprintf(ctx,
+                                                            ",%s:%lu",
+                                                            attr_strings.change_time_attr,
+                                                            change_time);
+                                        if (!p) {
+                                                errno = ENOMEM;
+                                                return -1;
+                                        }
+                                        n = strlen(p);
+                                } else {
+                                        n = snprintf(buf, bufsize,
+                                                     ",%s:%lu",
+                                                     attr_strings.change_time_attr,
+                                                     change_time);
+                                }
+                        } else if (StrCaseCmp(name, attr_strings.change_time_attr) == 0) {
+                                if (determine_size) {
+                                        p = talloc_asprintf(ctx, "%lu", change_time);
+                                        if (!p) {
+                                                errno = ENOMEM;
+                                                return -1;
+                                        }
+                                        n = strlen(p);
+                                } else {
+                                        n = snprintf(buf, bufsize,
+                                                     "%lu", change_time);
+                                }
+                        }
+        
+                        if (!determine_size && n > bufsize) {
+                                errno = ERANGE;
                                 return -1;
                         }
-                        n = strlen(p);
-                } else {
-                        n = snprintf(buf, bufsize, "%s", sidstr);
+                        buf += n;
+                        n_used += n;
+                        bufsize -= n;
                 }
-        }
 
-        if (!determine_size && n > bufsize) {
-                errno = ERANGE;
-                return -1;
-        }
-        buf += n;
-        n_used += n;
-        bufsize -= n;
-
-       /* Add aces to value buffer  */
-       for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
-
-               SEC_ACE *ace = &sd->dacl->ace[i];
-               convert_sid_to_string(ipc_cli, pol,
-                                      sidstr, numeric, &ace->trustee);
-
-                if (all) {
-                        if (determine_size) {
-                                p = talloc_asprintf(ctx, 
-                                                    ",ACL:%s:%d/%d/0x%08x", 
-                                                    sidstr,
-                                                    ace->type,
-                                                    ace->flags,
-                                                    ace->info.mask);
-                                if (!p) {
-                                        errno = ENOMEM;
-                                        return -1;
+                if (! exclude_dos_inode) {
+                        if (all || all_dos) {
+                                if (determine_size) {
+                                        p = talloc_asprintf(
+                                                ctx,
+                                                ",INODE:%.0f",
+                                                (double)ino);
+                                        if (!p) {
+                                                errno = ENOMEM;
+                                                return -1;
+                                        }
+                                        n = strlen(p);
+                                } else {
+                                        n = snprintf(buf, bufsize,
+                                                     ",INODE:%.0f",
+                                                     (double) ino);
                                 }
-                                n = strlen(p);
-                        } else {
-                                n = snprintf(buf, bufsize,
-                                             ",ACL:%s:%d/%d/0x%08x", 
-                                             sidstr,
-                                             ace->type,
-                                             ace->flags,
-                                             ace->info.mask);
-                        }
-                } else if ((StrnCaseCmp(name, "acl", 3) == 0 &&
-                            StrCaseCmp(name + 3, sidstr) == 0) ||
-                           (StrnCaseCmp(name, "acl+", 4) == 0 &&
-                            StrCaseCmp(name + 4, sidstr) == 0)) {
-                        if (determine_size) {
-                                p = talloc_asprintf(ctx, 
-                                                    "%d/%d/0x%08x", 
-                                                    ace->type,
-                                                    ace->flags,
-                                                    ace->info.mask);
-                                if (!p) {
-                                        errno = ENOMEM;
-                                        return -1;
+                        } else if (StrCaseCmp(name, "inode") == 0) {
+                                if (determine_size) {
+                                        p = talloc_asprintf(
+                                                ctx,
+                                                "%.0f",
+                                                (double) ino);
+                                        if (!p) {
+                                                errno = ENOMEM;
+                                                return -1;
+                                        }
+                                        n = strlen(p);
+                                } else {
+                                        n = snprintf(buf, bufsize,
+                                                     "%.0f",
+                                                     (double) ino);
                                 }
-                                n = strlen(p);
-                        } else {
-                                n = snprintf(buf, bufsize,
-                                             "%d/%d/0x%08x", 
-                                             ace->type, ace->flags, ace->info.mask);
                         }
+        
+                        if (!determine_size && n > bufsize) {
+                                errno = ERANGE;
+                                return -1;
+                        }
+                        buf += n;
+                        n_used += n;
+                        bufsize -= n;
                 }
-                if (n > bufsize) {
-                        errno = ERANGE;
-                        return -1;
-                }
-                buf += n;
-                n_used += n;
-                bufsize -= n;
-       }
+
+                /* Restore name pointer to its original value */
+                name -= 16;
+        }
 
         if (n_used == 0) {
                 errno = ENOATTR;
                 return -1;
         }
+
        return n_used;
 }
 
@@ -3455,10 +5053,15 @@ static int cacl_get(TALLOC_CTX *ctx, struct cli_state *cli,
 /***************************************************** 
 set the ACLs on a file given an ascii description
 *******************************************************/
-static int cacl_set(TALLOC_CTX *ctx, struct cli_state *cli,
-                    struct cli_state *ipc_cli, POLICY_HND *pol,
-                    const char *filename, const char *the_acl,
-                    int mode, int flags)
+static int
+cacl_set(TALLOC_CTX *ctx,
+         struct cli_state *cli,
+         struct cli_state *ipc_cli,
+         POLICY_HND *pol,
+         const char *filename,
+         const char *the_acl,
+         int mode,
+         int flags)
 {
        int fnum;
         int err = 0;
@@ -3484,15 +5087,23 @@ static int cacl_set(TALLOC_CTX *ctx, struct cli_state *cli,
                         the_acl = p + 1;
                 }
 
-                sd = sec_desc_parse(ctx, ipc_cli, pol,
-                                    numeric, (char *) the_acl);
+                sd = sec_desc_parse(ctx, ipc_cli, pol, numeric,
+                                    CONST_DISCARD(char *, the_acl));
 
                 if (!sd) {
-                        errno = EINVAL;
-                        return -1;
+                       errno = EINVAL;
+                       return -1;
                 }
         }
 
+       /* SMBC_XATTR_MODE_REMOVE_ALL is the only caller
+          that doesn't deref sd */
+
+       if (!sd && (mode != SMBC_XATTR_MODE_REMOVE_ALL)) {
+               errno = EINVAL;
+               return -1;
+       }
+
        /* The desired access below is the only one I could find that works
           with NT4, W2KP and Samba */
 
@@ -3518,9 +5129,9 @@ static int cacl_set(TALLOC_CTX *ctx, struct cli_state *cli,
        switch (mode) {
        case SMBC_XATTR_MODE_REMOVE_ALL:
                 old->dacl->num_aces = 0;
-                SAFE_FREE(old->dacl->ace);
+                SAFE_FREE(old->dacl->aces);
                 SAFE_FREE(old->dacl);
-                old->off_dacl = 0;
+                old->dacl = NULL;
                 dacl = old->dacl;
                 break;
 
@@ -3529,17 +5140,18 @@ static int cacl_set(TALLOC_CTX *ctx, struct cli_state *cli,
                        BOOL found = False;
 
                        for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
-                                if (sec_ace_equal(&sd->dacl->ace[i],
-                                                  &old->dacl->ace[j])) {
+                                if (sec_ace_equal(&sd->dacl->aces[i],
+                                                  &old->dacl->aces[j])) {
                                        uint32 k;
                                        for (k=j; k<old->dacl->num_aces-1;k++) {
-                                               old->dacl->ace[k] = old->dacl->ace[k+1];
+                                               old->dacl->aces[k] =
+                                                        old->dacl->aces[k+1];
                                        }
                                        old->dacl->num_aces--;
                                        if (old->dacl->num_aces == 0) {
-                                               SAFE_FREE(old->dacl->ace);
+                                               SAFE_FREE(old->dacl->aces);
                                                SAFE_FREE(old->dacl);
-                                               old->off_dacl = 0;
+                                               old->dacl = NULL;
                                        }
                                        found = True;
                                         dacl = old->dacl;
@@ -3560,14 +5172,14 @@ static int cacl_set(TALLOC_CTX *ctx, struct cli_state *cli,
                        BOOL found = False;
 
                        for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
-                               if (sid_equal(&sd->dacl->ace[i].trustee,
-                                             &old->dacl->ace[j].trustee)) {
+                               if (sid_equal(&sd->dacl->aces[i].trustee,
+                                             &old->dacl->aces[j].trustee)) {
                                         if (!(flags & SMBC_XATTR_FLAG_CREATE)) {
                                                 err = EEXIST;
                                                 ret = -1;
                                                 goto failed;
                                         }
-                                        old->dacl->ace[j] = sd->dacl->ace[i];
+                                        old->dacl->aces[j] = sd->dacl->aces[i];
                                         ret = -1;
                                        found = True;
                                }
@@ -3580,7 +5192,7 @@ static int cacl_set(TALLOC_CTX *ctx, struct cli_state *cli,
                        }
                         
                         for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
-                                add_ace(&old->dacl, &sd->dacl->ace[i], ctx);
+                                add_ace(&old->dacl, &sd->dacl->aces[i], ctx);
                         }
                }
                 dacl = old->dacl;
@@ -3589,7 +5201,7 @@ static int cacl_set(TALLOC_CTX *ctx, struct cli_state *cli,
        case SMBC_XATTR_MODE_SET:
                old = sd;
                 owner_sid = old->owner_sid;
-                grp_sid = old->grp_sid;
+                grp_sid = old->group_sid;
                 dacl = old->dacl;
                break;
 
@@ -3598,7 +5210,7 @@ static int cacl_set(TALLOC_CTX *ctx, struct cli_state *cli,
                 break;
 
         case SMBC_XATTR_MODE_CHGRP:
-                grp_sid = sd->grp_sid;
+                grp_sid = sd->group_sid;
                 break;
        }
 
@@ -3637,20 +5249,33 @@ static int cacl_set(TALLOC_CTX *ctx, struct cli_state *cli,
 }
 
 
-int smbc_setxattr_ctx(SMBCCTX *context,
-                      const char *fname,
-                      const char *name,
-                      const void *value,
-                      size_t size,
-                      int flags)
+static int
+smbc_setxattr_ctx(SMBCCTX *context,
+                  const char *fname,
+                  const char *name,
+                  const void *value,
+                  size_t size,
+                  int flags)
 {
         int ret;
+        int ret2;
         SMBCSRV *srv;
         SMBCSRV *ipc_srv;
-       fstring server, share, user, password, workgroup;
+       fstring server;
+        fstring share;
+        fstring user;
+        fstring password;
+        fstring workgroup;
        pstring path;
         TALLOC_CTX *ctx;
         POLICY_HND pol;
+        DOS_ATTR_DESC *dad;
+        struct {
+                const char * create_time_attr;
+                const char * access_time_attr;
+                const char * write_time_attr;
+                const char * change_time_attr;
+        } attr_strings;
 
        if (!context || !context->internal ||
            !context->internal->_initialized) {
@@ -3668,9 +5293,10 @@ int smbc_setxattr_ctx(SMBCCTX *context,
        }
   
        DEBUG(4, ("smbc_setxattr(%s, %s, %.*s)\n",
-                  fname, name, (int) size, (char *) value));
+                  fname, name, (int) size, (const char*)value));
 
        if (smbc_parse_path(context, fname,
+                            workgroup, sizeof(workgroup),
                             server, sizeof(server),
                             share, sizeof(share),
                             path, sizeof(path),
@@ -3683,18 +5309,21 @@ int smbc_setxattr_ctx(SMBCCTX *context,
 
        if (user[0] == (char)0) fstrcpy(user, context->user);
 
-       fstrcpy(workgroup, context->workgroup);
-
-       srv = smbc_server(context, server, share, workgroup, user, password);
+       srv = smbc_server(context, True,
+                          server, share, workgroup, user, password);
        if (!srv) {
                return -1;  /* errno set by smbc_server */
        }
 
-        ipc_srv = smbc_attr_server(context, server, share,
-                                   workgroup, user, password,
-                                   &pol);
-        if (!ipc_srv) {
-                return -1;
+        if (! srv->no_nt_session) {
+                ipc_srv = smbc_attr_server(context, server, share,
+                                           workgroup, user, password,
+                                           &pol);
+                if (! ipc_srv) {
+                        srv->no_nt_session = True;
+                }
+        } else {
+                ipc_srv = NULL;
         }
         
         ctx = talloc_init("smbc_setxattr");
@@ -3703,6 +5332,64 @@ int smbc_setxattr_ctx(SMBCCTX *context,
                 return -1;
         }
 
+        /*
+         * Are they asking to set the entire set of known attributes?
+         */
+        if (StrCaseCmp(name, "system.*") == 0 ||
+            StrCaseCmp(name, "system.*+") == 0) {
+                /* Yup. */
+                char *namevalue =
+                        talloc_asprintf(ctx, "%s:%s",
+                                        name+7, (const char *) value);
+                if (! namevalue) {
+                        errno = ENOMEM;
+                        ret = -1;
+                        return -1;
+                }
+
+                if (ipc_srv) {
+                        ret = cacl_set(ctx, srv->cli,
+                                       ipc_srv->cli, &pol, path,
+                                       namevalue,
+                                       (*namevalue == '*'
+                                        ? SMBC_XATTR_MODE_SET
+                                        : SMBC_XATTR_MODE_ADD),
+                                       flags);
+                } else {
+                        ret = 0;
+                }
+
+                /* get a DOS Attribute Descriptor with current attributes */
+                dad = dos_attr_query(context, ctx, path, srv);
+                if (dad) {
+                        /* Overwrite old with new, using what was provided */
+                        dos_attr_parse(context, dad, srv, namevalue);
+
+                        /* Set the new DOS attributes */
+                        if (! smbc_setatr(context, srv, path,
+                                          dad->create_time,
+                                          dad->access_time,
+                                          dad->write_time,
+                                          dad->change_time,
+                                          dad->mode)) {
+
+                                /* cause failure if NT failed too */
+                                dad = NULL; 
+                        }
+                }
+
+                /* we only fail if both NT and DOS sets failed */
+                if (ret < 0 && ! dad) {
+                        ret = -1; /* in case dad was null */
+                }
+                else {
+                        ret = 0;
+                }
+
+                talloc_destroy(ctx);
+                return ret;
+        }
+
         /*
          * Are they asking to set an access control element or to set
          * the entire access control list?
@@ -3715,13 +5402,18 @@ int smbc_setxattr_ctx(SMBCCTX *context,
 
                 /* Yup. */
                 char *namevalue =
-                        talloc_asprintf(ctx, "%s:%s", name+19, (char *) value);
-                if (! namevalue) {
+                        talloc_asprintf(ctx, "%s:%s",
+                                        name+19, (const char *) value);
+
+                if (! ipc_srv) {
+                        ret = -1; /* errno set by smbc_server() */
+                }
+                else if (! namevalue) {
                         errno = ENOMEM;
                         ret = -1;
                 } else {
-                        ret = cacl_set(ctx, &srv->cli,
-                                       &ipc_srv->cli, &pol, path,
+                        ret = cacl_set(ctx, srv->cli,
+                                       ipc_srv->cli, &pol, path,
                                        namevalue,
                                        (*namevalue == '*'
                                         ? SMBC_XATTR_MODE_SET
@@ -3740,13 +5432,19 @@ int smbc_setxattr_ctx(SMBCCTX *context,
 
                 /* Yup. */
                 char *namevalue =
-                        talloc_asprintf(ctx, "%s:%s", name+19, (char *) value);
-                if (! namevalue) {
+                        talloc_asprintf(ctx, "%s:%s",
+                                        name+19, (const char *) value);
+
+                if (! ipc_srv) {
+                        
+                        ret = -1; /* errno set by smbc_server() */
+                }
+                else if (! namevalue) {
                         errno = ENOMEM;
                         ret = -1;
                 } else {
-                        ret = cacl_set(ctx, &srv->cli,
-                                       &ipc_srv->cli, &pol, path,
+                        ret = cacl_set(ctx, srv->cli,
+                                       ipc_srv->cli, &pol, path,
                                        namevalue, SMBC_XATTR_MODE_CHOWN, 0);
                 }
                 talloc_destroy(ctx);
@@ -3761,38 +5459,118 @@ int smbc_setxattr_ctx(SMBCCTX *context,
 
                 /* Yup. */
                 char *namevalue =
-                        talloc_asprintf(ctx, "%s:%s", name+19, (char *) value);
-                if (! namevalue) {
+                        talloc_asprintf(ctx, "%s:%s",
+                                        name+19, (const char *) value);
+
+                if (! ipc_srv) {
+                        /* errno set by smbc_server() */
+                        ret = -1;
+                }
+                else if (! namevalue) {
                         errno = ENOMEM;
                         ret = -1;
                 } else {
-                        ret = cacl_set(ctx, &srv->cli,
-                                       &ipc_srv->cli, &pol, path,
+                        ret = cacl_set(ctx, srv->cli,
+                                       ipc_srv->cli, &pol, path,
                                        namevalue, SMBC_XATTR_MODE_CHOWN, 0);
                 }
                 talloc_destroy(ctx);
                 return ret;
         }
 
+        /* Determine whether to use old-style or new-style attribute names */
+        if (context->internal->_full_time_names) {
+                /* new-style names */
+                attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME";
+                attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME";
+                attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME";
+                attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME";
+        } else {
+                /* old-style names */
+                attr_strings.create_time_attr = NULL;
+                attr_strings.access_time_attr = "system.dos_attr.A_TIME";
+                attr_strings.write_time_attr = "system.dos_attr.M_TIME";
+                attr_strings.change_time_attr = "system.dos_attr.C_TIME";
+        }
+
+        /*
+         * Are they asking to set a DOS attribute?
+         */
+        if (StrCaseCmp(name, "system.dos_attr.*") == 0 ||
+            StrCaseCmp(name, "system.dos_attr.mode") == 0 ||
+            (attr_strings.create_time_attr != NULL &&
+             StrCaseCmp(name, attr_strings.create_time_attr) == 0) ||
+            StrCaseCmp(name, attr_strings.access_time_attr) == 0 ||
+            StrCaseCmp(name, attr_strings.write_time_attr) == 0 ||
+            StrCaseCmp(name, attr_strings.change_time_attr) == 0) {
+
+                /* get a DOS Attribute Descriptor with current attributes */
+                dad = dos_attr_query(context, ctx, path, srv);
+                if (dad) {
+                        char *namevalue =
+                                talloc_asprintf(ctx, "%s:%s",
+                                                name+16, (const char *) value);
+                        if (! namevalue) {
+                                errno = ENOMEM;
+                                ret = -1;
+                        } else {
+                                /* Overwrite old with provided new params */
+                                dos_attr_parse(context, dad, srv, namevalue);
+
+                                /* Set the new DOS attributes */
+                                ret2 = smbc_setatr(context, srv, path,
+                                                   dad->create_time,
+                                                   dad->access_time,
+                                                   dad->write_time,
+                                                   dad->change_time,
+                                                   dad->mode);
+
+                                /* ret2 has True (success) / False (failure) */
+                                if (ret2) {
+                                        ret = 0;
+                                } else {
+                                        ret = -1;
+                                }
+                        }
+                } else {
+                        ret = -1;
+                }
+
+                talloc_destroy(ctx);
+                return ret;
+        }
+
         /* Unsupported attribute name */
         talloc_destroy(ctx);
         errno = EINVAL;
         return -1;
 }
 
-int smbc_getxattr_ctx(SMBCCTX *context,
-                      const char *fname,
-                      const char *name,
-                      const void *value,
-                      size_t size)
+static int
+smbc_getxattr_ctx(SMBCCTX *context,
+                  const char *fname,
+                  const char *name,
+                  const void *value,
+                  size_t size)
 {
         int ret;
         SMBCSRV *srv;
         SMBCSRV *ipc_srv;
-        fstring server, share, user, password, workgroup;
+        fstring server;
+        fstring share;
+        fstring user;
+        fstring password;
+        fstring workgroup;
         pstring path;
         TALLOC_CTX *ctx;
         POLICY_HND pol;
+        struct {
+                const char * create_time_attr;
+                const char * access_time_attr;
+                const char * write_time_attr;
+                const char * change_time_attr;
+        } attr_strings;
+
 
         if (!context || !context->internal ||
             !context->internal->_initialized) {
@@ -3812,6 +5590,7 @@ int smbc_getxattr_ctx(SMBCCTX *context,
         DEBUG(4, ("smbc_getxattr(%s, %s)\n", fname, name));
 
         if (smbc_parse_path(context, fname,
+                            workgroup, sizeof(workgroup),
                             server, sizeof(server),
                             share, sizeof(share),
                             path, sizeof(path),
@@ -3824,18 +5603,21 @@ int smbc_getxattr_ctx(SMBCCTX *context,
 
         if (user[0] == (char)0) fstrcpy(user, context->user);
 
-        fstrcpy(workgroup, context->workgroup);
-
-        srv = smbc_server(context, server, share, workgroup, user, password);
+        srv = smbc_server(context, True,
+                          server, share, workgroup, user, password);
         if (!srv) {
                 return -1;  /* errno set by smbc_server */
         }
 
-        ipc_srv = smbc_attr_server(context, server, share,
-                                   workgroup, user, password,
-                                   &pol);
-        if (!ipc_srv) {
-                return -1;
+        if (! srv->no_nt_session) {
+                ipc_srv = smbc_attr_server(context, server, share,
+                                           workgroup, user, password,
+                                           &pol);
+                if (! ipc_srv) {
+                        srv->no_nt_session = True;
+                }
+        } else {
+                ipc_srv = NULL;
         }
         
         ctx = talloc_init("smbc:getxattr");
@@ -3844,24 +5626,56 @@ int smbc_getxattr_ctx(SMBCCTX *context,
                 return -1;
         }
 
+        /* Determine whether to use old-style or new-style attribute names */
+        if (context->internal->_full_time_names) {
+                /* new-style names */
+                attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME";
+                attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME";
+                attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME";
+                attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME";
+        } else {
+                /* old-style names */
+                attr_strings.create_time_attr = NULL;
+                attr_strings.access_time_attr = "system.dos_attr.A_TIME";
+                attr_strings.write_time_attr = "system.dos_attr.M_TIME";
+                attr_strings.change_time_attr = "system.dos_attr.C_TIME";
+        }
+
         /* Are they requesting a supported attribute? */
-        if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
+        if (StrCaseCmp(name, "system.*") == 0 ||
+            StrnCaseCmp(name, "system.*!", 9) == 0 ||
+            StrCaseCmp(name, "system.*+") == 0 ||
+            StrnCaseCmp(name, "system.*+!", 10) == 0 ||
+            StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
+            StrnCaseCmp(name, "system.nt_sec_desc.*!", 21) == 0 ||
             StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 ||
+            StrnCaseCmp(name, "system.nt_sec_desc.*+!", 22) == 0 ||
             StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
             StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
             StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 ||
             StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
             StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 ||
             StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
-            StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) {
+            StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0 ||
+            StrCaseCmp(name, "system.dos_attr.*") == 0 ||
+            StrnCaseCmp(name, "system.dos_attr.*!", 18) == 0 ||
+            StrCaseCmp(name, "system.dos_attr.mode") == 0 ||
+            StrCaseCmp(name, "system.dos_attr.size") == 0 ||
+            (attr_strings.create_time_attr != NULL &&
+             StrCaseCmp(name, attr_strings.create_time_attr) == 0) ||
+            StrCaseCmp(name, attr_strings.access_time_attr) == 0 ||
+            StrCaseCmp(name, attr_strings.write_time_attr) == 0 ||
+            StrCaseCmp(name, attr_strings.change_time_attr) == 0 ||
+            StrCaseCmp(name, "system.dos_attr.inode") == 0) {
 
                 /* Yup. */
-                ret = cacl_get(ctx, &srv->cli,
-                               &ipc_srv->cli, &pol, 
-                               (char *) path, (char *) name + 19,
-                               (char *) value, size);
+                ret = cacl_get(context, ctx, srv,
+                               ipc_srv == NULL ? NULL : ipc_srv->cli, 
+                               &pol, path,
+                               CONST_DISCARD(char *, name),
+                               CONST_DISCARD(char *, value), size);
                 if (ret < 0 && errno == 0) {
-                        errno = smbc_errno(context, &srv->cli);
+                        errno = smbc_errno(context, srv->cli);
                 }
                 talloc_destroy(ctx);
                 return ret;
@@ -3874,14 +5688,19 @@ int smbc_getxattr_ctx(SMBCCTX *context,
 }
 
 
-int smbc_removexattr_ctx(SMBCCTX *context,
-                      const char *fname,
-                      const char *name)
+static int
+smbc_removexattr_ctx(SMBCCTX *context,
+                     const char *fname,
+                     const char *name)
 {
         int ret;
         SMBCSRV *srv;
         SMBCSRV *ipc_srv;
-        fstring server, share, user, password, workgroup;
+        fstring server;
+        fstring share;
+        fstring user;
+        fstring password;
+        fstring workgroup;
         pstring path;
         TALLOC_CTX *ctx;
         POLICY_HND pol;
@@ -3904,6 +5723,7 @@ int smbc_removexattr_ctx(SMBCCTX *context,
         DEBUG(4, ("smbc_removexattr(%s, %s)\n", fname, name));
 
         if (smbc_parse_path(context, fname,
+                            workgroup, sizeof(workgroup),
                             server, sizeof(server),
                             share, sizeof(share),
                             path, sizeof(path),
@@ -3916,27 +5736,27 @@ int smbc_removexattr_ctx(SMBCCTX *context,
 
         if (user[0] == (char)0) fstrcpy(user, context->user);
 
-        fstrcpy(workgroup, context->workgroup);
-
-        srv = smbc_server(context, server, share, workgroup, user, password);
+        srv = smbc_server(context, True,
+                          server, share, workgroup, user, password);
         if (!srv) {
                 return -1;  /* errno set by smbc_server */
         }
 
-        ipc_srv = smbc_attr_server(context, server, share,
-                                   workgroup, user, password,
-                                   &pol);
-        if (!ipc_srv) {
-                return -1;
+        if (! srv->no_nt_session) {
+                ipc_srv = smbc_attr_server(context, server, share,
+                                           workgroup, user, password,
+                                           &pol);
+                if (! ipc_srv) {
+                        srv->no_nt_session = True;
+                }
+        } else {
+                ipc_srv = NULL;
         }
         
-        ipc_srv = smbc_attr_server(context, server, share,
-                                   workgroup, user, password,
-                                   &pol);
-        if (!ipc_srv) {
-                return -1;
+        if (! ipc_srv) {
+                return -1; /* errno set by smbc_attr_server */
         }
-        
+
         ctx = talloc_init("smbc_removexattr");
         if (!ctx) {
                 errno = ENOMEM;
@@ -3948,8 +5768,8 @@ int smbc_removexattr_ctx(SMBCCTX *context,
             StrCaseCmp(name, "system.nt_sec_desc.*+") == 0) {
 
                 /* Yup. */
-                ret = cacl_set(ctx, &srv->cli,
-                               &ipc_srv->cli, &pol, path,
+                ret = cacl_set(ctx, srv->cli,
+                               ipc_srv->cli, &pol, path,
                                NULL, SMBC_XATTR_MODE_REMOVE_ALL, 0);
                 talloc_destroy(ctx);
                 return ret;
@@ -3968,8 +5788,8 @@ int smbc_removexattr_ctx(SMBCCTX *context,
             StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) {
 
                 /* Yup. */
-                ret = cacl_set(ctx, &srv->cli,
-                               &ipc_srv->cli, &pol, path,
+                ret = cacl_set(ctx, srv->cli,
+                               ipc_srv->cli, &pol, path,
                                name + 19, SMBC_XATTR_MODE_REMOVE, 0);
                 talloc_destroy(ctx);
                 return ret;
@@ -3981,27 +5801,63 @@ int smbc_removexattr_ctx(SMBCCTX *context,
         return -1;
 }
 
-int smbc_listxattr_ctx(SMBCCTX *context,
-                       const char *fname,
-                       char *list,
-                       size_t size)
+static int
+smbc_listxattr_ctx(SMBCCTX *context,
+                   const char *fname,
+                   char *list,
+                   size_t size)
 {
         /*
          * This isn't quite what listxattr() is supposed to do.  This returns
-         * the complete set of attributes, always, rather than only those
+         * the complete set of attribute names, always, rather than only those
          * attribute names which actually exist for a file.  Hmmm...
          */
-        const char supported[] =
+        const char supported_old[] =
+                "system.*\0"
+                "system.*+\0"
+                "system.nt_sec_desc.revision\0"
+                "system.nt_sec_desc.owner\0"
+                "system.nt_sec_desc.owner+\0"
+                "system.nt_sec_desc.group\0"
+                "system.nt_sec_desc.group+\0"
+                "system.nt_sec_desc.acl.*\0"
+                "system.nt_sec_desc.acl\0"
+                "system.nt_sec_desc.acl+\0"
+                "system.nt_sec_desc.*\0"
+                "system.nt_sec_desc.*+\0"
+                "system.dos_attr.*\0"
+                "system.dos_attr.mode\0"
+                "system.dos_attr.c_time\0"
+                "system.dos_attr.a_time\0"
+                "system.dos_attr.m_time\0"
+                ;
+        const char supported_new[] =
+                "system.*\0"
+                "system.*+\0"
                 "system.nt_sec_desc.revision\0"
                 "system.nt_sec_desc.owner\0"
                 "system.nt_sec_desc.owner+\0"
                 "system.nt_sec_desc.group\0"
                 "system.nt_sec_desc.group+\0"
+                "system.nt_sec_desc.acl.*\0"
                 "system.nt_sec_desc.acl\0"
                 "system.nt_sec_desc.acl+\0"
                 "system.nt_sec_desc.*\0"
                 "system.nt_sec_desc.*+\0"
+                "system.dos_attr.*\0"
+                "system.dos_attr.mode\0"
+                "system.dos_attr.create_time\0"
+                "system.dos_attr.access_time\0"
+                "system.dos_attr.write_time\0"
+                "system.dos_attr.change_time\0"
                 ;
+        const char * supported;
+
+        if (context->internal->_full_time_names) {
+                supported = supported_new;
+        } else {
+                supported = supported_old;
+        }
 
         if (size == 0) {
                 return sizeof(supported);
@@ -4022,9 +5878,14 @@ int smbc_listxattr_ctx(SMBCCTX *context,
  * Open a print file to be written to by other calls
  */
 
-static SMBCFILE *smbc_open_print_job_ctx(SMBCCTX *context, const char *fname)
+static SMBCFILE *
+smbc_open_print_job_ctx(SMBCCTX *context,
+                        const char *fname)
 {
-        fstring server, share, user, password;
+        fstring server;
+        fstring share;
+        fstring user;
+        fstring password;
         pstring path;
         
         if (!context || !context->internal ||
@@ -4045,6 +5906,7 @@ static SMBCFILE *smbc_open_print_job_ctx(SMBCCTX *context, const char *fname)
         DEBUG(4, ("smbc_open_print_job_ctx(%s)\n", fname));
 
         if (smbc_parse_path(context, fname,
+                            NULL, 0,
                             server, sizeof(server),
                             share, sizeof(share),
                             path, sizeof(path),
@@ -4068,10 +5930,17 @@ static SMBCFILE *smbc_open_print_job_ctx(SMBCCTX *context, const char *fname)
  * copy it to a print file on the share specified by printq.
  */
 
-static int smbc_print_file_ctx(SMBCCTX *c_file, const char *fname, SMBCCTX *c_print, const char *printq)
+static int
+smbc_print_file_ctx(SMBCCTX *c_file,
+                    const char *fname,
+                    SMBCCTX *c_print,
+                    const char *printq)
 {
-        SMBCFILE *fid1, *fid2;
-        int bytes, saverr, tot_bytes = 0;
+        SMBCFILE *fid1;
+        SMBCFILE *fid2;
+        int bytes;
+        int saverr;
+        int tot_bytes = 0;
         char buf[4096];
 
         if (!c_file || !c_file->internal->_initialized || !c_print ||
@@ -4091,7 +5960,7 @@ static int smbc_print_file_ctx(SMBCCTX *c_file, const char *fname, SMBCCTX *c_pr
 
         /* Try to open the file for reading ... */
 
-        if ((int)(fid1 = c_file->open(c_file, fname, O_RDONLY, 0666)) < 0) {
+        if ((long)(fid1 = c_file->open(c_file, fname, O_RDONLY, 0666)) < 0) {
                 
                 DEBUG(3, ("Error, fname=%s, errno=%i\n", fname, errno));
                 return -1;  /* smbc_open sets errno */
@@ -4100,10 +5969,10 @@ static int smbc_print_file_ctx(SMBCCTX *c_file, const char *fname, SMBCCTX *c_pr
 
         /* Now, try to open the printer file for writing */
 
-        if ((int)(fid2 = c_print->open_print_job(c_print, printq)) < 0) {
+        if ((long)(fid2 = c_print->open_print_job(c_print, printq)) < 0) {
 
                 saverr = errno;  /* Save errno */
-                c_file->close(c_file, fid1);
+                c_file->close_fn(c_file, fid1);
                 errno = saverr;
                 return -1;
 
@@ -4116,8 +5985,8 @@ static int smbc_print_file_ctx(SMBCCTX *c_file, const char *fname, SMBCCTX *c_pr
                 if ((c_print->write(c_print, fid2, buf, bytes)) < 0) {
 
                         saverr = errno;
-                        c_file->close(c_file, fid1);
-                        c_print->close(c_print, fid2);
+                        c_file->close_fn(c_file, fid1);
+                        c_print->close_fn(c_print, fid2);
                         errno = saverr;
 
                 }
@@ -4126,8 +5995,8 @@ static int smbc_print_file_ctx(SMBCCTX *c_file, const char *fname, SMBCCTX *c_pr
 
         saverr = errno;
 
-        c_file->close(c_file, fid1);  /* We have to close these anyway */
-        c_print->close(c_print, fid2);
+        c_file->close_fn(c_file, fid1);  /* We have to close these anyway */
+        c_print->close_fn(c_print, fid2);
 
         if (bytes < 0) {
 
@@ -4144,10 +6013,17 @@ static int smbc_print_file_ctx(SMBCCTX *c_file, const char *fname, SMBCCTX *c_pr
  * Routine to list print jobs on a printer share ...
  */
 
-static int smbc_list_print_jobs_ctx(SMBCCTX *context, const char *fname, smbc_list_print_job_fn fn)
+static int
+smbc_list_print_jobs_ctx(SMBCCTX *context,
+                         const char *fname,
+                         smbc_list_print_job_fn fn)
 {
         SMBCSRV *srv;
-        fstring server, share, user, password, workgroup;
+        fstring server;
+        fstring share;
+        fstring user;
+        fstring password;
+        fstring workgroup;
         pstring path;
 
         if (!context || !context->internal ||
@@ -4168,6 +6044,7 @@ static int smbc_list_print_jobs_ctx(SMBCCTX *context, const char *fname, smbc_li
         DEBUG(4, ("smbc_list_print_jobs(%s)\n", fname));
 
         if (smbc_parse_path(context, fname,
+                            workgroup, sizeof(workgroup),
                             server, sizeof(server),
                             share, sizeof(share),
                             path, sizeof(path),
@@ -4180,9 +6057,8 @@ static int smbc_list_print_jobs_ctx(SMBCCTX *context, const char *fname, smbc_li
 
         if (user[0] == (char)0) fstrcpy(user, context->user);
         
-        fstrcpy(workgroup, context->workgroup);
-
-        srv = smbc_server(context, server, share, workgroup, user, password);
+        srv = smbc_server(context, True,
+                          server, share, workgroup, user, password);
 
         if (!srv) {
 
@@ -4190,9 +6066,10 @@ static int smbc_list_print_jobs_ctx(SMBCCTX *context, const char *fname, smbc_li
 
         }
 
-        if (cli_print_queue(&srv->cli, (void (*)(struct print_job_info *))fn) < 0) {
+        if (cli_print_queue(srv->cli,
+                            (void (*)(struct print_job_info *))fn) < 0) {
 
-                errno = smbc_errno(context, &srv->cli);
+                errno = smbc_errno(context, srv->cli);
                 return -1;
 
         }
@@ -4205,10 +6082,17 @@ static int smbc_list_print_jobs_ctx(SMBCCTX *context, const char *fname, smbc_li
  * Delete a print job from a remote printer share
  */
 
-static int smbc_unlink_print_job_ctx(SMBCCTX *context, const char *fname, int id)
+static int
+smbc_unlink_print_job_ctx(SMBCCTX *context,
+                          const char *fname,
+                          int id)
 {
         SMBCSRV *srv;
-        fstring server, share, user, password, workgroup;
+        fstring server;
+        fstring share;
+        fstring user;
+        fstring password;
+        fstring workgroup;
         pstring path;
         int err;
 
@@ -4230,6 +6114,7 @@ static int smbc_unlink_print_job_ctx(SMBCCTX *context, const char *fname, int id
         DEBUG(4, ("smbc_unlink_print_job(%s)\n", fname));
 
         if (smbc_parse_path(context, fname,
+                            workgroup, sizeof(workgroup),
                             server, sizeof(server),
                             share, sizeof(share),
                             path, sizeof(path),
@@ -4242,9 +6127,8 @@ static int smbc_unlink_print_job_ctx(SMBCCTX *context, const char *fname, int id
 
         if (user[0] == (char)0) fstrcpy(user, context->user);
 
-        fstrcpy(workgroup, context->workgroup);
-
-        srv = smbc_server(context, server, share, workgroup, user, password);
+        srv = smbc_server(context, True,
+                          server, share, workgroup, user, password);
 
         if (!srv) {
 
@@ -4252,10 +6136,10 @@ static int smbc_unlink_print_job_ctx(SMBCCTX *context, const char *fname, int id
 
         }
 
-        if ((err = cli_printjob_del(&srv->cli, id)) != 0) {
+        if ((err = cli_printjob_del(srv->cli, id)) != 0) {
 
                 if (err < 0)
-                        errno = smbc_errno(context, &srv->cli);
+                        errno = smbc_errno(context, srv->cli);
                 else if (err == ERRnosuchprintjob)
                         errno = EINVAL;
                 return -1;
@@ -4269,11 +6153,12 @@ static int smbc_unlink_print_job_ctx(SMBCCTX *context, const char *fname, int id
 /*
  * Get a new empty handle to fill in with your own info 
  */
-SMBCCTX * smbc_new_context(void)
+SMBCCTX *
+smbc_new_context(void)
 {
-        SMBCCTX * context;
+        SMBCCTX *context;
 
-        context = malloc(sizeof(SMBCCTX));
+        context = SMB_MALLOC_P(SMBCCTX);
         if (!context) {
                 errno = ENOMEM;
                 return NULL;
@@ -4281,8 +6166,9 @@ SMBCCTX * smbc_new_context(void)
 
         ZERO_STRUCTP(context);
 
-        context->internal = malloc(sizeof(struct smbc_internal_data));
+        context->internal = SMB_MALLOC_P(struct smbc_internal_data);
         if (!context->internal) {
+               SAFE_FREE(context);
                 errno = ENOMEM;
                 return NULL;
         }
@@ -4294,37 +6180,43 @@ SMBCCTX * smbc_new_context(void)
         context->debug            = 0;
         context->timeout          = 20000; /* 20 seconds */
 
-        context->open             = smbc_open_ctx;
-        context->creat            = smbc_creat_ctx;
-        context->read             = smbc_read_ctx;
-        context->write            = smbc_write_ctx;
-        context->close            = smbc_close_ctx;
-        context->unlink           = smbc_unlink_ctx;
-        context->rename           = smbc_rename_ctx;
-        context->lseek            = smbc_lseek_ctx;
-        context->stat             = smbc_stat_ctx;
-        context->fstat            = smbc_fstat_ctx;
-        context->opendir          = smbc_opendir_ctx;
-        context->closedir         = smbc_closedir_ctx;
-        context->readdir          = smbc_readdir_ctx;
-        context->getdents         = smbc_getdents_ctx;
-        context->mkdir            = smbc_mkdir_ctx;
-        context->rmdir            = smbc_rmdir_ctx;
-        context->telldir          = smbc_telldir_ctx;
-        context->lseekdir         = smbc_lseekdir_ctx;
-        context->fstatdir         = smbc_fstatdir_ctx;
-        context->chmod            = smbc_chmod_ctx;
-        context->utimes           = smbc_utimes_ctx;
-        context->setxattr         = smbc_setxattr_ctx;
-        context->getxattr         = smbc_getxattr_ctx;
-        context->removexattr      = smbc_removexattr_ctx;
-        context->listxattr        = smbc_listxattr_ctx;
-        context->open_print_job   = smbc_open_print_job_ctx;
-        context->print_file       = smbc_print_file_ctx;
-        context->list_print_jobs  = smbc_list_print_jobs_ctx;
-        context->unlink_print_job = smbc_unlink_print_job_ctx;
-
-        context->callbacks.check_server_fn      = smbc_check_server;
+       context->options.browse_max_lmb_count      = 3;    /* # LMBs to query */
+       context->options.urlencode_readdir_entries = False;/* backward compat */
+       context->options.one_share_per_server      = False;/* backward compat */
+        context->internal->_share_mode             = SMBC_SHAREMODE_DENY_NONE;
+                                /* backward compat */
+
+        context->open                              = smbc_open_ctx;
+        context->creat                             = smbc_creat_ctx;
+        context->read                              = smbc_read_ctx;
+        context->write                             = smbc_write_ctx;
+        context->close_fn                          = smbc_close_ctx;
+        context->unlink                            = smbc_unlink_ctx;
+        context->rename                            = smbc_rename_ctx;
+        context->lseek                             = smbc_lseek_ctx;
+        context->stat                              = smbc_stat_ctx;
+        context->fstat                             = smbc_fstat_ctx;
+        context->opendir                           = smbc_opendir_ctx;
+        context->closedir                          = smbc_closedir_ctx;
+        context->readdir                           = smbc_readdir_ctx;
+        context->getdents                          = smbc_getdents_ctx;
+        context->mkdir                             = smbc_mkdir_ctx;
+        context->rmdir                             = smbc_rmdir_ctx;
+        context->telldir                           = smbc_telldir_ctx;
+        context->lseekdir                          = smbc_lseekdir_ctx;
+        context->fstatdir                          = smbc_fstatdir_ctx;
+        context->chmod                             = smbc_chmod_ctx;
+        context->utimes                            = smbc_utimes_ctx;
+        context->setxattr                          = smbc_setxattr_ctx;
+        context->getxattr                          = smbc_getxattr_ctx;
+        context->removexattr                       = smbc_removexattr_ctx;
+        context->listxattr                         = smbc_listxattr_ctx;
+        context->open_print_job                    = smbc_open_print_job_ctx;
+        context->print_file                        = smbc_print_file_ctx;
+        context->list_print_jobs                   = smbc_list_print_jobs_ctx;
+        context->unlink_print_job                  = smbc_unlink_print_job_ctx;
+
+        context->callbacks.check_server_fn         = smbc_check_server;
         context->callbacks.remove_unused_server_fn = smbc_remove_unused_server;
 
         smbc_default_cache_functions(context);
@@ -4339,7 +6231,9 @@ SMBCCTX * smbc_new_context(void)
  * and thus you'll be leaking memory if not handled properly.
  *
  */
-int smbc_free_context(SMBCCTX * context, int shutdown_ctx)
+int
+smbc_free_context(SMBCCTX *context,
+                  int shutdown_ctx)
 {
         if (!context) {
                 errno = EBADF;
@@ -4352,7 +6246,7 @@ int smbc_free_context(SMBCCTX * context, int shutdown_ctx)
                 
                 f = context->internal->_files;
                 while (f) {
-                        context->close(context, f);
+                        context->close_fn(context, f);
                         f = f->next;
                 }
                 context->internal->_files = NULL;
@@ -4361,12 +6255,15 @@ int smbc_free_context(SMBCCTX * context, int shutdown_ctx)
                 if (context->callbacks.purge_cached_fn(context)) {
                         SMBCSRV * s;
                         SMBCSRV * next;
-                        DEBUG(1, ("Could not purge all servers, Nice way shutdown failed.\n"));
+                        DEBUG(1, ("Could not purge all servers, "
+                                  "Nice way shutdown failed.\n"));
                         s = context->internal->_servers;
                         while (s) {
-                                DEBUG(1, ("Forced shutdown: %p (fd=%d)\n", s, s->cli.fd));
-                                cli_shutdown(&s->cli);
-                                context->callbacks.remove_cached_srv_fn(context, s);
+                                DEBUG(1, ("Forced shutdown: %p (fd=%d)\n",
+                                          s, s->cli->fd));
+                                cli_shutdown(s->cli);
+                                context->callbacks.remove_cached_srv_fn(context,
+                                                                        s);
                                 next = s->next;
                                 DLIST_REMOVE(context->internal->_servers, s);
                                 SAFE_FREE(s);
@@ -4378,17 +6275,20 @@ int smbc_free_context(SMBCCTX * context, int shutdown_ctx)
         else {
                 /* This is the polite way */    
                 if (context->callbacks.purge_cached_fn(context)) {
-                        DEBUG(1, ("Could not purge all servers, free_context failed.\n"));
+                        DEBUG(1, ("Could not purge all servers, "
+                                  "free_context failed.\n"));
                         errno = EBUSY;
                         return 1;
                 }
                 if (context->internal->_servers) {
-                        DEBUG(1, ("Active servers in context, free_context failed.\n"));
+                        DEBUG(1, ("Active servers in context, "
+                                  "free_context failed.\n"));
                         errno = EBUSY;
                         return 1;
                 }
                 if (context->internal->_files) {
-                        DEBUG(1, ("Active files in context, free_context failed.\n"));
+                        DEBUG(1, ("Active files in context, "
+                                  "free_context failed.\n"));
                         errno = EBUSY;
                         return 1;
                 }               
@@ -4406,6 +6306,127 @@ int smbc_free_context(SMBCCTX * context, int shutdown_ctx)
 }
 
 
+/*
+ * Each time the context structure is changed, we have binary backward
+ * compatibility issues.  Instead of modifying the public portions of the
+ * context structure to add new options, instead, we put them in the internal
+ * portion of the context structure and provide a set function for these new
+ * options.
+ */
+void
+smbc_option_set(SMBCCTX *context,
+                char *option_name,
+                ... /* option_value */)
+{
+        va_list ap;
+        union {
+                int i;
+                BOOL b;
+                smbc_get_auth_data_with_context_fn auth_fn;
+                void *v;
+        } option_value;
+
+        va_start(ap, option_name);
+
+        if (strcmp(option_name, "debug_to_stderr") == 0) {
+                /*
+                 * Log to standard error instead of standard output.
+                 */
+                option_value.b = (BOOL) va_arg(ap, int);
+                context->internal->_debug_stderr = option_value.b;
+
+        } else if (strcmp(option_name, "full_time_names") == 0) {
+                /*
+                 * Use new-style time attribute names, e.g. WRITE_TIME rather
+                 * than the old-style names such as M_TIME.  This allows also
+                 * setting/getting CREATE_TIME which was previously
+                 * unimplemented.  (Note that the old C_TIME was supposed to
+                 * be CHANGE_TIME but was confused and sometimes referred to
+                 * CREATE_TIME.)
+                 */
+                option_value.b = (BOOL) va_arg(ap, int);
+                context->internal->_full_time_names = option_value.b;
+
+        } else if (strcmp(option_name, "open_share_mode") == 0) {
+                /*
+                 * The share mode to use for files opened with
+                 * smbc_open_ctx().  The default is SMBC_SHAREMODE_DENY_NONE.
+                 */
+                option_value.i = va_arg(ap, int);
+                context->internal->_share_mode =
+                        (smbc_share_mode) option_value.i;
+
+        } else if (strcmp(option_name, "auth_function") == 0) {
+                /*
+                 * Use the new-style authentication function which includes
+                 * the context.
+                 */
+                option_value.auth_fn =
+                        va_arg(ap, smbc_get_auth_data_with_context_fn);
+                context->internal->_auth_fn_with_context =
+                        option_value.auth_fn;
+        } else if (strcmp(option_name, "user_data") == 0) {
+                /*
+                 * Save a user data handle which may be retrieved by the user
+                 * with smbc_option_get()
+                 */
+                option_value.v = va_arg(ap, void *);
+                context->internal->_user_data = option_value.v;
+        }
+
+        va_end(ap);
+}
+
+
+/*
+ * Retrieve the current value of an option
+ */
+void *
+smbc_option_get(SMBCCTX *context,
+                char *option_name)
+{
+        if (strcmp(option_name, "debug_stderr") == 0) {
+                /*
+                 * Log to standard error instead of standard output.
+                 */
+#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
+               return (void *) (intptr_t) context->internal->_debug_stderr;
+#else
+               return (void *) context->internal->_debug_stderr;
+#endif
+        } else if (strcmp(option_name, "full_time_names") == 0) {
+                /*
+                 * Use new-style time attribute names, e.g. WRITE_TIME rather
+                 * than the old-style names such as M_TIME.  This allows also
+                 * setting/getting CREATE_TIME which was previously
+                 * unimplemented.  (Note that the old C_TIME was supposed to
+                 * be CHANGE_TIME but was confused and sometimes referred to
+                 * CREATE_TIME.)
+                 */
+#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
+               return (void *) (intptr_t) context->internal->_full_time_names;
+#else
+               return (void *) context->internal->_full_time_names;
+#endif
+
+        } else if (strcmp(option_name, "auth_function") == 0) {
+                /*
+                 * Use the new-style authentication function which includes
+                 * the context.
+                 */
+                return (void *) context->internal->_auth_fn_with_context;
+        } else if (strcmp(option_name, "user_data") == 0) {
+                /*
+                 * Save a user data handle which may be retrieved by the user
+                 * with smbc_option_get()
+                 */
+                return context->internal->_user_data;
+        }
+
+        return NULL;
+}
+
+
 /*
  * Initialise the library etc 
  *
@@ -4413,11 +6434,13 @@ int smbc_free_context(SMBCCTX * context, int shutdown_ctx)
  * valid values for info->debug from 0 to 100,
  * and insist that info->fn must be non-null.
  */
-SMBCCTX * smbc_init_context(SMBCCTX * context)
+SMBCCTX *
+smbc_init_context(SMBCCTX *context)
 {
         pstring conf;
         int pid;
-        char *user = NULL, *home = NULL;
+        char *user = NULL;
+        char *home = NULL;
 
         if (!context || !context->internal) {
                 errno = EBADF;
@@ -4429,7 +6452,10 @@ SMBCCTX * smbc_init_context(SMBCCTX * context)
                 return 0;
         }
 
-        if (!context->callbacks.auth_fn || context->debug < 0 || context->debug > 100) {
+        if ((!context->callbacks.auth_fn &&
+             !context->internal->_auth_fn_with_context) ||
+            context->debug < 0 ||
+            context->debug > 100) {
 
                 errno = EINVAL;
                 return NULL;
@@ -4437,25 +6463,39 @@ SMBCCTX * smbc_init_context(SMBCCTX * context)
         }
 
         if (!smbc_initialized) {
-                /* Do some library wide intialisations the first time we get called */
+                /*
+                 * Do some library-wide intializations the first time we get
+                 * called
+                 */
+               BOOL conf_loaded = False;
 
                 /* Set this to what the user wants */
                 DEBUGLEVEL = context->debug;
                 
-                setup_logging( "libsmbclient", True);
+                load_case_tables();
 
-                /* Here we would open the smb.conf file if needed ... */
-                
-                home = getenv("HOME");
+                setup_logging("libsmbclient", True);
+                if (context->internal->_debug_stderr) {
+                        dbf = x_stderr;
+                        x_setbuf(x_stderr, NULL);
+                }
 
-                slprintf(conf, sizeof(conf), "%s/.smb/smb.conf", home);
-                
-                load_interfaces();  /* Load the list of interfaces ... */
+                /* Here we would open the smb.conf file if needed ... */
                 
                 in_client = True; /* FIXME, make a param */
 
-                if (!lp_load(conf, True, False, False)) {
-
+                home = getenv("HOME");
+               if (home) {
+                       slprintf(conf, sizeof(conf), "%s/.smb/smb.conf", home);
+                       if (lp_load(conf, True, False, False, True)) {
+                               conf_loaded = True;
+                       } else {
+                                DEBUG(5, ("Could not load config file: %s\n",
+                                          conf));
+                       }
+                       }
+               if (!conf_loaded) {
                         /*
                          * Well, if that failed, try the dyn_CONFIGFILE
                          * Which points to the standard locn, and if that
@@ -4463,12 +6503,28 @@ SMBCCTX * smbc_init_context(SMBCCTX * context)
                          * defaults ...
                          */
 
-                   if (!lp_load(dyn_CONFIGFILE, True, False, False)) {
-                      DEBUG(5, ("Could not load either config file: %s or %s\n",
-                             conf, dyn_CONFIGFILE));
-                   }
+                        if (!lp_load(dyn_CONFIGFILE, True, False, False, False)) {
+                                DEBUG(5, ("Could not load config file: %s\n",
+                                          dyn_CONFIGFILE));
+                        } else if (home) {
+                                /*
+                                 * We loaded the global config file.  Now lets
+                                 * load user-specific modifications to the
+                                 * global config.
+                                 */
+                                slprintf(conf, sizeof(conf),
+                                         "%s/.smb/smb.conf.append", home);
+                                if (!lp_load(conf, True, False, False, False)) {
+                                        DEBUG(10,
+                                              ("Could not append config file: "
+                                               "%s\n",
+                                               conf));
+                                }
+                        }
                 }
 
+                load_interfaces();  /* Load the list of interfaces ... */
+                
                 reopen_logs();  /* Get logging working ... */
         
                 /* 
@@ -4488,29 +6544,32 @@ SMBCCTX * smbc_init_context(SMBCCTX * context)
                  */
                 user = getenv("USER");
                 /* walk around as "guest" if no username can be found */
-                if (!user) context->user = strdup("guest");
-                else context->user = strdup(user);
+                if (!user) context->user = SMB_STRDUP("guest");
+                else context->user = SMB_STRDUP(user);
         }
 
         if (!context->netbios_name) {
                 /*
-                 * We try to get our netbios name from the config. If that fails we fall
-                 * back on constructing our netbios name from our hostname etc
+                 * We try to get our netbios name from the config. If that
+                 * fails we fall back on constructing our netbios name from
+                 * our hostname etc
                  */
                 if (global_myname()) {
-                        context->netbios_name = strdup(global_myname());
+                        context->netbios_name = SMB_STRDUP(global_myname());
                 }
                 else {
                         /*
-                         * Hmmm, I want to get hostname as well, but I am too lazy for the moment
+                         * Hmmm, I want to get hostname as well, but I am too
+                         * lazy for the moment
                          */
                         pid = sys_getpid();
-                        context->netbios_name = malloc(17);
+                        context->netbios_name = (char *)SMB_MALLOC(17);
                         if (!context->netbios_name) {
                                 errno = ENOMEM;
                                 return NULL;
                         }
-                        slprintf(context->netbios_name, 16, "smbc%s%d", context->user, pid);
+                        slprintf(context->netbios_name, 16,
+                                 "smbc%s%d", context->user, pid);
                 }
         }
 
@@ -4518,11 +6577,11 @@ SMBCCTX * smbc_init_context(SMBCCTX * context)
 
         if (!context->workgroup) {
                 if (lp_workgroup()) {
-                        context->workgroup = strdup(lp_workgroup());
+                        context->workgroup = SMB_STRDUP(lp_workgroup());
                 }
                 else {
                         /* TODO: Think about a decent default workgroup */
-                        context->workgroup = strdup("samba");
+                        context->workgroup = SMB_STRDUP("samba");
                 }
         }
 
@@ -4536,7 +6595,15 @@ SMBCCTX * smbc_init_context(SMBCCTX * context)
          * FIXME: Should we check the function pointers here? 
          */
 
-        context->internal->_initialized = 1;
+        context->internal->_initialized = True;
         
         return context;
 }
+
+
+/* Return the verion of samba, and thus libsmbclient */
+const char *
+smbc_version(void)
+{
+        return samba_version_string();
+}