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