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