Fixes to libsmbclient so it will work when browsing real Windows systems which
[ira/wip.git] / source3 / libsmb / libsmbclient.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 2.0
4    SMB client library implementation
5    Copyright (C) Andrew Tridgell 1998
6    Copyright (C) Richard Sharpe 2000
7    Copyright (C) John Terpstra 2000
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25 #include "libsmbclient.h"
26
27 /* Structure for servers ... Held here so we don't need an include ...
28  * May be better to put in an include file
29  */
30
31 struct smbc_server {
32   struct smbc_server *next, *prev;
33   struct cli_state cli;
34   dev_t dev;
35   char *server_name;
36   char *share_name;
37   char *workgroup;
38   char *username;
39   BOOL no_pathinfo2;
40 };
41
42 /* Keep directory entries in a list */
43 struct smbc_dir_list {
44   struct smbc_dir_list *next;
45   struct smbc_dirent *dirent;
46 };
47
48 struct smbc_file {
49   int cli_fd; 
50   int smbc_fd;
51   char *fname;
52   off_t offset;
53   struct smbc_server *srv;
54   BOOL file;
55   struct smbc_dir_list *dir_list, *dir_end, *dir_next;
56   int dir_type, dir_error;
57 };
58
59 extern BOOL in_client;
60 static int smbc_initialized = 0;
61 static smbc_get_auth_data_fn smbc_auth_fn = NULL;
62 static int smbc_debug;
63 static int smbc_start_fd;
64 static int smbc_max_fd = 10000;
65 static struct smbc_file **smbc_file_table;
66 static struct smbc_server *smbc_srvs;
67 static pstring  my_netbios_name;
68 static pstring smbc_user;
69
70 /*
71  * Clean up a filename by removing redundent stuff 
72  */
73
74 static void
75 smbc_clean_fname(char *name)
76 {
77   char *p, *p2;
78   int l;
79   int modified = 1;
80
81   if (!name) return;
82   
83   while (modified) {
84     modified = 0;
85
86     DEBUG(5,("cleaning %s\n", name));
87
88     if ((p=strstr(name,"/./"))) {
89       modified = 1;
90       while (*p) {
91         p[0] = p[2];
92         p++;
93       }
94     }
95
96     if ((p=strstr(name,"//"))) {
97       modified = 1;
98       while (*p) {
99         p[0] = p[1];
100         p++;
101       }
102     }
103
104     if (strcmp(name,"/../")==0) {
105       modified = 1;
106       name[1] = 0;
107     }
108     
109     if ((p=strstr(name,"/../"))) {
110       modified = 1;
111       for (p2=(p>name?p-1:p);p2>name;p2--) {
112         if (p2[0] == '/') break;
113       }
114       while (*p2) {
115         p2[0] = p2[3];
116         p2++;
117       }
118     }
119
120     if (strcmp(name,"/..")==0) {
121       modified = 1;
122       name[1] = 0;
123     }
124
125     l = strlen(name);
126     p = l>=3?(name+l-3):name;
127     if (strcmp(p,"/..")==0) {
128       modified = 1;
129       for (p2=p-1;p2>name;p2--) {
130         if (p2[0] == '/') break;
131       }
132       if (p2==name) {
133         p[0] = '/';
134         p[1] = 0;
135       } else {
136         p2[0] = 0;
137       }
138     }
139     
140     l = strlen(name);
141     p = l>=2?(name+l-2):name;
142     if (strcmp(p,"/.")==0) {
143       if (p == name) {
144         p[1] = 0;
145       } else {
146         p[0] = 0;
147       }
148     }
149     
150     if (strncmp(p=name,"./",2) == 0) {      
151       modified = 1;
152       do {
153         p[0] = p[2];
154       } while (*p++);
155     }
156     
157     l = strlen(p=name);
158     if (l > 1 && p[l-1] == '/') {
159       modified = 1;
160       p[l-1] = 0;
161     }
162   }
163 }
164
165 /*
166  * Function to parse a path and turn it into components
167  *
168  * We accept smb://[[[domain;]user[:password@]]server[/share[/path[/file]]]]
169  * 
170  * smb://       means show all the workgroups
171  * smb://name/  means, if name<1D> exists, list servers in workgroup,
172  *              else, if name<20> exists, list all shares for server ...
173  */
174
175 static const char *smbc_prefix = "smb:";
176
177 static int
178 smbc_parse_path(const char *fname, char *server, char *share, char *path,
179                 char *user, char *password) /* FIXME, lengths of strings */
180 {
181   static pstring s;
182   pstring userinfo;
183   char *p;
184   int len;
185   fstring workgroup;
186
187   server[0] = share[0] = path[0] = user[0] = password[0] = (char)0;
188   pstrcpy(s, fname);
189
190   /*  clean_fname(s);  causing problems ... */
191
192   /* see if it has the right prefix */
193   len = strlen(smbc_prefix);
194   if (strncmp(s,smbc_prefix,len) || 
195       (s[len] != '/' && s[len] != 0)) return -1; /* What about no smb: ? */
196
197   p = s + len;
198
199   /* Watch the test below, we are testing to see if we should exit */
200
201   if (strncmp(p, "//", 2) && strncmp(p, "\\\\", 2)) {
202
203     return -1;
204
205   }
206
207   p += 2;  /* Skip the // or \\  */
208
209   if (*p == (char)0)
210     return 0;
211
212   if (*p == '/') {
213
214     strncpy(server, lp_workgroup(), 16); /* FIXME: Danger here */
215     return 0;
216
217   }
218
219   /*
220    * ok, its for us. Now parse out the server, share etc. 
221    *
222    * However, we want to parse out [[domain;]user[:password]@] if it
223    * exists ...
224    */
225
226   if (strchr(p, '@')) { 
227     pstring username, passwd, domain;
228     char *u = userinfo;
229
230     next_token(&p, userinfo, "@", sizeof(fstring));
231
232     username[0] = passwd[0] = domain[0] = 0;
233
234     if (strchr(u, ';')) {
235       
236       next_token(&u, domain, ";", sizeof(fstring));
237
238     }
239
240     if (strchr(u, ':')) {
241
242       next_token(&u, username, ":", sizeof(fstring));
243
244       pstrcpy(passwd, u);
245
246     }
247     else {
248
249       pstrcpy(username, u);
250
251     }
252
253     if (username[0])
254       strncpy(user, username, sizeof(fstring));  /* FIXME, size and domain */
255
256     if (passwd[0])
257       strncpy(password, passwd, sizeof(fstring)); /* FIXME, size */
258
259   }
260
261   if (!next_token(&p, server, "/", sizeof(fstring))) {
262
263     return -1;
264
265   }
266
267   if (*p == (char)0) return 0;  /* That's it ... */
268   
269   if (!next_token(&p, share, "/", sizeof(fstring))) {
270
271     return -1;
272
273   }
274
275   pstrcpy(path, p);
276   
277   all_string_sub(path, "/", "\\", 0);
278
279   return 0;
280 }
281
282 /*
283  * Convert an SMB error into a UNIX error ...
284  */
285
286 int smbc_errno(struct cli_state *c)
287 {
288         uint8 eclass;
289         uint32 ecode;
290         int ret;
291
292         ret = cli_error(c, &eclass, &ecode, NULL);
293
294         if (ret) {
295                 DEBUG(3,("smbc_error %d %d (0x%x) -> %d\n", 
296                          (int)eclass, (int)ecode, (int)ecode, ret));
297         }
298         return ret;
299 }
300
301 /*
302  * Connect to a server, possibly on an existing connection
303  *
304  * Here, what we want to do is: If the server and username
305  * match an existing connection, reuse that, otherwise, establish a 
306  * new connection.
307  *
308  * If we have to create a new connection, call the auth_fn to get the
309  * info we need, unless the username and password were passed in.
310  */
311
312 struct smbc_server *smbc_server(char *server, char *share, 
313                                 char *workgroup, char *username, 
314                                 char *password)
315 {
316   struct smbc_server *srv=NULL;
317   struct cli_state c;
318   struct nmb_name called, calling;
319   char *p, *server_n = server;
320   fstring group;
321   pstring ipenv;
322   struct in_addr ip;
323   extern struct in_addr ipzero;
324   
325   ip = ipzero;
326   ZERO_STRUCT(c);
327
328   /* try to use an existing connection */
329   for (srv=smbc_srvs;srv;srv=srv->next) {
330     if (strcmp(server,srv->server_name)==0 &&
331         strcmp(share,srv->share_name)==0 &&
332         strcmp(workgroup,srv->workgroup)==0 &&
333         strcmp(username, srv->username) == 0) 
334       return srv;
335   }
336
337   if (server[0] == 0) {
338     errno = EPERM;
339     return NULL;
340   }
341
342   /* 
343    * Pick up the auth info here, once we know we need to connect
344    * But only if we do not have a username and password ...
345    */
346
347   if (!username[0] || !password[0])
348     smbc_auth_fn(server, share, workgroup, sizeof(fstring),
349                  username, sizeof(fstring), password, sizeof(fstring));
350
351   /* 
352    * However, smbc_auth_fn may have picked up info relating to an 
353    * existing connection, so try for an existing connection again ...
354    */
355
356   for (srv=smbc_srvs;srv;srv=srv->next) {
357     if (strcmp(server,srv->server_name)==0 &&
358         strcmp(share,srv->share_name)==0 &&
359         strcmp(workgroup,srv->workgroup)==0 &&
360         strcmp(username, srv->username) == 0) 
361       return srv;
362   }
363
364   make_nmb_name(&calling, my_netbios_name, 0x0);
365   make_nmb_name(&called , server, 0x20);
366
367   DEBUG(4,("smbc_server: server_n=[%s] server=[%s]\n", server_n, server));
368   
369   if ((p=strchr(server_n,'#')) && 
370       (strcmp(p+1,"1D")==0 || strcmp(p+1,"01")==0)) {
371     struct in_addr sip;
372     pstring s;
373     
374     fstrcpy(group, server_n);
375     p = strchr(group,'#');
376     *p = 0;
377                 
378   }
379
380   DEBUG(4,(" -> server_n=[%s] server=[%s]\n", server_n, server));
381
382  again:
383   slprintf(ipenv,sizeof(ipenv)-1,"HOST_%s", server_n);
384
385   ip = ipzero;
386
387   /* have to open a new connection */
388   if (!cli_initialise(&c) || !cli_connect(&c, server_n, &ip)) {
389     errno = ENOENT;
390     return NULL;
391   }
392
393   if (!cli_session_request(&c, &calling, &called)) {
394     cli_shutdown(&c);
395     if (strcmp(called.name, "*SMBSERVER")) {
396       make_nmb_name(&called , "*SMBSERVER", 0x20);
397       goto again;
398     }
399     errno = ENOENT;
400     return NULL;
401   }
402   
403   DEBUG(4,(" session request ok\n"));
404   
405   if (!cli_negprot(&c)) {
406     cli_shutdown(&c);
407     errno = ENOENT;
408     return NULL;
409   }
410
411   if (!cli_session_setup(&c, username, 
412                          password, strlen(password),
413                          password, strlen(password),
414                          workgroup) &&
415       /* try an anonymous login if it failed */
416       !cli_session_setup(&c, "", "", 1,"", 0, workgroup)) {
417     cli_shutdown(&c);
418     errno = EPERM;
419     return NULL;
420   }
421
422   DEBUG(4,(" session setup ok\n"));
423
424   if (!cli_send_tconX(&c, share, "?????",
425                       password, strlen(password)+1)) {
426     errno = smbc_errno(&c);
427     cli_shutdown(&c);
428     return NULL;
429   }
430   
431   DEBUG(4,(" tconx ok\n"));
432   
433   srv = (struct smbc_server *)malloc(sizeof(*srv));
434   if (!srv) {
435     errno = ENOMEM;
436     goto failed;
437   }
438
439   ZERO_STRUCTP(srv);
440
441   srv->cli = c;
442
443   srv->dev = (dev_t)(str_checksum(server) ^ str_checksum(share));
444
445   srv->server_name = strdup(server);
446   if (!srv->server_name) {
447     errno = ENOMEM;
448     goto failed;
449   }
450
451   srv->share_name = strdup(share);
452   if (!srv->share_name) {
453     errno = ENOMEM;
454     goto failed;
455   }
456
457   srv->workgroup = strdup(workgroup);
458   if (!srv->workgroup) {
459     errno = ENOMEM;
460     goto failed;
461   }
462
463   srv->username = strdup(username);
464   if (!srv->username) {
465     errno = ENOMEM;
466     goto failed;
467   }
468
469   DLIST_ADD(smbc_srvs, srv);
470
471   return srv;
472
473  failed:
474   cli_shutdown(&c);
475   if (!srv) return NULL;
476   
477   if (srv->server_name) free(srv->server_name);
478   if (srv->share_name) free(srv->share_name);
479   free(srv);
480   return NULL;
481 }
482
483 /*
484  *Initialise the library etc 
485  *
486  * We accept valiv values for debug from 0 to 100,
487  * and insist that fn must be non-null.
488  */
489
490 int smbc_init(smbc_get_auth_data_fn fn, int debug)
491 {
492   pstring conf;
493   int p, pid;
494   char *user = NULL, *host = NULL, *home = NULL, *pname="libsmbclient";
495
496   /*
497    * Next lot ifdef'd out until test suite fixed ...
498    */
499
500   if (!fn || debug < 0 || debug > 100) {
501
502     errno = EINVAL;
503     return -1;
504
505   }
506
507   smbc_initialized = 1;
508   smbc_auth_fn = fn;
509   smbc_debug = debug;
510
511   DEBUGLEVEL = -1;
512
513   setup_logging(pname, False);
514
515   /*
516    * We try to construct our netbios name from our hostname etc
517    */
518
519   user = getenv("USER");
520   if (!user) user = "";  /* FIXME: What to do about this? */
521
522   /*
523    * FIXME: Is this the best way to get the user info? */
524
525   pstrcpy(smbc_user, user); /* Save for use elsewhere */
526
527   pid = getpid();
528
529   /*
530    * Hmmm, I want to get hostname as well, but I am too lazy for the moment
531    */
532
533   slprintf(my_netbios_name, 16, "smbc%s%d", user, pid);
534
535   charset_initialise();
536
537   /* Here we would open the smb.conf file if needed ... */
538
539   home = getenv("HOME");
540
541   slprintf(conf, sizeof(conf), "%s/.smb/smb.conf", home);
542
543   load_interfaces();  /* Load the list of interfaces ... */
544
545   in_client = True; /* FIXME, make a param */
546
547   if (!lp_load(conf, True, False, False)) {
548
549     /*
550      * Hmmm, what the hell do we do here ... we could not parse the
551      * config file ... We must return an error ... and keep info around
552      * about why we failed
553      */
554     /*
555     errno = ENOENT; /* Hmmm, what error resp does lp_load return ? */
556     return -1;
557
558   }
559
560   codepage_initialise(lp_client_code_page()); /* Get a codepage */
561
562   reopen_logs();  /* Get logging working ... */
563
564   name_register_wins(my_netbios_name, 0);
565
566   /* 
567    * Now initialize the file descriptor array and figure out what the
568    * max open files is, so we can return FD's that are above the max
569    * open file, and separated by a guard band
570    */
571
572 #if (defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE))
573   do {
574     struct rlimit rlp;
575
576     if (getrlimit(RLIMIT_NOFILE, &rlp)) {
577
578       DEBUG(0, ("smbc_init: getrlimit(1) for RLIMIT_NOFILE failed with error %s\n", strerror(errno)));
579
580       smbc_start_fd = 1000000;
581       smbc_max_fd = 10000;     /* FIXME, should be a define ... */
582
583     }
584     else {
585       
586       smbc_start_fd = rlp.rlim_max + 10000; /* Leave a guard space of 10,000 */
587       smbc_max_fd = 10000;
588
589     }
590   } while ( 0 );
591 #else /* !defined(HAVE_GETRLIMIT) || !defined(RLIMIT_NOFILE) */
592
593   smbc_start_fd = 1000000;
594   smbc_max_fd = 10000;     /* FIXME, should be a define ... */
595
596 #endif
597
598   smbc_file_table = malloc(smbc_max_fd * sizeof(struct smbc_file *));
599
600   for (p = 0; p < smbc_max_fd; p++)
601     smbc_file_table[p] = NULL;
602
603   if (!smbc_file_table)
604     return ENOMEM;
605
606   return 0;  /* Success */
607
608 }
609
610 /*
611  * Routine to open() a file ...
612  */
613
614 int smbc_open(const char *fname, int flags, mode_t mode)
615 {
616   fstring server, share, user, password;
617   pstring path;
618   struct smbc_server *srv = NULL;
619   struct smbc_file *file = NULL;
620   int fd;
621
622   if (!smbc_initialized) {
623
624     errno = EUCLEAN;  /* Best I can think of ... */
625     return -1;
626
627   }
628
629   if (!fname) {
630
631     errno = EINVAL;
632     return -1;
633
634   }
635
636   smbc_parse_path(fname, server, share, path, user, password); /* FIXME, check errors */
637
638   if (user[0] == (char)0) pstrcpy(user, smbc_user);
639
640   srv = smbc_server(server, share, lp_workgroup(), user, password);
641
642   if (!srv) {
643
644     if (errno == EPERM) errno = EACCES;
645     return -1;  /* smbc_server sets errno */
646
647   }
648
649   if (path[strlen(path) - 1] == '\\') {
650     
651     fd = -1;
652
653   }
654   else {
655
656     int slot = 0;
657
658     /* Find a free slot first */
659
660     while (smbc_file_table[slot])
661       slot++;
662
663     if (slot > smbc_max_fd) {
664
665       errno = ENOMEM; /* FIXME, is this best? */
666       return -1;
667
668     }
669
670     smbc_file_table[slot] = malloc(sizeof(struct smbc_file));
671
672     if (!smbc_file_table[slot]) {
673
674       errno = ENOMEM;
675       return -1;
676
677     }
678
679     if ((fd = cli_open(&srv->cli, path, flags, DENY_NONE)) < 0) {
680
681       /* Handle the error ... */
682
683       free(smbc_file_table[slot]);
684       smbc_file_table[slot] = NULL;
685       errno = smbc_errno(&srv->cli);
686       return -1;
687
688     }
689
690     /* Fill in file struct */
691
692     smbc_file_table[slot]->cli_fd  = fd;
693     smbc_file_table[slot]->smbc_fd = slot + smbc_start_fd;
694     smbc_file_table[slot]->fname   = strdup(fname);
695     smbc_file_table[slot]->srv     = srv;
696     smbc_file_table[slot]->offset  = 0;
697     smbc_file_table[slot]->file    = True;
698
699     return smbc_file_table[slot]->smbc_fd;
700
701   }
702
703   /* Check if opendir needed ... */
704
705   if (fd == -1) {
706     int eno = 0;
707
708     eno = smbc_errno(&srv->cli);
709     fd = smbc_opendir(fname);
710     if (fd < 0) errno = eno;
711     return fd;
712
713   }
714
715   return 1;  /* Success, with fd ... */
716
717  failed:
718
719   /*FIXME, clean up things ... */
720   return -1;
721
722 }
723
724 /*
725  * Routine to create a file 
726  */
727
728 static int creat_bits = O_WRONLY | O_CREAT | O_TRUNC; /* FIXME: Do we need this */
729
730 int smbc_creat(const char *path, mode_t mode)
731 {
732
733   if (!smbc_initialized) {
734
735     errno = EUCLEAN;
736     return -1;
737
738   }
739
740   return smbc_open(path, creat_bits, mode);
741 }
742
743 /*
744  * Routine to read() a file ...
745  */
746
747 ssize_t smbc_read(int fd, void *buf, size_t count)
748 {
749   struct smbc_file *fe;
750   int ret;
751
752   if (!smbc_initialized) {
753
754     errno = EUCLEAN;
755     return -1;
756
757   }
758
759   DEBUG(4, ("smbc_read(%d, %d)\n", fd, (int)count));
760
761   if (fd < smbc_start_fd || fd >= (smbc_start_fd + smbc_max_fd)) {
762
763     errno = EBADF;
764     return -1;
765
766   }
767
768   fe = smbc_file_table[fd - smbc_start_fd];
769
770   if (!fe->file) {
771
772     errno = EBADF;
773     return -1;
774
775   }
776
777   ret = cli_read(&fe->srv->cli, fe->cli_fd, buf, fe->offset, count);
778
779   if (ret < 0) {
780
781     errno = smbc_errno(&fe->srv->cli);
782     return -1;
783
784   }
785
786   fe->offset += ret;
787
788   DEBUG(4, ("  --> %d\n", ret));
789
790   return ret;  /* Success, ret bytes of data ... */
791
792 }
793
794 /*
795  * Routine to write() a file ...
796  */
797
798 ssize_t smbc_write(int fd, void *buf, size_t count)
799 {
800   int ret;
801   struct smbc_file *fe;
802
803   if (!smbc_initialized) {
804
805     errno = EUCLEAN;
806     return -1;
807
808   }
809
810   if (fd < smbc_start_fd || fd >= (smbc_start_fd + smbc_max_fd)) {
811
812     errno = EBADF;
813     return -1;
814     
815   }
816
817   fe = smbc_file_table[fd - smbc_start_fd];
818
819   ret = cli_write(&fe->srv->cli, fe->cli_fd, 0, buf, fe->offset, count);
820
821   if (ret < 0) {
822
823     errno = smbc_errno(&fe->srv->cli);
824     return -1;
825
826   }
827
828   fe->offset += ret;
829
830   return ret;  /* Success, 0 bytes of data ... */
831 }
832  
833 /*
834  * Routine to close() a file ...
835  */
836
837 int smbc_close(int fd)
838 {
839   struct smbc_file *fe;
840
841   if (!smbc_initialized) {
842
843     errno = EUCLEAN;
844     return -1;
845
846   }
847
848   if (fd < smbc_start_fd || fd >= (smbc_start_fd + smbc_max_fd)) {
849    
850     errno = EBADF;
851     return -1;
852
853   }
854
855   fe = smbc_file_table[fd - smbc_start_fd];
856
857   if (!fe->file) {
858
859     return smbc_closedir(fd);
860
861   }
862
863   if (!cli_close(&fe->srv->cli, fe->cli_fd)) {
864
865     errno = smbc_errno(&fe->srv->cli);  /* FIXME, should we deallocate slot? */
866     return -1;
867
868   }
869
870   free(fe);
871   smbc_file_table[fd - smbc_start_fd] = NULL;
872
873   return 0;
874 }
875
876 /*
877  * Routine to unlink() a file
878  */
879
880 int smbc_unlink(const char *fname)
881 {
882   fstring server, share, user, password;
883   pstring path;
884   struct smbc_server *srv = NULL;
885
886   if (!smbc_initialized) {
887
888     errno = EUCLEAN;  /* Best I can think of ... */
889     return -1;
890
891   }
892
893   if (!fname) {
894
895     errno = EINVAL;
896     return -1;
897
898   }
899
900   smbc_parse_path(fname, server, share, path, user, password); /* FIXME, check errors */
901
902   if (user[0] == (char)0) pstrcpy(user, smbc_user);
903
904   srv = smbc_server(server, share, lp_workgroup(), user, password);
905
906   if (!srv) {
907
908     return -1;  /* smbc_server sets errno */
909
910   }
911
912   /*  if (strncmp(srv->cli.dev, "LPT", 3) == 0) {
913
914     int job = smbc_stat_printjob(srv, path, NULL, NULL);
915     if (job == -1) {
916
917       return -1;
918
919     }
920     if (cli_printjob_del(&srv->cli, job) != 0) {
921
922       return -1;
923
924     }
925     } else */
926
927   if (!cli_unlink(&srv->cli, path)) {
928
929     errno = smbc_errno(&srv->cli);
930
931     if (errno == EACCES) { /* Check if the file is a directory */
932
933       int err, saverr = errno;
934       struct stat st;
935       size_t size = 0;
936       uint16 mode = 0;
937       time_t m_time = 0, a_time = 0, c_time = 0;
938       SMB_INO_T ino = 0;
939
940       if (!smbc_getatr(srv, path, &mode, &size,
941                        &c_time, &a_time, &m_time, &ino)) {
942
943         /* Hmmm, bad error ... What? */
944
945         errno = smbc_errno(&srv->cli);
946         return -1;
947
948       }
949       else {
950
951         if (IS_DOS_DIR(mode))
952           errno = EISDIR;
953         else
954           errno = saverr;  /* Restore this */
955
956       }
957     }
958
959     return -1;
960
961   }
962
963   return 0;  /* Success ... */
964
965 }
966
967 /*
968  * Routine to rename() a file
969  */
970
971 int smbc_rename(const char *oname, const char *nname)
972 {
973   fstring server1, share1, server2, share2, user1, user2, password1, password2;
974   pstring path1, path2;
975   struct smbc_server *srv = NULL;
976
977   if (!smbc_initialized) {
978
979     errno = EUCLEAN;  /* Best I can think of ... */
980     return -1;
981
982   }
983
984   if (!oname || !nname) {
985
986     errno = EINVAL;
987     return -1;
988
989   }
990   
991   DEBUG(4, ("smbc_rename(%s,%s)\n", oname, nname));
992
993   smbc_parse_path(oname, server1, share1, path1, user1, password1);
994
995   if (user1[0] == (char)0) pstrcpy(user1, smbc_user);
996
997   smbc_parse_path(nname, server2, share2, path2, user2, password2);
998
999   if (user2[0] == (char)0) pstrcpy(user2, smbc_user);
1000
1001   if (strcmp(server1, server2) || strcmp(share1, share2) ||
1002       strcmp(user1, user2)) {
1003
1004     /* Can't rename across file systems, or users?? */
1005
1006     errno = EXDEV;
1007     return -1;
1008
1009   }
1010
1011   srv = smbc_server(server1, share1, lp_workgroup(), user1, password1);
1012   if (!srv) {
1013
1014     return -1;
1015
1016   }
1017
1018   if (!cli_rename(&srv->cli, path1, path2)) {
1019     int eno = smbc_errno(&srv->cli);
1020
1021     if (eno != EEXIST ||
1022         !cli_unlink(&srv->cli, path2) ||
1023         !cli_rename(&srv->cli, path1, path2)) {
1024
1025       errno = eno;
1026       return -1;
1027
1028     }
1029   }
1030
1031   return 0; /* Success */
1032
1033 }
1034
1035 /*
1036  * A routine to lseek() a file
1037  */
1038
1039 off_t smbc_lseek(int fd, off_t offset, int whence)
1040 {
1041   struct smbc_file *fe;
1042   size_t size;
1043
1044   if (!smbc_initialized) {
1045
1046     errno = EUCLEAN;
1047     return -1;
1048
1049   }
1050
1051   if (fd < smbc_start_fd || fd >= (smbc_start_fd + smbc_max_fd)) {
1052
1053     errno = EBADF;
1054     return -1;
1055
1056   }
1057
1058   fe = smbc_file_table[fd - smbc_start_fd];
1059
1060   if (!fe->file) {
1061
1062     return smbc_lseekdir(fd, offset, whence);
1063
1064   }
1065
1066   switch (whence) {
1067   case SEEK_SET:
1068     fe->offset = offset;
1069     break;
1070
1071   case SEEK_CUR:
1072     fe->offset += offset;
1073     break;
1074
1075   case SEEK_END:
1076     if (!cli_qfileinfo(&fe->srv->cli, fe->cli_fd, NULL, &size, NULL, NULL,
1077                        NULL, NULL, NULL) &&
1078         !cli_getattrE(&fe->srv->cli, fe->cli_fd, NULL, &size, NULL, NULL,
1079                       NULL)) {
1080
1081       errno = EINVAL;
1082       return -1;
1083     }
1084     fe->offset = size + offset;
1085     break;
1086
1087   default:
1088     errno = EINVAL;
1089     break;
1090
1091   }
1092
1093   return fe->offset;
1094
1095 }
1096
1097 /* 
1098  * Generate an inode number from file name for those things that need it
1099  */
1100
1101 static
1102 ino_t smbc_inode(const char *name)
1103 {
1104
1105   if (!*name) return 2; /* FIXME, why 2 ??? */
1106   return (ino_t)str_checksum(name);
1107
1108 }
1109
1110 /*
1111  * Routine to put basic stat info into a stat structure ... Used by stat and
1112  * fstat below.
1113  */
1114
1115 static
1116 int smbc_setup_stat(struct stat *st, char *fname, size_t size, int mode)
1117 {
1118
1119   st->st_mode = 0;
1120
1121   if (IS_DOS_DIR(mode)) {
1122     st->st_mode = SMBC_DIR_MODE;
1123   } else {
1124     st->st_mode = SMBC_FILE_MODE;
1125   }
1126
1127   if (IS_DOS_ARCHIVE(mode)) st->st_mode |= S_IXUSR;
1128   if (IS_DOS_SYSTEM(mode)) st->st_mode |= S_IXGRP;
1129   if (IS_DOS_HIDDEN(mode)) st->st_mode |= S_IXOTH;
1130   if (!IS_DOS_READONLY(mode)) st->st_mode |= S_IWUSR;
1131
1132   st->st_size = size;
1133   st->st_blksize = 512;
1134   st->st_blocks = (size+511)/512;
1135   st->st_uid = getuid();
1136   st->st_gid = getgid();
1137
1138   if (IS_DOS_DIR(mode)) {
1139     st->st_nlink = 2;
1140   } else {
1141     st->st_nlink = 1;
1142   }
1143
1144   if (st->st_ino == 0) {
1145     st->st_ino = smbc_inode(fname);
1146   }
1147 }
1148
1149 /*
1150  * Get info from an SMB server on a file. Use a qpathinfo call first
1151  * and if that fails, use getatr, as Win95 sometimes refuses qpathinfo
1152  */
1153
1154 BOOL smbc_getatr(struct smbc_server *srv, char *path, 
1155                  uint16 *mode, size_t *size, 
1156                  time_t *c_time, time_t *a_time, time_t *m_time,
1157                  SMB_INO_T *ino)
1158 {
1159
1160   if (!smbc_initialized) {
1161
1162     errno = EUCLEAN;
1163     return -1;
1164
1165   }
1166
1167   DEBUG(4,("smbc_getatr: sending qpathinfo\n"));
1168   
1169   if (!srv->no_pathinfo2 &&
1170       cli_qpathinfo2(&srv->cli, path, c_time, a_time, m_time, NULL,
1171                      size, mode, ino)) return True;
1172
1173   /* if this is NT then don't bother with the getatr */
1174   if (srv->cli.capabilities & CAP_NT_SMBS) return False;
1175
1176   if (cli_getatr(&srv->cli, path, mode, size, m_time)) {
1177     a_time = c_time = m_time;
1178     srv->no_pathinfo2 = True;
1179     return True;
1180   }
1181   return False;
1182 }
1183
1184 /*
1185  * Routine to stat a file given a name
1186  */
1187
1188 int smbc_stat(const char *fname, struct stat *st)
1189 {
1190   struct smbc_server *srv;
1191   fstring server, share, user, password;
1192   pstring path;
1193   time_t m_time = 0, a_time = 0, c_time = 0;
1194   size_t size = 0;
1195   uint16 mode = 0;
1196   SMB_INO_T ino = 0;
1197
1198   if (!smbc_initialized) {
1199
1200     errno = EUCLEAN;  /* Best I can think of ... */
1201     return -1;
1202
1203   }
1204
1205   if (!fname) {
1206
1207     errno = EINVAL;
1208     return -1;
1209
1210   }
1211   
1212   DEBUG(4, ("smbc_stat(%s)\n", fname));
1213
1214   smbc_parse_path(fname, server, share, path, user, password); /*FIXME, errors*/
1215
1216   if (user[0] == (char)0) pstrcpy(user, smbc_user);
1217
1218   srv = smbc_server(server, share, lp_workgroup(), user, password);
1219
1220   if (!srv) {
1221
1222     return -1;  /* errno set by smbc_server */
1223
1224   }
1225
1226   /* if (strncmp(srv->cli.dev, "IPC", 3) == 0) {
1227
1228      mode = aDIR | aRONLY;
1229
1230      }
1231      else if (strncmp(srv->cli.dev, "LPT", 3) == 0) {
1232
1233        if (strcmp(path, "\\") == 0) {
1234
1235           mode = aDIR | aRONLY;
1236
1237        }
1238        else {
1239
1240          mode = aRONLY;
1241          smbc_stat_printjob(srv, path, &size, &m_time);
1242          c_time = a_time = m_time;
1243
1244        }
1245        else { */
1246
1247   if (!smbc_getatr(srv, path, &mode, &size, 
1248                    &c_time, &a_time, &m_time, &ino)) {
1249
1250     errno = smbc_errno(&srv->cli);
1251     return -1;
1252
1253   }
1254
1255   /* } */
1256
1257   st->st_ino = ino;
1258
1259   smbc_setup_stat(st, path, size, mode);
1260
1261   st->st_atime = a_time;
1262   st->st_ctime = c_time;
1263   st->st_mtime = m_time;
1264   st->st_dev   = srv->dev;
1265
1266   return 0;
1267
1268 }
1269
1270 /*
1271  * Routine to stat a file given an fd
1272  */
1273
1274 int smbc_fstat(int fd, struct stat *st)
1275 {
1276   struct smbc_file *fe;
1277   time_t c_time, a_time, m_time;
1278   size_t size;
1279   uint16 mode;
1280   SMB_INO_T ino = 0;
1281
1282   if (!smbc_initialized) {
1283
1284     errno = EUCLEAN;
1285     return -1;
1286
1287   }
1288
1289   if (fd < smbc_start_fd || fd >= (smbc_start_fd + smbc_max_fd)) {
1290
1291     errno = EBADF;
1292     return -1;
1293
1294   }
1295
1296   fe = smbc_file_table[fd - smbc_start_fd];
1297
1298   if (!fe->file) {
1299
1300     return smbc_fstatdir(fd, st);
1301
1302   }
1303
1304   if (!cli_qfileinfo(&fe->srv->cli, fe->cli_fd,
1305                      &mode, &size, &c_time, &a_time, &m_time, NULL, &ino) &&
1306       !cli_getattrE(&fe->srv->cli, fe->cli_fd,
1307                     &mode, &size, &c_time, &a_time, &m_time)) {
1308
1309     errno = EINVAL;
1310     return -1;
1311
1312   }
1313
1314   st->st_ino = ino;
1315
1316   smbc_setup_stat(st, fe->fname, size, mode);
1317
1318   st->st_atime = a_time;
1319   st->st_ctime = c_time;
1320   st->st_mtime = m_time;
1321   st->st_dev = fe->srv->dev;
1322
1323   return 0;
1324
1325 }
1326
1327 /*
1328  * Routine to open a directory
1329  *
1330  * We want to allow:
1331  *
1332  * smb: which should list all the workgroups available
1333  * smb:workgroup
1334  * smb:workgroup//server
1335  * smb://server
1336  * smb://server/share
1337  */
1338
1339 static void smbc_remove_dir(struct smbc_file *dir)
1340 {
1341   struct smbc_dir_list *d,*f;
1342
1343   d = dir->dir_list;
1344   while (d) {
1345
1346     f = d; d = d->next;
1347
1348     if (f->dirent) free(f->dirent);
1349     free(f);
1350
1351   }
1352
1353   dir->dir_list = dir->dir_end = dir->dir_next = NULL;
1354
1355 }
1356
1357 static int add_dirent(struct smbc_file *dir, const char *name, const char *comment, uint32 type)
1358 {
1359   struct smbc_dirent *dirent;
1360   int size;
1361
1362   /*
1363    * Allocate space for the dirent, which must be increased by the 
1364    * size of the name and the comment and 1 for the null on the comment.
1365    * The null on the name is already accounted for.
1366    */
1367
1368   size = sizeof(struct smbc_dirent) + (name?strlen(name):0) +
1369     (comment?strlen(comment):0) + 1; 
1370     
1371   dirent = malloc(size);
1372
1373   if (!dirent) {
1374
1375     dir->dir_error = ENOMEM;
1376     return -1;
1377
1378   }
1379
1380   if (dir->dir_list == NULL) {
1381
1382     dir->dir_list = malloc(sizeof(struct smbc_dir_list));
1383     if (!dir->dir_list) {
1384
1385       free(dirent);
1386       dir->dir_error = ENOMEM;
1387       return -1;
1388
1389     }
1390
1391     dir->dir_end = dir->dir_next = dir->dir_list;
1392   
1393   }
1394   else {
1395
1396     dir->dir_end->next = malloc(sizeof(struct smbc_dir_list));
1397
1398     if (!dir->dir_end) {
1399
1400       free(dirent);
1401       dir->dir_error = ENOMEM;
1402       return -1;
1403
1404     }
1405
1406     dir->dir_end = dir->dir_end->next;
1407
1408   }
1409
1410   dir->dir_end->next = NULL;
1411   dir->dir_end->dirent = dirent;
1412
1413   dirent->smbc_type = type;
1414   dirent->namelen = (name?strlen(name):0);
1415   dirent->commentlen = (comment?strlen(comment):0);
1416   dirent->dirlen = size;
1417   
1418   strncpy(dirent->name, (name?name:""), dirent->namelen + 1);
1419
1420   dirent->comment = (char *)(&dirent->name + dirent->namelen + 1);
1421   strncpy(dirent->comment, (comment?comment:""), dirent->commentlen + 1);
1422
1423   return 0;
1424
1425 }
1426
1427 static void
1428 list_fn(const char *name, uint32 type, const char *comment, void *state)
1429 {
1430   struct smbc_file *dir = (struct smbc_file *)state;
1431   int dirent_type;
1432
1433   /* We need to process the type a little ... */
1434
1435   if (dir->dir_type == SMBC_FILE_SHARE) {
1436
1437     switch (type) {
1438     case 0: /* Directory tree */
1439       dirent_type = SMBC_FILE_SHARE;
1440       break;
1441
1442     case 1:
1443       dirent_type = SMBC_PRINTER_SHARE;
1444       break;
1445
1446     case 2:
1447       dirent_type = SMBC_COMMS_SHARE;
1448       break;
1449
1450     case 3:
1451       dirent_type = SMBC_IPC_SHARE;
1452       break;
1453
1454     default:
1455       dirent_type = SMBC_FILE_SHARE; /* FIXME, error? */
1456       break;
1457     }
1458
1459   }
1460   else dirent_type = dir->dir_type;
1461
1462   if (add_dirent(dir, name, comment, dirent_type) < 0) {
1463
1464     /* An error occurred, what do we do? */
1465
1466   }
1467
1468 }
1469
1470 static void
1471 dir_list_fn(file_info *finfo, const char *mask, void *state)
1472 {
1473
1474   fprintf(stderr, "Finfo->name=%s, mask=%s\n", finfo->name, mask);
1475   if (add_dirent((struct smbc_file *)state, finfo->name, "", 
1476                  (finfo->mode&aDIR?SMBC_DIR:SMBC_FILE)) < 0) {
1477
1478     /* Handle an error ... */
1479
1480   } 
1481
1482 }
1483
1484 int smbc_opendir(const char *fname)
1485 {
1486   struct in_addr addr;
1487   fstring server, share, user, password;
1488   pstring path;
1489   struct smbc_server *srv = NULL;
1490   struct in_addr rem_ip;
1491   int slot = 0;
1492   uint8 eclass;
1493   uint32 ecode;
1494
1495   if (!smbc_initialized) {
1496
1497     errno = EUCLEAN;
1498     return -1;
1499
1500   }
1501
1502   if (!fname) {
1503     
1504     errno = EINVAL;
1505     return -1;
1506
1507   }
1508
1509   if (smbc_parse_path(fname, server, share, path, user, password)) {
1510
1511     errno = EINVAL;
1512     return -1;
1513
1514   }
1515
1516   if (user[0] == (char)0) pstrcpy(user, smbc_user);
1517
1518   /* Get a file entry ... */
1519
1520   slot = 0;
1521
1522   while (smbc_file_table[slot])
1523     slot++;
1524
1525   if (slot > smbc_max_fd) {
1526
1527     errno = ENOMEM;
1528     return -1; /* FIXME, ... move into a func */
1529       
1530   }
1531
1532   smbc_file_table[slot] = malloc(sizeof(struct smbc_file));
1533
1534   if (!smbc_file_table[slot]) {
1535
1536     errno = ENOMEM;
1537     return -1;
1538
1539   }
1540
1541   smbc_file_table[slot]->cli_fd   = 0;
1542   smbc_file_table[slot]->smbc_fd  = slot + smbc_start_fd;
1543   smbc_file_table[slot]->fname    = strdup(fname);
1544   smbc_file_table[slot]->srv      = NULL;
1545   smbc_file_table[slot]->offset   = 0;
1546   smbc_file_table[slot]->file     = False;
1547   smbc_file_table[slot]->dir_list = 
1548     smbc_file_table[slot]->dir_next =
1549     smbc_file_table[slot]->dir_end = NULL;
1550
1551   if (server[0] == (char)0) {
1552
1553     if (share[0] != (char)0 || path[0] != (char)0) {
1554     
1555       errno = EINVAL;
1556       if (smbc_file_table[slot]) free(smbc_file_table[slot]);
1557       smbc_file_table[slot] = NULL;
1558       return -1;
1559
1560     }
1561
1562     /* We have server and share and path empty ... so list the workgroups */
1563
1564     /*    fprintf(stderr, "Workgroup is: %s\n", lp_workgroup()); */
1565     cli_get_backup_server(my_netbios_name, lp_workgroup(), server, sizeof(server));
1566
1567     smbc_file_table[slot]->dir_type = SMBC_WORKGROUP;
1568
1569   /*
1570    * Get a connection to IPC$ on the server if we do not already have one
1571    */
1572
1573     srv = smbc_server(server, "IPC$", lp_workgroup(), user, password);
1574
1575     if (!srv) {
1576
1577       if (smbc_file_table[slot]) free(smbc_file_table[slot]);
1578       smbc_file_table[slot] = NULL;
1579       return -1;
1580
1581     }
1582
1583     smbc_file_table[slot]->srv = srv;
1584
1585     /* Now, list the stuff ... */
1586
1587     if (!cli_NetServerEnum(&srv->cli, lp_workgroup(), 0x80000000, list_fn,
1588                            (void *)smbc_file_table[slot])) {
1589
1590       if (smbc_file_table[slot]) free(smbc_file_table[slot]);
1591       smbc_file_table[slot] = NULL;
1592       errno = cli_error(&srv->cli, &eclass, &ecode, NULL);
1593       return -1;
1594
1595     }
1596   }
1597   else { /* Server not an empty string ... Check the rest and see what gives */
1598
1599     if (share[0] == (char)0) {
1600
1601       if (path[0] != (char)0) { /* Should not have empty share with path */
1602
1603         errno = EINVAL;
1604         if (smbc_file_table[slot]) free(smbc_file_table[slot]);
1605         smbc_file_table[slot] = NULL;
1606         return -1;
1607         
1608       }
1609
1610       /* Check to see if <server><1D> translates, or <server><20> translates */
1611
1612       if (resolve_name(server, &rem_ip, 0x1d)) { /* Found LMB */
1613         pstring buserver;
1614
1615         smbc_file_table[slot]->dir_type = SMBC_SERVER;
1616
1617         /*
1618          * Get the backup list ...
1619          */
1620
1621         cli_get_backup_server(my_netbios_name, server, buserver, sizeof(buserver));
1622
1623         /*
1624          * Get a connection to IPC$ on the server if we do not already have one
1625          */
1626
1627         srv = smbc_server(buserver, "IPC$", lp_workgroup(), user, password);
1628
1629         if (!srv) {
1630
1631           if (smbc_file_table[slot]) free(smbc_file_table[slot]);
1632           smbc_file_table[slot] = NULL;  /* FIXME: Memory leaks ... */
1633           return -1;
1634
1635         }
1636
1637         smbc_file_table[slot]->srv = srv;
1638
1639         /* Now, list the servers ... */
1640
1641         if (!cli_NetServerEnum(&srv->cli, server, 0x0000FFFE, list_fn,
1642                                (void *)smbc_file_table[slot])) {
1643
1644           if (smbc_file_table[slot]) free(smbc_file_table[slot]);
1645           smbc_file_table[slot] = NULL;
1646           errno = cli_error(&srv->cli, &eclass, &ecode, NULL);
1647           return -1;
1648
1649         }
1650
1651       }
1652       else {
1653
1654         if (resolve_name(server, &rem_ip, 0x20)) {
1655
1656           /* Now, list the shares ... */
1657
1658           smbc_file_table[slot]->dir_type = SMBC_FILE_SHARE;
1659
1660           srv = smbc_server(server, "IPC$", lp_workgroup(), user, password);
1661
1662           if (!srv) {
1663
1664             if (smbc_file_table[slot]) free(smbc_file_table[slot]);
1665             smbc_file_table[slot] = NULL;
1666             return -1;
1667
1668           }
1669
1670           smbc_file_table[slot]->srv = srv;
1671
1672           /* Now, list the servers ... */
1673
1674           if (cli_RNetShareEnum(&srv->cli, list_fn, 
1675                                 (void *)smbc_file_table[slot]) < 0) {
1676
1677             errno = cli_error(&srv->cli, &eclass, &ecode, NULL);
1678             if (smbc_file_table[slot]) free(smbc_file_table[slot]);
1679             smbc_file_table[slot] = NULL;
1680             return -1;
1681
1682           }
1683
1684         }
1685         else {
1686
1687           errno = EINVAL;
1688           if (smbc_file_table[slot]) free(smbc_file_table[slot]);
1689           smbc_file_table[slot] = NULL;
1690           return -1;
1691
1692         }
1693
1694       }
1695
1696     }
1697     else { /* The server and share are specified ... work from there ... */
1698
1699       /* Well, we connect to the server and list the directory */
1700
1701       smbc_file_table[slot]->dir_type = SMBC_FILE_SHARE;
1702
1703       srv = smbc_server(server, share, lp_workgroup(), user, password);
1704
1705       if (!srv) {
1706
1707         if (smbc_file_table[slot]) free(smbc_file_table[slot]);
1708         smbc_file_table[slot] = NULL;
1709         return -1;
1710
1711       }
1712
1713       smbc_file_table[slot]->srv = srv;
1714
1715       /* Now, list the files ... */
1716
1717       pstrcat(path, "\\*");
1718
1719       if (!cli_list(&srv->cli, path, aDIR | aSYSTEM | aHIDDEN, dir_list_fn, 
1720                     (void *)smbc_file_table[slot])) {
1721
1722         if (smbc_file_table[slot]) free(smbc_file_table[slot]);
1723         smbc_file_table[slot] = NULL;
1724         errno = smbc_errno(&srv->cli);
1725         return -1;
1726
1727       }
1728     }
1729
1730   }
1731
1732   return smbc_file_table[slot]->smbc_fd;
1733
1734 }
1735
1736 /*
1737  * Routine to close a directory
1738  */
1739
1740 int smbc_closedir(int fd)
1741 {
1742   struct smbc_file *fe;
1743
1744   if (!smbc_initialized) {
1745
1746     errno = EUCLEAN;
1747     return -1;
1748
1749   }
1750
1751   if (fd < smbc_start_fd || fd >= (smbc_start_fd + smbc_max_fd)) {
1752
1753     errno = EBADF;
1754     return -1;
1755
1756   }
1757
1758   fe = smbc_file_table[fd - smbc_start_fd];
1759
1760   if (!fe) {
1761
1762     errno = ENOENT;  /* FIXME: Is this correct */
1763     return -1;
1764
1765   }
1766
1767   smbc_remove_dir(fe); /* Clean it up */
1768
1769   if (fe) free(fe);    /* Free the space too */
1770
1771   smbc_file_table[fd - smbc_start_fd] = NULL;
1772
1773   return 0;
1774
1775 }
1776
1777 /*
1778  * Routine to get a directory entry
1779  */
1780
1781 static char smbc_local_dirent[512];  /* Make big enough */
1782
1783 struct smbc_dirent *smbc_readdir(unsigned int fd)
1784 {
1785   struct smbc_file *fe;
1786   struct smbc_dirent *dirp, *dirent;
1787
1788   /* Check that all is ok first ... */
1789
1790   if (!smbc_initialized) {
1791
1792     errno = EUCLEAN;
1793     return NULL;
1794
1795   }
1796
1797   if (fd < smbc_start_fd || fd >= (smbc_start_fd + smbc_max_fd)) {
1798
1799     errno = EBADF;
1800     return NULL;
1801
1802   }
1803
1804   fe = smbc_file_table[fd - smbc_start_fd];
1805
1806   if (fe->file != False) { /* FIXME, should be dir, perhaps */
1807
1808     errno = ENOTDIR;
1809     return NULL;
1810
1811   }
1812
1813   if (!fe->dir_next)
1814     return NULL;
1815   else {
1816
1817     dirent = fe->dir_next->dirent;
1818
1819     if (!dirent) {
1820
1821       errno = ENOENT;
1822       return NULL;
1823
1824     }
1825
1826     /* Hmmm, do I even need to copy it? */
1827
1828     bcopy(dirent, smbc_local_dirent, dirent->dirlen); /* Copy the dirent */
1829
1830     dirp = (struct smbc_dirent *)smbc_local_dirent;
1831
1832     dirp->comment = (char *)(&dirp->name + dirent->namelen + 1);
1833     
1834     fe->dir_next = fe->dir_next->next;
1835
1836     return (struct smbc_dirent *)smbc_local_dirent;
1837   }
1838
1839 }
1840
1841 /*
1842  * Routine to get directory entries
1843  */
1844
1845 int smbc_getdents(unsigned int fd, struct smbc_dirent *dirp, int count)
1846 {
1847   struct smbc_file *fe;
1848   struct smbc_dir_list *dir;
1849   int rem = count, reqd;
1850
1851   /* Check that all is ok first ... */
1852
1853   if (!smbc_initialized) {
1854
1855     errno = EUCLEAN;
1856     return -1;
1857
1858   }
1859
1860   if (fd < smbc_start_fd || fd >= (smbc_start_fd + smbc_max_fd)) {
1861
1862     errno = EBADF;
1863     return -1;
1864
1865   }
1866
1867   fe = smbc_file_table[fd - smbc_start_fd];
1868
1869   if (fe->file != False) { /* FIXME, should be dir, perhaps */
1870
1871     errno = ENOTDIR;
1872     return -1;
1873
1874   }
1875
1876   /* 
1877    * Now, retrieve the number of entries that will fit in what was passed
1878    * We have to figure out if the info is in the list, or we need to 
1879    * send a request to the server to get the info.
1880    */
1881
1882   while ((dir = fe->dir_next)) {
1883     struct smbc_dirent *dirent;
1884
1885     if (!dir->dirent) {
1886
1887       errno = ENOENT;  /* Bad error */
1888       return -1;
1889
1890     }
1891
1892     if (rem < (reqd = (sizeof(struct smbc_dirent) + dir->dirent->namelen + 
1893                          dir->dirent->commentlen + 1))) {
1894
1895       if (rem < count) { /* We managed to copy something */
1896
1897         errno = 0;
1898         return count - rem;
1899
1900       }
1901       else { /* Nothing copied ... */
1902
1903         errno = EINVAL;  /* Not enough space ... */
1904         return -1;
1905
1906       }
1907
1908     }
1909
1910     dirent = dir->dirent;
1911
1912     bcopy(dirent, dirp, reqd); /* Copy the data in ... */
1913     
1914     dirp->comment = (char *)(&dirp->name + dirent->namelen + 1);
1915
1916     (char *)dirp += reqd;
1917
1918     rem -= reqd;
1919
1920     fe->dir_next = dir = dir -> next;
1921   }
1922
1923   if (rem == count)
1924     return 0;
1925   else 
1926     return count - rem;
1927
1928 }
1929
1930 /*
1931  * Routine to create a directory ...
1932  */
1933
1934 int smbc_mkdir(const char *fname, mode_t mode)
1935 {
1936   struct smbc_server *srv;
1937   fstring server, share, user, password;
1938   pstring path;
1939
1940   if (!smbc_initialized) {
1941
1942     errno = EUCLEAN;
1943     return -1;
1944
1945   }
1946
1947   if (!fname) {
1948
1949     errno = EINVAL;
1950     return -1;
1951
1952   }
1953   
1954   DEBUG(4, ("smbc_mkdir(%s)\n", fname));
1955
1956   smbc_parse_path(fname, server, share, path, user, password); /*FIXME, errors*/
1957
1958   if (user[0] == (char)0) pstrcpy(user, smbc_user);
1959
1960   srv = smbc_server(server, share, lp_workgroup(), user, password);
1961
1962   if (!srv) {
1963
1964     return -1;  /* errno set by smbc_server */
1965
1966   }
1967
1968   /* if (strncmp(srv->cli.dev, "IPC", 3) == 0) {
1969
1970      mode = aDIR | aRONLY;
1971
1972      }
1973      else if (strncmp(srv->cli.dev, "LPT", 3) == 0) {
1974
1975        if (strcmp(path, "\\") == 0) {
1976
1977           mode = aDIR | aRONLY;
1978
1979        }
1980        else {
1981
1982          mode = aRONLY;
1983          smbc_stat_printjob(srv, path, &size, &m_time);
1984          c_time = a_time = m_time;
1985
1986        }
1987        else { */
1988
1989   if (!cli_mkdir(&srv->cli, path)) {
1990
1991     errno = smbc_errno(&srv->cli);
1992     return -1;
1993
1994   } 
1995
1996   return 0;
1997
1998 }
1999
2000 /*
2001  * Routine to remove a directory
2002  */
2003
2004 int smbc_rmdir(const char *fname)
2005 {
2006   struct smbc_server *srv;
2007   fstring server, share, user, password;
2008   pstring path;
2009
2010   if (!smbc_initialized) {
2011
2012     errno = EUCLEAN;
2013     return -1;
2014
2015   }
2016
2017   if (!fname) {
2018
2019     errno = EINVAL;
2020     return -1;
2021
2022   }
2023   
2024   DEBUG(4, ("smbc_rmdir(%s)\n", fname));
2025
2026   smbc_parse_path(fname, server, share, path, user, password); /*FIXME, errors*/
2027
2028   if (user[0] == (char)0) pstrcpy(user, smbc_user);
2029
2030   srv = smbc_server(server, share, lp_workgroup(), user, password);
2031
2032   if (!srv) {
2033
2034     return -1;  /* errno set by smbc_server */
2035
2036   }
2037
2038   /* if (strncmp(srv->cli.dev, "IPC", 3) == 0) {
2039
2040      mode = aDIR | aRONLY;
2041
2042      }
2043      else if (strncmp(srv->cli.dev, "LPT", 3) == 0) {
2044
2045        if (strcmp(path, "\\") == 0) {
2046
2047           mode = aDIR | aRONLY;
2048
2049        }
2050        else {
2051
2052          mode = aRONLY;
2053          smbc_stat_printjob(srv, path, &size, &m_time);
2054          c_time = a_time = m_time;
2055
2056        }
2057        else { */
2058
2059   if (!cli_rmdir(&srv->cli, path)) {
2060
2061     errno = smbc_errno(&srv->cli);
2062     return -1;
2063
2064   } 
2065
2066   return 0;
2067
2068 }
2069
2070 /*
2071  * Routine to return the current directory position
2072  */
2073
2074 off_t smbc_telldir(int fd)
2075 {
2076   struct smbc_file *fe;
2077
2078   if (!smbc_initialized) {
2079
2080     errno = EUCLEAN;
2081     return -1;
2082
2083   }
2084
2085   if (fd < smbc_start_fd || fd >= (smbc_start_fd + smbc_max_fd)) {
2086
2087     errno = EBADF;
2088     return -1;
2089
2090   }
2091
2092   fe = smbc_file_table[fd - smbc_start_fd];
2093
2094   if (fe->file != False) { /* FIXME, should be dir, perhaps */
2095
2096     errno = ENOTDIR;
2097     return -1;
2098
2099   }
2100
2101   return (off_t) fe->dir_next;
2102
2103 }
2104
2105 /*
2106  * Routine to seek on a directory
2107  */
2108
2109 int smbc_lseekdir(int fd, off_t offset, int whence)
2110 {
2111
2112   if (!smbc_initialized) {
2113
2114     errno = EUCLEAN;
2115     return -1;
2116
2117   }
2118
2119   return 0;
2120
2121 }
2122
2123 /*
2124  * Routine to fstat a dir
2125  */
2126
2127 int smbc_fstatdir(int fd, struct stat *st)
2128 {
2129
2130   if (!smbc_initialized) {
2131
2132     errno = EUCLEAN;
2133     return -1;
2134
2135   }
2136
2137   return 0;
2138
2139 }
2140
2141 /*
2142  * Routine to print a file on a remote server ...
2143  *
2144  * We open the file, which we assume to be on a remote server, and then
2145  * copy it to a print file on the share specified by printq.
2146  */
2147
2148 int smbc_print_file(const char *fname, const char *printq)
2149 {
2150   int fid1, fid2, bytes, saverr, tot_bytes = 0;
2151   char buf[4096];
2152
2153   if (!smbc_initialized) {
2154
2155     errno = EUCLEAN;
2156     return -1;
2157
2158   }
2159
2160   if (!fname && !printq) {
2161
2162     errno = EINVAL;
2163     return -1;
2164
2165   }
2166
2167   /* Try to open the file for reading ... */
2168
2169   if ((fid1 = smbc_open(fname, O_RDONLY, 0666)) < 0) {
2170
2171     return -1;  /* smbc_open sets errno */
2172
2173   }
2174
2175   /* Now, try to open the printer file for writing */
2176
2177   if ((fid2 = smbc_open_print_job(printq)) < 0) {
2178
2179     saverr = errno;  /* Save errno */
2180     smbc_close(fid1);
2181     errno = saverr;
2182     return -1;
2183
2184   }
2185
2186   while ((bytes = smbc_read(fid1, buf, sizeof(buf))) > 0) {
2187
2188     tot_bytes += bytes;
2189
2190     if ((smbc_write(fid2, buf, bytes)) < 0) {
2191
2192       saverr = errno;
2193       smbc_close(fid1);
2194       smbc_close(fid2);
2195       errno = saverr;
2196
2197     }
2198
2199   }
2200
2201   saverr = errno;
2202
2203   smbc_close(fid1);  /* We have to close these anyway */
2204   smbc_close(fid2);
2205
2206   if (bytes < 0) {
2207
2208     errno = saverr;
2209     return -1;
2210
2211   }
2212
2213   return tot_bytes;
2214
2215 }
2216
2217 /*
2218  * Open a print file to be written to by other calls
2219  */
2220
2221 int smbc_open_print_job(const char *fname)
2222 {
2223   struct smbc_server *srv;
2224   fstring server, share, user, password;
2225   pstring path;
2226
2227   if (!smbc_initialized) {
2228
2229     errno = EUCLEAN;
2230     return -1;
2231
2232   }
2233
2234   if (!fname) {
2235
2236     errno = EINVAL;
2237     return -1;
2238
2239   }
2240   
2241   DEBUG(4, ("smbc_open_print_job(%s)\n", fname));
2242
2243   smbc_parse_path(fname, server, share, path, user, password); /*FIXME, errors*/
2244
2245   /* What if the path is empty, or the file exists? */
2246
2247   return smbc_open(fname, O_WRONLY, 666);
2248
2249 }
2250
2251 /*
2252  * Routine to list print jobs on a printer share ...
2253  */
2254
2255 int smbc_list_print_jobs(const char *fname, void (*fn)(struct print_job_info *))
2256 {
2257   struct smbc_server *srv;
2258   fstring server, share, user, password;
2259   pstring path;
2260
2261   if (!smbc_initialized) {
2262
2263     errno = EUCLEAN;
2264     return -1;
2265
2266   }
2267
2268   if (!fname) {
2269
2270     errno = EINVAL;
2271     return -1;
2272
2273   }
2274   
2275   DEBUG(4, ("smbc_list_print_jobs(%s)\n", fname));
2276
2277   smbc_parse_path(fname, server, share, path, user, password); /*FIXME, errors*/
2278
2279   if (user[0] == (char)0) pstrcpy(user, smbc_user);
2280
2281   srv = smbc_server(server, share, lp_workgroup(), user, password);
2282
2283   if (!srv) {
2284
2285     return -1;  /* errno set by smbc_server */
2286
2287   }
2288
2289   if (cli_print_queue(&srv->cli, fn) < 0) {
2290
2291     errno = smbc_errno(&srv->cli);
2292     return -1;
2293
2294   }
2295
2296   return 0;
2297
2298 }
2299
2300 /*
2301  * Delete a print job from a remote printer share
2302  */
2303
2304 int smbc_unlink_print_job(const char *fname, int id)
2305 {
2306   struct smbc_server *srv;
2307   fstring server, share, user, password;
2308   pstring path;
2309
2310   if (!smbc_initialized) {
2311
2312     errno = EUCLEAN;
2313     return -1;
2314
2315   }
2316
2317   if (!fname) {
2318
2319     errno = EINVAL;
2320     return -1;
2321
2322   }
2323   
2324   DEBUG(4, ("smbc_unlink_print_job(%s)\n", fname));
2325
2326   smbc_parse_path(fname, server, share, path, user, password); /*FIXME, errors*/
2327
2328   if (user[0] == (char)0) pstrcpy(user, smbc_user);
2329
2330   srv = smbc_server(server, share, lp_workgroup(), user, password);
2331
2332   if (!srv) {
2333
2334     return -1;  /* errno set by smbc_server */
2335
2336   }
2337
2338   if (cli_printjob_del(&srv->cli, id) < 0) {
2339
2340     errno = smbc_errno(&srv->cli);
2341     return -1;
2342
2343   }
2344
2345   return 0;
2346
2347 }
2348