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