Append filename extension if necessary in IShellFolder::SetNameOf.
[wine] / dlls / netapi32 / netbios.c
1 /* Copyright (c) 2003 Juan Lang
2  *
3  * This library is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Lesser General Public
5  * License as published by the Free Software Foundation; either
6  * version 2.1 of the License, or (at your option) any later version.
7  *
8  * This library is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with this library; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  */
17 #include "config.h"
18 #include "wine/debug.h"
19 #include "nbcmdqueue.h"
20 #include "netbios.h"
21
22 WINE_DEFAULT_DEBUG_CHANNEL(netbios);
23
24 /* This file provides a NetBIOS emulator that implements the NetBIOS interface,
25  * including thread safety and asynchronous call support.  The protocol
26  * implementation is separate, with blocking (synchronous) functions.
27  */
28
29 #define ADAPTERS_INCR 8
30 #define DEFAULT_NUM_SESSIONS 16
31
32 typedef struct _NetBIOSTransportTableEntry
33 {
34     ULONG            id;
35     NetBIOSTransport transport;
36 } NetBIOSTransportTableEntry;
37
38 typedef struct _NetBIOSSession
39 {
40     BOOL  inUse;
41     UCHAR state;
42     UCHAR local_name[NCBNAMSZ];
43     UCHAR remote_name[NCBNAMSZ];
44     void *data;
45 } NetBIOSSession;
46
47 /* This struct needs a little explanation, unfortunately.  enabled is only
48  * used by nbInternalEnum (see).  If transport_id is not 0 and transport
49  * is not NULL, the adapter is considered valid.  (transport is a pointer to
50  * an entry in a NetBIOSTransportTableEntry.)  data has data for the callers of
51  * NetBIOSEnumAdapters to be able to see.  The lana is repeated there, even
52  * though I don't use it internally--it's for transports to use reenabling
53  * adapters using NetBIOSEnableAdapter.
54  */
55 typedef struct _NetBIOSAdapter
56 {
57     BOOL               enabled;
58     BOOL               shuttingDown;
59     LONG               resetting;
60     ULONG              transport_id;
61     NetBIOSTransport  *transport;
62     NetBIOSAdapterImpl impl;
63     struct NBCmdQueue *cmdQueue;
64     CRITICAL_SECTION   cs;
65     DWORD              sessionsLen;
66     NetBIOSSession    *sessions;
67 } NetBIOSAdapter;
68
69 typedef struct _NetBIOSAdapterTable {
70     CRITICAL_SECTION cs;
71     BOOL             enumerated;
72     BOOL             enumerating;
73     UCHAR            tableSize;
74     NetBIOSAdapter  *table;
75 } NetBIOSAdapterTable;
76
77 /* Just enough space for NBT right now */
78 static NetBIOSTransportTableEntry gTransports[1];
79 static UCHAR gNumTransports = 0;
80 static NetBIOSAdapterTable gNBTable;
81
82 static UCHAR nbResizeAdapterTable(UCHAR newSize)
83 {
84     UCHAR ret;
85
86     if (gNBTable.table)
87         gNBTable.table = HeapReAlloc(GetProcessHeap(),
88          HEAP_ZERO_MEMORY, gNBTable.table,
89          newSize * sizeof(NetBIOSAdapter));
90     else
91         gNBTable.table = HeapAlloc(GetProcessHeap(),
92          HEAP_ZERO_MEMORY, newSize * sizeof(NetBIOSAdapter));
93     if (gNBTable.table)
94     {
95         gNBTable.tableSize = newSize;
96         ret = NRC_GOODRET;
97     }
98     else
99         ret = NRC_OSRESNOTAV;
100     return ret;
101 }
102
103 void NetBIOSInit(void)
104 {
105     memset(&gNBTable, 0, sizeof(gNBTable));
106     InitializeCriticalSection(&gNBTable.cs);
107 }
108
109 void NetBIOSShutdown(void)
110 {
111     UCHAR i;
112
113     EnterCriticalSection(&gNBTable.cs);
114     for (i = 0; i < gNBTable.tableSize; i++)
115     {
116         if (gNBTable.table[i].transport &&
117          gNBTable.table[i].transport->cleanupAdapter)
118             gNBTable.table[i].transport->cleanupAdapter(
119              gNBTable.table[i].impl.data);
120     }
121     for (i = 0; i < gNumTransports; i++)
122         if (gTransports[i].transport.cleanup)
123             gTransports[i].transport.cleanup();
124     LeaveCriticalSection(&gNBTable.cs);
125     DeleteCriticalSection(&gNBTable.cs);
126     HeapFree(GetProcessHeap(), 0, gNBTable.table);
127 }
128
129 BOOL NetBIOSRegisterTransport(ULONG id, NetBIOSTransport *transport)
130 {
131     BOOL ret;
132
133     TRACE(": transport 0x%08lx, p %p\n", id, transport);
134     if (!transport)
135         ret = FALSE;
136     else if (gNumTransports >= sizeof(gTransports) / sizeof(gTransports[0]))
137     {
138         FIXME("You tried to add %d transports, but I only have space for %d\n",
139          gNumTransports + 1, sizeof(gTransports) / sizeof(gTransports[0]));
140         ret = FALSE;
141     }
142     else
143     {
144         UCHAR i;
145
146         ret = FALSE;
147         for (i = 0; !ret && i < gNumTransports; i++)
148         {
149             if (gTransports[i].id == id)
150             {
151                 WARN("Replacing NetBIOS transport ID %ld\n", id);
152                 memcpy(&gTransports[i].transport, transport,
153                  sizeof(NetBIOSTransport));
154                 ret = TRUE;
155             }
156         }
157         if (!ret)
158         {
159             gTransports[gNumTransports].id = id;
160             memcpy(&gTransports[gNumTransports].transport, transport,
161              sizeof(NetBIOSTransport));
162             gNumTransports++;
163             ret = TRUE;
164         }
165     }
166     TRACE("returning %d\n", ret);
167     return ret;
168 }
169
170 /* In this, I acquire the table lock to make sure no one else is modifying it.
171  * This is _probably_ overkill since it should only be called during the
172  * context of a NetBIOSEnum call, but just to be safe..
173  */
174 BOOL NetBIOSRegisterAdapter(ULONG transport, DWORD ifIndex, void *data)
175 {
176     BOOL ret;
177     UCHAR i;
178
179     TRACE(": transport 0x%08lx, ifIndex 0x%08lx, data %p\n", transport, ifIndex,
180      data);
181     for (i = 0; i < gNumTransports && gTransports[i].id != transport; i++)
182         ;
183     if (gTransports[i].id == transport)
184     {
185         NetBIOSTransport *transportPtr = &gTransports[i].transport;
186
187         TRACE(": found transport %p for id 0x%08lx\n", transportPtr, transport);
188
189         EnterCriticalSection(&gNBTable.cs);
190         ret = FALSE;
191         for (i = 0; i < gNBTable.tableSize &&
192          gNBTable.table[i].transport != 0; i++)
193             ;
194         if (i == gNBTable.tableSize && gNBTable.tableSize < MAX_LANA + 1)
195         {
196             UCHAR newSize;
197
198             if (gNBTable.tableSize < (MAX_LANA + 1) - ADAPTERS_INCR)
199                 newSize = gNBTable.tableSize + ADAPTERS_INCR;
200             else
201                 newSize = MAX_LANA + 1;
202             nbResizeAdapterTable(newSize);
203         }
204         if (i < gNBTable.tableSize && gNBTable.table[i].transport == 0)
205         {
206             TRACE(": registering as LANA %d\n", i);
207             gNBTable.table[i].transport_id = transport;
208             gNBTable.table[i].transport = transportPtr;
209             gNBTable.table[i].impl.lana = i;
210             gNBTable.table[i].impl.ifIndex = ifIndex;
211             gNBTable.table[i].impl.data = data;
212             gNBTable.table[i].cmdQueue = NBCmdQueueCreate(GetProcessHeap());
213             InitializeCriticalSection(&gNBTable.table[i].cs);
214             gNBTable.table[i].enabled = TRUE;
215             ret = TRUE;
216         }
217         LeaveCriticalSection(&gNBTable.cs);
218     }
219     else
220         ret = FALSE;
221     TRACE("returning %d\n", ret);
222     return ret;
223 }
224
225 /* In this, I acquire the table lock to make sure no one else is modifying it.
226  * This is _probably_ overkill since it should only be called during the
227  * context of a NetBIOSEnum call, but just to be safe..
228  */
229 void NetBIOSEnableAdapter(UCHAR lana)
230 {
231     TRACE(": %d\n", lana);
232     if (lana < gNBTable.tableSize)
233     {
234         EnterCriticalSection(&gNBTable.cs);
235         if (gNBTable.table[lana].transport != 0)
236             gNBTable.table[lana].enabled = TRUE;
237         LeaveCriticalSection(&gNBTable.cs);
238     }
239 }
240
241 static void nbShutdownAdapter(NetBIOSAdapter *adapter)
242 {
243     if (adapter)
244     {
245         adapter->shuttingDown = TRUE;
246         NBCmdQueueCancelAll(adapter->cmdQueue);
247         if (adapter->transport->cleanupAdapter)
248             adapter->transport->cleanupAdapter(adapter->impl.data);
249         NBCmdQueueDestroy(adapter->cmdQueue);
250         DeleteCriticalSection(&adapter->cs);
251         memset(adapter, 0, sizeof(NetBIOSAdapter));
252     }
253 }
254
255 static void nbInternalEnum(void)
256 {
257     UCHAR i;
258
259     EnterCriticalSection(&gNBTable.cs);
260     TRACE("before mark\n");
261     /* mark: */
262     for (i = 0; i < gNBTable.tableSize; i++)
263         if (gNBTable.table[i].enabled && gNBTable.table[i].transport != 0)
264             gNBTable.table[i].enabled = FALSE;
265
266     TRACE("marked, before store, %d transports\n", gNumTransports);
267     /* store adapters: */
268     for (i = 0; i < gNumTransports; i++)
269         if (gTransports[i].transport.enumerate)
270             gTransports[i].transport.enumerate();
271
272     TRACE("before sweep\n");
273     /* sweep: */
274     for (i = 0; i < gNBTable.tableSize; i++)
275         if (!gNBTable.table[i].enabled && gNBTable.table[i].transport != 0)
276             nbShutdownAdapter(&gNBTable.table[i]);
277     gNBTable.enumerated = TRUE;
278     LeaveCriticalSection(&gNBTable.cs);
279 }
280
281 UCHAR NetBIOSNumAdapters(void)
282 {
283     UCHAR ret, i;
284
285     if (!gNBTable.enumerated)
286         nbInternalEnum();
287     for (i = 0, ret = 0; i < gNBTable.tableSize; i++)
288         if (gNBTable.table[i].transport != 0)
289             ret++;
290     return ret;
291 }
292
293 void NetBIOSEnumAdapters(ULONG transport, NetBIOSEnumAdaptersCallback cb,
294  void *closure)
295 {
296     TRACE("transport 0x%08lx, callback %p, closure %p\n", transport, cb,
297      closure);
298     if (cb)
299     {
300         BOOL enumAll = memcmp(&transport, ALL_TRANSPORTS, sizeof(ULONG)) == 0;
301         UCHAR i, numLANAs = 0;
302
303         EnterCriticalSection(&gNBTable.cs);
304         if (!gNBTable.enumerating)
305         {
306             gNBTable.enumerating = TRUE;
307             nbInternalEnum();
308             gNBTable.enumerating = FALSE;
309         }
310         for (i = 0; i < gNBTable.tableSize; i++)
311             if (enumAll || gNBTable.table[i].transport_id == transport)
312                 numLANAs++;
313         if (numLANAs > 0)
314         {
315             UCHAR lanaIndex = 0;
316
317             for (i = 0; i < gNBTable.tableSize; i++)
318                 if (gNBTable.table[i].transport_id != 0 &&
319                  (enumAll || gNBTable.table[i].transport_id == transport))
320                     cb(numLANAs, lanaIndex++, gNBTable.table[i].transport_id,
321                      &gNBTable.table[i].impl, closure);
322         }
323         LeaveCriticalSection(&gNBTable.cs);
324     }
325 }
326
327 static NetBIOSAdapter *nbGetAdapter(UCHAR lana)
328 {
329     NetBIOSAdapter *ret = NULL;
330
331     TRACE(": lana %d, num allocated adapters %d\n", lana, gNBTable.tableSize);
332     if (lana < gNBTable.tableSize && gNBTable.table[lana].transport_id != 0
333      && gNBTable.table[lana].transport)
334         ret = &gNBTable.table[lana];
335     TRACE("returning %p\n", ret);
336     return ret;
337 }
338
339 static UCHAR nbEnum(PNCB ncb)
340 {
341     PLANA_ENUM lanas = (PLANA_ENUM)ncb->ncb_buffer;
342     UCHAR i, ret;
343
344     TRACE(": ncb %p\n", ncb);
345
346     if (!lanas)
347         ret = NRC_BUFLEN;
348     else if (ncb->ncb_length < sizeof(LANA_ENUM))
349         ret = NRC_BUFLEN;
350     else
351     {
352         nbInternalEnum();
353         lanas->length = 0;
354         for (i = 0; i < gNBTable.tableSize; i++)
355             if (gNBTable.table[i].transport)
356             {
357                 lanas->length++;
358                 lanas->lana[i] = i;
359             }
360         ret = NRC_GOODRET;
361     }
362     TRACE("returning 0x%02x\n", ret);
363     return ret;
364 }
365
366 static UCHAR nbInternalHangup(NetBIOSAdapter *adapter, NetBIOSSession *session);
367
368 static UCHAR nbCancel(NetBIOSAdapter *adapter, PNCB ncb)
369 {
370     UCHAR ret;
371
372     TRACE(": adapter %p, ncb %p\n", adapter, ncb);
373
374     if (!adapter) return NRC_BRIDGE;
375     if (!ncb) return NRC_INVADDRESS;
376
377     switch (ncb->ncb_command & 0x7f)
378     {
379         case NCBCANCEL:
380         case NCBADDNAME:
381         case NCBADDGRNAME:
382         case NCBDELNAME:
383         case NCBRESET:
384         case NCBSSTAT:
385             ret = NRC_CANCEL;
386             break;
387
388         /* NCBCALL, NCBCHAINSEND/NCBSEND, NCBHANGUP all close the associated
389          * session if cancelled */
390         case NCBCALL:
391         case NCBSEND:
392         case NCBCHAINSEND:
393         case NCBSENDNA:
394         case NCBCHAINSENDNA:
395         case NCBHANGUP:
396         {
397             if (ncb->ncb_lsn >= adapter->sessionsLen)
398                 ret = NRC_SNUMOUT;
399             else if (!adapter->sessions[ncb->ncb_lsn].inUse)
400                 ret = NRC_SNUMOUT;
401             else
402             {
403                 ret = NBCmdQueueCancel(adapter->cmdQueue, ncb);
404                 if (ret == NRC_CMDCAN || ret == NRC_CANOCCR)
405                     nbInternalHangup(adapter, &adapter->sessions[ncb->ncb_lsn]);
406             }
407             break;
408         }
409
410         default:
411             ret = NBCmdQueueCancel(adapter->cmdQueue, ncb);
412     }
413     TRACE("returning 0x%02x\n", ret);
414     return ret;
415 }
416
417 /* Resizes adapter to contain space for at least sessionsLen sessions.
418  * If allocating more space for sessions, sets the adapter's sessionsLen to
419  * sessionsLen.  If the adapter's sessionsLen was already at least sessionsLen,
420  * does nothing.  Does not modify existing sessions.  Assumes the adapter is
421  * locked.
422  * Returns NRC_GOODRET on success, and something else on failure.
423  */
424 static UCHAR nbResizeAdapter(NetBIOSAdapter *adapter, UCHAR sessionsLen)
425 {
426     UCHAR ret = NRC_GOODRET;
427
428     if (adapter && adapter->sessionsLen < sessionsLen)
429     {
430         NetBIOSSession *newSessions;
431
432         if (adapter->sessions)
433             newSessions = HeapReAlloc(GetProcessHeap(),
434              HEAP_ZERO_MEMORY, adapter->sessions, sessionsLen *
435              sizeof(NetBIOSSession));
436         else
437             newSessions = HeapAlloc(GetProcessHeap(),
438              HEAP_ZERO_MEMORY, sessionsLen * sizeof(NetBIOSSession));
439         if (newSessions)
440         {
441             adapter->sessions = newSessions;
442             adapter->sessionsLen = sessionsLen;
443         }
444         else
445             ret = NRC_OSRESNOTAV;
446     }
447     return ret;
448 }
449
450 static UCHAR nbReset(NetBIOSAdapter *adapter, PNCB ncb)
451 {
452     UCHAR ret;
453
454     TRACE(": adapter %p, ncb %p\n", adapter, ncb);
455
456     if (!adapter) return NRC_BRIDGE;
457     if (!ncb) return NRC_INVADDRESS;
458
459     if (InterlockedIncrement(&adapter->resetting) == 1)
460     {
461         UCHAR i, resizeTo;
462
463         NBCmdQueueCancelAll(adapter->cmdQueue);
464
465         EnterCriticalSection(&adapter->cs);
466         for (i = 0; i < adapter->sessionsLen; i++)
467             if (adapter->sessions[i].inUse)
468                 nbInternalHangup(adapter, &adapter->sessions[i]);
469         if (!ncb->ncb_lsn)
470             resizeTo = ncb->ncb_callname[0] == 0 ? DEFAULT_NUM_SESSIONS :
471              ncb->ncb_callname[0];
472         else if (adapter->sessionsLen == 0)
473             resizeTo = DEFAULT_NUM_SESSIONS;
474         else
475             resizeTo = 0;
476         if (resizeTo > 0)
477             ret = nbResizeAdapter(adapter, resizeTo);
478         else
479             ret = NRC_GOODRET;
480         LeaveCriticalSection(&adapter->cs);
481     }
482     else
483         ret = NRC_TOOMANY;
484     InterlockedDecrement(&adapter->resetting);
485     TRACE("returning 0x%02x\n", ret);
486     return ret;
487 }
488
489 static UCHAR nbSStat(NetBIOSAdapter *adapter, PNCB ncb)
490 {
491     UCHAR ret, i, spaceFor;
492     PSESSION_HEADER sstat;
493
494     TRACE(": adapter %p, NCB %p\n", adapter, ncb);
495
496     if (!adapter) return NRC_BADDR;
497     if (adapter->sessionsLen == 0) return NRC_ENVNOTDEF;
498     if (!ncb) return NRC_INVADDRESS;
499     if (!ncb->ncb_buffer) return NRC_BADDR;
500     if (ncb->ncb_length < sizeof(SESSION_HEADER)) return NRC_BUFLEN;
501
502     sstat = (PSESSION_HEADER)ncb->ncb_buffer;
503     ret = NRC_GOODRET;
504     memset(sstat, 0, sizeof(SESSION_HEADER));
505     spaceFor = (ncb->ncb_length - sizeof(SESSION_HEADER)) /
506      sizeof(SESSION_BUFFER);
507     EnterCriticalSection(&adapter->cs);
508     for (i = 0; ret == NRC_GOODRET && i < adapter->sessionsLen; i++)
509     {
510         if (adapter->sessions[i].inUse && (ncb->ncb_name[0] == '*' ||
511          !memcmp(ncb->ncb_name, adapter->sessions[i].local_name, NCBNAMSZ)))
512         {
513             if (sstat->num_sess < spaceFor)
514             {
515                 PSESSION_BUFFER buf;
516                
517                 buf = (PSESSION_BUFFER)((PUCHAR)sstat + sizeof(SESSION_HEADER)
518                  + sstat->num_sess * sizeof(SESSION_BUFFER));
519                 buf->lsn = i;
520                 buf->state = adapter->sessions[i].state;
521                 memcpy(buf->local_name, adapter->sessions[i].local_name,
522                  NCBNAMSZ);
523                 memcpy(buf->remote_name, adapter->sessions[i].remote_name,
524                  NCBNAMSZ);
525                 buf->rcvs_outstanding = buf->sends_outstanding = 0;
526                 sstat->num_sess++;
527             }
528             else
529                 ret = NRC_BUFLEN;
530         }
531     }
532     LeaveCriticalSection(&adapter->cs);
533
534     TRACE("returning 0x%02x\n", ret);
535     return ret;
536 }
537
538 static UCHAR nbCall(NetBIOSAdapter *adapter, PNCB ncb)
539 {
540     UCHAR ret, i;
541
542     TRACE(": adapter %p, NCB %p\n", adapter, ncb);
543
544     if (!adapter) return NRC_BRIDGE;
545     if (adapter->sessionsLen == 0) return NRC_ENVNOTDEF;
546     if (!adapter->transport->call) return NRC_ILLCMD;
547     if (!ncb) return NRC_INVADDRESS;
548
549     EnterCriticalSection(&adapter->cs);
550     for (i = 0; i < adapter->sessionsLen && adapter->sessions[i].inUse; i++)
551         ;
552     if (i < adapter->sessionsLen)
553     {
554         adapter->sessions[i].inUse = TRUE;
555         adapter->sessions[i].state = CALL_PENDING;
556         memcpy(adapter->sessions[i].local_name, ncb->ncb_name, NCBNAMSZ);
557         memcpy(adapter->sessions[i].remote_name, ncb->ncb_callname, NCBNAMSZ);
558         ret = NRC_GOODRET;
559     }
560     else
561         ret = NRC_LOCTFUL;
562     LeaveCriticalSection(&adapter->cs);
563
564     if (ret == NRC_GOODRET)
565     {
566         ret = adapter->transport->call(adapter->impl.data, ncb,
567          &adapter->sessions[i].data);
568         if (ret == NRC_GOODRET)
569         {
570             ncb->ncb_lsn = i;
571             adapter->sessions[i].state = SESSION_ESTABLISHED;
572         }
573         else
574         {
575             adapter->sessions[i].inUse = FALSE;
576             adapter->sessions[i].state = 0;
577         }
578     }
579     TRACE("returning 0x%02x\n", ret);
580     return ret;
581 }
582
583 static UCHAR nbSend(NetBIOSAdapter *adapter, PNCB ncb)
584 {
585     UCHAR ret;
586     NetBIOSSession *session;
587
588     if (!adapter) return NRC_BRIDGE;
589     if (!adapter->transport->send) return NRC_ILLCMD;
590     if (!ncb) return NRC_INVADDRESS;
591     if (ncb->ncb_lsn >= adapter->sessionsLen) return NRC_SNUMOUT;
592     if (!adapter->sessions[ncb->ncb_lsn].inUse) return NRC_SNUMOUT;
593     if (!ncb->ncb_buffer) return NRC_BADDR;
594
595     session = &adapter->sessions[ncb->ncb_lsn];
596     if (session->state != SESSION_ESTABLISHED)
597         ret = NRC_SNUMOUT;
598     else
599         ret = adapter->transport->send(adapter->impl.data, session->data, ncb);
600     return ret;
601 }
602
603 static UCHAR nbRecv(NetBIOSAdapter *adapter, PNCB ncb)
604 {
605     UCHAR ret;
606     NetBIOSSession *session;
607
608     if (!adapter) return NRC_BRIDGE;
609     if (!adapter->transport->recv) return NRC_ILLCMD;
610     if (!ncb) return NRC_INVADDRESS;
611     if (ncb->ncb_lsn >= adapter->sessionsLen) return NRC_SNUMOUT;
612     if (!adapter->sessions[ncb->ncb_lsn].inUse) return NRC_SNUMOUT;
613     if (!ncb->ncb_buffer) return NRC_BADDR;
614
615     session = &adapter->sessions[ncb->ncb_lsn];
616     if (session->state != SESSION_ESTABLISHED)
617         ret = NRC_SNUMOUT;
618     else
619         ret = adapter->transport->recv(adapter->impl.data, session->data, ncb);
620     return ret;
621 }
622
623 static UCHAR nbInternalHangup(NetBIOSAdapter *adapter, NetBIOSSession *session)
624 {
625     UCHAR ret;
626
627     if (!adapter) return NRC_BRIDGE;
628     if (!session) return NRC_SNUMOUT;
629
630     if (adapter->transport->hangup)
631         ret = adapter->transport->hangup(adapter->impl.data, session->data);
632     else
633         ret = NRC_ILLCMD;
634     EnterCriticalSection(&adapter->cs);
635     memset(session, 0, sizeof(NetBIOSSession));
636     LeaveCriticalSection(&adapter->cs);
637     return NRC_GOODRET;
638 }
639
640 static UCHAR nbHangup(NetBIOSAdapter *adapter, PNCB ncb)
641 {
642     UCHAR ret;
643     NetBIOSSession *session;
644
645     if (!adapter) return NRC_BRIDGE;
646     if (!ncb) return NRC_INVADDRESS;
647     if (ncb->ncb_lsn >= adapter->sessionsLen) return NRC_SNUMOUT;
648     if (!adapter->sessions[ncb->ncb_lsn].inUse) return NRC_SNUMOUT;
649
650     session = &adapter->sessions[ncb->ncb_lsn];
651     if (session->state != SESSION_ESTABLISHED)
652         ret = NRC_SNUMOUT;
653     else
654     {
655         session->state = HANGUP_PENDING;
656         ret = nbInternalHangup(adapter, session);
657     }
658     return ret;
659 }
660
661 void NetBIOSHangupSession(PNCB ncb)
662 {
663     NetBIOSAdapter *adapter;
664
665     if (!ncb) return;
666
667     adapter = nbGetAdapter(ncb->ncb_lana_num);
668     if (adapter)
669     {
670         if (ncb->ncb_lsn < adapter->sessionsLen &&
671          adapter->sessions[ncb->ncb_lsn].inUse)
672             nbHangup(adapter, ncb);
673     }
674 }
675
676 static UCHAR nbAStat(NetBIOSAdapter *adapter, PNCB ncb)
677 {
678     UCHAR ret;
679
680     if (!adapter) return NRC_BRIDGE;
681     if (!adapter->transport->astat) return NRC_ILLCMD;
682     if (!ncb) return NRC_INVADDRESS;
683     if (!ncb->ncb_buffer) return NRC_BADDR;
684     if (ncb->ncb_length < sizeof(ADAPTER_STATUS)) return NRC_BUFLEN;
685
686     ret = adapter->transport->astat(adapter->impl.data, ncb);
687     if (ncb->ncb_callname[0] == '*')
688     {
689         PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer;
690
691         astat->max_sess = astat->max_cfg_sess = adapter->sessionsLen;
692     }
693     return ret;
694 }
695
696 static UCHAR nbDispatch(NetBIOSAdapter *adapter, PNCB ncb)
697 {
698     UCHAR ret, cmd;
699
700     TRACE(": adapter %p, ncb %p\n", adapter, ncb);
701
702     if (!adapter) return NRC_BRIDGE;
703     if (!ncb) return NRC_INVADDRESS;
704
705     cmd = ncb->ncb_command & 0x7f;
706     if (cmd == NCBRESET)
707         ret = nbReset(adapter, ncb);
708     else
709     {
710         ret = NBCmdQueueAdd(adapter->cmdQueue, ncb);
711         if (ret == NRC_GOODRET)
712         {
713             switch (cmd)
714             {
715                 case NCBCALL:
716                     ret = nbCall(adapter, ncb);
717                     break;
718
719                 /* WinNT doesn't chain sends, it always sends immediately.
720                  * Doubt there's any real significance to the NA variants.
721                  */
722                 case NCBSEND:
723                 case NCBSENDNA:
724                 case NCBCHAINSEND:
725                 case NCBCHAINSENDNA:
726                     ret = nbSend(adapter, ncb);
727                     break;
728
729                 case NCBRECV:
730                     ret = nbRecv(adapter, ncb);
731                     break;
732
733                 case NCBHANGUP:
734                     ret = nbHangup(adapter, ncb);
735                     break;
736
737                 case NCBASTAT:
738                     ret = nbAStat(adapter, ncb);
739                     break;
740
741                 case NCBFINDNAME:
742                     if (adapter->transport->findName)
743                         ret = adapter->transport->findName(adapter->impl.data,
744                          ncb);
745                     else
746                         ret = NRC_ILLCMD;
747                     break;
748
749                 default:
750                     FIXME("(%p): command code 0x%02x\n", ncb, ncb->ncb_command);
751                     ret = NRC_ILLCMD;
752             }
753             NBCmdQueueComplete(adapter->cmdQueue, ncb, ret);
754         }
755     }
756     TRACE("returning 0x%02x\n", ret);
757     return ret;
758 }
759
760 static DWORD WINAPI nbCmdThread(LPVOID lpVoid)
761 {
762     PNCB ncb = (PNCB)lpVoid;
763
764     if (ncb)
765     {
766         UCHAR ret;
767         NetBIOSAdapter *adapter = nbGetAdapter(ncb->ncb_lana_num);
768
769         if (adapter)
770             ret = nbDispatch(adapter, ncb);
771         else
772             ret = NRC_BRIDGE;
773         ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret;
774         if (ncb->ncb_post)
775             ncb->ncb_post(ncb);
776         else if (ncb->ncb_event)
777             SetEvent(ncb->ncb_event);
778     }
779     return 0;
780 }
781
782 UCHAR WINAPI Netbios(PNCB ncb)
783 {
784     UCHAR ret, cmd;
785
786     TRACE("ncb = %p\n", ncb);
787
788     if (!ncb) return NRC_INVADDRESS;
789
790     TRACE("ncb_command 0x%02x, ncb_lana_num %d, ncb_buffer %p, ncb_length %d\n",
791      ncb->ncb_command, ncb->ncb_lana_num, ncb->ncb_buffer, ncb->ncb_length);
792     cmd = ncb->ncb_command & 0x7f;
793
794     if (cmd == NCBENUM)
795         ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret = nbEnum(ncb);
796     else if (cmd == NCBADDNAME)
797     {
798         FIXME("NCBADDNAME: stub, returning success");
799         ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret = NRC_GOODRET;
800     }
801     else
802     {
803         NetBIOSAdapter *adapter;
804
805         /* Apps not specifically written for WinNT won't do an NCBENUM first,
806          * so make sure the table has been enumerated at least once
807          */
808         if (!gNBTable.enumerated)
809             nbInternalEnum();
810         adapter = nbGetAdapter(ncb->ncb_lana_num);
811         if (!adapter)
812             ret = NRC_BRIDGE;
813         else
814         {
815             if (adapter->shuttingDown)
816                 ret = NRC_IFBUSY;
817             else if (adapter->resetting)
818                 ret = NRC_TOOMANY;
819             else
820             {
821                 /* non-asynch commands first */
822                 if (cmd == NCBCANCEL)
823                     ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret =
824                      nbCancel(adapter, ncb);
825                 else if (cmd == NCBSSTAT)
826                     ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret =
827                      nbSStat(adapter, ncb);
828                 else
829                 {
830                     if (ncb->ncb_command & ASYNCH)
831                     {
832                         HANDLE thread = CreateThread(NULL, 0, nbCmdThread, ncb,
833                          CREATE_SUSPENDED, NULL);
834
835                         if (thread != NULL)
836                         {
837                             ncb->ncb_retcode = ncb->ncb_cmd_cplt = NRC_PENDING;
838                             if (ncb->ncb_event)
839                                 ResetEvent(ncb->ncb_event);
840                             ResumeThread(thread);
841                             CloseHandle(thread);
842                             ret = NRC_GOODRET;
843                         }
844                         else
845                         ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret =
846                          NRC_OSRESNOTAV;
847                     }
848                     else
849                         ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret =
850                          nbDispatch(adapter, ncb);
851                 }
852             }
853         }
854     }
855     TRACE("returning 0x%02x\n", ret);
856     return ret;
857 }