- correct notifications
[wine] / files / smb.c
1 /*
2  * Copyright (C) 2002 Mike McCormack
3  *
4  * CIFS implementation for WINE
5  *
6  * This is a WINE's implementation of the Common Internet File System
7  *
8  * for specification see:
9  *
10  * http://www.codefx.com/CIFS_Explained.htm
11  * http://www.ubiqx.org/cifs/rfc-draft/rfc1002.html
12  * http://www.ubiqx.org/cifs/rfc-draft/draft-leach-cifs-v1-spec-02.html
13  * http://ubiqx.org/cifs/
14  * http://www.samba.org
15  *
16  * This library is free software; you can redistribute it and/or
17  * modify it under the terms of the GNU Lesser General Public
18  * License as published by the Free Software Foundation; either
19  * version 2.1 of the License, or (at your option) any later version.
20  *
21  * This library is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
24  * Lesser General Public License for more details.
25  *
26  * You should have received a copy of the GNU Lesser General Public
27  * License along with this library; if not, write to the Free Software
28  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
29  */
30
31 #include "config.h"
32 #include "wine/port.h"
33
34 #include <assert.h>
35 #include <ctype.h>
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <stdlib.h>
39 #include <stdio.h>
40 #include <string.h>
41 #ifdef HAVE_SYS_ERRNO_H
42 #include <sys/errno.h>
43 #endif
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #ifdef HAVE_SYS_MMAN_H
47 #include <sys/mman.h>
48 #endif
49 #include <sys/time.h>
50 #include <sys/poll.h>
51 #include <time.h>
52 #include <unistd.h>
53 #include <utime.h>
54 #ifdef HAVE_SYS_SOCKET_H
55 # include <sys/socket.h>
56 #endif
57 #include <sys/types.h>
58 #ifdef HAVE_NETINET_IN_SYSTM_H
59 #include <netinet/in_systm.h>
60 #endif
61 #ifdef HAVE_NETINET_IN_H
62 #include <netinet/in.h>
63 #endif
64 #ifdef HAVE_NETINET_IP_H
65 #include <netinet/ip.h>
66 #endif
67 #ifdef HAVE_ARPA_INET_H
68 #include <arpa/inet.h>
69 #endif
70
71 #include "winerror.h"
72 #include "windef.h"
73 #include "winbase.h"
74 #include "file.h"
75 #include "heap.h"
76
77 #include "smb.h"
78
79 #include "wine/server.h"
80 #include "wine/debug.h"
81
82 WINE_DEFAULT_DEBUG_CHANNEL(file);
83
84 #define MAX_HOST_NAME 15
85 #define NB_TIMEOUT 10000
86
87 USHORT SMB_MultiplexId = 0;
88
89 static int netbios_name(const char *p, unsigned char *buffer)
90 {
91     char ch;
92     int i,len=0;
93
94     buffer[len++]=' ';
95     for(i=0; i<=MAX_HOST_NAME; i++)
96     {
97         if(i<MAX_HOST_NAME)
98         {
99             if(*p)
100                 ch = *p++&0xdf; /* add character from hostname */
101             else
102                 ch = ' ';  /* add padding */
103         }
104         else
105             ch = 0;        /* add terminator */
106         buffer[len++] = ((ch&0xf0) >> 4) + 'A';
107         buffer[len++] =  (ch&0x0f) + 'A';
108     }
109     buffer[len++] = 0;     /* add second terminator */
110     return len;
111 }
112
113 static DWORD NB_NameReq(LPCSTR host, unsigned char *buffer, int len)
114 {
115     int trn = 1234,i=0;
116
117     NBR_ADDWORD(&buffer[i],trn);    i+=2;
118     NBR_ADDWORD(&buffer[i],0x0110); i+=2;
119     NBR_ADDWORD(&buffer[i],0x0001); i+=2;
120     NBR_ADDWORD(&buffer[i],0x0000); i+=2;
121     NBR_ADDWORD(&buffer[i],0x0000); i+=2;
122     NBR_ADDWORD(&buffer[i],0x0000); i+=2;
123
124     i += netbios_name(host,&buffer[i]);
125     
126     NBR_ADDWORD(&buffer[i],0x0020); i+=2;
127     NBR_ADDWORD(&buffer[i],0x0001); i+=2;
128
129     ERR("packet is %d bytes in length\n",i);
130
131     {
132         int j;
133         for(j=0; j<i; j++)
134             printf("%02x%c",buffer[j],(((j+1)%16)&&((j+1)!=j))?' ':'\n');
135     }
136
137     return i;
138 }
139
140 /* unc = \\hostname\share\file... */
141 static BOOL UNC_SplitName(LPSTR unc, LPSTR *hostname, LPSTR *share, LPSTR *file)
142 {
143     char *p;
144
145     ERR("%s\n",unc);
146
147     p = strchr(unc,'\\');
148     if(!p)
149         return FALSE;
150     p = strchr(p+1,'\\');
151     if(!p)
152         return FALSE;
153     *hostname=++p;
154
155     p = strchr(p,'\\');
156     if(!p)
157         return FALSE;
158     *p=0;
159     *share = ++p;
160
161     p = strchr(p,'\\');
162     if(!p)
163         return FALSE;
164     *p=0;
165     *file = ++p;
166
167     return TRUE;
168 }
169
170 static BOOL NB_Lookup(LPCSTR host, struct sockaddr_in *addr)
171 {
172     int fd,on=1,r,len;
173     struct pollfd fds;
174     struct sockaddr_in sin;
175     unsigned char buffer[256];
176
177     fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
178     if(fd<0)
179         return FALSE;
180
181     r = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof on);
182     if(r<0)
183         return FALSE;
184
185     if(0==inet_aton("255.255.255.255", (struct in_addr *)&sin.sin_addr.s_addr))
186     {
187         FIXME("Error getting bcast address\n");
188         return FALSE;
189     }
190     sin.sin_family = AF_INET;
191     sin.sin_port    = htons(137);
192
193     len = NB_NameReq(host,buffer,sizeof buffer);
194     if(len<=0)
195         return FALSE;
196
197     r = sendto(fd, buffer, len, 0, &sin, sizeof sin);
198     if(r<0)
199     {
200         FIXME("Error sending packet\n");
201         return FALSE;
202     }
203
204     fds.fd = fd;
205     fds.events = POLLIN;
206     fds.revents = 0;
207
208     r = poll(&fds,1,NB_TIMEOUT);
209     if(r!=1)
210         return FALSE;
211
212     close(fd);
213     TRACE("Got response!\n");
214     return TRUE;
215 }
216
217 #define NB_FIRST 0x40
218
219 #define NB_HDRSIZE 4
220
221 #define NB_SESSION_MSG 0x00
222 #define NB_SESSION_REQ 0x81
223
224 /* RFC 1002, section 4.3.2 */
225 static BOOL NB_SessionReq(int fd, char *called, char *calling)
226 {
227     unsigned char buffer[0x100];
228     int len = 0,r;
229     struct pollfd fds;
230
231     ERR("called %s, calling %s\n",called,calling);
232
233     buffer[0] = NB_SESSION_REQ;
234     buffer[1] = NB_FIRST;
235
236     netbios_name(called, &buffer[NB_HDRSIZE]);
237     len += 34;
238     netbios_name(calling, &buffer[NB_HDRSIZE+len]);
239     len += 34;
240
241     NBR_ADDWORD(&buffer[2],len);
242
243     /* for(i=0; i<(len+NB_HDRSIZE); i++)
244         DPRINTF("%02X%c",buffer[i],(((i+1)!=(len+4))&&((i+1)%16))?' ':'\n'); */
245
246     r = write(fd,buffer,len+4);
247     if(r<0)
248     {
249         ERR("Write failed\n");
250         return FALSE;
251     }
252
253     fds.fd = fd;
254     fds.events = POLLIN;
255     fds.revents = 0;
256
257     r = poll(&fds,1,NB_TIMEOUT);
258     if(r!=1)
259     {
260         ERR("Poll failed\n");
261         return FALSE;
262     }
263
264     r = read(fd, buffer, NB_HDRSIZE);
265     if((r!=NB_HDRSIZE) || (buffer[0]!=0x82))
266     {
267         ERR("Received %d bytes\n",r);
268         ERR("%02x %02x %02x %02x\n", buffer[0],buffer[1],buffer[2],buffer[3]);
269         return FALSE;
270     }
271
272     return TRUE;
273 }
274
275 static BOOL NB_SendData(int fd, unsigned char *data, int size)
276 {
277     unsigned char buffer[NB_HDRSIZE];
278     int r;
279
280     /* CHECK: is it always OK to do this in two writes?   */
281     /*        perhaps use scatter gather sendmsg instead? */
282
283     buffer[0] = NB_SESSION_MSG;
284     buffer[1] = NB_FIRST;
285     NBR_ADDWORD(&buffer[2],size);
286
287     r = write(fd, buffer, NB_HDRSIZE);
288     if(r!=NB_HDRSIZE)
289         return FALSE;
290
291     r = write(fd, data, size);
292     if(r!=size)
293     {
294         ERR("write failed\n");
295         return FALSE;
296     }
297
298     return TRUE;
299 }
300
301 static BOOL NB_RecvData(int fd, unsigned char *data, int *outlen)
302 {
303     int r,len;
304     unsigned char buffer[NB_HDRSIZE];
305
306     r = read(fd, buffer, NB_HDRSIZE);
307     if((r!=NB_HDRSIZE) || (buffer[0]!=NB_SESSION_MSG))
308     {
309         ERR("Received %d bytes\n",r);
310         return FALSE;
311     }
312
313     len = NBR_GETWORD(&buffer[2]);
314     r = read(fd, data, len);
315     if(len!=r)
316     {
317         ERR("Received %d bytes\n",r);
318         return FALSE;
319     }
320     *outlen = len;
321
322     return TRUE;
323 }
324
325 static BOOL NB_Transaction(int fd, unsigned char *buffer, int len, int *outlen)
326 {
327     int r,i;
328     struct pollfd fds;
329
330     DPRINTF("Sending request:\n");
331     for(i=0; i<len; i++)
332         DPRINTF("%02X%c",buffer[i],(((i+1)!=len)&&((i+1)%16))?' ':'\n');
333
334     if(!NB_SendData(fd,buffer,len))
335         return FALSE;
336
337     fds.fd = fd;
338     fds.events = POLLIN;
339     fds.revents = 0;
340
341     r = poll(&fds,1,NB_TIMEOUT);
342     if(r!=1)
343     {
344         ERR("Poll failed\n");
345         return FALSE;
346     }
347
348     if(!NB_RecvData(fd, buffer, outlen))
349         return FALSE;
350
351     len = *outlen;
352     DPRINTF("Got response:\n");
353     for(i=0; i<len; i++)
354         DPRINTF("%02X%c",buffer[i],(((i+1)!=len)&&((i+1)%16))?' ':'\n');
355
356     return TRUE;
357 }
358
359 #define SMB_ADDHEADER(b,l) { b[(l)++]=0xff; b[(l)++]='S'; b[(l)++]='M'; b[(l)++]='B'; }
360 #define SMB_ADDERRINFO(b,l) { b[(l)++]=0; b[(l)++]=0; b[(l)++]=0; b[(l)++]=0; }
361 #define SMB_ADDPADSIG(b,l) { memset(&b[l],0,12); l+=12; }
362
363 #define SMB_ERRCLASS 5
364 #define SMB_ERRCODE  7
365 #define SMB_TREEID  24
366 #define SMB_PROCID  26
367 #define SMB_USERID  28
368 #define SMB_PLEXID  30
369 #define SMB_PCOUNT  32
370 #define SMB_HDRSIZE 33
371
372 static DWORD SMB_GetError(unsigned char *buffer)
373 {
374     if(buffer[SMB_ERRCLASS]==0)
375         return STATUS_SUCCESS;
376     /* FIXME: return propper error codes */
377     return STATUS_INVALID_PARAMETER;
378 }
379
380 static int SMB_Header(unsigned char *buffer, unsigned char command, USHORT tree_id, USHORT user_id)
381 {
382     int len = 0;
383     DWORD id;
384
385     /* 0 */
386     SMB_ADDHEADER(buffer,len);  
387
388     /* 4 */
389     buffer[len++] = command;
390
391     /* 5 */
392     SMB_ADDERRINFO(buffer,len)
393
394     /* 9 */
395     buffer[len++] = 0x00; /* flags */
396     SMB_ADDWORD(&buffer[len],1); len += 2; /* flags2 */
397
398     /* 12 */
399     SMB_ADDPADSIG(buffer,len)
400
401     /* 24 */
402     SMB_ADDWORD(&buffer[len],tree_id); len += 2; /* treeid */
403     id = GetCurrentThreadId();
404     SMB_ADDWORD(&buffer[len],id); len += 2; /* process id */
405     SMB_ADDWORD(&buffer[len],user_id); len += 2; /* user id */
406     SMB_ADDWORD(&buffer[len],SMB_MultiplexId); len += 2; /* multiplex id */
407     SMB_MultiplexId++;
408
409     return len;
410 }
411
412 static const char *SMB_ProtocolDialect = "NT LM 0.12";
413 /* = "Windows for Workgroups 3.1a"; */
414
415 /* FIXME: support multiple SMB dialects */
416 static BOOL SMB_NegotiateProtocol(int fd, USHORT *dialect)
417 {
418     unsigned char buffer[0x100];
419     int buflen,len = 0;
420
421     ERR("\n");
422
423     memset(buffer,0,sizeof buffer);
424
425     len = SMB_Header(buffer, SMB_COM_NEGOTIATE, 0, 0);
426  
427     /* parameters */
428     buffer[len++] = 0; /* no parameters */
429
430     /* command buffer */
431     buflen = strlen(SMB_ProtocolDialect)+2;  /* include type and nul byte */
432     SMB_ADDWORD(&buffer[len],buflen); len += 2;
433
434     buffer[len] = 0x02;
435     strcpy(&buffer[len+1],SMB_ProtocolDialect);
436     len += buflen;
437
438     if(!NB_Transaction(fd, buffer, len, &len))
439     {
440         ERR("Failed\n");
441         return FALSE;
442     }
443
444     /* FIXME: check response */
445     if(SMB_GetError(buffer))
446     {
447         ERR("returned error\n");
448         return FALSE;
449     }
450
451     *dialect = 0;
452
453     return TRUE;
454 }
455
456 #define SMB_PARAM_COUNT(buffer) ((buffer)[SMB_PCOUNT])
457 #define SMB_PARAM(buffer,n) SMB_GETWORD(&(buffer)[SMB_HDRSIZE+2*(n)])
458 #define SMB_BUFFER_COUNT(buffer)  SMB_GETWORD(buffer+SMB_HDRSIZE+2*SMB_PARAM_COUNT(buffer))
459 #define SMB_BUFFER(buffer,n) ((buffer)[SMB_HDRSIZE + 2*SMB_PARAM_COUNT(buffer) + 2 + (n) ])
460
461 static BOOL SMB_SessionSetup(int fd, USHORT *userid)
462 {
463     unsigned char buffer[0x100];
464     int len = 0;
465     int i,pcount,bcount;
466
467     memset(buffer,0,sizeof buffer);
468
469     len = SMB_Header(buffer, SMB_COM_SESSION_SETUP_ANDX, 0, 0);
470
471     buffer[len++] = 0;    /* no parameters? */
472
473     buffer[len++] = 0xff; /* AndXCommand: secondary request */
474     buffer[len++] = 0x00; /* AndXReserved */
475     SMB_ADDWORD(&buffer[len],0); len += 2;     /* AndXOffset */
476     SMB_ADDWORD(&buffer[len],0x400); len += 2; /* MaxBufferSize */
477     SMB_ADDWORD(&buffer[len],1); len += 2;     /* MaxMpxCount */
478     SMB_ADDWORD(&buffer[len],0); len += 2;     /* VcNumber */
479     SMB_ADDWORD(&buffer[len],0); len += 2;     /* SessionKey */
480     SMB_ADDWORD(&buffer[len],0); len += 2;     /* SessionKey */
481     SMB_ADDWORD(&buffer[len],0); len += 2;     /* Password length */
482     SMB_ADDWORD(&buffer[len],0); len += 2;     /* Reserved */
483     SMB_ADDWORD(&buffer[len],0); len += 2;     /* Reserved */
484
485     /* FIXME: add name and password here */
486     buffer[len++] = 0; /* number of bytes in password */
487
488     if(!NB_Transaction(fd, buffer, len, &len))
489         return FALSE;
490
491     if(SMB_GetError(buffer))
492         return FALSE;
493
494     pcount = SMB_PARAM_COUNT(buffer);
495
496     if( (SMB_HDRSIZE+pcount*2) > len )
497     {
498         ERR("Bad parameter count %d\n",pcount);
499         return FALSE;
500     }
501
502     DPRINTF("SMB_COM_SESSION_SETUP response, %d args: ",pcount);
503     for(i=0; i<pcount; i++)
504         DPRINTF("%04x ",SMB_PARAM(buffer,i));
505     DPRINTF("\n");
506
507     bcount = SMB_BUFFER_COUNT(buffer);
508     if( (SMB_HDRSIZE+pcount*2+2+bcount) > len )
509     {
510         ERR("parameter count %x, buffer count %x, len %x\n",pcount,bcount,len);
511         return FALSE;
512     }
513
514     DPRINTF("response buffer %d bytes: ",bcount);
515     for(i=0; i<bcount; i++)
516     {
517         unsigned char ch = SMB_BUFFER(buffer,i);
518         DPRINTF("%c", isprint(ch)?ch:' ');
519     }
520     DPRINTF("\n");
521
522     *userid = SMB_GETWORD(&buffer[SMB_USERID]);
523
524     return TRUE;
525 }
526
527 static BOOL SMB_TreeConnect(int fd, USHORT user_id, LPCSTR share_name, USHORT *treeid)
528 {
529     unsigned char buffer[0x100];
530     int len = 0,slen;
531
532     ERR("%s\n",share_name);
533
534     memset(buffer,0,sizeof buffer);
535
536     len = SMB_Header(buffer, SMB_COM_TREE_CONNECT, 0, user_id);
537
538     buffer[len++] = 4; /* parameters */
539
540     buffer[len++] = 0xff; /* AndXCommand: secondary request */
541     buffer[len++] = 0x00; /* AndXReserved */
542     SMB_ADDWORD(&buffer[len],0); len += 2; /* AndXOffset */
543     SMB_ADDWORD(&buffer[len],0); len += 2; /* Flags */
544     SMB_ADDWORD(&buffer[len],1); len += 2; /* Password length */
545     
546     /* SMB command buffer */
547     SMB_ADDWORD(&buffer[len],3); len += 2; /* command buffer len */
548     buffer[len++] = 0; /* null terminated password */
549
550     slen = strlen(share_name);
551     if(slen<(sizeof buffer-len))
552         strcpy(&buffer[len], share_name);
553     else
554         return FALSE;
555     len += slen+1;
556  
557     /* name of the service */
558     buffer[len++] = 0;
559
560     if(!NB_Transaction(fd, buffer, len, &len))
561         return FALSE;
562
563     if(SMB_GetError(buffer))
564         return FALSE;
565
566     *treeid = SMB_GETWORD(&buffer[SMB_TREEID]);
567
568     ERR("OK, treeid = %04x\n", *treeid);
569
570     return TRUE;
571 }
572
573 static BOOL SMB_NtCreateOpen(int fd, USHORT tree_id, USHORT user_id, USHORT dialect,
574                               LPCSTR filename, DWORD access, DWORD sharing,
575                               LPSECURITY_ATTRIBUTES sa, DWORD creation,
576                               DWORD attributes, HANDLE template, USHORT *file_id )
577 {
578     unsigned char buffer[0x100];
579     int len = 0,slen;
580
581     ERR("%s\n",filename);
582
583     memset(buffer,0,sizeof buffer);
584
585     len = SMB_Header(buffer, SMB_COM_NT_CREATE_ANDX, tree_id, user_id);
586
587     /* 0 */
588     buffer[len++] = 24; /* parameters */
589
590     buffer[len++] = 0xff; /* AndXCommand: secondary request */
591     buffer[len++] = 0x00; /* AndXReserved */
592     SMB_ADDWORD(&buffer[len],0); len += 2; /* AndXOffset */
593
594     buffer[len++] = 0;                     /* reserved */
595     slen = strlen(filename);
596     SMB_ADDWORD(&buffer[len],slen);    len += 2; /* name length */
597
598     /* 0x08 */
599     SMB_ADDDWORD(&buffer[len],0);      len += 4; /* flags */
600     SMB_ADDDWORD(&buffer[len],0);      len += 4; /* root directory fid */
601     /* 0x10 */
602     SMB_ADDDWORD(&buffer[len],access); len += 4; /* access */
603     SMB_ADDDWORD(&buffer[len],0);      len += 4; /* allocation size */
604     /* 0x18 */
605     SMB_ADDDWORD(&buffer[len],0);      len += 4; /* root directory fid */
606
607     /* 0x1c */
608     SMB_ADDDWORD(&buffer[len],0);      len += 4; /* initial allocation */
609     SMB_ADDDWORD(&buffer[len],0);      len += 4; 
610  
611     /* 0x24 */
612     SMB_ADDDWORD(&buffer[len],attributes);      len += 4; /* ExtFileAttributes*/
613
614     /* 0x28 */
615     SMB_ADDDWORD(&buffer[len],sharing);      len += 4; /* ShareAccess */
616     
617     /* 0x2c */
618     ERR("creation = %08lx\n",creation);
619     SMB_ADDDWORD(&buffer[len],creation);      len += 4; /* CreateDisposition */
620     
621     /* 0x30 */
622     SMB_ADDDWORD(&buffer[len],creation);      len += 4; /* CreateOptions */
623     
624     /* 0x34 */
625     SMB_ADDDWORD(&buffer[len],0);      len += 4; /* Impersonation */
626
627     /* 0x38 */
628     buffer[len++] = 0;                     /* security flags */
629
630     /* 0x39 */
631     SMB_ADDWORD(&buffer[len],slen); len += 2; /* size of buffer */
632
633     if(slen<(sizeof buffer-len))
634         strcpy(&buffer[len], filename);
635     else
636         return FALSE;
637     len += slen+1;
638  
639     /* name of the file */
640     buffer[len++] = 0;
641
642     if(!NB_Transaction(fd, buffer, len, &len))
643         return FALSE;
644
645     if(SMB_GetError(buffer))
646         return FALSE;
647
648     ERR("OK\n");
649
650     /* FIXME */
651     /* *file_id = SMB_GETWORD(&buffer[xxx]); */
652     *file_id = 0;
653     return FALSE;
654
655     return TRUE;
656 }
657
658 static USHORT SMB_GetMode(DWORD access, DWORD sharing)
659 {
660     USHORT mode=0;
661
662     switch(access&(GENERIC_READ|GENERIC_WRITE))
663     {
664     case GENERIC_READ:
665         mode |= OF_READ;
666         break;
667     case GENERIC_WRITE:
668         mode |= OF_WRITE;
669         break;
670     case (GENERIC_READ|GENERIC_WRITE):
671         mode |= OF_READWRITE;
672         break;
673     }
674
675     switch(sharing&(FILE_SHARE_READ|FILE_SHARE_WRITE))
676     {
677     case (FILE_SHARE_READ|FILE_SHARE_WRITE):
678         mode |= OF_SHARE_DENY_NONE;
679         break;
680     case FILE_SHARE_READ:
681         mode |= OF_SHARE_DENY_WRITE;
682         break;
683     case FILE_SHARE_WRITE:
684         mode |= OF_SHARE_DENY_READ;
685         break;
686     default:
687         mode |= OF_SHARE_EXCLUSIVE;
688         break;
689     }
690
691     return mode;
692 }
693
694 /* inverse of FILE_ConvertOFMode */
695 static BOOL SMB_OpenAndX(int fd, USHORT tree_id, USHORT user_id, USHORT dialect,
696                               LPCSTR filename, DWORD access, DWORD sharing,
697                               DWORD creation, DWORD attributes, USHORT *file_id )
698 {
699     unsigned char buffer[0x100];
700     int len = 0;
701     USHORT mode;
702
703     ERR("%s\n",filename);
704
705     mode = SMB_GetMode(access,sharing);
706
707     memset(buffer,0,sizeof buffer);
708
709     len = SMB_Header(buffer, SMB_COM_OPEN_ANDX, tree_id, user_id);
710
711     /* 0 */
712     buffer[len++] = 15; /* parameters */
713     buffer[len++] = 0xff; /* AndXCommand: secondary request */
714     buffer[len++] = 0x00; /* AndXReserved */
715     SMB_ADDWORD(buffer+len,0); len+=2; /* AndXOffset */
716     SMB_ADDWORD(buffer+len,0); len+=2; /* Flags */
717     SMB_ADDWORD(buffer+len,mode); len+=2; /* desired access */
718     SMB_ADDWORD(buffer+len,0); len+=2; /* search attributes */
719     SMB_ADDWORD(buffer+len,0); len+=2;
720
721     /*FIXME: complete */
722     return FALSE;
723 }
724
725 static BOOL SMB_Open(int fd, USHORT tree_id, USHORT user_id, USHORT dialect,
726                               LPCSTR filename, DWORD access, DWORD sharing,
727                               DWORD creation, DWORD attributes, USHORT *file_id )
728 {
729     unsigned char buffer[0x100];
730     int len = 0,slen,pcount,i;
731     USHORT mode = SMB_GetMode(access,sharing);
732
733     ERR("%s\n",filename);
734
735     memset(buffer,0,sizeof buffer);
736
737     len = SMB_Header(buffer, SMB_COM_OPEN, tree_id, user_id);
738
739     /* 0 */
740     buffer[len++] = 2; /* parameters */
741     SMB_ADDWORD(buffer+len,mode); len+=2;
742     SMB_ADDWORD(buffer+len,0);    len+=2; /* search attributes */
743
744     slen = strlen(filename)+2;   /* inc. nul and BufferFormat */
745     SMB_ADDWORD(buffer+len,slen); len+=2;
746
747     buffer[len] = 0x04;  /* BufferFormat */
748     strcpy(&buffer[len+1],filename);
749     len += slen;
750
751     if(!NB_Transaction(fd, buffer, len, &len))
752         return FALSE;
753
754     if(SMB_GetError(buffer))
755         return FALSE;
756
757     pcount = SMB_PARAM_COUNT(buffer);
758
759     if( (SMB_HDRSIZE+pcount*2) > len )
760     {
761         ERR("Bad parameter count %d\n",pcount);
762         return FALSE;
763     }
764
765     ERR("response, %d args: ",pcount);
766     for(i=0; i<pcount; i++)
767         DPRINTF("%04x ",SMB_PARAM(buffer,i));
768     DPRINTF("\n");
769
770     *file_id = SMB_PARAM(buffer,0);
771
772     ERR("file_id = %04x\n",*file_id);
773
774     return TRUE;
775 }
776
777 static BOOL SMB_Read(int fd, USHORT tree_id, USHORT user_id, USHORT dialect, USHORT file_id, DWORD offset, LPVOID out, USHORT count, LPUSHORT read)
778 {
779     unsigned char *buffer;
780     int len,buf_size,n,i;
781
782     ERR("user %04x tree %04x file %04x count %04x offset %08lx\n",  
783         user_id, tree_id, file_id, count, offset);
784
785     buf_size = count+0x100;
786     buffer = (unsigned char *) HeapAlloc(GetProcessHeap(),0,buf_size);
787
788     memset(buffer,0,buf_size);
789
790     len = SMB_Header(buffer, SMB_COM_READ, tree_id, user_id);
791
792     buffer[len++] = 5;
793     SMB_ADDWORD(&buffer[len],file_id); len += 2;
794     SMB_ADDWORD(&buffer[len],count);   len += 2;
795     SMB_ADDDWORD(&buffer[len],offset); len += 4;
796     SMB_ADDWORD(&buffer[len],0);       len += 2; /* how many more bytes will be read */
797
798     buffer[len++] = 0;
799
800     if(!NB_Transaction(fd, buffer, len, &len))
801     {
802         HeapFree(GetProcessHeap(),0,buffer);
803         return FALSE;
804     }
805
806     if(SMB_GetError(buffer))
807     {
808         HeapFree(GetProcessHeap(),0,buffer);
809         return FALSE;
810     }
811
812     n = SMB_PARAM_COUNT(buffer);
813
814     if( (SMB_HDRSIZE+n*2) > len )
815     {
816         HeapFree(GetProcessHeap(),0,buffer);
817         ERR("Bad parameter count %d\n",n);
818         return FALSE;
819     }
820
821     ERR("response, %d args: ",n);
822     for(i=0; i<n; i++)
823         DPRINTF("%04x ",SMB_PARAM(buffer,i));
824     DPRINTF("\n");
825
826     n = SMB_PARAM(buffer,5) - 3;
827     if(n>count)
828         n=count;
829
830     memcpy( out, &SMB_BUFFER(buffer,3), n);
831
832     ERR("Read %d bytes\n",n);
833     *read = n;
834
835     HeapFree(GetProcessHeap(),0,buffer);
836     
837     return TRUE;
838 }
839
840 static int SMB_LoginAndConnect(LPCSTR host, LPCSTR share, USHORT *tree_id, USHORT *user_id, USHORT *dialect)
841 {
842     int fd=-1,r;
843     struct sockaddr_in sin;
844     LPSTR name=NULL;
845
846     ERR("host %s share %s\n",host,share);
847
848     /* FIXME: use various lookup methods */
849     if(0)
850         NB_Lookup(host,&sin);
851     else
852     {
853         if(0==inet_aton("127.0.0.1", (struct in_addr *)&sin.sin_addr.s_addr))
854         {
855             FIXME("Error getting localhost address\n");
856             SetLastError( ERROR_PATH_NOT_FOUND );
857             return INVALID_HANDLE_VALUE;
858         }
859         sin.sin_family = AF_INET;
860         sin.sin_port   = htons(139);  /* netbios session */
861     }
862
863     fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
864     if(fd<0)
865         goto fail;
866
867     ERR("Connecting...\n");
868     r = connect(fd, &sin, sizeof sin);
869     if(r<0)
870         goto fail;
871
872     if(!NB_SessionReq(fd, "*SMBSERVER", "WINE"))
873         goto fail;
874
875     if(!SMB_NegotiateProtocol(fd, dialect))
876         goto fail;
877
878     if(!SMB_SessionSetup(fd, user_id))
879         goto fail;
880
881     name = HeapAlloc(GetProcessHeap(),0,strlen(host)+strlen(share)+5);
882     if(!name)
883         goto fail;
884     sprintf(name,"\\\\%s\\%s",host,share);
885     if(!SMB_TreeConnect(fd,*user_id,name,tree_id))
886         goto fail;
887     HeapFree(GetProcessHeap(),0,name);
888
889     return fd;
890
891 fail:
892     if(name)
893         HeapFree(GetProcessHeap(),0,name);
894     ERR("Failed\n");
895     if(fd>=0)
896         close(fd);
897     return -1;
898 }
899
900 static HANDLE SMB_RegisterFile( int fd, USHORT tree_id, USHORT user_id, USHORT dialect, USHORT file_id)
901 {
902     int r;
903     HANDLE ret;
904
905     wine_server_send_fd( fd );
906
907     SERVER_START_REQ( create_smb )
908     {
909         req->tree_id = tree_id;
910         req->user_id = user_id;
911         req->file_id = file_id;
912         req->dialect = 0;
913         req->fd      = fd;
914         SetLastError(0);
915         r = wine_server_call_err( req );
916         ret = reply->handle;
917     }
918     SERVER_END_REQ;
919
920     if(!r)
921         ERR("created wineserver smb object, handle = %04x\n",ret);
922     else
923         SetLastError( ERROR_PATH_NOT_FOUND );
924
925     return ret;
926 }
927
928 HANDLE WINAPI SMB_CreateFileA( LPCSTR uncname, DWORD access, DWORD sharing,
929                               LPSECURITY_ATTRIBUTES sa, DWORD creation,
930                               DWORD attributes, HANDLE template )
931 {
932     int fd;
933     USHORT tree_id=0, user_id=0, dialect=0, file_id=0;
934     LPSTR name,host,share,file;
935     HANDLE handle = 0;
936
937     name = HeapAlloc(GetProcessHeap(),0,lstrlenA(uncname));
938     if(!name)
939         return -1;
940
941     lstrcpyA(name,uncname);
942
943     if( !UNC_SplitName(name, &host, &share, &file) )
944     {
945         HeapFree(GetProcessHeap(),0,name);
946         return handle;
947     }
948
949     ERR("server is %s, share is %s, file is %s\n", host, share, file);
950     fd = SMB_LoginAndConnect(host, share, &tree_id, &user_id, &dialect);
951     if(fd < 0)
952     {
953         HeapFree(GetProcessHeap(),0,name);
954         return handle;
955     }
956
957 #if 0
958     if(!SMB_NtCreateOpen(fd, tree_id, user_id, dialect, file, 
959                     access, sharing, sa, creation, attributes, template, &file_id ))
960     {
961         close(fd);
962         HeapFree(GetProcessHeap(),0,name);
963         ERR("CreateOpen failed\n");
964         return handle;
965     }
966 #endif
967     if(!SMB_Open(fd, tree_id, user_id, dialect, file, 
968                     access, sharing, creation, attributes, &file_id ))
969     {
970         close(fd);
971         HeapFree(GetProcessHeap(),0,name);
972         ERR("CreateOpen failed\n");
973         return handle;
974     }
975
976     HeapFree(GetProcessHeap(),0,name);
977
978     handle = SMB_RegisterFile(fd, tree_id, user_id, dialect, file_id);
979     if(!handle)
980     {
981         ERR("register failed\n");
982         close(fd);
983     }
984  
985     return handle;
986 }
987
988 static BOOL SMB_GetSmbInfo(HANDLE hFile, USHORT *tree_id, USHORT *user_id, USHORT *dialect, USHORT *file_id, LPDWORD offset)
989 {
990     int r;
991
992     SERVER_START_REQ( get_smb_info )
993     {
994         req->handle  = hFile;
995         req->flags   = 0;
996         SetLastError(0);
997         r = wine_server_call_err( req );
998         if(tree_id)
999             *tree_id = reply->tree_id;
1000         if(user_id)
1001             *user_id = reply->user_id;
1002         if(file_id)
1003             *file_id = reply->file_id;
1004         if(dialect)
1005             *dialect = reply->dialect;
1006         if(offset)
1007             *offset = reply->offset;
1008     }
1009     SERVER_END_REQ;
1010
1011     return !r;
1012 }
1013
1014 static BOOL SMB_SetOffset(HANDLE hFile, DWORD offset)
1015 {
1016     int r;
1017
1018     ERR("offset = %08lx\n",offset);
1019
1020     SERVER_START_REQ( get_smb_info )
1021     {
1022         req->handle  = hFile;
1023         req->flags   = SMBINFO_SET_OFFSET;
1024         req->offset  = offset;
1025         SetLastError(0);
1026         r = wine_server_call_err( req );
1027         /* if(offset)
1028             *offset = reply->offset; */
1029     }
1030     SERVER_END_REQ;
1031
1032     return !r;
1033 }
1034
1035 BOOL WINAPI SMB_ReadFile(HANDLE hFile, LPVOID buffer, DWORD bytesToRead, LPDWORD bytesRead, LPOVERLAPPED lpOverlapped)
1036 {
1037     int fd;
1038     DWORD total, count, offset;
1039     USHORT user_id, tree_id, dialect, file_id, read;
1040     BOOL r=TRUE;
1041
1042     ERR("%04x %p %ld %p\n", hFile, buffer, bytesToRead, bytesRead);
1043
1044     if(!SMB_GetSmbInfo(hFile, &tree_id, &user_id, &dialect, &file_id, &offset))
1045         return FALSE;
1046
1047     fd = FILE_GetUnixHandle(hFile, GENERIC_READ);
1048     if(fd<0)
1049         return FALSE;
1050
1051     total = 0;
1052     while(1)
1053     {
1054         count = bytesToRead - total;
1055         if(count>0x400)
1056             count = 0x400;
1057         if(count==0)
1058             break;
1059         read = 0;
1060         r = SMB_Read(fd, tree_id, user_id, dialect, file_id, offset, buffer, count, &read);
1061         if(!r)
1062             break;
1063         if(!read)
1064             break;
1065         total += read;
1066         buffer += read;
1067         offset += read;
1068         if(total>=bytesToRead)
1069             break;
1070     }
1071     close(fd);
1072
1073     if(bytesRead)
1074         *bytesRead = total;
1075
1076     if(!SMB_SetOffset(hFile, offset))
1077         return FALSE;
1078
1079     return r;
1080 }
1081