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