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