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