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