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