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