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