r21132: - Fixes bug 4366. Documentation for smbc_utimes() was incorrect.
[bbaumbach/samba-autobuild/.git] / source3 / libsmb / libsmbclient.c
1 /* 
2    Unix SMB/Netbios implementation.
3    SMB client library implementation
4    Copyright (C) Andrew Tridgell 1998
5    Copyright (C) Richard Sharpe 2000, 2002
6    Copyright (C) John Terpstra 2000
7    Copyright (C) Tom Jansen (Ninja ISD) 2002 
8    Copyright (C) Derrell Lipman 2003, 2004
9    
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 #include "includes.h"
26
27 #include "include/libsmb_internal.h"
28
29 struct smbc_dirent *smbc_readdir_ctx(SMBCCTX *context, SMBCFILE *dir);
30 struct smbc_dir_list *smbc_check_dir_ent(struct smbc_dir_list *list, 
31                                          struct smbc_dirent *dirent);
32
33 /*
34  * DOS Attribute values (used internally)
35  */
36 typedef struct DOS_ATTR_DESC {
37         int mode;
38         SMB_OFF_T size;
39         time_t create_time;
40         time_t access_time;
41         time_t write_time;
42         time_t change_time;
43         SMB_INO_T inode;
44 } DOS_ATTR_DESC;
45
46
47 /*
48  * Internal flags for extended attributes
49  */
50
51 /* internal mode values */
52 #define SMBC_XATTR_MODE_ADD          1
53 #define SMBC_XATTR_MODE_REMOVE       2
54 #define SMBC_XATTR_MODE_REMOVE_ALL   3
55 #define SMBC_XATTR_MODE_SET          4
56 #define SMBC_XATTR_MODE_CHOWN        5
57 #define SMBC_XATTR_MODE_CHGRP        6
58
59 #define CREATE_ACCESS_READ      READ_CONTROL_ACCESS
60
61 /*We should test for this in configure ... */
62 #ifndef ENOTSUP
63 #define ENOTSUP EOPNOTSUPP
64 #endif
65
66 /*
67  * Functions exported by libsmb_cache.c that we need here
68  */
69 int smbc_default_cache_functions(SMBCCTX *context);
70
71 /* 
72  * check if an element is part of the list. 
73  * FIXME: Does not belong here !  
74  * Can anyone put this in a macro in dlinklist.h ?
75  * -- Tom
76  */
77 static int DLIST_CONTAINS(SMBCFILE * list, SMBCFILE *p) {
78         if (!p || !list) return False;
79         do {
80                 if (p == list) return True;
81                 list = list->next;
82         } while (list);
83         return False;
84 }
85
86 /*
87  * Find an lsa pipe handle associated with a cli struct.
88  */
89 static struct rpc_pipe_client *
90 find_lsa_pipe_hnd(struct cli_state *ipc_cli)
91 {
92         struct rpc_pipe_client *pipe_hnd;
93
94         for (pipe_hnd = ipc_cli->pipe_list;
95              pipe_hnd;
96              pipe_hnd = pipe_hnd->next) {
97             
98                 if (pipe_hnd->pipe_idx == PI_LSARPC) {
99                         return pipe_hnd;
100                 }
101         }
102
103         return NULL;
104 }
105
106 static int
107 smbc_close_ctx(SMBCCTX *context,
108                SMBCFILE *file);
109 static off_t
110 smbc_lseek_ctx(SMBCCTX *context,
111                SMBCFILE *file,
112                off_t offset,
113                int whence);
114
115 extern BOOL in_client;
116
117 /*
118  * Is the logging working / configfile read ? 
119  */
120 static int smbc_initialized = 0;
121
122 static int 
123 hex2int( unsigned int _char )
124 {
125     if ( _char >= 'A' && _char <='F')
126         return _char - 'A' + 10;
127     if ( _char >= 'a' && _char <='f')
128         return _char - 'a' + 10;
129     if ( _char >= '0' && _char <='9')
130         return _char - '0';
131     return -1;
132 }
133
134 /*
135  * smbc_urldecode()
136  *
137  * Convert strings of %xx to their single character equivalent.  Each 'x' must
138  * be a valid hexadecimal digit, or that % sequence is left undecoded.
139  *
140  * dest may, but need not be, the same pointer as src.
141  *
142  * Returns the number of % sequences which could not be converted due to lack
143  * of two following hexadecimal digits.
144  */
145 int
146 smbc_urldecode(char *dest, char * src, size_t max_dest_len)
147 {
148         int old_length = strlen(src);
149         int i = 0;
150         int err_count = 0;
151         pstring temp;
152         char * p;
153
154         if ( old_length == 0 ) {
155                 return 0;
156         }
157
158         p = temp;
159         while ( i < old_length ) {
160                 unsigned char character = src[ i++ ];
161
162                 if (character == '%') {
163                         int a = i+1 < old_length ? hex2int( src[i] ) : -1;
164                         int b = i+1 < old_length ? hex2int( src[i+1] ) : -1;
165
166                         /* Replace valid sequence */
167                         if (a != -1 && b != -1) {
168
169                                 /* Replace valid %xx sequence with %dd */
170                                 character = (a * 16) + b;
171
172                                 if (character == '\0') {
173                                         break; /* Stop at %00 */
174                                 }
175
176                                 i += 2;
177                         } else {
178
179                                 err_count++;
180                         }
181                 }
182
183                 *p++ = character;
184         }
185
186         *p = '\0';
187
188         strncpy(dest, temp, max_dest_len - 1);
189         dest[max_dest_len - 1] = '\0';
190
191         return err_count;
192 }
193
194 /*
195  * smbc_urlencode()
196  *
197  * Convert any characters not specifically allowed in a URL into their %xx
198  * equivalent.
199  *
200  * Returns the remaining buffer length.
201  */
202 int
203 smbc_urlencode(char * dest, char * src, int max_dest_len)
204 {
205         char hex[] = "0123456789ABCDEF";
206
207         for (; *src != '\0' && max_dest_len >= 3; src++) {
208
209                 if ((*src < '0' &&
210                      *src != '-' &&
211                      *src != '.') ||
212                     (*src > '9' &&
213                      *src < 'A') ||
214                     (*src > 'Z' &&
215                      *src < 'a' &&
216                      *src != '_') ||
217                     (*src > 'z')) {
218                         *dest++ = '%';
219                         *dest++ = hex[(*src >> 4) & 0x0f];
220                         *dest++ = hex[*src & 0x0f];
221                         max_dest_len -= 3;
222                 } else {
223                         *dest++ = *src;
224                         max_dest_len--;
225                 }
226         }
227
228         *dest++ = '\0';
229         max_dest_len--;
230         
231         return max_dest_len;
232 }
233
234 /*
235  * Function to parse a path and turn it into components
236  *
237  * The general format of an SMB URI is explain in Christopher Hertel's CIFS
238  * book, at http://ubiqx.org/cifs/Appendix-D.html.  We accept a subset of the
239  * general format ("smb:" only; we do not look for "cifs:").
240  *
241  *
242  * We accept:
243  *  smb://[[[domain;]user[:password]@]server[/share[/path[/file]]]][?options]
244  *
245  * Meaning of URLs:
246  *
247  * smb://           Show all workgroups.
248  *
249  *                  The method of locating the list of workgroups varies
250  *                  depending upon the setting of the context variable
251  *                  context->options.browse_max_lmb_count.  This value
252  *                  determine the maximum number of local master browsers to
253  *                  query for the list of workgroups.  In order to ensure that
254  *                  a complete list of workgroups is obtained, all master
255  *                  browsers must be queried, but if there are many
256  *                  workgroups, the time spent querying can begin to add up.
257  *                  For small networks (not many workgroups), it is suggested
258  *                  that this variable be set to 0, indicating query all local
259  *                  master browsers.  When the network has many workgroups, a
260  *                  reasonable setting for this variable might be around 3.
261  *
262  * smb://name/      if name<1D> or name<1B> exists, list servers in
263  *                  workgroup, else, if name<20> exists, list all shares
264  *                  for server ...
265  *
266  * If "options" are provided, this function returns the entire option list as a
267  * string, for later parsing by the caller.  Note that currently, no options
268  * are supported.
269  */
270
271 static const char *smbc_prefix = "smb:";
272
273 static int
274 smbc_parse_path(SMBCCTX *context,
275                 const char *fname,
276                 char *workgroup, int workgroup_len,
277                 char *server, int server_len,
278                 char *share, int share_len,
279                 char *path, int path_len,
280                 char *user, int user_len,
281                 char *password, int password_len,
282                 char *options, int options_len)
283 {
284         static pstring s;
285         pstring userinfo;
286         const char *p;
287         char *q, *r;
288         int len;
289
290         server[0] = share[0] = path[0] = user[0] = password[0] = (char)0;
291
292         /*
293          * Assume we wont find an authentication domain to parse, so default
294          * to the workgroup in the provided context.
295          */
296         if (workgroup != NULL) {
297                 strncpy(workgroup, context->workgroup, workgroup_len - 1);
298                 workgroup[workgroup_len - 1] = '\0';
299         }
300
301         if (options != NULL && options_len > 0) {
302                 options[0] = (char)0;
303         }
304         pstrcpy(s, fname);
305
306         /* see if it has the right prefix */
307         len = strlen(smbc_prefix);
308         if (strncmp(s,smbc_prefix,len) || (s[len] != '/' && s[len] != 0)) {
309                 return -1; /* What about no smb: ? */
310         }
311
312         p = s + len;
313
314         /* Watch the test below, we are testing to see if we should exit */
315
316         if (strncmp(p, "//", 2) && strncmp(p, "\\\\", 2)) {
317
318                 DEBUG(1, ("Invalid path (does not begin with smb://"));
319                 return -1;
320
321         }
322
323         p += 2;  /* Skip the double slash */
324
325         /* See if any options were specified */
326         if ((q = strrchr(p, '?')) != NULL ) {
327                 /* There are options.  Null terminate here and point to them */
328                 *q++ = '\0';
329                 
330                 DEBUG(4, ("Found options '%s'", q));
331
332                 /* Copy the options */
333                 if (options != NULL && options_len > 0) {
334                         safe_strcpy(options, q, options_len - 1);
335                 }
336         }
337
338         if (*p == (char)0)
339             goto decoding;
340
341         if (*p == '/') {
342
343                 strncpy(server, context->workgroup, 
344                         ((strlen(context->workgroup) < 16)
345                          ? strlen(context->workgroup)
346                          : 16));
347                 server[server_len - 1] = '\0';
348                 return 0;
349                 
350         }
351
352         /*
353          * ok, its for us. Now parse out the server, share etc. 
354          *
355          * However, we want to parse out [[domain;]user[:password]@] if it
356          * exists ...
357          */
358
359         /* check that '@' occurs before '/', if '/' exists at all */
360         q = strchr_m(p, '@');
361         r = strchr_m(p, '/');
362         if (q && (!r || q < r)) {
363                 pstring username, passwd, domain;
364                 const char *u = userinfo;
365
366                 next_token_no_ltrim(&p, userinfo, "@", sizeof(fstring));
367
368                 username[0] = passwd[0] = domain[0] = 0;
369
370                 if (strchr_m(u, ';')) {
371       
372                         next_token_no_ltrim(&u, domain, ";", sizeof(fstring));
373
374                 }
375
376                 if (strchr_m(u, ':')) {
377
378                         next_token_no_ltrim(&u, username, ":", sizeof(fstring));
379
380                         pstrcpy(passwd, u);
381
382                 }
383                 else {
384
385                         pstrcpy(username, u);
386
387                 }
388
389                 if (domain[0] && workgroup) {
390                         strncpy(workgroup, domain, workgroup_len - 1);
391                         workgroup[workgroup_len - 1] = '\0';
392                 }
393
394                 if (username[0]) {
395                         strncpy(user, username, user_len - 1);
396                         user[user_len - 1] = '\0';
397                 }
398
399                 if (passwd[0]) {
400                         strncpy(password, passwd, password_len - 1);
401                         password[password_len - 1] = '\0';
402                 }
403
404         }
405
406         if (!next_token(&p, server, "/", sizeof(fstring))) {
407
408                 return -1;
409
410         }
411
412         if (*p == (char)0) goto decoding;  /* That's it ... */
413   
414         if (!next_token(&p, share, "/", sizeof(fstring))) {
415
416                 return -1;
417
418         }
419
420         /*
421          * Prepend a leading slash if there's a file path, as required by
422          * NetApp filers.
423          */
424         *path = '\0';
425         if (*p != '\0') {
426                 *path = '/';
427                 safe_strcpy(path + 1, p, path_len - 2);
428         }
429
430         all_string_sub(path, "/", "\\", 0);
431
432  decoding:
433         (void) smbc_urldecode(path, path, path_len);
434         (void) smbc_urldecode(server, server, server_len);
435         (void) smbc_urldecode(share, share, share_len);
436         (void) smbc_urldecode(user, user, user_len);
437         (void) smbc_urldecode(password, password, password_len);
438
439         return 0;
440 }
441
442 /*
443  * Verify that the options specified in a URL are valid
444  */
445 static int
446 smbc_check_options(char *server,
447                    char *share,
448                    char *path,
449                    char *options)
450 {
451         DEBUG(4, ("smbc_check_options(): server='%s' share='%s' "
452                   "path='%s' options='%s'\n",
453                   server, share, path, options));
454
455         /* No options at all is always ok */
456         if (! *options) return 0;
457
458         /* Currently, we don't support any options. */
459         return -1;
460 }
461
462 /*
463  * Convert an SMB error into a UNIX error ...
464  */
465 static int
466 smbc_errno(SMBCCTX *context,
467            struct cli_state *c)
468 {
469         int ret = cli_errno(c);
470         
471         if (cli_is_dos_error(c)) {
472                 uint8 eclass;
473                 uint32 ecode;
474
475                 cli_dos_error(c, &eclass, &ecode);
476                 
477                 DEBUG(3,("smbc_error %d %d (0x%x) -> %d\n", 
478                          (int)eclass, (int)ecode, (int)ecode, ret));
479         } else {
480                 NTSTATUS status;
481
482                 status = cli_nt_error(c);
483
484                 DEBUG(3,("smbc errno %s -> %d\n",
485                          nt_errstr(status), ret));
486         }
487
488         return ret;
489 }
490
491 /* 
492  * Check a server for being alive and well.
493  * returns 0 if the server is in shape. Returns 1 on error 
494  * 
495  * Also useable outside libsmbclient to enable external cache
496  * to do some checks too.
497  */
498 static int
499 smbc_check_server(SMBCCTX * context,
500                   SMBCSRV * server) 
501 {
502         int size;
503         struct sockaddr addr;
504
505         /*
506          * Although the use of port 139 is not a guarantee that we're using
507          * netbios, we assume so.  We don't want to send a keepalive packet if
508          * not netbios because it's not valid, and Vista, at least,
509          * disconnects the client on such a request.
510          */
511         if (server->cli->port == 139) {
512                 /* Assuming netbios.  Send a keepalive packet */
513                 if ( send_keepalive(server->cli->fd) == False ) {
514                         return 1;
515                 }
516         } else {
517                 /*
518                  * Assuming not netbios.  Try a different method to detect if
519                  * the connection is still alive.
520                  */
521                 size = sizeof(addr);
522                 if (getpeername(server->cli->fd, &addr, &size) == -1) {
523                         return 1;
524                 }
525         }
526
527         /* connection is ok */
528         return 0;
529 }
530
531 /* 
532  * Remove a server from the cached server list it's unused.
533  * On success, 0 is returned. 1 is returned if the server could not be removed.
534  * 
535  * Also useable outside libsmbclient
536  */
537 int
538 smbc_remove_unused_server(SMBCCTX * context,
539                           SMBCSRV * srv)
540 {
541         SMBCFILE * file;
542
543         /* are we being fooled ? */
544         if (!context || !context->internal ||
545             !context->internal->_initialized || !srv) return 1;
546
547         
548         /* Check all open files/directories for a relation with this server */
549         for (file = context->internal->_files; file; file=file->next) {
550                 if (file->srv == srv) {
551                         /* Still used */
552                         DEBUG(3, ("smbc_remove_usused_server: "
553                                   "%p still used by %p.\n", 
554                                   srv, file));
555                         return 1;
556                 }
557         }
558
559         DLIST_REMOVE(context->internal->_servers, srv);
560
561         cli_shutdown(srv->cli);
562         srv->cli = NULL;
563
564         DEBUG(3, ("smbc_remove_usused_server: %p removed.\n", srv));
565
566         context->callbacks.remove_cached_srv_fn(context, srv);
567
568         SAFE_FREE(srv);
569         
570         return 0;
571 }
572
573 static SMBCSRV *
574 find_server(SMBCCTX *context,
575             const char *server,
576             const char *share,
577             fstring workgroup,
578             fstring username,
579             fstring password)
580 {
581         SMBCSRV *srv;
582         int auth_called = 0;
583         
584  check_server_cache:
585
586         srv = context->callbacks.get_cached_srv_fn(context, server, share, 
587                                                    workgroup, username);
588
589         if (!auth_called && !srv && (!username[0] || !password[0])) {
590                 if (context->internal->_auth_fn_with_context != NULL) {
591                          context->internal->_auth_fn_with_context(
592                                 context,
593                                 server, share,
594                                 workgroup, sizeof(fstring),
595                                 username, sizeof(fstring),
596                                 password, sizeof(fstring));
597                 } else {
598                         context->callbacks.auth_fn(
599                                 server, share,
600                                 workgroup, sizeof(fstring),
601                                 username, sizeof(fstring),
602                                 password, sizeof(fstring));
603                 }
604
605                 /*
606                  * However, smbc_auth_fn may have picked up info relating to
607                  * an existing connection, so try for an existing connection
608                  * again ...
609                  */
610                 auth_called = 1;
611                 goto check_server_cache;
612                 
613         }
614         
615         if (srv) {
616                 if (context->callbacks.check_server_fn(context, srv)) {
617                         /*
618                          * This server is no good anymore 
619                          * Try to remove it and check for more possible
620                          * servers in the cache
621                          */
622                         if (context->callbacks.remove_unused_server_fn(context,
623                                                                        srv)) { 
624                                 /*
625                                  * We could not remove the server completely,
626                                  * remove it from the cache so we will not get
627                                  * it again. It will be removed when the last
628                                  * file/dir is closed.
629                                  */
630                                 context->callbacks.remove_cached_srv_fn(context,
631                                                                         srv);
632                         }
633                         
634                         /*
635                          * Maybe there are more cached connections to this
636                          * server
637                          */
638                         goto check_server_cache; 
639                 }
640
641                 return srv;
642         }
643
644         return NULL;
645 }
646
647 /*
648  * Connect to a server, possibly on an existing connection
649  *
650  * Here, what we want to do is: If the server and username
651  * match an existing connection, reuse that, otherwise, establish a 
652  * new connection.
653  *
654  * If we have to create a new connection, call the auth_fn to get the
655  * info we need, unless the username and password were passed in.
656  */
657
658 static SMBCSRV *
659 smbc_server(SMBCCTX *context,
660             BOOL connect_if_not_found,
661             const char *server,
662             const char *share, 
663             fstring workgroup,
664             fstring username, 
665             fstring password)
666 {
667         SMBCSRV *srv=NULL;
668         struct cli_state *c;
669         struct nmb_name called, calling;
670         const char *server_n = server;
671         pstring ipenv;
672         struct in_addr ip;
673         int tried_reverse = 0;
674         int port_try_first;
675         int port_try_next;
676         const char *username_used;
677   
678         zero_ip(&ip);
679         ZERO_STRUCT(c);
680
681         if (server[0] == 0) {
682                 errno = EPERM;
683                 return NULL;
684         }
685
686         /* Look for a cached connection */
687         srv = find_server(context, server, share,
688                           workgroup, username, password);
689         
690         /*
691          * If we found a connection and we're only allowed one share per
692          * server...
693          */
694         if (srv && *share != '\0' && context->options.one_share_per_server) {
695
696                 /*
697                  * ... then if there's no current connection to the share,
698                  * connect to it.  find_server(), or rather the function
699                  * pointed to by context->callbacks.get_cached_srv_fn which
700                  * was called by find_server(), will have issued a tree
701                  * disconnect if the requested share is not the same as the
702                  * one that was already connected.
703                  */
704                 if (srv->cli->cnum == (uint16) -1) {
705                         /* Ensure we have accurate auth info */
706                         if (context->internal->_auth_fn_with_context != NULL) {
707                                 context->internal->_auth_fn_with_context(
708                                         context,
709                                         server, share,
710                                         workgroup, sizeof(fstring),
711                                         username, sizeof(fstring),
712                                         password, sizeof(fstring));
713                         } else {
714                                 context->callbacks.auth_fn(
715                                         server, share,
716                                         workgroup, sizeof(fstring),
717                                         username, sizeof(fstring),
718                                         password, sizeof(fstring));
719                         }
720
721                         if (! cli_send_tconX(srv->cli, share, "?????",
722                                              password, strlen(password)+1)) {
723                         
724                                 errno = smbc_errno(context, srv->cli);
725                                 cli_shutdown(srv->cli);
726                                 srv->cli = NULL;
727                                 context->callbacks.remove_cached_srv_fn(context,
728                                                                         srv);
729                                 srv = NULL;
730                         }
731
732                         /*
733                          * Regenerate the dev value since it's based on both
734                          * server and share
735                          */
736                         if (srv) {
737                                 srv->dev = (dev_t)(str_checksum(server) ^
738                                                    str_checksum(share));
739                         }
740                 }
741         }
742         
743         /* If we have a connection... */
744         if (srv) {
745
746                 /* ... then we're done here.  Give 'em what they came for. */
747                 return srv;
748         }
749
750         /* If we're not asked to connect when a connection doesn't exist... */
751         if (! connect_if_not_found) {
752                 /* ... then we're done here. */
753                 return NULL;
754         }
755
756         make_nmb_name(&calling, context->netbios_name, 0x0);
757         make_nmb_name(&called , server, 0x20);
758
759         DEBUG(4,("smbc_server: server_n=[%s] server=[%s]\n", server_n, server));
760   
761         DEBUG(4,(" -> server_n=[%s] server=[%s]\n", server_n, server));
762
763  again:
764         slprintf(ipenv,sizeof(ipenv)-1,"HOST_%s", server_n);
765
766         zero_ip(&ip);
767
768         /* have to open a new connection */
769         if ((c = cli_initialise()) == NULL) {
770                 errno = ENOMEM;
771                 return NULL;
772         }
773
774         if (context->flags & SMB_CTX_FLAG_USE_KERBEROS) {
775                 c->use_kerberos = True;
776         }
777         if (context->flags & SMB_CTX_FLAG_FALLBACK_AFTER_KERBEROS) {
778                 c->fallback_after_kerberos = True;
779         }
780
781         c->timeout = context->timeout;
782
783         /*
784          * Force use of port 139 for first try if share is $IPC, empty, or
785          * null, so browse lists can work
786          */
787         if (share == NULL || *share == '\0' || strcmp(share, "IPC$") == 0) {
788                 port_try_first = 139;
789                 port_try_next = 445;
790         } else {
791                 port_try_first = 445;
792                 port_try_next = 139;
793         }
794
795         c->port = port_try_first;
796
797         if (!cli_connect(c, server_n, &ip)) {
798
799                 /* First connection attempt failed.  Try alternate port. */
800                 c->port = port_try_next;
801
802                 if (!cli_connect(c, server_n, &ip)) {
803                         cli_shutdown(c);
804                         errno = ETIMEDOUT;
805                         return NULL;
806                 }
807         }
808
809         if (!cli_session_request(c, &calling, &called)) {
810                 cli_shutdown(c);
811                 if (strcmp(called.name, "*SMBSERVER")) {
812                         make_nmb_name(&called , "*SMBSERVER", 0x20);
813                         goto again;
814                 } else {  /* Try one more time, but ensure we don't loop */
815
816                         /* Only try this if server is an IP address ... */
817
818                         if (is_ipaddress(server) && !tried_reverse) {
819                                 fstring remote_name;
820                                 struct in_addr rem_ip;
821
822                                 if ((rem_ip.s_addr=inet_addr(server)) == INADDR_NONE) {
823                                         DEBUG(4, ("Could not convert IP address "
824                                                 "%s to struct in_addr\n", server));
825                                         errno = ETIMEDOUT;
826                                         return NULL;
827                                 }
828
829                                 tried_reverse++; /* Yuck */
830
831                                 if (name_status_find("*", 0, 0, rem_ip, remote_name)) {
832                                         make_nmb_name(&called, remote_name, 0x20);
833                                         goto again;
834                                 }
835                         }
836                 }
837                 errno = ETIMEDOUT;
838                 return NULL;
839         }
840   
841         DEBUG(4,(" session request ok\n"));
842   
843         if (!cli_negprot(c)) {
844                 cli_shutdown(c);
845                 errno = ETIMEDOUT;
846                 return NULL;
847         }
848
849         username_used = username;
850
851         if (!NT_STATUS_IS_OK(cli_session_setup(c, username_used, 
852                                                password, strlen(password),
853                                                password, strlen(password),
854                                                workgroup))) {
855                 
856                 /* Failed.  Try an anonymous login, if allowed by flags. */
857                 username_used = "";
858
859                 if ((context->flags & SMBCCTX_FLAG_NO_AUTO_ANONYMOUS_LOGON) ||
860                      !NT_STATUS_IS_OK(cli_session_setup(c, username_used,
861                                                         password, 1,
862                                                         password, 0,
863                                                         workgroup))) {
864
865                         cli_shutdown(c);
866                         errno = EPERM;
867                         return NULL;
868                 }
869         }
870
871         DEBUG(4,(" session setup ok\n"));
872
873         if (!cli_send_tconX(c, share, "?????",
874                             password, strlen(password)+1)) {
875                 errno = smbc_errno(context, c);
876                 cli_shutdown(c);
877                 return NULL;
878         }
879   
880         DEBUG(4,(" tconx ok\n"));
881   
882         /*
883          * Ok, we have got a nice connection
884          * Let's allocate a server structure.
885          */
886
887         srv = SMB_MALLOC_P(SMBCSRV);
888         if (!srv) {
889                 errno = ENOMEM;
890                 goto failed;
891         }
892
893         ZERO_STRUCTP(srv);
894         srv->cli = c;
895         srv->dev = (dev_t)(str_checksum(server) ^ str_checksum(share));
896         srv->no_pathinfo = False;
897         srv->no_pathinfo2 = False;
898         srv->no_nt_session = False;
899
900         /* now add it to the cache (internal or external)  */
901         /* Let the cache function set errno if it wants to */
902         errno = 0;
903         if (context->callbacks.add_cached_srv_fn(context, srv, server, share, workgroup, username)) {
904                 int saved_errno = errno;
905                 DEBUG(3, (" Failed to add server to cache\n"));
906                 errno = saved_errno;
907                 if (errno == 0) {
908                         errno = ENOMEM;
909                 }
910                 goto failed;
911         }
912         
913         DEBUG(2, ("Server connect ok: //%s/%s: %p\n", 
914                   server, share, srv));
915
916         DLIST_ADD(context->internal->_servers, srv);
917         return srv;
918
919  failed:
920         cli_shutdown(c);
921         if (!srv) {
922                 return NULL;
923         }
924   
925         SAFE_FREE(srv);
926         return NULL;
927 }
928
929 /*
930  * Connect to a server for getting/setting attributes, possibly on an existing
931  * connection.  This works similarly to smbc_server().
932  */
933 static SMBCSRV *
934 smbc_attr_server(SMBCCTX *context,
935                  const char *server,
936                  const char *share, 
937                  fstring workgroup,
938                  fstring username,
939                  fstring password,
940                  POLICY_HND *pol)
941 {
942         int flags;
943         struct in_addr ip;
944         struct cli_state *ipc_cli;
945         struct rpc_pipe_client *pipe_hnd;
946         NTSTATUS nt_status;
947         SMBCSRV *ipc_srv=NULL;
948
949         /*
950          * See if we've already created this special connection.  Reference
951          * our "special" share name '*IPC$', which is an impossible real share
952          * name due to the leading asterisk.
953          */
954         ipc_srv = find_server(context, server, "*IPC$",
955                               workgroup, username, password);
956         if (!ipc_srv) {
957
958                 /* We didn't find a cached connection.  Get the password */
959                 if (*password == '\0') {
960                         /* ... then retrieve it now. */
961                         if (context->internal->_auth_fn_with_context != NULL) {
962                                 context->internal->_auth_fn_with_context(
963                                         context,
964                                         server, share,
965                                         workgroup, sizeof(fstring),
966                                         username, sizeof(fstring),
967                                         password, sizeof(fstring));
968                         } else {
969                                 context->callbacks.auth_fn(
970                                         server, share,
971                                         workgroup, sizeof(fstring),
972                                         username, sizeof(fstring),
973                                         password, sizeof(fstring));
974                         }
975                 }
976         
977                 flags = 0;
978                 if (context->flags & SMB_CTX_FLAG_USE_KERBEROS) {
979                         flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
980                 }
981
982                 zero_ip(&ip);
983                 nt_status = cli_full_connection(&ipc_cli,
984                                                 global_myname(), server, 
985                                                 &ip, 0, "IPC$", "?????",  
986                                                 username, workgroup,
987                                                 password, flags,
988                                                 Undefined, NULL);
989                 if (! NT_STATUS_IS_OK(nt_status)) {
990                         DEBUG(1,("cli_full_connection failed! (%s)\n",
991                                  nt_errstr(nt_status)));
992                         errno = ENOTSUP;
993                         return NULL;
994                 }
995
996                 ipc_srv = SMB_MALLOC_P(SMBCSRV);
997                 if (!ipc_srv) {
998                         errno = ENOMEM;
999                         cli_shutdown(ipc_cli);
1000                         return NULL;
1001                 }
1002
1003                 ZERO_STRUCTP(ipc_srv);
1004                 ipc_srv->cli = ipc_cli;
1005
1006                 if (pol) {
1007                         pipe_hnd = cli_rpc_pipe_open_noauth(ipc_srv->cli,
1008                                                             PI_LSARPC,
1009                                                             &nt_status);
1010                         if (!pipe_hnd) {
1011                                 DEBUG(1, ("cli_nt_session_open fail!\n"));
1012                                 errno = ENOTSUP;
1013                                 cli_shutdown(ipc_srv->cli);
1014                                 free(ipc_srv);
1015                                 return NULL;
1016                         }
1017
1018                         /*
1019                          * Some systems don't support
1020                          * SEC_RIGHTS_MAXIMUM_ALLOWED, but NT sends 0x2000000
1021                          * so we might as well do it too.
1022                          */
1023         
1024                         nt_status = rpccli_lsa_open_policy(
1025                                 pipe_hnd,
1026                                 ipc_srv->cli->mem_ctx,
1027                                 True, 
1028                                 GENERIC_EXECUTE_ACCESS,
1029                                 pol);
1030         
1031                         if (!NT_STATUS_IS_OK(nt_status)) {
1032                                 errno = smbc_errno(context, ipc_srv->cli);
1033                                 cli_shutdown(ipc_srv->cli);
1034                                 return NULL;
1035                         }
1036                 }
1037
1038                 /* now add it to the cache (internal or external) */
1039
1040                 errno = 0;      /* let cache function set errno if it likes */
1041                 if (context->callbacks.add_cached_srv_fn(context, ipc_srv,
1042                                                          server,
1043                                                          "*IPC$",
1044                                                          workgroup,
1045                                                          username)) {
1046                         DEBUG(3, (" Failed to add server to cache\n"));
1047                         if (errno == 0) {
1048                                 errno = ENOMEM;
1049                         }
1050                         cli_shutdown(ipc_srv->cli);
1051                         free(ipc_srv);
1052                         return NULL;
1053                 }
1054
1055                 DLIST_ADD(context->internal->_servers, ipc_srv);
1056         }
1057
1058         return ipc_srv;
1059 }
1060
1061 /*
1062  * Routine to open() a file ...
1063  */
1064
1065 static SMBCFILE *
1066 smbc_open_ctx(SMBCCTX *context,
1067               const char *fname,
1068               int flags,
1069               mode_t mode)
1070 {
1071         fstring server, share, user, password, workgroup;
1072         pstring path;
1073         pstring targetpath;
1074         struct cli_state *targetcli;
1075         SMBCSRV *srv   = NULL;
1076         SMBCFILE *file = NULL;
1077         int fd;
1078
1079         if (!context || !context->internal ||
1080             !context->internal->_initialized) {
1081
1082                 errno = EINVAL;  /* Best I can think of ... */
1083                 return NULL;
1084
1085         }
1086
1087         if (!fname) {
1088
1089                 errno = EINVAL;
1090                 return NULL;
1091
1092         }
1093
1094         if (smbc_parse_path(context, fname,
1095                             workgroup, sizeof(workgroup),
1096                             server, sizeof(server),
1097                             share, sizeof(share),
1098                             path, sizeof(path),
1099                             user, sizeof(user),
1100                             password, sizeof(password),
1101                             NULL, 0)) {
1102                 errno = EINVAL;
1103                 return NULL;
1104         }
1105
1106         if (user[0] == (char)0) fstrcpy(user, context->user);
1107
1108         srv = smbc_server(context, True,
1109                           server, share, workgroup, user, password);
1110
1111         if (!srv) {
1112
1113                 if (errno == EPERM) errno = EACCES;
1114                 return NULL;  /* smbc_server sets errno */
1115     
1116         }
1117
1118         /* Hmmm, the test for a directory is suspect here ... FIXME */
1119
1120         if (strlen(path) > 0 && path[strlen(path) - 1] == '\\') {
1121     
1122                 fd = -1;
1123
1124         }
1125         else {
1126           
1127                 file = SMB_MALLOC_P(SMBCFILE);
1128
1129                 if (!file) {
1130
1131                         errno = ENOMEM;
1132                         return NULL;
1133
1134                 }
1135
1136                 ZERO_STRUCTP(file);
1137
1138                 /*d_printf(">>>open: resolving %s\n", path);*/
1139                 if (!cli_resolve_path( "", srv->cli, path, &targetcli, targetpath))
1140                 {
1141                         d_printf("Could not resolve %s\n", path);
1142                         SAFE_FREE(file);
1143                         return NULL;
1144                 }
1145                 /*d_printf(">>>open: resolved %s as %s\n", path, targetpath);*/
1146                 
1147                 if ( targetcli->dfsroot )
1148                 {
1149                         pstring temppath;
1150                         pstrcpy(temppath, targetpath);
1151                         cli_dfs_make_full_path( targetpath, targetcli->desthost, targetcli->share, temppath);
1152                 }
1153                 
1154                 if ((fd = cli_open(targetcli, targetpath, flags,
1155                                    context->internal->_share_mode)) < 0) {
1156
1157                         /* Handle the error ... */
1158
1159                         SAFE_FREE(file);
1160                         errno = smbc_errno(context, targetcli);
1161                         return NULL;
1162
1163                 }
1164
1165                 /* Fill in file struct */
1166
1167                 file->cli_fd  = fd;
1168                 file->fname   = SMB_STRDUP(fname);
1169                 file->srv     = srv;
1170                 file->offset  = 0;
1171                 file->file    = True;
1172
1173                 DLIST_ADD(context->internal->_files, file);
1174
1175                 /*
1176                  * If the file was opened in O_APPEND mode, all write
1177                  * operations should be appended to the file.  To do that,
1178                  * though, using this protocol, would require a getattrE()
1179                  * call for each and every write, to determine where the end
1180                  * of the file is. (There does not appear to be an append flag
1181                  * in the protocol.)  Rather than add all of that overhead of
1182                  * retrieving the current end-of-file offset prior to each
1183                  * write operation, we'll assume that most append operations
1184                  * will continuously write, so we'll just set the offset to
1185                  * the end of the file now and hope that's adequate.
1186                  *
1187                  * Note to self: If this proves inadequate, and O_APPEND
1188                  * should, in some cases, be forced for each write, add a
1189                  * field in the context options structure, for
1190                  * "strict_append_mode" which would select between the current
1191                  * behavior (if FALSE) or issuing a getattrE() prior to each
1192                  * write and forcing the write to the end of the file (if
1193                  * TRUE).  Adding that capability will likely require adding
1194                  * an "append" flag into the _SMBCFILE structure to track
1195                  * whether a file was opened in O_APPEND mode.  -- djl
1196                  */
1197                 if (flags & O_APPEND) {
1198                         if (smbc_lseek_ctx(context, file, 0, SEEK_END) < 0) {
1199                                 (void) smbc_close_ctx(context, file);
1200                                 errno = ENXIO;
1201                                 return NULL;
1202                         }
1203                 }
1204
1205                 return file;
1206
1207         }
1208
1209         /* Check if opendir needed ... */
1210
1211         if (fd == -1) {
1212                 int eno = 0;
1213
1214                 eno = smbc_errno(context, srv->cli);
1215                 file = context->opendir(context, fname);
1216                 if (!file) errno = eno;
1217                 return file;
1218
1219         }
1220
1221         errno = EINVAL; /* FIXME, correct errno ? */
1222         return NULL;
1223
1224 }
1225
1226 /*
1227  * Routine to create a file 
1228  */
1229
1230 static int creat_bits = O_WRONLY | O_CREAT | O_TRUNC; /* FIXME: Do we need this */
1231
1232 static SMBCFILE *
1233 smbc_creat_ctx(SMBCCTX *context,
1234                const char *path,
1235                mode_t mode)
1236 {
1237
1238         if (!context || !context->internal ||
1239             !context->internal->_initialized) {
1240
1241                 errno = EINVAL;
1242                 return NULL;
1243
1244         }
1245
1246         return smbc_open_ctx(context, path, creat_bits, mode);
1247 }
1248
1249 /*
1250  * Routine to read() a file ...
1251  */
1252
1253 static ssize_t
1254 smbc_read_ctx(SMBCCTX *context,
1255               SMBCFILE *file,
1256               void *buf,
1257               size_t count)
1258 {
1259         int ret;
1260         fstring server, share, user, password;
1261         pstring path, targetpath;
1262         struct cli_state *targetcli;
1263
1264         /*
1265          * offset:
1266          *
1267          * Compiler bug (possibly) -- gcc (GCC) 3.3.5 (Debian 1:3.3.5-2) --
1268          * appears to pass file->offset (which is type off_t) differently than
1269          * a local variable of type off_t.  Using local variable "offset" in
1270          * the call to cli_read() instead of file->offset fixes a problem
1271          * retrieving data at an offset greater than 4GB.
1272          */
1273         off_t offset;
1274
1275         if (!context || !context->internal ||
1276             !context->internal->_initialized) {
1277
1278                 errno = EINVAL;
1279                 return -1;
1280
1281         }
1282
1283         DEBUG(4, ("smbc_read(%p, %d)\n", file, (int)count));
1284
1285         if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
1286
1287                 errno = EBADF;
1288                 return -1;
1289
1290         }
1291
1292         offset = file->offset;
1293
1294         /* Check that the buffer exists ... */
1295
1296         if (buf == NULL) {
1297
1298                 errno = EINVAL;
1299                 return -1;
1300
1301         }
1302
1303         /*d_printf(">>>read: parsing %s\n", file->fname);*/
1304         if (smbc_parse_path(context, file->fname,
1305                             NULL, 0,
1306                             server, sizeof(server),
1307                             share, sizeof(share),
1308                             path, sizeof(path),
1309                             user, sizeof(user),
1310                             password, sizeof(password),
1311                             NULL, 0)) {
1312                 errno = EINVAL;
1313                 return -1;
1314         }
1315         
1316         /*d_printf(">>>read: resolving %s\n", path);*/
1317         if (!cli_resolve_path("", file->srv->cli, path,
1318                               &targetcli, targetpath))
1319         {
1320                 d_printf("Could not resolve %s\n", path);
1321                 return -1;
1322         }
1323         /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
1324         
1325         ret = cli_read(targetcli, file->cli_fd, (char *)buf, offset, count);
1326
1327         if (ret < 0) {
1328
1329                 errno = smbc_errno(context, targetcli);
1330                 return -1;
1331
1332         }
1333
1334         file->offset += ret;
1335
1336         DEBUG(4, ("  --> %d\n", ret));
1337
1338         return ret;  /* Success, ret bytes of data ... */
1339
1340 }
1341
1342 /*
1343  * Routine to write() a file ...
1344  */
1345
1346 static ssize_t
1347 smbc_write_ctx(SMBCCTX *context,
1348                SMBCFILE *file,
1349                void *buf,
1350                size_t count)
1351 {
1352         int ret;
1353         off_t offset;
1354         fstring server, share, user, password;
1355         pstring path, targetpath;
1356         struct cli_state *targetcli;
1357
1358         /* First check all pointers before dereferencing them */
1359         
1360         if (!context || !context->internal ||
1361             !context->internal->_initialized) {
1362
1363                 errno = EINVAL;
1364                 return -1;
1365
1366         }
1367
1368         if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
1369
1370                 errno = EBADF;
1371                 return -1;
1372     
1373         }
1374
1375         /* Check that the buffer exists ... */
1376
1377         if (buf == NULL) {
1378
1379                 errno = EINVAL;
1380                 return -1;
1381
1382         }
1383
1384         offset = file->offset; /* See "offset" comment in smbc_read_ctx() */
1385
1386         /*d_printf(">>>write: parsing %s\n", file->fname);*/
1387         if (smbc_parse_path(context, file->fname,
1388                             NULL, 0,
1389                             server, sizeof(server),
1390                             share, sizeof(share),
1391                             path, sizeof(path),
1392                             user, sizeof(user),
1393                             password, sizeof(password),
1394                             NULL, 0)) {
1395                 errno = EINVAL;
1396                 return -1;
1397         }
1398         
1399         /*d_printf(">>>write: resolving %s\n", path);*/
1400         if (!cli_resolve_path("", file->srv->cli, path,
1401                               &targetcli, targetpath))
1402         {
1403                 d_printf("Could not resolve %s\n", path);
1404                 return -1;
1405         }
1406         /*d_printf(">>>write: resolved path as %s\n", targetpath);*/
1407
1408
1409         ret = cli_write(targetcli, file->cli_fd, 0, (char *)buf, offset, count);
1410
1411         if (ret <= 0) {
1412
1413                 errno = smbc_errno(context, targetcli);
1414                 return -1;
1415
1416         }
1417
1418         file->offset += ret;
1419
1420         return ret;  /* Success, 0 bytes of data ... */
1421 }
1422  
1423 /*
1424  * Routine to close() a file ...
1425  */
1426
1427 static int
1428 smbc_close_ctx(SMBCCTX *context,
1429                SMBCFILE *file)
1430 {
1431         SMBCSRV *srv; 
1432         fstring server, share, user, password;
1433         pstring path, targetpath;
1434         struct cli_state *targetcli;
1435
1436         if (!context || !context->internal ||
1437             !context->internal->_initialized) {
1438
1439                 errno = EINVAL;
1440                 return -1;
1441
1442         }
1443
1444         if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
1445    
1446                 errno = EBADF;
1447                 return -1;
1448
1449         }
1450
1451         /* IS a dir ... */
1452         if (!file->file) {
1453                 
1454                 return context->closedir(context, file);
1455
1456         }
1457
1458         /*d_printf(">>>close: parsing %s\n", file->fname);*/
1459         if (smbc_parse_path(context, file->fname,
1460                             NULL, 0,
1461                             server, sizeof(server),
1462                             share, sizeof(share),
1463                             path, sizeof(path),
1464                             user, sizeof(user),
1465                             password, sizeof(password),
1466                             NULL, 0)) {
1467                 errno = EINVAL;
1468                 return -1;
1469         }
1470         
1471         /*d_printf(">>>close: resolving %s\n", path);*/
1472         if (!cli_resolve_path("", file->srv->cli, path,
1473                               &targetcli, targetpath))
1474         {
1475                 d_printf("Could not resolve %s\n", path);
1476                 return -1;
1477         }
1478         /*d_printf(">>>close: resolved path as %s\n", targetpath);*/
1479
1480         if (!cli_close(targetcli, file->cli_fd)) {
1481
1482                 DEBUG(3, ("cli_close failed on %s. purging server.\n", 
1483                           file->fname));
1484                 /* Deallocate slot and remove the server 
1485                  * from the server cache if unused */
1486                 errno = smbc_errno(context, targetcli);
1487                 srv = file->srv;
1488                 DLIST_REMOVE(context->internal->_files, file);
1489                 SAFE_FREE(file->fname);
1490                 SAFE_FREE(file);
1491                 context->callbacks.remove_unused_server_fn(context, srv);
1492
1493                 return -1;
1494
1495         }
1496
1497         DLIST_REMOVE(context->internal->_files, file);
1498         SAFE_FREE(file->fname);
1499         SAFE_FREE(file);
1500
1501         return 0;
1502 }
1503
1504 /*
1505  * Get info from an SMB server on a file. Use a qpathinfo call first
1506  * and if that fails, use getatr, as Win95 sometimes refuses qpathinfo
1507  */
1508 static BOOL
1509 smbc_getatr(SMBCCTX * context,
1510             SMBCSRV *srv,
1511             char *path, 
1512             uint16 *mode,
1513             SMB_OFF_T *size, 
1514             struct timespec *create_time_ts,
1515             struct timespec *access_time_ts,
1516             struct timespec *write_time_ts,
1517             struct timespec *change_time_ts,
1518             SMB_INO_T *ino)
1519 {
1520         pstring fixedpath;
1521         pstring targetpath;
1522         struct cli_state *targetcli;
1523         time_t write_time;
1524
1525         if (!context || !context->internal ||
1526             !context->internal->_initialized) {
1527  
1528                 errno = EINVAL;
1529                 return -1;
1530  
1531         }
1532
1533         /* path fixup for . and .. */
1534         if (strequal(path, ".") || strequal(path, ".."))
1535                 pstrcpy(fixedpath, "\\");
1536         else
1537         {
1538                 pstrcpy(fixedpath, path);
1539                 trim_string(fixedpath, NULL, "\\..");
1540                 trim_string(fixedpath, NULL, "\\.");
1541         }
1542         DEBUG(4,("smbc_getatr: sending qpathinfo\n"));
1543   
1544         if (!cli_resolve_path( "", srv->cli, fixedpath, &targetcli, targetpath))
1545         {
1546                 d_printf("Couldn't resolve %s\n", path);
1547                 return False;
1548         }
1549         
1550         if ( targetcli->dfsroot )
1551         {
1552                 pstring temppath;
1553                 pstrcpy(temppath, targetpath);
1554                 cli_dfs_make_full_path(targetpath, targetcli->desthost,
1555                                        targetcli->share, temppath);
1556         }
1557   
1558         if (!srv->no_pathinfo2 &&
1559             cli_qpathinfo2(targetcli, targetpath,
1560                            create_time_ts,
1561                            access_time_ts,
1562                            write_time_ts,
1563                            change_time_ts,
1564                            size, mode, ino)) {
1565             return True;
1566         }
1567
1568         /* if this is NT then don't bother with the getatr */
1569         if (targetcli->capabilities & CAP_NT_SMBS) {
1570                 errno = EPERM;
1571                 return False;
1572         }
1573
1574         if (cli_getatr(targetcli, targetpath, mode, size, &write_time)) {
1575
1576                 struct timespec w_time_ts;
1577
1578                 w_time_ts = convert_time_t_to_timespec(write_time);
1579
1580                 if (write_time_ts != NULL) {
1581                         *write_time_ts = w_time_ts;
1582                 }
1583
1584                 if (create_time_ts != NULL) {
1585                         *create_time_ts = w_time_ts;
1586                 }
1587                 
1588                 if (access_time_ts != NULL) {
1589                         *access_time_ts = w_time_ts;
1590                 }
1591                 
1592                 if (change_time_ts != NULL) {
1593                         *change_time_ts = w_time_ts;
1594                 }
1595
1596                 srv->no_pathinfo2 = True;
1597                 return True;
1598         }
1599
1600         errno = EPERM;
1601         return False;
1602
1603 }
1604
1605 /*
1606  * Set file info on an SMB server.  Use setpathinfo call first.  If that
1607  * fails, use setattrE..
1608  *
1609  * Access and modification time parameters are always used and must be
1610  * provided.  Create time, if zero, will be determined from the actual create
1611  * time of the file.  If non-zero, the create time will be set as well.
1612  *
1613  * "mode" (attributes) parameter may be set to -1 if it is not to be set.
1614  */
1615 static BOOL
1616 smbc_setatr(SMBCCTX * context, SMBCSRV *srv, char *path, 
1617             time_t create_time,
1618             time_t access_time,
1619             time_t write_time,
1620             time_t change_time,
1621             uint16 mode)
1622 {
1623         int fd;
1624         int ret;
1625
1626         /*
1627          * First, try setpathinfo (if qpathinfo succeeded), for it is the
1628          * modern function for "new code" to be using, and it works given a
1629          * filename rather than requiring that the file be opened to have its
1630          * attributes manipulated.
1631          */
1632         if (srv->no_pathinfo ||
1633             ! cli_setpathinfo(srv->cli, path,
1634                               create_time,
1635                               access_time,
1636                               write_time,
1637                               change_time,
1638                               mode)) {
1639
1640                 /*
1641                  * setpathinfo is not supported; go to plan B. 
1642                  *
1643                  * cli_setatr() does not work on win98, and it also doesn't
1644                  * support setting the access time (only the modification
1645                  * time), so in all cases, we open the specified file and use
1646                  * cli_setattrE() which should work on all OS versions, and
1647                  * supports both times.
1648                  */
1649
1650                 /* Don't try {q,set}pathinfo() again, with this server */
1651                 srv->no_pathinfo = True;
1652
1653                 /* Open the file */
1654                 if ((fd = cli_open(srv->cli, path, O_RDWR, DENY_NONE)) < 0) {
1655
1656                         errno = smbc_errno(context, srv->cli);
1657                         return -1;
1658                 }
1659
1660                 /* Set the new attributes */
1661                 ret = cli_setattrE(srv->cli, fd,
1662                                    change_time,
1663                                    access_time,
1664                                    write_time);
1665
1666                 /* Close the file */
1667                 cli_close(srv->cli, fd);
1668
1669                 /*
1670                  * Unfortunately, setattrE() doesn't have a provision for
1671                  * setting the access mode (attributes).  We'll have to try
1672                  * cli_setatr() for that, and with only this parameter, it
1673                  * seems to work on win98.
1674                  */
1675                 if (ret && mode != (uint16) -1) {
1676                         ret = cli_setatr(srv->cli, path, mode, 0);
1677                 }
1678
1679                 if (! ret) {
1680                         errno = smbc_errno(context, srv->cli);
1681                         return False;
1682                 }
1683         }
1684
1685         return True;
1686 }
1687
1688  /*
1689   * Routine to unlink() a file
1690   */
1691
1692 static int
1693 smbc_unlink_ctx(SMBCCTX *context,
1694                 const char *fname)
1695 {
1696         fstring server, share, user, password, workgroup;
1697         pstring path, targetpath;
1698         struct cli_state *targetcli;
1699         SMBCSRV *srv = NULL;
1700
1701         if (!context || !context->internal ||
1702             !context->internal->_initialized) {
1703
1704                 errno = EINVAL;  /* Best I can think of ... */
1705                 return -1;
1706
1707         }
1708
1709         if (!fname) {
1710
1711                 errno = EINVAL;
1712                 return -1;
1713
1714         }
1715
1716         if (smbc_parse_path(context, fname,
1717                             workgroup, sizeof(workgroup),
1718                             server, sizeof(server),
1719                             share, sizeof(share),
1720                             path, sizeof(path),
1721                             user, sizeof(user),
1722                             password, sizeof(password),
1723                             NULL, 0)) {
1724                 errno = EINVAL;
1725                 return -1;
1726         }
1727
1728         if (user[0] == (char)0) fstrcpy(user, context->user);
1729
1730         srv = smbc_server(context, True,
1731                           server, share, workgroup, user, password);
1732
1733         if (!srv) {
1734
1735                 return -1;  /* smbc_server sets errno */
1736
1737         }
1738
1739         /*d_printf(">>>unlink: resolving %s\n", path);*/
1740         if (!cli_resolve_path( "", srv->cli, path, &targetcli, targetpath))
1741         {
1742                 d_printf("Could not resolve %s\n", path);
1743                 return -1;
1744         }
1745         /*d_printf(">>>unlink: resolved path as %s\n", targetpath);*/
1746
1747         if (!cli_unlink(targetcli, targetpath)) {
1748
1749                 errno = smbc_errno(context, targetcli);
1750
1751                 if (errno == EACCES) { /* Check if the file is a directory */
1752
1753                         int saverr = errno;
1754                         SMB_OFF_T size = 0;
1755                         uint16 mode = 0;
1756                         struct timespec write_time_ts;
1757                         struct timespec access_time_ts;
1758                         struct timespec change_time_ts;
1759                         SMB_INO_T ino = 0;
1760
1761                         if (!smbc_getatr(context, srv, path, &mode, &size,
1762                                          NULL,
1763                                          &access_time_ts,
1764                                          &write_time_ts,
1765                                          &change_time_ts,
1766                                          &ino)) {
1767
1768                                 /* Hmmm, bad error ... What? */
1769
1770                                 errno = smbc_errno(context, targetcli);
1771                                 return -1;
1772
1773                         }
1774                         else {
1775
1776                                 if (IS_DOS_DIR(mode))
1777                                         errno = EISDIR;
1778                                 else
1779                                         errno = saverr;  /* Restore this */
1780
1781                         }
1782                 }
1783
1784                 return -1;
1785
1786         }
1787
1788         return 0;  /* Success ... */
1789
1790 }
1791
1792 /*
1793  * Routine to rename() a file
1794  */
1795
1796 static int
1797 smbc_rename_ctx(SMBCCTX *ocontext,
1798                 const char *oname, 
1799                 SMBCCTX *ncontext,
1800                 const char *nname)
1801 {
1802         fstring server1;
1803         fstring share1;
1804         fstring server2;
1805         fstring share2;
1806         fstring user1;
1807         fstring user2;
1808         fstring password1;
1809         fstring password2;
1810         fstring workgroup;
1811         pstring path1;
1812         pstring path2;
1813         pstring targetpath1;
1814         pstring targetpath2;
1815         struct cli_state *targetcli1;
1816         struct cli_state *targetcli2;
1817         SMBCSRV *srv = NULL;
1818
1819         if (!ocontext || !ncontext || 
1820             !ocontext->internal || !ncontext->internal ||
1821             !ocontext->internal->_initialized || 
1822             !ncontext->internal->_initialized) {
1823
1824                 errno = EINVAL;  /* Best I can think of ... */
1825                 return -1;
1826
1827         }
1828         
1829         if (!oname || !nname) {
1830
1831                 errno = EINVAL;
1832                 return -1;
1833
1834         }
1835         
1836         DEBUG(4, ("smbc_rename(%s,%s)\n", oname, nname));
1837
1838         smbc_parse_path(ocontext, oname,
1839                         workgroup, sizeof(workgroup),
1840                         server1, sizeof(server1),
1841                         share1, sizeof(share1),
1842                         path1, sizeof(path1),
1843                         user1, sizeof(user1),
1844                         password1, sizeof(password1),
1845                         NULL, 0);
1846
1847         if (user1[0] == (char)0) fstrcpy(user1, ocontext->user);
1848
1849         smbc_parse_path(ncontext, nname,
1850                         NULL, 0,
1851                         server2, sizeof(server2),
1852                         share2, sizeof(share2),
1853                         path2, sizeof(path2),
1854                         user2, sizeof(user2),
1855                         password2, sizeof(password2),
1856                         NULL, 0);
1857
1858         if (user2[0] == (char)0) fstrcpy(user2, ncontext->user);
1859
1860         if (strcmp(server1, server2) || strcmp(share1, share2) ||
1861             strcmp(user1, user2)) {
1862
1863                 /* Can't rename across file systems, or users?? */
1864
1865                 errno = EXDEV;
1866                 return -1;
1867
1868         }
1869
1870         srv = smbc_server(ocontext, True,
1871                           server1, share1, workgroup, user1, password1);
1872         if (!srv) {
1873
1874                 return -1;
1875
1876         }
1877
1878         /*d_printf(">>>rename: resolving %s\n", path1);*/
1879         if (!cli_resolve_path( "", srv->cli, path1, &targetcli1, targetpath1))
1880         {
1881                 d_printf("Could not resolve %s\n", path1);
1882                 return -1;
1883         }
1884         /*d_printf(">>>rename: resolved path as %s\n", targetpath1);*/
1885         /*d_printf(">>>rename: resolving %s\n", path2);*/
1886         if (!cli_resolve_path( "", srv->cli, path2, &targetcli2, targetpath2))
1887         {
1888                 d_printf("Could not resolve %s\n", path2);
1889                 return -1;
1890         }
1891         /*d_printf(">>>rename: resolved path as %s\n", targetpath2);*/
1892         
1893         if (strcmp(targetcli1->desthost, targetcli2->desthost) ||
1894             strcmp(targetcli1->share, targetcli2->share))
1895         {
1896                 /* can't rename across file systems */
1897                 
1898                 errno = EXDEV;
1899                 return -1;
1900         }
1901
1902         if (!cli_rename(targetcli1, targetpath1, targetpath2)) {
1903                 int eno = smbc_errno(ocontext, targetcli1);
1904
1905                 if (eno != EEXIST ||
1906                     !cli_unlink(targetcli1, targetpath2) ||
1907                     !cli_rename(targetcli1, targetpath1, targetpath2)) {
1908
1909                         errno = eno;
1910                         return -1;
1911
1912                 }
1913         }
1914
1915         return 0; /* Success */
1916
1917 }
1918
1919 /*
1920  * A routine to lseek() a file
1921  */
1922
1923 static off_t
1924 smbc_lseek_ctx(SMBCCTX *context,
1925                SMBCFILE *file,
1926                off_t offset,
1927                int whence)
1928 {
1929         SMB_OFF_T size;
1930         fstring server, share, user, password;
1931         pstring path, targetpath;
1932         struct cli_state *targetcli;
1933
1934         if (!context || !context->internal ||
1935             !context->internal->_initialized) {
1936
1937                 errno = EINVAL;
1938                 return -1;
1939                 
1940         }
1941
1942         if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
1943
1944                 errno = EBADF;
1945                 return -1;
1946
1947         }
1948
1949         if (!file->file) {
1950
1951                 errno = EINVAL;
1952                 return -1;      /* Can't lseek a dir ... */
1953
1954         }
1955
1956         switch (whence) {
1957         case SEEK_SET:
1958                 file->offset = offset;
1959                 break;
1960
1961         case SEEK_CUR:
1962                 file->offset += offset;
1963                 break;
1964
1965         case SEEK_END:
1966                 /*d_printf(">>>lseek: parsing %s\n", file->fname);*/
1967                 if (smbc_parse_path(context, file->fname,
1968                                     NULL, 0,
1969                                     server, sizeof(server),
1970                                     share, sizeof(share),
1971                                     path, sizeof(path),
1972                                     user, sizeof(user),
1973                                     password, sizeof(password),
1974                                     NULL, 0)) {
1975                         
1976                                         errno = EINVAL;
1977                                         return -1;
1978                         }
1979                 
1980                 /*d_printf(">>>lseek: resolving %s\n", path);*/
1981                 if (!cli_resolve_path("", file->srv->cli, path,
1982                                       &targetcli, targetpath))
1983                 {
1984                         d_printf("Could not resolve %s\n", path);
1985                         return -1;
1986                 }
1987                 /*d_printf(">>>lseek: resolved path as %s\n", targetpath);*/
1988                 
1989                 if (!cli_qfileinfo(targetcli, file->cli_fd, NULL,
1990                                    &size, NULL, NULL, NULL, NULL, NULL)) 
1991                 {
1992                     SMB_OFF_T b_size = size;
1993                         if (!cli_getattrE(targetcli, file->cli_fd,
1994                                           NULL, &b_size, NULL, NULL, NULL)) 
1995                     {
1996                         errno = EINVAL;
1997                         return -1;
1998                     } else
1999                         size = b_size;
2000                 }
2001                 file->offset = size + offset;
2002                 break;
2003
2004         default:
2005                 errno = EINVAL;
2006                 break;
2007
2008         }
2009
2010         return file->offset;
2011
2012 }
2013
2014 /* 
2015  * Generate an inode number from file name for those things that need it
2016  */
2017
2018 static ino_t
2019 smbc_inode(SMBCCTX *context,
2020            const char *name)
2021 {
2022
2023         if (!context || !context->internal ||
2024             !context->internal->_initialized) {
2025
2026                 errno = EINVAL;
2027                 return -1;
2028
2029         }
2030
2031         if (!*name) return 2; /* FIXME, why 2 ??? */
2032         return (ino_t)str_checksum(name);
2033
2034 }
2035
2036 /*
2037  * Routine to put basic stat info into a stat structure ... Used by stat and
2038  * fstat below.
2039  */
2040
2041 static int
2042 smbc_setup_stat(SMBCCTX *context,
2043                 struct stat *st,
2044                 char *fname,
2045                 SMB_OFF_T size,
2046                 int mode)
2047 {
2048         
2049         st->st_mode = 0;
2050
2051         if (IS_DOS_DIR(mode)) {
2052                 st->st_mode = SMBC_DIR_MODE;
2053         } else {
2054                 st->st_mode = SMBC_FILE_MODE;
2055         }
2056
2057         if (IS_DOS_ARCHIVE(mode)) st->st_mode |= S_IXUSR;
2058         if (IS_DOS_SYSTEM(mode)) st->st_mode |= S_IXGRP;
2059         if (IS_DOS_HIDDEN(mode)) st->st_mode |= S_IXOTH;
2060         if (!IS_DOS_READONLY(mode)) st->st_mode |= S_IWUSR;
2061
2062         st->st_size = size;
2063 #ifdef HAVE_STAT_ST_BLKSIZE
2064         st->st_blksize = 512;
2065 #endif
2066 #ifdef HAVE_STAT_ST_BLOCKS
2067         st->st_blocks = (size+511)/512;
2068 #endif
2069         st->st_uid = getuid();
2070         st->st_gid = getgid();
2071
2072         if (IS_DOS_DIR(mode)) {
2073                 st->st_nlink = 2;
2074         } else {
2075                 st->st_nlink = 1;
2076         }
2077
2078         if (st->st_ino == 0) {
2079                 st->st_ino = smbc_inode(context, fname);
2080         }
2081         
2082         return True;  /* FIXME: Is this needed ? */
2083
2084 }
2085
2086 /*
2087  * Routine to stat a file given a name
2088  */
2089
2090 static int
2091 smbc_stat_ctx(SMBCCTX *context,
2092               const char *fname,
2093               struct stat *st)
2094 {
2095         SMBCSRV *srv;
2096         fstring server;
2097         fstring share;
2098         fstring user;
2099         fstring password;
2100         fstring workgroup;
2101         pstring path;
2102         struct timespec write_time_ts;
2103         struct timespec access_time_ts;
2104         struct timespec change_time_ts;
2105         SMB_OFF_T size = 0;
2106         uint16 mode = 0;
2107         SMB_INO_T ino = 0;
2108
2109         if (!context || !context->internal ||
2110             !context->internal->_initialized) {
2111
2112                 errno = EINVAL;  /* Best I can think of ... */
2113                 return -1;
2114     
2115         }
2116
2117         if (!fname) {
2118
2119                 errno = EINVAL;
2120                 return -1;
2121
2122         }
2123   
2124         DEBUG(4, ("smbc_stat(%s)\n", fname));
2125
2126         if (smbc_parse_path(context, fname,
2127                             workgroup, sizeof(workgroup),
2128                             server, sizeof(server),
2129                             share, sizeof(share),
2130                             path, sizeof(path),
2131                             user, sizeof(user),
2132                             password, sizeof(password),
2133                             NULL, 0)) {
2134                 errno = EINVAL;
2135                 return -1;
2136         }
2137
2138         if (user[0] == (char)0) fstrcpy(user, context->user);
2139
2140         srv = smbc_server(context, True,
2141                           server, share, workgroup, user, password);
2142
2143         if (!srv) {
2144                 return -1;  /* errno set by smbc_server */
2145         }
2146
2147         if (!smbc_getatr(context, srv, path, &mode, &size, 
2148                          NULL,
2149                          &access_time_ts,
2150                          &write_time_ts,
2151                          &change_time_ts,
2152                          &ino)) {
2153
2154                 errno = smbc_errno(context, srv->cli);
2155                 return -1;
2156                 
2157         }
2158
2159         st->st_ino = ino;
2160
2161         smbc_setup_stat(context, st, path, size, mode);
2162
2163         set_atimespec(st, access_time_ts);
2164         set_ctimespec(st, change_time_ts);
2165         set_mtimespec(st, write_time_ts);
2166         st->st_dev   = srv->dev;
2167
2168         return 0;
2169
2170 }
2171
2172 /*
2173  * Routine to stat a file given an fd
2174  */
2175
2176 static int
2177 smbc_fstat_ctx(SMBCCTX *context,
2178                SMBCFILE *file,
2179                struct stat *st)
2180 {
2181         struct timespec change_time_ts;
2182         struct timespec access_time_ts;
2183         struct timespec write_time_ts;
2184         SMB_OFF_T size;
2185         uint16 mode;
2186         fstring server;
2187         fstring share;
2188         fstring user;
2189         fstring password;
2190         pstring path;
2191         pstring targetpath;
2192         struct cli_state *targetcli;
2193         SMB_INO_T ino = 0;
2194
2195         if (!context || !context->internal ||
2196             !context->internal->_initialized) {
2197
2198                 errno = EINVAL;
2199                 return -1;
2200
2201         }
2202
2203         if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
2204
2205                 errno = EBADF;
2206                 return -1;
2207
2208         }
2209
2210         if (!file->file) {
2211
2212                 return context->fstatdir(context, file, st);
2213
2214         }
2215
2216         /*d_printf(">>>fstat: parsing %s\n", file->fname);*/
2217         if (smbc_parse_path(context, file->fname,
2218                             NULL, 0,
2219                             server, sizeof(server),
2220                             share, sizeof(share),
2221                             path, sizeof(path),
2222                             user, sizeof(user),
2223                             password, sizeof(password),
2224                             NULL, 0)) {
2225                 errno = EINVAL;
2226                 return -1;
2227         }
2228         
2229         /*d_printf(">>>fstat: resolving %s\n", path);*/
2230         if (!cli_resolve_path("", file->srv->cli, path,
2231                               &targetcli, targetpath))
2232         {
2233                 d_printf("Could not resolve %s\n", path);
2234                 return -1;
2235         }
2236         /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
2237
2238         if (!cli_qfileinfo(targetcli, file->cli_fd, &mode, &size,
2239                            NULL,
2240                            &access_time_ts,
2241                            &write_time_ts,
2242                            &change_time_ts,
2243                            &ino)) {
2244
2245                 time_t change_time, access_time, write_time;
2246
2247                 if (!cli_getattrE(targetcli, file->cli_fd, &mode, &size,
2248                                 &change_time, &access_time, &write_time)) {
2249
2250                         errno = EINVAL;
2251                         return -1;
2252                 }
2253
2254                 change_time_ts = convert_time_t_to_timespec(change_time);
2255                 access_time_ts = convert_time_t_to_timespec(access_time);
2256                 write_time_ts = convert_time_t_to_timespec(write_time);
2257         }
2258
2259         st->st_ino = ino;
2260
2261         smbc_setup_stat(context, st, file->fname, size, mode);
2262
2263         set_atimespec(st, access_time_ts);
2264         set_ctimespec(st, change_time_ts);
2265         set_mtimespec(st, write_time_ts);
2266         st->st_dev = file->srv->dev;
2267
2268         return 0;
2269
2270 }
2271
2272 /*
2273  * Routine to open a directory
2274  * We accept the URL syntax explained in smbc_parse_path(), above.
2275  */
2276
2277 static void
2278 smbc_remove_dir(SMBCFILE *dir)
2279 {
2280         struct smbc_dir_list *d,*f;
2281
2282         d = dir->dir_list;
2283         while (d) {
2284
2285                 f = d; d = d->next;
2286
2287                 SAFE_FREE(f->dirent);
2288                 SAFE_FREE(f);
2289
2290         }
2291
2292         dir->dir_list = dir->dir_end = dir->dir_next = NULL;
2293
2294 }
2295
2296 static int
2297 add_dirent(SMBCFILE *dir,
2298            const char *name,
2299            const char *comment,
2300            uint32 type)
2301 {
2302         struct smbc_dirent *dirent;
2303         int size;
2304         int name_length = (name == NULL ? 0 : strlen(name));
2305         int comment_len = (comment == NULL ? 0 : strlen(comment));
2306
2307         /*
2308          * Allocate space for the dirent, which must be increased by the 
2309          * size of the name and the comment and 1 each for the null terminator.
2310          */
2311
2312         size = sizeof(struct smbc_dirent) + name_length + comment_len + 2;
2313     
2314         dirent = (struct smbc_dirent *)SMB_MALLOC(size);
2315
2316         if (!dirent) {
2317
2318                 dir->dir_error = ENOMEM;
2319                 return -1;
2320
2321         }
2322
2323         ZERO_STRUCTP(dirent);
2324
2325         if (dir->dir_list == NULL) {
2326
2327                 dir->dir_list = SMB_MALLOC_P(struct smbc_dir_list);
2328                 if (!dir->dir_list) {
2329
2330                         SAFE_FREE(dirent);
2331                         dir->dir_error = ENOMEM;
2332                         return -1;
2333
2334                 }
2335                 ZERO_STRUCTP(dir->dir_list);
2336
2337                 dir->dir_end = dir->dir_next = dir->dir_list;
2338         }
2339         else {
2340
2341                 dir->dir_end->next = SMB_MALLOC_P(struct smbc_dir_list);
2342                 
2343                 if (!dir->dir_end->next) {
2344                         
2345                         SAFE_FREE(dirent);
2346                         dir->dir_error = ENOMEM;
2347                         return -1;
2348
2349                 }
2350                 ZERO_STRUCTP(dir->dir_end->next);
2351
2352                 dir->dir_end = dir->dir_end->next;
2353         }
2354
2355         dir->dir_end->next = NULL;
2356         dir->dir_end->dirent = dirent;
2357         
2358         dirent->smbc_type = type;
2359         dirent->namelen = name_length;
2360         dirent->commentlen = comment_len;
2361         dirent->dirlen = size;
2362   
2363         /*
2364          * dirent->namelen + 1 includes the null (no null termination needed)
2365          * Ditto for dirent->commentlen.
2366          * The space for the two null bytes was allocated.
2367          */
2368         strncpy(dirent->name, (name?name:""), dirent->namelen + 1);
2369         dirent->comment = (char *)(&dirent->name + dirent->namelen + 1);
2370         strncpy(dirent->comment, (comment?comment:""), dirent->commentlen + 1);
2371         
2372         return 0;
2373
2374 }
2375
2376 static void
2377 list_unique_wg_fn(const char *name,
2378                   uint32 type,
2379                   const char *comment,
2380                   void *state)
2381 {
2382         SMBCFILE *dir = (SMBCFILE *)state;
2383         struct smbc_dir_list *dir_list;
2384         struct smbc_dirent *dirent;
2385         int dirent_type;
2386         int do_remove = 0;
2387
2388         dirent_type = dir->dir_type;
2389
2390         if (add_dirent(dir, name, comment, dirent_type) < 0) {
2391
2392                 /* An error occurred, what do we do? */
2393                 /* FIXME: Add some code here */
2394         }
2395
2396         /* Point to the one just added */
2397         dirent = dir->dir_end->dirent;
2398
2399         /* See if this was a duplicate */
2400         for (dir_list = dir->dir_list;
2401              dir_list != dir->dir_end;
2402              dir_list = dir_list->next) {
2403                 if (! do_remove &&
2404                     strcmp(dir_list->dirent->name, dirent->name) == 0) {
2405                         /* Duplicate.  End end of list need to be removed. */
2406                         do_remove = 1;
2407                 }
2408
2409                 if (do_remove && dir_list->next == dir->dir_end) {
2410                         /* Found the end of the list.  Remove it. */
2411                         dir->dir_end = dir_list;
2412                         free(dir_list->next);
2413                         free(dirent);
2414                         dir_list->next = NULL;
2415                         break;
2416                 }
2417         }
2418 }
2419
2420 static void
2421 list_fn(const char *name,
2422         uint32 type,
2423         const char *comment,
2424         void *state)
2425 {
2426         SMBCFILE *dir = (SMBCFILE *)state;
2427         int dirent_type;
2428
2429         /*
2430          * We need to process the type a little ...
2431          *
2432          * Disk share     = 0x00000000
2433          * Print share    = 0x00000001
2434          * Comms share    = 0x00000002 (obsolete?)
2435          * IPC$ share     = 0x00000003 
2436          *
2437          * administrative shares:
2438          * ADMIN$, IPC$, C$, D$, E$ ...  are type |= 0x80000000
2439          */
2440         
2441         if (dir->dir_type == SMBC_FILE_SHARE) {
2442                 
2443                 switch (type) {
2444                 case 0 | 0x80000000:
2445                 case 0:
2446                         dirent_type = SMBC_FILE_SHARE;
2447                         break;
2448
2449                 case 1:
2450                         dirent_type = SMBC_PRINTER_SHARE;
2451                         break;
2452
2453                 case 2:
2454                         dirent_type = SMBC_COMMS_SHARE;
2455                         break;
2456
2457                 case 3 | 0x80000000:
2458                 case 3:
2459                         dirent_type = SMBC_IPC_SHARE;
2460                         break;
2461
2462                 default:
2463                         dirent_type = SMBC_FILE_SHARE; /* FIXME, error? */
2464                         break;
2465                 }
2466         }
2467         else {
2468                 dirent_type = dir->dir_type;
2469         }
2470
2471         if (add_dirent(dir, name, comment, dirent_type) < 0) {
2472
2473                 /* An error occurred, what do we do? */
2474                 /* FIXME: Add some code here */
2475
2476         }
2477 }
2478
2479 static void
2480 dir_list_fn(const char *mnt,
2481             file_info *finfo,
2482             const char *mask,
2483             void *state)
2484 {
2485
2486         if (add_dirent((SMBCFILE *)state, finfo->name, "", 
2487                        (finfo->mode&aDIR?SMBC_DIR:SMBC_FILE)) < 0) {
2488
2489                 /* Handle an error ... */
2490
2491                 /* FIXME: Add some code ... */
2492
2493         } 
2494
2495 }
2496
2497 static int
2498 net_share_enum_rpc(struct cli_state *cli,
2499                    void (*fn)(const char *name,
2500                               uint32 type,
2501                               const char *comment,
2502                               void *state),
2503                    void *state)
2504 {
2505         int i;
2506         NTSTATUS result;
2507         uint32 enum_hnd;
2508         uint32 info_level = 1;
2509         uint32 preferred_len = 0xffffffff;
2510         struct srvsvc_NetShareCtr1 ctr1;
2511         union srvsvc_NetShareCtr ctr;
2512         void *mem_ctx;
2513         struct rpc_pipe_client *pipe_hnd;
2514         uint32 numentries;
2515         NTSTATUS nt_status;
2516
2517         /* Open the server service pipe */
2518         pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SRVSVC, &nt_status);
2519         if (!pipe_hnd) {
2520                 DEBUG(1, ("net_share_enum_rpc pipe open fail!\n"));
2521                 return -1;
2522         }
2523
2524         /* Allocate a context for parsing and for the entries in "ctr" */
2525         mem_ctx = talloc_init("libsmbclient: net_share_enum_rpc");
2526         if (mem_ctx == NULL) {
2527                 DEBUG(0, ("out of memory for net_share_enum_rpc!\n"));
2528                 cli_rpc_pipe_close(pipe_hnd);
2529                 return -1; 
2530         }
2531
2532         ZERO_STRUCT(ctr1);
2533         ctr.ctr1 = &ctr1;
2534
2535         /* Issue the NetShareEnum RPC call and retrieve the response */
2536         enum_hnd = 0;
2537         result = rpccli_srvsvc_NetShareEnum(pipe_hnd, mem_ctx, NULL,
2538                                             &info_level, &ctr, preferred_len,
2539                                             &numentries, &enum_hnd);
2540
2541         /* Was it successful? */
2542         if (!NT_STATUS_IS_OK(result) || numentries == 0) {
2543                 /*  Nope.  Go clean up. */
2544                 goto done;
2545         }
2546
2547         /* For each returned entry... */
2548         for (i = 0; i < numentries; i++) {
2549
2550                 /* Add this share to the list */
2551                 (*fn)(ctr.ctr1->array[i].name, 
2552                                           ctr.ctr1->array[i].type, 
2553                                           ctr.ctr1->array[i].comment, state);
2554         }
2555
2556 done:
2557         /* Close the server service pipe */
2558         cli_rpc_pipe_close(pipe_hnd);
2559
2560         /* Free all memory which was allocated for this request */
2561         TALLOC_FREE(mem_ctx);
2562
2563         /* Tell 'em if it worked */
2564         return NT_STATUS_IS_OK(result) ? 0 : -1;
2565 }
2566
2567
2568
2569 static SMBCFILE *
2570 smbc_opendir_ctx(SMBCCTX *context,
2571                  const char *fname)
2572 {
2573         int saved_errno;
2574         fstring server, share, user, password, options;
2575         pstring workgroup;
2576         pstring path;
2577         uint16 mode;
2578         char *p;
2579         SMBCSRV *srv  = NULL;
2580         SMBCFILE *dir = NULL;
2581         struct _smbc_callbacks *cb;
2582         struct in_addr rem_ip;
2583
2584         if (!context || !context->internal ||
2585             !context->internal->_initialized) {
2586                 DEBUG(4, ("no valid context\n"));
2587                 errno = EINVAL + 8192;
2588                 return NULL;
2589
2590         }
2591
2592         if (!fname) {
2593                 DEBUG(4, ("no valid fname\n"));
2594                 errno = EINVAL + 8193;
2595                 return NULL;
2596         }
2597
2598         if (smbc_parse_path(context, fname,
2599                             workgroup, sizeof(workgroup),
2600                             server, sizeof(server),
2601                             share, sizeof(share),
2602                             path, sizeof(path),
2603                             user, sizeof(user),
2604                             password, sizeof(password),
2605                             options, sizeof(options))) {
2606                 DEBUG(4, ("no valid path\n"));
2607                 errno = EINVAL + 8194;
2608                 return NULL;
2609         }
2610
2611         DEBUG(4, ("parsed path: fname='%s' server='%s' share='%s' "
2612                   "path='%s' options='%s'\n",
2613                   fname, server, share, path, options));
2614
2615         /* Ensure the options are valid */
2616         if (smbc_check_options(server, share, path, options)) {
2617                 DEBUG(4, ("unacceptable options (%s)\n", options));
2618                 errno = EINVAL + 8195;
2619                 return NULL;
2620         }
2621
2622         if (user[0] == (char)0) fstrcpy(user, context->user);
2623
2624         dir = SMB_MALLOC_P(SMBCFILE);
2625
2626         if (!dir) {
2627
2628                 errno = ENOMEM;
2629                 return NULL;
2630
2631         }
2632
2633         ZERO_STRUCTP(dir);
2634
2635         dir->cli_fd   = 0;
2636         dir->fname    = SMB_STRDUP(fname);
2637         dir->srv      = NULL;
2638         dir->offset   = 0;
2639         dir->file     = False;
2640         dir->dir_list = dir->dir_next = dir->dir_end = NULL;
2641
2642         if (server[0] == (char)0) {
2643
2644                 int i;
2645                 int count;
2646                 int max_lmb_count;
2647                 struct ip_service *ip_list;
2648                 struct ip_service server_addr;
2649                 struct user_auth_info u_info;
2650                 struct cli_state *cli;
2651
2652                 if (share[0] != (char)0 || path[0] != (char)0) {
2653
2654                         errno = EINVAL + 8196;
2655                         if (dir) {
2656                                 SAFE_FREE(dir->fname);
2657                                 SAFE_FREE(dir);
2658                         }
2659                         return NULL;
2660                 }
2661
2662                 /* Determine how many local master browsers to query */
2663                 max_lmb_count = (context->options.browse_max_lmb_count == 0
2664                                  ? INT_MAX
2665                                  : context->options.browse_max_lmb_count);
2666
2667                 pstrcpy(u_info.username, user);
2668                 pstrcpy(u_info.password, password);
2669
2670                 /*
2671                  * We have server and share and path empty but options
2672                  * requesting that we scan all master browsers for their list
2673                  * of workgroups/domains.  This implies that we must first try
2674                  * broadcast queries to find all master browsers, and if that
2675                  * doesn't work, then try our other methods which return only
2676                  * a single master browser.
2677                  */
2678
2679                 ip_list = NULL;
2680                 if (!name_resolve_bcast(MSBROWSE, 1, &ip_list, &count)) {
2681
2682                         SAFE_FREE(ip_list);
2683
2684                         if (!find_master_ip(workgroup, &server_addr.ip)) {
2685
2686                                 if (dir) {
2687                                         SAFE_FREE(dir->fname);
2688                                         SAFE_FREE(dir);
2689                                 }
2690                                 errno = ENOENT;
2691                                 return NULL;
2692                         }
2693
2694                         ip_list = &server_addr;
2695                         count = 1;
2696                 }
2697
2698                 for (i = 0; i < count && i < max_lmb_count; i++) {
2699                         DEBUG(99, ("Found master browser %d of %d: %s\n",
2700                                    i+1, MAX(count, max_lmb_count),
2701                                    inet_ntoa(ip_list[i].ip)));
2702                         
2703                         cli = get_ipc_connect_master_ip(&ip_list[i],
2704                                                         workgroup, &u_info);
2705                         /* cli == NULL is the master browser refused to talk or 
2706                            could not be found */
2707                         if ( !cli )
2708                                 continue;
2709
2710                         fstrcpy(server, cli->desthost);
2711                         cli_shutdown(cli);
2712
2713                         DEBUG(4, ("using workgroup %s %s\n",
2714                                   workgroup, server));
2715
2716                         /*
2717                          * For each returned master browser IP address, get a
2718                          * connection to IPC$ on the server if we do not
2719                          * already have one, and determine the
2720                          * workgroups/domains that it knows about.
2721                          */
2722                 
2723                         srv = smbc_server(context, True, server, "IPC$",
2724                                           workgroup, user, password);
2725                         if (!srv) {
2726                                 continue;
2727                         }
2728                 
2729                         dir->srv = srv;
2730                         dir->dir_type = SMBC_WORKGROUP;
2731
2732                         /* Now, list the stuff ... */
2733                         
2734                         if (!cli_NetServerEnum(srv->cli,
2735                                                workgroup,
2736                                                SV_TYPE_DOMAIN_ENUM,
2737                                                list_unique_wg_fn,
2738                                                (void *)dir)) {
2739                                 continue;
2740                         }
2741                 }
2742
2743                 SAFE_FREE(ip_list);
2744         } else { 
2745                 /*
2746                  * Server not an empty string ... Check the rest and see what
2747                  * gives
2748                  */
2749                 if (*share == '\0') {
2750                         if (*path != '\0') {
2751
2752                                 /* Should not have empty share with path */
2753                                 errno = EINVAL + 8197;
2754                                 if (dir) {
2755                                         SAFE_FREE(dir->fname);
2756                                         SAFE_FREE(dir);
2757                                 }
2758                                 return NULL;
2759         
2760                         }
2761
2762                         /*
2763                          * We don't know if <server> is really a server name
2764                          * or is a workgroup/domain name.  If we already have
2765                          * a server structure for it, we'll use it.
2766                          * Otherwise, check to see if <server><1D>,
2767                          * <server><1B>, or <server><20> translates.  We check
2768                          * to see if <server> is an IP address first.
2769                          */
2770
2771                         /*
2772                          * See if we have an existing server.  Do not
2773                          * establish a connection if one does not already
2774                          * exist.
2775                          */
2776                         srv = smbc_server(context, False, server, "IPC$",
2777                                           workgroup, user, password);
2778
2779                         /*
2780                          * If no existing server and not an IP addr, look for
2781                          * LMB or DMB
2782                          */
2783                         if (!srv &&
2784                             !is_ipaddress(server) &&
2785                             (resolve_name(server, &rem_ip, 0x1d) ||   /* LMB */
2786                              resolve_name(server, &rem_ip, 0x1b) )) { /* DMB */
2787
2788                                 fstring buserver;
2789
2790                                 dir->dir_type = SMBC_SERVER;
2791
2792                                 /*
2793                                  * Get the backup list ...
2794                                  */
2795                                 if (!name_status_find(server, 0, 0,
2796                                                       rem_ip, buserver)) {
2797
2798                                         DEBUG(0, ("Could not get name of "
2799                                                   "local/domain master browser "
2800                                                   "for server %s\n", server));
2801                                         if (dir) {
2802                                                 SAFE_FREE(dir->fname);
2803                                                 SAFE_FREE(dir);
2804                                         }
2805                                         errno = EPERM;
2806                                         return NULL;
2807
2808                                 }
2809
2810                                 /*
2811                                  * Get a connection to IPC$ on the server if
2812                                  * we do not already have one
2813                                  */
2814                                 srv = smbc_server(context, True,
2815                                                   buserver, "IPC$",
2816                                                   workgroup, user, password);
2817                                 if (!srv) {
2818                                         DEBUG(0, ("got no contact to IPC$\n"));
2819                                         if (dir) {
2820                                                 SAFE_FREE(dir->fname);
2821                                                 SAFE_FREE(dir);
2822                                         }
2823                                         return NULL;
2824
2825                                 }
2826
2827                                 dir->srv = srv;
2828
2829                                 /* Now, list the servers ... */
2830                                 if (!cli_NetServerEnum(srv->cli, server,
2831                                                        0x0000FFFE, list_fn,
2832                                                        (void *)dir)) {
2833
2834                                         if (dir) {
2835                                                 SAFE_FREE(dir->fname);
2836                                                 SAFE_FREE(dir);
2837                                         }
2838                                         return NULL;
2839                                 }
2840                         } else if (srv ||
2841                                    (resolve_name(server, &rem_ip, 0x20))) {
2842                                 
2843                                 /* If we hadn't found the server, get one now */
2844                                 if (!srv) {
2845                                         srv = smbc_server(context, True,
2846                                                           server, "IPC$",
2847                                                           workgroup,
2848                                                           user, password);
2849                                 }
2850
2851                                 if (!srv) {
2852                                         if (dir) {
2853                                                 SAFE_FREE(dir->fname);
2854                                                 SAFE_FREE(dir);
2855                                         }
2856                                         return NULL;
2857
2858                                 }
2859
2860                                 dir->dir_type = SMBC_FILE_SHARE;
2861                                 dir->srv = srv;
2862
2863                                 /* List the shares ... */
2864
2865                                 if (net_share_enum_rpc(
2866                                             srv->cli,
2867                                             list_fn,
2868                                             (void *) dir) < 0 &&
2869                                     cli_RNetShareEnum(
2870                                             srv->cli,
2871                                             list_fn, 
2872                                             (void *)dir) < 0) {
2873                                                 
2874                                         errno = cli_errno(srv->cli);
2875                                         if (dir) {
2876                                                 SAFE_FREE(dir->fname);
2877                                                 SAFE_FREE(dir);
2878                                         }
2879                                         return NULL;
2880
2881                                 }
2882                         } else {
2883                                 /* Neither the workgroup nor server exists */
2884                                 errno = ECONNREFUSED;   
2885                                 if (dir) {
2886                                         SAFE_FREE(dir->fname);
2887                                         SAFE_FREE(dir);
2888                                 }
2889                                 return NULL;
2890                         }
2891
2892                 }
2893                 else {
2894                         /*
2895                          * The server and share are specified ... work from
2896                          * there ...
2897                          */
2898                         pstring targetpath;
2899                         struct cli_state *targetcli;
2900
2901                         /* We connect to the server and list the directory */
2902                         dir->dir_type = SMBC_FILE_SHARE;
2903
2904                         srv = smbc_server(context, True, server, share,
2905                                           workgroup, user, password);
2906
2907                         if (!srv) {
2908
2909                                 if (dir) {
2910                                         SAFE_FREE(dir->fname);
2911                                         SAFE_FREE(dir);
2912                                 }
2913                                 return NULL;
2914
2915                         }
2916
2917                         dir->srv = srv;
2918
2919                         /* Now, list the files ... */
2920
2921                         p = path + strlen(path);
2922                         pstrcat(path, "\\*");
2923
2924                         if (!cli_resolve_path("", srv->cli, path,
2925                                               &targetcli, targetpath))
2926                         {
2927                                 d_printf("Could not resolve %s\n", path);
2928                                 if (dir) {
2929                                         SAFE_FREE(dir->fname);
2930                                         SAFE_FREE(dir);
2931                                 }
2932                                 return NULL;
2933                         }
2934                         
2935                         if (cli_list(targetcli, targetpath,
2936                                      aDIR | aSYSTEM | aHIDDEN,
2937                                      dir_list_fn, (void *)dir) < 0) {
2938
2939                                 if (dir) {
2940                                         SAFE_FREE(dir->fname);
2941                                         SAFE_FREE(dir);
2942                                 }
2943                                 saved_errno = smbc_errno(context, targetcli);
2944
2945                                 if (saved_errno == EINVAL) {
2946                                     /*
2947                                      * See if they asked to opendir something
2948                                      * other than a directory.  If so, the
2949                                      * converted error value we got would have
2950                                      * been EINVAL rather than ENOTDIR.
2951                                      */
2952                                     *p = '\0'; /* restore original path */
2953
2954                                     if (smbc_getatr(context, srv, path,
2955                                                     &mode, NULL,
2956                                                     NULL, NULL, NULL, NULL,
2957                                                     NULL) &&
2958                                         ! IS_DOS_DIR(mode)) {
2959
2960                                         /* It is.  Correct the error value */
2961                                         saved_errno = ENOTDIR;
2962                                     }
2963                                 }
2964
2965                                 /*
2966                                  * If there was an error and the server is no
2967                                  * good any more...
2968                                  */
2969                                 cb = &context->callbacks;
2970                                 if (cli_is_error(targetcli) &&
2971                                     cb->check_server_fn(context, srv)) {
2972
2973                                     /* ... then remove it. */
2974                                     if (cb->remove_unused_server_fn(context,
2975                                                                     srv)) { 
2976                                         /*
2977                                          * We could not remove the server
2978                                          * completely, remove it from the
2979                                          * cache so we will not get it
2980                                          * again. It will be removed when the
2981                                          * last file/dir is closed.
2982                                          */
2983                                         cb->remove_cached_srv_fn(context, srv);
2984                                     }
2985                                 }
2986
2987                                 errno = saved_errno;
2988                                 return NULL;
2989                         }
2990                 }
2991
2992         }
2993
2994         DLIST_ADD(context->internal->_files, dir);
2995         return dir;
2996
2997 }
2998
2999 /*
3000  * Routine to close a directory
3001  */
3002
3003 static int
3004 smbc_closedir_ctx(SMBCCTX *context,
3005                   SMBCFILE *dir)
3006 {
3007
3008         if (!context || !context->internal ||
3009             !context->internal->_initialized) {
3010
3011                 errno = EINVAL;
3012                 return -1;
3013
3014         }
3015
3016         if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
3017
3018                 errno = EBADF;
3019                 return -1;
3020     
3021         }
3022
3023         smbc_remove_dir(dir); /* Clean it up */
3024
3025         DLIST_REMOVE(context->internal->_files, dir);
3026
3027         if (dir) {
3028
3029                 SAFE_FREE(dir->fname);
3030                 SAFE_FREE(dir);    /* Free the space too */
3031         }
3032
3033         return 0;
3034
3035 }
3036
3037 static void
3038 smbc_readdir_internal(SMBCCTX * context,
3039                       struct smbc_dirent *dest,
3040                       struct smbc_dirent *src,
3041                       int max_namebuf_len)
3042 {
3043         if (context->options.urlencode_readdir_entries) {
3044
3045                 /* url-encode the name.  get back remaining buffer space */
3046                 max_namebuf_len =
3047                         smbc_urlencode(dest->name, src->name, max_namebuf_len);
3048
3049                 /* We now know the name length */
3050                 dest->namelen = strlen(dest->name);
3051
3052                 /* Save the pointer to the beginning of the comment */
3053                 dest->comment = dest->name + dest->namelen + 1;
3054
3055                 /* Copy the comment */
3056                 strncpy(dest->comment, src->comment, max_namebuf_len - 1);
3057                 dest->comment[max_namebuf_len - 1] = '\0';
3058
3059                 /* Save other fields */
3060                 dest->smbc_type = src->smbc_type;
3061                 dest->commentlen = strlen(dest->comment);
3062                 dest->dirlen = ((dest->comment + dest->commentlen + 1) -
3063                                 (char *) dest);
3064         } else {
3065
3066                 /* No encoding.  Just copy the entry as is. */
3067                 memcpy(dest, src, src->dirlen);
3068                 dest->comment = (char *)(&dest->name + src->namelen + 1);
3069         }
3070         
3071 }
3072
3073 /*
3074  * Routine to get a directory entry
3075  */
3076
3077 struct smbc_dirent *
3078 smbc_readdir_ctx(SMBCCTX *context,
3079                  SMBCFILE *dir)
3080 {
3081         int maxlen;
3082         struct smbc_dirent *dirp, *dirent;
3083
3084         /* Check that all is ok first ... */
3085
3086         if (!context || !context->internal ||
3087             !context->internal->_initialized) {
3088
3089                 errno = EINVAL;
3090                 DEBUG(0, ("Invalid context in smbc_readdir_ctx()\n"));
3091                 return NULL;
3092
3093         }
3094
3095         if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
3096
3097                 errno = EBADF;
3098                 DEBUG(0, ("Invalid dir in smbc_readdir_ctx()\n"));
3099                 return NULL;
3100
3101         }
3102
3103         if (dir->file != False) { /* FIXME, should be dir, perhaps */
3104
3105                 errno = ENOTDIR;
3106                 DEBUG(0, ("Found file vs directory in smbc_readdir_ctx()\n"));
3107                 return NULL;
3108
3109         }
3110
3111         if (!dir->dir_next) {
3112                 return NULL;
3113         }
3114
3115         dirent = dir->dir_next->dirent;
3116         if (!dirent) {
3117
3118                 errno = ENOENT;
3119                 return NULL;
3120
3121         }
3122
3123         dirp = (struct smbc_dirent *)context->internal->_dirent;
3124         maxlen = (sizeof(context->internal->_dirent) -
3125                   sizeof(struct smbc_dirent));
3126
3127         smbc_readdir_internal(context, dirp, dirent, maxlen);
3128
3129         dir->dir_next = dir->dir_next->next;
3130
3131         return dirp;
3132 }
3133
3134 /*
3135  * Routine to get directory entries
3136  */
3137
3138 static int
3139 smbc_getdents_ctx(SMBCCTX *context,
3140                   SMBCFILE *dir,
3141                   struct smbc_dirent *dirp,
3142                   int count)
3143 {
3144         int rem = count;
3145         int reqd;
3146         int maxlen;
3147         char *ndir = (char *)dirp;
3148         struct smbc_dir_list *dirlist;
3149
3150         /* Check that all is ok first ... */
3151
3152         if (!context || !context->internal ||
3153             !context->internal->_initialized) {
3154
3155                 errno = EINVAL;
3156                 return -1;
3157
3158         }
3159
3160         if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
3161
3162                 errno = EBADF;
3163                 return -1;
3164     
3165         }
3166
3167         if (dir->file != False) { /* FIXME, should be dir, perhaps */
3168
3169                 errno = ENOTDIR;
3170                 return -1;
3171
3172         }
3173
3174         /* 
3175          * Now, retrieve the number of entries that will fit in what was passed
3176          * We have to figure out if the info is in the list, or we need to 
3177          * send a request to the server to get the info.
3178          */
3179
3180         while ((dirlist = dir->dir_next)) {
3181                 struct smbc_dirent *dirent;
3182
3183                 if (!dirlist->dirent) {
3184
3185                         errno = ENOENT;  /* Bad error */
3186                         return -1;
3187
3188                 }
3189
3190                 /* Do urlencoding of next entry, if so selected */
3191                 dirent = (struct smbc_dirent *)context->internal->_dirent;
3192                 maxlen = (sizeof(context->internal->_dirent) -
3193                           sizeof(struct smbc_dirent));
3194                 smbc_readdir_internal(context, dirent, dirlist->dirent, maxlen);
3195
3196                 reqd = dirent->dirlen;
3197
3198                 if (rem < reqd) {
3199
3200                         if (rem < count) { /* We managed to copy something */
3201
3202                                 errno = 0;
3203                                 return count - rem;
3204
3205                         }
3206                         else { /* Nothing copied ... */
3207
3208                                 errno = EINVAL;  /* Not enough space ... */
3209                                 return -1;
3210
3211                         }
3212
3213                 }
3214
3215                 memcpy(ndir, dirent, reqd); /* Copy the data in ... */
3216     
3217                 ((struct smbc_dirent *)ndir)->comment = 
3218                         (char *)(&((struct smbc_dirent *)ndir)->name +
3219                                  dirent->namelen +
3220                                  1);
3221
3222                 ndir += reqd;
3223
3224                 rem -= reqd;
3225
3226                 dir->dir_next = dirlist = dirlist -> next;
3227         }
3228
3229         if (rem == count)
3230                 return 0;
3231         else 
3232                 return count - rem;
3233
3234 }
3235
3236 /*
3237  * Routine to create a directory ...
3238  */
3239
3240 static int
3241 smbc_mkdir_ctx(SMBCCTX *context,
3242                const char *fname,
3243                mode_t mode)
3244 {
3245         SMBCSRV *srv;
3246         fstring server;
3247         fstring share;
3248         fstring user;
3249         fstring password;
3250         fstring workgroup;
3251         pstring path, targetpath;
3252         struct cli_state *targetcli;
3253
3254         if (!context || !context->internal || 
3255             !context->internal->_initialized) {
3256
3257                 errno = EINVAL;
3258                 return -1;
3259
3260         }
3261
3262         if (!fname) {
3263
3264                 errno = EINVAL;
3265                 return -1;
3266
3267         }
3268   
3269         DEBUG(4, ("smbc_mkdir(%s)\n", fname));
3270
3271         if (smbc_parse_path(context, fname,
3272                             workgroup, sizeof(workgroup),
3273                             server, sizeof(server),
3274                             share, sizeof(share),
3275                             path, sizeof(path),
3276                             user, sizeof(user),
3277                             password, sizeof(password),
3278                             NULL, 0)) {
3279                 errno = EINVAL;
3280                 return -1;
3281         }
3282
3283         if (user[0] == (char)0) fstrcpy(user, context->user);
3284
3285         srv = smbc_server(context, True,
3286                           server, share, workgroup, user, password);
3287
3288         if (!srv) {
3289
3290                 return -1;  /* errno set by smbc_server */
3291
3292         }
3293
3294         /*d_printf(">>>mkdir: resolving %s\n", path);*/
3295         if (!cli_resolve_path( "", srv->cli, path, &targetcli, targetpath))
3296         {
3297                 d_printf("Could not resolve %s\n", path);
3298                 return -1;
3299         }
3300         /*d_printf(">>>mkdir: resolved path as %s\n", targetpath);*/
3301
3302         if (!cli_mkdir(targetcli, targetpath)) {
3303
3304                 errno = smbc_errno(context, targetcli);
3305                 return -1;
3306
3307         } 
3308
3309         return 0;
3310
3311 }
3312
3313 /*
3314  * Our list function simply checks to see if a directory is not empty
3315  */
3316
3317 static int smbc_rmdir_dirempty = True;
3318
3319 static void
3320 rmdir_list_fn(const char *mnt,
3321               file_info *finfo,
3322               const char *mask,
3323               void *state)
3324 {
3325         if (strncmp(finfo->name, ".", 1) != 0 &&
3326             strncmp(finfo->name, "..", 2) != 0) {
3327                 
3328                 smbc_rmdir_dirempty = False;
3329         }
3330 }
3331
3332 /*
3333  * Routine to remove a directory
3334  */
3335
3336 static int
3337 smbc_rmdir_ctx(SMBCCTX *context,
3338                const char *fname)
3339 {
3340         SMBCSRV *srv;
3341         fstring server;
3342         fstring share;
3343         fstring user;
3344         fstring password;
3345         fstring workgroup;
3346         pstring path;
3347         pstring targetpath;
3348         struct cli_state *targetcli;
3349
3350         if (!context || !context->internal || 
3351             !context->internal->_initialized) {
3352
3353                 errno = EINVAL;
3354                 return -1;
3355
3356         }
3357
3358         if (!fname) {
3359
3360                 errno = EINVAL;
3361                 return -1;
3362
3363         }
3364   
3365         DEBUG(4, ("smbc_rmdir(%s)\n", fname));
3366
3367         if (smbc_parse_path(context, fname,
3368                             workgroup, sizeof(workgroup),
3369                             server, sizeof(server),
3370                             share, sizeof(share),
3371                             path, sizeof(path),
3372                             user, sizeof(user),
3373                             password, sizeof(password),
3374                             NULL, 0))
3375         {
3376                 errno = EINVAL;
3377                 return -1;
3378         }
3379
3380         if (user[0] == (char)0) fstrcpy(user, context->user);
3381
3382         srv = smbc_server(context, True,
3383                           server, share, workgroup, user, password);
3384
3385         if (!srv) {
3386
3387                 return -1;  /* errno set by smbc_server */
3388
3389         }
3390
3391         /*d_printf(">>>rmdir: resolving %s\n", path);*/
3392         if (!cli_resolve_path( "", srv->cli, path, &targetcli, targetpath))
3393         {
3394                 d_printf("Could not resolve %s\n", path);
3395                 return -1;
3396         }
3397         /*d_printf(">>>rmdir: resolved path as %s\n", targetpath);*/
3398
3399
3400         if (!cli_rmdir(targetcli, targetpath)) {
3401
3402                 errno = smbc_errno(context, targetcli);
3403
3404                 if (errno == EACCES) {  /* Check if the dir empty or not */
3405
3406                         /* Local storage to avoid buffer overflows */
3407                         pstring lpath; 
3408
3409                         smbc_rmdir_dirempty = True;  /* Make this so ... */
3410
3411                         pstrcpy(lpath, targetpath);
3412                         pstrcat(lpath, "\\*");
3413
3414                         if (cli_list(targetcli, lpath,
3415                                      aDIR | aSYSTEM | aHIDDEN,
3416                                      rmdir_list_fn, NULL) < 0) {
3417
3418                                 /* Fix errno to ignore latest error ... */
3419                                 DEBUG(5, ("smbc_rmdir: "
3420                                           "cli_list returned an error: %d\n", 
3421                                           smbc_errno(context, targetcli)));
3422                                 errno = EACCES;
3423
3424                         }
3425
3426                         if (smbc_rmdir_dirempty)
3427                                 errno = EACCES;
3428                         else
3429                                 errno = ENOTEMPTY;
3430
3431                 }
3432
3433                 return -1;
3434
3435         } 
3436
3437         return 0;
3438
3439 }
3440
3441 /*
3442  * Routine to return the current directory position
3443  */
3444
3445 static off_t
3446 smbc_telldir_ctx(SMBCCTX *context,
3447                  SMBCFILE *dir)
3448 {
3449         off_t ret_val; /* Squash warnings about cast */
3450
3451         if (!context || !context->internal ||
3452             !context->internal->_initialized) {
3453
3454                 errno = EINVAL;
3455                 return -1;
3456
3457         }
3458
3459         if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
3460
3461                 errno = EBADF;
3462                 return -1;
3463
3464         }
3465
3466         if (dir->file != False) { /* FIXME, should be dir, perhaps */
3467
3468                 errno = ENOTDIR;
3469                 return -1;
3470
3471         }
3472
3473         /*
3474          * We return the pointer here as the offset
3475          */
3476         ret_val = (off_t)(long)dir->dir_next;
3477         return ret_val;
3478
3479 }
3480
3481 /*
3482  * A routine to run down the list and see if the entry is OK
3483  */
3484
3485 struct smbc_dir_list *
3486 smbc_check_dir_ent(struct smbc_dir_list *list, 
3487                    struct smbc_dirent *dirent)
3488 {
3489
3490         /* Run down the list looking for what we want */
3491
3492         if (dirent) {
3493
3494                 struct smbc_dir_list *tmp = list;
3495
3496                 while (tmp) {
3497
3498                         if (tmp->dirent == dirent)
3499                                 return tmp;
3500
3501                         tmp = tmp->next;
3502
3503                 }
3504
3505         }
3506
3507         return NULL;  /* Not found, or an error */
3508
3509 }
3510
3511
3512 /*
3513  * Routine to seek on a directory
3514  */
3515
3516 static int
3517 smbc_lseekdir_ctx(SMBCCTX *context,
3518                   SMBCFILE *dir,
3519                   off_t offset)
3520 {
3521         long int l_offset = offset;  /* Handle problems of size */
3522         struct smbc_dirent *dirent = (struct smbc_dirent *)l_offset;
3523         struct smbc_dir_list *list_ent = (struct smbc_dir_list *)NULL;
3524
3525         if (!context || !context->internal ||
3526             !context->internal->_initialized) {
3527
3528                 errno = EINVAL;
3529                 return -1;
3530
3531         }
3532
3533         if (dir->file != False) { /* FIXME, should be dir, perhaps */
3534
3535                 errno = ENOTDIR;
3536                 return -1;
3537
3538         }
3539
3540         /* Now, check what we were passed and see if it is OK ... */
3541
3542         if (dirent == NULL) {  /* Seek to the begining of the list */
3543
3544                 dir->dir_next = dir->dir_list;
3545                 return 0;
3546
3547         }
3548
3549         /* Now, run down the list and make sure that the entry is OK       */
3550         /* This may need to be changed if we change the format of the list */
3551
3552         if ((list_ent = smbc_check_dir_ent(dir->dir_list, dirent)) == NULL) {
3553
3554                 errno = EINVAL;   /* Bad entry */
3555                 return -1;
3556
3557         }
3558
3559         dir->dir_next = list_ent;
3560
3561         return 0; 
3562
3563 }
3564
3565 /*
3566  * Routine to fstat a dir
3567  */
3568
3569 static int
3570 smbc_fstatdir_ctx(SMBCCTX *context,
3571                   SMBCFILE *dir,
3572                   struct stat *st)
3573 {
3574
3575         if (!context || !context->internal || 
3576             !context->internal->_initialized) {
3577
3578                 errno = EINVAL;
3579                 return -1;
3580
3581         }
3582
3583         /* No code yet ... */
3584
3585         return 0;
3586
3587 }
3588
3589 static int
3590 smbc_chmod_ctx(SMBCCTX *context,
3591                const char *fname,
3592                mode_t newmode)
3593 {
3594         SMBCSRV *srv;
3595         fstring server;
3596         fstring share;
3597         fstring user;
3598         fstring password;
3599         fstring workgroup;
3600         pstring path;
3601         uint16 mode;
3602
3603         if (!context || !context->internal ||
3604             !context->internal->_initialized) {
3605
3606                 errno = EINVAL;  /* Best I can think of ... */
3607                 return -1;
3608     
3609         }
3610
3611         if (!fname) {
3612
3613                 errno = EINVAL;
3614                 return -1;
3615
3616         }
3617   
3618         DEBUG(4, ("smbc_chmod(%s, 0%3o)\n", fname, newmode));
3619
3620         if (smbc_parse_path(context, fname,
3621                             workgroup, sizeof(workgroup),
3622                             server, sizeof(server),
3623                             share, sizeof(share),
3624                             path, sizeof(path),
3625                             user, sizeof(user),
3626                             password, sizeof(password),
3627                             NULL, 0)) {
3628                 errno = EINVAL;
3629                 return -1;
3630         }
3631
3632         if (user[0] == (char)0) fstrcpy(user, context->user);
3633
3634         srv = smbc_server(context, True,
3635                           server, share, workgroup, user, password);
3636
3637         if (!srv) {
3638                 return -1;  /* errno set by smbc_server */
3639         }
3640
3641         mode = 0;
3642
3643         if (!(newmode & (S_IWUSR | S_IWGRP | S_IWOTH))) mode |= aRONLY;
3644         if ((newmode & S_IXUSR) && lp_map_archive(-1)) mode |= aARCH;
3645         if ((newmode & S_IXGRP) && lp_map_system(-1)) mode |= aSYSTEM;
3646         if ((newmode & S_IXOTH) && lp_map_hidden(-1)) mode |= aHIDDEN;
3647
3648         if (!cli_setatr(srv->cli, path, mode, 0)) {
3649                 errno = smbc_errno(context, srv->cli);
3650                 return -1;
3651         }
3652         
3653         return 0;
3654 }
3655
3656 static int
3657 smbc_utimes_ctx(SMBCCTX *context,
3658                 const char *fname,
3659                 struct timeval *tbuf)
3660 {
3661         SMBCSRV *srv;
3662         fstring server;
3663         fstring share;
3664         fstring user;
3665         fstring password;
3666         fstring workgroup;
3667         pstring path;
3668         time_t access_time;
3669         time_t write_time;
3670
3671         if (!context || !context->internal ||
3672             !context->internal->_initialized) {
3673
3674                 errno = EINVAL;  /* Best I can think of ... */
3675                 return -1;
3676     
3677         }
3678
3679         if (!fname) {
3680
3681                 errno = EINVAL;
3682                 return -1;
3683
3684         }
3685   
3686         if (tbuf == NULL) {
3687                 access_time = write_time = time(NULL);
3688         } else {
3689                 access_time = tbuf[0].tv_sec;
3690                 write_time = tbuf[1].tv_sec;
3691         }
3692
3693         if (DEBUGLVL(4)) 
3694         {
3695                 char *p;
3696                 char atimebuf[32];
3697                 char mtimebuf[32];
3698
3699                 strncpy(atimebuf, ctime(&access_time), sizeof(atimebuf) - 1);
3700                 atimebuf[sizeof(atimebuf) - 1] = '\0';
3701                 if ((p = strchr(atimebuf, '\n')) != NULL) {
3702                         *p = '\0';
3703                 }
3704
3705                 strncpy(mtimebuf, ctime(&write_time), sizeof(mtimebuf) - 1);
3706                 mtimebuf[sizeof(mtimebuf) - 1] = '\0';
3707                 if ((p = strchr(mtimebuf, '\n')) != NULL) {
3708                         *p = '\0';
3709                 }
3710
3711                 dbgtext("smbc_utimes(%s, atime = %s mtime = %s)\n",
3712                         fname, atimebuf, mtimebuf);
3713         }
3714
3715         if (smbc_parse_path(context, fname,
3716                             workgroup, sizeof(workgroup),
3717                             server, sizeof(server),
3718                             share, sizeof(share),
3719                             path, sizeof(path),
3720                             user, sizeof(user),
3721                             password, sizeof(password),
3722                             NULL, 0)) {
3723                 errno = EINVAL;
3724                 return -1;
3725         }
3726
3727         if (user[0] == (char)0) fstrcpy(user, context->user);
3728
3729         srv = smbc_server(context, True,
3730                           server, share, workgroup, user, password);
3731
3732         if (!srv) {
3733                 return -1;      /* errno set by smbc_server */
3734         }
3735
3736         if (!smbc_setatr(context, srv, path,
3737                          0, access_time, write_time, 0, 0)) {
3738                 return -1;      /* errno set by smbc_setatr */
3739         }
3740
3741         return 0;
3742 }
3743
3744
3745 /* The MSDN is contradictory over the ordering of ACE entries in an ACL.
3746    However NT4 gives a "The information may have been modified by a
3747    computer running Windows NT 5.0" if denied ACEs do not appear before
3748    allowed ACEs. */
3749
3750 static int
3751 ace_compare(SEC_ACE *ace1,
3752             SEC_ACE *ace2)
3753 {
3754         if (sec_ace_equal(ace1, ace2)) 
3755                 return 0;
3756
3757         if (ace1->type != ace2->type) 
3758                 return ace2->type - ace1->type;
3759
3760         if (sid_compare(&ace1->trustee, &ace2->trustee)) 
3761                 return sid_compare(&ace1->trustee, &ace2->trustee);
3762
3763         if (ace1->flags != ace2->flags) 
3764                 return ace1->flags - ace2->flags;
3765
3766         if (ace1->access_mask != ace2->access_mask) 
3767                 return ace1->access_mask - ace2->access_mask;
3768
3769         if (ace1->size != ace2->size) 
3770                 return ace1->size - ace2->size;
3771
3772         return memcmp(ace1, ace2, sizeof(SEC_ACE));
3773 }
3774
3775
3776 static void
3777 sort_acl(SEC_ACL *the_acl)
3778 {
3779         uint32 i;
3780         if (!the_acl) return;
3781
3782         qsort(the_acl->aces, the_acl->num_aces, sizeof(the_acl->aces[0]),
3783               QSORT_CAST ace_compare);
3784
3785         for (i=1;i<the_acl->num_aces;) {
3786                 if (sec_ace_equal(&the_acl->aces[i-1], &the_acl->aces[i])) {
3787                         int j;
3788                         for (j=i; j<the_acl->num_aces-1; j++) {
3789                                 the_acl->aces[j] = the_acl->aces[j+1];
3790                         }
3791                         the_acl->num_aces--;
3792                 } else {
3793                         i++;
3794                 }
3795         }
3796 }
3797
3798 /* convert a SID to a string, either numeric or username/group */
3799 static void
3800 convert_sid_to_string(struct cli_state *ipc_cli,
3801                       POLICY_HND *pol,
3802                       fstring str,
3803                       BOOL numeric,
3804                       DOM_SID *sid)
3805 {
3806         char **domains = NULL;
3807         char **names = NULL;
3808         enum lsa_SidType *types = NULL;
3809         struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli);
3810         sid_to_string(str, sid);
3811
3812         if (numeric) {
3813                 return;     /* no lookup desired */
3814         }
3815        
3816         if (!pipe_hnd) {
3817                 return;
3818         }
3819  
3820         /* Ask LSA to convert the sid to a name */
3821
3822         if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_sids(pipe_hnd, ipc_cli->mem_ctx,  
3823                                                  pol, 1, sid, &domains, 
3824                                                  &names, &types)) ||
3825             !domains || !domains[0] || !names || !names[0]) {
3826                 return;
3827         }
3828
3829         /* Converted OK */
3830
3831         slprintf(str, sizeof(fstring) - 1, "%s%s%s",
3832                  domains[0], lp_winbind_separator(),
3833                  names[0]);
3834 }
3835
3836 /* convert a string to a SID, either numeric or username/group */
3837 static BOOL
3838 convert_string_to_sid(struct cli_state *ipc_cli,
3839                       POLICY_HND *pol,
3840                       BOOL numeric,
3841                       DOM_SID *sid,
3842                       const char *str)
3843 {
3844         enum lsa_SidType *types = NULL;
3845         DOM_SID *sids = NULL;
3846         BOOL result = True;
3847         struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli);
3848
3849         if (!pipe_hnd) {
3850                 return False;
3851         }
3852
3853         if (numeric) {
3854                 if (strncmp(str, "S-", 2) == 0) {
3855                         return string_to_sid(sid, str);
3856                 }
3857
3858                 result = False;
3859                 goto done;
3860         }
3861
3862         if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_names(pipe_hnd, ipc_cli->mem_ctx, 
3863                                                   pol, 1, &str, NULL, &sids, 
3864                                                   &types))) {
3865                 result = False;
3866                 goto done;
3867         }
3868
3869         sid_copy(sid, &sids[0]);
3870  done:
3871
3872         return result;
3873 }
3874
3875
3876 /* parse an ACE in the same format as print_ace() */
3877 static BOOL
3878 parse_ace(struct cli_state *ipc_cli,
3879           POLICY_HND *pol,
3880           SEC_ACE *ace,
3881           BOOL numeric,
3882           char *str)
3883 {
3884         char *p;
3885         const char *cp;
3886         fstring tok;
3887         unsigned int atype;
3888         unsigned int aflags;
3889         unsigned int amask;
3890         DOM_SID sid;
3891         SEC_ACCESS mask;
3892         const struct perm_value *v;
3893         struct perm_value {
3894                 const char *perm;
3895                 uint32 mask;
3896         };
3897
3898         /* These values discovered by inspection */
3899         static const struct perm_value special_values[] = {
3900                 { "R", 0x00120089 },
3901                 { "W", 0x00120116 },
3902                 { "X", 0x001200a0 },
3903                 { "D", 0x00010000 },
3904                 { "P", 0x00040000 },
3905                 { "O", 0x00080000 },
3906                 { NULL, 0 },
3907         };
3908
3909         static const struct perm_value standard_values[] = {
3910                 { "READ",   0x001200a9 },
3911                 { "CHANGE", 0x001301bf },
3912                 { "FULL",   0x001f01ff },
3913                 { NULL, 0 },
3914         };
3915
3916
3917         ZERO_STRUCTP(ace);
3918         p = strchr_m(str,':');
3919         if (!p) return False;
3920         *p = '\0';
3921         p++;
3922         /* Try to parse numeric form */
3923
3924         if (sscanf(p, "%i/%i/%i", &atype, &aflags, &amask) == 3 &&
3925             convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
3926                 goto done;
3927         }
3928
3929         /* Try to parse text form */
3930
3931         if (!convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
3932                 return False;
3933         }
3934
3935         cp = p;
3936         if (!next_token(&cp, tok, "/", sizeof(fstring))) {
3937                 return False;
3938         }
3939
3940         if (StrnCaseCmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
3941                 atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
3942         } else if (StrnCaseCmp(tok, "DENIED", strlen("DENIED")) == 0) {
3943                 atype = SEC_ACE_TYPE_ACCESS_DENIED;
3944         } else {
3945                 return False;
3946         }
3947
3948         /* Only numeric form accepted for flags at present */
3949
3950         if (!(next_token(&cp, tok, "/", sizeof(fstring)) &&
3951               sscanf(tok, "%i", &aflags))) {
3952                 return False;
3953         }
3954
3955         if (!next_token(&cp, tok, "/", sizeof(fstring))) {
3956                 return False;
3957         }
3958
3959         if (strncmp(tok, "0x", 2) == 0) {
3960                 if (sscanf(tok, "%i", &amask) != 1) {
3961                         return False;
3962                 }
3963                 goto done;
3964         }
3965
3966         for (v = standard_values; v->perm; v++) {
3967                 if (strcmp(tok, v->perm) == 0) {
3968                         amask = v->mask;
3969                         goto done;
3970                 }
3971         }
3972
3973         p = tok;
3974
3975         while(*p) {
3976                 BOOL found = False;
3977
3978                 for (v = special_values; v->perm; v++) {
3979                         if (v->perm[0] == *p) {
3980                                 amask |= v->mask;
3981                                 found = True;
3982                         }
3983                 }
3984
3985                 if (!found) return False;
3986                 p++;
3987         }
3988
3989         if (*p) {
3990                 return False;
3991         }
3992
3993  done:
3994         mask = amask;
3995         init_sec_ace(ace, &sid, atype, mask, aflags);
3996         return True;
3997 }
3998
3999 /* add an ACE to a list of ACEs in a SEC_ACL */
4000 static BOOL
4001 add_ace(SEC_ACL **the_acl,
4002         SEC_ACE *ace,
4003         TALLOC_CTX *ctx)
4004 {
4005         SEC_ACL *newacl;
4006         SEC_ACE *aces;
4007
4008         if (! *the_acl) {
4009                 (*the_acl) = make_sec_acl(ctx, 3, 1, ace);
4010                 return True;
4011         }
4012
4013         if ((aces = SMB_CALLOC_ARRAY(SEC_ACE, 1+(*the_acl)->num_aces)) == NULL) {
4014                 return False;
4015         }
4016         memcpy(aces, (*the_acl)->aces, (*the_acl)->num_aces * sizeof(SEC_ACE));
4017         memcpy(aces+(*the_acl)->num_aces, ace, sizeof(SEC_ACE));
4018         newacl = make_sec_acl(ctx, (*the_acl)->revision,
4019                               1+(*the_acl)->num_aces, aces);
4020         SAFE_FREE(aces);
4021         (*the_acl) = newacl;
4022         return True;
4023 }
4024
4025
4026 /* parse a ascii version of a security descriptor */
4027 static SEC_DESC *
4028 sec_desc_parse(TALLOC_CTX *ctx,
4029                struct cli_state *ipc_cli,
4030                POLICY_HND *pol,
4031                BOOL numeric,
4032                char *str)
4033 {
4034         const char *p = str;
4035         fstring tok;
4036         SEC_DESC *ret = NULL;
4037         size_t sd_size;
4038         DOM_SID *grp_sid=NULL;
4039         DOM_SID *owner_sid=NULL;
4040         SEC_ACL *dacl=NULL;
4041         int revision=1;
4042
4043         while (next_token(&p, tok, "\t,\r\n", sizeof(tok))) {
4044
4045                 if (StrnCaseCmp(tok,"REVISION:", 9) == 0) {
4046                         revision = strtol(tok+9, NULL, 16);
4047                         continue;
4048                 }
4049
4050                 if (StrnCaseCmp(tok,"OWNER:", 6) == 0) {
4051                         if (owner_sid) {
4052                                 DEBUG(5, ("OWNER specified more than once!\n"));
4053                                 goto done;
4054                         }
4055                         owner_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
4056                         if (!owner_sid ||
4057                             !convert_string_to_sid(ipc_cli, pol,
4058                                                    numeric,
4059                                                    owner_sid, tok+6)) {
4060                                 DEBUG(5, ("Failed to parse owner sid\n"));
4061                                 goto done;
4062                         }
4063                         continue;
4064                 }
4065
4066                 if (StrnCaseCmp(tok,"OWNER+:", 7) == 0) {
4067                         if (owner_sid) {
4068                                 DEBUG(5, ("OWNER specified more than once!\n"));
4069                                 goto done;
4070                         }
4071                         owner_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
4072                         if (!owner_sid ||
4073                             !convert_string_to_sid(ipc_cli, pol,
4074                                                    False,
4075                                                    owner_sid, tok+7)) {
4076                                 DEBUG(5, ("Failed to parse owner sid\n"));
4077                                 goto done;
4078                         }
4079                         continue;
4080                 }
4081
4082                 if (StrnCaseCmp(tok,"GROUP:", 6) == 0) {
4083                         if (grp_sid) {
4084                                 DEBUG(5, ("GROUP specified more than once!\n"));
4085                                 goto done;
4086                         }
4087                         grp_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
4088                         if (!grp_sid ||
4089                             !convert_string_to_sid(ipc_cli, pol,
4090                                                    numeric,
4091                                                    grp_sid, tok+6)) {
4092                                 DEBUG(5, ("Failed to parse group sid\n"));
4093                                 goto done;
4094                         }
4095                         continue;
4096                 }
4097
4098                 if (StrnCaseCmp(tok,"GROUP+:", 7) == 0) {
4099                         if (grp_sid) {
4100                                 DEBUG(5, ("GROUP specified more than once!\n"));
4101                                 goto done;
4102                         }
4103                         grp_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
4104                         if (!grp_sid ||
4105                             !convert_string_to_sid(ipc_cli, pol,
4106                                                    False,
4107                                                    grp_sid, tok+6)) {
4108                                 DEBUG(5, ("Failed to parse group sid\n"));
4109                                 goto done;
4110                         }
4111                         continue;
4112                 }
4113
4114                 if (StrnCaseCmp(tok,"ACL:", 4) == 0) {
4115                         SEC_ACE ace;
4116                         if (!parse_ace(ipc_cli, pol, &ace, numeric, tok+4)) {
4117                                 DEBUG(5, ("Failed to parse ACL %s\n", tok));
4118                                 goto done;
4119                         }
4120                         if(!add_ace(&dacl, &ace, ctx)) {
4121                                 DEBUG(5, ("Failed to add ACL %s\n", tok));
4122                                 goto done;
4123                         }
4124                         continue;
4125                 }
4126
4127                 if (StrnCaseCmp(tok,"ACL+:", 5) == 0) {
4128                         SEC_ACE ace;
4129                         if (!parse_ace(ipc_cli, pol, &ace, False, tok+5)) {
4130                                 DEBUG(5, ("Failed to parse ACL %s\n", tok));
4131                                 goto done;
4132                         }
4133                         if(!add_ace(&dacl, &ace, ctx)) {
4134                                 DEBUG(5, ("Failed to add ACL %s\n", tok));
4135                                 goto done;
4136                         }
4137                         continue;
4138                 }
4139
4140                 DEBUG(5, ("Failed to parse security descriptor\n"));
4141                 goto done;
4142         }
4143
4144         ret = make_sec_desc(ctx, revision, SEC_DESC_SELF_RELATIVE, 
4145                             owner_sid, grp_sid, NULL, dacl, &sd_size);
4146
4147   done:
4148         SAFE_FREE(grp_sid);
4149         SAFE_FREE(owner_sid);
4150
4151         return ret;
4152 }
4153
4154
4155 /* Obtain the current dos attributes */
4156 static DOS_ATTR_DESC *
4157 dos_attr_query(SMBCCTX *context,
4158                TALLOC_CTX *ctx,
4159                const char *filename,
4160                SMBCSRV *srv)
4161 {
4162         struct timespec create_time_ts;
4163         struct timespec write_time_ts;
4164         struct timespec access_time_ts;
4165         struct timespec change_time_ts;
4166         SMB_OFF_T size = 0;
4167         uint16 mode = 0;
4168         SMB_INO_T inode = 0;
4169         DOS_ATTR_DESC *ret;
4170     
4171         ret = TALLOC_P(ctx, DOS_ATTR_DESC);
4172         if (!ret) {
4173                 errno = ENOMEM;
4174                 return NULL;
4175         }
4176
4177         /* Obtain the DOS attributes */
4178         if (!smbc_getatr(context, srv, CONST_DISCARD(char *, filename),
4179                          &mode, &size, 
4180                          &create_time_ts,
4181                          &access_time_ts,
4182                          &write_time_ts,
4183                          &change_time_ts, 
4184                          &inode)) {
4185         
4186                 errno = smbc_errno(context, srv->cli);
4187                 DEBUG(5, ("dos_attr_query Failed to query old attributes\n"));
4188                 return NULL;
4189         
4190         }
4191                 
4192         ret->mode = mode;
4193         ret->size = size;
4194         ret->create_time = convert_timespec_to_time_t(create_time_ts);
4195         ret->access_time = convert_timespec_to_time_t(access_time_ts);
4196         ret->write_time = convert_timespec_to_time_t(write_time_ts);
4197         ret->change_time = convert_timespec_to_time_t(change_time_ts);
4198         ret->inode = inode;
4199
4200         return ret;
4201 }
4202
4203
4204 /* parse a ascii version of a security descriptor */
4205 static void
4206 dos_attr_parse(SMBCCTX *context,
4207                DOS_ATTR_DESC *dad,
4208                SMBCSRV *srv,
4209                char *str)
4210 {
4211         int n;
4212         const char *p = str;
4213         fstring tok;
4214         struct {
4215                 const char * create_time_attr;
4216                 const char * access_time_attr;
4217                 const char * write_time_attr;
4218                 const char * change_time_attr;
4219         } attr_strings;
4220
4221         /* Determine whether to use old-style or new-style attribute names */
4222         if (context->internal->_full_time_names) {
4223                 /* new-style names */
4224                 attr_strings.create_time_attr = "CREATE_TIME";
4225                 attr_strings.access_time_attr = "ACCESS_TIME";
4226                 attr_strings.write_time_attr = "WRITE_TIME";
4227                 attr_strings.change_time_attr = "CHANGE_TIME";
4228         } else {
4229                 /* old-style names */
4230                 attr_strings.create_time_attr = NULL;
4231                 attr_strings.access_time_attr = "A_TIME";
4232                 attr_strings.write_time_attr = "M_TIME";
4233                 attr_strings.change_time_attr = "C_TIME";
4234         }
4235
4236         /* if this is to set the entire ACL... */
4237         if (*str == '*') {
4238                 /* ... then increment past the first colon if there is one */
4239                 if ((p = strchr(str, ':')) != NULL) {
4240                         ++p;
4241                 } else {
4242                         p = str;
4243                 }
4244         }
4245
4246         while (next_token(&p, tok, "\t,\r\n", sizeof(tok))) {
4247
4248                 if (StrnCaseCmp(tok, "MODE:", 5) == 0) {
4249                         dad->mode = strtol(tok+5, NULL, 16);
4250                         continue;
4251                 }
4252
4253                 if (StrnCaseCmp(tok, "SIZE:", 5) == 0) {
4254                         dad->size = (SMB_OFF_T)atof(tok+5);
4255                         continue;
4256                 }
4257
4258                 n = strlen(attr_strings.access_time_attr);
4259                 if (StrnCaseCmp(tok, attr_strings.access_time_attr, n) == 0) {
4260                         dad->access_time = (time_t)strtol(tok+n+1, NULL, 10);
4261                         continue;
4262                 }
4263
4264                 n = strlen(attr_strings.change_time_attr);
4265                 if (StrnCaseCmp(tok, attr_strings.change_time_attr, n) == 0) {
4266                         dad->change_time = (time_t)strtol(tok+n+1, NULL, 10);
4267                         continue;
4268                 }
4269
4270                 n = strlen(attr_strings.write_time_attr);
4271                 if (StrnCaseCmp(tok, attr_strings.write_time_attr, n) == 0) {
4272                         dad->write_time = (time_t)strtol(tok+n+1, NULL, 10);
4273                         continue;
4274                 }
4275
4276                 if (attr_strings.create_time_attr != NULL) {
4277                         n = strlen(attr_strings.create_time_attr);
4278                         if (StrnCaseCmp(tok, attr_strings.create_time_attr,
4279                                         n) == 0) {
4280                                 dad->create_time = (time_t)strtol(tok+n+1,
4281                                                                   NULL, 10);
4282                                 continue;
4283                         }
4284                 }
4285
4286                 if (StrnCaseCmp(tok, "INODE:", 6) == 0) {
4287                         dad->inode = (SMB_INO_T)atof(tok+6);
4288                         continue;
4289                 }
4290         }
4291 }
4292
4293 /***************************************************** 
4294  Retrieve the acls for a file.
4295 *******************************************************/
4296
4297 static int
4298 cacl_get(SMBCCTX *context,
4299          TALLOC_CTX *ctx,
4300          SMBCSRV *srv,
4301          struct cli_state *ipc_cli,
4302          POLICY_HND *pol,
4303          char *filename,
4304          char *attr_name,
4305          char *buf,
4306          int bufsize)
4307 {
4308         uint32 i;
4309         int n = 0;
4310         int n_used;
4311         BOOL all;
4312         BOOL all_nt;
4313         BOOL all_nt_acls;
4314         BOOL all_dos;
4315         BOOL some_nt;
4316         BOOL some_dos;
4317         BOOL exclude_nt_revision = False;
4318         BOOL exclude_nt_owner = False;
4319         BOOL exclude_nt_group = False;
4320         BOOL exclude_nt_acl = False;
4321         BOOL exclude_dos_mode = False;
4322         BOOL exclude_dos_size = False;
4323         BOOL exclude_dos_create_time = False;
4324         BOOL exclude_dos_access_time = False;
4325         BOOL exclude_dos_write_time = False;
4326         BOOL exclude_dos_change_time = False;
4327         BOOL exclude_dos_inode = False;
4328         BOOL numeric = True;
4329         BOOL determine_size = (bufsize == 0);
4330         int fnum = -1;
4331         SEC_DESC *sd;
4332         fstring sidstr;
4333         fstring name_sandbox;
4334         char *name;
4335         char *pExclude;
4336         char *p;
4337         struct timespec create_time_ts;
4338         struct timespec write_time_ts;
4339         struct timespec access_time_ts;
4340         struct timespec change_time_ts;
4341         time_t create_time = (time_t)0;
4342         time_t write_time = (time_t)0;
4343         time_t access_time = (time_t)0;
4344         time_t change_time = (time_t)0;
4345         SMB_OFF_T size = 0;
4346         uint16 mode = 0;
4347         SMB_INO_T ino = 0;
4348         struct cli_state *cli = srv->cli;
4349         struct {
4350                 const char * create_time_attr;
4351                 const char * access_time_attr;
4352                 const char * write_time_attr;
4353                 const char * change_time_attr;
4354         } attr_strings;
4355         struct {
4356                 const char * create_time_attr;
4357                 const char * access_time_attr;
4358                 const char * write_time_attr;
4359                 const char * change_time_attr;
4360         } excl_attr_strings;
4361
4362         /* Determine whether to use old-style or new-style attribute names */
4363         if (context->internal->_full_time_names) {
4364                 /* new-style names */
4365                 attr_strings.create_time_attr = "CREATE_TIME";
4366                 attr_strings.access_time_attr = "ACCESS_TIME";
4367                 attr_strings.write_time_attr = "WRITE_TIME";
4368                 attr_strings.change_time_attr = "CHANGE_TIME";
4369
4370                 excl_attr_strings.create_time_attr = "CREATE_TIME";
4371                 excl_attr_strings.access_time_attr = "ACCESS_TIME";
4372                 excl_attr_strings.write_time_attr = "WRITE_TIME";
4373                 excl_attr_strings.change_time_attr = "CHANGE_TIME";
4374         } else {
4375                 /* old-style names */
4376                 attr_strings.create_time_attr = NULL;
4377                 attr_strings.access_time_attr = "A_TIME";
4378                 attr_strings.write_time_attr = "M_TIME";
4379                 attr_strings.change_time_attr = "C_TIME";
4380
4381                 excl_attr_strings.create_time_attr = NULL;
4382                 excl_attr_strings.access_time_attr = "dos_attr.A_TIME";
4383                 excl_attr_strings.write_time_attr = "dos_attr.M_TIME";
4384                 excl_attr_strings.change_time_attr = "dos_attr.C_TIME";
4385         }
4386
4387         /* Copy name so we can strip off exclusions (if any are specified) */
4388         strncpy(name_sandbox, attr_name, sizeof(name_sandbox) - 1);
4389
4390         /* Ensure name is null terminated */
4391         name_sandbox[sizeof(name_sandbox) - 1] = '\0';
4392
4393         /* Play in the sandbox */
4394         name = name_sandbox;
4395
4396         /* If there are any exclusions, point to them and mask them from name */
4397         if ((pExclude = strchr(name, '!')) != NULL)
4398         {
4399                 *pExclude++ = '\0';
4400         }
4401
4402         all = (StrnCaseCmp(name, "system.*", 8) == 0);
4403         all_nt = (StrnCaseCmp(name, "system.nt_sec_desc.*", 20) == 0);
4404         all_nt_acls = (StrnCaseCmp(name, "system.nt_sec_desc.acl.*", 24) == 0);
4405         all_dos = (StrnCaseCmp(name, "system.dos_attr.*", 17) == 0);
4406         some_nt = (StrnCaseCmp(name, "system.nt_sec_desc.", 19) == 0);
4407         some_dos = (StrnCaseCmp(name, "system.dos_attr.", 16) == 0);
4408         numeric = (* (name + strlen(name) - 1) != '+');
4409
4410         /* Look for exclusions from "all" requests */
4411         if (all || all_nt || all_dos) {
4412
4413                 /* Exclusions are delimited by '!' */
4414                 for (;
4415                      pExclude != NULL;
4416                      pExclude = (p == NULL ? NULL : p + 1)) {
4417
4418                 /* Find end of this exclusion name */
4419                 if ((p = strchr(pExclude, '!')) != NULL)
4420                 {
4421                     *p = '\0';
4422                 }
4423
4424                 /* Which exclusion name is this? */
4425                 if (StrCaseCmp(pExclude, "nt_sec_desc.revision") == 0) {
4426                     exclude_nt_revision = True;
4427                 }
4428                 else if (StrCaseCmp(pExclude, "nt_sec_desc.owner") == 0) {
4429                     exclude_nt_owner = True;
4430                 }
4431                 else if (StrCaseCmp(pExclude, "nt_sec_desc.group") == 0) {
4432                     exclude_nt_group = True;
4433                 }
4434                 else if (StrCaseCmp(pExclude, "nt_sec_desc.acl") == 0) {
4435                     exclude_nt_acl = True;
4436                 }
4437                 else if (StrCaseCmp(pExclude, "dos_attr.mode") == 0) {
4438                     exclude_dos_mode = True;
4439                 }
4440                 else if (StrCaseCmp(pExclude, "dos_attr.size") == 0) {
4441                     exclude_dos_size = True;
4442                 }
4443                 else if (excl_attr_strings.create_time_attr != NULL &&
4444                          StrCaseCmp(pExclude,
4445                                     excl_attr_strings.change_time_attr) == 0) {
4446                     exclude_dos_create_time = True;
4447                 }
4448                 else if (StrCaseCmp(pExclude,
4449                                     excl_attr_strings.access_time_attr) == 0) {
4450                     exclude_dos_access_time = True;
4451                 }
4452                 else if (StrCaseCmp(pExclude,
4453                                     excl_attr_strings.write_time_attr) == 0) {
4454                     exclude_dos_write_time = True;
4455                 }
4456                 else if (StrCaseCmp(pExclude,
4457                                     excl_attr_strings.change_time_attr) == 0) {
4458                     exclude_dos_change_time = True;
4459                 }
4460                 else if (StrCaseCmp(pExclude, "dos_attr.inode") == 0) {
4461                     exclude_dos_inode = True;
4462                 }
4463                 else {
4464                     DEBUG(5, ("cacl_get received unknown exclusion: %s\n",
4465                               pExclude));
4466                     errno = ENOATTR;
4467                     return -1;
4468                 }
4469             }
4470         }
4471
4472         n_used = 0;
4473
4474         /*
4475          * If we are (possibly) talking to an NT or new system and some NT
4476          * attributes have been requested...
4477          */
4478         if (ipc_cli && (all || some_nt || all_nt_acls)) {
4479                 /* Point to the portion after "system.nt_sec_desc." */
4480                 name += 19;     /* if (all) this will be invalid but unused */
4481
4482                 /* ... then obtain any NT attributes which were requested */
4483                 fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
4484
4485                 if (fnum == -1) {
4486                         DEBUG(5, ("cacl_get failed to open %s: %s\n",
4487                                   filename, cli_errstr(cli)));
4488                         errno = 0;
4489                         return -1;
4490                 }
4491
4492                 sd = cli_query_secdesc(cli, fnum, ctx);
4493
4494                 if (!sd) {
4495                         DEBUG(5,
4496                               ("cacl_get Failed to query old descriptor\n"));
4497                         errno = 0;
4498                         return -1;
4499                 }
4500
4501                 cli_close(cli, fnum);
4502
4503                 if (! exclude_nt_revision) {
4504                         if (all || all_nt) {
4505                                 if (determine_size) {
4506                                         p = talloc_asprintf(ctx,
4507                                                             "REVISION:%d",
4508                                                             sd->revision);
4509                                         if (!p) {
4510                                                 errno = ENOMEM;
4511                                                 return -1;
4512                                         }
4513                                         n = strlen(p);
4514                                 } else {
4515                                         n = snprintf(buf, bufsize,
4516                                                      "REVISION:%d",
4517                                                      sd->revision);
4518                                 }
4519                         } else if (StrCaseCmp(name, "revision") == 0) {
4520                                 if (determine_size) {
4521                                         p = talloc_asprintf(ctx, "%d",
4522                                                             sd->revision);
4523                                         if (!p) {
4524                                                 errno = ENOMEM;
4525                                                 return -1;
4526                                         }
4527                                         n = strlen(p);
4528                                 } else {
4529                                         n = snprintf(buf, bufsize, "%d",
4530                                                      sd->revision);
4531                                 }
4532                         }
4533         
4534                         if (!determine_size && n > bufsize) {
4535                                 errno = ERANGE;
4536                                 return -1;
4537                         }
4538                         buf += n;
4539                         n_used += n;
4540                         bufsize -= n;
4541                 }
4542
4543                 if (! exclude_nt_owner) {
4544                         /* Get owner and group sid */
4545                         if (sd->owner_sid) {
4546                                 convert_sid_to_string(ipc_cli, pol,
4547                                                       sidstr,
4548                                                       numeric,
4549                                                       sd->owner_sid);
4550                         } else {
4551                                 fstrcpy(sidstr, "");
4552                         }
4553
4554                         if (all || all_nt) {
4555                                 if (determine_size) {
4556                                         p = talloc_asprintf(ctx, ",OWNER:%s",
4557                                                             sidstr);
4558                                         if (!p) {
4559                                                 errno = ENOMEM;
4560                                                 return -1;
4561                                         }
4562                                         n = strlen(p);
4563                                 } else {
4564                                         n = snprintf(buf, bufsize,
4565                                                      ",OWNER:%s", sidstr);
4566                                 }
4567                         } else if (StrnCaseCmp(name, "owner", 5) == 0) {
4568                                 if (determine_size) {
4569                                         p = talloc_asprintf(ctx, "%s", sidstr);
4570                                         if (!p) {
4571                                                 errno = ENOMEM;
4572                                                 return -1;
4573                                         }
4574                                         n = strlen(p);
4575                                 } else {
4576                                         n = snprintf(buf, bufsize, "%s",
4577                                                      sidstr);
4578                                 }
4579                         }
4580
4581                         if (!determine_size && n > bufsize) {
4582                                 errno = ERANGE;
4583                                 return -1;
4584                         }
4585                         buf += n;
4586                         n_used += n;
4587                         bufsize -= n;
4588                 }
4589
4590                 if (! exclude_nt_group) {
4591                         if (sd->group_sid) {
4592                                 convert_sid_to_string(ipc_cli, pol,
4593                                                       sidstr, numeric,
4594                                                       sd->group_sid);
4595                         } else {
4596                                 fstrcpy(sidstr, "");
4597                         }
4598
4599                         if (all || all_nt) {
4600                                 if (determine_size) {
4601                                         p = talloc_asprintf(ctx, ",GROUP:%s",
4602                                                             sidstr);
4603                                         if (!p) {
4604                                                 errno = ENOMEM;
4605                                                 return -1;
4606                                         }
4607                                         n = strlen(p);
4608                                 } else {
4609                                         n = snprintf(buf, bufsize,
4610                                                      ",GROUP:%s", sidstr);
4611                                 }
4612                         } else if (StrnCaseCmp(name, "group", 5) == 0) {
4613                                 if (determine_size) {
4614                                         p = talloc_asprintf(ctx, "%s", sidstr);
4615                                         if (!p) {
4616                                                 errno = ENOMEM;
4617                                                 return -1;
4618                                         }
4619                                         n = strlen(p);
4620                                 } else {
4621                                         n = snprintf(buf, bufsize,
4622                                                      "%s", sidstr);
4623                                 }
4624                         }
4625
4626                         if (!determine_size && n > bufsize) {
4627                                 errno = ERANGE;
4628                                 return -1;
4629                         }
4630                         buf += n;
4631                         n_used += n;
4632                         bufsize -= n;
4633                 }
4634
4635                 if (! exclude_nt_acl) {
4636                         /* Add aces to value buffer  */
4637                         for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
4638
4639                                 SEC_ACE *ace = &sd->dacl->aces[i];
4640                                 convert_sid_to_string(ipc_cli, pol,
4641                                                       sidstr, numeric,
4642                                                       &ace->trustee);
4643
4644                                 if (all || all_nt) {
4645                                         if (determine_size) {
4646                                                 p = talloc_asprintf(
4647                                                         ctx, 
4648                                                         ",ACL:"
4649                                                         "%s:%d/%d/0x%08x", 
4650                                                         sidstr,
4651                                                         ace->type,
4652                                                         ace->flags,
4653                                                         ace->access_mask);
4654                                                 if (!p) {
4655                                                         errno = ENOMEM;
4656                                                         return -1;
4657                                                 }
4658                                                 n = strlen(p);
4659                                         } else {
4660                                                 n = snprintf(
4661                                                         buf, bufsize,
4662                                                         ",ACL:%s:%d/%d/0x%08x", 
4663                                                         sidstr,
4664                                                         ace->type,
4665                                                         ace->flags,
4666                                                         ace->access_mask);
4667                                         }
4668                                 } else if ((StrnCaseCmp(name, "acl", 3) == 0 &&
4669                                             StrCaseCmp(name+3, sidstr) == 0) ||
4670                                            (StrnCaseCmp(name, "acl+", 4) == 0 &&
4671                                             StrCaseCmp(name+4, sidstr) == 0)) {
4672                                         if (determine_size) {
4673                                                 p = talloc_asprintf(
4674                                                         ctx, 
4675                                                         "%d/%d/0x%08x", 
4676                                                         ace->type,
4677                                                         ace->flags,
4678                                                         ace->access_mask);
4679                                                 if (!p) {
4680                                                         errno = ENOMEM;
4681                                                         return -1;
4682                                                 }
4683                                                 n = strlen(p);
4684                                         } else {
4685                                                 n = snprintf(buf, bufsize,
4686                                                              "%d/%d/0x%08x", 
4687                                                              ace->type,
4688                                                              ace->flags,
4689                                                              ace->access_mask);
4690                                         }
4691                                 } else if (all_nt_acls) {
4692                                         if (determine_size) {
4693                                                 p = talloc_asprintf(
4694                                                         ctx, 
4695                                                         "%s%s:%d/%d/0x%08x",
4696                                                         i ? "," : "",
4697                                                         sidstr,
4698                                                         ace->type,
4699                                                         ace->flags,
4700                                                         ace->access_mask);
4701                                                 if (!p) {
4702                                                         errno = ENOMEM;
4703                                                         return -1;
4704                                                 }
4705                                                 n = strlen(p);
4706                                         } else {
4707                                                 n = snprintf(buf, bufsize,
4708                                                              "%s%s:%d/%d/0x%08x",
4709                                                              i ? "," : "",
4710                                                              sidstr,
4711                                                              ace->type,
4712                                                              ace->flags,
4713                                                              ace->access_mask);
4714                                         }
4715                                 }
4716                                 if (n > bufsize) {
4717                                         errno = ERANGE;
4718                                         return -1;
4719                                 }
4720                                 buf += n;
4721                                 n_used += n;
4722                                 bufsize -= n;
4723                         }
4724                 }
4725
4726                 /* Restore name pointer to its original value */
4727                 name -= 19;
4728         }
4729
4730         if (all || some_dos) {
4731                 /* Point to the portion after "system.dos_attr." */
4732                 name += 16;     /* if (all) this will be invalid but unused */
4733
4734                 /* Obtain the DOS attributes */
4735                 if (!smbc_getatr(context, srv, filename, &mode, &size, 
4736                                  &create_time_ts,
4737                                  &access_time_ts,
4738                                  &write_time_ts,
4739                                  &change_time_ts,
4740                                  &ino)) {
4741                         
4742                         errno = smbc_errno(context, srv->cli);
4743                         return -1;
4744                         
4745                 }
4746
4747                 create_time = convert_timespec_to_time_t(create_time_ts);
4748                 access_time = convert_timespec_to_time_t(access_time_ts);
4749                 write_time = convert_timespec_to_time_t(write_time_ts);
4750                 change_time = convert_timespec_to_time_t(change_time_ts);
4751
4752                 if (! exclude_dos_mode) {
4753                         if (all || all_dos) {
4754                                 if (determine_size) {
4755                                         p = talloc_asprintf(ctx,
4756                                                             "%sMODE:0x%x",
4757                                                             (ipc_cli &&
4758                                                              (all || some_nt)
4759                                                              ? ","
4760                                                              : ""),
4761                                                             mode);
4762                                         if (!p) {
4763                                                 errno = ENOMEM;
4764                                                 return -1;
4765                                         }
4766                                         n = strlen(p);
4767                                 } else {
4768                                         n = snprintf(buf, bufsize,
4769                                                      "%sMODE:0x%x",
4770                                                      (ipc_cli &&
4771                                                       (all || some_nt)
4772                                                       ? ","
4773                                                       : ""),
4774                                                      mode);
4775                                 }
4776                         } else if (StrCaseCmp(name, "mode") == 0) {
4777                                 if (determine_size) {
4778                                         p = talloc_asprintf(ctx, "0x%x", mode);
4779                                         if (!p) {
4780                                                 errno = ENOMEM;
4781                                                 return -1;
4782                                         }
4783                                         n = strlen(p);
4784                                 } else {
4785                                         n = snprintf(buf, bufsize,
4786                                                      "0x%x", mode);
4787                                 }
4788                         }
4789         
4790                         if (!determine_size && n > bufsize) {
4791                                 errno = ERANGE;
4792                                 return -1;
4793                         }
4794                         buf += n;
4795                         n_used += n;
4796                         bufsize -= n;
4797                 }
4798
4799                 if (! exclude_dos_size) {
4800                         if (all || all_dos) {
4801                                 if (determine_size) {
4802                                         p = talloc_asprintf(
4803                                                 ctx,
4804                                                 ",SIZE:%.0f",
4805                                                 (double)size);
4806                                         if (!p) {
4807                                                 errno = ENOMEM;
4808                                                 return -1;
4809                                         }
4810                                         n = strlen(p);
4811                                 } else {
4812                                         n = snprintf(buf, bufsize,
4813                                                      ",SIZE:%.0f",
4814                                                      (double)size);
4815                                 }
4816                         } else if (StrCaseCmp(name, "size") == 0) {
4817                                 if (determine_size) {
4818                                         p = talloc_asprintf(
4819                                                 ctx,
4820                                                 "%.0f",
4821                                                 (double)size);
4822                                         if (!p) {
4823                                                 errno = ENOMEM;
4824                                                 return -1;
4825                                         }
4826                                         n = strlen(p);
4827                                 } else {
4828                                         n = snprintf(buf, bufsize,
4829                                                      "%.0f",
4830                                                      (double)size);
4831                                 }
4832                         }
4833         
4834                         if (!determine_size && n > bufsize) {
4835                                 errno = ERANGE;
4836                                 return -1;
4837                         }
4838                         buf += n;
4839                         n_used += n;
4840                         bufsize -= n;
4841                 }
4842
4843                 if (! exclude_dos_create_time &&
4844                     attr_strings.create_time_attr != NULL) {
4845                         if (all || all_dos) {
4846                                 if (determine_size) {
4847                                         p = talloc_asprintf(ctx,
4848                                                             ",%s:%lu",
4849                                                             attr_strings.create_time_attr,
4850                                                             create_time);
4851                                         if (!p) {
4852                                                 errno = ENOMEM;
4853                                                 return -1;
4854                                         }
4855                                         n = strlen(p);
4856                                 } else {
4857                                         n = snprintf(buf, bufsize,
4858                                                      ",%s:%lu",
4859                                                      attr_strings.create_time_attr,
4860                                                      create_time);
4861                                 }
4862                         } else if (StrCaseCmp(name, attr_strings.create_time_attr) == 0) {
4863                                 if (determine_size) {
4864                                         p = talloc_asprintf(ctx, "%lu", create_time);
4865                                         if (!p) {
4866                                                 errno = ENOMEM;
4867                                                 return -1;
4868                                         }
4869                                         n = strlen(p);
4870                                 } else {
4871                                         n = snprintf(buf, bufsize,
4872                                                      "%lu", create_time);
4873                                 }
4874                         }
4875         
4876                         if (!determine_size && n > bufsize) {
4877                                 errno = ERANGE;
4878                                 return -1;
4879                         }
4880                         buf += n;
4881                         n_used += n;
4882                         bufsize -= n;
4883                 }
4884
4885                 if (! exclude_dos_access_time) {
4886                         if (all || all_dos) {
4887                                 if (determine_size) {
4888                                         p = talloc_asprintf(ctx,
4889                                                             ",%s:%lu",
4890                                                             attr_strings.access_time_attr,
4891                                                             access_time);
4892                                         if (!p) {
4893                                                 errno = ENOMEM;
4894                                                 return -1;
4895                                         }
4896                                         n = strlen(p);
4897                                 } else {
4898                                         n = snprintf(buf, bufsize,
4899                                                      ",%s:%lu",
4900                                                      attr_strings.access_time_attr,
4901                                                      access_time);
4902                                 }
4903                         } else if (StrCaseCmp(name, attr_strings.access_time_attr) == 0) {
4904                                 if (determine_size) {
4905                                         p = talloc_asprintf(ctx, "%lu", access_time);
4906                                         if (!p) {
4907                                                 errno = ENOMEM;
4908                                                 return -1;
4909                                         }
4910                                         n = strlen(p);
4911                                 } else {
4912                                         n = snprintf(buf, bufsize,
4913                                                      "%lu", access_time);
4914                                 }
4915                         }
4916         
4917                         if (!determine_size && n > bufsize) {
4918                                 errno = ERANGE;
4919                                 return -1;
4920                         }
4921                         buf += n;
4922                         n_used += n;
4923                         bufsize -= n;
4924                 }
4925
4926                 if (! exclude_dos_write_time) {
4927                         if (all || all_dos) {
4928                                 if (determine_size) {
4929                                         p = talloc_asprintf(ctx,
4930                                                             ",%s:%lu",
4931                                                             attr_strings.write_time_attr,
4932                                                             write_time);
4933                                         if (!p) {
4934                                                 errno = ENOMEM;
4935                                                 return -1;
4936                                         }
4937                                         n = strlen(p);
4938                                 } else {
4939                                         n = snprintf(buf, bufsize,
4940                                                      ",%s:%lu",
4941                                                      attr_strings.write_time_attr,
4942                                                      write_time);
4943                                 }
4944                         } else if (StrCaseCmp(name, attr_strings.write_time_attr) == 0) {
4945                                 if (determine_size) {
4946                                         p = talloc_asprintf(ctx, "%lu", write_time);
4947                                         if (!p) {
4948                                                 errno = ENOMEM;
4949                                                 return -1;
4950                                         }
4951                                         n = strlen(p);
4952                                 } else {
4953                                         n = snprintf(buf, bufsize,
4954                                                      "%lu", write_time);
4955                                 }
4956                         }
4957         
4958                         if (!determine_size && n > bufsize) {
4959                                 errno = ERANGE;
4960                                 return -1;
4961                         }
4962                         buf += n;
4963                         n_used += n;
4964                         bufsize -= n;
4965                 }
4966
4967                 if (! exclude_dos_change_time) {
4968                         if (all || all_dos) {
4969                                 if (determine_size) {
4970                                         p = talloc_asprintf(ctx,
4971                                                             ",%s:%lu",
4972                                                             attr_strings.change_time_attr,
4973                                                             change_time);
4974                                         if (!p) {
4975                                                 errno = ENOMEM;
4976                                                 return -1;
4977                                         }
4978                                         n = strlen(p);
4979                                 } else {
4980                                         n = snprintf(buf, bufsize,
4981                                                      ",%s:%lu",
4982                                                      attr_strings.change_time_attr,
4983                                                      change_time);
4984                                 }
4985                         } else if (StrCaseCmp(name, attr_strings.change_time_attr) == 0) {
4986                                 if (determine_size) {
4987                                         p = talloc_asprintf(ctx, "%lu", change_time);
4988                                         if (!p) {
4989                                                 errno = ENOMEM;
4990                                                 return -1;
4991                                         }
4992                                         n = strlen(p);
4993                                 } else {
4994                                         n = snprintf(buf, bufsize,
4995                                                      "%lu", change_time);
4996                                 }
4997                         }
4998         
4999                         if (!determine_size && n > bufsize) {
5000                                 errno = ERANGE;
5001                                 return -1;
5002                         }
5003                         buf += n;
5004                         n_used += n;
5005                         bufsize -= n;
5006                 }
5007
5008                 if (! exclude_dos_inode) {
5009                         if (all || all_dos) {
5010                                 if (determine_size) {
5011                                         p = talloc_asprintf(
5012                                                 ctx,
5013                                                 ",INODE:%.0f",
5014                                                 (double)ino);
5015                                         if (!p) {
5016                                                 errno = ENOMEM;
5017                                                 return -1;
5018                                         }
5019                                         n = strlen(p);
5020                                 } else {
5021                                         n = snprintf(buf, bufsize,
5022                                                      ",INODE:%.0f",
5023                                                      (double) ino);
5024                                 }
5025                         } else if (StrCaseCmp(name, "inode") == 0) {
5026                                 if (determine_size) {
5027                                         p = talloc_asprintf(
5028                                                 ctx,
5029                                                 "%.0f",
5030                                                 (double) ino);
5031                                         if (!p) {
5032                                                 errno = ENOMEM;
5033                                                 return -1;
5034                                         }
5035                                         n = strlen(p);
5036                                 } else {
5037                                         n = snprintf(buf, bufsize,
5038                                                      "%.0f",
5039                                                      (double) ino);
5040                                 }
5041                         }
5042         
5043                         if (!determine_size && n > bufsize) {
5044                                 errno = ERANGE;
5045                                 return -1;
5046                         }
5047                         buf += n;
5048                         n_used += n;
5049                         bufsize -= n;
5050                 }
5051
5052                 /* Restore name pointer to its original value */
5053                 name -= 16;
5054         }
5055
5056         if (n_used == 0) {
5057                 errno = ENOATTR;
5058                 return -1;
5059         }
5060
5061         return n_used;
5062 }
5063
5064
5065 /***************************************************** 
5066 set the ACLs on a file given an ascii description
5067 *******************************************************/
5068 static int
5069 cacl_set(TALLOC_CTX *ctx,
5070          struct cli_state *cli,
5071          struct cli_state *ipc_cli,
5072          POLICY_HND *pol,
5073          const char *filename,
5074          const char *the_acl,
5075          int mode,
5076          int flags)
5077 {
5078         int fnum;
5079         int err = 0;
5080         SEC_DESC *sd = NULL, *old;
5081         SEC_ACL *dacl = NULL;
5082         DOM_SID *owner_sid = NULL; 
5083         DOM_SID *grp_sid = NULL;
5084         uint32 i, j;
5085         size_t sd_size;
5086         int ret = 0;
5087         char *p;
5088         BOOL numeric = True;
5089
5090         /* the_acl will be null for REMOVE_ALL operations */
5091         if (the_acl) {
5092                 numeric = ((p = strchr(the_acl, ':')) != NULL &&
5093                            p > the_acl &&
5094                            p[-1] != '+');
5095
5096                 /* if this is to set the entire ACL... */
5097                 if (*the_acl == '*') {
5098                         /* ... then increment past the first colon */
5099                         the_acl = p + 1;
5100                 }
5101
5102                 sd = sec_desc_parse(ctx, ipc_cli, pol, numeric,
5103                                     CONST_DISCARD(char *, the_acl));
5104
5105                 if (!sd) {
5106                         errno = EINVAL;
5107                         return -1;
5108                 }
5109         }
5110
5111         /* SMBC_XATTR_MODE_REMOVE_ALL is the only caller
5112            that doesn't deref sd */
5113
5114         if (!sd && (mode != SMBC_XATTR_MODE_REMOVE_ALL)) {
5115                 errno = EINVAL;
5116                 return -1;
5117         }
5118
5119         /* The desired access below is the only one I could find that works
5120            with NT4, W2KP and Samba */
5121
5122         fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
5123
5124         if (fnum == -1) {
5125                 DEBUG(5, ("cacl_set failed to open %s: %s\n",
5126                           filename, cli_errstr(cli)));
5127                 errno = 0;
5128                 return -1;
5129         }
5130
5131         old = cli_query_secdesc(cli, fnum, ctx);
5132
5133         if (!old) {
5134                 DEBUG(5, ("cacl_set Failed to query old descriptor\n"));
5135                 errno = 0;
5136                 return -1;
5137         }
5138
5139         cli_close(cli, fnum);
5140
5141         switch (mode) {
5142         case SMBC_XATTR_MODE_REMOVE_ALL:
5143                 old->dacl->num_aces = 0;
5144                 SAFE_FREE(old->dacl->aces);
5145                 SAFE_FREE(old->dacl);
5146                 old->dacl = NULL;
5147                 dacl = old->dacl;
5148                 break;
5149
5150         case SMBC_XATTR_MODE_REMOVE:
5151                 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
5152                         BOOL found = False;
5153
5154                         for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
5155                                 if (sec_ace_equal(&sd->dacl->aces[i],
5156                                                   &old->dacl->aces[j])) {
5157                                         uint32 k;
5158                                         for (k=j; k<old->dacl->num_aces-1;k++) {
5159                                                 old->dacl->aces[k] =
5160                                                         old->dacl->aces[k+1];
5161                                         }
5162                                         old->dacl->num_aces--;
5163                                         if (old->dacl->num_aces == 0) {
5164                                                 SAFE_FREE(old->dacl->aces);
5165                                                 SAFE_FREE(old->dacl);
5166                                                 old->dacl = NULL;
5167                                         }
5168                                         found = True;
5169                                         dacl = old->dacl;
5170                                         break;
5171                                 }
5172                         }
5173
5174                         if (!found) {
5175                                 err = ENOATTR;
5176                                 ret = -1;
5177                                 goto failed;
5178                         }
5179                 }
5180                 break;
5181
5182         case SMBC_XATTR_MODE_ADD:
5183                 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
5184                         BOOL found = False;
5185
5186                         for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
5187                                 if (sid_equal(&sd->dacl->aces[i].trustee,
5188                                               &old->dacl->aces[j].trustee)) {
5189                                         if (!(flags & SMBC_XATTR_FLAG_CREATE)) {
5190                                                 err = EEXIST;
5191                                                 ret = -1;
5192                                                 goto failed;
5193                                         }
5194                                         old->dacl->aces[j] = sd->dacl->aces[i];
5195                                         ret = -1;
5196                                         found = True;
5197                                 }
5198                         }
5199
5200                         if (!found && (flags & SMBC_XATTR_FLAG_REPLACE)) {
5201                                 err = ENOATTR;
5202                                 ret = -1;
5203                                 goto failed;
5204                         }
5205                         
5206                         for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
5207                                 add_ace(&old->dacl, &sd->dacl->aces[i], ctx);
5208                         }
5209                 }
5210                 dacl = old->dacl;
5211                 break;
5212
5213         case SMBC_XATTR_MODE_SET:
5214                 old = sd;
5215                 owner_sid = old->owner_sid;
5216                 grp_sid = old->group_sid;
5217                 dacl = old->dacl;
5218                 break;
5219
5220         case SMBC_XATTR_MODE_CHOWN:
5221                 owner_sid = sd->owner_sid;
5222                 break;
5223
5224         case SMBC_XATTR_MODE_CHGRP:
5225                 grp_sid = sd->group_sid;
5226                 break;
5227         }
5228
5229         /* Denied ACE entries must come before allowed ones */
5230         sort_acl(old->dacl);
5231
5232         /* Create new security descriptor and set it */
5233         sd = make_sec_desc(ctx, old->revision, SEC_DESC_SELF_RELATIVE, 
5234                            owner_sid, grp_sid, NULL, dacl, &sd_size);
5235
5236         fnum = cli_nt_create(cli, filename,
5237                              WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS);
5238
5239         if (fnum == -1) {
5240                 DEBUG(5, ("cacl_set failed to open %s: %s\n",
5241                           filename, cli_errstr(cli)));
5242                 errno = 0;
5243                 return -1;
5244         }
5245
5246         if (!cli_set_secdesc(cli, fnum, sd)) {
5247                 DEBUG(5, ("ERROR: secdesc set failed: %s\n", cli_errstr(cli)));
5248                 ret = -1;
5249         }
5250
5251         /* Clean up */
5252
5253  failed:
5254         cli_close(cli, fnum);
5255
5256         if (err != 0) {
5257                 errno = err;
5258         }
5259         
5260         return ret;
5261 }
5262
5263
5264 static int
5265 smbc_setxattr_ctx(SMBCCTX *context,
5266                   const char *fname,
5267                   const char *name,
5268                   const void *value,
5269                   size_t size,
5270                   int flags)
5271 {
5272         int ret;
5273         int ret2;
5274         SMBCSRV *srv;
5275         SMBCSRV *ipc_srv;
5276         fstring server;
5277         fstring share;
5278         fstring user;
5279         fstring password;
5280         fstring workgroup;
5281         pstring path;
5282         TALLOC_CTX *ctx;
5283         POLICY_HND pol;
5284         DOS_ATTR_DESC *dad;
5285         struct {
5286                 const char * create_time_attr;
5287                 const char * access_time_attr;
5288                 const char * write_time_attr;
5289                 const char * change_time_attr;
5290         } attr_strings;
5291
5292         if (!context || !context->internal ||
5293             !context->internal->_initialized) {
5294
5295                 errno = EINVAL;  /* Best I can think of ... */
5296                 return -1;
5297     
5298         }
5299
5300         if (!fname) {
5301
5302                 errno = EINVAL;
5303                 return -1;
5304
5305         }
5306   
5307         DEBUG(4, ("smbc_setxattr(%s, %s, %.*s)\n",
5308                   fname, name, (int) size, (const char*)value));
5309
5310         if (smbc_parse_path(context, fname,
5311                             workgroup, sizeof(workgroup),
5312                             server, sizeof(server),
5313                             share, sizeof(share),
5314                             path, sizeof(path),
5315                             user, sizeof(user),
5316                             password, sizeof(password),
5317                             NULL, 0)) {
5318                 errno = EINVAL;
5319                 return -1;
5320         }
5321
5322         if (user[0] == (char)0) fstrcpy(user, context->user);
5323
5324         srv = smbc_server(context, True,
5325                           server, share, workgroup, user, password);
5326         if (!srv) {
5327                 return -1;  /* errno set by smbc_server */
5328         }
5329
5330         if (! srv->no_nt_session) {
5331                 ipc_srv = smbc_attr_server(context, server, share,
5332                                            workgroup, user, password,
5333                                            &pol);
5334                 srv->no_nt_session = True;
5335         } else {
5336                 ipc_srv = NULL;
5337         }
5338         
5339         ctx = talloc_init("smbc_setxattr");
5340         if (!ctx) {
5341                 errno = ENOMEM;
5342                 return -1;
5343         }
5344
5345         /*
5346          * Are they asking to set the entire set of known attributes?
5347          */
5348         if (StrCaseCmp(name, "system.*") == 0 ||
5349             StrCaseCmp(name, "system.*+") == 0) {
5350                 /* Yup. */
5351                 char *namevalue =
5352                         talloc_asprintf(ctx, "%s:%s",
5353                                         name+7, (const char *) value);
5354                 if (! namevalue) {
5355                         errno = ENOMEM;
5356                         ret = -1;
5357                         return -1;
5358                 }
5359
5360                 if (ipc_srv) {
5361                         ret = cacl_set(ctx, srv->cli,
5362                                        ipc_srv->cli, &pol, path,
5363                                        namevalue,
5364                                        (*namevalue == '*'
5365                                         ? SMBC_XATTR_MODE_SET
5366                                         : SMBC_XATTR_MODE_ADD),
5367                                        flags);
5368                 } else {
5369                         ret = 0;
5370                 }
5371
5372                 /* get a DOS Attribute Descriptor with current attributes */
5373                 dad = dos_attr_query(context, ctx, path, srv);
5374                 if (dad) {
5375                         /* Overwrite old with new, using what was provided */
5376                         dos_attr_parse(context, dad, srv, namevalue);
5377
5378                         /* Set the new DOS attributes */
5379                         if (! smbc_setatr(context, srv, path,
5380                                           dad->create_time,
5381                                           dad->access_time,
5382                                           dad->write_time,
5383                                           dad->change_time,
5384                                           dad->mode)) {
5385
5386                                 /* cause failure if NT failed too */
5387                                 dad = NULL; 
5388                         }
5389                 }
5390
5391                 /* we only fail if both NT and DOS sets failed */
5392                 if (ret < 0 && ! dad) {
5393                         ret = -1; /* in case dad was null */
5394                 }
5395                 else {
5396                         ret = 0;
5397                 }
5398
5399                 talloc_destroy(ctx);
5400                 return ret;
5401         }
5402
5403         /*
5404          * Are they asking to set an access control element or to set
5405          * the entire access control list?
5406          */
5407         if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
5408             StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 ||
5409             StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
5410             StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
5411             StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) {
5412
5413                 /* Yup. */
5414                 char *namevalue =
5415                         talloc_asprintf(ctx, "%s:%s",
5416                                         name+19, (const char *) value);
5417
5418                 if (! ipc_srv) {
5419                         ret = -1; /* errno set by smbc_server() */
5420                 }
5421                 else if (! namevalue) {
5422                         errno = ENOMEM;
5423                         ret = -1;
5424                 } else {
5425                         ret = cacl_set(ctx, srv->cli,
5426                                        ipc_srv->cli, &pol, path,
5427                                        namevalue,
5428                                        (*namevalue == '*'
5429                                         ? SMBC_XATTR_MODE_SET
5430                                         : SMBC_XATTR_MODE_ADD),
5431                                        flags);
5432                 }
5433                 talloc_destroy(ctx);
5434                 return ret;
5435         }
5436
5437         /*
5438          * Are they asking to set the owner?
5439          */
5440         if (StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
5441             StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0) {
5442
5443                 /* Yup. */
5444                 char *namevalue =
5445                         talloc_asprintf(ctx, "%s:%s",
5446                                         name+19, (const char *) value);
5447
5448                 if (! ipc_srv) {
5449                         
5450                         ret = -1; /* errno set by smbc_server() */
5451                 }
5452                 else if (! namevalue) {
5453                         errno = ENOMEM;
5454                         ret = -1;
5455                 } else {
5456                         ret = cacl_set(ctx, srv->cli,
5457                                        ipc_srv->cli, &pol, path,
5458                                        namevalue, SMBC_XATTR_MODE_CHOWN, 0);
5459                 }
5460                 talloc_destroy(ctx);
5461                 return ret;
5462         }
5463
5464         /*
5465          * Are they asking to set the group?
5466          */
5467         if (StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
5468             StrCaseCmp(name, "system.nt_sec_desc.group+") == 0) {
5469
5470                 /* Yup. */
5471                 char *namevalue =
5472                         talloc_asprintf(ctx, "%s:%s",
5473                                         name+19, (const char *) value);
5474
5475                 if (! ipc_srv) {
5476                         /* errno set by smbc_server() */
5477                         ret = -1;
5478                 }
5479                 else if (! namevalue) {
5480                         errno = ENOMEM;
5481                         ret = -1;
5482                 } else {
5483                         ret = cacl_set(ctx, srv->cli,
5484                                        ipc_srv->cli, &pol, path,
5485                                        namevalue, SMBC_XATTR_MODE_CHOWN, 0);
5486                 }
5487                 talloc_destroy(ctx);
5488                 return ret;
5489         }
5490
5491         /* Determine whether to use old-style or new-style attribute names */
5492         if (context->internal->_full_time_names) {
5493                 /* new-style names */
5494                 attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME";
5495                 attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME";
5496                 attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME";
5497                 attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME";
5498         } else {
5499                 /* old-style names */
5500                 attr_strings.create_time_attr = NULL;
5501                 attr_strings.access_time_attr = "system.dos_attr.A_TIME";
5502                 attr_strings.write_time_attr = "system.dos_attr.M_TIME";
5503                 attr_strings.change_time_attr = "system.dos_attr.C_TIME";
5504         }
5505
5506         /*
5507          * Are they asking to set a DOS attribute?
5508          */
5509         if (StrCaseCmp(name, "system.dos_attr.*") == 0 ||
5510             StrCaseCmp(name, "system.dos_attr.mode") == 0 ||
5511             (attr_strings.create_time_attr != NULL &&
5512              StrCaseCmp(name, attr_strings.create_time_attr) == 0) ||
5513             StrCaseCmp(name, attr_strings.access_time_attr) == 0 ||
5514             StrCaseCmp(name, attr_strings.write_time_attr) == 0 ||
5515             StrCaseCmp(name, attr_strings.change_time_attr) == 0) {
5516
5517                 /* get a DOS Attribute Descriptor with current attributes */
5518                 dad = dos_attr_query(context, ctx, path, srv);
5519                 if (dad) {
5520                         char *namevalue =
5521                                 talloc_asprintf(ctx, "%s:%s",
5522                                                 name+16, (const char *) value);
5523                         if (! namevalue) {
5524                                 errno = ENOMEM;
5525                                 ret = -1;
5526                         } else {
5527                                 /* Overwrite old with provided new params */
5528                                 dos_attr_parse(context, dad, srv, namevalue);
5529
5530                                 /* Set the new DOS attributes */
5531                                 ret2 = smbc_setatr(context, srv, path,
5532                                                    dad->create_time,
5533                                                    dad->access_time,
5534                                                    dad->write_time,
5535                                                    dad->change_time,
5536                                                    dad->mode);
5537
5538                                 /* ret2 has True (success) / False (failure) */
5539                                 if (ret2) {
5540                                         ret = 0;
5541                                 } else {
5542                                         ret = -1;
5543                                 }
5544                         }
5545                 } else {
5546                         ret = -1;
5547                 }
5548
5549                 talloc_destroy(ctx);
5550                 return ret;
5551         }
5552
5553         /* Unsupported attribute name */
5554         talloc_destroy(ctx);
5555         errno = EINVAL;
5556         return -1;
5557 }
5558
5559 static int
5560 smbc_getxattr_ctx(SMBCCTX *context,
5561                   const char *fname,
5562                   const char *name,
5563                   const void *value,
5564                   size_t size)
5565 {
5566         int ret;
5567         SMBCSRV *srv;
5568         SMBCSRV *ipc_srv;
5569         fstring server;
5570         fstring share;
5571         fstring user;
5572         fstring password;
5573         fstring workgroup;
5574         pstring path;
5575         TALLOC_CTX *ctx;
5576         POLICY_HND pol;
5577         struct {
5578                 const char * create_time_attr;
5579                 const char * access_time_attr;
5580                 const char * write_time_attr;
5581                 const char * change_time_attr;
5582         } attr_strings;
5583
5584
5585         if (!context || !context->internal ||
5586             !context->internal->_initialized) {
5587
5588                 errno = EINVAL;  /* Best I can think of ... */
5589                 return -1;
5590     
5591         }
5592
5593         if (!fname) {
5594
5595                 errno = EINVAL;
5596                 return -1;
5597
5598         }
5599   
5600         DEBUG(4, ("smbc_getxattr(%s, %s)\n", fname, name));
5601
5602         if (smbc_parse_path(context, fname,
5603                             workgroup, sizeof(workgroup),
5604                             server, sizeof(server),
5605                             share, sizeof(share),
5606                             path, sizeof(path),
5607                             user, sizeof(user),
5608                             password, sizeof(password),
5609                             NULL, 0)) {
5610                 errno = EINVAL;
5611                 return -1;
5612         }
5613
5614         if (user[0] == (char)0) fstrcpy(user, context->user);
5615
5616         srv = smbc_server(context, True,
5617                           server, share, workgroup, user, password);
5618         if (!srv) {
5619                 return -1;  /* errno set by smbc_server */
5620         }
5621
5622         if (! srv->no_nt_session) {
5623                 ipc_srv = smbc_attr_server(context, server, share,
5624                                            workgroup, user, password,
5625                                            &pol);
5626                 if (! ipc_srv) {
5627                         srv->no_nt_session = True;
5628                 }
5629         } else {
5630                 ipc_srv = NULL;
5631         }
5632         
5633         ctx = talloc_init("smbc:getxattr");
5634         if (!ctx) {
5635                 errno = ENOMEM;
5636                 return -1;
5637         }
5638
5639         /* Determine whether to use old-style or new-style attribute names */
5640         if (context->internal->_full_time_names) {
5641                 /* new-style names */
5642                 attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME";
5643                 attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME";
5644                 attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME";
5645                 attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME";
5646         } else {
5647                 /* old-style names */
5648                 attr_strings.create_time_attr = NULL;
5649                 attr_strings.access_time_attr = "system.dos_attr.A_TIME";
5650                 attr_strings.write_time_attr = "system.dos_attr.M_TIME";
5651                 attr_strings.change_time_attr = "system.dos_attr.C_TIME";
5652         }
5653
5654         /* Are they requesting a supported attribute? */
5655         if (StrCaseCmp(name, "system.*") == 0 ||
5656             StrnCaseCmp(name, "system.*!", 9) == 0 ||
5657             StrCaseCmp(name, "system.*+") == 0 ||
5658             StrnCaseCmp(name, "system.*+!", 10) == 0 ||
5659             StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
5660             StrnCaseCmp(name, "system.nt_sec_desc.*!", 21) == 0 ||
5661             StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 ||
5662             StrnCaseCmp(name, "system.nt_sec_desc.*+!", 22) == 0 ||
5663             StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
5664             StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
5665             StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 ||
5666             StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
5667             StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 ||
5668             StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
5669             StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0 ||
5670             StrCaseCmp(name, "system.dos_attr.*") == 0 ||
5671             StrnCaseCmp(name, "system.dos_attr.*!", 18) == 0 ||
5672             StrCaseCmp(name, "system.dos_attr.mode") == 0 ||
5673             StrCaseCmp(name, "system.dos_attr.size") == 0 ||
5674             (attr_strings.create_time_attr != NULL &&
5675              StrCaseCmp(name, attr_strings.create_time_attr) == 0) ||
5676             StrCaseCmp(name, attr_strings.access_time_attr) == 0 ||
5677             StrCaseCmp(name, attr_strings.write_time_attr) == 0 ||
5678             StrCaseCmp(name, attr_strings.change_time_attr) == 0 ||
5679             StrCaseCmp(name, "system.dos_attr.inode") == 0) {
5680
5681                 /* Yup. */
5682                 ret = cacl_get(context, ctx, srv,
5683                                ipc_srv == NULL ? NULL : ipc_srv->cli, 
5684                                &pol, path,
5685                                CONST_DISCARD(char *, name),
5686                                CONST_DISCARD(char *, value), size);
5687                 if (ret < 0 && errno == 0) {
5688                         errno = smbc_errno(context, srv->cli);
5689                 }
5690                 talloc_destroy(ctx);
5691                 return ret;
5692         }
5693
5694         /* Unsupported attribute name */
5695         talloc_destroy(ctx);
5696         errno = EINVAL;
5697         return -1;
5698 }
5699
5700
5701 static int
5702 smbc_removexattr_ctx(SMBCCTX *context,
5703                      const char *fname,
5704                      const char *name)
5705 {
5706         int ret;
5707         SMBCSRV *srv;
5708         SMBCSRV *ipc_srv;
5709         fstring server;
5710         fstring share;
5711         fstring user;
5712         fstring password;
5713         fstring workgroup;
5714         pstring path;
5715         TALLOC_CTX *ctx;
5716         POLICY_HND pol;
5717
5718         if (!context || !context->internal ||
5719             !context->internal->_initialized) {
5720
5721                 errno = EINVAL;  /* Best I can think of ... */
5722                 return -1;
5723     
5724         }
5725
5726         if (!fname) {
5727
5728                 errno = EINVAL;
5729                 return -1;
5730
5731         }
5732   
5733         DEBUG(4, ("smbc_removexattr(%s, %s)\n", fname, name));
5734
5735         if (smbc_parse_path(context, fname,
5736                             workgroup, sizeof(workgroup),
5737                             server, sizeof(server),
5738                             share, sizeof(share),
5739                             path, sizeof(path),
5740                             user, sizeof(user),
5741                             password, sizeof(password),
5742                             NULL, 0)) {
5743                 errno = EINVAL;
5744                 return -1;
5745         }
5746
5747         if (user[0] == (char)0) fstrcpy(user, context->user);
5748
5749         srv = smbc_server(context, True,
5750                           server, share, workgroup, user, password);
5751         if (!srv) {
5752                 return -1;  /* errno set by smbc_server */
5753         }
5754
5755         if (! srv->no_nt_session) {
5756                 ipc_srv = smbc_attr_server(context, server, share,
5757                                            workgroup, user, password,
5758                                            &pol);
5759                 srv->no_nt_session = True;
5760         } else {
5761                 ipc_srv = NULL;
5762         }
5763         
5764         if (! ipc_srv) {
5765                 return -1; /* errno set by smbc_attr_server */
5766         }
5767
5768         ctx = talloc_init("smbc_removexattr");
5769         if (!ctx) {
5770                 errno = ENOMEM;
5771                 return -1;
5772         }
5773
5774         /* Are they asking to set the entire ACL? */
5775         if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
5776             StrCaseCmp(name, "system.nt_sec_desc.*+") == 0) {
5777
5778                 /* Yup. */
5779                 ret = cacl_set(ctx, srv->cli,
5780                                ipc_srv->cli, &pol, path,
5781                                NULL, SMBC_XATTR_MODE_REMOVE_ALL, 0);
5782                 talloc_destroy(ctx);
5783                 return ret;
5784         }
5785
5786         /*
5787          * Are they asking to remove one or more spceific security descriptor
5788          * attributes?
5789          */
5790         if (StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
5791             StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
5792             StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 ||
5793             StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
5794             StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 ||
5795             StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
5796             StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) {
5797
5798                 /* Yup. */
5799                 ret = cacl_set(ctx, srv->cli,
5800                                ipc_srv->cli, &pol, path,
5801                                name + 19, SMBC_XATTR_MODE_REMOVE, 0);
5802                 talloc_destroy(ctx);
5803                 return ret;
5804         }
5805
5806         /* Unsupported attribute name */
5807         talloc_destroy(ctx);
5808         errno = EINVAL;
5809         return -1;
5810 }
5811
5812 static int
5813 smbc_listxattr_ctx(SMBCCTX *context,
5814                    const char *fname,
5815                    char *list,
5816                    size_t size)
5817 {
5818         /*
5819          * This isn't quite what listxattr() is supposed to do.  This returns
5820          * the complete set of attribute names, always, rather than only those
5821          * attribute names which actually exist for a file.  Hmmm...
5822          */
5823         const char supported_old[] =
5824                 "system.*\0"
5825                 "system.*+\0"
5826                 "system.nt_sec_desc.revision\0"
5827                 "system.nt_sec_desc.owner\0"
5828                 "system.nt_sec_desc.owner+\0"
5829                 "system.nt_sec_desc.group\0"
5830                 "system.nt_sec_desc.group+\0"
5831                 "system.nt_sec_desc.acl.*\0"
5832                 "system.nt_sec_desc.acl\0"
5833                 "system.nt_sec_desc.acl+\0"
5834                 "system.nt_sec_desc.*\0"
5835                 "system.nt_sec_desc.*+\0"
5836                 "system.dos_attr.*\0"
5837                 "system.dos_attr.mode\0"
5838                 "system.dos_attr.c_time\0"
5839                 "system.dos_attr.a_time\0"
5840                 "system.dos_attr.m_time\0"
5841                 ;
5842         const char supported_new[] =
5843                 "system.*\0"
5844                 "system.*+\0"
5845                 "system.nt_sec_desc.revision\0"
5846                 "system.nt_sec_desc.owner\0"
5847                 "system.nt_sec_desc.owner+\0"
5848                 "system.nt_sec_desc.group\0"
5849                 "system.nt_sec_desc.group+\0"
5850                 "system.nt_sec_desc.acl.*\0"
5851                 "system.nt_sec_desc.acl\0"
5852                 "system.nt_sec_desc.acl+\0"
5853                 "system.nt_sec_desc.*\0"
5854                 "system.nt_sec_desc.*+\0"
5855                 "system.dos_attr.*\0"
5856                 "system.dos_attr.mode\0"
5857                 "system.dos_attr.create_time\0"
5858                 "system.dos_attr.access_time\0"
5859                 "system.dos_attr.write_time\0"
5860                 "system.dos_attr.change_time\0"
5861                 ;
5862         const char * supported;
5863
5864         if (context->internal->_full_time_names) {
5865                 supported = supported_new;
5866         } else {
5867                 supported = supported_old;
5868         }
5869
5870         if (size == 0) {
5871                 return sizeof(supported);
5872         }
5873
5874         if (sizeof(supported) > size) {
5875                 errno = ERANGE;
5876                 return -1;
5877         }
5878
5879         /* this can't be strcpy() because there are embedded null characters */
5880         memcpy(list, supported, sizeof(supported));
5881         return sizeof(supported);
5882 }
5883
5884
5885 /*
5886  * Open a print file to be written to by other calls
5887  */
5888
5889 static SMBCFILE *
5890 smbc_open_print_job_ctx(SMBCCTX *context,
5891                         const char *fname)
5892 {
5893         fstring server;
5894         fstring share;
5895         fstring user;
5896         fstring password;
5897         pstring path;
5898         
5899         if (!context || !context->internal ||
5900             !context->internal->_initialized) {
5901
5902                 errno = EINVAL;
5903                 return NULL;
5904     
5905         }
5906
5907         if (!fname) {
5908
5909                 errno = EINVAL;
5910                 return NULL;
5911
5912         }
5913   
5914         DEBUG(4, ("smbc_open_print_job_ctx(%s)\n", fname));
5915
5916         if (smbc_parse_path(context, fname,
5917                             NULL, 0,
5918                             server, sizeof(server),
5919                             share, sizeof(share),
5920                             path, sizeof(path),
5921                             user, sizeof(user),
5922                             password, sizeof(password),
5923                             NULL, 0)) {
5924                 errno = EINVAL;
5925                 return NULL;
5926         }
5927
5928         /* What if the path is empty, or the file exists? */
5929
5930         return context->open(context, fname, O_WRONLY, 666);
5931
5932 }
5933
5934 /*
5935  * Routine to print a file on a remote server ...
5936  *
5937  * We open the file, which we assume to be on a remote server, and then
5938  * copy it to a print file on the share specified by printq.
5939  */
5940
5941 static int
5942 smbc_print_file_ctx(SMBCCTX *c_file,
5943                     const char *fname,
5944                     SMBCCTX *c_print,
5945                     const char *printq)
5946 {
5947         SMBCFILE *fid1;
5948         SMBCFILE *fid2;
5949         int bytes;
5950         int saverr;
5951         int tot_bytes = 0;
5952         char buf[4096];
5953
5954         if (!c_file || !c_file->internal->_initialized || !c_print ||
5955             !c_print->internal->_initialized) {
5956
5957                 errno = EINVAL;
5958                 return -1;
5959
5960         }
5961
5962         if (!fname && !printq) {
5963
5964                 errno = EINVAL;
5965                 return -1;
5966
5967         }
5968
5969         /* Try to open the file for reading ... */
5970
5971         if ((long)(fid1 = c_file->open(c_file, fname, O_RDONLY, 0666)) < 0) {
5972                 
5973                 DEBUG(3, ("Error, fname=%s, errno=%i\n", fname, errno));
5974                 return -1;  /* smbc_open sets errno */
5975                 
5976         }
5977
5978         /* Now, try to open the printer file for writing */
5979
5980         if ((long)(fid2 = c_print->open_print_job(c_print, printq)) < 0) {
5981
5982                 saverr = errno;  /* Save errno */
5983                 c_file->close_fn(c_file, fid1);
5984                 errno = saverr;
5985                 return -1;
5986
5987         }
5988
5989         while ((bytes = c_file->read(c_file, fid1, buf, sizeof(buf))) > 0) {
5990
5991                 tot_bytes += bytes;
5992
5993                 if ((c_print->write(c_print, fid2, buf, bytes)) < 0) {
5994
5995                         saverr = errno;
5996                         c_file->close_fn(c_file, fid1);
5997                         c_print->close_fn(c_print, fid2);
5998                         errno = saverr;
5999
6000                 }
6001
6002         }
6003
6004         saverr = errno;
6005
6006         c_file->close_fn(c_file, fid1);  /* We have to close these anyway */
6007         c_print->close_fn(c_print, fid2);
6008
6009         if (bytes < 0) {
6010
6011                 errno = saverr;
6012                 return -1;
6013
6014         }
6015
6016         return tot_bytes;
6017
6018 }
6019
6020 /*
6021  * Routine to list print jobs on a printer share ...
6022  */
6023
6024 static int
6025 smbc_list_print_jobs_ctx(SMBCCTX *context,
6026                          const char *fname,
6027                          smbc_list_print_job_fn fn)
6028 {
6029         SMBCSRV *srv;
6030         fstring server;
6031         fstring share;
6032         fstring user;
6033         fstring password;
6034         fstring workgroup;
6035         pstring path;
6036
6037         if (!context || !context->internal ||
6038             !context->internal->_initialized) {
6039
6040                 errno = EINVAL;
6041                 return -1;
6042
6043         }
6044
6045         if (!fname) {
6046                 
6047                 errno = EINVAL;
6048                 return -1;
6049
6050         }
6051   
6052         DEBUG(4, ("smbc_list_print_jobs(%s)\n", fname));
6053
6054         if (smbc_parse_path(context, fname,
6055                             workgroup, sizeof(workgroup),
6056                             server, sizeof(server),
6057                             share, sizeof(share),
6058                             path, sizeof(path),
6059                             user, sizeof(user),
6060                             password, sizeof(password),
6061                             NULL, 0)) {
6062                 errno = EINVAL;
6063                 return -1;
6064         }
6065
6066         if (user[0] == (char)0) fstrcpy(user, context->user);
6067         
6068         srv = smbc_server(context, True,
6069                           server, share, workgroup, user, password);
6070
6071         if (!srv) {
6072
6073                 return -1;  /* errno set by smbc_server */
6074
6075         }
6076
6077         if (cli_print_queue(srv->cli,
6078                             (void (*)(struct print_job_info *))fn) < 0) {
6079
6080                 errno = smbc_errno(context, srv->cli);
6081                 return -1;
6082
6083         }
6084         
6085         return 0;
6086
6087 }
6088
6089 /*
6090  * Delete a print job from a remote printer share
6091  */
6092
6093 static int
6094 smbc_unlink_print_job_ctx(SMBCCTX *context,
6095                           const char *fname,
6096                           int id)
6097 {
6098         SMBCSRV *srv;
6099         fstring server;
6100         fstring share;
6101         fstring user;
6102         fstring password;
6103         fstring workgroup;
6104         pstring path;
6105         int err;
6106
6107         if (!context || !context->internal ||
6108             !context->internal->_initialized) {
6109
6110                 errno = EINVAL;
6111                 return -1;
6112
6113         }
6114
6115         if (!fname) {
6116
6117                 errno = EINVAL;
6118                 return -1;
6119
6120         }
6121   
6122         DEBUG(4, ("smbc_unlink_print_job(%s)\n", fname));
6123
6124         if (smbc_parse_path(context, fname,
6125                             workgroup, sizeof(workgroup),
6126                             server, sizeof(server),
6127                             share, sizeof(share),
6128                             path, sizeof(path),
6129                             user, sizeof(user),
6130                             password, sizeof(password),
6131                             NULL, 0)) {
6132                 errno = EINVAL;
6133                 return -1;
6134         }
6135
6136         if (user[0] == (char)0) fstrcpy(user, context->user);
6137
6138         srv = smbc_server(context, True,
6139                           server, share, workgroup, user, password);
6140
6141         if (!srv) {
6142
6143                 return -1;  /* errno set by smbc_server */
6144
6145         }
6146
6147         if ((err = cli_printjob_del(srv->cli, id)) != 0) {
6148
6149                 if (err < 0)
6150                         errno = smbc_errno(context, srv->cli);
6151                 else if (err == ERRnosuchprintjob)
6152                         errno = EINVAL;
6153                 return -1;
6154
6155         }
6156
6157         return 0;
6158
6159 }
6160
6161 /*
6162  * Get a new empty handle to fill in with your own info 
6163  */
6164 SMBCCTX *
6165 smbc_new_context(void)
6166 {
6167         SMBCCTX *context;
6168
6169         context = SMB_MALLOC_P(SMBCCTX);
6170         if (!context) {
6171                 errno = ENOMEM;
6172                 return NULL;
6173         }
6174
6175         ZERO_STRUCTP(context);
6176
6177         context->internal = SMB_MALLOC_P(struct smbc_internal_data);
6178         if (!context->internal) {
6179                 SAFE_FREE(context);
6180                 errno = ENOMEM;
6181                 return NULL;
6182         }
6183
6184         ZERO_STRUCTP(context->internal);
6185
6186         
6187         /* ADD REASONABLE DEFAULTS */
6188         context->debug            = 0;
6189         context->timeout          = 20000; /* 20 seconds */
6190
6191         context->options.browse_max_lmb_count      = 3;    /* # LMBs to query */
6192         context->options.urlencode_readdir_entries = False;/* backward compat */
6193         context->options.one_share_per_server      = False;/* backward compat */
6194         context->internal->_share_mode             = SMBC_SHAREMODE_DENY_NONE;
6195                                 /* backward compat */
6196
6197         context->open                              = smbc_open_ctx;
6198         context->creat                             = smbc_creat_ctx;
6199         context->read                              = smbc_read_ctx;
6200         context->write                             = smbc_write_ctx;
6201         context->close_fn                          = smbc_close_ctx;
6202         context->unlink                            = smbc_unlink_ctx;
6203         context->rename                            = smbc_rename_ctx;
6204         context->lseek                             = smbc_lseek_ctx;
6205         context->stat                              = smbc_stat_ctx;
6206         context->fstat                             = smbc_fstat_ctx;
6207         context->opendir                           = smbc_opendir_ctx;
6208         context->closedir                          = smbc_closedir_ctx;
6209         context->readdir                           = smbc_readdir_ctx;
6210         context->getdents                          = smbc_getdents_ctx;
6211         context->mkdir                             = smbc_mkdir_ctx;
6212         context->rmdir                             = smbc_rmdir_ctx;
6213         context->telldir                           = smbc_telldir_ctx;
6214         context->lseekdir                          = smbc_lseekdir_ctx;
6215         context->fstatdir                          = smbc_fstatdir_ctx;
6216         context->chmod                             = smbc_chmod_ctx;
6217         context->utimes                            = smbc_utimes_ctx;
6218         context->setxattr                          = smbc_setxattr_ctx;
6219         context->getxattr                          = smbc_getxattr_ctx;
6220         context->removexattr                       = smbc_removexattr_ctx;
6221         context->listxattr                         = smbc_listxattr_ctx;
6222         context->open_print_job                    = smbc_open_print_job_ctx;
6223         context->print_file                        = smbc_print_file_ctx;
6224         context->list_print_jobs                   = smbc_list_print_jobs_ctx;
6225         context->unlink_print_job                  = smbc_unlink_print_job_ctx;
6226
6227         context->callbacks.check_server_fn         = smbc_check_server;
6228         context->callbacks.remove_unused_server_fn = smbc_remove_unused_server;
6229
6230         smbc_default_cache_functions(context);
6231
6232         return context;
6233 }
6234
6235 /* 
6236  * Free a context
6237  *
6238  * Returns 0 on success. Otherwise returns 1, the SMBCCTX is _not_ freed 
6239  * and thus you'll be leaking memory if not handled properly.
6240  *
6241  */
6242 int
6243 smbc_free_context(SMBCCTX *context,
6244                   int shutdown_ctx)
6245 {
6246         if (!context) {
6247                 errno = EBADF;
6248                 return 1;
6249         }
6250         
6251         if (shutdown_ctx) {
6252                 SMBCFILE * f;
6253                 DEBUG(1,("Performing aggressive shutdown.\n"));
6254                 
6255                 f = context->internal->_files;
6256                 while (f) {
6257                         context->close_fn(context, f);
6258                         f = f->next;
6259                 }
6260                 context->internal->_files = NULL;
6261
6262                 /* First try to remove the servers the nice way. */
6263                 if (context->callbacks.purge_cached_fn(context)) {
6264                         SMBCSRV * s;
6265                         SMBCSRV * next;
6266                         DEBUG(1, ("Could not purge all servers, "
6267                                   "Nice way shutdown failed.\n"));
6268                         s = context->internal->_servers;
6269                         while (s) {
6270                                 DEBUG(1, ("Forced shutdown: %p (fd=%d)\n",
6271                                           s, s->cli->fd));
6272                                 cli_shutdown(s->cli);
6273                                 context->callbacks.remove_cached_srv_fn(context,
6274                                                                         s);
6275                                 next = s->next;
6276                                 DLIST_REMOVE(context->internal->_servers, s);
6277                                 SAFE_FREE(s);
6278                                 s = next;
6279                         }
6280                         context->internal->_servers = NULL;
6281                 }
6282         }
6283         else {
6284                 /* This is the polite way */    
6285                 if (context->callbacks.purge_cached_fn(context)) {
6286                         DEBUG(1, ("Could not purge all servers, "
6287                                   "free_context failed.\n"));
6288                         errno = EBUSY;
6289                         return 1;
6290                 }
6291                 if (context->internal->_servers) {
6292                         DEBUG(1, ("Active servers in context, "
6293                                   "free_context failed.\n"));
6294                         errno = EBUSY;
6295                         return 1;
6296                 }
6297                 if (context->internal->_files) {
6298                         DEBUG(1, ("Active files in context, "
6299                                   "free_context failed.\n"));
6300                         errno = EBUSY;
6301                         return 1;
6302                 }               
6303         }
6304
6305         /* Things we have to clean up */
6306         SAFE_FREE(context->workgroup);
6307         SAFE_FREE(context->netbios_name);
6308         SAFE_FREE(context->user);
6309         
6310         DEBUG(3, ("Context %p succesfully freed\n", context));
6311         SAFE_FREE(context->internal);
6312         SAFE_FREE(context);
6313         return 0;
6314 }
6315
6316
6317 /*
6318  * Each time the context structure is changed, we have binary backward
6319  * compatibility issues.  Instead of modifying the public portions of the
6320  * context structure to add new options, instead, we put them in the internal
6321  * portion of the context structure and provide a set function for these new
6322  * options.
6323  */
6324 void
6325 smbc_option_set(SMBCCTX *context,
6326                 char *option_name,
6327                 ... /* option_value */)
6328 {
6329         va_list ap;
6330         union {
6331                 int i;
6332                 BOOL b;
6333                 smbc_get_auth_data_with_context_fn auth_fn;
6334                 void *v;
6335         } option_value;
6336
6337         va_start(ap, option_name);
6338
6339         if (strcmp(option_name, "debug_to_stderr") == 0) {
6340                 /*
6341                  * Log to standard error instead of standard output.
6342                  */
6343                 option_value.b = (BOOL) va_arg(ap, int);
6344                 context->internal->_debug_stderr = option_value.b;
6345
6346         } else if (strcmp(option_name, "full_time_names") == 0) {
6347                 /*
6348                  * Use new-style time attribute names, e.g. WRITE_TIME rather
6349                  * than the old-style names such as M_TIME.  This allows also
6350                  * setting/getting CREATE_TIME which was previously
6351                  * unimplemented.  (Note that the old C_TIME was supposed to
6352                  * be CHANGE_TIME but was confused and sometimes referred to
6353                  * CREATE_TIME.)
6354                  */
6355                 option_value.b = (BOOL) va_arg(ap, int);
6356                 context->internal->_full_time_names = option_value.b;
6357
6358         } else if (strcmp(option_name, "open_share_mode") == 0) {
6359                 /*
6360                  * The share mode to use for files opened with
6361                  * smbc_open_ctx().  The default is SMBC_SHAREMODE_DENY_NONE.
6362                  */
6363                 option_value.i = va_arg(ap, int);
6364                 context->internal->_share_mode =
6365                         (smbc_share_mode) option_value.i;
6366
6367         } else if (strcmp(option_name, "auth_function") == 0) {
6368                 /*
6369                  * Use the new-style authentication function which includes
6370                  * the context.
6371                  */
6372                 option_value.auth_fn =
6373                         va_arg(ap, smbc_get_auth_data_with_context_fn);
6374                 context->internal->_auth_fn_with_context =
6375                         option_value.auth_fn;
6376         } else if (strcmp(option_name, "user_data") == 0) {
6377                 /*
6378                  * Save a user data handle which may be retrieved by the user
6379                  * with smbc_option_get()
6380                  */
6381                 option_value.v = va_arg(ap, void *);
6382                 context->internal->_user_data = option_value.v;
6383         }
6384
6385         va_end(ap);
6386 }
6387
6388
6389 /*
6390  * Retrieve the current value of an option
6391  */
6392 void *
6393 smbc_option_get(SMBCCTX *context,
6394                 char *option_name)
6395 {
6396         if (strcmp(option_name, "debug_stderr") == 0) {
6397                 /*
6398                  * Log to standard error instead of standard output.
6399                  */
6400 #if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
6401                 return (void *) (intptr_t) context->internal->_debug_stderr;
6402 #else
6403                 return (void *) context->internal->_debug_stderr;
6404 #endif
6405         } else if (strcmp(option_name, "full_time_names") == 0) {
6406                 /*
6407                  * Use new-style time attribute names, e.g. WRITE_TIME rather
6408                  * than the old-style names such as M_TIME.  This allows also
6409                  * setting/getting CREATE_TIME which was previously
6410                  * unimplemented.  (Note that the old C_TIME was supposed to
6411                  * be CHANGE_TIME but was confused and sometimes referred to
6412                  * CREATE_TIME.)
6413                  */
6414 #if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
6415                 return (void *) (intptr_t) context->internal->_full_time_names;
6416 #else
6417                 return (void *) context->internal->_full_time_names;
6418 #endif
6419
6420         } else if (strcmp(option_name, "auth_function") == 0) {
6421                 /*
6422                  * Use the new-style authentication function which includes
6423                  * the context.
6424                  */
6425                 return (void *) context->internal->_auth_fn_with_context;
6426         } else if (strcmp(option_name, "user_data") == 0) {
6427                 /*
6428                  * Save a user data handle which may be retrieved by the user
6429                  * with smbc_option_get()
6430                  */
6431                 return context->internal->_user_data;
6432         }
6433
6434         return NULL;
6435 }
6436
6437
6438 /*
6439  * Initialise the library etc 
6440  *
6441  * We accept a struct containing handle information.
6442  * valid values for info->debug from 0 to 100,
6443  * and insist that info->fn must be non-null.
6444  */
6445 SMBCCTX *
6446 smbc_init_context(SMBCCTX *context)
6447 {
6448         pstring conf;
6449         int pid;
6450         char *user = NULL;
6451         char *home = NULL;
6452
6453         if (!context || !context->internal) {
6454                 errno = EBADF;
6455                 return NULL;
6456         }
6457
6458         /* Do not initialise the same client twice */
6459         if (context->internal->_initialized) { 
6460                 return 0;
6461         }
6462
6463         if ((!context->callbacks.auth_fn &&
6464              !context->internal->_auth_fn_with_context) ||
6465             context->debug < 0 ||
6466             context->debug > 100) {
6467
6468                 errno = EINVAL;
6469                 return NULL;
6470
6471         }
6472
6473         if (!smbc_initialized) {
6474                 /*
6475                  * Do some library-wide intializations the first time we get
6476                  * called
6477                  */
6478                 BOOL conf_loaded = False;
6479
6480                 /* Set this to what the user wants */
6481                 DEBUGLEVEL = context->debug;
6482                 
6483                 load_case_tables();
6484
6485                 setup_logging("libsmbclient", True);
6486                 if (context->internal->_debug_stderr) {
6487                         dbf = x_stderr;
6488                         x_setbuf(x_stderr, NULL);
6489                 }
6490
6491                 /* Here we would open the smb.conf file if needed ... */
6492                 
6493                 in_client = True; /* FIXME, make a param */
6494
6495                 home = getenv("HOME");
6496                 if (home) {
6497                         slprintf(conf, sizeof(conf), "%s/.smb/smb.conf", home);
6498                         if (lp_load(conf, True, False, False, True)) {
6499                                 conf_loaded = True;
6500                         } else {
6501                                 DEBUG(5, ("Could not load config file: %s\n",
6502                                           conf));
6503                         }
6504                 }
6505  
6506                 if (!conf_loaded) {
6507                         /*
6508                          * Well, if that failed, try the dyn_CONFIGFILE
6509                          * Which points to the standard locn, and if that
6510                          * fails, silently ignore it and use the internal
6511                          * defaults ...
6512                          */
6513
6514                         if (!lp_load(dyn_CONFIGFILE, True, False, False, False)) {
6515                                 DEBUG(5, ("Could not load config file: %s\n",
6516                                           dyn_CONFIGFILE));
6517                         } else if (home) {
6518                                 /*
6519                                  * We loaded the global config file.  Now lets
6520                                  * load user-specific modifications to the
6521                                  * global config.
6522                                  */
6523                                 slprintf(conf, sizeof(conf),
6524                                          "%s/.smb/smb.conf.append", home);
6525                                 if (!lp_load(conf, True, False, False, False)) {
6526                                         DEBUG(10,
6527                                               ("Could not append config file: "
6528                                                "%s\n",
6529                                                conf));
6530                                 }
6531                         }
6532                 }
6533
6534                 load_interfaces();  /* Load the list of interfaces ... */
6535                 
6536                 reopen_logs();  /* Get logging working ... */
6537         
6538                 /* 
6539                  * Block SIGPIPE (from lib/util_sock.c: write())  
6540                  * It is not needed and should not stop execution 
6541                  */
6542                 BlockSignals(True, SIGPIPE);
6543                 
6544                 /* Done with one-time initialisation */
6545                 smbc_initialized = 1; 
6546
6547         }
6548         
6549         if (!context->user) {
6550                 /*
6551                  * FIXME: Is this the best way to get the user info? 
6552                  */
6553                 user = getenv("USER");
6554                 /* walk around as "guest" if no username can be found */
6555                 if (!user) context->user = SMB_STRDUP("guest");
6556                 else context->user = SMB_STRDUP(user);
6557         }
6558
6559         if (!context->netbios_name) {
6560                 /*
6561                  * We try to get our netbios name from the config. If that
6562                  * fails we fall back on constructing our netbios name from
6563                  * our hostname etc
6564                  */
6565                 if (global_myname()) {
6566                         context->netbios_name = SMB_STRDUP(global_myname());
6567                 }
6568                 else {
6569                         /*
6570                          * Hmmm, I want to get hostname as well, but I am too
6571                          * lazy for the moment
6572                          */
6573                         pid = sys_getpid();
6574                         context->netbios_name = (char *)SMB_MALLOC(17);
6575                         if (!context->netbios_name) {
6576                                 errno = ENOMEM;
6577                                 return NULL;
6578                         }
6579                         slprintf(context->netbios_name, 16,
6580                                  "smbc%s%d", context->user, pid);
6581                 }
6582         }
6583
6584         DEBUG(1, ("Using netbios name %s.\n", context->netbios_name));
6585
6586         if (!context->workgroup) {
6587                 if (lp_workgroup()) {
6588                         context->workgroup = SMB_STRDUP(lp_workgroup());
6589                 }
6590                 else {
6591                         /* TODO: Think about a decent default workgroup */
6592                         context->workgroup = SMB_STRDUP("samba");
6593                 }
6594         }
6595
6596         DEBUG(1, ("Using workgroup %s.\n", context->workgroup));
6597                                         
6598         /* shortest timeout is 1 second */
6599         if (context->timeout > 0 && context->timeout < 1000) 
6600                 context->timeout = 1000;
6601
6602         /*
6603          * FIXME: Should we check the function pointers here? 
6604          */
6605
6606         context->internal->_initialized = True;
6607         
6608         return context;
6609 }
6610
6611
6612 /* Return the verion of samba, and thus libsmbclient */
6613 const char *
6614 smbc_version(void)
6615 {
6616         return samba_version_string();
6617 }