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