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