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