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