r24410: - I got tricked by function naming. Contrary to what seemed obvious to me,
[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                 old->dacl = NULL;
5146                 dacl = old->dacl;
5147                 break;
5148
5149         case SMBC_XATTR_MODE_REMOVE:
5150                 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
5151                         BOOL found = False;
5152
5153                         for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
5154                                 if (sec_ace_equal(&sd->dacl->aces[i],
5155                                                   &old->dacl->aces[j])) {
5156                                         uint32 k;
5157                                         for (k=j; k<old->dacl->num_aces-1;k++) {
5158                                                 old->dacl->aces[k] =
5159                                                         old->dacl->aces[k+1];
5160                                         }
5161                                         old->dacl->num_aces--;
5162                                         if (old->dacl->num_aces == 0) {
5163                                                 old->dacl = NULL;
5164                                         }
5165                                         found = True;
5166                                         dacl = old->dacl;
5167                                         break;
5168                                 }
5169                         }
5170
5171                         if (!found) {
5172                                 err = ENOATTR;
5173                                 ret = -1;
5174                                 goto failed;
5175                         }
5176                 }
5177                 break;
5178
5179         case SMBC_XATTR_MODE_ADD:
5180                 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
5181                         BOOL found = False;
5182
5183                         for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
5184                                 if (sid_equal(&sd->dacl->aces[i].trustee,
5185                                               &old->dacl->aces[j].trustee)) {
5186                                         if (!(flags & SMBC_XATTR_FLAG_CREATE)) {
5187                                                 err = EEXIST;
5188                                                 ret = -1;
5189                                                 goto failed;
5190                                         }
5191                                         old->dacl->aces[j] = sd->dacl->aces[i];
5192                                         ret = -1;
5193                                         found = True;
5194                                 }
5195                         }
5196
5197                         if (!found && (flags & SMBC_XATTR_FLAG_REPLACE)) {
5198                                 err = ENOATTR;
5199                                 ret = -1;
5200                                 goto failed;
5201                         }
5202                         
5203                         for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
5204                                 add_ace(&old->dacl, &sd->dacl->aces[i], ctx);
5205                         }
5206                 }
5207                 dacl = old->dacl;
5208                 break;
5209
5210         case SMBC_XATTR_MODE_SET:
5211                 old = sd;
5212                 owner_sid = old->owner_sid;
5213                 grp_sid = old->group_sid;
5214                 dacl = old->dacl;
5215                 break;
5216
5217         case SMBC_XATTR_MODE_CHOWN:
5218                 owner_sid = sd->owner_sid;
5219                 break;
5220
5221         case SMBC_XATTR_MODE_CHGRP:
5222                 grp_sid = sd->group_sid;
5223                 break;
5224         }
5225
5226         /* Denied ACE entries must come before allowed ones */
5227         sort_acl(old->dacl);
5228
5229         /* Create new security descriptor and set it */
5230         sd = make_sec_desc(ctx, old->revision, SEC_DESC_SELF_RELATIVE, 
5231                            owner_sid, grp_sid, NULL, dacl, &sd_size);
5232
5233         fnum = cli_nt_create(cli, filename,
5234                              WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS);
5235
5236         if (fnum == -1) {
5237                 DEBUG(5, ("cacl_set failed to open %s: %s\n",
5238                           filename, cli_errstr(cli)));
5239                 errno = 0;
5240                 return -1;
5241         }
5242
5243         if (!cli_set_secdesc(cli, fnum, sd)) {
5244                 DEBUG(5, ("ERROR: secdesc set failed: %s\n", cli_errstr(cli)));
5245                 ret = -1;
5246         }
5247
5248         /* Clean up */
5249
5250  failed:
5251         cli_close(cli, fnum);
5252
5253         if (err != 0) {
5254                 errno = err;
5255         }
5256         
5257         return ret;
5258 }
5259
5260
5261 static int
5262 smbc_setxattr_ctx(SMBCCTX *context,
5263                   const char *fname,
5264                   const char *name,
5265                   const void *value,
5266                   size_t size,
5267                   int flags)
5268 {
5269         int ret;
5270         int ret2;
5271         SMBCSRV *srv;
5272         SMBCSRV *ipc_srv;
5273         fstring server;
5274         fstring share;
5275         fstring user;
5276         fstring password;
5277         fstring workgroup;
5278         pstring path;
5279         TALLOC_CTX *ctx;
5280         POLICY_HND pol;
5281         DOS_ATTR_DESC *dad;
5282         struct {
5283                 const char * create_time_attr;
5284                 const char * access_time_attr;
5285                 const char * write_time_attr;
5286                 const char * change_time_attr;
5287         } attr_strings;
5288
5289         if (!context || !context->internal ||
5290             !context->internal->_initialized) {
5291
5292                 errno = EINVAL;  /* Best I can think of ... */
5293                 return -1;
5294     
5295         }
5296
5297         if (!fname) {
5298
5299                 errno = EINVAL;
5300                 return -1;
5301
5302         }
5303   
5304         DEBUG(4, ("smbc_setxattr(%s, %s, %.*s)\n",
5305                   fname, name, (int) size, (const char*)value));
5306
5307         if (smbc_parse_path(context, fname,
5308                             workgroup, sizeof(workgroup),
5309                             server, sizeof(server),
5310                             share, sizeof(share),
5311                             path, sizeof(path),
5312                             user, sizeof(user),
5313                             password, sizeof(password),
5314                             NULL, 0)) {
5315                 errno = EINVAL;
5316                 return -1;
5317         }
5318
5319         if (user[0] == (char)0) fstrcpy(user, context->user);
5320
5321         srv = smbc_server(context, True,
5322                           server, share, workgroup, user, password);
5323         if (!srv) {
5324                 return -1;  /* errno set by smbc_server */
5325         }
5326
5327         if (! srv->no_nt_session) {
5328                 ipc_srv = smbc_attr_server(context, server, share,
5329                                            workgroup, user, password,
5330                                            &pol);
5331                 if (! ipc_srv) {
5332                         srv->no_nt_session = True;
5333                 }
5334         } else {
5335                 ipc_srv = NULL;
5336         }
5337         
5338         ctx = talloc_init("smbc_setxattr");
5339         if (!ctx) {
5340                 errno = ENOMEM;
5341                 return -1;
5342         }
5343
5344         /*
5345          * Are they asking to set the entire set of known attributes?
5346          */
5347         if (StrCaseCmp(name, "system.*") == 0 ||
5348             StrCaseCmp(name, "system.*+") == 0) {
5349                 /* Yup. */
5350                 char *namevalue =
5351                         talloc_asprintf(ctx, "%s:%s",
5352                                         name+7, (const char *) value);
5353                 if (! namevalue) {
5354                         errno = ENOMEM;
5355                         ret = -1;
5356                         return -1;
5357                 }
5358
5359                 if (ipc_srv) {
5360                         ret = cacl_set(ctx, srv->cli,
5361                                        ipc_srv->cli, &pol, path,
5362                                        namevalue,
5363                                        (*namevalue == '*'
5364                                         ? SMBC_XATTR_MODE_SET
5365                                         : SMBC_XATTR_MODE_ADD),
5366                                        flags);
5367                 } else {
5368                         ret = 0;
5369                 }
5370
5371                 /* get a DOS Attribute Descriptor with current attributes */
5372                 dad = dos_attr_query(context, ctx, path, srv);
5373                 if (dad) {
5374                         /* Overwrite old with new, using what was provided */
5375                         dos_attr_parse(context, dad, srv, namevalue);
5376
5377                         /* Set the new DOS attributes */
5378                         if (! smbc_setatr(context, srv, path,
5379                                           dad->create_time,
5380                                           dad->access_time,
5381                                           dad->write_time,
5382                                           dad->change_time,
5383                                           dad->mode)) {
5384
5385                                 /* cause failure if NT failed too */
5386                                 dad = NULL; 
5387                         }
5388                 }
5389
5390                 /* we only fail if both NT and DOS sets failed */
5391                 if (ret < 0 && ! dad) {
5392                         ret = -1; /* in case dad was null */
5393                 }
5394                 else {
5395                         ret = 0;
5396                 }
5397
5398                 talloc_destroy(ctx);
5399                 return ret;
5400         }
5401
5402         /*
5403          * Are they asking to set an access control element or to set
5404          * the entire access control list?
5405          */
5406         if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
5407             StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 ||
5408             StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
5409             StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
5410             StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) {
5411
5412                 /* Yup. */
5413                 char *namevalue =
5414                         talloc_asprintf(ctx, "%s:%s",
5415                                         name+19, (const char *) value);
5416
5417                 if (! ipc_srv) {
5418                         ret = -1; /* errno set by smbc_server() */
5419                 }
5420                 else if (! namevalue) {
5421                         errno = ENOMEM;
5422                         ret = -1;
5423                 } else {
5424                         ret = cacl_set(ctx, srv->cli,
5425                                        ipc_srv->cli, &pol, path,
5426                                        namevalue,
5427                                        (*namevalue == '*'
5428                                         ? SMBC_XATTR_MODE_SET
5429                                         : SMBC_XATTR_MODE_ADD),
5430                                        flags);
5431                 }
5432                 talloc_destroy(ctx);
5433                 return ret;
5434         }
5435
5436         /*
5437          * Are they asking to set the owner?
5438          */
5439         if (StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
5440             StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0) {
5441
5442                 /* Yup. */
5443                 char *namevalue =
5444                         talloc_asprintf(ctx, "%s:%s",
5445                                         name+19, (const char *) value);
5446
5447                 if (! ipc_srv) {
5448                         
5449                         ret = -1; /* errno set by smbc_server() */
5450                 }
5451                 else if (! namevalue) {
5452                         errno = ENOMEM;
5453                         ret = -1;
5454                 } else {
5455                         ret = cacl_set(ctx, srv->cli,
5456                                        ipc_srv->cli, &pol, path,
5457                                        namevalue, SMBC_XATTR_MODE_CHOWN, 0);
5458                 }
5459                 talloc_destroy(ctx);
5460                 return ret;
5461         }
5462
5463         /*
5464          * Are they asking to set the group?
5465          */
5466         if (StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
5467             StrCaseCmp(name, "system.nt_sec_desc.group+") == 0) {
5468
5469                 /* Yup. */
5470                 char *namevalue =
5471                         talloc_asprintf(ctx, "%s:%s",
5472                                         name+19, (const char *) value);
5473
5474                 if (! ipc_srv) {
5475                         /* errno set by smbc_server() */
5476                         ret = -1;
5477                 }
5478                 else if (! namevalue) {
5479                         errno = ENOMEM;
5480                         ret = -1;
5481                 } else {
5482                         ret = cacl_set(ctx, srv->cli,
5483                                        ipc_srv->cli, &pol, path,
5484                                        namevalue, SMBC_XATTR_MODE_CHOWN, 0);
5485                 }
5486                 talloc_destroy(ctx);
5487                 return ret;
5488         }
5489
5490         /* Determine whether to use old-style or new-style attribute names */
5491         if (context->internal->_full_time_names) {
5492                 /* new-style names */
5493                 attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME";
5494                 attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME";
5495                 attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME";
5496                 attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME";
5497         } else {
5498                 /* old-style names */
5499                 attr_strings.create_time_attr = NULL;
5500                 attr_strings.access_time_attr = "system.dos_attr.A_TIME";
5501                 attr_strings.write_time_attr = "system.dos_attr.M_TIME";
5502                 attr_strings.change_time_attr = "system.dos_attr.C_TIME";
5503         }
5504
5505         /*
5506          * Are they asking to set a DOS attribute?
5507          */
5508         if (StrCaseCmp(name, "system.dos_attr.*") == 0 ||
5509             StrCaseCmp(name, "system.dos_attr.mode") == 0 ||
5510             (attr_strings.create_time_attr != NULL &&
5511              StrCaseCmp(name, attr_strings.create_time_attr) == 0) ||
5512             StrCaseCmp(name, attr_strings.access_time_attr) == 0 ||
5513             StrCaseCmp(name, attr_strings.write_time_attr) == 0 ||
5514             StrCaseCmp(name, attr_strings.change_time_attr) == 0) {
5515
5516                 /* get a DOS Attribute Descriptor with current attributes */
5517                 dad = dos_attr_query(context, ctx, path, srv);
5518                 if (dad) {
5519                         char *namevalue =
5520                                 talloc_asprintf(ctx, "%s:%s",
5521                                                 name+16, (const char *) value);
5522                         if (! namevalue) {
5523                                 errno = ENOMEM;
5524                                 ret = -1;
5525                         } else {
5526                                 /* Overwrite old with provided new params */
5527                                 dos_attr_parse(context, dad, srv, namevalue);
5528
5529                                 /* Set the new DOS attributes */
5530                                 ret2 = smbc_setatr(context, srv, path,
5531                                                    dad->create_time,
5532                                                    dad->access_time,
5533                                                    dad->write_time,
5534                                                    dad->change_time,
5535                                                    dad->mode);
5536
5537                                 /* ret2 has True (success) / False (failure) */
5538                                 if (ret2) {
5539                                         ret = 0;
5540                                 } else {
5541                                         ret = -1;
5542                                 }
5543                         }
5544                 } else {
5545                         ret = -1;
5546                 }
5547
5548                 talloc_destroy(ctx);
5549                 return ret;
5550         }
5551
5552         /* Unsupported attribute name */
5553         talloc_destroy(ctx);
5554         errno = EINVAL;
5555         return -1;
5556 }
5557
5558 static int
5559 smbc_getxattr_ctx(SMBCCTX *context,
5560                   const char *fname,
5561                   const char *name,
5562                   const void *value,
5563                   size_t size)
5564 {
5565         int ret;
5566         SMBCSRV *srv;
5567         SMBCSRV *ipc_srv;
5568         fstring server;
5569         fstring share;
5570         fstring user;
5571         fstring password;
5572         fstring workgroup;
5573         pstring path;
5574         TALLOC_CTX *ctx;
5575         POLICY_HND pol;
5576         struct {
5577                 const char * create_time_attr;
5578                 const char * access_time_attr;
5579                 const char * write_time_attr;
5580                 const char * change_time_attr;
5581         } attr_strings;
5582
5583
5584         if (!context || !context->internal ||
5585             !context->internal->_initialized) {
5586
5587                 errno = EINVAL;  /* Best I can think of ... */
5588                 return -1;
5589     
5590         }
5591
5592         if (!fname) {
5593
5594                 errno = EINVAL;
5595                 return -1;
5596
5597         }
5598   
5599         DEBUG(4, ("smbc_getxattr(%s, %s)\n", fname, name));
5600
5601         if (smbc_parse_path(context, fname,
5602                             workgroup, sizeof(workgroup),
5603                             server, sizeof(server),
5604                             share, sizeof(share),
5605                             path, sizeof(path),
5606                             user, sizeof(user),
5607                             password, sizeof(password),
5608                             NULL, 0)) {
5609                 errno = EINVAL;
5610                 return -1;
5611         }
5612
5613         if (user[0] == (char)0) fstrcpy(user, context->user);
5614
5615         srv = smbc_server(context, True,
5616                           server, share, workgroup, user, password);
5617         if (!srv) {
5618                 return -1;  /* errno set by smbc_server */
5619         }
5620
5621         if (! srv->no_nt_session) {
5622                 ipc_srv = smbc_attr_server(context, server, share,
5623                                            workgroup, user, password,
5624                                            &pol);
5625                 if (! ipc_srv) {
5626                         srv->no_nt_session = True;
5627                 }
5628         } else {
5629                 ipc_srv = NULL;
5630         }
5631         
5632         ctx = talloc_init("smbc:getxattr");
5633         if (!ctx) {
5634                 errno = ENOMEM;
5635                 return -1;
5636         }
5637
5638         /* Determine whether to use old-style or new-style attribute names */
5639         if (context->internal->_full_time_names) {
5640                 /* new-style names */
5641                 attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME";
5642                 attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME";
5643                 attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME";
5644                 attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME";
5645         } else {
5646                 /* old-style names */
5647                 attr_strings.create_time_attr = NULL;
5648                 attr_strings.access_time_attr = "system.dos_attr.A_TIME";
5649                 attr_strings.write_time_attr = "system.dos_attr.M_TIME";
5650                 attr_strings.change_time_attr = "system.dos_attr.C_TIME";
5651         }
5652
5653         /* Are they requesting a supported attribute? */
5654         if (StrCaseCmp(name, "system.*") == 0 ||
5655             StrnCaseCmp(name, "system.*!", 9) == 0 ||
5656             StrCaseCmp(name, "system.*+") == 0 ||
5657             StrnCaseCmp(name, "system.*+!", 10) == 0 ||
5658             StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
5659             StrnCaseCmp(name, "system.nt_sec_desc.*!", 21) == 0 ||
5660             StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 ||
5661             StrnCaseCmp(name, "system.nt_sec_desc.*+!", 22) == 0 ||
5662             StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
5663             StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
5664             StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 ||
5665             StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
5666             StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 ||
5667             StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
5668             StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0 ||
5669             StrCaseCmp(name, "system.dos_attr.*") == 0 ||
5670             StrnCaseCmp(name, "system.dos_attr.*!", 18) == 0 ||
5671             StrCaseCmp(name, "system.dos_attr.mode") == 0 ||
5672             StrCaseCmp(name, "system.dos_attr.size") == 0 ||
5673             (attr_strings.create_time_attr != NULL &&
5674              StrCaseCmp(name, attr_strings.create_time_attr) == 0) ||
5675             StrCaseCmp(name, attr_strings.access_time_attr) == 0 ||
5676             StrCaseCmp(name, attr_strings.write_time_attr) == 0 ||
5677             StrCaseCmp(name, attr_strings.change_time_attr) == 0 ||
5678             StrCaseCmp(name, "system.dos_attr.inode") == 0) {
5679
5680                 /* Yup. */
5681                 ret = cacl_get(context, ctx, srv,
5682                                ipc_srv == NULL ? NULL : ipc_srv->cli, 
5683                                &pol, path,
5684                                CONST_DISCARD(char *, name),
5685                                CONST_DISCARD(char *, value), size);
5686                 if (ret < 0 && errno == 0) {
5687                         errno = smbc_errno(context, srv->cli);
5688                 }
5689                 talloc_destroy(ctx);
5690                 return ret;
5691         }
5692
5693         /* Unsupported attribute name */
5694         talloc_destroy(ctx);
5695         errno = EINVAL;
5696         return -1;
5697 }
5698
5699
5700 static int
5701 smbc_removexattr_ctx(SMBCCTX *context,
5702                      const char *fname,
5703                      const char *name)
5704 {
5705         int ret;
5706         SMBCSRV *srv;
5707         SMBCSRV *ipc_srv;
5708         fstring server;
5709         fstring share;
5710         fstring user;
5711         fstring password;
5712         fstring workgroup;
5713         pstring path;
5714         TALLOC_CTX *ctx;
5715         POLICY_HND pol;
5716
5717         if (!context || !context->internal ||
5718             !context->internal->_initialized) {
5719
5720                 errno = EINVAL;  /* Best I can think of ... */
5721                 return -1;
5722     
5723         }
5724
5725         if (!fname) {
5726
5727                 errno = EINVAL;
5728                 return -1;
5729
5730         }
5731   
5732         DEBUG(4, ("smbc_removexattr(%s, %s)\n", fname, name));
5733
5734         if (smbc_parse_path(context, fname,
5735                             workgroup, sizeof(workgroup),
5736                             server, sizeof(server),
5737                             share, sizeof(share),
5738                             path, sizeof(path),
5739                             user, sizeof(user),
5740                             password, sizeof(password),
5741                             NULL, 0)) {
5742                 errno = EINVAL;
5743                 return -1;
5744         }
5745
5746         if (user[0] == (char)0) fstrcpy(user, context->user);
5747
5748         srv = smbc_server(context, True,
5749                           server, share, workgroup, user, password);
5750         if (!srv) {
5751                 return -1;  /* errno set by smbc_server */
5752         }
5753
5754         if (! srv->no_nt_session) {
5755                 ipc_srv = smbc_attr_server(context, server, share,
5756                                            workgroup, user, password,
5757                                            &pol);
5758                 if (! ipc_srv) {
5759                         srv->no_nt_session = True;
5760                 }
5761         } else {
5762                 ipc_srv = NULL;
5763         }
5764         
5765         if (! ipc_srv) {
5766                 return -1; /* errno set by smbc_attr_server */
5767         }
5768
5769         ctx = talloc_init("smbc_removexattr");
5770         if (!ctx) {
5771                 errno = ENOMEM;
5772                 return -1;
5773         }
5774
5775         /* Are they asking to set the entire ACL? */
5776         if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
5777             StrCaseCmp(name, "system.nt_sec_desc.*+") == 0) {
5778
5779                 /* Yup. */
5780                 ret = cacl_set(ctx, srv->cli,
5781                                ipc_srv->cli, &pol, path,
5782                                NULL, SMBC_XATTR_MODE_REMOVE_ALL, 0);
5783                 talloc_destroy(ctx);
5784                 return ret;
5785         }
5786
5787         /*
5788          * Are they asking to remove one or more spceific security descriptor
5789          * attributes?
5790          */
5791         if (StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
5792             StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
5793             StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 ||
5794             StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
5795             StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 ||
5796             StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
5797             StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) {
5798
5799                 /* Yup. */
5800                 ret = cacl_set(ctx, srv->cli,
5801                                ipc_srv->cli, &pol, path,
5802                                name + 19, SMBC_XATTR_MODE_REMOVE, 0);
5803                 talloc_destroy(ctx);
5804                 return ret;
5805         }
5806
5807         /* Unsupported attribute name */
5808         talloc_destroy(ctx);
5809         errno = EINVAL;
5810         return -1;
5811 }
5812
5813 static int
5814 smbc_listxattr_ctx(SMBCCTX *context,
5815                    const char *fname,
5816                    char *list,
5817                    size_t size)
5818 {
5819         /*
5820          * This isn't quite what listxattr() is supposed to do.  This returns
5821          * the complete set of attribute names, always, rather than only those
5822          * attribute names which actually exist for a file.  Hmmm...
5823          */
5824         const char supported_old[] =
5825                 "system.*\0"
5826                 "system.*+\0"
5827                 "system.nt_sec_desc.revision\0"
5828                 "system.nt_sec_desc.owner\0"
5829                 "system.nt_sec_desc.owner+\0"
5830                 "system.nt_sec_desc.group\0"
5831                 "system.nt_sec_desc.group+\0"
5832                 "system.nt_sec_desc.acl.*\0"
5833                 "system.nt_sec_desc.acl\0"
5834                 "system.nt_sec_desc.acl+\0"
5835                 "system.nt_sec_desc.*\0"
5836                 "system.nt_sec_desc.*+\0"
5837                 "system.dos_attr.*\0"
5838                 "system.dos_attr.mode\0"
5839                 "system.dos_attr.c_time\0"
5840                 "system.dos_attr.a_time\0"
5841                 "system.dos_attr.m_time\0"
5842                 ;
5843         const char supported_new[] =
5844                 "system.*\0"
5845                 "system.*+\0"
5846                 "system.nt_sec_desc.revision\0"
5847                 "system.nt_sec_desc.owner\0"
5848                 "system.nt_sec_desc.owner+\0"
5849                 "system.nt_sec_desc.group\0"
5850                 "system.nt_sec_desc.group+\0"
5851                 "system.nt_sec_desc.acl.*\0"
5852                 "system.nt_sec_desc.acl\0"
5853                 "system.nt_sec_desc.acl+\0"
5854                 "system.nt_sec_desc.*\0"
5855                 "system.nt_sec_desc.*+\0"
5856                 "system.dos_attr.*\0"
5857                 "system.dos_attr.mode\0"
5858                 "system.dos_attr.create_time\0"
5859                 "system.dos_attr.access_time\0"
5860                 "system.dos_attr.write_time\0"
5861                 "system.dos_attr.change_time\0"
5862                 ;
5863         const char * supported;
5864
5865         if (context->internal->_full_time_names) {
5866                 supported = supported_new;
5867         } else {
5868                 supported = supported_old;
5869         }
5870
5871         if (size == 0) {
5872                 return sizeof(supported);
5873         }
5874
5875         if (sizeof(supported) > size) {
5876                 errno = ERANGE;
5877                 return -1;
5878         }
5879
5880         /* this can't be strcpy() because there are embedded null characters */
5881         memcpy(list, supported, sizeof(supported));
5882         return sizeof(supported);
5883 }
5884
5885
5886 /*
5887  * Open a print file to be written to by other calls
5888  */
5889
5890 static SMBCFILE *
5891 smbc_open_print_job_ctx(SMBCCTX *context,
5892                         const char *fname)
5893 {
5894         fstring server;
5895         fstring share;
5896         fstring user;
5897         fstring password;
5898         pstring path;
5899         
5900         if (!context || !context->internal ||
5901             !context->internal->_initialized) {
5902
5903                 errno = EINVAL;
5904                 return NULL;
5905     
5906         }
5907
5908         if (!fname) {
5909
5910                 errno = EINVAL;
5911                 return NULL;
5912
5913         }
5914   
5915         DEBUG(4, ("smbc_open_print_job_ctx(%s)\n", fname));
5916
5917         if (smbc_parse_path(context, fname,
5918                             NULL, 0,
5919                             server, sizeof(server),
5920                             share, sizeof(share),
5921                             path, sizeof(path),
5922                             user, sizeof(user),
5923                             password, sizeof(password),
5924                             NULL, 0)) {
5925                 errno = EINVAL;
5926                 return NULL;
5927         }
5928
5929         /* What if the path is empty, or the file exists? */
5930
5931         return context->open(context, fname, O_WRONLY, 666);
5932
5933 }
5934
5935 /*
5936  * Routine to print a file on a remote server ...
5937  *
5938  * We open the file, which we assume to be on a remote server, and then
5939  * copy it to a print file on the share specified by printq.
5940  */
5941
5942 static int
5943 smbc_print_file_ctx(SMBCCTX *c_file,
5944                     const char *fname,
5945                     SMBCCTX *c_print,
5946                     const char *printq)
5947 {
5948         SMBCFILE *fid1;
5949         SMBCFILE *fid2;
5950         int bytes;
5951         int saverr;
5952         int tot_bytes = 0;
5953         char buf[4096];
5954
5955         if (!c_file || !c_file->internal->_initialized || !c_print ||
5956             !c_print->internal->_initialized) {
5957
5958                 errno = EINVAL;
5959                 return -1;
5960
5961         }
5962
5963         if (!fname && !printq) {
5964
5965                 errno = EINVAL;
5966                 return -1;
5967
5968         }
5969
5970         /* Try to open the file for reading ... */
5971
5972         if ((long)(fid1 = c_file->open(c_file, fname, O_RDONLY, 0666)) < 0) {
5973                 
5974                 DEBUG(3, ("Error, fname=%s, errno=%i\n", fname, errno));
5975                 return -1;  /* smbc_open sets errno */
5976                 
5977         }
5978
5979         /* Now, try to open the printer file for writing */
5980
5981         if ((long)(fid2 = c_print->open_print_job(c_print, printq)) < 0) {
5982
5983                 saverr = errno;  /* Save errno */
5984                 c_file->close_fn(c_file, fid1);
5985                 errno = saverr;
5986                 return -1;
5987
5988         }
5989
5990         while ((bytes = c_file->read(c_file, fid1, buf, sizeof(buf))) > 0) {
5991
5992                 tot_bytes += bytes;
5993
5994                 if ((c_print->write(c_print, fid2, buf, bytes)) < 0) {
5995
5996                         saverr = errno;
5997                         c_file->close_fn(c_file, fid1);
5998                         c_print->close_fn(c_print, fid2);
5999                         errno = saverr;
6000
6001                 }
6002
6003         }
6004
6005         saverr = errno;
6006
6007         c_file->close_fn(c_file, fid1);  /* We have to close these anyway */
6008         c_print->close_fn(c_print, fid2);
6009
6010         if (bytes < 0) {
6011
6012                 errno = saverr;
6013                 return -1;
6014
6015         }
6016
6017         return tot_bytes;
6018
6019 }
6020
6021 /*
6022  * Routine to list print jobs on a printer share ...
6023  */
6024
6025 static int
6026 smbc_list_print_jobs_ctx(SMBCCTX *context,
6027                          const char *fname,
6028                          smbc_list_print_job_fn fn)
6029 {
6030         SMBCSRV *srv;
6031         fstring server;
6032         fstring share;
6033         fstring user;
6034         fstring password;
6035         fstring workgroup;
6036         pstring path;
6037
6038         if (!context || !context->internal ||
6039             !context->internal->_initialized) {
6040
6041                 errno = EINVAL;
6042                 return -1;
6043
6044         }
6045
6046         if (!fname) {
6047                 
6048                 errno = EINVAL;
6049                 return -1;
6050
6051         }
6052   
6053         DEBUG(4, ("smbc_list_print_jobs(%s)\n", fname));
6054
6055         if (smbc_parse_path(context, fname,
6056                             workgroup, sizeof(workgroup),
6057                             server, sizeof(server),
6058                             share, sizeof(share),
6059                             path, sizeof(path),
6060                             user, sizeof(user),
6061                             password, sizeof(password),
6062                             NULL, 0)) {
6063                 errno = EINVAL;
6064                 return -1;
6065         }
6066
6067         if (user[0] == (char)0) fstrcpy(user, context->user);
6068         
6069         srv = smbc_server(context, True,
6070                           server, share, workgroup, user, password);
6071
6072         if (!srv) {
6073
6074                 return -1;  /* errno set by smbc_server */
6075
6076         }
6077
6078         if (cli_print_queue(srv->cli,
6079                             (void (*)(struct print_job_info *))fn) < 0) {
6080
6081                 errno = smbc_errno(context, srv->cli);
6082                 return -1;
6083
6084         }
6085         
6086         return 0;
6087
6088 }
6089
6090 /*
6091  * Delete a print job from a remote printer share
6092  */
6093
6094 static int
6095 smbc_unlink_print_job_ctx(SMBCCTX *context,
6096                           const char *fname,
6097                           int id)
6098 {
6099         SMBCSRV *srv;
6100         fstring server;
6101         fstring share;
6102         fstring user;
6103         fstring password;
6104         fstring workgroup;
6105         pstring path;
6106         int err;
6107
6108         if (!context || !context->internal ||
6109             !context->internal->_initialized) {
6110
6111                 errno = EINVAL;
6112                 return -1;
6113
6114         }
6115
6116         if (!fname) {
6117
6118                 errno = EINVAL;
6119                 return -1;
6120
6121         }
6122   
6123         DEBUG(4, ("smbc_unlink_print_job(%s)\n", fname));
6124
6125         if (smbc_parse_path(context, fname,
6126                             workgroup, sizeof(workgroup),
6127                             server, sizeof(server),
6128                             share, sizeof(share),
6129                             path, sizeof(path),
6130                             user, sizeof(user),
6131                             password, sizeof(password),
6132                             NULL, 0)) {
6133                 errno = EINVAL;
6134                 return -1;
6135         }
6136
6137         if (user[0] == (char)0) fstrcpy(user, context->user);
6138
6139         srv = smbc_server(context, True,
6140                           server, share, workgroup, user, password);
6141
6142         if (!srv) {
6143
6144                 return -1;  /* errno set by smbc_server */
6145
6146         }
6147
6148         if ((err = cli_printjob_del(srv->cli, id)) != 0) {
6149
6150                 if (err < 0)
6151                         errno = smbc_errno(context, srv->cli);
6152                 else if (err == ERRnosuchprintjob)
6153                         errno = EINVAL;
6154                 return -1;
6155
6156         }
6157
6158         return 0;
6159
6160 }
6161
6162 /*
6163  * Get a new empty handle to fill in with your own info 
6164  */
6165 SMBCCTX *
6166 smbc_new_context(void)
6167 {
6168         SMBCCTX *context;
6169
6170         context = SMB_MALLOC_P(SMBCCTX);
6171         if (!context) {
6172                 errno = ENOMEM;
6173                 return NULL;
6174         }
6175
6176         ZERO_STRUCTP(context);
6177
6178         context->internal = SMB_MALLOC_P(struct smbc_internal_data);
6179         if (!context->internal) {
6180                 SAFE_FREE(context);
6181                 errno = ENOMEM;
6182                 return NULL;
6183         }
6184
6185         ZERO_STRUCTP(context->internal);
6186
6187         
6188         /* ADD REASONABLE DEFAULTS */
6189         context->debug            = 0;
6190         context->timeout          = 20000; /* 20 seconds */
6191
6192         context->options.browse_max_lmb_count      = 3;    /* # LMBs to query */
6193         context->options.urlencode_readdir_entries = False;/* backward compat */
6194         context->options.one_share_per_server      = False;/* backward compat */
6195         context->internal->_share_mode             = SMBC_SHAREMODE_DENY_NONE;
6196                                 /* backward compat */
6197
6198         context->open                              = smbc_open_ctx;
6199         context->creat                             = smbc_creat_ctx;
6200         context->read                              = smbc_read_ctx;
6201         context->write                             = smbc_write_ctx;
6202         context->close_fn                          = smbc_close_ctx;
6203         context->unlink                            = smbc_unlink_ctx;
6204         context->rename                            = smbc_rename_ctx;
6205         context->lseek                             = smbc_lseek_ctx;
6206         context->stat                              = smbc_stat_ctx;
6207         context->fstat                             = smbc_fstat_ctx;
6208         context->opendir                           = smbc_opendir_ctx;
6209         context->closedir                          = smbc_closedir_ctx;
6210         context->readdir                           = smbc_readdir_ctx;
6211         context->getdents                          = smbc_getdents_ctx;
6212         context->mkdir                             = smbc_mkdir_ctx;
6213         context->rmdir                             = smbc_rmdir_ctx;
6214         context->telldir                           = smbc_telldir_ctx;
6215         context->lseekdir                          = smbc_lseekdir_ctx;
6216         context->fstatdir                          = smbc_fstatdir_ctx;
6217         context->chmod                             = smbc_chmod_ctx;
6218         context->utimes                            = smbc_utimes_ctx;
6219         context->setxattr                          = smbc_setxattr_ctx;
6220         context->getxattr                          = smbc_getxattr_ctx;
6221         context->removexattr                       = smbc_removexattr_ctx;
6222         context->listxattr                         = smbc_listxattr_ctx;
6223         context->open_print_job                    = smbc_open_print_job_ctx;
6224         context->print_file                        = smbc_print_file_ctx;
6225         context->list_print_jobs                   = smbc_list_print_jobs_ctx;
6226         context->unlink_print_job                  = smbc_unlink_print_job_ctx;
6227
6228         context->callbacks.check_server_fn         = smbc_check_server;
6229         context->callbacks.remove_unused_server_fn = smbc_remove_unused_server;
6230
6231         smbc_default_cache_functions(context);
6232
6233         return context;
6234 }
6235
6236 /* 
6237  * Free a context
6238  *
6239  * Returns 0 on success. Otherwise returns 1, the SMBCCTX is _not_ freed 
6240  * and thus you'll be leaking memory if not handled properly.
6241  *
6242  */
6243 int
6244 smbc_free_context(SMBCCTX *context,
6245                   int shutdown_ctx)
6246 {
6247         if (!context) {
6248                 errno = EBADF;
6249                 return 1;
6250         }
6251         
6252         if (shutdown_ctx) {
6253                 SMBCFILE * f;
6254                 DEBUG(1,("Performing aggressive shutdown.\n"));
6255                 
6256                 f = context->internal->_files;
6257                 while (f) {
6258                         context->close_fn(context, f);
6259                         f = f->next;
6260                 }
6261                 context->internal->_files = NULL;
6262
6263                 /* First try to remove the servers the nice way. */
6264                 if (context->callbacks.purge_cached_fn(context)) {
6265                         SMBCSRV * s;
6266                         SMBCSRV * next;
6267                         DEBUG(1, ("Could not purge all servers, "
6268                                   "Nice way shutdown failed.\n"));
6269                         s = context->internal->_servers;
6270                         while (s) {
6271                                 DEBUG(1, ("Forced shutdown: %p (fd=%d)\n",
6272                                           s, s->cli->fd));
6273                                 cli_shutdown(s->cli);
6274                                 context->callbacks.remove_cached_srv_fn(context,
6275                                                                         s);
6276                                 next = s->next;
6277                                 DLIST_REMOVE(context->internal->_servers, s);
6278                                 SAFE_FREE(s);
6279                                 s = next;
6280                         }
6281                         context->internal->_servers = NULL;
6282                 }
6283         }
6284         else {
6285                 /* This is the polite way */    
6286                 if (context->callbacks.purge_cached_fn(context)) {
6287                         DEBUG(1, ("Could not purge all servers, "
6288                                   "free_context failed.\n"));
6289                         errno = EBUSY;
6290                         return 1;
6291                 }
6292                 if (context->internal->_servers) {
6293                         DEBUG(1, ("Active servers in context, "
6294                                   "free_context failed.\n"));
6295                         errno = EBUSY;
6296                         return 1;
6297                 }
6298                 if (context->internal->_files) {
6299                         DEBUG(1, ("Active files in context, "
6300                                   "free_context failed.\n"));
6301                         errno = EBUSY;
6302                         return 1;
6303                 }               
6304         }
6305
6306         /* Things we have to clean up */
6307         SAFE_FREE(context->workgroup);
6308         SAFE_FREE(context->netbios_name);
6309         SAFE_FREE(context->user);
6310         
6311         DEBUG(3, ("Context %p succesfully freed\n", context));
6312         SAFE_FREE(context->internal);
6313         SAFE_FREE(context);
6314         return 0;
6315 }
6316
6317
6318 /*
6319  * Each time the context structure is changed, we have binary backward
6320  * compatibility issues.  Instead of modifying the public portions of the
6321  * context structure to add new options, instead, we put them in the internal
6322  * portion of the context structure and provide a set function for these new
6323  * options.
6324  */
6325 void
6326 smbc_option_set(SMBCCTX *context,
6327                 char *option_name,
6328                 ... /* option_value */)
6329 {
6330         va_list ap;
6331         union {
6332                 int i;
6333                 BOOL b;
6334                 smbc_get_auth_data_with_context_fn auth_fn;
6335                 void *v;
6336         } option_value;
6337
6338         va_start(ap, option_name);
6339
6340         if (strcmp(option_name, "debug_to_stderr") == 0) {
6341                 /*
6342                  * Log to standard error instead of standard output.
6343                  */
6344                 option_value.b = (BOOL) va_arg(ap, int);
6345                 context->internal->_debug_stderr = option_value.b;
6346
6347         } else if (strcmp(option_name, "full_time_names") == 0) {
6348                 /*
6349                  * Use new-style time attribute names, e.g. WRITE_TIME rather
6350                  * than the old-style names such as M_TIME.  This allows also
6351                  * setting/getting CREATE_TIME which was previously
6352                  * unimplemented.  (Note that the old C_TIME was supposed to
6353                  * be CHANGE_TIME but was confused and sometimes referred to
6354                  * CREATE_TIME.)
6355                  */
6356                 option_value.b = (BOOL) va_arg(ap, int);
6357                 context->internal->_full_time_names = option_value.b;
6358
6359         } else if (strcmp(option_name, "open_share_mode") == 0) {
6360                 /*
6361                  * The share mode to use for files opened with
6362                  * smbc_open_ctx().  The default is SMBC_SHAREMODE_DENY_NONE.
6363                  */
6364                 option_value.i = va_arg(ap, int);
6365                 context->internal->_share_mode =
6366                         (smbc_share_mode) option_value.i;
6367
6368         } else if (strcmp(option_name, "auth_function") == 0) {
6369                 /*
6370                  * Use the new-style authentication function which includes
6371                  * the context.
6372                  */
6373                 option_value.auth_fn =
6374                         va_arg(ap, smbc_get_auth_data_with_context_fn);
6375                 context->internal->_auth_fn_with_context =
6376                         option_value.auth_fn;
6377         } else if (strcmp(option_name, "user_data") == 0) {
6378                 /*
6379                  * Save a user data handle which may be retrieved by the user
6380                  * with smbc_option_get()
6381                  */
6382                 option_value.v = va_arg(ap, void *);
6383                 context->internal->_user_data = option_value.v;
6384         }
6385
6386         va_end(ap);
6387 }
6388
6389
6390 /*
6391  * Retrieve the current value of an option
6392  */
6393 void *
6394 smbc_option_get(SMBCCTX *context,
6395                 char *option_name)
6396 {
6397         if (strcmp(option_name, "debug_stderr") == 0) {
6398                 /*
6399                  * Log to standard error instead of standard output.
6400                  */
6401 #if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
6402                 return (void *) (intptr_t) context->internal->_debug_stderr;
6403 #else
6404                 return (void *) context->internal->_debug_stderr;
6405 #endif
6406         } else if (strcmp(option_name, "full_time_names") == 0) {
6407                 /*
6408                  * Use new-style time attribute names, e.g. WRITE_TIME rather
6409                  * than the old-style names such as M_TIME.  This allows also
6410                  * setting/getting CREATE_TIME which was previously
6411                  * unimplemented.  (Note that the old C_TIME was supposed to
6412                  * be CHANGE_TIME but was confused and sometimes referred to
6413                  * CREATE_TIME.)
6414                  */
6415 #if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
6416                 return (void *) (intptr_t) context->internal->_full_time_names;
6417 #else
6418                 return (void *) context->internal->_full_time_names;
6419 #endif
6420
6421         } else if (strcmp(option_name, "auth_function") == 0) {
6422                 /*
6423                  * Use the new-style authentication function which includes
6424                  * the context.
6425                  */
6426                 return (void *) context->internal->_auth_fn_with_context;
6427         } else if (strcmp(option_name, "user_data") == 0) {
6428                 /*
6429                  * Save a user data handle which may be retrieved by the user
6430                  * with smbc_option_get()
6431                  */
6432                 return context->internal->_user_data;
6433         }
6434
6435         return NULL;
6436 }
6437
6438
6439 /*
6440  * Initialise the library etc 
6441  *
6442  * We accept a struct containing handle information.
6443  * valid values for info->debug from 0 to 100,
6444  * and insist that info->fn must be non-null.
6445  */
6446 SMBCCTX *
6447 smbc_init_context(SMBCCTX *context)
6448 {
6449         pstring conf;
6450         int pid;
6451         char *user = NULL;
6452         char *home = NULL;
6453
6454         if (!context || !context->internal) {
6455                 errno = EBADF;
6456                 return NULL;
6457         }
6458
6459         /* Do not initialise the same client twice */
6460         if (context->internal->_initialized) { 
6461                 return 0;
6462         }
6463
6464         if ((!context->callbacks.auth_fn &&
6465              !context->internal->_auth_fn_with_context) ||
6466             context->debug < 0 ||
6467             context->debug > 100) {
6468
6469                 errno = EINVAL;
6470                 return NULL;
6471
6472         }
6473
6474         if (!smbc_initialized) {
6475                 /*
6476                  * Do some library-wide intializations the first time we get
6477                  * called
6478                  */
6479                 BOOL conf_loaded = False;
6480
6481                 /* Set this to what the user wants */
6482                 DEBUGLEVEL = context->debug;
6483                 
6484                 load_case_tables();
6485
6486                 setup_logging("libsmbclient", True);
6487                 if (context->internal->_debug_stderr) {
6488                         dbf = x_stderr;
6489                         x_setbuf(x_stderr, NULL);
6490                 }
6491
6492                 /* Here we would open the smb.conf file if needed ... */
6493                 
6494                 in_client = True; /* FIXME, make a param */
6495
6496                 home = getenv("HOME");
6497                 if (home) {
6498                         slprintf(conf, sizeof(conf), "%s/.smb/smb.conf", home);
6499                         if (lp_load(conf, True, False, False, True)) {
6500                                 conf_loaded = True;
6501                         } else {
6502                                 DEBUG(5, ("Could not load config file: %s\n",
6503                                           conf));
6504                         }
6505                 }
6506  
6507                 if (!conf_loaded) {
6508                         /*
6509                          * Well, if that failed, try the dyn_CONFIGFILE
6510                          * Which points to the standard locn, and if that
6511                          * fails, silently ignore it and use the internal
6512                          * defaults ...
6513                          */
6514
6515                         if (!lp_load(dyn_CONFIGFILE, True, False, False, False)) {
6516                                 DEBUG(5, ("Could not load config file: %s\n",
6517                                           dyn_CONFIGFILE));
6518                         } else if (home) {
6519                                 /*
6520                                  * We loaded the global config file.  Now lets
6521                                  * load user-specific modifications to the
6522                                  * global config.
6523                                  */
6524                                 slprintf(conf, sizeof(conf),
6525                                          "%s/.smb/smb.conf.append", home);
6526                                 if (!lp_load(conf, True, False, False, False)) {
6527                                         DEBUG(10,
6528                                               ("Could not append config file: "
6529                                                "%s\n",
6530                                                conf));
6531                                 }
6532                         }
6533                 }
6534
6535                 load_interfaces();  /* Load the list of interfaces ... */
6536                 
6537                 reopen_logs();  /* Get logging working ... */
6538         
6539                 /* 
6540                  * Block SIGPIPE (from lib/util_sock.c: write())  
6541                  * It is not needed and should not stop execution 
6542                  */
6543                 BlockSignals(True, SIGPIPE);
6544                 
6545                 /* Done with one-time initialisation */
6546                 smbc_initialized = 1; 
6547
6548         }
6549         
6550         if (!context->user) {
6551                 /*
6552                  * FIXME: Is this the best way to get the user info? 
6553                  */
6554                 user = getenv("USER");
6555                 /* walk around as "guest" if no username can be found */
6556                 if (!user) context->user = SMB_STRDUP("guest");
6557                 else context->user = SMB_STRDUP(user);
6558         }
6559
6560         if (!context->netbios_name) {
6561                 /*
6562                  * We try to get our netbios name from the config. If that
6563                  * fails we fall back on constructing our netbios name from
6564                  * our hostname etc
6565                  */
6566                 if (global_myname()) {
6567                         context->netbios_name = SMB_STRDUP(global_myname());
6568                 }
6569                 else {
6570                         /*
6571                          * Hmmm, I want to get hostname as well, but I am too
6572                          * lazy for the moment
6573                          */
6574                         pid = sys_getpid();
6575                         context->netbios_name = (char *)SMB_MALLOC(17);
6576                         if (!context->netbios_name) {
6577                                 errno = ENOMEM;
6578                                 return NULL;
6579                         }
6580                         slprintf(context->netbios_name, 16,
6581                                  "smbc%s%d", context->user, pid);
6582                 }
6583         }
6584
6585         DEBUG(1, ("Using netbios name %s.\n", context->netbios_name));
6586
6587         if (!context->workgroup) {
6588                 if (lp_workgroup()) {
6589                         context->workgroup = SMB_STRDUP(lp_workgroup());
6590                 }
6591                 else {
6592                         /* TODO: Think about a decent default workgroup */
6593                         context->workgroup = SMB_STRDUP("samba");
6594                 }
6595         }
6596
6597         DEBUG(1, ("Using workgroup %s.\n", context->workgroup));
6598                                         
6599         /* shortest timeout is 1 second */
6600         if (context->timeout > 0 && context->timeout < 1000) 
6601                 context->timeout = 1000;
6602
6603         /*
6604          * FIXME: Should we check the function pointers here? 
6605          */
6606
6607         context->internal->_initialized = True;
6608         
6609         return context;
6610 }
6611
6612
6613 /* Return the verion of samba, and thus libsmbclient */
6614 const char *
6615 smbc_version(void)
6616 {
6617         return samba_version_string();
6618 }