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