This commit was manufactured by cvs2svn to create branch 'SAMBA_3_0'.(This used to...
[ira/wip.git] / source3 / libsmb / libsmbclient.c
1 /* 
2    Unix SMB/Netbios implementation.
3    SMB client library implementation
4    Copyright (C) Andrew Tridgell 1998
5    Copyright (C) Richard Sharpe 2000, 2002
6    Copyright (C) John Terpstra 2000
7    Copyright (C) Tom Jansen (Ninja ISD) 2002 
8    
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
26 #include "../include/libsmbclient.h"
27
28 /*
29  * Functions exported by libsmb_cache.c that we need here
30  */
31 int smbc_default_cache_functions(SMBCCTX *context);
32
33 /* 
34  * check if an element is part of the list. 
35  * FIXME: Does not belong here !  
36  * Can anyone put this in a macro in dlinklist.h ?
37  * -- Tom
38  */
39 static int DLIST_CONTAINS(SMBCFILE * list, SMBCFILE *p) {
40         if (!p || !list) return False;
41         do {
42                 if (p == list) return True;
43                 list = list->next;
44         } while (list);
45         return False;
46 }
47
48 extern BOOL in_client;
49 extern pstring global_myname;
50
51 /*
52  * Is the logging working / configfile read ? 
53  */
54 static int smbc_initialized = 0;
55
56 /*
57  * Function to parse a path and turn it into components
58  *
59  * We accept smb://[[[domain;]user[:password@]]server[/share[/path[/file]]]]
60  * 
61  * smb://       means show all the workgroups
62  * smb://name/  means, if name<1D> or name<1B> exists, list servers in workgroup,
63  *              else, if name<20> exists, list all shares for server ...
64  */
65
66 static const char *smbc_prefix = "smb:";
67
68 static int
69 smbc_parse_path(SMBCCTX *context, const char *fname, char *server, char *share, char *path,
70                 char *user, char *password) /* FIXME, lengths of strings */
71 {
72         static pstring s;
73         pstring userinfo;
74         char *p;
75         char *q, *r;
76         int len;
77
78         server[0] = share[0] = path[0] = user[0] = password[0] = (char)0;
79         pstrcpy(s, fname);
80
81         /*  clean_fname(s);  causing problems ... */
82
83         /* see if it has the right prefix */
84         len = strlen(smbc_prefix);
85         if (strncmp(s,smbc_prefix,len) || 
86             (s[len] != '/' && s[len] != 0)) return -1; /* What about no smb: ? */
87
88         p = s + len;
89
90         /* Watch the test below, we are testing to see if we should exit */
91
92         if (strncmp(p, "//", 2) && strncmp(p, "\\\\", 2)) {
93
94                 return -1;
95
96         }
97
98         p += 2;  /* Skip the // or \\  */
99
100         if (*p == (char)0)
101                 return 0;
102
103         if (*p == '/') {
104
105                 strncpy(server, context->workgroup, 
106                         (strlen(context->workgroup) < 16)?strlen(context->workgroup):16);
107                 return 0;
108                 
109         }
110
111         /*
112          * ok, its for us. Now parse out the server, share etc. 
113          *
114          * However, we want to parse out [[domain;]user[:password]@] if it
115          * exists ...
116          */
117
118         /* check that '@' occurs before '/', if '/' exists at all */
119         q = strchr_m(p, '@');
120         r = strchr_m(p, '/');
121         if (q && (!r || q < r)) {
122                 pstring username, passwd, domain;
123                 char *u = userinfo;
124
125                 next_token(&p, userinfo, "@", sizeof(fstring));
126
127                 username[0] = passwd[0] = domain[0] = 0;
128
129                 if (strchr_m(u, ';')) {
130       
131                         next_token(&u, domain, ";", sizeof(fstring));
132
133                 }
134
135                 if (strchr_m(u, ':')) {
136
137                         next_token(&u, username, ":", sizeof(fstring));
138
139                         pstrcpy(passwd, u);
140
141                 }
142                 else {
143
144                         pstrcpy(username, u);
145
146                 }
147
148                 if (username[0])
149                         strncpy(user, username, sizeof(fstring));  /* FIXME, size and domain */
150
151                 if (passwd[0])
152                         strncpy(password, passwd, sizeof(fstring)); /* FIXME, size */
153
154         }
155
156         if (!next_token(&p, server, "/", sizeof(fstring))) {
157
158                 return -1;
159
160         }
161
162         if (*p == (char)0) return 0;  /* That's it ... */
163   
164         if (!next_token(&p, share, "/", sizeof(fstring))) {
165
166                 return -1;
167
168         }
169
170         pstrcpy(path, p);
171   
172         all_string_sub(path, "/", "\\", 0);
173
174         return 0;
175 }
176
177 /*
178  * Convert an SMB error into a UNIX error ...
179  */
180
181 static int smbc_errno(SMBCCTX *context, struct cli_state *c)
182 {
183         int ret = cli_errno(c);
184         
185         if (cli_is_dos_error(c)) {
186                 uint8 eclass;
187                 uint32 ecode;
188
189                 cli_dos_error(c, &eclass, &ecode);
190                 
191                 DEBUG(3,("smbc_error %d %d (0x%x) -> %d\n", 
192                          (int)eclass, (int)ecode, (int)ecode, ret));
193         } else {
194                 NTSTATUS status;
195
196                 status = cli_nt_error(c);
197
198                 DEBUG(3,("smbc errno %s -> %d\n",
199                          nt_errstr(status), ret));
200         }
201
202         return ret;
203 }
204
205 /* 
206  * Check a server_fd.
207  * returns 0 if the server is in shape. Returns 1 on error 
208  * 
209  * Also useable outside libsmbclient to enable external cache
210  * to do some checks too.
211  */
212 int smbc_check_server(SMBCCTX * context, SMBCSRV * server) 
213 {
214         if ( send_keepalive(server->cli.fd) == False )
215                 return 1;
216
217         /* connection is ok */
218         return 0;
219 }
220
221 /* 
222  * Remove a server from the list server_table if it's unused.
223  * On success, 0 is returned. 1 is returned if the server could not be removed.
224  * 
225  * Also useable outside libsmbclient
226  */
227 int smbc_remove_unused_server(SMBCCTX * context, SMBCSRV * srv)
228 {
229         SMBCFILE * file;
230
231         /* are we being fooled ? */
232         if (!context || !context->_initialized || !srv) return 1;
233
234         
235         /* Check all open files/directories for a relation with this server */
236         for (file = context->_files; file; file=file->next) {
237                 if (file->srv == srv) {
238                         /* Still used */
239                         DEBUG(3, ("smbc_remove_usused_server: %p still used by %p.\n", 
240                                   srv, file));
241                         return 1;
242                 }
243         }
244
245         DLIST_REMOVE(context->_servers, srv);
246
247         cli_shutdown(&srv->cli);
248
249         DEBUG(3, ("smbc_remove_usused_server: %p removed.\n", srv));
250
251         context->callbacks.remove_cached_srv_fn(context, srv);
252         
253         SAFE_FREE(srv);
254         
255         return 0;
256 }
257
258 /*
259  * Connect to a server, possibly on an existing connection
260  *
261  * Here, what we want to do is: If the server and username
262  * match an existing connection, reuse that, otherwise, establish a 
263  * new connection.
264  *
265  * If we have to create a new connection, call the auth_fn to get the
266  * info we need, unless the username and password were passed in.
267  */
268
269 SMBCSRV *smbc_server(SMBCCTX *context,
270                      char *server, char *share, 
271                      char *workgroup, char *username, 
272                      char *password)
273 {
274         SMBCSRV *srv=NULL;
275         int auth_called = 0;
276         struct cli_state c;
277         struct nmb_name called, calling;
278         char *p, *server_n = server;
279         fstring group;
280         pstring ipenv;
281         struct in_addr ip;
282         int tried_reverse = 0;
283   
284         zero_ip(&ip);
285         ZERO_STRUCT(c);
286
287         if (server[0] == 0) {
288                 errno = EPERM;
289                 return NULL;
290         }
291
292  check_server_cache:
293
294         srv = context->callbacks.get_cached_srv_fn(context, server, share, 
295                                                    workgroup, username);
296         
297         if (!auth_called && !srv && (!username[0] || !password[0])) {
298                 context->callbacks.auth_fn(server, share, workgroup, sizeof(fstring),
299                              username, sizeof(fstring), password, sizeof(fstring));
300                 /* 
301                  * However, smbc_auth_fn may have picked up info relating to an 
302                  * existing connection, so try for an existing connection again ...
303                  */
304                 auth_called = 1;
305                 goto check_server_cache;
306                 
307         }
308         
309         if (srv) {
310                 if (context->callbacks.check_server_fn(context, srv)) {
311                         /* 
312                          * This server is no good anymore 
313                          * Try to remove it and check for more possible servers in the cache 
314                          */
315                         if (context->callbacks.remove_unused_server_fn(context, srv)) { 
316                                 /* 
317                                  * We could not remove the server completely, remove it from the cache
318                                  * so we will not get it again. It will be removed when the last file/dir
319                                  * is closed.
320                                  */
321                                 context->callbacks.remove_cached_srv_fn(context, srv);
322                         }
323                         
324                         /* 
325                          * Maybe there are more cached connections to this server 
326                          */
327                         goto check_server_cache; 
328                 }
329                 return srv;
330         }
331
332         make_nmb_name(&calling, context->netbios_name, 0x0);
333         make_nmb_name(&called , server, 0x20);
334
335         DEBUG(4,("smbc_server: server_n=[%s] server=[%s]\n", server_n, server));
336   
337         if ((p=strchr_m(server_n,'#')) && 
338             (strcmp(p+1,"1D")==0 || strcmp(p+1,"01")==0)) {
339     
340                 fstrcpy(group, server_n);
341                 p = strchr_m(group,'#');
342                 *p = 0;
343                 
344         }
345
346         DEBUG(4,(" -> server_n=[%s] server=[%s]\n", server_n, server));
347
348  again:
349         slprintf(ipenv,sizeof(ipenv)-1,"HOST_%s", server_n);
350
351         zero_ip(&ip);
352
353         /* have to open a new connection */
354         if (!cli_initialise(&c)) {
355                 errno = ENOENT;
356                 return NULL;
357         }
358
359         c.timeout = context->timeout;
360
361         if (!cli_connect(&c, server_n, &ip)) {
362                 cli_shutdown(&c);
363                 errno = ENOENT;
364                 return NULL;
365         }
366
367         if (!cli_session_request(&c, &calling, &called)) {
368                 cli_shutdown(&c);
369                 if (strcmp(called.name, "*SMBSERVER")) {
370                         make_nmb_name(&called , "*SMBSERVER", 0x20);
371                         goto again;
372                 }
373                 else {  /* Try one more time, but ensure we don't loop */
374
375                   /* Only try this if server is an IP address ... */
376
377                   if (is_ipaddress(server) && !tried_reverse) {
378                     fstring remote_name;
379                     struct in_addr rem_ip;
380
381                     if ((rem_ip.s_addr=inet_addr(server)) == INADDR_NONE) {
382                       DEBUG(4, ("Could not convert IP address %s to struct in_addr\n", server));
383                       errno = ENOENT;
384                       return NULL;
385                     }
386
387                     tried_reverse++; /* Yuck */
388
389                     if (name_status_find("*", 0, 0, rem_ip, remote_name)) {
390                       make_nmb_name(&called, remote_name, 0x20);
391                       goto again;
392                     }
393
394
395                   }
396                 }
397                 errno = ENOENT;
398                 return NULL;
399         }
400   
401         DEBUG(4,(" session request ok\n"));
402   
403         if (!cli_negprot(&c)) {
404                 cli_shutdown(&c);
405                 errno = ENOENT;
406                 return NULL;
407         }
408
409         if (!cli_session_setup(&c, username, 
410                                password, strlen(password),
411                                password, strlen(password),
412                                workgroup) &&
413             /* try an anonymous login if it failed */
414             !cli_session_setup(&c, "", "", 1,"", 0, workgroup)) {
415                 cli_shutdown(&c);
416                 errno = EPERM;
417                 return NULL;
418         }
419
420         DEBUG(4,(" session setup ok\n"));
421
422         if (!cli_send_tconX(&c, share, "?????",
423                             password, strlen(password)+1)) {
424                 errno = smbc_errno(context, &c);
425                 cli_shutdown(&c);
426                 return NULL;
427         }
428   
429         DEBUG(4,(" tconx ok\n"));
430   
431         /*
432          * Ok, we have got a nice connection
433          * Let's find a free server_fd 
434          */
435
436         srv = (SMBCSRV *)malloc(sizeof(*srv));
437         if (!srv) {
438                 errno = ENOMEM;
439                 goto failed;
440         }
441
442         ZERO_STRUCTP(srv);
443         srv->cli = c;
444         srv->dev = (dev_t)(str_checksum(server) ^ str_checksum(share));
445
446         /* now add it to the cache (internal or external) */
447         if (context->callbacks.add_cached_srv_fn(context, srv, server, share, workgroup, username)) {
448                 DEBUG(3, (" Failed to add server to cache\n"));
449                 goto failed;
450         }
451
452         
453         DEBUG(2, ("Server connect ok: //%s/%s: %p\n", 
454                   server, share, srv));
455
456         return srv;
457
458  failed:
459         cli_shutdown(&c);
460         if (!srv) return NULL;
461   
462         SAFE_FREE(srv);
463         return NULL;
464 }
465
466 /*
467  * Routine to open() a file ...
468  */
469
470 static SMBCFILE *smbc_open_ctx(SMBCCTX *context, const char *fname, int flags, mode_t mode)
471 {
472         fstring server, share, user, password, workgroup;
473         pstring path;
474         SMBCSRV *srv   = NULL;
475         SMBCFILE *file = NULL;
476         int fd;
477
478         if (!context || !context->_initialized) {
479
480                 errno = EINVAL;  /* Best I can think of ... */
481                 return NULL;
482
483         }
484
485         if (!fname) {
486
487                 errno = EINVAL;
488                 return NULL;
489
490         }
491
492         smbc_parse_path(context, fname, server, share, path, user, password); /* FIXME, check errors */
493
494         if (user[0] == (char)0) pstrcpy(user, context->user);
495
496         pstrcpy(workgroup, context->workgroup);
497
498         srv = smbc_server(context, server, share, workgroup, user, password);
499
500         if (!srv) {
501
502                 if (errno == EPERM) errno = EACCES;
503                 return NULL;  /* smbc_server sets errno */
504     
505         }
506
507         /* Hmmm, the test for a directory is suspect here ... FIXME */
508
509         if (strlen(path) > 0 && path[strlen(path) - 1] == '\\') {
510     
511                 fd = -1;
512
513         }
514         else {
515           
516                 file = malloc(sizeof(SMBCFILE));
517
518                 if (!file) {
519
520                         errno = ENOMEM;
521                         return NULL;
522
523                 }
524
525                 ZERO_STRUCTP(file);
526
527                 if ((fd = cli_open(&srv->cli, path, flags, DENY_NONE)) < 0) {
528
529                         /* Handle the error ... */
530
531                         SAFE_FREE(file);
532                         errno = smbc_errno(context, &srv->cli);
533                         return NULL;
534
535                 }
536
537                 /* Fill in file struct */
538
539                 file->cli_fd  = fd;
540                 file->fname   = strdup(fname);
541                 file->srv     = srv;
542                 file->offset  = 0;
543                 file->file    = True;
544
545                 DLIST_ADD(context->_files, file);
546                 return file;
547
548         }
549
550         /* Check if opendir needed ... */
551
552         if (fd == -1) {
553                 int eno = 0;
554
555                 eno = smbc_errno(context, &srv->cli);
556                 file = context->opendir(context, fname);
557                 if (!file) errno = eno;
558                 return file;
559
560         }
561
562         errno = EINVAL; /* FIXME, correct errno ? */
563         return NULL;
564
565 }
566
567 /*
568  * Routine to create a file 
569  */
570
571 static int creat_bits = O_WRONLY | O_CREAT | O_TRUNC; /* FIXME: Do we need this */
572
573 static SMBCFILE *smbc_creat_ctx(SMBCCTX *context, const char *path, mode_t mode)
574 {
575
576         if (!context || !context->_initialized) {
577
578                 errno = EINVAL;
579                 return NULL;
580
581         }
582
583         return smbc_open_ctx(context, path, creat_bits, mode);
584 }
585
586 /*
587  * Routine to read() a file ...
588  */
589
590 static ssize_t smbc_read_ctx(SMBCCTX *context, SMBCFILE *file, void *buf, size_t count)
591 {
592         int ret;
593
594         if (!context || !context->_initialized) {
595
596                 errno = EINVAL;
597                 return -1;
598
599         }
600
601         DEBUG(4, ("smbc_read(%p, %d)\n", file, (int)count));
602
603         if (!file || !DLIST_CONTAINS(context->_files, file)) {
604
605                 errno = EBADF;
606                 return -1;
607
608         }
609
610         /* Check that the buffer exists ... */
611
612         if (buf == NULL) {
613
614                 errno = EINVAL;
615                 return -1;
616
617         }
618
619         ret = cli_read(&file->srv->cli, file->cli_fd, buf, file->offset, count);
620
621         if (ret < 0) {
622
623                 errno = smbc_errno(context, &file->srv->cli);
624                 return -1;
625
626         }
627
628         file->offset += ret;
629
630         DEBUG(4, ("  --> %d\n", ret));
631
632         return ret;  /* Success, ret bytes of data ... */
633
634 }
635
636 /*
637  * Routine to write() a file ...
638  */
639
640 static ssize_t smbc_write_ctx(SMBCCTX *context, SMBCFILE *file, void *buf, size_t count)
641 {
642         int ret;
643
644         if (!context || context->_initialized) {
645
646                 errno = EINVAL;
647                 return -1;
648
649         }
650
651         if (!file || !DLIST_CONTAINS(context->_files, file)) {
652
653                 errno = EBADF;
654                 return -1;
655     
656         }
657
658         /* Check that the buffer exists ... */
659
660         if (buf == NULL) {
661
662                 errno = EINVAL;
663                 return -1;
664
665         }
666
667         ret = cli_write(&file->srv->cli, file->cli_fd, 0, buf, file->offset, count);
668
669         if (ret <= 0) {
670
671                 errno = smbc_errno(context, &file->srv->cli);
672                 return -1;
673
674         }
675
676         file->offset += ret;
677
678         return ret;  /* Success, 0 bytes of data ... */
679 }
680  
681 /*
682  * Routine to close() a file ...
683  */
684
685 static int smbc_close_ctx(SMBCCTX *context, SMBCFILE *file)
686 {
687         SMBCSRV *srv; 
688
689         if (!context || !context->_initialized) {
690
691                 errno = EINVAL;
692                 return -1;
693
694         }
695
696         if (!file || !DLIST_CONTAINS(context->_files, file)) {
697    
698                 errno = EBADF;
699                 return -1;
700
701         }
702
703         /* IS a dir ... */
704         if (!file->file) {
705                 
706                 return context->closedir(context, file);
707
708         }
709
710         if (!cli_close(&file->srv->cli, file->cli_fd)) {
711
712                 DEBUG(3, ("cli_close failed on %s. purging server.\n", 
713                           file->fname));
714                 /* Deallocate slot and remove the server 
715                  * from the server cache if unused */
716                 errno = smbc_errno(context, &file->srv->cli);  
717                 srv = file->srv;
718                 DLIST_REMOVE(context->_files, file);
719                 SAFE_FREE(file->fname);
720                 SAFE_FREE(file);
721                 context->callbacks.remove_unused_server_fn(context, srv);
722
723                 return -1;
724
725         }
726
727         if (!file->file) {
728
729                 return context->closedir(context, file);
730
731         }
732
733         if (!cli_close(&file->srv->cli, file->cli_fd)) {
734                 DEBUG(3, ("cli_close failed on %s. purging server.\n", 
735                           file->fname));
736                 /* Deallocate slot and remove the server 
737                  * from the server cache if unused */
738                 errno = smbc_errno(context, &file->srv->cli);  
739                 srv = file->srv;
740                 DLIST_REMOVE(context->_files, file);
741                 SAFE_FREE(file->fname);
742                 SAFE_FREE(file);
743                 context->callbacks.remove_unused_server_fn(context, srv);
744
745                 return -1;
746         }
747
748         DLIST_REMOVE(context->_files, file);
749         SAFE_FREE(file->fname);
750         SAFE_FREE(file);
751
752         return 0;
753 }
754
755 /*
756  * Get info from an SMB server on a file. Use a qpathinfo call first
757  * and if that fails, use getatr, as Win95 sometimes refuses qpathinfo
758  */
759 static BOOL smbc_getatr(SMBCCTX * context, SMBCSRV *srv, char *path, 
760                  uint16 *mode, size_t *size, 
761                  time_t *c_time, time_t *a_time, time_t *m_time,
762                  SMB_INO_T *ino)
763 {
764
765         if (!context || !context->_initialized) {
766  
767                 errno = EINVAL;
768                 return -1;
769  
770         }
771
772         DEBUG(4,("smbc_getatr: sending qpathinfo\n"));
773   
774         if (!srv->no_pathinfo2 &&
775             cli_qpathinfo2(&srv->cli, path, c_time, a_time, m_time, NULL,
776                            size, mode, ino)) return True;
777
778         /* if this is NT then don't bother with the getatr */
779         if (srv->cli.capabilities & CAP_NT_SMBS) return False;
780
781         if (cli_getatr(&srv->cli, path, mode, size, m_time)) {
782                 a_time = c_time = m_time;
783                 srv->no_pathinfo2 = True;
784                 return True;
785         }
786
787         return False;
788
789 }
790
791 /*
792  * Routine to unlink() a file
793  */
794
795 static int smbc_unlink_ctx(SMBCCTX *context, const char *fname)
796 {
797         fstring server, share, user, password, workgroup;
798         pstring path;
799         SMBCSRV *srv = NULL;
800
801         if (!context || context->_initialized) {
802
803                 errno = EINVAL;  /* Best I can think of ... */
804                 return -1;
805
806         }
807
808         if (!fname) {
809
810                 errno = EINVAL;
811                 return -1;
812
813         }
814
815         smbc_parse_path(context, fname, server, share, path, user, password); /* FIXME, check errors */
816
817         if (user[0] == (char)0) pstrcpy(user, context->user);
818
819         pstrcpy(workgroup, context->workgroup);
820
821         srv = smbc_server(context, server, share, workgroup, user, password);
822
823         if (!srv) {
824
825                 return -1;  /* smbc_server sets errno */
826
827         }
828
829         /*  if (strncmp(srv->cli.dev, "LPT", 3) == 0) {
830
831     int job = smbc_stat_printjob(srv, path, NULL, NULL);
832     if (job == -1) {
833
834       return -1;
835
836     }
837     if ((err = cli_printjob_del(&srv->cli, job)) != 0) {
838
839     
840       return -1;
841
842     }
843     } else */
844
845         if (!cli_unlink(&srv->cli, path)) {
846
847                 errno = smbc_errno(context, &srv->cli);
848
849                 if (errno == EACCES) { /* Check if the file is a directory */
850
851                         int saverr = errno;
852                         size_t size = 0;
853                         uint16 mode = 0;
854                         time_t m_time = 0, a_time = 0, c_time = 0;
855                         SMB_INO_T ino = 0;
856
857                         if (!smbc_getatr(context, srv, path, &mode, &size,
858                                          &c_time, &a_time, &m_time, &ino)) {
859
860                                 /* Hmmm, bad error ... What? */
861
862                                 errno = smbc_errno(context, &srv->cli);
863                                 return -1;
864
865                         }
866                         else {
867
868                                 if (IS_DOS_DIR(mode))
869                                         errno = EISDIR;
870                                 else
871                                         errno = saverr;  /* Restore this */
872
873                         }
874                 }
875
876                 return -1;
877
878         }
879
880         return 0;  /* Success ... */
881
882 }
883
884 /*
885  * Routine to rename() a file
886  */
887
888 static int smbc_rename_ctx(SMBCCTX *ocontext, const char *oname, 
889                            SMBCCTX *ncontext, const char *nname)
890 {
891         fstring server1, share1, server2, share2, user1, user2, password1, password2, workgroup;
892         pstring path1, path2;
893         SMBCSRV *srv = NULL;
894
895         if (!ocontext || !ncontext ||
896             !ocontext->_initialized || !ncontext->_initialized) {
897
898                 errno = EINVAL;  /* Best I can think of ... */
899                 return -1;
900
901         }
902         
903         if (!oname || !nname) {
904
905                 errno = EINVAL;
906                 return -1;
907
908         }
909         
910         DEBUG(4, ("smbc_rename(%s,%s)\n", oname, nname));
911
912         smbc_parse_path(ocontext, oname, server1, share1, path1, user1, password1);
913
914         if (user1[0] == (char)0) pstrcpy(user1, ocontext->user);
915
916         smbc_parse_path(ncontext, nname, server2, share2, path2, user2, password2);
917
918         if (user2[0] == (char)0) pstrcpy(user2, ncontext->user);
919
920         if (strcmp(server1, server2) || strcmp(share1, share2) ||
921             strcmp(user1, user2)) {
922
923                 /* Can't rename across file systems, or users?? */
924
925                 errno = EXDEV;
926                 return -1;
927
928         }
929
930         pstrcpy(workgroup, ocontext->workgroup);
931         /* HELP !!! Which workgroup should I use ? Or are they always the same -- Tom */ 
932         srv = smbc_server(ocontext, server1, share1, workgroup, user1, password1);
933         if (!srv) {
934
935                 return -1;
936
937         }
938
939         if (!cli_rename(&srv->cli, path1, path2)) {
940                 int eno = smbc_errno(ocontext, &srv->cli);
941
942                 if (eno != EEXIST ||
943                     !cli_unlink(&srv->cli, path2) ||
944                     !cli_rename(&srv->cli, path1, path2)) {
945
946                         errno = eno;
947                         return -1;
948
949                 }
950         }
951
952         return 0; /* Success */
953
954 }
955
956 /*
957  * A routine to lseek() a file
958  */
959
960 static off_t smbc_lseek_ctx(SMBCCTX *context, SMBCFILE *file, off_t offset, int whence)
961 {
962         size_t size;
963
964         if (!context || !context->_initialized) {
965
966                 errno = EINVAL;
967                 return -1;
968                 
969         }
970
971         if (!file || !DLIST_CONTAINS(context->_files, file)) {
972
973                 errno = EBADF;
974                 return -1;
975
976         }
977
978         if (!file->file) {
979
980                 errno = EINVAL;
981                 return -1;      /* Can't lseek a dir ... */
982
983         }
984
985         switch (whence) {
986         case SEEK_SET:
987                 file->offset = offset;
988                 break;
989
990         case SEEK_CUR:
991                 file->offset += offset;
992                 break;
993
994         case SEEK_END:
995                 if (!cli_qfileinfo(&file->srv->cli, file->cli_fd, NULL, &size, NULL, NULL,
996                                    NULL, NULL, NULL) &&
997                     !cli_getattrE(&file->srv->cli, file->cli_fd, NULL, &size, NULL, NULL,
998                                   NULL)) {
999
1000                         errno = EINVAL;
1001                         return -1;
1002                 }
1003                 file->offset = size + offset;
1004                 break;
1005
1006         default:
1007                 errno = EINVAL;
1008                 break;
1009
1010         }
1011
1012         return file->offset;
1013
1014 }
1015
1016 /* 
1017  * Generate an inode number from file name for those things that need it
1018  */
1019
1020 static
1021 ino_t smbc_inode(SMBCCTX *context, const char *name)
1022 {
1023
1024         if (!context || !context->_initialized) {
1025
1026                 errno = EINVAL;
1027                 return -1;
1028
1029         }
1030
1031         if (!*name) return 2; /* FIXME, why 2 ??? */
1032         return (ino_t)str_checksum(name);
1033
1034 }
1035
1036 /*
1037  * Routine to put basic stat info into a stat structure ... Used by stat and
1038  * fstat below.
1039  */
1040
1041 static
1042 int smbc_setup_stat(SMBCCTX *context, struct stat *st, char *fname, size_t size, int mode)
1043 {
1044         
1045         st->st_mode = 0;
1046
1047         if (IS_DOS_DIR(mode)) {
1048                 st->st_mode = SMBC_DIR_MODE;
1049         } else {
1050                 st->st_mode = SMBC_FILE_MODE;
1051         }
1052
1053         if (IS_DOS_ARCHIVE(mode)) st->st_mode |= S_IXUSR;
1054         if (IS_DOS_SYSTEM(mode)) st->st_mode |= S_IXGRP;
1055         if (IS_DOS_HIDDEN(mode)) st->st_mode |= S_IXOTH;
1056         if (!IS_DOS_READONLY(mode)) st->st_mode |= S_IWUSR;
1057
1058         st->st_size = size;
1059         st->st_blksize = 512;
1060         st->st_blocks = (size+511)/512;
1061         st->st_uid = getuid();
1062         st->st_gid = getgid();
1063
1064         if (IS_DOS_DIR(mode)) {
1065                 st->st_nlink = 2;
1066         } else {
1067                 st->st_nlink = 1;
1068         }
1069
1070         if (st->st_ino == 0) {
1071                 st->st_ino = smbc_inode(context, fname);
1072         }
1073         
1074         return True;  /* FIXME: Is this needed ? */
1075
1076 }
1077
1078 /*
1079  * Routine to stat a file given a name
1080  */
1081
1082 static int smbc_stat_ctx(SMBCCTX *context, const char *fname, struct stat *st)
1083 {
1084         SMBCSRV *srv;
1085         fstring server, share, user, password, workgroup;
1086         pstring path;
1087         time_t m_time = 0, a_time = 0, c_time = 0;
1088         size_t size = 0;
1089         uint16 mode = 0;
1090         SMB_INO_T ino = 0;
1091
1092         if (!context || !context->_initialized) {
1093
1094                 errno = EINVAL;  /* Best I can think of ... */
1095                 return -1;
1096     
1097         }
1098
1099         if (!fname) {
1100
1101                 errno = EINVAL;
1102                 return -1;
1103
1104         }
1105   
1106         DEBUG(4, ("smbc_stat(%s)\n", fname));
1107
1108         smbc_parse_path(context, fname, server, share, path, user, password); /*FIXME, errors*/
1109
1110         if (user[0] == (char)0) pstrcpy(user, context->user);
1111
1112         pstrcpy(workgroup, context->workgroup);
1113
1114         srv = smbc_server(context, server, share, workgroup, user, password);
1115
1116         if (!srv) {
1117
1118                 return -1;  /* errno set by smbc_server */
1119
1120         }
1121
1122         /* if (strncmp(srv->cli.dev, "IPC", 3) == 0) {
1123
1124            mode = aDIR | aRONLY;
1125
1126            }
1127            else if (strncmp(srv->cli.dev, "LPT", 3) == 0) {
1128            
1129            if (strcmp(path, "\\") == 0) {
1130            
1131            mode = aDIR | aRONLY;
1132
1133            }
1134            else {
1135
1136            mode = aRONLY;
1137            smbc_stat_printjob(srv, path, &size, &m_time);
1138            c_time = a_time = m_time;
1139
1140            }
1141            else { */
1142
1143         if (!smbc_getatr(context, srv, path, &mode, &size, 
1144                          &c_time, &a_time, &m_time, &ino)) {
1145
1146                 errno = smbc_errno(context, &srv->cli);
1147                 return -1;
1148                 
1149         }
1150
1151         st->st_ino = ino;
1152
1153         smbc_setup_stat(context, st, path, size, mode);
1154
1155         st->st_atime = a_time;
1156         st->st_ctime = c_time;
1157         st->st_mtime = m_time;
1158         st->st_dev   = srv->dev;
1159
1160         return 0;
1161
1162 }
1163
1164 /*
1165  * Routine to stat a file given an fd
1166  */
1167
1168 static int smbc_fstat_ctx(SMBCCTX *context, SMBCFILE *file, struct stat *st)
1169 {
1170         time_t c_time, a_time, m_time;
1171         size_t size;
1172         uint16 mode;
1173         SMB_INO_T ino = 0;
1174
1175         if (!context || !context->_initialized) {
1176
1177                 errno = EINVAL;
1178                 return -1;
1179
1180         }
1181
1182         if (!file || !DLIST_CONTAINS(context->_files, file)) {
1183
1184                 errno = EBADF;
1185                 return -1;
1186
1187         }
1188
1189         if (!file->file) {
1190
1191                 return context->fstatdir(context, file, st);
1192
1193         }
1194
1195         if (!cli_qfileinfo(&file->srv->cli, file->cli_fd,
1196                            &mode, &size, &c_time, &a_time, &m_time, NULL, &ino) &&
1197             !cli_getattrE(&file->srv->cli, file->cli_fd,
1198                           &mode, &size, &c_time, &a_time, &m_time)) {
1199
1200                 errno = EINVAL;
1201                 return -1;
1202
1203         }
1204
1205         st->st_ino = ino;
1206
1207         smbc_setup_stat(context, st, file->fname, size, mode);
1208
1209         st->st_atime = a_time;
1210         st->st_ctime = c_time;
1211         st->st_mtime = m_time;
1212         st->st_dev = file->srv->dev;
1213
1214         return 0;
1215
1216 }
1217
1218 /*
1219  * Routine to open a directory
1220  *
1221  * We want to allow:
1222  *
1223  * smb: which should list all the workgroups available
1224  * smb:workgroup
1225  * smb:workgroup//server
1226  * smb://server
1227  * smb://server/share
1228  * smb://<IP-addr> which should list shares on server
1229  * smb://<IP-addr>/share which should list files on share
1230  */
1231
1232 static void smbc_remove_dir(SMBCFILE *dir)
1233 {
1234         struct smbc_dir_list *d,*f;
1235
1236         d = dir->dir_list;
1237         while (d) {
1238
1239                 f = d; d = d->next;
1240
1241                 SAFE_FREE(f->dirent);
1242                 SAFE_FREE(f);
1243
1244         }
1245
1246         dir->dir_list = dir->dir_end = dir->dir_next = NULL;
1247
1248 }
1249
1250 static int add_dirent(SMBCFILE *dir, const char *name, const char *comment, uint32 type)
1251 {
1252         struct smbc_dirent *dirent;
1253         int size;
1254
1255         /*
1256          * Allocate space for the dirent, which must be increased by the 
1257          * size of the name and the comment and 1 for the null on the comment.
1258          * The null on the name is already accounted for.
1259          */
1260
1261         size = sizeof(struct smbc_dirent) + (name?strlen(name):0) +
1262                 (comment?strlen(comment):0) + 1; 
1263     
1264         dirent = malloc(size);
1265
1266         if (!dirent) {
1267
1268                 dir->dir_error = ENOMEM;
1269                 return -1;
1270
1271         }
1272
1273         ZERO_STRUCTP(dirent);
1274
1275         ZERO_STRUCTP(dirent);
1276
1277
1278         if (dir->dir_list == NULL) {
1279
1280                 dir->dir_list = malloc(sizeof(struct smbc_dir_list));
1281                 if (!dir->dir_list) {
1282
1283                         SAFE_FREE(dirent);
1284                         dir->dir_error = ENOMEM;
1285                         return -1;
1286
1287                 }
1288                 ZERO_STRUCTP(dir->dir_list);
1289
1290                 dir->dir_end = dir->dir_next = dir->dir_list;
1291   
1292         }
1293         else {
1294
1295                 dir->dir_end->next = malloc(sizeof(struct smbc_dir_list));
1296                 
1297                 if (!dir->dir_end->next) {
1298                         
1299                         SAFE_FREE(dirent);
1300                         dir->dir_error = ENOMEM;
1301                         return -1;
1302
1303                 }
1304                 ZERO_STRUCTP(dir->dir_end->next);
1305
1306                 dir->dir_end = dir->dir_end->next;
1307
1308         }
1309
1310         dir->dir_end->next = NULL;
1311         dir->dir_end->dirent = dirent;
1312         
1313         dirent->smbc_type = type;
1314         dirent->namelen = (name?strlen(name):0);
1315         dirent->commentlen = (comment?strlen(comment):0);
1316         dirent->dirlen = size;
1317   
1318         strncpy(dirent->name, (name?name:""), dirent->namelen + 1);
1319
1320         dirent->comment = (char *)(&dirent->name + dirent->namelen + 1);
1321         strncpy(dirent->comment, (comment?comment:""), dirent->commentlen + 1);
1322
1323         return 0;
1324
1325 }
1326
1327 static void
1328 list_fn(const char *name, uint32 type, const char *comment, void *state)
1329 {
1330         SMBCFILE *dir = (SMBCFILE *)state;
1331         int dirent_type;
1332
1333         /* We need to process the type a little ... */
1334
1335         if (dir->dir_type == SMBC_FILE_SHARE) {
1336                 
1337                 switch (type) {
1338                 case 0: /* Directory tree */
1339                         dirent_type = SMBC_FILE_SHARE;
1340                         break;
1341
1342                 case 1:
1343                         dirent_type = SMBC_PRINTER_SHARE;
1344                         break;
1345
1346                 case 2:
1347                         dirent_type = SMBC_COMMS_SHARE;
1348                         break;
1349
1350                 case 3:
1351                         dirent_type = SMBC_IPC_SHARE;
1352                         break;
1353
1354                 default:
1355                         dirent_type = SMBC_FILE_SHARE; /* FIXME, error? */
1356                         break;
1357                 }
1358                 ZERO_STRUCTP(dir->dir_list);
1359
1360         }
1361         else dirent_type = dir->dir_type;
1362
1363         if (add_dirent(dir, name, comment, dirent_type) < 0) {
1364
1365                 /* An error occurred, what do we do? */
1366                 /* FIXME: Add some code here */
1367
1368         }
1369
1370 }
1371
1372 static void
1373 dir_list_fn(file_info *finfo, const char *mask, void *state)
1374 {
1375
1376         if (add_dirent((SMBCFILE *)state, finfo->name, "", 
1377                        (finfo->mode&aDIR?SMBC_DIR:SMBC_FILE)) < 0) {
1378
1379                 /* Handle an error ... */
1380
1381                 /* FIXME: Add some code ... */
1382
1383         } 
1384
1385 }
1386
1387 static SMBCFILE *smbc_opendir_ctx(SMBCCTX *context, const char *fname)
1388 {
1389         fstring server, share, user, password, workgroup;
1390         pstring path;
1391         SMBCSRV *srv  = NULL;
1392         SMBCFILE *dir = NULL;
1393         struct in_addr rem_ip;
1394         int slot = 0;
1395
1396         if (!context || !context->_initialized) {
1397
1398                 errno = EINVAL;
1399                 return NULL;
1400
1401         }
1402
1403         if (!fname) {
1404     
1405                 errno = EINVAL;
1406                 return NULL;
1407
1408         }
1409
1410         if (smbc_parse_path(context, fname, server, share, path, user, password)) {
1411
1412                 errno = EINVAL;
1413                 return NULL;
1414
1415         }
1416
1417         if (user[0] == (char)0) pstrcpy(user, context->user);
1418
1419         pstrcpy(workgroup, context->workgroup);
1420
1421         dir = malloc(sizeof(*dir));
1422
1423         if (!dir) {
1424
1425                 errno = ENOMEM;
1426                 return NULL;
1427
1428         }
1429
1430         ZERO_STRUCTP(dir);
1431
1432         dir->cli_fd   = 0;
1433         dir->fname    = strdup(fname);
1434         dir->srv      = NULL;
1435         dir->offset   = 0;
1436         dir->file     = False;
1437         dir->dir_list = dir->dir_next = dir->dir_end = NULL;
1438
1439         if (server[0] == (char)0) {
1440
1441                 if (share[0] != (char)0 || path[0] != (char)0) {
1442     
1443                         errno = EINVAL;
1444                         if (dir) {
1445                                 SAFE_FREE(dir->fname);
1446                                 SAFE_FREE(dir);
1447                         }
1448                         return NULL;
1449
1450                 }
1451
1452                 /* We have server and share and path empty ... so list the workgroups */
1453                 /* first try to get the LMB for our workgroup, and if that fails,     */
1454                 /* try the DMB                                                        */
1455
1456                 if (!(resolve_name(context->workgroup, &rem_ip, 0x1d) ||
1457                       resolve_name(context->workgroup, &rem_ip, 0x1b))) {
1458       
1459                         errno = EINVAL;  /* Something wrong with smb.conf? */
1460                         return NULL;
1461
1462                 }
1463
1464                 dir->dir_type = SMBC_WORKGROUP;
1465
1466                 /* find the name of the server ... */
1467
1468                 if (!name_status_find("*", 0, 0, rem_ip, server)) {
1469
1470                         DEBUG(0,("Could not get the name of local/domain master browser for server %s\n", server));
1471                         errno = EINVAL;
1472                         return NULL;
1473
1474                 }
1475
1476                 /*
1477                  * Get a connection to IPC$ on the server if we do not already have one
1478                  */
1479
1480                 srv = smbc_server(context, server, "IPC$", workgroup, user, password);
1481
1482                 if (!srv) {
1483
1484                         if (dir) {
1485                                 SAFE_FREE(dir->fname);
1486                                 SAFE_FREE(dir);
1487                         }
1488                         
1489                         return NULL;
1490
1491                 }
1492                 ZERO_STRUCTP(dir->dir_end);
1493
1494                 dir->srv = srv;
1495
1496                 /* Now, list the stuff ... */
1497
1498                 if (!cli_NetServerEnum(&srv->cli, workgroup, 0x80000000, list_fn,
1499                                        (void *)dir)) {
1500
1501                         if (dir) {
1502                                 SAFE_FREE(dir->fname);
1503                                 SAFE_FREE(dir);
1504                         }
1505                         errno = cli_errno(&srv->cli);
1506
1507                         return NULL;
1508
1509                 }
1510         }
1511         else { /* Server not an empty string ... Check the rest and see what gives */
1512
1513                 if (share[0] == (char)0) {
1514
1515                         if (path[0] != (char)0) { /* Should not have empty share with path */
1516
1517                                 errno = EINVAL;
1518                                 if (dir) {
1519                                         SAFE_FREE(dir->fname);
1520                                         SAFE_FREE(dir);
1521                                 }
1522                                 return NULL;
1523         
1524                         }
1525
1526                         /* Check to see if <server><1D>, <server><1B>, or <server><20> translates */
1527                         /* However, we check to see if <server> is an IP address first */
1528
1529                         if (!is_ipaddress(server) &&  /* Not an IP addr so check next */
1530                             (resolve_name(server, &rem_ip, 0x1d) ||   /* Found LMB */
1531                                     resolve_name(server, &rem_ip, 0x1b) )) { /* Found DMB */
1532                                 pstring buserver;
1533
1534                                 dir->dir_type = SMBC_SERVER;
1535
1536                                 /*
1537                                  * Get the backup list ...
1538                                  */
1539
1540
1541                                 if (!name_status_find("*", 0, 0, rem_ip, buserver)) {
1542
1543                                         DEBUG(0, ("Could not get name of local/domain master browser for server %s\n", server));
1544                                         errno = EPERM;  /* FIXME, is this correct */
1545                                         return NULL;
1546
1547                                 }
1548
1549                                 /*
1550                                  * Get a connection to IPC$ on the server if we do not already have one
1551                                  */
1552
1553                                 srv = smbc_server(context, buserver, "IPC$", workgroup, user, password);
1554
1555                                 if (!srv) {
1556
1557                                         if (dir) {
1558                                                 SAFE_FREE(dir->fname);
1559                                                 SAFE_FREE(dir);
1560                                         }
1561                                         return NULL;
1562
1563                                 }
1564
1565                                 dir->srv = srv;
1566
1567                                 /* Now, list the servers ... */
1568
1569                                 if (!cli_NetServerEnum(&srv->cli, server, 0x0000FFFE, list_fn,
1570                                                        (void *)dir)) {
1571
1572                                         if (dir) {
1573                                                 SAFE_FREE(dir->fname);
1574                                                 SAFE_FREE(dir);
1575                                         }
1576                                         errno = cli_errno(&srv->cli);
1577                                         return NULL;
1578                                         
1579                                 }
1580
1581                         }
1582                         else {
1583
1584                                 if (resolve_name(server, &rem_ip, 0x20)) {
1585
1586                                         /* Now, list the shares ... */
1587
1588                                         dir->dir_type = SMBC_FILE_SHARE;
1589
1590                                         srv = smbc_server(context, server, "IPC$", workgroup, user, password);
1591
1592                                         if (!srv) {
1593
1594                                                 if (dir) {
1595                                                         SAFE_FREE(dir->fname);
1596                                                         SAFE_FREE(dir);
1597                                                 }
1598                                                 return NULL;
1599
1600                                         }
1601
1602                                         dir->srv = srv;
1603
1604                                         /* Now, list the servers ... */
1605
1606                                         if (cli_RNetShareEnum(&srv->cli, list_fn, 
1607                                                               (void *)dir) < 0) {
1608
1609                                                 errno = cli_errno(&srv->cli);
1610                                                 if (dir) {
1611                                                         SAFE_FREE(dir->fname);
1612                                                         SAFE_FREE(dir);
1613                                                 }
1614                                                 return NULL;
1615
1616                                         }
1617
1618                                 }
1619                                 else {
1620
1621                                         errno = ENODEV;   /* Neither the workgroup nor server exists */
1622                                         if (dir) {
1623                                                 SAFE_FREE(dir->fname);
1624                                                 SAFE_FREE(dir);
1625                                         }
1626                                         return NULL;
1627
1628                                 }
1629
1630                         }
1631
1632                 }
1633                 else { /* The server and share are specified ... work from there ... */
1634
1635                         /* Well, we connect to the server and list the directory */
1636
1637                         dir->dir_type = SMBC_FILE_SHARE;
1638
1639                         srv = smbc_server(context, server, share, workgroup, user, password);
1640
1641                         if (!srv) {
1642
1643                                 if (dir) {
1644                                         SAFE_FREE(dir->fname);
1645                                         SAFE_FREE(dir);
1646                                 }
1647                                 return NULL;
1648
1649                         }
1650
1651                         dir->srv = srv;
1652
1653                         /* Now, list the files ... */
1654
1655                         pstrcat(path, "\\*");
1656
1657                         if (cli_list(&srv->cli, path, aDIR | aSYSTEM | aHIDDEN, dir_list_fn, 
1658                                      (void *)dir) < 0) {
1659
1660                                 if (dir) {
1661                                         SAFE_FREE(dir->fname);
1662                                         SAFE_FREE(dir);
1663                                 }
1664                                 errno = smbc_errno(context, &srv->cli);
1665                                 return NULL;
1666
1667                         }
1668                 }
1669
1670         }
1671
1672         DLIST_ADD(context->_files, dir);
1673         return dir;
1674
1675 }
1676
1677 /*
1678  * Routine to close a directory
1679  */
1680
1681 static int smbc_closedir_ctx(SMBCCTX *context, SMBCFILE *dir)
1682 {
1683
1684         if (!context || !context->_initialized) {
1685
1686                 errno = EINVAL;
1687                 return -1;
1688
1689         }
1690
1691         if (!dir || !DLIST_CONTAINS(context->_files, dir)) {
1692
1693                 errno = EBADF;
1694                 return -1;
1695     
1696         }
1697
1698         smbc_remove_dir(dir); /* Clean it up */
1699
1700         DLIST_REMOVE(context->_files, dir);
1701
1702         if (dir) {
1703
1704                 SAFE_FREE(dir->fname);
1705                 SAFE_FREE(dir);    /* Free the space too */
1706
1707         }
1708
1709         return 0;
1710
1711 }
1712
1713 /*
1714  * Routine to get a directory entry
1715  */
1716
1717 struct smbc_dirent *smbc_readdir_ctx(SMBCCTX *context, SMBCFILE *dir)
1718 {
1719         struct smbc_dirent *dirp, *dirent;
1720
1721         /* Check that all is ok first ... */
1722
1723         if (!context || !context->_initialized) {
1724
1725                 errno = EINVAL;
1726                 return NULL;
1727
1728         }
1729
1730         if (!dir || !DLIST_CONTAINS(context->_files, dir)) {
1731
1732                 errno = EBADF;
1733                 return NULL;
1734
1735         }
1736
1737         if (dir->file != False) { /* FIXME, should be dir, perhaps */
1738
1739                 errno = ENOTDIR;
1740                 return NULL;
1741
1742         }
1743
1744         if (!dir->dir_next)
1745                 return NULL;
1746         else {
1747
1748                 dirent = dir->dir_next->dirent;
1749
1750                 if (!dirent) {
1751
1752                         errno = ENOENT;
1753                         return NULL;
1754
1755                 }
1756
1757                 /* Hmmm, do I even need to copy it? */
1758
1759                 memcpy(context->_dirent, dirent, dirent->dirlen); /* Copy the dirent */
1760                 dirp = (struct smbc_dirent *)context->_dirent;
1761                 dirp->comment = (char *)(&dirp->name + dirent->namelen + 1);
1762                 dir->dir_next = dir->dir_next->next;
1763
1764                 return (struct smbc_dirent *)context->_dirent;
1765         }
1766
1767 }
1768
1769 /*
1770  * Routine to get directory entries
1771  */
1772
1773 static int smbc_getdents_ctx(SMBCCTX *context, SMBCFILE *dir, struct smbc_dirent *dirp, int count)
1774 {
1775         struct smbc_dir_list *dirlist;
1776         int rem = count, reqd;
1777         char *ndir = (char *)dirp;
1778
1779         /* Check that all is ok first ... */
1780
1781         if (!context || !context->_initialized) {
1782
1783                 errno = EINVAL;
1784                 return -1;
1785
1786         }
1787
1788         if (!dir || !DLIST_CONTAINS(context->_files, dir)) {
1789
1790                 errno = EBADF;
1791                 return -1;
1792     
1793         }
1794
1795         if (dir->file != False) { /* FIXME, should be dir, perhaps */
1796
1797                 errno = ENOTDIR;
1798                 return -1;
1799
1800         }
1801
1802         /* 
1803          * Now, retrieve the number of entries that will fit in what was passed
1804          * We have to figure out if the info is in the list, or we need to 
1805          * send a request to the server to get the info.
1806          */
1807
1808         while ((dirlist = dir->dir_next)) {
1809                 struct smbc_dirent *dirent;
1810
1811                 if (!dirlist->dirent) {
1812
1813                         errno = ENOENT;  /* Bad error */
1814                         return -1;
1815
1816                 }
1817
1818                 if (rem < (reqd = (sizeof(struct smbc_dirent) + dirlist->dirent->namelen + 
1819                                    dirlist->dirent->commentlen + 1))) {
1820
1821                         if (rem < count) { /* We managed to copy something */
1822
1823                                 errno = 0;
1824                                 return count - rem;
1825
1826                         }
1827                         else { /* Nothing copied ... */
1828
1829                                 errno = EINVAL;  /* Not enough space ... */
1830                                 return -1;
1831
1832                         }
1833
1834                 }
1835
1836                 dirent = dirlist->dirent;
1837
1838                 memcpy(ndir, dirent, reqd); /* Copy the data in ... */
1839     
1840                 ((struct smbc_dirent *)ndir)->comment = 
1841                         (char *)(&((struct smbc_dirent *)ndir)->name + dirent->namelen + 1);
1842
1843                 ndir += reqd;
1844
1845                 rem -= reqd;
1846
1847                 dir->dir_next = dirlist = dirlist -> next;
1848         }
1849
1850         if (rem == count)
1851                 return 0;
1852         else 
1853                 return count - rem;
1854
1855 }
1856
1857 /*
1858  * Routine to create a directory ...
1859  */
1860
1861 static int smbc_mkdir_ctx(SMBCCTX *context, const char *fname, mode_t mode)
1862 {
1863         SMBCSRV *srv;
1864         fstring server, share, user, password, workgroup;
1865         pstring path;
1866
1867         if (!context || !context->_initialized) {
1868
1869                 errno = EINVAL;
1870                 return -1;
1871
1872         }
1873
1874         if (!fname) {
1875
1876                 errno = EINVAL;
1877                 return -1;
1878
1879         }
1880   
1881         DEBUG(4, ("smbc_mkdir(%s)\n", fname));
1882
1883         smbc_parse_path(context, fname, server, share, path, user, password); /*FIXME, errors*/
1884
1885         if (user[0] == (char)0) pstrcpy(user, context->user);
1886
1887         pstrcpy(workgroup, context->workgroup);
1888
1889         srv = smbc_server(context, server, share, workgroup, user, password);
1890
1891         if (!srv) {
1892
1893                 return -1;  /* errno set by smbc_server */
1894
1895         }
1896
1897         /* if (strncmp(srv->cli.dev, "IPC", 3) == 0) {
1898
1899            mode = aDIR | aRONLY;
1900
1901            }
1902            else if (strncmp(srv->cli.dev, "LPT", 3) == 0) {
1903
1904            if (strcmp(path, "\\") == 0) {
1905
1906            mode = aDIR | aRONLY;
1907
1908            }
1909            else {
1910
1911            mode = aRONLY;
1912            smbc_stat_printjob(srv, path, &size, &m_time);
1913            c_time = a_time = m_time;
1914
1915            }
1916            else { */
1917
1918         if (!cli_mkdir(&srv->cli, path)) {
1919
1920                 errno = smbc_errno(context, &srv->cli);
1921                 return -1;
1922
1923         } 
1924
1925         return 0;
1926
1927 }
1928
1929 /*
1930  * Our list function simply checks to see if a directory is not empty
1931  */
1932
1933 static int smbc_rmdir_dirempty = True;
1934
1935 static void rmdir_list_fn(file_info *finfo, const char *mask, void *state)
1936 {
1937
1938         if (strncmp(finfo->name, ".", 1) != 0 && strncmp(finfo->name, "..", 2) != 0)
1939                 smbc_rmdir_dirempty = False;
1940
1941 }
1942
1943 /*
1944  * Routine to remove a directory
1945  */
1946
1947 static int smbc_rmdir_ctx(SMBCCTX *context, const char *fname)
1948 {
1949         SMBCSRV *srv;
1950         fstring server, share, user, password, workgroup;
1951         pstring path;
1952
1953         if (!context || !context->_initialized) {
1954
1955                 errno = EINVAL;
1956                 return -1;
1957
1958         }
1959
1960         if (!fname) {
1961
1962                 errno = EINVAL;
1963                 return -1;
1964
1965         }
1966   
1967         DEBUG(4, ("smbc_rmdir(%s)\n", fname));
1968
1969         smbc_parse_path(context, fname, server, share, path, user, password); /*FIXME, errors*/
1970
1971         if (user[0] == (char)0) pstrcpy(user, context->user);
1972
1973         pstrcpy(workgroup, context->workgroup);
1974
1975         srv = smbc_server(context, server, share, workgroup, user, password);
1976
1977         if (!srv) {
1978
1979                 return -1;  /* errno set by smbc_server */
1980
1981         }
1982
1983         /* if (strncmp(srv->cli.dev, "IPC", 3) == 0) {
1984
1985            mode = aDIR | aRONLY;
1986
1987            }
1988            else if (strncmp(srv->cli.dev, "LPT", 3) == 0) {
1989
1990            if (strcmp(path, "\\") == 0) {
1991
1992            mode = aDIR | aRONLY;
1993
1994            }
1995            else {
1996
1997            mode = aRONLY;
1998            smbc_stat_printjob(srv, path, &size, &m_time);
1999            c_time = a_time = m_time;
2000            
2001            }
2002            else { */
2003
2004         if (!cli_rmdir(&srv->cli, path)) {
2005
2006                 errno = smbc_errno(context, &srv->cli);
2007
2008                 if (errno == EACCES) {  /* Check if the dir empty or not */
2009
2010                         pstring lpath; /* Local storage to avoid buffer overflows */
2011
2012                         smbc_rmdir_dirempty = True;  /* Make this so ... */
2013
2014                         pstrcpy(lpath, path);
2015                         pstrcat(lpath, "\\*");
2016
2017                         if (cli_list(&srv->cli, lpath, aDIR | aSYSTEM | aHIDDEN, rmdir_list_fn,
2018                                      NULL) < 0) {
2019
2020                                 /* Fix errno to ignore latest error ... */
2021
2022                                 DEBUG(5, ("smbc_rmdir: cli_list returned an error: %d\n", 
2023                                           smbc_errno(context, &srv->cli)));
2024                                 errno = EACCES;
2025
2026                         }
2027
2028                         if (smbc_rmdir_dirempty)
2029                                 errno = EACCES;
2030                         else
2031                                 errno = ENOTEMPTY;
2032
2033                 }
2034
2035                 return -1;
2036
2037         } 
2038
2039         return 0;
2040
2041 }
2042
2043 /*
2044  * Routine to return the current directory position
2045  */
2046
2047 static off_t smbc_telldir_ctx(SMBCCTX *context, SMBCFILE *dir)
2048 {
2049
2050         if (!context || !context->_initialized) {
2051
2052                 errno = EINVAL;
2053                 return -1;
2054
2055         }
2056
2057         if (!dir || !DLIST_CONTAINS(context->_files, dir)) {
2058
2059                 errno = EBADF;
2060                 return -1;
2061
2062         }
2063
2064         if (dir->file != False) { /* FIXME, should be dir, perhaps */
2065
2066                 errno = ENOTDIR;
2067                 return -1;
2068
2069         }
2070
2071         return (off_t) dir->dir_next;
2072
2073 }
2074
2075 /*
2076  * A routine to run down the list and see if the entry is OK
2077  */
2078
2079 struct smbc_dir_list *smbc_check_dir_ent(struct smbc_dir_list *list, 
2080                                          struct smbc_dirent *dirent)
2081 {
2082
2083         /* Run down the list looking for what we want */
2084
2085         if (dirent) {
2086
2087                 struct smbc_dir_list *tmp = list;
2088
2089                 while (tmp) {
2090
2091                         if (tmp->dirent == dirent)
2092                                 return tmp;
2093
2094                         tmp = tmp->next;
2095
2096                 }
2097
2098         }
2099
2100         return NULL;  /* Not found, or an error */
2101
2102 }
2103
2104
2105 /*
2106  * Routine to seek on a directory
2107  */
2108
2109 static int smbc_lseekdir_ctx(SMBCCTX *context, SMBCFILE *dir, off_t offset)
2110 {
2111         struct smbc_dirent *dirent = (struct smbc_dirent *)offset;
2112         struct smbc_dir_list *list_ent = NULL;
2113
2114         if (!context || !context->_initialized) {
2115
2116                 errno = EINVAL;
2117                 return -1;
2118
2119         }
2120
2121         if (dir->file != False) { /* FIXME, should be dir, perhaps */
2122
2123                 errno = ENOTDIR;
2124                 return -1;
2125
2126         }
2127
2128         /* Now, check what we were passed and see if it is OK ... */
2129
2130         if (dirent == NULL) {  /* Seek to the begining of the list */
2131
2132                 dir->dir_next = dir->dir_list;
2133                 return 0;
2134
2135         }
2136
2137         /* Now, run down the list and make sure that the entry is OK       */
2138         /* This may need to be changed if we change the format of the list */
2139
2140         if ((list_ent = smbc_check_dir_ent(dir->dir_list, dirent)) == NULL) {
2141
2142                 errno = EINVAL;   /* Bad entry */
2143                 return -1;
2144
2145         }
2146
2147         dir->dir_next = list_ent;
2148
2149         return 0; 
2150
2151 }
2152
2153 /*
2154  * Routine to fstat a dir
2155  */
2156
2157 static int smbc_fstatdir_ctx(SMBCCTX *context, SMBCFILE *dir, struct stat *st)
2158 {
2159
2160         if (context || !context->_initialized) {
2161
2162                 errno = EINVAL;
2163                 return -1;
2164
2165         }
2166
2167         /* No code yet ... */
2168
2169         return 0;
2170
2171 }
2172
2173 /*
2174  * Open a print file to be written to by other calls
2175  */
2176
2177 static SMBCFILE *smbc_open_print_job_ctx(SMBCCTX *context, const char *fname)
2178 {
2179         fstring server, share, user, password;
2180         pstring path;
2181         
2182         if (!context || context->_initialized) {
2183
2184                 errno = EINVAL;
2185                 return NULL;
2186     
2187         }
2188
2189         if (!fname) {
2190
2191                 errno = EINVAL;
2192                 return NULL;
2193
2194         }
2195   
2196         DEBUG(4, ("smbc_open_print_job_ctx(%s)\n", fname));
2197
2198         smbc_parse_path(context, fname, server, share, path, user, password); /*FIXME, errors*/
2199
2200         /* What if the path is empty, or the file exists? */
2201
2202         return context->open(context, fname, O_WRONLY, 666);
2203
2204 }
2205
2206 /*
2207  * Routine to print a file on a remote server ...
2208  *
2209  * We open the file, which we assume to be on a remote server, and then
2210  * copy it to a print file on the share specified by printq.
2211  */
2212
2213 static int smbc_print_file_ctx(SMBCCTX *c_file, const char *fname, SMBCCTX *c_print, const char *printq)
2214 {
2215         SMBCFILE *fid1, *fid2;
2216         int bytes, saverr, tot_bytes = 0;
2217         char buf[4096];
2218
2219         if (!c_file || !c_file->_initialized || !c_print ||
2220             !c_print->_initialized) {
2221
2222                 errno = EINVAL;
2223                 return -1;
2224
2225         }
2226
2227         if (!fname && !printq) {
2228
2229                 errno = EINVAL;
2230                 return -1;
2231
2232         }
2233
2234         /* Try to open the file for reading ... */
2235
2236         if ((fid1 = c_file->open(c_file, fname, O_RDONLY, 0666)) < 0) {
2237                 
2238                 DEBUG(3, ("Error, fname=%s, errno=%i\n", fname, errno));
2239                 return -1;  /* smbc_open sets errno */
2240                 
2241         }
2242
2243         /* Now, try to open the printer file for writing */
2244
2245         if ((fid2 = c_print->open_print_job(c_print, printq)) < 0) {
2246
2247                 saverr = errno;  /* Save errno */
2248                 c_file->close(c_file, fid1);
2249                 errno = saverr;
2250                 return -1;
2251
2252         }
2253
2254         while ((bytes = c_file->read(c_file, fid1, buf, sizeof(buf))) > 0) {
2255
2256                 tot_bytes += bytes;
2257
2258                 if ((c_print->write(c_print, fid2, buf, bytes)) < 0) {
2259
2260                         saverr = errno;
2261                         c_file->close(c_file, fid1);
2262                         c_print->close(c_print, fid2);
2263                         errno = saverr;
2264
2265                 }
2266
2267         }
2268
2269         saverr = errno;
2270
2271         c_file->close(c_file, fid1);  /* We have to close these anyway */
2272         c_print->close(c_print, fid2);
2273
2274         if (bytes < 0) {
2275
2276                 errno = saverr;
2277                 return -1;
2278
2279         }
2280
2281         return tot_bytes;
2282
2283 }
2284
2285 /*
2286  * Routine to list print jobs on a printer share ...
2287  */
2288
2289 static int smbc_list_print_jobs_ctx(SMBCCTX *context, const char *fname, void (*fn)(struct print_job_info *))
2290 {
2291         SMBCSRV *srv;
2292         fstring server, share, user, password, workgroup;
2293         pstring path;
2294
2295         if (!context || !context->_initialized) {
2296
2297                 errno = EINVAL;
2298                 return -1;
2299
2300         }
2301
2302         if (!fname) {
2303                 
2304                 errno = EINVAL;
2305                 return -1;
2306
2307         }
2308   
2309         DEBUG(4, ("smbc_list_print_jobs(%s)\n", fname));
2310
2311         smbc_parse_path(context, fname, server, share, path, user, password); /*FIXME, errors*/
2312
2313         if (user[0] == (char)0) pstrcpy(user, context->user);
2314         
2315         pstrcpy(workgroup, context->workgroup);
2316
2317         srv = smbc_server(context, server, share, workgroup, user, password);
2318
2319         if (!srv) {
2320
2321                 return -1;  /* errno set by smbc_server */
2322
2323         }
2324
2325         if (cli_print_queue(&srv->cli, fn) < 0) {
2326
2327                 errno = smbc_errno(context, &srv->cli);
2328                 return -1;
2329
2330         }
2331         
2332         return 0;
2333
2334 }
2335
2336 /*
2337  * Delete a print job from a remote printer share
2338  */
2339
2340 static int smbc_unlink_print_job_ctx(SMBCCTX *context, const char *fname, int id)
2341 {
2342         SMBCSRV *srv;
2343         fstring server, share, user, password, workgroup;
2344         pstring path;
2345         int err;
2346
2347         if (!context || !context->_initialized) {
2348
2349                 errno = EINVAL;
2350                 return -1;
2351
2352         }
2353
2354         if (!fname) {
2355
2356                 errno = EINVAL;
2357                 return -1;
2358
2359         }
2360   
2361         DEBUG(4, ("smbc_unlink_print_job(%s)\n", fname));
2362
2363         smbc_parse_path(context, fname, server, share, path, user, password); /*FIXME, errors*/
2364
2365         if (user[0] == (char)0) pstrcpy(user, context->user);
2366
2367         pstrcpy(workgroup, context->workgroup);
2368
2369         srv = smbc_server(context, server, share, workgroup, user, password);
2370
2371         if (!srv) {
2372
2373                 return -1;  /* errno set by smbc_server */
2374
2375         }
2376
2377         if ((err = cli_printjob_del(&srv->cli, id)) != 0) {
2378
2379                 if (err < 0)
2380                         errno = smbc_errno(context, &srv->cli);
2381                 else if (err == ERRnosuchprintjob)
2382                         errno = EINVAL;
2383                 return -1;
2384
2385         }
2386
2387         return 0;
2388
2389 }
2390
2391 /*
2392  * Get a new empty handle to fill in with your own info 
2393  */
2394 SMBCCTX * smbc_new_context(void)
2395 {
2396         SMBCCTX * context;
2397
2398         context = malloc(sizeof(*context));
2399         if (!context) {
2400                 errno = ENOMEM;
2401                 return NULL;
2402         }
2403         
2404         ZERO_STRUCTP(context);
2405
2406         /* ADD REASONABLE DEFAULTS */
2407         context->debug            = 0;
2408         context->timeout          = 20000; /* 20 seconds */
2409
2410         context->open             = smbc_open_ctx;
2411         context->creat            = smbc_creat_ctx;
2412         context->read             = smbc_read_ctx;
2413         context->write            = smbc_write_ctx;
2414         context->close            = smbc_close_ctx;
2415         context->unlink           = smbc_unlink_ctx;
2416         context->rename           = smbc_rename_ctx;
2417         context->lseek            = smbc_lseek_ctx;
2418         context->stat             = smbc_stat_ctx;
2419         context->fstat            = smbc_fstat_ctx;
2420         context->opendir          = smbc_opendir_ctx;
2421         context->closedir         = smbc_closedir_ctx;
2422         context->readdir          = smbc_readdir_ctx;
2423         context->getdents         = smbc_getdents_ctx;
2424         context->mkdir            = smbc_mkdir_ctx;
2425         context->rmdir            = smbc_rmdir_ctx;
2426         context->telldir          = smbc_telldir_ctx;
2427         context->lseekdir         = smbc_lseekdir_ctx;
2428         context->fstatdir         = smbc_fstatdir_ctx;
2429         context->open_print_job   = smbc_open_print_job_ctx;
2430         context->print_file       = smbc_print_file_ctx;
2431         context->list_print_jobs  = smbc_list_print_jobs_ctx;
2432         context->unlink_print_job = smbc_unlink_print_job_ctx;
2433
2434         context->callbacks.check_server_fn      = smbc_check_server;
2435         context->callbacks.remove_unused_server_fn = smbc_remove_unused_server;
2436
2437         smbc_default_cache_functions(context);
2438
2439         return context;
2440 }
2441
2442 /* 
2443  * Free a context
2444  *
2445  * Returns 0 on success. Otherwise returns 1, the SMBCCTX is _not_ freed 
2446  * and thus you'll be leaking memory if not handled properly.
2447  *
2448  */
2449 int smbc_free_context(SMBCCTX * context, int shutdown_ctx)
2450 {
2451         if (!context) {
2452                 errno = EBADF;
2453                 return 1;
2454         }
2455         
2456         if (shutdown_ctx) {
2457                 SMBCFILE * f;
2458                 DEBUG(1,("Performing aggressive shutdown.\n"));
2459                 
2460                 f = context->_files;
2461                 while (f) {
2462                         context->close(context, f);
2463                         f = f->next;
2464                 }
2465                 context->_files = NULL;
2466
2467                 /* First try to remove the servers the nice way. */
2468                 if (context->callbacks.purge_cached_fn(context)) {
2469                         SMBCSRV * s;
2470                         DEBUG(1, ("Could not purge all servers, Nice way shutdown failed.\n"));
2471                         s = context->_servers;
2472                         while (s) {
2473                                 cli_shutdown(&s->cli);
2474                                 context->callbacks.remove_cached_srv_fn(context, s);
2475                                 SAFE_FREE(s);
2476                                 s = s->next;
2477                         }
2478                         context->_servers = NULL;
2479                 }
2480         }
2481         else {
2482                 /* This is the polite way */    
2483                 if (context->callbacks.purge_cached_fn(context)) {
2484                         DEBUG(1, ("Could not purge all servers, free_context failed.\n"));
2485                         errno = EBUSY;
2486                         return 1;
2487                 }
2488                 if (context->_servers) {
2489                         DEBUG(1, ("Active servers in context, free_context failed.\n"));
2490                         errno = EBUSY;
2491                         return 1;
2492                 }
2493                 if (context->_files) {
2494                         DEBUG(1, ("Active files in context, free_context failed.\n"));
2495                         errno = EBUSY;
2496                         return 1;
2497                 }               
2498         }
2499
2500         /* Things we have to clean up */
2501         SAFE_FREE(context->workgroup);
2502         SAFE_FREE(context->netbios_name);
2503         SAFE_FREE(context->user);
2504         
2505         DEBUG(3, ("Context %p succesfully freed\n", context));
2506         SAFE_FREE(context);
2507         return 0;
2508 }
2509
2510
2511 /*
2512  * Initialise the library etc 
2513  *
2514  * We accept a struct containing handle information.
2515  * valid values for info->debug from 0 to 100,
2516  * and insist that info->fn must be non-null.
2517  */
2518 SMBCCTX * smbc_init_context(SMBCCTX * context)
2519 {
2520         pstring conf;
2521         int pid;
2522         char *user = NULL, *home = NULL;
2523
2524         if (!context) {
2525                 errno = EBADF;
2526                 return NULL;
2527         }
2528
2529         /* Do not initialise the same client twice */
2530         if (context->_initialized) { 
2531                 return 0;
2532         }
2533
2534         if (!context->callbacks.auth_fn || context->debug < 0 || context->debug > 100) {
2535
2536                 errno = EINVAL;
2537                 return NULL;
2538
2539         }
2540
2541         if (!smbc_initialized) {
2542                 /* Do some library wide intialisations the first time we get called */
2543
2544                 /* Do we still need this ? */
2545                 DEBUGLEVEL = 10;
2546                 
2547                 setup_logging( "libsmbclient", False);
2548
2549                 /* Here we would open the smb.conf file if needed ... */
2550                 
2551                 home = getenv("HOME");
2552
2553                 slprintf(conf, sizeof(conf), "%s/.smb/smb.conf", home);
2554                 
2555                 load_interfaces();  /* Load the list of interfaces ... */
2556                 
2557                 in_client = True; /* FIXME, make a param */
2558
2559                 if (!lp_load(conf, True, False, False)) {
2560
2561                         /*
2562                          * Hmmm, what the hell do we do here ... we could not parse the
2563                          * config file ... We must return an error ... and keep info around
2564                          * about why we failed
2565                          */
2566                         
2567                         errno = ENOENT; /* FIXME: Figure out the correct error response */
2568                         return NULL;
2569                 }
2570
2571                 reopen_logs();  /* Get logging working ... */
2572         
2573                 /* 
2574                  * Block SIGPIPE (from lib/util_sock.c: write())  
2575                  * It is not needed and should not stop execution 
2576                  */
2577                 BlockSignals(True, SIGPIPE);
2578                 
2579                 /* Done with one-time initialisation */
2580                 smbc_initialized = 1; 
2581
2582         }
2583         
2584         if (!context->user) {
2585                 /*
2586                  * FIXME: Is this the best way to get the user info? 
2587                  */
2588                 user = getenv("USER");
2589                 /* walk around as "guest" if no username can be found */
2590                 if (!user) context->user = strdup("guest");
2591                 else context->user = strdup(user);
2592         }
2593
2594         if (!context->netbios_name) {
2595                 /*
2596                  * We try to get our netbios name from the config. If that fails we fall
2597                  * back on constructing our netbios name from our hostname etc
2598                  */
2599                 if (global_myname) {
2600                         context->netbios_name = strdup(global_myname);
2601                 }
2602                 else {
2603                         /*
2604                          * Hmmm, I want to get hostname as well, but I am too lazy for the moment
2605                          */
2606                         pid = sys_getpid();
2607                         context->netbios_name = malloc(17);
2608                         if (!context->netbios_name) {
2609                                 errno = ENOMEM;
2610                                 return NULL;
2611                         }
2612                         slprintf(context->netbios_name, 16, "smbc%s%d", context->user, pid);
2613                 }
2614         }
2615         DEBUG(0,("Using netbios name %s.\n", context->netbios_name));
2616         
2617
2618         if (!context->workgroup) {
2619                 if (lp_workgroup()) {
2620                         context->workgroup = strdup(lp_workgroup());
2621                 }
2622                 else {
2623                         /* TODO: Think about a decent default workgroup */
2624                         context->workgroup = strdup("samba");
2625                 }
2626         }
2627         DEBUG(0,("Using workgroup %s.\n", context->workgroup));
2628                                         
2629         /* shortest timeout is 1 second */
2630         if (context->timeout > 0 && context->timeout < 1000) 
2631                 context->timeout = 1000;
2632
2633         /*
2634          * FIXME: Should we check the function pointers here? 
2635          */
2636
2637         context->_initialized = 1;
2638         
2639         return context;
2640 }