- Handle "? :" conditionals.
[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  * FIXME:
32  *
33  *   - There is a race condition when two threads try to read from the same
34  *     SMB handle. Either we need to lock the SMB handle for the time we
35  *     use it in the client, or do all reading and writing to the socket
36  *     fd in the server.
37  *
38  *   - Each new handle opens up a new connection to the SMB server. This
39  *     is not ideal, since operations can be multiplexed on one socket. For
40  *     this to work properly we would need to have some way of discovering
41  *     connections that are already open.
42  *
43  *   - All access is currently anonymous. Password protected shares cannot
44  *     be accessed.  We need some way of organising passwords, storing them
45  *     in the config file, or putting up a dialog box for the user.
46  *
47  *   - We don't deal with SMB dialects at all.
48  *
49  *   - SMB supports passing unicode over the wire, should use this if possible.
50  *
51  *   - Implement ability to read named pipes over the network. Would require
52  *     integrate this code with the named pipes code in the server, and
53  *     possibly implementing some support for security tokens.
54  */
55
56 #include "config.h"
57 #include "wine/port.h"
58
59 #include <assert.h>
60 #include <ctype.h>
61 #include <fcntl.h>
62 #include <stdlib.h>
63 #include <stdarg.h>
64 #include <stdio.h>
65 #include <string.h>
66 #include <sys/types.h>
67 #include <sys/stat.h>
68 #ifdef HAVE_SYS_MMAN_H
69 #include <sys/mman.h>
70 #endif
71 #ifdef HAVE_SYS_TIME_H
72 # include <sys/time.h>
73 #endif
74 #ifdef HAVE_SYS_POLL_H
75 # include <sys/poll.h>
76 #endif
77 #include <time.h>
78 #ifdef HAVE_UNISTD_H
79 # include <unistd.h>
80 #endif
81 #ifdef HAVE_UTIME_H
82 # include <utime.h>
83 #endif
84 #ifdef HAVE_SYS_SOCKET_H
85 # include <sys/socket.h>
86 #endif
87 #include <sys/types.h>
88 #ifdef HAVE_NETINET_IN_SYSTM_H
89 #include <netinet/in_systm.h>
90 #endif
91 #ifdef HAVE_NETINET_IN_H
92 #include <netinet/in.h>
93 #endif
94 #ifdef HAVE_NETINET_IP_H
95 #include <netinet/ip.h>
96 #endif
97 #ifdef HAVE_ARPA_INET_H
98 #include <arpa/inet.h>
99 #endif
100 #ifdef HAVE_NETDB_H
101 #include <netdb.h>
102 #endif
103
104 #define NONAMELESSUNION
105 #define NONAMELESSSTRUCT
106 #include "winerror.h"
107 #include "ntstatus.h"
108 #include "windef.h"
109 #include "winbase.h"
110 #include "winnls.h"
111 #include "winreg.h"
112 #include "winternl.h"
113 #include "lmerr.h"
114 #include "file.h"
115 #include "smb.h"
116
117 #include "wine/server.h"
118 #include "wine/debug.h"
119
120 WINE_DEFAULT_DEBUG_CHANNEL(file);
121
122 #define NBR_ADDWORD(p,word) { (p)[1] = (word & 0xff); (p)[0] = ((word)>>8)&0xff; }
123 #define NBR_GETWORD(p) ( (((p)[0])<<8) | ((p)[1]) )
124
125 #define SMB_ADDWORD(p,word) { (p)[0] = (word & 0xff); (p)[1] = ((word)>>8)&0xff; }
126 #define SMB_GETWORD(p) ( (((p)[1])<<8) | ((p)[0]) )
127 #define SMB_ADDDWORD(p,w) { (p)[3]=((w)>>24)&0xff; (p)[2]=((w)>>16)&0xff; (p)[1]=((w)>>8)&0xff; (p)[0]=(w)&0xff; }
128 #define SMB_GETDWORD(p) ( (((p)[3])<<24) | (((p)[2])<<16) | (((p)[1])<<8) | ((p)[0]) )
129
130 #define SMB_COM_CREATE_DIRECTORY       0x00
131 #define SMB_COM_DELETE_DIRECTORY       0x01
132 #define SMB_COM_OPEN                   0x02
133 #define SMB_COM_CREATE                 0x03
134 #define SMB_COM_CLOSE                  0x04
135 #define SMB_COM_FLUSH                  0x05
136 #define SMB_COM_DELETE                 0x06
137 #define SMB_COM_RENAME                 0x07
138 #define SMB_COM_QUERY_INFORMATION      0x08
139 #define SMB_COM_SET_INFORMATION        0x09
140 #define SMB_COM_READ                   0x0A
141 #define SMB_COM_WRITE                  0x0B
142 #define SMB_COM_LOCK_BYTE_RANGE        0x0C
143 #define SMB_COM_UNLOCK_BYTE_RANGE      0x0D
144 #define SMB_COM_CREATE_TEMPORARY       0x0E
145 #define SMB_COM_CREATE_NEW             0x0F
146 #define SMB_COM_CHECK_DIRECTORY        0x10
147 #define SMB_COM_PROCESS_EXIT           0x11
148 #define SMB_COM_SEEK                   0x12
149 #define SMB_COM_LOCK_AND_READ          0x13
150 #define SMB_COM_WRITE_AND_UNLOCK       0x14
151 #define SMB_COM_READ_RAW               0x1A
152 #define SMB_COM_READ_MPX               0x1B
153 #define SMB_COM_READ_MPX_SECONDARY     0x1C
154 #define SMB_COM_WRITE_RAW              0x1D
155 #define SMB_COM_WRITE_MPX              0x1E
156 #define SMB_COM_WRITE_COMPLETE         0x20
157 #define SMB_COM_SET_INFORMATION2       0x22
158 #define SMB_COM_QUERY_INFORMATION2     0x23
159 #define SMB_COM_LOCKING_ANDX           0x24
160 #define SMB_COM_TRANSACTION            0x25
161 #define SMB_COM_TRANSACTION_SECONDARY  0x26
162 #define SMB_COM_IOCTL                  0x27
163 #define SMB_COM_IOCTL_SECONDARY        0x28
164 #define SMB_COM_COPY                   0x29
165 #define SMB_COM_MOVE                   0x2A
166 #define SMB_COM_ECHO                   0x2B
167 #define SMB_COM_WRITE_AND_CLOSE        0x2C
168 #define SMB_COM_OPEN_ANDX              0x2D
169 #define SMB_COM_READ_ANDX              0x2E
170 #define SMB_COM_WRITE_ANDX             0x2F
171 #define SMB_COM_CLOSE_AND_TREE_DISC    0x31
172 #define SMB_COM_TRANSACTION2           0x32
173 #define SMB_COM_TRANSACTION2_SECONDARY 0x33
174 #define SMB_COM_FIND_CLOSE2            0x34
175 #define SMB_COM_FIND_NOTIFY_CLOSE      0x35
176 #define SMB_COM_TREE_CONNECT           0x70
177 #define SMB_COM_TREE_DISCONNECT        0x71
178 #define SMB_COM_NEGOTIATE              0x72
179 #define SMB_COM_SESSION_SETUP_ANDX     0x73
180 #define SMB_COM_LOGOFF_ANDX            0x74
181 #define SMB_COM_TREE_CONNECT_ANDX      0x75
182 #define SMB_COM_QUERY_INFORMATION_DISK 0x80
183 #define SMB_COM_SEARCH                 0x81
184 #define SMB_COM_FIND                   0x82
185 #define SMB_COM_FIND_UNIQUE            0x83
186 #define SMB_COM_NT_TRANSACT            0xA0
187 #define SMB_COM_NT_TRANSACT_SECONDARY  0xA1
188 #define SMB_COM_NT_CREATE_ANDX         0xA2
189 #define SMB_COM_NT_CANCEL              0xA4
190 #define SMB_COM_OPEN_PRINT_FILE        0xC0
191 #define SMB_COM_WRITE_PRINT_FILE       0xC1
192 #define SMB_COM_CLOSE_PRINT_FILE       0xC2
193 #define SMB_COM_GET_PRINT_QUEUE        0xC3
194
195 #define TRANS2_FIND_FIRST2             0x01
196 #define TRANS2_FIND_NEXT2              0x02
197
198 #define MAX_HOST_NAME 15
199 #define NB_TIMEOUT 10000
200
201 /* We only need the A versions locally currently */
202 static inline int SMB_isSepA (CHAR c) {return (c == '\\' || c == '/');}
203 static inline int SMB_isUNCA (LPCSTR filename) {return (filename && SMB_isSepW (filename[0]) && SMB_isSepW (filename[1]));}
204 static inline CHAR *SMB_nextSepA (CHAR *s) {while (*s && !SMB_isSepA (*s)) s++; return (*s? s : 0);}
205 /* NB SM_nextSepA cannot return const CHAR * since it is going to be used for
206  * replacing separators with null characters
207  */
208
209 static USHORT SMB_MultiplexId = 0;
210
211 struct NB_Buffer
212 {
213     unsigned char *buffer;
214     int len;
215 };
216
217 static int netbios_name(const char *p, unsigned char *buffer)
218 {
219     char ch;
220     int i,len=0;
221
222     buffer[len++]=' ';
223     for(i=0; i<=MAX_HOST_NAME; i++)
224     {
225         if(i<MAX_HOST_NAME)
226         {
227             if(*p)
228                 ch = *p++&0xdf; /* add character from hostname */
229             else
230                 ch = ' ';  /* add padding */
231         }
232         else
233             ch = 0;        /* add terminator */
234         buffer[len++] = ((ch&0xf0) >> 4) + 'A';
235         buffer[len++] =  (ch&0x0f) + 'A';
236     }
237     buffer[len++] = 0;     /* add second terminator */
238     return len;
239 }
240
241 static DWORD NB_NameReq(LPCSTR host, unsigned char *buffer, int len)
242 {
243     int trn = 1234,i=0;
244
245     NBR_ADDWORD(&buffer[i],trn);    i+=2;
246     NBR_ADDWORD(&buffer[i],0x0110); i+=2;
247     NBR_ADDWORD(&buffer[i],0x0001); i+=2;
248     NBR_ADDWORD(&buffer[i],0x0000); i+=2;
249     NBR_ADDWORD(&buffer[i],0x0000); i+=2;
250     NBR_ADDWORD(&buffer[i],0x0000); i+=2;
251
252     i += netbios_name(host,&buffer[i]);
253
254     NBR_ADDWORD(&buffer[i],0x0020); i+=2;
255     NBR_ADDWORD(&buffer[i],0x0001); i+=2;
256
257     TRACE("packet is %d bytes in length\n",i);
258
259     {
260         int j;
261         for(j=0; j<i; j++)
262             printf("%02x%c",buffer[j],(((j+1)%16)&&((j+1)!=j))?' ':'\n');
263     }
264
265     return i;
266 }
267
268 /* unc = \\hostname\share\file... */
269 static BOOL UNC_SplitName(LPSTR unc, LPSTR *hostname, LPSTR *share, LPSTR *file)
270 {
271     char *p;
272
273     TRACE("%s\n",unc);
274
275     if (!SMB_isUNCA (unc))
276         return FALSE;
277     p = unc + 2;
278     *hostname=p;
279
280     p = SMB_nextSepA (p);
281     if(!p)
282         return FALSE;
283     *p=0;
284     *share = ++p;
285
286     p = SMB_nextSepA (p);
287     if(!p)
288         return FALSE;
289     *p=0;
290     *file = ++p;
291
292     return TRUE;
293 }
294
295 static BOOL NB_Lookup(LPCSTR host, struct sockaddr_in *addr)
296 {
297     int fd,on=1,r,len,i,fromsize;
298     struct pollfd fds;
299     struct sockaddr_in sin,fromaddr;
300     unsigned char buffer[256];
301
302     fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
303     if(fd<0)
304         return FALSE;
305
306     r = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
307     if(r<0)
308         goto err;
309
310     sin.sin_family = AF_INET;
311     sin.sin_port    = htons(137);
312     sin.sin_addr.s_addr = 0xffffffff;
313
314     len = NB_NameReq(host,buffer,sizeof(buffer));
315     if(len<=0)
316         goto err;
317
318     r = sendto(fd, buffer, len, 0, (struct sockaddr*)&sin, sizeof(sin));
319     if(r<0)
320     {
321         FIXME("Error sending packet\n");
322         goto err;
323     }
324
325     fds.fd = fd;
326     fds.events = POLLIN;
327     fds.revents = 0;
328
329     /* FIXME: this is simple and easily fooled logic
330      *  we should loop until we receive the correct packet or timeout
331      */
332     r = poll(&fds,1,NB_TIMEOUT);
333     if(r!=1)
334         goto err;
335
336     TRACE("Got response!\n");
337
338     fromsize = sizeof (fromaddr);
339     r = recvfrom(fd, buffer, sizeof(buffer), 0, (struct sockaddr*)&fromaddr, &fromsize);
340     if(r<0)
341         goto err;
342
343     TRACE("%d bytes received\n",r);
344
345     if(r!=62)
346         goto err;
347
348     for(i=0; i<r; i++)
349         DPRINTF("%02X%c",buffer[i],(((i+1)!=r)&&((i+1)%16))?' ':'\n');
350     DPRINTF("\n");
351
352     if(0x0f & buffer[3])
353         goto err;
354
355     TRACE("packet is OK\n");
356
357     memcpy(&addr->sin_addr, &buffer[58], sizeof(addr->sin_addr));
358
359     close(fd);
360     return TRUE;
361
362 err:
363     close(fd);
364     return FALSE;
365 }
366
367 #define NB_FIRST 0x40
368
369 #define NB_HDRSIZE 4
370
371 #define NB_SESSION_MSG 0x00
372 #define NB_SESSION_REQ 0x81
373
374 /* RFC 1002, section 4.3.2 */
375 static BOOL NB_SessionReq(int fd, const char *called, const char *calling)
376 {
377     unsigned char buffer[0x100];
378     int len = 0,r;
379     struct pollfd fds;
380
381     TRACE("called %s, calling %s\n",called,calling);
382
383     buffer[0] = NB_SESSION_REQ;
384     buffer[1] = NB_FIRST;
385
386     netbios_name(called, &buffer[NB_HDRSIZE]);
387     len += 34;
388     netbios_name(calling, &buffer[NB_HDRSIZE+len]);
389     len += 34;
390
391     NBR_ADDWORD(&buffer[2],len);
392
393     /* for(i=0; i<(len+NB_HDRSIZE); i++)
394         DPRINTF("%02X%c",buffer[i],(((i+1)!=(len+4))&&((i+1)%16))?' ':'\n'); */
395
396     r = write(fd,buffer,len+4);
397     if(r<0)
398     {
399         ERR("Write failed\n");
400         return FALSE;
401     }
402
403     fds.fd = fd;
404     fds.events = POLLIN;
405     fds.revents = 0;
406
407     r = poll(&fds,1,NB_TIMEOUT);
408     if(r!=1)
409     {
410         ERR("Poll failed\n");
411         return FALSE;
412     }
413
414     r = read(fd, buffer, NB_HDRSIZE);
415     if((r!=NB_HDRSIZE) || (buffer[0]!=0x82))
416     {
417         TRACE("Received %d bytes\n",r);
418         TRACE("%02x %02x %02x %02x\n", buffer[0],buffer[1],buffer[2],buffer[3]);
419         return FALSE;
420     }
421
422     return TRUE;
423 }
424
425 static BOOL NB_SendData(int fd, struct NB_Buffer *out)
426 {
427     unsigned char buffer[NB_HDRSIZE];
428     int r;
429
430     /* CHECK: is it always OK to do this in two writes?   */
431     /*        perhaps use scatter gather sendmsg instead? */
432
433     buffer[0] = NB_SESSION_MSG;
434     buffer[1] = NB_FIRST;
435     NBR_ADDWORD(&buffer[2],out->len);
436
437     r = write(fd, buffer, NB_HDRSIZE);
438     if(r!=NB_HDRSIZE)
439         return FALSE;
440
441     r = write(fd, out->buffer, out->len);
442     if(r!=out->len)
443     {
444         ERR("write failed\n");
445         return FALSE;
446     }
447
448     return TRUE;
449 }
450
451 static BOOL NB_RecvData(int fd, struct NB_Buffer *rx)
452 {
453     int r;
454     unsigned char buffer[NB_HDRSIZE];
455
456     r = read(fd, buffer, NB_HDRSIZE);
457     if((r!=NB_HDRSIZE) || (buffer[0]!=NB_SESSION_MSG))
458     {
459         ERR("Received %d bytes\n",r);
460         return FALSE;
461     }
462
463     rx->len = NBR_GETWORD(&buffer[2]);
464
465     rx->buffer = RtlAllocateHeap(GetProcessHeap(), 0, rx->len);
466     if(!rx->buffer)
467         return FALSE;
468
469     r = read(fd, rx->buffer, rx->len);
470     if(rx->len!=r)
471     {
472         TRACE("Received %d bytes\n",r);
473         RtlFreeHeap(GetProcessHeap(), 0, rx->buffer);
474         rx->buffer = 0;
475         rx->len = 0;
476         return FALSE;
477     }
478
479     return TRUE;
480 }
481
482 static BOOL NB_Transaction(int fd, struct NB_Buffer *in, struct NB_Buffer *out)
483 {
484     int r;
485     struct pollfd fds;
486
487     if(TRACE_ON(file))
488     {
489         int i;
490     DPRINTF("Sending request:\n");
491         for(i=0; i<in->len; i++)
492             DPRINTF("%02X%c",in->buffer[i],(((i+1)!=in->len)&&((i+1)%16))?' ':'\n');
493     }
494
495     if(!NB_SendData(fd,in))
496         return FALSE;
497
498     fds.fd = fd;
499     fds.events = POLLIN;
500     fds.revents = 0;
501
502     r = poll(&fds,1,NB_TIMEOUT);
503     if(r!=1)
504     {
505         ERR("Poll failed\n");
506         return FALSE;
507     }
508
509     if(!NB_RecvData(fd, out))
510         return FALSE;
511
512     if(TRACE_ON(file))
513     {
514         int i;
515     DPRINTF("Got response:\n");
516         for(i=0; i<out->len; i++)
517             DPRINTF("%02X%c",out->buffer[i],(((i+1)!=out->len)&&((i+1)%16))?' ':'\n');
518     }
519
520     return TRUE;
521 }
522
523 #define SMB_ADDHEADER(b,l) { b[(l)++]=0xff; b[(l)++]='S'; b[(l)++]='M'; b[(l)++]='B'; }
524 #define SMB_ADDERRINFO(b,l) { b[(l)++]=0; b[(l)++]=0; b[(l)++]=0; b[(l)++]=0; }
525 #define SMB_ADDPADSIG(b,l) { memset(&b[l],0,12); l+=12; }
526
527 #define SMB_ERRCLASS 5
528 #define SMB_ERRCODE  7
529 #define SMB_TREEID  24
530 #define SMB_PROCID  26
531 #define SMB_USERID  28
532 #define SMB_PLEXID  30
533 #define SMB_PCOUNT  32
534 #define SMB_HDRSIZE 33
535
536 static DWORD NetSrvErrorToDOSError(USHORT code)
537 {
538     DWORD ret;
539
540     switch (code)
541     {
542     case 1:
543         ret = ERROR_INTERNAL_ERROR;
544         break;
545     case 2:
546         ret = ERROR_INVALID_PASSWORD;
547         break;
548     case 3:
549         ret = ERROR_BAD_NETPATH;
550         break;
551     case 4:
552         ret = ERROR_ACCESS_DENIED;
553         break;
554     /* unmapped: case 5: invalid transaction ID? */
555     case 6:
556         ret = ERROR_INVALID_NETNAME;
557         break;
558     case 7:
559         ret = ERROR_BAD_DEV_TYPE;
560         break;
561     case 49:
562         ret = ERROR_PRINTQ_FULL;
563         break;
564     case 50:
565         ret = ERROR_NO_SPOOL_SPACE;
566         break;
567     case 65:
568         ret = ERROR_INTERNAL_ERROR;
569         break;
570     case 69:
571         ret = ERROR_ACCESS_DENIED;
572         break;
573     case 81:
574         ret = NERR_PausedRemote;
575         break;
576     case 82:
577         ret = NERR_BadReceive;
578         break;
579     case 83:
580         ret = NERR_RemoteFull;
581         break;
582     case 87:
583         ret = NERR_TooManyNames;
584         break;
585     case 88:
586         ret = ERROR_TIMEOUT;
587         break;
588     case 89:
589         ret = ERROR_NO_SYSTEM_RESOURCES;
590         break;
591     case 91:
592         ret = ERROR_BAD_USERNAME;
593         break;
594     case 0xffff:
595         ret = ERROR_NOT_SUPPORTED;
596         break;
597     default:
598         ret = ERROR_INVALID_PARAMETER;
599     }
600     return ret;
601 }
602
603 DWORD SMBErrorToDOSError(UCHAR errorClass, USHORT code)
604 {
605     DWORD ret;
606
607     switch (errorClass)
608     {
609     case 0:
610         ret = NO_ERROR;
611         break;
612     case 1:
613         /* the DOS class corresponds exactly to DOS error codes */
614         ret = code;
615         break;
616     case 2:
617         ret = NetSrvErrorToDOSError(code);
618         break;
619     case 3:
620         /* the hardware error values are the same as the DOS error codes */
621         ret = code;
622         break;
623     default:
624         ret = ERROR_INVALID_PARAMETER;
625     }
626     return ret;
627 }
628
629 static DWORD SMB_GetError(unsigned char *buffer)
630 {
631     /* FIXME: should check to see whether the error was a DOS error or an
632      * NT status
633      */
634     return SMBErrorToDOSError(buffer[SMB_ERRCLASS],
635      *(PSHORT)(buffer + SMB_ERRCODE));
636 }
637
638 static int SMB_Header(unsigned char *buffer, unsigned char command, USHORT tree_id, USHORT user_id)
639 {
640     int len = 0;
641     DWORD id;
642
643     /* 0 */
644     SMB_ADDHEADER(buffer,len);
645
646     /* 4 */
647     buffer[len++] = command;
648
649     /* 5 */
650     SMB_ADDERRINFO(buffer,len)
651
652     /* 9 */
653     buffer[len++] = 0x00; /* flags */
654     SMB_ADDWORD(&buffer[len],1); len += 2; /* flags2 */
655
656     /* 12 */
657     SMB_ADDPADSIG(buffer,len)
658
659     /* 24 */
660     SMB_ADDWORD(&buffer[len],tree_id); len += 2; /* treeid */
661     id = GetCurrentThreadId();
662     SMB_ADDWORD(&buffer[len],id); len += 2; /* process id */
663     SMB_ADDWORD(&buffer[len],user_id); len += 2; /* user id */
664     SMB_ADDWORD(&buffer[len],SMB_MultiplexId); len += 2; /* multiplex id */
665     SMB_MultiplexId++;
666
667     return len;
668 }
669
670 static const char *SMB_ProtocolDialect = "NT LM 0.12";
671 /* = "Windows for Workgroups 3.1a"; */
672
673 /* FIXME: support multiple SMB dialects */
674 static BOOL SMB_NegotiateProtocol(int fd, USHORT *dialect)
675 {
676     unsigned char buf[0x100];
677     int buflen = 0;
678     struct NB_Buffer tx, rx;
679
680     TRACE("\n");
681
682     memset(buf,0,sizeof(buf));
683
684     tx.buffer = buf;
685     tx.len = SMB_Header(tx.buffer, SMB_COM_NEGOTIATE, 0, 0);
686
687     /* parameters */
688     tx.buffer[tx.len++] = 0; /* no parameters */
689
690     /* command buffer */
691     buflen = strlen(SMB_ProtocolDialect)+2;  /* include type and nul byte */
692     SMB_ADDWORD(&tx.buffer[tx.len],buflen); tx.len += 2;
693
694     tx.buffer[tx.len] = 0x02;
695     strcpy(&tx.buffer[tx.len+1],SMB_ProtocolDialect);
696     tx.len += buflen;
697
698     rx.buffer = NULL;
699     rx.len = 0;
700     if(!NB_Transaction(fd, &tx, &rx))
701     {
702         ERR("Failed\n");
703         return FALSE;
704     }
705
706     if(!rx.buffer)
707         return FALSE;
708
709     /* FIXME: check response */
710     if(SMB_GetError(rx.buffer))
711     {
712         ERR("returned error\n");
713         RtlFreeHeap(GetProcessHeap(),0,rx.buffer);
714         return FALSE;
715     }
716
717     RtlFreeHeap(GetProcessHeap(),0,rx.buffer);
718
719     *dialect = 0;
720
721     return TRUE;
722 }
723
724 #define SMB_PARAM_COUNT(buffer) ((buffer)[SMB_PCOUNT])
725 #define SMB_PARAM(buffer,n) SMB_GETWORD(&(buffer)[SMB_HDRSIZE+2*(n)])
726 #define SMB_BUFFER_COUNT(buffer)  SMB_GETWORD(buffer+SMB_HDRSIZE+2*SMB_PARAM_COUNT(buffer))
727 #define SMB_BUFFER(buffer,n) ((buffer)[SMB_HDRSIZE + 2*SMB_PARAM_COUNT(buffer) + 2 + (n) ])
728
729 static BOOL SMB_SessionSetup(int fd, USHORT *userid)
730 {
731     unsigned char buf[0x100];
732     int pcount,bcount;
733     struct NB_Buffer rx, tx;
734
735     memset(buf,0,sizeof(buf));
736     tx.buffer = buf;
737
738     tx.len = SMB_Header(tx.buffer, SMB_COM_SESSION_SETUP_ANDX, 0, 0);
739
740     tx.buffer[tx.len++] = 0;    /* no parameters? */
741
742     tx.buffer[tx.len++] = 0xff; /* AndXCommand: secondary request */
743     tx.buffer[tx.len++] = 0x00; /* AndXReserved */
744     SMB_ADDWORD(&tx.buffer[tx.len],0);     /* AndXOffset */
745     tx.len += 2;
746     SMB_ADDWORD(&tx.buffer[tx.len],0x400); /* MaxBufferSize */
747     tx.len += 2;
748     SMB_ADDWORD(&tx.buffer[tx.len],1);     /* MaxMpxCount */
749     tx.len += 2;
750     SMB_ADDWORD(&tx.buffer[tx.len],0);     /* VcNumber */
751     tx.len += 2;
752     SMB_ADDWORD(&tx.buffer[tx.len],0);     /* SessionKey */
753     tx.len += 2;
754     SMB_ADDWORD(&tx.buffer[tx.len],0);     /* SessionKey */
755     tx.len += 2;
756     SMB_ADDWORD(&tx.buffer[tx.len],0);     /* Password length */
757     tx.len += 2;
758     SMB_ADDWORD(&tx.buffer[tx.len],0);     /* Reserved */
759     tx.len += 2;
760     SMB_ADDWORD(&tx.buffer[tx.len],0);     /* Reserved */
761     tx.len += 2;
762
763     /* FIXME: add name and password here */
764     tx.buffer[tx.len++] = 0; /* number of bytes in password */
765
766     rx.buffer = NULL;
767     rx.len = 0;
768     if(!NB_Transaction(fd, &tx, &rx))
769         return FALSE;
770
771     if(!rx.buffer)
772         return FALSE;
773
774     if(SMB_GetError(rx.buffer))
775         goto done;
776
777     pcount = SMB_PARAM_COUNT(rx.buffer);
778
779     if( (SMB_HDRSIZE+pcount*2) > rx.len )
780     {
781         ERR("Bad parameter count %d\n",pcount);
782         goto done;
783     }
784
785     if(TRACE_ON(file))
786     {
787         int i;
788     DPRINTF("SMB_COM_SESSION_SETUP response, %d args: ",pcount);
789     for(i=0; i<pcount; i++)
790             DPRINTF("%04x ",SMB_PARAM(rx.buffer,i));
791     DPRINTF("\n");
792     }
793
794     bcount = SMB_BUFFER_COUNT(rx.buffer);
795     if( (SMB_HDRSIZE+pcount*2+2+bcount) > rx.len )
796     {
797         ERR("parameter count %x, buffer count %x, len %x\n",pcount,bcount,rx.len);
798         goto done;
799     }
800
801     if(TRACE_ON(file))
802     {
803         int i;
804     DPRINTF("response buffer %d bytes: ",bcount);
805     for(i=0; i<bcount; i++)
806     {
807             unsigned char ch = SMB_BUFFER(rx.buffer,i);
808         DPRINTF("%c", isprint(ch)?ch:' ');
809     }
810     DPRINTF("\n");
811     }
812
813     *userid = SMB_GETWORD(&rx.buffer[SMB_USERID]);
814
815     RtlFreeHeap(GetProcessHeap(),0,rx.buffer);
816     return TRUE;
817
818 done:
819     RtlFreeHeap(GetProcessHeap(),0,rx.buffer);
820     return FALSE;
821 }
822
823
824 static BOOL SMB_TreeConnect(int fd, USHORT user_id, LPCSTR share_name, USHORT *treeid)
825 {
826     unsigned char buf[0x100];
827     int slen;
828     struct NB_Buffer rx,tx;
829
830     TRACE("%s\n",share_name);
831
832     memset(buf,0,sizeof(buf));
833     tx.buffer = buf;
834
835     tx.len = SMB_Header(tx.buffer, SMB_COM_TREE_CONNECT, 0, user_id);
836
837     tx.buffer[tx.len++] = 4; /* parameters */
838
839     tx.buffer[tx.len++] = 0xff; /* AndXCommand: secondary request */
840     tx.buffer[tx.len++] = 0x00; /* AndXReserved */
841     SMB_ADDWORD(&tx.buffer[tx.len],0); /* AndXOffset */
842     tx.len += 2;
843     SMB_ADDWORD(&tx.buffer[tx.len],0); /* Flags */
844     tx.len += 2;
845     SMB_ADDWORD(&tx.buffer[tx.len],1); /* Password length */
846     tx.len += 2;
847
848     /* SMB command buffer */
849     SMB_ADDWORD(&tx.buffer[tx.len],3); /* command buffer len */
850     tx.len += 2;
851     tx.buffer[tx.len++] = 0; /* null terminated password */
852
853     slen = strlen(share_name);
854     if(slen<(sizeof(buf)-tx.len))
855         strcpy(&tx.buffer[tx.len], share_name);
856     else
857         return FALSE;
858     tx.len += slen+1;
859
860     /* name of the service */
861     tx.buffer[tx.len++] = 0;
862
863     rx.buffer = NULL;
864     rx.len = 0;
865     if(!NB_Transaction(fd, &tx, &rx))
866         return FALSE;
867
868     if(!rx.buffer)
869         return FALSE;
870
871     if(SMB_GetError(rx.buffer))
872     {
873         RtlFreeHeap(GetProcessHeap(),0,rx.buffer);
874         return FALSE;
875     }
876
877     *treeid = SMB_GETWORD(&rx.buffer[SMB_TREEID]);
878
879     RtlFreeHeap(GetProcessHeap(),0,rx.buffer);
880     TRACE("OK, treeid = %04x\n", *treeid);
881
882     return TRUE;
883 }
884
885 #if 0  /* not yet */
886 static BOOL SMB_NtCreateOpen(int fd, USHORT tree_id, USHORT user_id, USHORT dialect,
887                               LPCSTR filename, DWORD access, DWORD sharing,
888                               LPSECURITY_ATTRIBUTES sa, DWORD creation,
889                               DWORD attributes, HANDLE template, USHORT *file_id )
890 {
891     unsigned char buffer[0x100];
892     int len = 0,slen;
893
894     TRACE("%s\n",filename);
895
896     memset(buffer,0,sizeof(buffer));
897
898     len = SMB_Header(buffer, SMB_COM_NT_CREATE_ANDX, tree_id, user_id);
899
900     /* 0 */
901     buffer[len++] = 24; /* parameters */
902
903     buffer[len++] = 0xff; /* AndXCommand: secondary request */
904     buffer[len++] = 0x00; /* AndXReserved */
905     SMB_ADDWORD(&buffer[len],0); len += 2; /* AndXOffset */
906
907     buffer[len++] = 0;                     /* reserved */
908     slen = strlen(filename);
909     SMB_ADDWORD(&buffer[len],slen);    len += 2; /* name length */
910
911     /* 0x08 */
912     SMB_ADDDWORD(&buffer[len],0);      len += 4; /* flags */
913     SMB_ADDDWORD(&buffer[len],0);      len += 4; /* root directory fid */
914     /* 0x10 */
915     SMB_ADDDWORD(&buffer[len],access); len += 4; /* access */
916     SMB_ADDDWORD(&buffer[len],0);      len += 4; /* allocation size */
917     /* 0x18 */
918     SMB_ADDDWORD(&buffer[len],0);      len += 4; /* root directory fid */
919
920     /* 0x1c */
921     SMB_ADDDWORD(&buffer[len],0);      len += 4; /* initial allocation */
922     SMB_ADDDWORD(&buffer[len],0);      len += 4;
923
924     /* 0x24 */
925     SMB_ADDDWORD(&buffer[len],attributes);      len += 4; /* ExtFileAttributes*/
926
927     /* 0x28 */
928     SMB_ADDDWORD(&buffer[len],sharing);      len += 4; /* ShareAccess */
929
930     /* 0x2c */
931     TRACE("creation = %08lx\n",creation);
932     SMB_ADDDWORD(&buffer[len],creation);      len += 4; /* CreateDisposition */
933
934     /* 0x30 */
935     SMB_ADDDWORD(&buffer[len],creation);      len += 4; /* CreateOptions */
936
937     /* 0x34 */
938     SMB_ADDDWORD(&buffer[len],0);      len += 4; /* Impersonation */
939
940     /* 0x38 */
941     buffer[len++] = 0;                     /* security flags */
942
943     /* 0x39 */
944     SMB_ADDWORD(&buffer[len],slen); len += 2; /* size of buffer */
945
946     if(slen<(sizeof(buffer)-len))
947         strcpy(&buffer[len], filename);
948     else
949         return FALSE;
950     len += slen+1;
951
952     /* name of the file */
953     buffer[len++] = 0;
954
955     if(!NB_Transaction(fd, buffer, len, &len))
956         return FALSE;
957
958     if(SMB_GetError(buffer))
959         return FALSE;
960
961     TRACE("OK\n");
962
963     /* FIXME */
964     /* *file_id = SMB_GETWORD(&buffer[xxx]); */
965     *file_id = 0;
966     return FALSE;
967
968     return TRUE;
969 }
970 #endif
971
972 static USHORT SMB_GetMode(DWORD access, DWORD sharing)
973 {
974     USHORT mode=0;
975
976     switch(access&(GENERIC_READ|GENERIC_WRITE))
977     {
978     case GENERIC_READ:
979         mode |= OF_READ;
980         break;
981     case GENERIC_WRITE:
982         mode |= OF_WRITE;
983         break;
984     case (GENERIC_READ|GENERIC_WRITE):
985         mode |= OF_READWRITE;
986         break;
987     }
988
989     switch(sharing&(FILE_SHARE_READ|FILE_SHARE_WRITE))
990     {
991     case (FILE_SHARE_READ|FILE_SHARE_WRITE):
992         mode |= OF_SHARE_DENY_NONE;
993         break;
994     case FILE_SHARE_READ:
995         mode |= OF_SHARE_DENY_WRITE;
996         break;
997     case FILE_SHARE_WRITE:
998         mode |= OF_SHARE_DENY_READ;
999         break;
1000     default:
1001         mode |= OF_SHARE_EXCLUSIVE;
1002         break;
1003     }
1004
1005     return mode;
1006 }
1007
1008 #if 0  /* not yet */
1009 /* inverse of FILE_ConvertOFMode */
1010 static BOOL SMB_OpenAndX(int fd, USHORT tree_id, USHORT user_id, USHORT dialect,
1011                               LPCSTR filename, DWORD access, DWORD sharing,
1012                               DWORD creation, DWORD attributes, USHORT *file_id )
1013 {
1014     unsigned char buffer[0x100];
1015     int len = 0;
1016     USHORT mode;
1017
1018     TRACE("%s\n",filename);
1019
1020     mode = SMB_GetMode(access,sharing);
1021
1022     memset(buffer,0,sizeof(buffer));
1023
1024     len = SMB_Header(buffer, SMB_COM_OPEN_ANDX, tree_id, user_id);
1025
1026     /* 0 */
1027     buffer[len++] = 15; /* parameters */
1028     buffer[len++] = 0xff; /* AndXCommand: secondary request */
1029     buffer[len++] = 0x00; /* AndXReserved */
1030     SMB_ADDWORD(buffer+len,0); len+=2; /* AndXOffset */
1031     SMB_ADDWORD(buffer+len,0); len+=2; /* Flags */
1032     SMB_ADDWORD(buffer+len,mode); len+=2; /* desired access */
1033     SMB_ADDWORD(buffer+len,0); len+=2; /* search attributes */
1034     SMB_ADDWORD(buffer+len,0); len+=2;
1035
1036     /*FIXME: complete */
1037     return FALSE;
1038 }
1039 #endif
1040
1041
1042 static BOOL SMB_Open(int fd, USHORT tree_id, USHORT user_id, USHORT dialect,
1043                               LPCSTR filename, DWORD access, DWORD sharing,
1044                               DWORD creation, DWORD attributes, USHORT *file_id )
1045 {
1046     unsigned char buf[0x100];
1047     int slen,pcount,i;
1048     USHORT mode = SMB_GetMode(access,sharing);
1049     struct NB_Buffer rx,tx;
1050
1051     TRACE("%s\n",filename);
1052
1053     memset(buf,0,sizeof(buf));
1054
1055     tx.buffer = buf;
1056     tx.len = SMB_Header(tx.buffer, SMB_COM_OPEN, tree_id, user_id);
1057
1058     /* 0 */
1059     tx.buffer[tx.len++] = 2; /* parameters */
1060     SMB_ADDWORD(tx.buffer+tx.len,mode); tx.len+=2;
1061     SMB_ADDWORD(tx.buffer+tx.len,0);    tx.len+=2; /* search attributes */
1062
1063     slen = strlen(filename)+2;   /* inc. nul and BufferFormat */
1064     SMB_ADDWORD(tx.buffer+tx.len,slen); tx.len+=2;
1065
1066     tx.buffer[tx.len] = 0x04;  /* BufferFormat */
1067     strcpy(&tx.buffer[tx.len+1],filename);
1068     tx.len += slen;
1069
1070     rx.buffer = NULL;
1071     rx.len = 0;
1072     if(!NB_Transaction(fd, &tx, &rx))
1073         return FALSE;
1074
1075     if(!rx.buffer)
1076         return FALSE;
1077
1078     if(SMB_GetError(rx.buffer))
1079         return FALSE;
1080
1081     pcount = SMB_PARAM_COUNT(rx.buffer);
1082
1083     if( (SMB_HDRSIZE+pcount*2) > rx.len )
1084     {
1085         ERR("Bad parameter count %d\n",pcount);
1086         return FALSE;
1087     }
1088
1089     TRACE("response, %d args: ",pcount);
1090     for(i=0; i<pcount; i++)
1091         TRACE("%04x ",SMB_PARAM(rx.buffer,i));
1092     TRACE("\n");
1093
1094     *file_id = SMB_PARAM(rx.buffer,0);
1095
1096     TRACE("file_id = %04x\n",*file_id);
1097
1098     return TRUE;
1099 }
1100
1101
1102 static BOOL SMB_Read(int fd, USHORT tree_id, USHORT user_id, USHORT dialect,
1103        USHORT file_id, DWORD offset, LPVOID out, USHORT count, USHORT* read)
1104 {
1105     int buf_size,n,i;
1106     struct NB_Buffer rx,tx;
1107
1108     TRACE("user %04x tree %04x file %04x count %04x offset %08lx\n",
1109         user_id, tree_id, file_id, count, offset);
1110
1111     buf_size = count+0x100;
1112     tx.buffer = (unsigned char *) RtlAllocateHeap(GetProcessHeap(),0,buf_size);
1113
1114     memset(tx.buffer,0,buf_size);
1115
1116     tx.len = SMB_Header(tx.buffer, SMB_COM_READ, tree_id, user_id);
1117
1118     tx.buffer[tx.len++] = 5;
1119     SMB_ADDWORD(&tx.buffer[tx.len],file_id); tx.len += 2;
1120     SMB_ADDWORD(&tx.buffer[tx.len],count);   tx.len += 2;
1121     SMB_ADDDWORD(&tx.buffer[tx.len],offset); tx.len += 4;
1122     SMB_ADDWORD(&tx.buffer[tx.len],0);       tx.len += 2; /* how many more bytes will be read */
1123
1124     tx.buffer[tx.len++] = 0;
1125
1126     rx.buffer = NULL;
1127     rx.len = 0;
1128     if(!NB_Transaction(fd, &tx, &rx))
1129     {
1130         RtlFreeHeap(GetProcessHeap(),0,tx.buffer);
1131         return FALSE;
1132     }
1133
1134     if(SMB_GetError(rx.buffer))
1135     {
1136         RtlFreeHeap(GetProcessHeap(),0,rx.buffer);
1137         RtlFreeHeap(GetProcessHeap(),0,tx.buffer);
1138         return FALSE;
1139     }
1140
1141     n = SMB_PARAM_COUNT(rx.buffer);
1142
1143     if( (SMB_HDRSIZE+n*2) > rx.len )
1144     {
1145         RtlFreeHeap(GetProcessHeap(),0,rx.buffer);
1146         RtlFreeHeap(GetProcessHeap(),0,tx.buffer);
1147         ERR("Bad parameter count %d\n",n);
1148         return FALSE;
1149     }
1150
1151     TRACE("response, %d args: ",n);
1152     for(i=0; i<n; i++)
1153         TRACE("%04x ",SMB_PARAM(rx.buffer,i));
1154     TRACE("\n");
1155
1156     n = SMB_PARAM(rx.buffer,5) - 3;
1157     if(n>count)
1158         n=count;
1159
1160     memcpy( out, &SMB_BUFFER(rx.buffer,3), n);
1161
1162     TRACE("Read %d bytes\n",n);
1163     *read = n;
1164
1165     RtlFreeHeap(GetProcessHeap(),0,tx.buffer);
1166     RtlFreeHeap(GetProcessHeap(),0,rx.buffer);
1167
1168     return TRUE;
1169 }
1170
1171
1172 /*
1173  * setup_count : number of USHORTs in the setup string
1174  */
1175 struct SMB_Trans2Info
1176 {
1177     struct NB_Buffer buf;
1178     unsigned char *setup;
1179     int setup_count;
1180     unsigned char *params;
1181     int param_count;
1182     unsigned char *data;
1183     int data_count;
1184 };
1185
1186 /*
1187  * Do an SMB transaction
1188  *
1189  * This function allocates memory in the recv structure. It is
1190  * the caller's responsibility to free the memory if it finds
1191  * that recv->buf.buffer is nonzero.
1192  */
1193 static BOOL SMB_Transaction2(int fd, int tree_id, int user_id,
1194                  struct SMB_Trans2Info *send,
1195                  struct SMB_Trans2Info *recv)
1196 {
1197     int buf_size;
1198     const int retmaxparams = 0xf000;
1199     const int retmaxdata = 1024;
1200     const int retmaxsetup = 0; /* FIXME */
1201     const int flags = 0;
1202     const int timeout = 0;
1203     int param_ofs, data_ofs;
1204     struct NB_Buffer tx;
1205     BOOL ret = FALSE;
1206
1207     buf_size = 0x100 + send->setup_count*2 + send->param_count + send->data_count ;
1208     tx.buffer = (unsigned char *) RtlAllocateHeap(GetProcessHeap(),0,buf_size);
1209
1210     tx.len = SMB_Header(tx.buffer, SMB_COM_TRANSACTION2, tree_id, user_id);
1211
1212     tx.buffer[tx.len++] = 14 + send->setup_count;
1213     SMB_ADDWORD(&tx.buffer[tx.len],send->param_count); /* total param bytes sent */
1214     tx.len += 2;
1215     SMB_ADDWORD(&tx.buffer[tx.len],send->data_count);  /* total data bytes sent */
1216     tx.len += 2;
1217     SMB_ADDWORD(&tx.buffer[tx.len],retmaxparams); /*max parameter bytes to return */
1218     tx.len += 2;
1219     SMB_ADDWORD(&tx.buffer[tx.len],retmaxdata);  /* max data bytes to return */
1220     tx.len += 2;
1221     tx.buffer[tx.len++] = retmaxsetup;
1222     tx.buffer[tx.len++] = 0;                     /* reserved1 */
1223
1224     SMB_ADDWORD(&tx.buffer[tx.len],flags);       /* flags */
1225     tx.len += 2;
1226     SMB_ADDDWORD(&tx.buffer[tx.len],timeout);    /* timeout */
1227     tx.len += 4;
1228     SMB_ADDWORD(&tx.buffer[tx.len],0);           /* reserved2 */
1229     tx.len += 2;
1230     SMB_ADDWORD(&tx.buffer[tx.len],send->param_count); /* parameter count - this buffer */
1231     tx.len += 2;
1232
1233     param_ofs = tx.len;                          /* parameter offset */
1234     tx.len += 2;
1235     SMB_ADDWORD(&tx.buffer[tx.len],send->data_count);  /* data count */
1236     tx.len += 2;
1237
1238     data_ofs = tx.len;                           /* data offset */
1239     tx.len += 2;
1240     tx.buffer[tx.len++] = send->setup_count;     /* setup count */
1241     tx.buffer[tx.len++] = 0;                     /* reserved3 */
1242
1243     memcpy(&tx.buffer[tx.len], send->setup, send->setup_count*2); /* setup */
1244     tx.len += send->setup_count*2;
1245
1246     /* add string here when implementing SMB_COM_TRANS */
1247
1248     SMB_ADDWORD(&tx.buffer[param_ofs], tx.len);
1249     memcpy(&tx.buffer[tx.len], send->params, send->param_count); /* parameters */
1250     tx.len += send->param_count;
1251     if(tx.len%2)
1252         tx.len ++;                                      /* pad2 */
1253
1254     SMB_ADDWORD(&tx.buffer[data_ofs], tx.len);
1255     if(send->data_count && send->data)
1256     {
1257         memcpy(&tx.buffer[tx.len], send->data, send->data_count); /* data */
1258         tx.len += send->data_count;
1259     }
1260
1261     recv->buf.buffer = NULL;
1262     recv->buf.len = 0;
1263     if(!NB_Transaction(fd, &tx, &recv->buf))
1264         goto done;
1265
1266     if(!recv->buf.buffer)
1267         goto done;
1268
1269     if(SMB_GetError(recv->buf.buffer))
1270         goto done;
1271
1272     /* reuse these two offsets to check the received message */
1273     param_ofs = SMB_PARAM(recv->buf.buffer,4);
1274     data_ofs = SMB_PARAM(recv->buf.buffer,7);
1275
1276     if( (recv->param_count + param_ofs) > recv->buf.len )
1277         goto done;
1278
1279     if( (recv->data_count + data_ofs) > recv->buf.len )
1280         goto done;
1281
1282     TRACE("Success\n");
1283
1284     recv->setup = NULL;
1285     recv->setup_count = 0;
1286
1287     recv->param_count = SMB_PARAM(recv->buf.buffer,0);
1288     recv->params = &recv->buf.buffer[param_ofs];
1289
1290     recv->data_count = SMB_PARAM(recv->buf.buffer,6);
1291     recv->data = &recv->buf.buffer[data_ofs];
1292
1293    /*
1294     TRACE("%d words\n",SMB_PARAM_COUNT(recv->buf.buffer));
1295     TRACE("total parameters = %d\n",SMB_PARAM(recv->buf.buffer,0));
1296     TRACE("total data       = %d\n",SMB_PARAM(recv->buf.buffer,1));
1297     TRACE("parameters       = %d\n",SMB_PARAM(recv->buf.buffer,3));
1298     TRACE("parameter offset = %d\n",SMB_PARAM(recv->buf.buffer,4));
1299     TRACE("param displace   = %d\n",SMB_PARAM(recv->buf.buffer,5));
1300
1301     TRACE("data count       = %d\n",SMB_PARAM(recv->buf.buffer,6));
1302     TRACE("data offset      = %d\n",SMB_PARAM(recv->buf.buffer,7));
1303     TRACE("data displace    = %d\n",SMB_PARAM(recv->buf.buffer,8));
1304    */
1305
1306     ret = TRUE;
1307
1308 done:
1309     if(tx.buffer)
1310         RtlFreeHeap(GetProcessHeap(),0,tx.buffer);
1311
1312     return ret;
1313 }
1314
1315 static BOOL SMB_SetupFindFirst(struct SMB_Trans2Info *send, LPSTR filename)
1316 {
1317     int search_attribs = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM;
1318     int search_count = 10;
1319     int flags = 0;
1320     int infolevel = 0x104; /* SMB_FILE_BOTH_DIRECTORY_INFO */
1321     int storagetype = 0;
1322     int len, buf_size;
1323
1324     memset(send,0,sizeof(send));
1325
1326     send->setup_count = 1;
1327     send->setup = RtlAllocateHeap(GetProcessHeap(),0,send->setup_count*2);
1328     if(!send->setup)
1329         return FALSE;
1330
1331     buf_size = 0x10 + strlen(filename);
1332     send->params = RtlAllocateHeap(GetProcessHeap(),0,buf_size);
1333     if(!send->params)
1334     {
1335         RtlFreeHeap(GetProcessHeap(),0,send->setup);
1336         return FALSE;
1337     }
1338
1339     SMB_ADDWORD(send->setup,TRANS2_FIND_FIRST2);
1340
1341     len = 0;
1342     memset(send->params,0,buf_size);
1343     SMB_ADDWORD(&send->params[len],search_attribs); len += 2;
1344     SMB_ADDWORD(&send->params[len],search_count); len += 2;
1345     SMB_ADDWORD(&send->params[len],flags); len += 2;
1346     SMB_ADDWORD(&send->params[len],infolevel); len += 2;
1347     SMB_ADDDWORD(&send->params[len],storagetype); len += 4;
1348
1349     strcpy(&send->params[len],filename);
1350     len += strlen(filename)+1;
1351
1352     send->param_count = len;
1353     send->data = NULL;
1354     send->data_count = 0;
1355
1356     return TRUE;
1357 }
1358
1359 static SMB_DIR *SMB_Trans2FindFirst(int fd, USHORT tree_id,
1360                     USHORT user_id, USHORT dialect, LPSTR filename )
1361 {
1362     int num;
1363     BOOL ret;
1364     /* char *filename = "\\*"; */
1365     struct SMB_Trans2Info send, recv;
1366     SMB_DIR *smbdir = NULL;
1367
1368     TRACE("pattern = %s\n",filename);
1369
1370     if(!SMB_SetupFindFirst(&send, filename))
1371         return FALSE;
1372
1373     memset(&recv,0,sizeof(recv));
1374
1375     ret = SMB_Transaction2(fd, tree_id, user_id, &send, &recv);
1376     RtlFreeHeap(GetProcessHeap(),0,send.params);
1377     RtlFreeHeap(GetProcessHeap(),0,send.setup);
1378
1379     if(!ret)
1380         goto done;
1381
1382     if(recv.setup_count)
1383         goto done;
1384
1385     if(recv.param_count != 10)
1386         goto done;
1387
1388     num = SMB_GETWORD(&recv.params[2]);
1389     TRACE("Success, search id: %d\n",num);
1390
1391     if(SMB_GETWORD(&recv.params[4]))
1392         FIXME("need to read more!\n");
1393
1394     smbdir = RtlAllocateHeap(GetProcessHeap(),0,sizeof(*smbdir));
1395     if(smbdir)
1396     {
1397         int i, ofs=0;
1398
1399         smbdir->current = 0;
1400         smbdir->num_entries = num;
1401         smbdir->entries = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(unsigned char*)*num);
1402         if(!smbdir->entries)
1403             goto done;
1404         smbdir->buffer = recv.buf.buffer; /* save to free later */
1405
1406         for(i=0; i<num; i++)
1407         {
1408             int size = SMB_GETDWORD(&recv.data[ofs]);
1409
1410             smbdir->entries[i] = &recv.data[ofs];
1411
1412             if(TRACE_ON(file))
1413             {
1414                 int j;
1415                 for(j=0; j<size; j++)
1416                     DPRINTF("%02x%c",recv.data[ofs+j],((j+1)%16)?' ':'\n');
1417             }
1418             TRACE("file %d : %s\n", i, &recv.data[ofs+0x5e]);
1419             ofs += size;
1420             if(ofs>recv.data_count)
1421                 goto done;
1422         }
1423
1424         ret = TRUE;
1425     }
1426
1427 done:
1428     if(!ret)
1429     {
1430         if( recv.buf.buffer )
1431             RtlFreeHeap(GetProcessHeap(),0,recv.buf.buffer);
1432         if( smbdir )
1433         {
1434             if( smbdir->entries )
1435                 RtlFreeHeap(GetProcessHeap(),0,smbdir->entries);
1436             RtlFreeHeap(GetProcessHeap(),0,smbdir);
1437         }
1438         smbdir = NULL;
1439     }
1440
1441     return smbdir;
1442 }
1443
1444 static int SMB_GetSocket(LPCSTR host)
1445 {
1446     int fd=-1,r;
1447     struct sockaddr_in sin;
1448     struct hostent *he;
1449
1450     TRACE("host %s\n",host);
1451
1452     he = gethostbyname(host);
1453     if(he)
1454     {
1455         memcpy(&sin.sin_addr,he->h_addr, sizeof (sin.sin_addr));
1456         goto connect;
1457     }
1458
1459     if(NB_Lookup(host,&sin))
1460         goto connect;
1461
1462     /* FIXME: resolve by WINS too */
1463
1464     ERR("couldn't resolve SMB host %s\n", host);
1465
1466     return -1;
1467
1468 connect:
1469     sin.sin_family = AF_INET;
1470     sin.sin_port   = htons(139);  /* netbios session */
1471
1472     fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
1473     if(fd<0)
1474         return fd;
1475
1476     {
1477         unsigned char *x = (unsigned char *)&sin.sin_addr;
1478         TRACE("Connecting to %d.%d.%d.%d ...\n", x[0],x[1],x[2],x[3]);
1479     }
1480     r = connect(fd, (struct sockaddr*)&sin, sizeof(sin));
1481
1482     if(!NB_SessionReq(fd, "*SMBSERVER", "WINE"))
1483     {
1484         close(fd);
1485         return -1;
1486     }
1487
1488     return fd;
1489 }
1490
1491 static BOOL SMB_LoginAndConnect(int fd, LPCSTR host, LPCSTR share, USHORT *tree_id, USHORT *user_id, USHORT *dialect)
1492 {
1493     LPSTR name=NULL;
1494
1495     TRACE("host %s share %s\n",host,share);
1496
1497     if(!SMB_NegotiateProtocol(fd, dialect))
1498         return FALSE;
1499
1500     if(!SMB_SessionSetup(fd, user_id))
1501         return FALSE;
1502
1503     name = RtlAllocateHeap(GetProcessHeap(),0,strlen(host)+strlen(share)+5);
1504     if(!name)
1505         return FALSE;
1506
1507     sprintf(name,"\\\\%s\\%s",host,share);
1508     if(!SMB_TreeConnect(fd,*user_id,name,tree_id))
1509     {
1510         RtlFreeHeap(GetProcessHeap(),0,name);
1511         return FALSE;
1512     }
1513
1514     return TRUE;
1515 }
1516
1517 static HANDLE SMB_RegisterFile( int fd, USHORT tree_id, USHORT user_id, USHORT dialect, USHORT file_id)
1518 {
1519     int r;
1520     HANDLE ret;
1521
1522     wine_server_send_fd( fd );
1523
1524     SERVER_START_REQ( create_smb )
1525     {
1526         req->tree_id = tree_id;
1527         req->user_id = user_id;
1528         req->file_id = file_id;
1529         req->dialect = 0;
1530         req->fd      = fd;
1531         SetLastError(0);
1532         r = wine_server_call_err( req );
1533         ret = reply->handle;
1534     }
1535     SERVER_END_REQ;
1536
1537     if(!r)
1538         TRACE("created wineserver smb object, handle = %p\n",ret);
1539     else
1540         SetLastError( ERROR_PATH_NOT_FOUND );
1541
1542     return ret;
1543 }
1544
1545 HANDLE WINAPI SMB_CreateFileW( LPCWSTR uncname, DWORD access, DWORD sharing,
1546                               LPSECURITY_ATTRIBUTES sa, DWORD creation,
1547                               DWORD attributes, HANDLE template )
1548 {
1549     int fd;
1550     USHORT tree_id=0, user_id=0, dialect=0, file_id=0;
1551     LPSTR name,host,share,file;
1552     HANDLE handle = INVALID_HANDLE_VALUE;
1553     INT len;
1554
1555     len = WideCharToMultiByte(CP_ACP, 0, uncname, -1, NULL, 0, NULL, NULL);
1556     name = RtlAllocateHeap(GetProcessHeap(), 0, len);
1557     if(!name)
1558         return handle;
1559
1560     WideCharToMultiByte(CP_ACP, 0, uncname, -1, name, len, NULL, NULL);
1561
1562     if( !UNC_SplitName(name, &host, &share, &file) )
1563     {
1564         RtlFreeHeap(GetProcessHeap(),0,name);
1565         return handle;
1566     }
1567
1568     TRACE("server is %s, share is %s, file is %s\n", host, share, file);
1569
1570     fd = SMB_GetSocket(host);
1571     if(fd < 0)
1572         goto done;
1573
1574     if(!SMB_LoginAndConnect(fd, host, share, &tree_id, &user_id, &dialect))
1575         goto done;
1576
1577 #if 0
1578     if(!SMB_NtCreateOpen(fd, tree_id, user_id, dialect, file,
1579                     access, sharing, sa, creation, attributes, template, &file_id ))
1580     {
1581         close(fd);
1582         ERR("CreateOpen failed\n");
1583         goto done;
1584     }
1585 #endif
1586     if(!SMB_Open(fd, tree_id, user_id, dialect, file,
1587                     access, sharing, creation, attributes, &file_id ))
1588     {
1589         close(fd);
1590         ERR("CreateOpen failed\n");
1591         goto done;
1592     }
1593
1594     handle = SMB_RegisterFile(fd, tree_id, user_id, dialect, file_id);
1595     if(!handle)
1596     {
1597         ERR("register failed\n");
1598         close(fd);
1599     }
1600
1601 done:
1602     RtlFreeHeap(GetProcessHeap(),0,name);
1603     return handle;
1604 }
1605
1606 static NTSTATUS SMB_GetSmbInfo(HANDLE hFile, USHORT *tree_id, USHORT *user_id, USHORT *dialect, USHORT *file_id, LPDWORD offset)
1607 {
1608     NTSTATUS status;
1609
1610     SERVER_START_REQ( get_smb_info )
1611     {
1612         req->handle  = hFile;
1613         req->flags   = 0;
1614         status = wine_server_call( req );
1615         if(tree_id)
1616             *tree_id = reply->tree_id;
1617         if(user_id)
1618             *user_id = reply->user_id;
1619         if(file_id)
1620             *file_id = reply->file_id;
1621         if(dialect)
1622             *dialect = reply->dialect;
1623         if(offset)
1624             *offset = reply->offset;
1625     }
1626     SERVER_END_REQ;
1627
1628     return status;
1629 }
1630
1631 static NTSTATUS SMB_SetOffset(HANDLE hFile, DWORD offset)
1632 {
1633     NTSTATUS status;
1634
1635     TRACE("offset = %08lx\n",offset);
1636
1637     SERVER_START_REQ( get_smb_info )
1638     {
1639         req->handle  = hFile;
1640         req->flags   = SMBINFO_SET_OFFSET;
1641         req->offset  = offset;
1642         status = wine_server_call( req );
1643         /* if(offset)
1644             *offset = reply->offset; */
1645     }
1646     SERVER_END_REQ;
1647
1648     return status;
1649 }
1650
1651 NTSTATUS WINAPI SMB_ReadFile(HANDLE hFile, int fd, LPVOID buffer, DWORD bytesToRead,
1652                              PIO_STATUS_BLOCK io_status)
1653 {
1654     DWORD count, offset;
1655     USHORT user_id, tree_id, dialect, file_id, read;
1656
1657     TRACE("%p %p %ld %p\n", hFile, buffer, bytesToRead, io_status);
1658
1659     io_status->Information = 0;
1660
1661     io_status->u.Status = SMB_GetSmbInfo(hFile, &tree_id, &user_id, &dialect, &file_id, &offset);
1662     if (io_status->u.Status) return io_status->u.Status;
1663
1664     while(1)
1665     {
1666         count = bytesToRead - io_status->Information;
1667         if(count>0x400)
1668             count = 0x400;
1669         if(count==0)
1670             break;
1671         read = 0;
1672         if (!SMB_Read(fd, tree_id, user_id, dialect, file_id, offset, buffer, count, &read))
1673             break;
1674         if(!read)
1675             break;
1676         io_status->Information += read;
1677         buffer = (char*)buffer + read;
1678         offset += read;
1679         if(io_status->Information >= bytesToRead)
1680             break;
1681     }
1682     return io_status->u.Status = SMB_SetOffset(hFile, offset);
1683 }
1684
1685 SMB_DIR* WINAPI SMB_FindFirst(LPCWSTR name)
1686 {
1687     int fd = -1;
1688     LPSTR host,share,file;
1689     USHORT tree_id=0, user_id=0, dialect=0;
1690     SMB_DIR *ret = NULL;
1691     LPSTR filename;
1692     DWORD len;
1693
1694     TRACE("Find %s\n",debugstr_w(name));
1695
1696     len = WideCharToMultiByte( CP_ACP, 0, name, -1, NULL, 0, NULL, NULL );
1697     filename = RtlAllocateHeap(GetProcessHeap(),0,len);
1698     if(!filename)
1699         return ret;
1700     WideCharToMultiByte( CP_ACP, 0, name, -1, filename, len, NULL, NULL );
1701
1702     if( !UNC_SplitName(filename, &host, &share, &file) )
1703         goto done;
1704
1705     fd = SMB_GetSocket(host);
1706     if(fd < 0)
1707         goto done;
1708
1709     if(!SMB_LoginAndConnect(fd, host, share, &tree_id, &user_id, &dialect))
1710         goto done;
1711
1712     TRACE("server is %s, share is %s, file is %s\n", host, share, file);
1713
1714     ret = SMB_Trans2FindFirst(fd, tree_id, user_id, dialect, file);
1715
1716 done:
1717     /* disconnect */
1718     if(fd != -1)
1719         close(fd);
1720
1721     if(filename)
1722         RtlFreeHeap(GetProcessHeap(),0,filename);
1723
1724     return ret;
1725 }
1726
1727
1728 BOOL WINAPI SMB_FindNext(SMB_DIR *dir, WIN32_FIND_DATAW *data )
1729 {
1730     unsigned char *ent;
1731     int len, fnlen;
1732
1733     TRACE("%d of %d\n",dir->current,dir->num_entries);
1734
1735     if(dir->current >= dir->num_entries)
1736         return FALSE;
1737
1738     memset(data, 0, sizeof(*data));
1739
1740     ent = dir->entries[dir->current];
1741     len = SMB_GETDWORD(&ent[0]);
1742     if(len<0x5e)
1743         return FALSE;
1744
1745     memcpy(&data->ftCreationTime, &ent[8], 8);
1746     memcpy(&data->ftLastAccessTime, &ent[0x10], 8);
1747     memcpy(&data->ftLastWriteTime, &ent[0x18], 8);
1748     data->nFileSizeHigh = SMB_GETDWORD(&ent[0x30]);
1749     data->nFileSizeLow = SMB_GETDWORD(&ent[0x34]);
1750     data->dwFileAttributes = SMB_GETDWORD(&ent[0x38]);
1751
1752     /* copy the long filename */
1753     fnlen = SMB_GETDWORD(&ent[0x3c]);
1754     if ( fnlen > (sizeof(data->cFileName)/sizeof(WCHAR)) )
1755         return FALSE;
1756     MultiByteToWideChar( CP_ACP, 0, &ent[0x5e], fnlen, data->cFileName,
1757                          sizeof(data->cFileName)/sizeof(WCHAR) );
1758
1759     /* copy the short filename */
1760     if ( ent[0x44] > (sizeof(data->cAlternateFileName)/sizeof(WCHAR)) )
1761         return FALSE;
1762     MultiByteToWideChar( CP_ACP, 0, &ent[0x5e + len], ent[0x44], data->cAlternateFileName,
1763                          sizeof(data->cAlternateFileName)/sizeof(WCHAR) );
1764
1765     dir->current++;
1766
1767     return TRUE;
1768 }
1769
1770 BOOL WINAPI SMB_CloseDir(SMB_DIR *dir)
1771 {
1772     RtlFreeHeap(GetProcessHeap(),0,dir->buffer);
1773     RtlFreeHeap(GetProcessHeap(),0,dir->entries);
1774     memset(dir,0,sizeof(*dir));
1775     RtlFreeHeap(GetProcessHeap(),0,dir);
1776     return TRUE;
1777 }