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