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