r12485: r12044@cabra: derrell | 2005-12-25 16:46:47 -0500
[nivanova/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') {
2513                         if (*path != '\0') {
2514
2515                                 /* Should not have empty share with path */
2516                                 errno = EINVAL + 8197;
2517                                 if (dir) {
2518                                         SAFE_FREE(dir->fname);
2519                                         SAFE_FREE(dir);
2520                                 }
2521                                 return NULL;
2522         
2523                         }
2524
2525                         /*
2526                          * We don't know if <server> is really a server name
2527                          * or is a workgroup/domain name.  If we already have
2528                          * a server structure for it, we'll use it.
2529                          * Otherwise, check to see if <server><1D>,
2530                          * <server><1B>, or <server><20> translates.  We check
2531                          * to see if <server> is an IP address first.
2532                          */
2533
2534                         /* See if we have an existing server */
2535                         srv = smbc_server(context, server, "IPC$",
2536                                           workgroup, user, password);
2537
2538                         /*
2539                          * If no existing server and not an IP addr, look for
2540                          * LMB or DMB
2541                          */
2542                         if (!srv &&
2543                             !is_ipaddress(server) &&
2544                             (resolve_name(server, &rem_ip, 0x1d) ||   /* LMB */
2545                              resolve_name(server, &rem_ip, 0x1b) )) { /* DMB */
2546
2547                                 fstring buserver;
2548
2549                                 dir->dir_type = SMBC_SERVER;
2550
2551                                 /*
2552                                  * Get the backup list ...
2553                                  */
2554                                 if (!name_status_find(server, 0, 0,
2555                                                       rem_ip, buserver)) {
2556
2557                                         DEBUG(0, ("Could not get name of "
2558                                                   "local/domain master browser "
2559                                                   "for server %s\n", server));
2560                                         errno = EPERM;
2561                                         return NULL;
2562
2563                                 }
2564
2565                                 /*
2566                                  * Get a connection to IPC$ on the server if
2567                                  * we do not already have one
2568                                  */
2569                                 srv = smbc_server(context, buserver, "IPC$",
2570                                                   workgroup, user, password);
2571                                 if (!srv) {
2572                                         DEBUG(0, ("got no contact to IPC$\n"));
2573                                         if (dir) {
2574                                                 SAFE_FREE(dir->fname);
2575                                                 SAFE_FREE(dir);
2576                                         }
2577                                         return NULL;
2578
2579                                 }
2580
2581                                 dir->srv = srv;
2582
2583                                 /* Now, list the servers ... */
2584                                 if (!cli_NetServerEnum(&srv->cli, server,
2585                                                        0x0000FFFE, list_fn,
2586                                                        (void *)dir)) {
2587
2588                                         if (dir) {
2589                                                 SAFE_FREE(dir->fname);
2590                                                 SAFE_FREE(dir);
2591                                         }
2592                                         return NULL;
2593                                 }
2594                         } else if (srv ||
2595                                    (resolve_name(server, &rem_ip, 0x20))) {
2596                                 
2597                                 /* If we hadn't found the server, get one now */
2598                                 if (!srv) {
2599                                         srv = smbc_server(context, server,
2600                                                           "IPC$", workgroup,
2601                                                           user, password);
2602                                 }
2603
2604                                 if (!srv) {
2605                                         if (dir) {
2606                                                 SAFE_FREE(dir->fname);
2607                                                 SAFE_FREE(dir);
2608                                         }
2609                                         return NULL;
2610
2611                                 }
2612
2613                                 dir->dir_type = SMBC_FILE_SHARE;
2614                                 dir->srv = srv;
2615
2616                                 /* List the shares ... */
2617
2618                                 if (net_share_enum_rpc(
2619                                             &srv->cli,
2620                                             list_fn,
2621                                             (void *) dir) < 0 &&
2622                                     cli_RNetShareEnum(
2623                                             &srv->cli,
2624                                             list_fn, 
2625                                             (void *)dir) < 0) {
2626                                                 
2627                                         errno = cli_errno(&srv->cli);
2628                                         if (dir) {
2629                                                 SAFE_FREE(dir->fname);
2630                                                 SAFE_FREE(dir);
2631                                         }
2632                                         return NULL;
2633
2634                                 }
2635                         } else {
2636                                 /* Neither the workgroup nor server exists */
2637                                 errno = ECONNREFUSED;   
2638                                 if (dir) {
2639                                         SAFE_FREE(dir->fname);
2640                                         SAFE_FREE(dir);
2641                                 }
2642                                 return NULL;
2643                         }
2644
2645                 }
2646                 else {
2647                         /*
2648                          * The server and share are specified ... work from
2649                          * there ...
2650                          */
2651                         pstring targetpath;
2652                         struct cli_state *targetcli;
2653
2654                         /* We connect to the server and list the directory */
2655                         dir->dir_type = SMBC_FILE_SHARE;
2656
2657                         srv = smbc_server(context, server, share,
2658                                           workgroup, user, password);
2659
2660                         if (!srv) {
2661
2662                                 if (dir) {
2663                                         SAFE_FREE(dir->fname);
2664                                         SAFE_FREE(dir);
2665                                 }
2666                                 return NULL;
2667
2668                         }
2669
2670                         dir->srv = srv;
2671
2672                         /* Now, list the files ... */
2673
2674                         p = path + strlen(path);
2675                         pstrcat(path, "\\*");
2676
2677                         if (!cli_resolve_path( "", &srv->cli, path, &targetcli, targetpath))
2678                         {
2679                                 d_printf("Could not resolve %s\n", path);
2680                                 return NULL;
2681                         }
2682                         
2683                         if (cli_list(targetcli, targetpath, aDIR | aSYSTEM | aHIDDEN, dir_list_fn,
2684                                      (void *)dir) < 0) {
2685
2686                                 if (dir) {
2687                                         SAFE_FREE(dir->fname);
2688                                         SAFE_FREE(dir);
2689                                 }
2690                                 errno = smbc_errno(context, targetcli);
2691
2692                                 if (errno == EINVAL) {
2693                                     /*
2694                                      * See if they asked to opendir something
2695                                      * other than a directory.  If so, the
2696                                      * converted error value we got would have
2697                                      * been EINVAL rather than ENOTDIR.
2698                                      */
2699                                     *p = '\0'; /* restore original path */
2700
2701                                     if (smbc_getatr(context, srv, path,
2702                                                     &mode, NULL,
2703                                                     NULL, NULL, NULL,
2704                                                     NULL) &&
2705                                         ! IS_DOS_DIR(mode)) {
2706
2707                                         /* It is.  Correct the error value */
2708                                         errno = ENOTDIR;
2709                                     }
2710                                 }
2711
2712                                 return NULL;
2713
2714                         }
2715                 }
2716
2717         }
2718
2719         DLIST_ADD(context->internal->_files, dir);
2720         return dir;
2721
2722 }
2723
2724 /*
2725  * Routine to close a directory
2726  */
2727
2728 static int smbc_closedir_ctx(SMBCCTX *context, SMBCFILE *dir)
2729 {
2730
2731         if (!context || !context->internal ||
2732             !context->internal->_initialized) {
2733
2734                 errno = EINVAL;
2735                 return -1;
2736
2737         }
2738
2739         if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
2740
2741                 errno = EBADF;
2742                 return -1;
2743     
2744         }
2745
2746         smbc_remove_dir(dir); /* Clean it up */
2747
2748         DLIST_REMOVE(context->internal->_files, dir);
2749
2750         if (dir) {
2751
2752                 SAFE_FREE(dir->fname);
2753                 SAFE_FREE(dir);    /* Free the space too */
2754         }
2755
2756         return 0;
2757
2758 }
2759
2760 static void smbc_readdir_internal(SMBCCTX * context,
2761                                   struct smbc_dirent *dest,
2762                                   struct smbc_dirent *src,
2763                                   int max_namebuf_len)
2764 {
2765         if (context->options.urlencode_readdir_entries) {
2766
2767                 /* url-encode the name.  get back remaining buffer space */
2768                 max_namebuf_len =
2769                         smbc_urlencode(dest->name, src->name, max_namebuf_len);
2770
2771                 /* We now know the name length */
2772                 dest->namelen = strlen(dest->name);
2773
2774                 /* Save the pointer to the beginning of the comment */
2775                 dest->comment = dest->name + dest->namelen + 1;
2776
2777                 /* Copy the comment */
2778                 strncpy(dest->comment, src->comment, max_namebuf_len);
2779
2780                 /* Ensure the comment is null terminated */
2781                 if (max_namebuf_len > src->commentlen) {
2782                         dest->comment[src->commentlen] = '\0';
2783                 } else {
2784                         dest->comment[max_namebuf_len - 1] = '\0';
2785                 }
2786
2787                 /* Save other fields */
2788                 dest->smbc_type = src->smbc_type;
2789                 dest->commentlen = strlen(dest->comment);
2790                 dest->dirlen = ((dest->comment + dest->commentlen + 1) -
2791                                 (char *) dest);
2792         } else {
2793
2794                 /* No encoding.  Just copy the entry as is. */
2795                 memcpy(dest, src, src->dirlen);
2796                 dest->comment = (char *)(&dest->name + src->namelen + 1);
2797         }
2798         
2799 }
2800
2801 /*
2802  * Routine to get a directory entry
2803  */
2804
2805 struct smbc_dirent *smbc_readdir_ctx(SMBCCTX *context, SMBCFILE *dir)
2806 {
2807         int maxlen;
2808         struct smbc_dirent *dirp, *dirent;
2809
2810         /* Check that all is ok first ... */
2811
2812         if (!context || !context->internal ||
2813             !context->internal->_initialized) {
2814
2815                 errno = EINVAL;
2816                 DEBUG(0, ("Invalid context in smbc_readdir_ctx()\n"));
2817                 return NULL;
2818
2819         }
2820
2821         if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
2822
2823                 errno = EBADF;
2824                 DEBUG(0, ("Invalid dir in smbc_readdir_ctx()\n"));
2825                 return NULL;
2826
2827         }
2828
2829         if (dir->file != False) { /* FIXME, should be dir, perhaps */
2830
2831                 errno = ENOTDIR;
2832                 DEBUG(0, ("Found file vs directory in smbc_readdir_ctx()\n"));
2833                 return NULL;
2834
2835         }
2836
2837         if (!dir->dir_next) {
2838                 return NULL;
2839         }
2840
2841         dirent = dir->dir_next->dirent;
2842         if (!dirent) {
2843
2844                 errno = ENOENT;
2845                 return NULL;
2846
2847         }
2848
2849         dirp = (struct smbc_dirent *)context->internal->_dirent;
2850         maxlen = (sizeof(context->internal->_dirent) -
2851                   sizeof(struct smbc_dirent));
2852
2853         smbc_readdir_internal(context, dirp, dirent, maxlen);
2854
2855         dir->dir_next = dir->dir_next->next;
2856
2857         return dirp;
2858 }
2859
2860 /*
2861  * Routine to get directory entries
2862  */
2863
2864 static int smbc_getdents_ctx(SMBCCTX *context,
2865                              SMBCFILE *dir,
2866                              struct smbc_dirent *dirp,
2867                              int count)
2868 {
2869         int rem = count;
2870         int reqd;
2871         int maxlen;
2872         char *ndir = (char *)dirp;
2873         struct smbc_dir_list *dirlist;
2874
2875         /* Check that all is ok first ... */
2876
2877         if (!context || !context->internal ||
2878             !context->internal->_initialized) {
2879
2880                 errno = EINVAL;
2881                 return -1;
2882
2883         }
2884
2885         if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
2886
2887                 errno = EBADF;
2888                 return -1;
2889     
2890         }
2891
2892         if (dir->file != False) { /* FIXME, should be dir, perhaps */
2893
2894                 errno = ENOTDIR;
2895                 return -1;
2896
2897         }
2898
2899         /* 
2900          * Now, retrieve the number of entries that will fit in what was passed
2901          * We have to figure out if the info is in the list, or we need to 
2902          * send a request to the server to get the info.
2903          */
2904
2905         while ((dirlist = dir->dir_next)) {
2906                 struct smbc_dirent *dirent;
2907
2908                 if (!dirlist->dirent) {
2909
2910                         errno = ENOENT;  /* Bad error */
2911                         return -1;
2912
2913                 }
2914
2915                 /* Do urlencoding of next entry, if so selected */
2916                 dirent = (struct smbc_dirent *)context->internal->_dirent;
2917                 maxlen = (sizeof(context->internal->_dirent) -
2918                           sizeof(struct smbc_dirent));
2919                 smbc_readdir_internal(context, dirent, dirlist->dirent, maxlen);
2920
2921                 reqd = dirent->dirlen;
2922
2923                 if (rem < reqd) {
2924
2925                         if (rem < count) { /* We managed to copy something */
2926
2927                                 errno = 0;
2928                                 return count - rem;
2929
2930                         }
2931                         else { /* Nothing copied ... */
2932
2933                                 errno = EINVAL;  /* Not enough space ... */
2934                                 return -1;
2935
2936                         }
2937
2938                 }
2939
2940                 memcpy(ndir, dirent, reqd); /* Copy the data in ... */
2941     
2942                 ((struct smbc_dirent *)ndir)->comment = 
2943                         (char *)(&((struct smbc_dirent *)ndir)->name +
2944                                  dirent->namelen +
2945                                  1);
2946
2947                 ndir += reqd;
2948
2949                 rem -= reqd;
2950
2951                 dir->dir_next = dirlist = dirlist -> next;
2952         }
2953
2954         if (rem == count)
2955                 return 0;
2956         else 
2957                 return count - rem;
2958
2959 }
2960
2961 /*
2962  * Routine to create a directory ...
2963  */
2964
2965 static int smbc_mkdir_ctx(SMBCCTX *context, const char *fname, mode_t mode)
2966 {
2967         SMBCSRV *srv;
2968         fstring server, share, user, password, workgroup;
2969         pstring path, targetpath;
2970         struct cli_state *targetcli;
2971
2972         if (!context || !context->internal || 
2973             !context->internal->_initialized) {
2974
2975                 errno = EINVAL;
2976                 return -1;
2977
2978         }
2979
2980         if (!fname) {
2981
2982                 errno = EINVAL;
2983                 return -1;
2984
2985         }
2986   
2987         DEBUG(4, ("smbc_mkdir(%s)\n", fname));
2988
2989         if (smbc_parse_path(context, fname,
2990                             server, sizeof(server),
2991                             share, sizeof(share),
2992                             path, sizeof(path),
2993                             user, sizeof(user),
2994                             password, sizeof(password),
2995                             NULL, 0)) {
2996                 errno = EINVAL;
2997                 return -1;
2998         }
2999
3000         if (user[0] == (char)0) fstrcpy(user, context->user);
3001
3002         fstrcpy(workgroup, context->workgroup);
3003
3004         srv = smbc_server(context, server, share, workgroup, user, password);
3005
3006         if (!srv) {
3007
3008                 return -1;  /* errno set by smbc_server */
3009
3010         }
3011
3012         /*d_printf(">>>mkdir: resolving %s\n", path);*/
3013         if (!cli_resolve_path( "", &srv->cli, path, &targetcli, targetpath))
3014         {
3015                 d_printf("Could not resolve %s\n", path);
3016                 return -1;
3017         }
3018         /*d_printf(">>>mkdir: resolved path as %s\n", targetpath);*/
3019
3020         if (!cli_mkdir(targetcli, targetpath)) {
3021
3022                 errno = smbc_errno(context, targetcli);
3023                 return -1;
3024
3025         } 
3026
3027         return 0;
3028
3029 }
3030
3031 /*
3032  * Our list function simply checks to see if a directory is not empty
3033  */
3034
3035 static int smbc_rmdir_dirempty = True;
3036
3037 static void rmdir_list_fn(const char *mnt, file_info *finfo, const char *mask, void *state)
3038 {
3039
3040         if (strncmp(finfo->name, ".", 1) != 0 && strncmp(finfo->name, "..", 2) != 0)
3041                 smbc_rmdir_dirempty = False;
3042
3043 }
3044
3045 /*
3046  * Routine to remove a directory
3047  */
3048
3049 static int smbc_rmdir_ctx(SMBCCTX *context, const char *fname)
3050 {
3051         SMBCSRV *srv;
3052         fstring server, share, user, password, workgroup;
3053         pstring path, targetpath;
3054         struct cli_state *targetcli;
3055
3056         if (!context || !context->internal || 
3057             !context->internal->_initialized) {
3058
3059                 errno = EINVAL;
3060                 return -1;
3061
3062         }
3063
3064         if (!fname) {
3065
3066                 errno = EINVAL;
3067                 return -1;
3068
3069         }
3070   
3071         DEBUG(4, ("smbc_rmdir(%s)\n", fname));
3072
3073         if (smbc_parse_path(context, fname,
3074                             server, sizeof(server),
3075                             share, sizeof(share),
3076                             path, sizeof(path),
3077                             user, sizeof(user),
3078                             password, sizeof(password),
3079                             NULL, 0))
3080         {
3081                 errno = EINVAL;
3082                 return -1;
3083         }
3084
3085         if (user[0] == (char)0) fstrcpy(user, context->user);
3086
3087         fstrcpy(workgroup, context->workgroup);
3088
3089         srv = smbc_server(context, server, share, workgroup, user, password);
3090
3091         if (!srv) {
3092
3093                 return -1;  /* errno set by smbc_server */
3094
3095         }
3096
3097         /* if (strncmp(srv->cli.dev, "IPC", 3) == 0) {
3098
3099            mode = aDIR | aRONLY;
3100
3101            }
3102            else if (strncmp(srv->cli.dev, "LPT", 3) == 0) {
3103
3104            if (strcmp(path, "\\") == 0) {
3105
3106            mode = aDIR | aRONLY;
3107
3108            }
3109            else {
3110
3111            mode = aRONLY;
3112            smbc_stat_printjob(srv, path, &size, &m_time);
3113            c_time = a_time = m_time;
3114            
3115            }
3116            else { */
3117
3118         /*d_printf(">>>rmdir: resolving %s\n", path);*/
3119         if (!cli_resolve_path( "", &srv->cli, path, &targetcli, targetpath))
3120         {
3121                 d_printf("Could not resolve %s\n", path);
3122                 return -1;
3123         }
3124         /*d_printf(">>>rmdir: resolved path as %s\n", targetpath);*/
3125
3126
3127         if (!cli_rmdir(targetcli, targetpath)) {
3128
3129                 errno = smbc_errno(context, targetcli);
3130
3131                 if (errno == EACCES) {  /* Check if the dir empty or not */
3132
3133                         pstring lpath; /* Local storage to avoid buffer overflows */
3134
3135                         smbc_rmdir_dirempty = True;  /* Make this so ... */
3136
3137                         pstrcpy(lpath, targetpath);
3138                         pstrcat(lpath, "\\*");
3139
3140                         if (cli_list(targetcli, lpath, aDIR | aSYSTEM | aHIDDEN, rmdir_list_fn,
3141                                      NULL) < 0) {
3142
3143                                 /* Fix errno to ignore latest error ... */
3144
3145                                 DEBUG(5, ("smbc_rmdir: cli_list returned an error: %d\n", 
3146                                           smbc_errno(context, targetcli)));
3147                                 errno = EACCES;
3148
3149                         }
3150
3151                         if (smbc_rmdir_dirempty)
3152                                 errno = EACCES;
3153                         else
3154                                 errno = ENOTEMPTY;
3155
3156                 }
3157
3158                 return -1;
3159
3160         } 
3161
3162         return 0;
3163
3164 }
3165
3166 /*
3167  * Routine to return the current directory position
3168  */
3169
3170 static off_t smbc_telldir_ctx(SMBCCTX *context, SMBCFILE *dir)
3171 {
3172         off_t ret_val; /* Squash warnings about cast */
3173
3174         if (!context || !context->internal ||
3175             !context->internal->_initialized) {
3176
3177                 errno = EINVAL;
3178                 return -1;
3179
3180         }
3181
3182         if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
3183
3184                 errno = EBADF;
3185                 return -1;
3186
3187         }
3188
3189         if (dir->file != False) { /* FIXME, should be dir, perhaps */
3190
3191                 errno = ENOTDIR;
3192                 return -1;
3193
3194         }
3195
3196         /*
3197          * We return the pointer here as the offset
3198          */
3199         ret_val = (off_t)(long)dir->dir_next;
3200         return ret_val;
3201
3202 }
3203
3204 /*
3205  * A routine to run down the list and see if the entry is OK
3206  */
3207
3208 struct smbc_dir_list *smbc_check_dir_ent(struct smbc_dir_list *list, 
3209                                          struct smbc_dirent *dirent)
3210 {
3211
3212         /* Run down the list looking for what we want */
3213
3214         if (dirent) {
3215
3216                 struct smbc_dir_list *tmp = list;
3217
3218                 while (tmp) {
3219
3220                         if (tmp->dirent == dirent)
3221                                 return tmp;
3222
3223                         tmp = tmp->next;
3224
3225                 }
3226
3227         }
3228
3229         return NULL;  /* Not found, or an error */
3230
3231 }
3232
3233
3234 /*
3235  * Routine to seek on a directory
3236  */
3237
3238 static int smbc_lseekdir_ctx(SMBCCTX *context, SMBCFILE *dir, off_t offset)
3239 {
3240         long int l_offset = offset;  /* Handle problems of size */
3241         struct smbc_dirent *dirent = (struct smbc_dirent *)l_offset;
3242         struct smbc_dir_list *list_ent = (struct smbc_dir_list *)NULL;
3243
3244         if (!context || !context->internal ||
3245             !context->internal->_initialized) {
3246
3247                 errno = EINVAL;
3248                 return -1;
3249
3250         }
3251
3252         if (dir->file != False) { /* FIXME, should be dir, perhaps */
3253
3254                 errno = ENOTDIR;
3255                 return -1;
3256
3257         }
3258
3259         /* Now, check what we were passed and see if it is OK ... */
3260
3261         if (dirent == NULL) {  /* Seek to the begining of the list */
3262
3263                 dir->dir_next = dir->dir_list;
3264                 return 0;
3265
3266         }
3267
3268         /* Now, run down the list and make sure that the entry is OK       */
3269         /* This may need to be changed if we change the format of the list */
3270
3271         if ((list_ent = smbc_check_dir_ent(dir->dir_list, dirent)) == NULL) {
3272
3273                 errno = EINVAL;   /* Bad entry */
3274                 return -1;
3275
3276         }
3277
3278         dir->dir_next = list_ent;
3279
3280         return 0; 
3281
3282 }
3283
3284 /*
3285  * Routine to fstat a dir
3286  */
3287
3288 static int smbc_fstatdir_ctx(SMBCCTX *context, SMBCFILE *dir, struct stat *st)
3289 {
3290
3291         if (!context || !context->internal || 
3292             !context->internal->_initialized) {
3293
3294                 errno = EINVAL;
3295                 return -1;
3296
3297         }
3298
3299         /* No code yet ... */
3300
3301         return 0;
3302
3303 }
3304
3305 int smbc_chmod_ctx(SMBCCTX *context, const char *fname, mode_t newmode)
3306 {
3307         SMBCSRV *srv;
3308         fstring server, share, user, password, workgroup;
3309         pstring path;
3310         uint16 mode;
3311
3312         if (!context || !context->internal ||
3313             !context->internal->_initialized) {
3314
3315                 errno = EINVAL;  /* Best I can think of ... */
3316                 return -1;
3317     
3318         }
3319
3320         if (!fname) {
3321
3322                 errno = EINVAL;
3323                 return -1;
3324
3325         }
3326   
3327         DEBUG(4, ("smbc_chmod(%s, 0%3o)\n", fname, newmode));
3328
3329         if (smbc_parse_path(context, fname,
3330                             server, sizeof(server),
3331                             share, sizeof(share),
3332                             path, sizeof(path),
3333                             user, sizeof(user),
3334                             password, sizeof(password),
3335                             NULL, 0)) {
3336                 errno = EINVAL;
3337                 return -1;
3338         }
3339
3340         if (user[0] == (char)0) fstrcpy(user, context->user);
3341
3342         fstrcpy(workgroup, context->workgroup);
3343
3344         srv = smbc_server(context, server, share, workgroup, user, password);
3345
3346         if (!srv) {
3347                 return -1;  /* errno set by smbc_server */
3348         }
3349
3350         mode = 0;
3351
3352         if (!(newmode & (S_IWUSR | S_IWGRP | S_IWOTH))) mode |= aRONLY;
3353         if ((newmode & S_IXUSR) && lp_map_archive(-1)) mode |= aARCH;
3354         if ((newmode & S_IXGRP) && lp_map_system(-1)) mode |= aSYSTEM;
3355         if ((newmode & S_IXOTH) && lp_map_hidden(-1)) mode |= aHIDDEN;
3356
3357         if (!cli_setatr(&srv->cli, path, mode, 0)) {
3358                 errno = smbc_errno(context, &srv->cli);
3359                 return -1;
3360         }
3361         
3362         return 0;
3363 }
3364
3365 int smbc_utimes_ctx(SMBCCTX *context, const char *fname, struct timeval *tbuf)
3366 {
3367         SMBCSRV *srv;
3368         fstring server, share, user, password, workgroup;
3369         pstring path;
3370         time_t a_time;
3371         time_t m_time;
3372
3373         if (!context || !context->internal ||
3374             !context->internal->_initialized) {
3375
3376                 errno = EINVAL;  /* Best I can think of ... */
3377                 return -1;
3378     
3379         }
3380
3381         if (!fname) {
3382
3383                 errno = EINVAL;
3384                 return -1;
3385
3386         }
3387   
3388         if (tbuf == NULL) {
3389                 a_time = m_time = time(NULL);
3390         } else {
3391                 a_time = tbuf[0].tv_sec;
3392                 m_time = tbuf[1].tv_sec;
3393         }
3394
3395         if (DEBUGLVL(4)) 
3396         {
3397                 char *p;
3398                 char atimebuf[32];
3399                 char mtimebuf[32];
3400
3401                 strncpy(atimebuf, ctime(&a_time), sizeof(atimebuf));
3402                 atimebuf[sizeof(atimebuf) - 1] = '\0';
3403                 if ((p = strchr(atimebuf, '\n')) != NULL) {
3404                         *p = '\0';
3405                 }
3406
3407                 strncpy(mtimebuf, ctime(&m_time), sizeof(mtimebuf));
3408                 mtimebuf[sizeof(mtimebuf) - 1] = '\0';
3409                 if ((p = strchr(mtimebuf, '\n')) != NULL) {
3410                         *p = '\0';
3411                 }
3412
3413                 dbgtext("smbc_utimes(%s, atime = %s mtime = %s)\n",
3414                         fname, atimebuf, mtimebuf);
3415         }
3416
3417         if (smbc_parse_path(context, fname,
3418                             server, sizeof(server),
3419                             share, sizeof(share),
3420                             path, sizeof(path),
3421                             user, sizeof(user),
3422                             password, sizeof(password),
3423                             NULL, 0)) {
3424                 errno = EINVAL;
3425                 return -1;
3426         }
3427
3428         if (user[0] == (char)0) fstrcpy(user, context->user);
3429
3430         fstrcpy(workgroup, context->workgroup);
3431
3432         srv = smbc_server(context, server, share, workgroup, user, password);
3433
3434         if (!srv) {
3435                 return -1;      /* errno set by smbc_server */
3436         }
3437
3438         if (!smbc_setatr(context, srv, path, 0, a_time, m_time, 0)) {
3439                 return -1;      /* errno set by smbc_setatr */
3440         }
3441
3442         return 0;
3443 }
3444
3445
3446 /* The MSDN is contradictory over the ordering of ACE entries in an ACL.
3447    However NT4 gives a "The information may have been modified by a
3448    computer running Windows NT 5.0" if denied ACEs do not appear before
3449    allowed ACEs. */
3450
3451 static int ace_compare(SEC_ACE *ace1, SEC_ACE *ace2)
3452 {
3453         if (sec_ace_equal(ace1, ace2)) 
3454                 return 0;
3455
3456         if (ace1->type != ace2->type) 
3457                 return ace2->type - ace1->type;
3458
3459         if (sid_compare(&ace1->trustee, &ace2->trustee)) 
3460                 return sid_compare(&ace1->trustee, &ace2->trustee);
3461
3462         if (ace1->flags != ace2->flags) 
3463                 return ace1->flags - ace2->flags;
3464
3465         if (ace1->info.mask != ace2->info.mask) 
3466                 return ace1->info.mask - ace2->info.mask;
3467
3468         if (ace1->size != ace2->size) 
3469                 return ace1->size - ace2->size;
3470
3471         return memcmp(ace1, ace2, sizeof(SEC_ACE));
3472 }
3473
3474
3475 static void sort_acl(SEC_ACL *the_acl)
3476 {
3477         uint32 i;
3478         if (!the_acl) return;
3479
3480         qsort(the_acl->ace, the_acl->num_aces, sizeof(the_acl->ace[0]), QSORT_CAST ace_compare);
3481
3482         for (i=1;i<the_acl->num_aces;) {
3483                 if (sec_ace_equal(&the_acl->ace[i-1], &the_acl->ace[i])) {
3484                         int j;
3485                         for (j=i; j<the_acl->num_aces-1; j++) {
3486                                 the_acl->ace[j] = the_acl->ace[j+1];
3487                         }
3488                         the_acl->num_aces--;
3489                 } else {
3490                         i++;
3491                 }
3492         }
3493 }
3494
3495 /* convert a SID to a string, either numeric or username/group */
3496 static void convert_sid_to_string(struct cli_state *ipc_cli,
3497                                   POLICY_HND *pol,
3498                                   fstring str,
3499                                   BOOL numeric,
3500                                   DOM_SID *sid)
3501 {
3502         char **domains = NULL;
3503         char **names = NULL;
3504         uint32 *types = NULL;
3505         struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli);
3506         sid_to_string(str, sid);
3507
3508         if (numeric) {
3509                 return;     /* no lookup desired */
3510         }
3511        
3512         if (!pipe_hnd) {
3513                 return;
3514         }
3515  
3516         /* Ask LSA to convert the sid to a name */
3517
3518         if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_sids(pipe_hnd, ipc_cli->mem_ctx,  
3519                                                  pol, 1, sid, &domains, 
3520                                                  &names, &types)) ||
3521             !domains || !domains[0] || !names || !names[0]) {
3522                 return;
3523         }
3524
3525         /* Converted OK */
3526
3527         slprintf(str, sizeof(fstring) - 1, "%s%s%s",
3528                  domains[0], lp_winbind_separator(),
3529                  names[0]);
3530 }
3531
3532 /* convert a string to a SID, either numeric or username/group */
3533 static BOOL convert_string_to_sid(struct cli_state *ipc_cli,
3534                                   POLICY_HND *pol,
3535                                   BOOL numeric,
3536                                   DOM_SID *sid,
3537                                   const char *str)
3538 {
3539         uint32 *types = NULL;
3540         DOM_SID *sids = NULL;
3541         BOOL result = True;
3542         struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli);
3543
3544         if (!pipe_hnd) {
3545                 return False;
3546         }
3547
3548         if (numeric) {
3549                 if (strncmp(str, "S-", 2) == 0) {
3550                         return string_to_sid(sid, str);
3551                 }
3552
3553                 result = False;
3554                 goto done;
3555         }
3556
3557         if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_names(pipe_hnd, ipc_cli->mem_ctx, 
3558                                                   pol, 1, &str, &sids, 
3559                                                   &types))) {
3560                 result = False;
3561                 goto done;
3562         }
3563
3564         sid_copy(sid, &sids[0]);
3565  done:
3566
3567         return result;
3568 }
3569
3570
3571 /* parse an ACE in the same format as print_ace() */
3572 static BOOL parse_ace(struct cli_state *ipc_cli,
3573                       POLICY_HND *pol,
3574                       SEC_ACE *ace,
3575                       BOOL numeric,
3576                       char *str)
3577 {
3578         char *p;
3579         const char *cp;
3580         fstring tok;
3581         unsigned atype, aflags, amask;
3582         DOM_SID sid;
3583         SEC_ACCESS mask;
3584         const struct perm_value *v;
3585         struct perm_value {
3586                 const char *perm;
3587                 uint32 mask;
3588         };
3589
3590         /* These values discovered by inspection */
3591         static const struct perm_value special_values[] = {
3592                 { "R", 0x00120089 },
3593                 { "W", 0x00120116 },
3594                 { "X", 0x001200a0 },
3595                 { "D", 0x00010000 },
3596                 { "P", 0x00040000 },
3597                 { "O", 0x00080000 },
3598                 { NULL, 0 },
3599         };
3600
3601         static const struct perm_value standard_values[] = {
3602                 { "READ",   0x001200a9 },
3603                 { "CHANGE", 0x001301bf },
3604                 { "FULL",   0x001f01ff },
3605                 { NULL, 0 },
3606         };
3607
3608
3609         ZERO_STRUCTP(ace);
3610         p = strchr_m(str,':');
3611         if (!p) return False;
3612         *p = '\0';
3613         p++;
3614         /* Try to parse numeric form */
3615
3616         if (sscanf(p, "%i/%i/%i", &atype, &aflags, &amask) == 3 &&
3617             convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
3618                 goto done;
3619         }
3620
3621         /* Try to parse text form */
3622
3623         if (!convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
3624                 return False;
3625         }
3626
3627         cp = p;
3628         if (!next_token(&cp, tok, "/", sizeof(fstring))) {
3629                 return False;
3630         }
3631
3632         if (StrnCaseCmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
3633                 atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
3634         } else if (StrnCaseCmp(tok, "DENIED", strlen("DENIED")) == 0) {
3635                 atype = SEC_ACE_TYPE_ACCESS_DENIED;
3636         } else {
3637                 return False;
3638         }
3639
3640         /* Only numeric form accepted for flags at present */
3641
3642         if (!(next_token(&cp, tok, "/", sizeof(fstring)) &&
3643               sscanf(tok, "%i", &aflags))) {
3644                 return False;
3645         }
3646
3647         if (!next_token(&cp, tok, "/", sizeof(fstring))) {
3648                 return False;
3649         }
3650
3651         if (strncmp(tok, "0x", 2) == 0) {
3652                 if (sscanf(tok, "%i", &amask) != 1) {
3653                         return False;
3654                 }
3655                 goto done;
3656         }
3657
3658         for (v = standard_values; v->perm; v++) {
3659                 if (strcmp(tok, v->perm) == 0) {
3660                         amask = v->mask;
3661                         goto done;
3662                 }
3663         }
3664
3665         p = tok;
3666
3667         while(*p) {
3668                 BOOL found = False;
3669
3670                 for (v = special_values; v->perm; v++) {
3671                         if (v->perm[0] == *p) {
3672                                 amask |= v->mask;
3673                                 found = True;
3674                         }
3675                 }
3676
3677                 if (!found) return False;
3678                 p++;
3679         }
3680
3681         if (*p) {
3682                 return False;
3683         }
3684
3685  done:
3686         mask.mask = amask;
3687         init_sec_ace(ace, &sid, atype, mask, aflags);
3688         return True;
3689 }
3690
3691 /* add an ACE to a list of ACEs in a SEC_ACL */
3692 static BOOL add_ace(SEC_ACL **the_acl, SEC_ACE *ace, TALLOC_CTX *ctx)
3693 {
3694         SEC_ACL *newacl;
3695         SEC_ACE *aces;
3696         if (! *the_acl) {
3697                 (*the_acl) = make_sec_acl(ctx, 3, 1, ace);
3698                 return True;
3699         }
3700
3701         aces = SMB_CALLOC_ARRAY(SEC_ACE, 1+(*the_acl)->num_aces);
3702         memcpy(aces, (*the_acl)->ace, (*the_acl)->num_aces * sizeof(SEC_ACE));
3703         memcpy(aces+(*the_acl)->num_aces, ace, sizeof(SEC_ACE));
3704         newacl = make_sec_acl(ctx,(*the_acl)->revision,1+(*the_acl)->num_aces, aces);
3705         SAFE_FREE(aces);
3706         (*the_acl) = newacl;
3707         return True;
3708 }
3709
3710
3711 /* parse a ascii version of a security descriptor */
3712 static SEC_DESC *sec_desc_parse(TALLOC_CTX *ctx,
3713                                 struct cli_state *ipc_cli,
3714                                 POLICY_HND *pol,
3715                                 BOOL numeric,
3716                                 char *str)
3717 {
3718         const char *p = str;
3719         fstring tok;
3720         SEC_DESC *ret;
3721         size_t sd_size;
3722         DOM_SID *grp_sid=NULL, *owner_sid=NULL;
3723         SEC_ACL *dacl=NULL;
3724         int revision=1;
3725
3726         while (next_token(&p, tok, "\t,\r\n", sizeof(tok))) {
3727
3728                 if (StrnCaseCmp(tok,"REVISION:", 9) == 0) {
3729                         revision = strtol(tok+9, NULL, 16);
3730                         continue;
3731                 }
3732
3733                 if (StrnCaseCmp(tok,"OWNER:", 6) == 0) {
3734                         owner_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
3735                         if (!owner_sid ||
3736                             !convert_string_to_sid(ipc_cli, pol,
3737                                                    numeric,
3738                                                    owner_sid, tok+6)) {
3739                                 DEBUG(5, ("Failed to parse owner sid\n"));
3740                                 return NULL;
3741                         }
3742                         continue;
3743                 }
3744
3745                 if (StrnCaseCmp(tok,"OWNER+:", 7) == 0) {
3746                         owner_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
3747                         if (!owner_sid ||
3748                             !convert_string_to_sid(ipc_cli, pol,
3749                                                    False,
3750                                                    owner_sid, tok+7)) {
3751                                 DEBUG(5, ("Failed to parse owner sid\n"));
3752                                 return NULL;
3753                         }
3754                         continue;
3755                 }
3756
3757                 if (StrnCaseCmp(tok,"GROUP:", 6) == 0) {
3758                         grp_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
3759                         if (!grp_sid ||
3760                             !convert_string_to_sid(ipc_cli, pol,
3761                                                    numeric,
3762                                                    grp_sid, tok+6)) {
3763                                 DEBUG(5, ("Failed to parse group sid\n"));
3764                                 return NULL;
3765                         }
3766                         continue;
3767                 }
3768
3769                 if (StrnCaseCmp(tok,"GROUP+:", 7) == 0) {
3770                         grp_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
3771                         if (!grp_sid ||
3772                             !convert_string_to_sid(ipc_cli, pol,
3773                                                    False,
3774                                                    grp_sid, tok+6)) {
3775                                 DEBUG(5, ("Failed to parse group sid\n"));
3776                                 return NULL;
3777                         }
3778                         continue;
3779                 }
3780
3781                 if (StrnCaseCmp(tok,"ACL:", 4) == 0) {
3782                         SEC_ACE ace;
3783                         if (!parse_ace(ipc_cli, pol, &ace, numeric, tok+4)) {
3784                                 DEBUG(5, ("Failed to parse ACL %s\n", tok));
3785                                 return NULL;
3786                         }
3787                         if(!add_ace(&dacl, &ace, ctx)) {
3788                                 DEBUG(5, ("Failed to add ACL %s\n", tok));
3789                                 return NULL;
3790                         }
3791                         continue;
3792                 }
3793
3794                 if (StrnCaseCmp(tok,"ACL+:", 5) == 0) {
3795                         SEC_ACE ace;
3796                         if (!parse_ace(ipc_cli, pol, &ace, False, tok+5)) {
3797                                 DEBUG(5, ("Failed to parse ACL %s\n", tok));
3798                                 return NULL;
3799                         }
3800                         if(!add_ace(&dacl, &ace, ctx)) {
3801                                 DEBUG(5, ("Failed to add ACL %s\n", tok));
3802                                 return NULL;
3803                         }
3804                         continue;
3805                 }
3806
3807                 DEBUG(5, ("Failed to parse security descriptor\n"));
3808                 return NULL;
3809         }
3810
3811         ret = make_sec_desc(ctx, revision, SEC_DESC_SELF_RELATIVE, 
3812                             owner_sid, grp_sid, NULL, dacl, &sd_size);
3813
3814         SAFE_FREE(grp_sid);
3815         SAFE_FREE(owner_sid);
3816
3817         return ret;
3818 }
3819
3820
3821 /* Obtain the current dos attributes */
3822 static DOS_ATTR_DESC *dos_attr_query(SMBCCTX *context,
3823                                      TALLOC_CTX *ctx,
3824                                      const char *filename,
3825                                      SMBCSRV *srv)
3826 {
3827         time_t m_time = 0, a_time = 0, c_time = 0;
3828         SMB_OFF_T size = 0;
3829         uint16 mode = 0;
3830         SMB_INO_T inode = 0;
3831         DOS_ATTR_DESC *ret;
3832     
3833         ret = TALLOC_P(ctx, DOS_ATTR_DESC);
3834         if (!ret) {
3835                 errno = ENOMEM;
3836                 return NULL;
3837         }
3838
3839         /* Obtain the DOS attributes */
3840         if (!smbc_getatr(context, srv, CONST_DISCARD(char *, filename),
3841                          &mode, &size, 
3842                          &c_time, &a_time, &m_time, &inode)) {
3843         
3844                 errno = smbc_errno(context, &srv->cli);
3845                 DEBUG(5, ("dos_attr_query Failed to query old attributes\n"));
3846                 return NULL;
3847         
3848         }
3849                 
3850         ret->mode = mode;
3851         ret->size = size;
3852         ret->a_time = a_time;
3853         ret->c_time = c_time;
3854         ret->m_time = m_time;
3855         ret->inode = inode;
3856
3857         return ret;
3858 }
3859
3860
3861 /* parse a ascii version of a security descriptor */
3862 static void dos_attr_parse(SMBCCTX *context,
3863                            DOS_ATTR_DESC *dad,
3864                            SMBCSRV *srv,
3865                            char *str)
3866 {
3867         const char *p = str;
3868         fstring tok;
3869
3870         while (next_token(&p, tok, "\t,\r\n", sizeof(tok))) {
3871
3872                 if (StrnCaseCmp(tok, "MODE:", 5) == 0) {
3873                         dad->mode = strtol(tok+5, NULL, 16);
3874                         continue;
3875                 }
3876
3877                 if (StrnCaseCmp(tok, "SIZE:", 5) == 0) {
3878                         dad->size = (SMB_OFF_T)atof(tok+5);
3879                         continue;
3880                 }
3881
3882                 if (StrnCaseCmp(tok, "A_TIME:", 7) == 0) {
3883                         dad->a_time = (time_t)strtol(tok+7, NULL, 10);
3884                         continue;
3885                 }
3886
3887                 if (StrnCaseCmp(tok, "C_TIME:", 7) == 0) {
3888                         dad->c_time = (time_t)strtol(tok+7, NULL, 10);
3889                         continue;
3890                 }
3891
3892                 if (StrnCaseCmp(tok, "M_TIME:", 7) == 0) {
3893                         dad->m_time = (time_t)strtol(tok+7, NULL, 10);
3894                         continue;
3895                 }
3896
3897                 if (StrnCaseCmp(tok, "INODE:", 6) == 0) {
3898                         dad->inode = (SMB_INO_T)atof(tok+6);
3899                         continue;
3900                 }
3901         }
3902 }
3903
3904 /***************************************************** 
3905  Retrieve the acls for a file.
3906 *******************************************************/
3907
3908 static int cacl_get(SMBCCTX *context, TALLOC_CTX *ctx, SMBCSRV *srv,
3909                     struct cli_state *ipc_cli, POLICY_HND *pol,
3910                     char *filename, char *attr_name, char *buf, int bufsize)
3911 {
3912         uint32 i;
3913         int n = 0;
3914         int n_used;
3915         BOOL all;
3916         BOOL all_nt;
3917         BOOL all_nt_acls;
3918         BOOL all_dos;
3919         BOOL some_nt;
3920         BOOL some_dos;
3921         BOOL exclude_nt_revision = False;
3922         BOOL exclude_nt_owner = False;
3923         BOOL exclude_nt_group = False;
3924         BOOL exclude_nt_acl = False;
3925         BOOL exclude_dos_mode = False;
3926         BOOL exclude_dos_size = False;
3927         BOOL exclude_dos_ctime = False;
3928         BOOL exclude_dos_atime = False;
3929         BOOL exclude_dos_mtime = False;
3930         BOOL exclude_dos_inode = False;
3931         BOOL numeric = True;
3932         BOOL determine_size = (bufsize == 0);
3933         int fnum = -1;
3934         SEC_DESC *sd;
3935         fstring sidstr;
3936         fstring name_sandbox;
3937         char *name;
3938         char *pExclude;
3939         char *p;
3940         time_t m_time = 0, a_time = 0, c_time = 0;
3941         SMB_OFF_T size = 0;
3942         uint16 mode = 0;
3943         SMB_INO_T ino = 0;
3944         struct cli_state *cli = &srv->cli;
3945
3946         /* Copy name so we can strip off exclusions (if any are specified) */
3947         strncpy(name_sandbox, attr_name, sizeof(name_sandbox) - 1);
3948
3949         /* Ensure name is null terminated */
3950         name_sandbox[sizeof(name_sandbox) - 1] = '\0';
3951
3952         /* Play in the sandbox */
3953         name = name_sandbox;
3954
3955         /* If there are any exclusions, point to them and mask them from name */
3956         if ((pExclude = strchr(name, '!')) != NULL)
3957         {
3958                 *pExclude++ = '\0';
3959         }
3960
3961         all = (StrnCaseCmp(name, "system.*", 8) == 0);
3962         all_nt = (StrnCaseCmp(name, "system.nt_sec_desc.*", 20) == 0);
3963         all_nt_acls = (StrnCaseCmp(name, "system.nt_sec_desc.acl.*", 24) == 0);
3964         all_dos = (StrnCaseCmp(name, "system.dos_attr.*", 17) == 0);
3965         some_nt = (StrnCaseCmp(name, "system.nt_sec_desc.", 19) == 0);
3966         some_dos = (StrnCaseCmp(name, "system.dos_attr.", 16) == 0);
3967         numeric = (* (name + strlen(name) - 1) != '+');
3968
3969         /* Look for exclusions from "all" requests */
3970         if (all || all_nt || all_dos) {
3971
3972                 /* Exclusions are delimited by '!' */
3973                 for (; pExclude != NULL; pExclude = (p == NULL ? NULL : p + 1)) {
3974
3975                 /* Find end of this exclusion name */
3976                 if ((p = strchr(pExclude, '!')) != NULL)
3977                 {
3978                     *p = '\0';
3979                 }
3980
3981                 /* Which exclusion name is this? */
3982                 if (StrCaseCmp(pExclude, "nt_sec_desc.revision") == 0) {
3983                     exclude_nt_revision = True;
3984                 }
3985                 else if (StrCaseCmp(pExclude, "nt_sec_desc.owner") == 0) {
3986                     exclude_nt_owner = True;
3987                 }
3988                 else if (StrCaseCmp(pExclude, "nt_sec_desc.group") == 0) {
3989                     exclude_nt_group = True;
3990                 }
3991                 else if (StrCaseCmp(pExclude, "nt_sec_desc.acl") == 0) {
3992                     exclude_nt_acl = True;
3993                 }
3994                 else if (StrCaseCmp(pExclude, "dos_attr.mode") == 0) {
3995                     exclude_dos_mode = True;
3996                 }
3997                 else if (StrCaseCmp(pExclude, "dos_attr.size") == 0) {
3998                     exclude_dos_size = True;
3999                 }
4000                 else if (StrCaseCmp(pExclude, "dos_attr.c_time") == 0) {
4001                     exclude_dos_ctime = True;
4002                 }
4003                 else if (StrCaseCmp(pExclude, "dos_attr.a_time") == 0) {
4004                     exclude_dos_atime = True;
4005                 }
4006                 else if (StrCaseCmp(pExclude, "dos_attr.m_time") == 0) {
4007                     exclude_dos_mtime = True;
4008                 }
4009                 else if (StrCaseCmp(pExclude, "dos_attr.inode") == 0) {
4010                     exclude_dos_inode = True;
4011                 }
4012                 else {
4013                     DEBUG(5, ("cacl_get received unknown exclusion: %s\n",
4014                               pExclude));
4015                     errno = ENOATTR;
4016                     return -1;
4017                 }
4018             }
4019         }
4020
4021         n_used = 0;
4022
4023         /*
4024          * If we are (possibly) talking to an NT or new system and some NT
4025          * attributes have been requested...
4026          */
4027         if (ipc_cli && (all || some_nt || all_nt_acls)) {
4028                 /* Point to the portion after "system.nt_sec_desc." */
4029                 name += 19;     /* if (all) this will be invalid but unused */
4030
4031                 /* ... then obtain any NT attributes which were requested */
4032                 fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
4033
4034                 if (fnum == -1) {
4035                         DEBUG(5, ("cacl_get failed to open %s: %s\n",
4036                                   filename, cli_errstr(cli)));
4037                         errno = 0;
4038                         return -1;
4039                 }
4040
4041                 sd = cli_query_secdesc(cli, fnum, ctx);
4042
4043                 if (!sd) {
4044                         DEBUG(5,
4045                               ("cacl_get Failed to query old descriptor\n"));
4046                         errno = 0;
4047                         return -1;
4048                 }
4049
4050                 cli_close(cli, fnum);
4051
4052                 if (! exclude_nt_revision) {
4053                         if (all || all_nt) {
4054                                 if (determine_size) {
4055                                         p = talloc_asprintf(ctx,
4056                                                             "REVISION:%d",
4057                                                             sd->revision);
4058                                         if (!p) {
4059                                                 errno = ENOMEM;
4060                                                 return -1;
4061                                         }
4062                                         n = strlen(p);
4063                                 } else {
4064                                         n = snprintf(buf, bufsize,
4065                                                      "REVISION:%d", sd->revision);
4066                                 }
4067                         } else if (StrCaseCmp(name, "revision") == 0) {
4068                                 if (determine_size) {
4069                                         p = talloc_asprintf(ctx, "%d",
4070                                                             sd->revision);
4071                                         if (!p) {
4072                                                 errno = ENOMEM;
4073                                                 return -1;
4074                                         }
4075                                         n = strlen(p);
4076                                 } else {
4077                                         n = snprintf(buf, bufsize, "%d",
4078                                                      sd->revision);
4079                                 }
4080                         }
4081         
4082                         if (!determine_size && n > bufsize) {
4083                                 errno = ERANGE;
4084                                 return -1;
4085                         }
4086                         buf += n;
4087                         n_used += n;
4088                         bufsize -= n;
4089                 }
4090
4091                 if (! exclude_nt_owner) {
4092                         /* Get owner and group sid */
4093                         if (sd->owner_sid) {
4094                                 convert_sid_to_string(ipc_cli, pol,
4095                                                       sidstr,
4096                                                       numeric,
4097                                                       sd->owner_sid);
4098                         } else {
4099                                 fstrcpy(sidstr, "");
4100                         }
4101
4102                         if (all || all_nt) {
4103                                 if (determine_size) {
4104                                         p = talloc_asprintf(ctx, ",OWNER:%s",
4105                                                             sidstr);
4106                                         if (!p) {
4107                                                 errno = ENOMEM;
4108                                                 return -1;
4109                                         }
4110                                         n = strlen(p);
4111                                 } else {
4112                                         n = snprintf(buf, bufsize,
4113                                                      ",OWNER:%s", sidstr);
4114                                 }
4115                         } else if (StrnCaseCmp(name, "owner", 5) == 0) {
4116                                 if (determine_size) {
4117                                         p = talloc_asprintf(ctx, "%s", sidstr);
4118                                         if (!p) {
4119                                                 errno = ENOMEM;
4120                                                 return -1;
4121                                         }
4122                                         n = strlen(p);
4123                                 } else {
4124                                         n = snprintf(buf, bufsize, "%s",
4125                                                      sidstr);
4126                                 }
4127                         }
4128
4129                         if (!determine_size && n > bufsize) {
4130                                 errno = ERANGE;
4131                                 return -1;
4132                         }
4133                         buf += n;
4134                         n_used += n;
4135                         bufsize -= n;
4136                 }
4137
4138                 if (! exclude_nt_group) {
4139                         if (sd->grp_sid) {
4140                                 convert_sid_to_string(ipc_cli, pol,
4141                                                       sidstr, numeric,
4142                                                       sd->grp_sid);
4143                         } else {
4144                                 fstrcpy(sidstr, "");
4145                         }
4146
4147                         if (all || all_nt) {
4148                                 if (determine_size) {
4149                                         p = talloc_asprintf(ctx, ",GROUP:%s",
4150                                                             sidstr);
4151                                         if (!p) {
4152                                                 errno = ENOMEM;
4153                                                 return -1;
4154                                         }
4155                                         n = strlen(p);
4156                                 } else {
4157                                         n = snprintf(buf, bufsize,
4158                                                      ",GROUP:%s", sidstr);
4159                                 }
4160                         } else if (StrnCaseCmp(name, "group", 5) == 0) {
4161                                 if (determine_size) {
4162                                         p = talloc_asprintf(ctx, "%s", sidstr);
4163                                         if (!p) {
4164                                                 errno = ENOMEM;
4165                                                 return -1;
4166                                         }
4167                                         n = strlen(p);
4168                                 } else {
4169                                         n = snprintf(buf, bufsize, "%s", sidstr);
4170                                 }
4171                         }
4172
4173                         if (!determine_size && n > bufsize) {
4174                                 errno = ERANGE;
4175                                 return -1;
4176                         }
4177                         buf += n;
4178                         n_used += n;
4179                         bufsize -= n;
4180                 }
4181
4182                 if (! exclude_nt_acl) {
4183                         /* Add aces to value buffer  */
4184                         for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
4185
4186                                 SEC_ACE *ace = &sd->dacl->ace[i];
4187                                 convert_sid_to_string(ipc_cli, pol,
4188                                                       sidstr, numeric,
4189                                                       &ace->trustee);
4190
4191                                 if (all || all_nt) {
4192                                         if (determine_size) {
4193                                                 p = talloc_asprintf(
4194                                                         ctx, 
4195                                                         ",ACL:"
4196                                                         "%s:%d/%d/0x%08x", 
4197                                                         sidstr,
4198                                                         ace->type,
4199                                                         ace->flags,
4200                                                         ace->info.mask);
4201                                                 if (!p) {
4202                                                         errno = ENOMEM;
4203                                                         return -1;
4204                                                 }
4205                                                 n = strlen(p);
4206                                         } else {
4207                                                 n = snprintf(
4208                                                         buf, bufsize,
4209                                                         ",ACL:%s:%d/%d/0x%08x", 
4210                                                         sidstr,
4211                                                         ace->type,
4212                                                         ace->flags,
4213                                                         ace->info.mask);
4214                                         }
4215                                 } else if ((StrnCaseCmp(name, "acl", 3) == 0 &&
4216                                             StrCaseCmp(name + 3, sidstr) == 0) ||
4217                                            (StrnCaseCmp(name, "acl+", 4) == 0 &&
4218                                             StrCaseCmp(name + 4, sidstr) == 0)) {
4219                                         if (determine_size) {
4220                                                 p = talloc_asprintf(
4221                                                         ctx, 
4222                                                         "%d/%d/0x%08x", 
4223                                                         ace->type,
4224                                                         ace->flags,
4225                                                         ace->info.mask);
4226                                                 if (!p) {
4227                                                         errno = ENOMEM;
4228                                                         return -1;
4229                                                 }
4230                                                 n = strlen(p);
4231                                         } else {
4232                                                 n = snprintf(buf, bufsize,
4233                                                              "%d/%d/0x%08x", 
4234                                                              ace->type,
4235                                                              ace->flags,
4236                                                              ace->info.mask);
4237                                         }
4238                                 } else if (all_nt_acls) {
4239                                         if (determine_size) {
4240                                                 p = talloc_asprintf(
4241                                                         ctx, 
4242                                                         "%s%s:%d/%d/0x%08x",
4243                                                         i ? "," : "",
4244                                                         sidstr,
4245                                                         ace->type,
4246                                                         ace->flags,
4247                                                         ace->info.mask);
4248                                                 if (!p) {
4249                                                         errno = ENOMEM;
4250                                                         return -1;
4251                                                 }
4252                                                 n = strlen(p);
4253                                         } else {
4254                                                 n = snprintf(buf, bufsize,
4255                                                              "%s%s:%d/%d/0x%08x",
4256                                                              i ? "," : "",
4257                                                              sidstr,
4258                                                              ace->type,
4259                                                              ace->flags,
4260                                                              ace->info.mask);
4261                                         }
4262                                 }
4263                                 if (n > bufsize) {
4264                                         errno = ERANGE;
4265                                         return -1;
4266                                 }
4267                                 buf += n;
4268                                 n_used += n;
4269                                 bufsize -= n;
4270                         }
4271                 }
4272
4273                 /* Restore name pointer to its original value */
4274                 name -= 19;
4275         }
4276
4277         if (all || some_dos) {
4278                 /* Point to the portion after "system.dos_attr." */
4279                 name += 16;     /* if (all) this will be invalid but unused */
4280
4281                 /* Obtain the DOS attributes */
4282                 if (!smbc_getatr(context, srv, filename, &mode, &size, 
4283                                  &c_time, &a_time, &m_time, &ino)) {
4284                         
4285                         errno = smbc_errno(context, &srv->cli);
4286                         return -1;
4287                         
4288                 }
4289                 
4290                 if (! exclude_dos_mode) {
4291                         if (all || all_dos) {
4292                                 if (determine_size) {
4293                                         p = talloc_asprintf(ctx,
4294                                                             "%sMODE:0x%x",
4295                                                             (ipc_cli &&
4296                                                              (all || some_nt)
4297                                                              ? ","
4298                                                              : ""),
4299                                                             mode);
4300                                         if (!p) {
4301                                                 errno = ENOMEM;
4302                                                 return -1;
4303                                         }
4304                                         n = strlen(p);
4305                                 } else {
4306                                         n = snprintf(buf, bufsize,
4307                                                      "%sMODE:0x%x",
4308                                                      (ipc_cli &&
4309                                                       (all || some_nt)
4310                                                       ? ","
4311                                                       : ""),
4312                                                      mode);
4313                                 }
4314                         } else if (StrCaseCmp(name, "mode") == 0) {
4315                                 if (determine_size) {
4316                                         p = talloc_asprintf(ctx, "0x%x", mode);
4317                                         if (!p) {
4318                                                 errno = ENOMEM;
4319                                                 return -1;
4320                                         }
4321                                         n = strlen(p);
4322                                 } else {
4323                                         n = snprintf(buf, bufsize, "0x%x", mode);
4324                                 }
4325                         }
4326         
4327                         if (!determine_size && n > bufsize) {
4328                                 errno = ERANGE;
4329                                 return -1;
4330                         }
4331                         buf += n;
4332                         n_used += n;
4333                         bufsize -= n;
4334                 }
4335
4336                 if (! exclude_dos_size) {
4337                         if (all || all_dos) {
4338                                 if (determine_size) {
4339                                         p = talloc_asprintf(
4340                                                 ctx,
4341                                                 ",SIZE:%.0f",
4342                                                 (double)size);
4343                                         if (!p) {
4344                                                 errno = ENOMEM;
4345                                                 return -1;
4346                                         }
4347                                         n = strlen(p);
4348                                 } else {
4349                                         n = snprintf(buf, bufsize,
4350                                                      ",SIZE:%.0f",
4351                                                      (double)size);
4352                                 }
4353                         } else if (StrCaseCmp(name, "size") == 0) {
4354                                 if (determine_size) {
4355                                         p = talloc_asprintf(
4356                                                 ctx,
4357                                                 "%.0f",
4358                                                 (double)size);
4359                                         if (!p) {
4360                                                 errno = ENOMEM;
4361                                                 return -1;
4362                                         }
4363                                         n = strlen(p);
4364                                 } else {
4365                                         n = snprintf(buf, bufsize,
4366                                                      "%.0f",
4367                                                      (double)size);
4368                                 }
4369                         }
4370         
4371                         if (!determine_size && n > bufsize) {
4372                                 errno = ERANGE;
4373                                 return -1;
4374                         }
4375                         buf += n;
4376                         n_used += n;
4377                         bufsize -= n;
4378                 }
4379
4380                 if (! exclude_dos_ctime) {
4381                         if (all || all_dos) {
4382                                 if (determine_size) {
4383                                         p = talloc_asprintf(ctx,
4384                                                             ",C_TIME:%lu",
4385                                                             c_time);
4386                                         if (!p) {
4387                                                 errno = ENOMEM;
4388                                                 return -1;
4389                                         }
4390                                         n = strlen(p);
4391                                 } else {
4392                                         n = snprintf(buf, bufsize,
4393                                                      ",C_TIME:%lu", c_time);
4394                                 }
4395                         } else if (StrCaseCmp(name, "c_time") == 0) {
4396                                 if (determine_size) {
4397                                         p = talloc_asprintf(ctx, "%lu", c_time);
4398                                         if (!p) {
4399                                                 errno = ENOMEM;
4400                                                 return -1;
4401                                         }
4402                                         n = strlen(p);
4403                                 } else {
4404                                         n = snprintf(buf, bufsize, "%lu", c_time);
4405                                 }
4406                         }
4407         
4408                         if (!determine_size && n > bufsize) {
4409                                 errno = ERANGE;
4410                                 return -1;
4411                         }
4412                         buf += n;
4413                         n_used += n;
4414                         bufsize -= n;
4415                 }
4416
4417                 if (! exclude_dos_atime) {
4418                         if (all || all_dos) {
4419                                 if (determine_size) {
4420                                         p = talloc_asprintf(ctx,
4421                                                             ",A_TIME:%lu",
4422                                                             a_time);
4423                                         if (!p) {
4424                                                 errno = ENOMEM;
4425                                                 return -1;
4426                                         }
4427                                         n = strlen(p);
4428                                 } else {
4429                                         n = snprintf(buf, bufsize,
4430                                                      ",A_TIME:%lu", a_time);
4431                                 }
4432                         } else if (StrCaseCmp(name, "a_time") == 0) {
4433                                 if (determine_size) {
4434                                         p = talloc_asprintf(ctx, "%lu", a_time);
4435                                         if (!p) {
4436                                                 errno = ENOMEM;
4437                                                 return -1;
4438                                         }
4439                                         n = strlen(p);
4440                                 } else {
4441                                         n = snprintf(buf, bufsize, "%lu", a_time);
4442                                 }
4443                         }
4444         
4445                         if (!determine_size && n > bufsize) {
4446                                 errno = ERANGE;
4447                                 return -1;
4448                         }
4449                         buf += n;
4450                         n_used += n;
4451                         bufsize -= n;
4452                 }
4453
4454                 if (! exclude_dos_mtime) {
4455                         if (all || all_dos) {
4456                                 if (determine_size) {
4457                                         p = talloc_asprintf(ctx,
4458                                                             ",M_TIME:%lu",
4459                                                             m_time);
4460                                         if (!p) {
4461                                                 errno = ENOMEM;
4462                                                 return -1;
4463                                         }
4464                                         n = strlen(p);
4465                                 } else {
4466                                         n = snprintf(buf, bufsize,
4467                                                      ",M_TIME:%lu", m_time);
4468                                 }
4469                         } else if (StrCaseCmp(name, "m_time") == 0) {
4470                                 if (determine_size) {
4471                                         p = talloc_asprintf(ctx, "%lu", m_time);
4472                                         if (!p) {
4473                                                 errno = ENOMEM;
4474                                                 return -1;
4475                                         }
4476                                         n = strlen(p);
4477                                 } else {
4478                                         n = snprintf(buf, bufsize, "%lu", m_time);
4479                                 }
4480                         }
4481         
4482                         if (!determine_size && n > bufsize) {
4483                                 errno = ERANGE;
4484                                 return -1;
4485                         }
4486                         buf += n;
4487                         n_used += n;
4488                         bufsize -= n;
4489                 }
4490
4491                 if (! exclude_dos_inode) {
4492                         if (all || all_dos) {
4493                                 if (determine_size) {
4494                                         p = talloc_asprintf(
4495                                                 ctx,
4496                                                 ",INODE:%.0f",
4497                                                 (double)ino);
4498                                         if (!p) {
4499                                                 errno = ENOMEM;
4500                                                 return -1;
4501                                         }
4502                                         n = strlen(p);
4503                                 } else {
4504                                         n = snprintf(buf, bufsize,
4505                                                      ",INODE:%.0f",
4506                                                      (double) ino);
4507                                 }
4508                         } else if (StrCaseCmp(name, "inode") == 0) {
4509                                 if (determine_size) {
4510                                         p = talloc_asprintf(
4511                                                 ctx,
4512                                                 "%.0f",
4513                                                 (double) ino);
4514                                         if (!p) {
4515                                                 errno = ENOMEM;
4516                                                 return -1;
4517                                         }
4518                                         n = strlen(p);
4519                                 } else {
4520                                         n = snprintf(buf, bufsize,
4521                                                      "%.0f",
4522                                                      (double) ino);
4523                                 }
4524                         }
4525         
4526                         if (!determine_size && n > bufsize) {
4527                                 errno = ERANGE;
4528                                 return -1;
4529                         }
4530                         buf += n;
4531                         n_used += n;
4532                         bufsize -= n;
4533                 }
4534
4535                 /* Restore name pointer to its original value */
4536                 name -= 16;
4537         }
4538
4539         if (n_used == 0) {
4540                 errno = ENOATTR;
4541                 return -1;
4542         }
4543
4544         return n_used;
4545 }
4546
4547
4548 /***************************************************** 
4549 set the ACLs on a file given an ascii description
4550 *******************************************************/
4551 static int cacl_set(TALLOC_CTX *ctx, struct cli_state *cli,
4552                     struct cli_state *ipc_cli, POLICY_HND *pol,
4553                     const char *filename, const char *the_acl,
4554                     int mode, int flags)
4555 {
4556         int fnum;
4557         int err = 0;
4558         SEC_DESC *sd = NULL, *old;
4559         SEC_ACL *dacl = NULL;
4560         DOM_SID *owner_sid = NULL; 
4561         DOM_SID *grp_sid = NULL;
4562         uint32 i, j;
4563         size_t sd_size;
4564         int ret = 0;
4565         char *p;
4566         BOOL numeric = True;
4567
4568         /* the_acl will be null for REMOVE_ALL operations */
4569         if (the_acl) {
4570                 numeric = ((p = strchr(the_acl, ':')) != NULL &&
4571                            p > the_acl &&
4572                            p[-1] != '+');
4573
4574                 /* if this is to set the entire ACL... */
4575                 if (*the_acl == '*') {
4576                         /* ... then increment past the first colon */
4577                         the_acl = p + 1;
4578                 }
4579
4580                 sd = sec_desc_parse(ctx, ipc_cli, pol, numeric,
4581                                     CONST_DISCARD(char *, the_acl));
4582
4583                 if (!sd) {
4584                         errno = EINVAL;
4585                         return -1;
4586                 }
4587         }
4588
4589         /* The desired access below is the only one I could find that works
4590            with NT4, W2KP and Samba */
4591
4592         fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
4593
4594         if (fnum == -1) {
4595                 DEBUG(5, ("cacl_set failed to open %s: %s\n",
4596                           filename, cli_errstr(cli)));
4597                 errno = 0;
4598                 return -1;
4599         }
4600
4601         old = cli_query_secdesc(cli, fnum, ctx);
4602
4603         if (!old) {
4604                 DEBUG(5, ("cacl_set Failed to query old descriptor\n"));
4605                 errno = 0;
4606                 return -1;
4607         }
4608
4609         cli_close(cli, fnum);
4610
4611         switch (mode) {
4612         case SMBC_XATTR_MODE_REMOVE_ALL:
4613                 old->dacl->num_aces = 0;
4614                 SAFE_FREE(old->dacl->ace);
4615                 SAFE_FREE(old->dacl);
4616                 old->off_dacl = 0;
4617                 dacl = old->dacl;
4618                 break;
4619
4620         case SMBC_XATTR_MODE_REMOVE:
4621                 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
4622                         BOOL found = False;
4623
4624                         for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
4625                                 if (sec_ace_equal(&sd->dacl->ace[i],
4626                                                   &old->dacl->ace[j])) {
4627                                         uint32 k;
4628                                         for (k=j; k<old->dacl->num_aces-1;k++) {
4629                                                 old->dacl->ace[k] = old->dacl->ace[k+1];
4630                                         }
4631                                         old->dacl->num_aces--;
4632                                         if (old->dacl->num_aces == 0) {
4633                                                 SAFE_FREE(old->dacl->ace);
4634                                                 SAFE_FREE(old->dacl);
4635                                                 old->off_dacl = 0;
4636                                         }
4637                                         found = True;
4638                                         dacl = old->dacl;
4639                                         break;
4640                                 }
4641                         }
4642
4643                         if (!found) {
4644                                 err = ENOATTR;
4645                                 ret = -1;
4646                                 goto failed;
4647                         }
4648                 }
4649                 break;
4650
4651         case SMBC_XATTR_MODE_ADD:
4652                 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
4653                         BOOL found = False;
4654
4655                         for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
4656                                 if (sid_equal(&sd->dacl->ace[i].trustee,
4657                                               &old->dacl->ace[j].trustee)) {
4658                                         if (!(flags & SMBC_XATTR_FLAG_CREATE)) {
4659                                                 err = EEXIST;
4660                                                 ret = -1;
4661                                                 goto failed;
4662                                         }
4663                                         old->dacl->ace[j] = sd->dacl->ace[i];
4664                                         ret = -1;
4665                                         found = True;
4666                                 }
4667                         }
4668
4669                         if (!found && (flags & SMBC_XATTR_FLAG_REPLACE)) {
4670                                 err = ENOATTR;
4671                                 ret = -1;
4672                                 goto failed;
4673                         }
4674                         
4675                         for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
4676                                 add_ace(&old->dacl, &sd->dacl->ace[i], ctx);
4677                         }
4678                 }
4679                 dacl = old->dacl;
4680                 break;
4681
4682         case SMBC_XATTR_MODE_SET:
4683                 old = sd;
4684                 owner_sid = old->owner_sid;
4685                 grp_sid = old->grp_sid;
4686                 dacl = old->dacl;
4687                 break;
4688
4689         case SMBC_XATTR_MODE_CHOWN:
4690                 owner_sid = sd->owner_sid;
4691                 break;
4692
4693         case SMBC_XATTR_MODE_CHGRP:
4694                 grp_sid = sd->grp_sid;
4695                 break;
4696         }
4697
4698         /* Denied ACE entries must come before allowed ones */
4699         sort_acl(old->dacl);
4700
4701         /* Create new security descriptor and set it */
4702         sd = make_sec_desc(ctx, old->revision, SEC_DESC_SELF_RELATIVE, 
4703                            owner_sid, grp_sid, NULL, dacl, &sd_size);
4704
4705         fnum = cli_nt_create(cli, filename,
4706                              WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS);
4707
4708         if (fnum == -1) {
4709                 DEBUG(5, ("cacl_set failed to open %s: %s\n",
4710                           filename, cli_errstr(cli)));
4711                 errno = 0;
4712                 return -1;
4713         }
4714
4715         if (!cli_set_secdesc(cli, fnum, sd)) {
4716                 DEBUG(5, ("ERROR: secdesc set failed: %s\n", cli_errstr(cli)));
4717                 ret = -1;
4718         }
4719
4720         /* Clean up */
4721
4722  failed:
4723         cli_close(cli, fnum);
4724
4725         if (err != 0) {
4726                 errno = err;
4727         }
4728         
4729         return ret;
4730 }
4731
4732
4733 int smbc_setxattr_ctx(SMBCCTX *context,
4734                       const char *fname,
4735                       const char *name,
4736                       const void *value,
4737                       size_t size,
4738                       int flags)
4739 {
4740         int ret;
4741         int ret2;
4742         SMBCSRV *srv;
4743         SMBCSRV *ipc_srv;
4744         fstring server, share, user, password, workgroup;
4745         pstring path;
4746         TALLOC_CTX *ctx;
4747         POLICY_HND pol;
4748         DOS_ATTR_DESC *dad;
4749
4750         if (!context || !context->internal ||
4751             !context->internal->_initialized) {
4752
4753                 errno = EINVAL;  /* Best I can think of ... */
4754                 return -1;
4755     
4756         }
4757
4758         if (!fname) {
4759
4760                 errno = EINVAL;
4761                 return -1;
4762
4763         }
4764   
4765         DEBUG(4, ("smbc_setxattr(%s, %s, %.*s)\n", fname, name, (int) size, (const char*)value));
4766
4767         if (smbc_parse_path(context, fname,
4768                             server, sizeof(server),
4769                             share, sizeof(share),
4770                             path, sizeof(path),
4771                             user, sizeof(user),
4772                             password, sizeof(password),
4773                             NULL, 0)) {
4774                 errno = EINVAL;
4775                 return -1;
4776         }
4777
4778         if (user[0] == (char)0) fstrcpy(user, context->user);
4779
4780         fstrcpy(workgroup, context->workgroup);
4781
4782         srv = smbc_server(context, server, share, workgroup, user, password);
4783         if (!srv) {
4784                 return -1;  /* errno set by smbc_server */
4785         }
4786
4787         if (! srv->no_nt_session) {
4788                 ipc_srv = smbc_attr_server(context, server, share,
4789                                            workgroup, user, password,
4790                                            &pol);
4791                 srv->no_nt_session = True;
4792         } else {
4793                 ipc_srv = NULL;
4794         }
4795         
4796         ctx = talloc_init("smbc_setxattr");
4797         if (!ctx) {
4798                 errno = ENOMEM;
4799                 return -1;
4800         }
4801
4802         /*
4803          * Are they asking to set the entire set of known attributes?
4804          */
4805         if (StrCaseCmp(name, "system.*") == 0 ||
4806             StrCaseCmp(name, "system.*+") == 0) {
4807                 /* Yup. */
4808                 char *namevalue =
4809                         talloc_asprintf(ctx, "%s:%s", name+7, (const char *) value);
4810                 if (! namevalue) {
4811                         errno = ENOMEM;
4812                         ret = -1;
4813                         return -1;
4814                 }
4815
4816                 if (ipc_srv) {
4817                         ret = cacl_set(ctx, &srv->cli,
4818                                        &ipc_srv->cli, &pol, path,
4819                                        namevalue,
4820                                        (*namevalue == '*'
4821                                         ? SMBC_XATTR_MODE_SET
4822                                         : SMBC_XATTR_MODE_ADD),
4823                                        flags);
4824                 } else {
4825                         ret = 0;
4826                 }
4827
4828                 /* get a DOS Attribute Descriptor with current attributes */
4829                 dad = dos_attr_query(context, ctx, path, srv);
4830                 if (dad) {
4831                         /* Overwrite old with new, using what was provided */
4832                         dos_attr_parse(context, dad, srv, namevalue);
4833
4834                         /* Set the new DOS attributes */
4835                         if (! smbc_setatr(context, srv, path,
4836                                           dad->c_time,
4837                                           dad->a_time,
4838                                           dad->m_time,
4839                                           dad->mode)) {
4840
4841                                 /* cause failure if NT failed too */
4842                                 dad = NULL; 
4843                         }
4844                 }
4845
4846                 /* we only fail if both NT and DOS sets failed */
4847                 if (ret < 0 && ! dad) {
4848                         ret = -1; /* in case dad was null */
4849                 }
4850                 else {
4851                         ret = 0;
4852                 }
4853
4854                 talloc_destroy(ctx);
4855                 return ret;
4856         }
4857
4858         /*
4859          * Are they asking to set an access control element or to set
4860          * the entire access control list?
4861          */
4862         if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
4863             StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 ||
4864             StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
4865             StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
4866             StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) {
4867
4868                 /* Yup. */
4869                 char *namevalue =
4870                         talloc_asprintf(ctx, "%s:%s", name+19, (const char *) value);
4871
4872                 if (! ipc_srv) {
4873                         ret = -1; /* errno set by smbc_server() */
4874                 }
4875                 else if (! namevalue) {
4876                         errno = ENOMEM;
4877                         ret = -1;
4878                 } else {
4879                         ret = cacl_set(ctx, &srv->cli,
4880                                        &ipc_srv->cli, &pol, path,
4881                                        namevalue,
4882                                        (*namevalue == '*'
4883                                         ? SMBC_XATTR_MODE_SET
4884                                         : SMBC_XATTR_MODE_ADD),
4885                                        flags);
4886                 }
4887                 talloc_destroy(ctx);
4888                 return ret;
4889         }
4890
4891         /*
4892          * Are they asking to set the owner?
4893          */
4894         if (StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
4895             StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0) {
4896
4897                 /* Yup. */
4898                 char *namevalue =
4899                         talloc_asprintf(ctx, "%s:%s", name+19, (const char *) value);
4900
4901                 if (! ipc_srv) {
4902                         
4903                         ret = -1; /* errno set by smbc_server() */
4904                 }
4905                 else if (! namevalue) {
4906                         errno = ENOMEM;
4907                         ret = -1;
4908                 } else {
4909                         ret = cacl_set(ctx, &srv->cli,
4910                                        &ipc_srv->cli, &pol, path,
4911                                        namevalue, SMBC_XATTR_MODE_CHOWN, 0);
4912                 }
4913                 talloc_destroy(ctx);
4914                 return ret;
4915         }
4916
4917         /*
4918          * Are they asking to set the group?
4919          */
4920         if (StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
4921             StrCaseCmp(name, "system.nt_sec_desc.group+") == 0) {
4922
4923                 /* Yup. */
4924                 char *namevalue =
4925                         talloc_asprintf(ctx, "%s:%s", name+19, (const char *) value);
4926
4927                 if (! ipc_srv) {
4928                         /* errno set by smbc_server() */
4929                         ret = -1;
4930                 }
4931                 else if (! namevalue) {
4932                         errno = ENOMEM;
4933                         ret = -1;
4934                 } else {
4935                         ret = cacl_set(ctx, &srv->cli,
4936                                        &ipc_srv->cli, &pol, path,
4937                                        namevalue, SMBC_XATTR_MODE_CHOWN, 0);
4938                 }
4939                 talloc_destroy(ctx);
4940                 return ret;
4941         }
4942
4943         /*
4944          * Are they asking to set a DOS attribute?
4945          */
4946         if (StrCaseCmp(name, "system.dos_attr.*") == 0 ||
4947             StrCaseCmp(name, "system.dos_attr.mode") == 0 ||
4948             StrCaseCmp(name, "system.dos_attr.c_time") == 0 ||
4949             StrCaseCmp(name, "system.dos_attr.a_time") == 0 ||
4950             StrCaseCmp(name, "system.dos_attr.m_time") == 0) {
4951
4952                 /* get a DOS Attribute Descriptor with current attributes */
4953                 dad = dos_attr_query(context, ctx, path, srv);
4954                 if (dad) {
4955                         char *namevalue =
4956                                 talloc_asprintf(ctx, "%s:%s", name+16, (const char *) value);
4957                         if (! namevalue) {
4958                                 errno = ENOMEM;
4959                                 ret = -1;
4960                         } else {
4961                                 /* Overwrite old with provided new params */
4962                                 dos_attr_parse(context, dad, srv, namevalue);
4963
4964                                 /* Set the new DOS attributes */
4965                                 ret2 = smbc_setatr(context, srv, path,
4966                                                    dad->c_time,
4967                                                    dad->a_time,
4968                                                    dad->m_time,
4969                                                    dad->mode);
4970
4971                                 /* ret2 has True (success) / False (failure) */
4972                                 if (ret2) {
4973                                         ret = 0;
4974                                 } else {
4975                                         ret = -1;
4976                                 }
4977                         }
4978                 } else {
4979                         ret = -1;
4980                 }
4981
4982                 talloc_destroy(ctx);
4983                 return ret;
4984         }
4985
4986         /* Unsupported attribute name */
4987         talloc_destroy(ctx);
4988         errno = EINVAL;
4989         return -1;
4990 }
4991
4992 int smbc_getxattr_ctx(SMBCCTX *context,
4993                       const char *fname,
4994                       const char *name,
4995                       const void *value,
4996                       size_t size)
4997 {
4998         int ret;
4999         SMBCSRV *srv;
5000         SMBCSRV *ipc_srv;
5001         fstring server, share, user, password, workgroup;
5002         pstring path;
5003         TALLOC_CTX *ctx;
5004         POLICY_HND pol;
5005
5006
5007         if (!context || !context->internal ||
5008             !context->internal->_initialized) {
5009
5010                 errno = EINVAL;  /* Best I can think of ... */
5011                 return -1;
5012     
5013         }
5014
5015         if (!fname) {
5016
5017                 errno = EINVAL;
5018                 return -1;
5019
5020         }
5021   
5022         DEBUG(4, ("smbc_getxattr(%s, %s)\n", fname, name));
5023
5024         if (smbc_parse_path(context, fname,
5025                             server, sizeof(server),
5026                             share, sizeof(share),
5027                             path, sizeof(path),
5028                             user, sizeof(user),
5029                             password, sizeof(password),
5030                             NULL, 0)) {
5031                 errno = EINVAL;
5032                 return -1;
5033         }
5034
5035         if (user[0] == (char)0) fstrcpy(user, context->user);
5036
5037         fstrcpy(workgroup, context->workgroup);
5038
5039         srv = smbc_server(context, server, share, workgroup, user, password);
5040         if (!srv) {
5041                 return -1;  /* errno set by smbc_server */
5042         }
5043
5044         if (! srv->no_nt_session) {
5045                 ipc_srv = smbc_attr_server(context, server, share,
5046                                            workgroup, user, password,
5047                                            &pol);
5048                 if (! ipc_srv) {
5049                         srv->no_nt_session = True;
5050                 }
5051         } else {
5052                 ipc_srv = NULL;
5053         }
5054         
5055         ctx = talloc_init("smbc:getxattr");
5056         if (!ctx) {
5057                 errno = ENOMEM;
5058                 return -1;
5059         }
5060
5061         /* Are they requesting a supported attribute? */
5062         if (StrCaseCmp(name, "system.*") == 0 ||
5063             StrnCaseCmp(name, "system.*!", 9) == 0 ||
5064             StrCaseCmp(name, "system.*+") == 0 ||
5065             StrnCaseCmp(name, "system.*+!", 10) == 0 ||
5066             StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
5067             StrnCaseCmp(name, "system.nt_sec_desc.*!", 21) == 0 ||
5068             StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 ||
5069             StrnCaseCmp(name, "system.nt_sec_desc.*+!", 22) == 0 ||
5070             StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
5071             StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
5072             StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 ||
5073             StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
5074             StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 ||
5075             StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
5076             StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0 ||
5077             StrCaseCmp(name, "system.dos_attr.*") == 0 ||
5078             StrnCaseCmp(name, "system.dos_attr.*!", 18) == 0 ||
5079             StrCaseCmp(name, "system.dos_attr.mode") == 0 ||
5080             StrCaseCmp(name, "system.dos_attr.size") == 0 ||
5081             StrCaseCmp(name, "system.dos_attr.c_time") == 0 ||
5082             StrCaseCmp(name, "system.dos_attr.a_time") == 0 ||
5083             StrCaseCmp(name, "system.dos_attr.m_time") == 0 ||
5084             StrCaseCmp(name, "system.dos_attr.inode") == 0) {
5085
5086                 /* Yup. */
5087                 ret = cacl_get(context, ctx, srv,
5088                                ipc_srv == NULL ? NULL : &ipc_srv->cli, 
5089                                &pol, path,
5090                                CONST_DISCARD(char *, name),
5091                                CONST_DISCARD(char *, value), size);
5092                 if (ret < 0 && errno == 0) {
5093                         errno = smbc_errno(context, &srv->cli);
5094                 }
5095                 talloc_destroy(ctx);
5096                 return ret;
5097         }
5098
5099         /* Unsupported attribute name */
5100         talloc_destroy(ctx);
5101         errno = EINVAL;
5102         return -1;
5103 }
5104
5105
5106 int smbc_removexattr_ctx(SMBCCTX *context,
5107                       const char *fname,
5108                       const char *name)
5109 {
5110         int ret;
5111         SMBCSRV *srv;
5112         SMBCSRV *ipc_srv;
5113         fstring server, share, user, password, workgroup;
5114         pstring path;
5115         TALLOC_CTX *ctx;
5116         POLICY_HND pol;
5117
5118         if (!context || !context->internal ||
5119             !context->internal->_initialized) {
5120
5121                 errno = EINVAL;  /* Best I can think of ... */
5122                 return -1;
5123     
5124         }
5125
5126         if (!fname) {
5127
5128                 errno = EINVAL;
5129                 return -1;
5130
5131         }
5132   
5133         DEBUG(4, ("smbc_removexattr(%s, %s)\n", fname, name));
5134
5135         if (smbc_parse_path(context, fname,
5136                             server, sizeof(server),
5137                             share, sizeof(share),
5138                             path, sizeof(path),
5139                             user, sizeof(user),
5140                             password, sizeof(password),
5141                             NULL, 0)) {
5142                 errno = EINVAL;
5143                 return -1;
5144         }
5145
5146         if (user[0] == (char)0) fstrcpy(user, context->user);
5147
5148         fstrcpy(workgroup, context->workgroup);
5149
5150         srv = smbc_server(context, server, share, workgroup, user, password);
5151         if (!srv) {
5152                 return -1;  /* errno set by smbc_server */
5153         }
5154
5155         if (! srv->no_nt_session) {
5156                 ipc_srv = smbc_attr_server(context, server, share,
5157                                            workgroup, user, password,
5158                                            &pol);
5159                 srv->no_nt_session = True;
5160         } else {
5161                 ipc_srv = NULL;
5162         }
5163         
5164         if (! ipc_srv) {
5165                 return -1; /* errno set by smbc_attr_server */
5166         }
5167
5168         ctx = talloc_init("smbc_removexattr");
5169         if (!ctx) {
5170                 errno = ENOMEM;
5171                 return -1;
5172         }
5173
5174         /* Are they asking to set the entire ACL? */
5175         if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
5176             StrCaseCmp(name, "system.nt_sec_desc.*+") == 0) {
5177
5178                 /* Yup. */
5179                 ret = cacl_set(ctx, &srv->cli,
5180                                &ipc_srv->cli, &pol, path,
5181                                NULL, SMBC_XATTR_MODE_REMOVE_ALL, 0);
5182                 talloc_destroy(ctx);
5183                 return ret;
5184         }
5185
5186         /*
5187          * Are they asking to remove one or more spceific security descriptor
5188          * attributes?
5189          */
5190         if (StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
5191             StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
5192             StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 ||
5193             StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
5194             StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 ||
5195             StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
5196             StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) {
5197
5198                 /* Yup. */
5199                 ret = cacl_set(ctx, &srv->cli,
5200                                &ipc_srv->cli, &pol, path,
5201                                name + 19, SMBC_XATTR_MODE_REMOVE, 0);
5202                 talloc_destroy(ctx);
5203                 return ret;
5204         }
5205
5206         /* Unsupported attribute name */
5207         talloc_destroy(ctx);
5208         errno = EINVAL;
5209         return -1;
5210 }
5211
5212 int smbc_listxattr_ctx(SMBCCTX *context,
5213                        const char *fname,
5214                        char *list,
5215                        size_t size)
5216 {
5217         /*
5218          * This isn't quite what listxattr() is supposed to do.  This returns
5219          * the complete set of attribute names, always, rather than only those
5220          * attribute names which actually exist for a file.  Hmmm...
5221          */
5222         const char supported[] =
5223                 "system.*\0"
5224                 "system.*+\0"
5225                 "system.nt_sec_desc.revision\0"
5226                 "system.nt_sec_desc.owner\0"
5227                 "system.nt_sec_desc.owner+\0"
5228                 "system.nt_sec_desc.group\0"
5229                 "system.nt_sec_desc.group+\0"
5230                 "system.nt_sec_desc.acl.*\0"
5231                 "system.nt_sec_desc.acl\0"
5232                 "system.nt_sec_desc.acl+\0"
5233                 "system.nt_sec_desc.*\0"
5234                 "system.nt_sec_desc.*+\0"
5235                 "system.dos_attr.*\0"
5236                 "system.dos_attr.mode\0"
5237                 "system.dos_attr.c_time\0"
5238                 "system.dos_attr.a_time\0"
5239                 "system.dos_attr.m_time\0"
5240                 ;
5241
5242         if (size == 0) {
5243                 return sizeof(supported);
5244         }
5245
5246         if (sizeof(supported) > size) {
5247                 errno = ERANGE;
5248                 return -1;
5249         }
5250
5251         /* this can't be strcpy() because there are embedded null characters */
5252         memcpy(list, supported, sizeof(supported));
5253         return sizeof(supported);
5254 }
5255
5256
5257 /*
5258  * Open a print file to be written to by other calls
5259  */
5260
5261 static SMBCFILE *smbc_open_print_job_ctx(SMBCCTX *context, const char *fname)
5262 {
5263         fstring server, share, user, password;
5264         pstring path;
5265         
5266         if (!context || !context->internal ||
5267             !context->internal->_initialized) {
5268
5269                 errno = EINVAL;
5270                 return NULL;
5271     
5272         }
5273
5274         if (!fname) {
5275
5276                 errno = EINVAL;
5277                 return NULL;
5278
5279         }
5280   
5281         DEBUG(4, ("smbc_open_print_job_ctx(%s)\n", fname));
5282
5283         if (smbc_parse_path(context, fname,
5284                             server, sizeof(server),
5285                             share, sizeof(share),
5286                             path, sizeof(path),
5287                             user, sizeof(user),
5288                             password, sizeof(password),
5289                             NULL, 0)) {
5290                 errno = EINVAL;
5291                 return NULL;
5292         }
5293
5294         /* What if the path is empty, or the file exists? */
5295
5296         return context->open(context, fname, O_WRONLY, 666);
5297
5298 }
5299
5300 /*
5301  * Routine to print a file on a remote server ...
5302  *
5303  * We open the file, which we assume to be on a remote server, and then
5304  * copy it to a print file on the share specified by printq.
5305  */
5306
5307 static int smbc_print_file_ctx(SMBCCTX *c_file, const char *fname, SMBCCTX *c_print, const char *printq)
5308 {
5309         SMBCFILE *fid1, *fid2;
5310         int bytes, saverr, tot_bytes = 0;
5311         char buf[4096];
5312
5313         if (!c_file || !c_file->internal->_initialized || !c_print ||
5314             !c_print->internal->_initialized) {
5315
5316                 errno = EINVAL;
5317                 return -1;
5318
5319         }
5320
5321         if (!fname && !printq) {
5322
5323                 errno = EINVAL;
5324                 return -1;
5325
5326         }
5327
5328         /* Try to open the file for reading ... */
5329
5330         if ((long)(fid1 = c_file->open(c_file, fname, O_RDONLY, 0666)) < 0) {
5331                 
5332                 DEBUG(3, ("Error, fname=%s, errno=%i\n", fname, errno));
5333                 return -1;  /* smbc_open sets errno */
5334                 
5335         }
5336
5337         /* Now, try to open the printer file for writing */
5338
5339         if ((long)(fid2 = c_print->open_print_job(c_print, printq)) < 0) {
5340
5341                 saverr = errno;  /* Save errno */
5342                 c_file->close_fn(c_file, fid1);
5343                 errno = saverr;
5344                 return -1;
5345
5346         }
5347
5348         while ((bytes = c_file->read(c_file, fid1, buf, sizeof(buf))) > 0) {
5349
5350                 tot_bytes += bytes;
5351
5352                 if ((c_print->write(c_print, fid2, buf, bytes)) < 0) {
5353
5354                         saverr = errno;
5355                         c_file->close_fn(c_file, fid1);
5356                         c_print->close_fn(c_print, fid2);
5357                         errno = saverr;
5358
5359                 }
5360
5361         }
5362
5363         saverr = errno;
5364
5365         c_file->close_fn(c_file, fid1);  /* We have to close these anyway */
5366         c_print->close_fn(c_print, fid2);
5367
5368         if (bytes < 0) {
5369
5370                 errno = saverr;
5371                 return -1;
5372
5373         }
5374
5375         return tot_bytes;
5376
5377 }
5378
5379 /*
5380  * Routine to list print jobs on a printer share ...
5381  */
5382
5383 static int smbc_list_print_jobs_ctx(SMBCCTX *context, const char *fname, smbc_list_print_job_fn fn)
5384 {
5385         SMBCSRV *srv;
5386         fstring server, share, user, password, workgroup;
5387         pstring path;
5388
5389         if (!context || !context->internal ||
5390             !context->internal->_initialized) {
5391
5392                 errno = EINVAL;
5393                 return -1;
5394
5395         }
5396
5397         if (!fname) {
5398                 
5399                 errno = EINVAL;
5400                 return -1;
5401
5402         }
5403   
5404         DEBUG(4, ("smbc_list_print_jobs(%s)\n", fname));
5405
5406         if (smbc_parse_path(context, fname,
5407                             server, sizeof(server),
5408                             share, sizeof(share),
5409                             path, sizeof(path),
5410                             user, sizeof(user),
5411                             password, sizeof(password),
5412                             NULL, 0)) {
5413                 errno = EINVAL;
5414                 return -1;
5415         }
5416
5417         if (user[0] == (char)0) fstrcpy(user, context->user);
5418         
5419         fstrcpy(workgroup, context->workgroup);
5420
5421         srv = smbc_server(context, server, share, workgroup, user, password);
5422
5423         if (!srv) {
5424
5425                 return -1;  /* errno set by smbc_server */
5426
5427         }
5428
5429         if (cli_print_queue(&srv->cli, (void (*)(struct print_job_info *))fn) < 0) {
5430
5431                 errno = smbc_errno(context, &srv->cli);
5432                 return -1;
5433
5434         }
5435         
5436         return 0;
5437
5438 }
5439
5440 /*
5441  * Delete a print job from a remote printer share
5442  */
5443
5444 static int smbc_unlink_print_job_ctx(SMBCCTX *context, const char *fname, int id)
5445 {
5446         SMBCSRV *srv;
5447         fstring server, share, user, password, workgroup;
5448         pstring path;
5449         int err;
5450
5451         if (!context || !context->internal ||
5452             !context->internal->_initialized) {
5453
5454                 errno = EINVAL;
5455                 return -1;
5456
5457         }
5458
5459         if (!fname) {
5460
5461                 errno = EINVAL;
5462                 return -1;
5463
5464         }
5465   
5466         DEBUG(4, ("smbc_unlink_print_job(%s)\n", fname));
5467
5468         if (smbc_parse_path(context, fname,
5469                             server, sizeof(server),
5470                             share, sizeof(share),
5471                             path, sizeof(path),
5472                             user, sizeof(user),
5473                             password, sizeof(password),
5474                             NULL, 0)) {
5475                 errno = EINVAL;
5476                 return -1;
5477         }
5478
5479         if (user[0] == (char)0) fstrcpy(user, context->user);
5480
5481         fstrcpy(workgroup, context->workgroup);
5482
5483         srv = smbc_server(context, server, share, workgroup, user, password);
5484
5485         if (!srv) {
5486
5487                 return -1;  /* errno set by smbc_server */
5488
5489         }
5490
5491         if ((err = cli_printjob_del(&srv->cli, id)) != 0) {
5492
5493                 if (err < 0)
5494                         errno = smbc_errno(context, &srv->cli);
5495                 else if (err == ERRnosuchprintjob)
5496                         errno = EINVAL;
5497                 return -1;
5498
5499         }
5500
5501         return 0;
5502
5503 }
5504
5505 /*
5506  * Get a new empty handle to fill in with your own info 
5507  */
5508 SMBCCTX * smbc_new_context(void)
5509 {
5510         SMBCCTX * context;
5511
5512         context = SMB_MALLOC_P(SMBCCTX);
5513         if (!context) {
5514                 errno = ENOMEM;
5515                 return NULL;
5516         }
5517
5518         ZERO_STRUCTP(context);
5519
5520         context->internal = SMB_MALLOC_P(struct smbc_internal_data);
5521         if (!context->internal) {
5522                 errno = ENOMEM;
5523                 return NULL;
5524         }
5525
5526         ZERO_STRUCTP(context->internal);
5527
5528         
5529         /* ADD REASONABLE DEFAULTS */
5530         context->debug            = 0;
5531         context->timeout          = 20000; /* 20 seconds */
5532
5533         context->options.browse_max_lmb_count      = 3;    /* # LMBs to query */
5534         context->options.urlencode_readdir_entries = False;/* backward compat */
5535         context->options.one_share_per_server      = False;/* backward compat */
5536
5537         context->open                              = smbc_open_ctx;
5538         context->creat                             = smbc_creat_ctx;
5539         context->read                              = smbc_read_ctx;
5540         context->write                             = smbc_write_ctx;
5541         context->close_fn                          = smbc_close_ctx;
5542         context->unlink                            = smbc_unlink_ctx;
5543         context->rename                            = smbc_rename_ctx;
5544         context->lseek                             = smbc_lseek_ctx;
5545         context->stat                              = smbc_stat_ctx;
5546         context->fstat                             = smbc_fstat_ctx;
5547         context->opendir                           = smbc_opendir_ctx;
5548         context->closedir                          = smbc_closedir_ctx;
5549         context->readdir                           = smbc_readdir_ctx;
5550         context->getdents                          = smbc_getdents_ctx;
5551         context->mkdir                             = smbc_mkdir_ctx;
5552         context->rmdir                             = smbc_rmdir_ctx;
5553         context->telldir                           = smbc_telldir_ctx;
5554         context->lseekdir                          = smbc_lseekdir_ctx;
5555         context->fstatdir                          = smbc_fstatdir_ctx;
5556         context->chmod                             = smbc_chmod_ctx;
5557         context->utimes                            = smbc_utimes_ctx;
5558         context->setxattr                          = smbc_setxattr_ctx;
5559         context->getxattr                          = smbc_getxattr_ctx;
5560         context->removexattr                       = smbc_removexattr_ctx;
5561         context->listxattr                         = smbc_listxattr_ctx;
5562         context->open_print_job                    = smbc_open_print_job_ctx;
5563         context->print_file                        = smbc_print_file_ctx;
5564         context->list_print_jobs                   = smbc_list_print_jobs_ctx;
5565         context->unlink_print_job                  = smbc_unlink_print_job_ctx;
5566
5567         context->callbacks.check_server_fn         = smbc_check_server;
5568         context->callbacks.remove_unused_server_fn = smbc_remove_unused_server;
5569
5570         smbc_default_cache_functions(context);
5571
5572         return context;
5573 }
5574
5575 /* 
5576  * Free a context
5577  *
5578  * Returns 0 on success. Otherwise returns 1, the SMBCCTX is _not_ freed 
5579  * and thus you'll be leaking memory if not handled properly.
5580  *
5581  */
5582 int smbc_free_context(SMBCCTX * context, int shutdown_ctx)
5583 {
5584         if (!context) {
5585                 errno = EBADF;
5586                 return 1;
5587         }
5588         
5589         if (shutdown_ctx) {
5590                 SMBCFILE * f;
5591                 DEBUG(1,("Performing aggressive shutdown.\n"));
5592                 
5593                 f = context->internal->_files;
5594                 while (f) {
5595                         context->close_fn(context, f);
5596                         f = f->next;
5597                 }
5598                 context->internal->_files = NULL;
5599
5600                 /* First try to remove the servers the nice way. */
5601                 if (context->callbacks.purge_cached_fn(context)) {
5602                         SMBCSRV * s;
5603                         SMBCSRV * next;
5604                         DEBUG(1, ("Could not purge all servers, Nice way shutdown failed.\n"));
5605                         s = context->internal->_servers;
5606                         while (s) {
5607                                 DEBUG(1, ("Forced shutdown: %p (fd=%d)\n", s, s->cli.fd));
5608                                 cli_shutdown(&s->cli);
5609                                 context->callbacks.remove_cached_srv_fn(context, s);
5610                                 next = s->next;
5611                                 DLIST_REMOVE(context->internal->_servers, s);
5612                                 SAFE_FREE(s);
5613                                 s = next;
5614                         }
5615                         context->internal->_servers = NULL;
5616                 }
5617         }
5618         else {
5619                 /* This is the polite way */    
5620                 if (context->callbacks.purge_cached_fn(context)) {
5621                         DEBUG(1, ("Could not purge all servers, free_context failed.\n"));
5622                         errno = EBUSY;
5623                         return 1;
5624                 }
5625                 if (context->internal->_servers) {
5626                         DEBUG(1, ("Active servers in context, free_context failed.\n"));
5627                         errno = EBUSY;
5628                         return 1;
5629                 }
5630                 if (context->internal->_files) {
5631                         DEBUG(1, ("Active files in context, free_context failed.\n"));
5632                         errno = EBUSY;
5633                         return 1;
5634                 }               
5635         }
5636
5637         /* Things we have to clean up */
5638         SAFE_FREE(context->workgroup);
5639         SAFE_FREE(context->netbios_name);
5640         SAFE_FREE(context->user);
5641         
5642         DEBUG(3, ("Context %p succesfully freed\n", context));
5643         SAFE_FREE(context->internal);
5644         SAFE_FREE(context);
5645         return 0;
5646 }
5647
5648
5649 /*
5650  * Initialise the library etc 
5651  *
5652  * We accept a struct containing handle information.
5653  * valid values for info->debug from 0 to 100,
5654  * and insist that info->fn must be non-null.
5655  */
5656 SMBCCTX * smbc_init_context(SMBCCTX * context)
5657 {
5658         pstring conf;
5659         int pid;
5660         char *user = NULL, *home = NULL;
5661
5662         if (!context || !context->internal) {
5663                 errno = EBADF;
5664                 return NULL;
5665         }
5666
5667         /* Do not initialise the same client twice */
5668         if (context->internal->_initialized) { 
5669                 return 0;
5670         }
5671
5672         if (!context->callbacks.auth_fn || context->debug < 0 || context->debug > 100) {
5673
5674                 errno = EINVAL;
5675                 return NULL;
5676
5677         }
5678
5679         if (!smbc_initialized) {
5680                 /* Do some library wide intialisations the first time we get called */
5681                 BOOL conf_loaded = False;
5682
5683                 /* Set this to what the user wants */
5684                 DEBUGLEVEL = context->debug;
5685                 
5686                 setup_logging( "libsmbclient", True);
5687
5688                 /* Here we would open the smb.conf file if needed ... */
5689                 
5690                 in_client = True; /* FIXME, make a param */
5691
5692                 home = getenv("HOME");
5693                 if (home) {
5694                         slprintf(conf, sizeof(conf), "%s/.smb/smb.conf", home);
5695                         if (lp_load(conf, True, False, False)) {
5696                                 conf_loaded = True;
5697                         } else {
5698                                 DEBUG(5, ("Could not load config file: %s\n",
5699                                           conf));
5700                         }
5701                 }
5702  
5703                 if (!conf_loaded) {
5704                         /*
5705                          * Well, if that failed, try the dyn_CONFIGFILE
5706                          * Which points to the standard locn, and if that
5707                          * fails, silently ignore it and use the internal
5708                          * defaults ...
5709                          */
5710
5711                         if (!lp_load(dyn_CONFIGFILE, True, False, False)) {
5712                                 DEBUG(5, ("Could not load config file: %s\n",
5713                                           dyn_CONFIGFILE));
5714                         } else if (home) {
5715                                 /*
5716                                  * We loaded the global config file.  Now lets
5717                                  * load user-specific modifications to the
5718                                  * global config.
5719                                  */
5720                                 slprintf(conf, sizeof(conf),
5721                                          "%s/.smb/smb.conf.append", home);
5722                                 if (!lp_load(conf, True, False, False)) {
5723                                         DEBUG(10,
5724                                               ("Could not append config file: "
5725                                                "%s\n",
5726                                                conf));
5727                                 }
5728                         }
5729                 }
5730
5731                 load_interfaces();  /* Load the list of interfaces ... */
5732                 
5733                 reopen_logs();  /* Get logging working ... */
5734         
5735                 /* 
5736                  * Block SIGPIPE (from lib/util_sock.c: write())  
5737                  * It is not needed and should not stop execution 
5738                  */
5739                 BlockSignals(True, SIGPIPE);
5740                 
5741                 /* Done with one-time initialisation */
5742                 smbc_initialized = 1; 
5743
5744         }
5745         
5746         if (!context->user) {
5747                 /*
5748                  * FIXME: Is this the best way to get the user info? 
5749                  */
5750                 user = getenv("USER");
5751                 /* walk around as "guest" if no username can be found */
5752                 if (!user) context->user = SMB_STRDUP("guest");
5753                 else context->user = SMB_STRDUP(user);
5754         }
5755
5756         if (!context->netbios_name) {
5757                 /*
5758                  * We try to get our netbios name from the config. If that fails we fall
5759                  * back on constructing our netbios name from our hostname etc
5760                  */
5761                 if (global_myname()) {
5762                         context->netbios_name = SMB_STRDUP(global_myname());
5763                 }
5764                 else {
5765                         /*
5766                          * Hmmm, I want to get hostname as well, but I am too lazy for the moment
5767                          */
5768                         pid = sys_getpid();
5769                         context->netbios_name = SMB_MALLOC(17);
5770                         if (!context->netbios_name) {
5771                                 errno = ENOMEM;
5772                                 return NULL;
5773                         }
5774                         slprintf(context->netbios_name, 16, "smbc%s%d", context->user, pid);
5775                 }
5776         }
5777
5778         DEBUG(1, ("Using netbios name %s.\n", context->netbios_name));
5779
5780         if (!context->workgroup) {
5781                 if (lp_workgroup()) {
5782                         context->workgroup = SMB_STRDUP(lp_workgroup());
5783                 }
5784                 else {
5785                         /* TODO: Think about a decent default workgroup */
5786                         context->workgroup = SMB_STRDUP("samba");
5787                 }
5788         }
5789
5790         DEBUG(1, ("Using workgroup %s.\n", context->workgroup));
5791                                         
5792         /* shortest timeout is 1 second */
5793         if (context->timeout > 0 && context->timeout < 1000) 
5794                 context->timeout = 1000;
5795
5796         /*
5797          * FIXME: Should we check the function pointers here? 
5798          */
5799
5800         context->internal->_initialized = 1;
5801         
5802         return context;
5803 }
5804
5805
5806 /* Return the verion of samba, and thus libsmbclient */
5807 const char *
5808 smbc_version(void)
5809 {
5810         return samba_version_string();
5811 }