New netapi32 vendor import
authorGé van Geldorp <ge@gse.nl>
Sun, 20 Nov 2005 21:19:17 +0000 (21:19 +0000)
committerGé van Geldorp <ge@gse.nl>
Sun, 20 Nov 2005 21:19:17 +0000 (21:19 +0000)
svn path=/trunk/; revision=19391

23 files changed:
reactos/include/wine/lmbrowsr.h [new file with mode: 0644]
reactos/lib/aclui/precomp.h
reactos/lib/directory.xml
reactos/lib/netapi32/access.c [new file with mode: 0644]
reactos/lib/netapi32/apibuf.c [new file with mode: 0644]
reactos/lib/netapi32/browsr.c [new file with mode: 0644]
reactos/lib/netapi32/nbcmdqueue.c [new file with mode: 0644]
reactos/lib/netapi32/nbcmdqueue.h [new file with mode: 0644]
reactos/lib/netapi32/nbnamecache.c [new file with mode: 0644]
reactos/lib/netapi32/nbnamecache.h [new file with mode: 0644]
reactos/lib/netapi32/nbt.c [new file with mode: 0644]
reactos/lib/netapi32/netapi32.c [new file with mode: 0644]
reactos/lib/netapi32/netapi32.spec [new file with mode: 0644]
reactos/lib/netapi32/netapi32.xml [new file with mode: 0644]
reactos/lib/netapi32/netapi32_misc.h [new file with mode: 0644]
reactos/lib/netapi32/netbios.c [new file with mode: 0644]
reactos/lib/netapi32/netbios.h [new file with mode: 0644]
reactos/lib/netapi32/wksta.c [new file with mode: 0644]
reactos/media/doc/README.WINE
reactos/w32api/include/lm.h
reactos/w32api/include/lmbrowsr.h [deleted file]
reactos/w32api/include/lmjoin.h [new file with mode: 0644]
reactos/w32api/include/ntsecapi.h

diff --git a/reactos/include/wine/lmbrowsr.h b/reactos/include/wine/lmbrowsr.h
new file mode 100644 (file)
index 0000000..9e6abf6
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2002 Andriy Palamarchuk
+ *
+ * Browser NET API calls
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __WINE_LMBROWSR_H
+#define __WINE_LMBROWSR_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct _BROWSER_EMULATED_DOMAIN {
+    LPWSTR DomainName;
+    LPWSTR EmulatedServerName;
+    DWORD Role;
+} BROWSER_EMULATED_DOMAIN, *PBROWSER_EMULATED_DOMAIN;
+
+NET_API_STATUS WINAPI I_BrowserSetNetlogonState(
+    LPWSTR ServerName, LPWSTR DomainName, LPWSTR EmulatedServerName,
+    DWORD Role);
+
+NET_API_STATUS WINAPI I_BrowserQueryEmulatedDomains(
+    LPWSTR ServerName, PBROWSER_EMULATED_DOMAIN *EmulatedDomains,
+    LPDWORD EntriesRead);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
index 1581d31..ce0f130 100644 (file)
@@ -5,6 +5,7 @@
 #include <prsht.h>
 #include <aclui.h>
 #include <sddl.h>
+#include <winternl.h>
 #include <ntsecapi.h>
 #if SUPPORT_UXTHEME
 #include <uxtheme.h>
index b5e9e30..4ce3571 100644 (file)
 <directory name="mswsock">
        <xi:include href="mswsock/mswsock.xml" />
 </directory>
+<directory name="netapi32">
+       <xi:include href="netapi32/netapi32.xml" />
+</directory>
 <directory name="netcfgx">
        <xi:include href="netcfgx/netcfgx.xml" />
 </directory>
diff --git a/reactos/lib/netapi32/access.c b/reactos/lib/netapi32/access.c
new file mode 100644 (file)
index 0000000..e3e0a36
--- /dev/null
@@ -0,0 +1,580 @@
+/*
+ * Copyright 2002 Andriy Palamarchuk
+ *
+ * netapi32 access functions
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "lmcons.h"
+#include "lmaccess.h"
+#include "lmapibuf.h"
+#include "lmerr.h"
+#include "netapi32_misc.h"
+#include "wine/debug.h"
+#include "wine/unicode.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(netapi32);
+
+static const WCHAR sAdminUserName[] = {'A','d','m','i','n','i','s','t','r','a','t',
+                                'o','r',0};
+static const WCHAR sGuestUserName[] = {'G','u','e','s','t',0};
+
+/************************************************************
+ *                NETAPI_ValidateServername
+ *
+ * Validates server name
+ */
+static NET_API_STATUS NETAPI_ValidateServername(LPCWSTR ServerName)
+{
+    if (ServerName)
+    {
+        if (ServerName[0] == 0)
+            return ERROR_BAD_NETPATH;
+        else if (
+            ((ServerName[0] == '\\') &&
+             (ServerName[1] != '\\'))
+            ||
+            ((ServerName[0] == '\\') &&
+             (ServerName[1] == '\\') &&
+             (ServerName[2] == 0))
+            )
+            return ERROR_INVALID_NAME;
+    }
+    return NERR_Success;
+}
+
+/************************************************************
+ *                NETAPI_IsKnownUser
+ *
+ * Checks whether the user name indicates current user.
+ */
+static BOOL NETAPI_IsKnownUser(LPCWSTR UserName)
+{
+    DWORD dwSize = UNLEN + 1;
+    BOOL Result;
+    LPWSTR buf;
+
+    if (!lstrcmpW(UserName, sAdminUserName) ||
+        !lstrcmpW(UserName, sGuestUserName))
+        return TRUE;
+    NetApiBufferAllocate(dwSize * sizeof(WCHAR), (LPVOID *) &buf);
+    Result = GetUserNameW(buf, &dwSize);
+
+    Result = Result && !lstrcmpW(UserName, buf);
+    NetApiBufferFree(buf);
+
+    return Result;
+}
+
+#define NETAPI_ForceKnownUser(UserName, FailureCode) \
+    if (!NETAPI_IsKnownUser(UserName)) \
+    { \
+        FIXME("Can't find information for user %s\n", \
+              debugstr_w(UserName)); \
+        return FailureCode; \
+    }
+
+/************************************************************
+ *                NetUserAdd (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetUserAdd(LPCWSTR servername,
+                  DWORD level, LPBYTE bufptr, LPDWORD parm_err)
+{
+    NET_API_STATUS status;
+    FIXME("(%s, %ld, %p, %p) stub!\n", debugstr_w(servername), level, bufptr, parm_err);
+
+    status = NETAPI_ValidateServername(servername);
+    if (status != NERR_Success)
+        return status;
+    
+    if ((bufptr != NULL) && (level > 0) && (level <= 4))
+    {
+        PUSER_INFO_1 ui = (PUSER_INFO_1) bufptr;
+        TRACE("usri%ld_name: %s\n", level, debugstr_w(ui->usri1_name));
+        TRACE("usri%ld_password: %s\n", level, debugstr_w(ui->usri1_password));
+        TRACE("usri%ld_comment: %s\n", level, debugstr_w(ui->usri1_comment));
+    }
+    return status;
+}
+
+/************************************************************
+ *                NetUserDel  (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetUserDel(LPCWSTR servername, LPCWSTR username)
+{
+    NET_API_STATUS status;
+    FIXME("(%s, %s) stub!\n", debugstr_w(servername), debugstr_w(username));
+
+    status = NETAPI_ValidateServername(servername);
+    if (status != NERR_Success)
+        return status;
+
+    if (!NETAPI_IsKnownUser(username))
+        return NERR_UserNotFound;
+
+    /* Delete the user here */
+    return status;
+}
+
+/************************************************************
+ *                NetUserGetInfo  (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI
+NetUserGetInfo(LPCWSTR servername, LPCWSTR username, DWORD level,
+               LPBYTE* bufptr)
+{
+    NET_API_STATUS status;
+    TRACE("(%s, %s, %ld, %p)\n", debugstr_w(servername), debugstr_w(username),
+          level, bufptr);
+    status = NETAPI_ValidateServername(servername);
+    if (status != NERR_Success)
+        return status;
+    NETAPI_ForceLocalComputer(servername, NERR_InvalidComputer);
+    NETAPI_ForceKnownUser(username, NERR_UserNotFound);
+
+    switch (level)
+    {
+    case 0:
+    {
+        PUSER_INFO_0 ui;
+        int name_sz;
+
+        name_sz = lstrlenW(username) + 1;
+
+        /* set up buffer */
+        NetApiBufferAllocate(sizeof(USER_INFO_0) + name_sz * sizeof(WCHAR),
+                             (LPVOID *) bufptr);
+
+        ui = (PUSER_INFO_0) *bufptr;
+        ui->usri0_name = (LPWSTR) (*bufptr + sizeof(USER_INFO_0));
+
+        /* get data */
+        lstrcpyW(ui->usri0_name, username);
+        break;
+    }
+
+    case 10:
+    {
+        PUSER_INFO_10 ui;
+        PUSER_INFO_0 ui0;
+        NET_API_STATUS status;
+        /* sizes of the field buffers in WCHARS */
+        int name_sz, comment_sz, usr_comment_sz, full_name_sz;
+
+        comment_sz = 1;
+        usr_comment_sz = 1;
+        full_name_sz = 1;
+
+        /* get data */
+        status = NetUserGetInfo(servername, username, 0, (LPBYTE *) &ui0);
+        if (status != NERR_Success)
+        {
+            NetApiBufferFree(ui0);
+            return status;
+        }
+        name_sz = lstrlenW(ui0->usri0_name) + 1;
+
+        /* set up buffer */
+        NetApiBufferAllocate(sizeof(USER_INFO_10) +
+                             (name_sz + comment_sz + usr_comment_sz +
+                              full_name_sz) * sizeof(WCHAR),
+                             (LPVOID *) bufptr);
+        ui = (PUSER_INFO_10) *bufptr;
+        ui->usri10_name = (LPWSTR) (*bufptr + sizeof(USER_INFO_10));
+        ui->usri10_comment = (LPWSTR) (
+            ((PBYTE) ui->usri10_name) + name_sz * sizeof(WCHAR));
+        ui->usri10_usr_comment = (LPWSTR) (
+            ((PBYTE) ui->usri10_comment) + comment_sz * sizeof(WCHAR));
+        ui->usri10_full_name = (LPWSTR) (
+            ((PBYTE) ui->usri10_usr_comment) + usr_comment_sz * sizeof(WCHAR));
+
+        /* set data */
+        lstrcpyW(ui->usri10_name, ui0->usri0_name);
+        NetApiBufferFree(ui0);
+        ui->usri10_comment[0] = 0;
+        ui->usri10_usr_comment[0] = 0;
+        ui->usri10_full_name[0] = 0;
+        break;
+    }
+
+    case 1:
+      {
+        static const WCHAR homedirW[] = {'H','O','M','E',0};
+        PUSER_INFO_1 ui;
+        PUSER_INFO_0 ui0;
+        NET_API_STATUS status;
+        /* sizes of the field buffers in WCHARS */
+        int name_sz, password_sz, home_dir_sz, comment_sz, script_path_sz;
+
+        password_sz = 1; /* not filled out for security reasons for NetUserGetInfo*/
+        comment_sz = 1;
+        script_path_sz = 1;
+
+       /* get data */
+        status = NetUserGetInfo(servername, username, 0, (LPBYTE *) &ui0);
+        if (status != NERR_Success)
+        {
+            NetApiBufferFree(ui0);
+            return status;
+        }
+        name_sz = lstrlenW(ui0->usri0_name) + 1;
+        home_dir_sz = GetEnvironmentVariableW(homedirW, NULL,0);
+        /* set up buffer */
+        NetApiBufferAllocate(sizeof(USER_INFO_1) +
+                             (name_sz + password_sz + home_dir_sz +
+                              comment_sz + script_path_sz) * sizeof(WCHAR),
+                             (LPVOID *) bufptr);
+
+        ui = (PUSER_INFO_1) *bufptr;
+        ui->usri1_name = (LPWSTR) (ui + 1);
+        ui->usri1_password = ui->usri1_name + name_sz;
+        ui->usri1_home_dir = ui->usri1_password + password_sz;
+        ui->usri1_comment = ui->usri1_home_dir + home_dir_sz;
+        ui->usri1_script_path = ui->usri1_comment + comment_sz;
+        /* set data */
+        lstrcpyW(ui->usri1_name, ui0->usri0_name);
+        NetApiBufferFree(ui0);
+        ui->usri1_password[0] = 0;
+        ui->usri1_password_age = 0;
+        ui->usri1_priv = 0;
+        GetEnvironmentVariableW(homedirW, ui->usri1_home_dir,home_dir_sz);
+        ui->usri1_comment[0] = 0;
+        ui->usri1_flags = 0;
+        ui->usri1_script_path[0] = 0;
+        break;
+      }
+    case 2:
+    case 3:
+    case 4:
+    case 11:
+    case 20:
+    case 23:
+    case 1003:
+    case 1005:
+    case 1006:
+    case 1007:
+    case 1008:
+    case 1009:
+    case 1010:
+    case 1011:
+    case 1012:
+    case 1013:
+    case 1014:
+    case 1017:
+    case 1018:
+    case 1020:
+    case 1023:
+    case 1024:
+    case 1025:
+    case 1051:
+    case 1052:
+    case 1053:
+    {
+        FIXME("Level %ld is not implemented\n", level);
+        break;
+    }
+    default:
+        ERR("Invalid level %ld is specified\n", level);
+        return ERROR_INVALID_LEVEL;
+    }
+    return NERR_Success;
+}
+
+
+
+/************************************************************
+ *                NetUserEnum  (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI
+NetUserEnum(LPCWSTR servername, DWORD level, DWORD filter, LPBYTE* bufptr,
+           DWORD prefmaxlen, LPDWORD entriesread, LPDWORD totalentries,
+           LPDWORD resume_handle)
+{
+  FIXME("(%s,%ld, 0x%ld,%p,%ld,%p,%p,%p) stub!\n", debugstr_w(servername), level,
+        filter, bufptr, prefmaxlen, entriesread, totalentries, resume_handle);
+
+  return ERROR_ACCESS_DENIED;
+}
+
+/************************************************************
+ *                ACCESS_QueryAdminDisplayInformation
+ *
+ *  Creates a buffer with information for the Admin User
+ */
+static void ACCESS_QueryAdminDisplayInformation(PNET_DISPLAY_USER *buf, PDWORD pdwSize)
+{
+    static const WCHAR sAdminUserName[] = {
+        'A','d','m','i','n','i','s','t','r','a','t','o','r',0};
+
+    /* sizes of the field buffers in WCHARS */
+    int name_sz, comment_sz, full_name_sz;
+    PNET_DISPLAY_USER usr;
+
+    /* set up buffer */
+    name_sz = lstrlenW(sAdminUserName);
+    comment_sz = 1;
+    full_name_sz = 1;
+    
+    *pdwSize = sizeof(NET_DISPLAY_USER);
+    *pdwSize += (name_sz + comment_sz + full_name_sz) * sizeof(WCHAR);
+    NetApiBufferAllocate(*pdwSize, (LPVOID *) buf);
+
+    usr = *buf;
+    usr->usri1_name = (LPWSTR) ((PBYTE) usr + sizeof(NET_DISPLAY_USER));
+    usr->usri1_comment = (LPWSTR) (
+        ((PBYTE) usr->usri1_name) + name_sz * sizeof(WCHAR));
+    usr->usri1_full_name = (LPWSTR) (
+        ((PBYTE) usr->usri1_comment) + comment_sz * sizeof(WCHAR));
+
+    /* set data */
+    lstrcpyW(usr->usri1_name, sAdminUserName);
+    usr->usri1_comment[0] = 0;
+    usr->usri1_flags = UF_SCRIPT | UF_NORMAL_ACCOUNT | UF_DONT_EXPIRE_PASSWD;
+    usr->usri1_full_name[0] = 0;
+    usr->usri1_user_id = 500;
+    usr->usri1_next_index = 0;
+}
+
+/************************************************************
+ *                ACCESS_QueryGuestDisplayInformation
+ *
+ *  Creates a buffer with information for the Guest User
+ */
+static void ACCESS_QueryGuestDisplayInformation(PNET_DISPLAY_USER *buf, PDWORD pdwSize)
+{
+    static const WCHAR sGuestUserName[] = {
+        'G','u','e','s','t',0 };
+
+    /* sizes of the field buffers in WCHARS */
+    int name_sz, comment_sz, full_name_sz;
+    PNET_DISPLAY_USER usr;
+
+    /* set up buffer */
+    name_sz = lstrlenW(sGuestUserName);
+    comment_sz = 1;
+    full_name_sz = 1;
+    
+    *pdwSize = sizeof(NET_DISPLAY_USER);
+    *pdwSize += (name_sz + comment_sz + full_name_sz) * sizeof(WCHAR);
+    NetApiBufferAllocate(*pdwSize, (LPVOID *) buf);
+
+    usr = *buf;
+    usr->usri1_name = (LPWSTR) ((PBYTE) usr + sizeof(NET_DISPLAY_USER));
+    usr->usri1_comment = (LPWSTR) (
+        ((PBYTE) usr->usri1_name) + name_sz * sizeof(WCHAR));
+    usr->usri1_full_name = (LPWSTR) (
+        ((PBYTE) usr->usri1_comment) + comment_sz * sizeof(WCHAR));
+
+    /* set data */
+    lstrcpyW(usr->usri1_name, sGuestUserName);
+    usr->usri1_comment[0] = 0;
+    usr->usri1_flags = UF_ACCOUNTDISABLE | UF_SCRIPT | UF_NORMAL_ACCOUNT |
+        UF_DONT_EXPIRE_PASSWD;
+    usr->usri1_full_name[0] = 0;
+    usr->usri1_user_id = 500;
+    usr->usri1_next_index = 0;
+}
+
+/************************************************************
+ *                NetQueryDisplayInformation  (NETAPI32.@)
+ * Copies NET_DISPLAY_USER record.
+ */
+static void ACCESS_CopyDisplayUser(PNET_DISPLAY_USER dest, LPWSTR *dest_buf,
+                            PNET_DISPLAY_USER src)
+{
+    LPWSTR str = *dest_buf;
+
+    src->usri1_name = str;
+    lstrcpyW(src->usri1_name, dest->usri1_name);
+    str = (LPWSTR) (
+        ((PBYTE) str) + (lstrlenW(str) + 1) * sizeof(WCHAR));
+
+    src->usri1_comment = str;
+    lstrcpyW(src->usri1_comment, dest->usri1_comment);
+    str = (LPWSTR) (
+        ((PBYTE) str) + (lstrlenW(str) + 1) * sizeof(WCHAR));
+
+    src->usri1_flags = dest->usri1_flags;
+
+    src->usri1_full_name = str;
+    lstrcpyW(src->usri1_full_name, dest->usri1_full_name);
+    str = (LPWSTR) (
+        ((PBYTE) str) + (lstrlenW(str) + 1) * sizeof(WCHAR));
+
+    src->usri1_user_id = dest->usri1_user_id;
+    src->usri1_next_index = dest->usri1_next_index;
+    *dest_buf = str;
+}
+
+/************************************************************
+ *                NetQueryDisplayInformation  (NETAPI32.@)
+ *
+ * The buffer structure:
+ * - array of fixed size record of the level type
+ * - strings, referenced by the record of the level type
+ */
+NET_API_STATUS WINAPI
+NetQueryDisplayInformation(
+    LPCWSTR ServerName, DWORD Level, DWORD Index, DWORD EntriesRequested,
+    DWORD PreferredMaximumLength, LPDWORD ReturnedEntryCount,
+    PVOID *SortedBuffer)
+{
+    TRACE("(%s, %ld, %ld, %ld, %ld, %p, %p)\n", debugstr_w(ServerName),
+          Level, Index, EntriesRequested, PreferredMaximumLength,
+          ReturnedEntryCount, SortedBuffer);
+    NETAPI_ForceLocalComputer(ServerName, ERROR_ACCESS_DENIED);
+    switch (Level)
+    {
+    case 1:
+    {
+        /* current record */
+        PNET_DISPLAY_USER inf;
+        /* current available strings buffer */
+        LPWSTR str;
+        PNET_DISPLAY_USER admin, guest;
+        DWORD admin_size, guest_size;
+        LPWSTR name = NULL;
+        DWORD dwSize;
+
+        /* sizes of the field buffers in WCHARS */
+        int name_sz, comment_sz, full_name_sz;
+
+        /* number of the records, returned in SortedBuffer
+           3 - for current user, Administrator and Guest users
+         */
+        int records = 3;
+
+        FIXME("Level %ld partially implemented\n", Level);
+        *ReturnedEntryCount = records;
+        comment_sz = 1;
+        full_name_sz = 1;
+
+        /* get data */
+        dwSize = UNLEN + 1;
+        NetApiBufferAllocate(dwSize, (LPVOID *) &name);
+        if (!GetUserNameW(name, &dwSize))
+        {
+            NetApiBufferFree(name);
+            return ERROR_ACCESS_DENIED;
+        }
+        name_sz = dwSize;
+        ACCESS_QueryAdminDisplayInformation(&admin, &admin_size);
+        ACCESS_QueryGuestDisplayInformation(&guest, &guest_size);
+
+        /* set up buffer */
+        dwSize = sizeof(NET_DISPLAY_USER) * records;
+        dwSize += (name_sz + comment_sz + full_name_sz) * sizeof(WCHAR);
+
+        NetApiBufferAllocate(dwSize +
+                             admin_size - sizeof(NET_DISPLAY_USER) +
+                             guest_size - sizeof(NET_DISPLAY_USER),
+                             (LPVOID *) SortedBuffer);
+        inf = (PNET_DISPLAY_USER) *SortedBuffer;
+        str = (LPWSTR) ((PBYTE) inf + sizeof(NET_DISPLAY_USER) * records);
+        inf->usri1_name = str;
+        str = (LPWSTR) (
+            ((PBYTE) str) + name_sz * sizeof(WCHAR));
+        inf->usri1_comment = str;
+        str = (LPWSTR) (
+            ((PBYTE) str) + comment_sz * sizeof(WCHAR));
+        inf->usri1_full_name = str;
+        str = (LPWSTR) (
+            ((PBYTE) str) + full_name_sz * sizeof(WCHAR));
+
+        /* set data */
+        lstrcpyW(inf->usri1_name, name);
+        NetApiBufferFree(name);
+        inf->usri1_comment[0] = 0;
+        inf->usri1_flags =
+            UF_SCRIPT | UF_NORMAL_ACCOUNT | UF_DONT_EXPIRE_PASSWD;
+        inf->usri1_full_name[0] = 0;
+        inf->usri1_user_id = 0;
+        inf->usri1_next_index = 0;
+
+        inf++;
+        ACCESS_CopyDisplayUser(admin, &str, inf);
+        NetApiBufferFree(admin);
+
+        inf++;
+        ACCESS_CopyDisplayUser(guest, &str, inf);
+        NetApiBufferFree(guest);
+        break;
+    }
+
+    case 2:
+    case 3:
+    {
+        FIXME("Level %ld is not implemented\n", Level);
+        break;
+    }
+
+    default:
+        ERR("Invalid level %ld is specified\n", Level);
+        return ERROR_INVALID_LEVEL;
+    }
+    return NERR_Success;
+}
+
+/************************************************************
+ *                NetGetDCName  (NETAPI32.@)
+ *
+ *  Return the name of the primary domain controller (PDC)
+ */
+
+NET_API_STATUS WINAPI
+NetGetDCName(LPCWSTR servername, LPCWSTR domainname, LPBYTE *bufptr)
+{
+  FIXME("(%s, %s, %p) stub!\n", debugstr_w(servername),
+                 debugstr_w(domainname), bufptr);
+  return NERR_DCNotFound; /* say we can't find a domain controller */  
+}
+
+
+/************************************************************
+ *                NetUserModalsGet  (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetUserModalsGet(LPCWSTR szServer, DWORD level, LPBYTE *pbuffer)
+{
+    FIXME("(%s %ld %p) stub!\n", debugstr_w(szServer), level, pbuffer);
+    return NERR_InternalError;
+}
+
+/************************************************************
+ *                NetLocalGroupAdd  (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetLocalGroupAdd(LPCWSTR servername, DWORD level,
+                 LPBYTE buf, LPDWORD parm_err)
+{
+    FIXME("(%s %ld %p %p) stub!\n", debugstr_w(servername), level, buf, parm_err);
+    return NERR_Success;
+}
+
+/************************************************************
+ *                NetLocalGroupSetMember (NETAPI32.@)
+ */
+
+NET_API_STATUS WINAPI NetLocalGroupSetMembers(LPCWSTR servername,
+             LPCWSTR groupname, DWORD level, LPBYTE buf, DWORD totalentries)
+{
+    FIXME("(%s %s %ld %p %ld) stub!\n", debugstr_w(servername), 
+            debugstr_w(groupname),level, buf, totalentries);
+    return NERR_Success;
+}
diff --git a/reactos/lib/netapi32/apibuf.c b/reactos/lib/netapi32/apibuf.c
new file mode 100644 (file)
index 0000000..6a8b8db
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2002 Andriy Palamarchuk
+ *
+ * Net API buffer calls
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "lmcons.h"
+#include "lmapibuf.h"
+#include "lmerr.h"
+#include "winerror.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(netapi32);
+
+/************************************************************
+ *                NetApiBufferAllocate  (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetApiBufferAllocate(DWORD ByteCount, LPVOID* Buffer)
+{
+    TRACE("(%ld, %p)\n", ByteCount, Buffer);
+    *Buffer = HeapAlloc(GetProcessHeap(), 0, ByteCount);
+    if (*Buffer)
+        return NERR_Success;
+    else
+        return GetLastError();
+}
+
+/************************************************************
+ *                NetApiBufferFree  (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetApiBufferFree(LPVOID Buffer)
+{
+    TRACE("(%p)\n", Buffer);
+    HeapFree(GetProcessHeap(), 0, Buffer);
+    return NERR_Success;
+}
+
+/************************************************************
+ *                NetApiBufferReallocate  (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetApiBufferReallocate(LPVOID OldBuffer, DWORD NewByteCount,
+                                             LPVOID* NewBuffer)
+{
+    TRACE("(%p, %ld, %p)\n", OldBuffer, NewByteCount, NewBuffer);
+    if (NewByteCount) 
+    {
+        if (OldBuffer)
+            *NewBuffer = HeapReAlloc(GetProcessHeap(), 0, OldBuffer, NewByteCount);
+        else
+            *NewBuffer = HeapAlloc(GetProcessHeap(), 0, NewByteCount);
+       return *NewBuffer ? NERR_Success : GetLastError();
+    } 
+    else 
+    {
+       if (!HeapFree(GetProcessHeap(), 0, OldBuffer)) return GetLastError();
+       *NewBuffer = 0;
+       return NERR_Success;
+    }
+}
+
+/************************************************************
+ *                NetApiBufferSize  (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetApiBufferSize(LPVOID Buffer, LPDWORD ByteCount)
+{
+    DWORD dw;
+
+    TRACE("(%p, %p)\n", Buffer, ByteCount);
+    if (Buffer == NULL)
+        return ERROR_INVALID_PARAMETER;
+    dw = HeapSize(GetProcessHeap(), 0, Buffer);
+    TRACE("size: %ld\n", dw);
+    if (dw != 0xFFFFFFFF)
+        *ByteCount = dw;
+    else
+        *ByteCount = 0;
+
+    return NERR_Success;
+}
diff --git a/reactos/lib/netapi32/browsr.c b/reactos/lib/netapi32/browsr.c
new file mode 100644 (file)
index 0000000..3cda17e
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2002 Andriy Palamarchuk
+ *
+ * netapi32 browser functions
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "lmcons.h"
+#include "lmbrowsr.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(netapi32);
+
+/************************************************************
+ *                I_BrowserSetNetlogonState  (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI I_BrowserSetNetlogonState(
+    LPWSTR ServerName, LPWSTR DomainName, LPWSTR EmulatedServerName,
+    DWORD Role)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+/************************************************************
+ *                I_BrowserQueryEmulatedDomains  (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI I_BrowserQueryEmulatedDomains(
+    LPWSTR ServerName, PBROWSER_EMULATED_DOMAIN *EmulatedDomains,
+    LPDWORD EntriesRead)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+/************************************************************
+ *                NetShareEnum  (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetShareEnum( LPWSTR servername, DWORD level, LPBYTE* bufptr,
+  DWORD prefmaxlen, LPDWORD entriesread, LPDWORD totalentries, LPDWORD resume_handle)
+{
+    FIXME("%s %ld %p %ld %p %p %p\n", debugstr_w(servername), level, bufptr,
+          prefmaxlen, entriesread, totalentries, resume_handle);
+    return ERROR_NOT_SUPPORTED;
+}
diff --git a/reactos/lib/netapi32/nbcmdqueue.c b/reactos/lib/netapi32/nbcmdqueue.c
new file mode 100644 (file)
index 0000000..d8948f7
--- /dev/null
@@ -0,0 +1,199 @@
+/* Copyright (c) 2003 Juan Lang
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include "config.h"
+#include "wine/debug.h"
+#include "nbcmdqueue.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(netbios);
+
+struct NBCmdQueue
+{
+    HANDLE           heap;
+    CRITICAL_SECTION cs;
+    PNCB             head;
+};
+
+#define CANCEL_EVENT_PTR(ncb) (PHANDLE)((ncb)->ncb_reserve)
+#define NEXT_PTR(ncb) (PNCB *)((ncb)->ncb_reserve + sizeof(HANDLE))
+
+/* The reserved area of an ncb will be used for the following data:
+ * - a cancelled flag (BOOL, 4 bytes??)
+ * - a handle to an event that's set by a cancelled command on completion
+ *   (HANDLE, 4 bytes)
+ * These members are used in the following way
+ * - on cancel, set the event member of the reserved field (with create event)
+ * - NBCmdComplete will delete the ncb from the queue of there's no event;
+ *   otherwise it will set the event and not delete the ncb
+ * - cancel must lock the queue before finding the ncb in it, and can unlock it
+ *   once it's set the event (and the cancelled flag)
+ * - NBCmdComplete must lock the queue before attempting to remove the ncb or
+ *   check the event
+ * - NBCmdQueueCancelAll will lock the queue, and cancel all ncb's in the queue.
+ *   It'll then unlock the queue, and wait on the event in the head of the queue
+ *   until there's no more ncb's in the queue.
+ * Space optimization: use the handle as a boolean.  NULL == 0 => not cancelled.
+ * Non-NULL == valid handle => cancelled.  This allows storing a next pointer
+ * in the ncb's reserved field as well, avoiding a memory alloc for a new
+ * command (cool).
+ */
+
+struct NBCmdQueue *NBCmdQueueCreate(HANDLE heap)
+{
+    struct NBCmdQueue *queue;
+
+    if (heap == NULL)
+        heap = GetProcessHeap();
+    queue = HeapAlloc(heap, 0, sizeof(struct NBCmdQueue));
+    if (queue)
+    {
+        queue->heap = heap;
+        InitializeCriticalSection(&queue->cs);
+        queue->head = NULL;
+    }
+    return queue;
+}
+
+UCHAR NBCmdQueueAdd(struct NBCmdQueue *queue, PNCB ncb)
+{
+    UCHAR ret;
+
+    TRACE(": queue %p, ncb %p\n", queue, ncb);
+
+    if (!queue)
+        return NRC_BADDR;
+    if (!ncb)
+        return NRC_INVADDRESS;
+
+    *CANCEL_EVENT_PTR(ncb) = NULL;
+    EnterCriticalSection(&queue->cs);
+    *NEXT_PTR(ncb) = queue->head;
+    queue->head = ncb;
+    ret = NRC_GOODRET;
+    LeaveCriticalSection(&queue->cs);
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+static PNCB *NBCmdQueueFindNBC(struct NBCmdQueue *queue, PNCB ncb)
+{
+    PNCB *ret;
+
+    if (!queue || !ncb)
+        ret = NULL;
+    else
+    {
+        ret = &queue->head;
+        while (ret && *ret != ncb)
+            ret = NEXT_PTR(*ret);
+    }
+    return ret;
+}
+
+UCHAR NBCmdQueueCancel(struct NBCmdQueue *queue, PNCB ncb)
+{
+    UCHAR ret;
+    PNCB *spot;
+
+    TRACE(": queue %p, ncb %p\n", queue, ncb);
+
+    if (!queue)
+        return NRC_BADDR;
+    if (!ncb)
+        return NRC_INVADDRESS;
+
+    EnterCriticalSection(&queue->cs);
+    spot = NBCmdQueueFindNBC(queue, ncb);
+    if (spot)
+    {
+        *CANCEL_EVENT_PTR(*spot) = CreateEventW(NULL, FALSE, FALSE, NULL);
+        WaitForSingleObject(*CANCEL_EVENT_PTR(*spot), INFINITE);
+        CloseHandle(*CANCEL_EVENT_PTR(*spot));
+        *spot = *NEXT_PTR(*spot);
+        if (ncb->ncb_retcode == NRC_CMDCAN)
+            ret = NRC_CMDCAN;
+        else
+            ret = NRC_CANOCCR;
+    }
+    else
+        ret = NRC_INVADDRESS;
+    LeaveCriticalSection(&queue->cs);
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+UCHAR NBCmdQueueComplete(struct NBCmdQueue *queue, PNCB ncb, UCHAR retcode)
+{
+    UCHAR ret;
+    PNCB *spot;
+
+    TRACE(": queue %p, ncb %p\n", queue, ncb);
+
+    if (!queue)
+        return NRC_BADDR;
+    if (!ncb)
+        return NRC_INVADDRESS;
+
+    EnterCriticalSection(&queue->cs);
+    spot = NBCmdQueueFindNBC(queue, ncb);
+    if (spot)
+    {
+        if (*CANCEL_EVENT_PTR(*spot))
+            SetEvent(*CANCEL_EVENT_PTR(*spot));
+        else
+            *spot = *NEXT_PTR(*spot);
+        ret = NRC_GOODRET;
+    }
+    else
+        ret = NRC_INVADDRESS;
+    LeaveCriticalSection(&queue->cs);
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+UCHAR NBCmdQueueCancelAll(struct NBCmdQueue *queue)
+{
+    UCHAR ret;
+
+    TRACE(": queue %p\n", queue);
+
+    if (!queue)
+        return NRC_BADDR;
+
+    EnterCriticalSection(&queue->cs);
+    while (queue->head)
+    {
+        TRACE(": waiting for ncb %p (command 0x%02x)\n", queue->head,
+         queue->head->ncb_command);
+        NBCmdQueueCancel(queue, queue->head);
+    }
+    LeaveCriticalSection(&queue->cs);
+    ret = NRC_GOODRET;
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+void NBCmdQueueDestroy(struct NBCmdQueue *queue)
+{
+    TRACE(": queue %p\n", queue);
+
+    if (queue)
+    {
+        NBCmdQueueCancelAll(queue);
+        DeleteCriticalSection(&queue->cs);
+        HeapFree(queue->heap, 0, queue);
+    }
+}
diff --git a/reactos/lib/netapi32/nbcmdqueue.h b/reactos/lib/netapi32/nbcmdqueue.h
new file mode 100644 (file)
index 0000000..f06f95b
--- /dev/null
@@ -0,0 +1,66 @@
+/* Copyright (c) 2003 Juan Lang
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __NBCMDQUEUE_H__
+#define __NBCMDQUEUE_H__
+
+#include <stdarg.h>
+#include "windef.h"
+#include "winbase.h"
+#include "nb30.h"
+
+/* This file defines a queue of pending NetBIOS commands.  The queue operations
+ * are thread safe, with the exception of NBCmdQueueDestroy:  ensure no other
+ * threads are manipulating the queue when calling NBCmdQueueDestroy.
+ */
+
+struct NBCmdQueue;
+
+/* Allocates a new command queue from heap. */
+struct NBCmdQueue *NBCmdQueueCreate(HANDLE heap);
+
+/* Adds ncb to queue.  Assumes queue is not NULL, and ncb is not already in the
+ * queue.  If ncb is already in the queue, returns NRC_TOOMANY.
+ */
+UCHAR NBCmdQueueAdd(struct NBCmdQueue *queue, PNCB ncb);
+
+/* Cancels the given ncb.  Blocks until the command completes.  Implicitly
+ * removes ncb from the queue.  Assumes queue and ncb are not NULL, and that
+ * ncb has been added to queue previously.
+ * Returns NRC_CMDCAN on a successful cancellation, NRC_CMDOCCR if the command
+ * completed before it could be cancelled, and various other return values for
+ * different failures.
+ */
+UCHAR NBCmdQueueCancel(struct NBCmdQueue *queue, PNCB ncb);
+
+/* Sets the return code of the given ncb, and implicitly removes the command
+ * from the queue.  Assumes queue and ncb are not NULL, and that ncb has been
+ * added to queue previously.
+ * Returns NRC_GOODRET on success.
+ */
+UCHAR NBCmdQueueComplete(struct NBCmdQueue *queue, PNCB ncb, UCHAR retcode);
+
+/* Cancels all pending commands in the queue (useful for a RESET or a shutdown).
+ * Returns when all commands have been completed.
+ */
+UCHAR NBCmdQueueCancelAll(struct NBCmdQueue *queue);
+
+/* Frees all memory associated with the queue.  Blocks until all commands
+ * pending in the queue have been completed.
+ */
+void NBCmdQueueDestroy(struct NBCmdQueue *queue);
+
+#endif /* __NBCMDQUEUE_H__ */
diff --git a/reactos/lib/netapi32/nbnamecache.c b/reactos/lib/netapi32/nbnamecache.c
new file mode 100644 (file)
index 0000000..1fdb705
--- /dev/null
@@ -0,0 +1,215 @@
+/* Copyright (c) 2003 Juan Lang
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * This implementation uses a linked list, because I don't have a decent
+ * hash table implementation handy.  This is somewhat inefficient, but it's
+ * rather more efficient than not having a name cache at all.
+ */
+
+#include "config.h"
+#include "wine/port.h"
+#include "wine/debug.h"
+
+#include "nbnamecache.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(netbios);
+
+typedef struct _NBNameCacheNode
+{
+    DWORD expireTime;
+    NBNameCacheEntry *entry;
+    struct _NBNameCacheNode *next;
+} NBNameCacheNode;
+
+struct NBNameCache
+{
+    HANDLE heap;
+    CRITICAL_SECTION cs;
+    DWORD entryExpireTimeMS;
+    NBNameCacheNode *head;
+};
+
+/* Unlinks the node pointed to by *prev, and frees any associated memory.
+ * If that node's next pointed to another node, *prev now points to it.
+ * Assumes the caller owns cache's lock.
+ */
+static void NBNameCacheUnlinkNode(struct NBNameCache *cache,
+ NBNameCacheNode **prev)
+{
+    if (cache && prev && *prev)
+    {
+        NBNameCacheNode *next = (*prev)->next;
+
+        HeapFree(cache->heap, 0, (*prev)->entry);
+        HeapFree(cache->heap, 0, *prev);
+        *prev = next;
+    }
+}
+
+/* Walks the list beginning with cache->head looking for the node with name
+ * name.  If the node is found, returns a pointer to the next pointer of the
+ * node _prior_ to the found node (or head if head points to it).  Thus, if the
+ * node's all you want, dereference the return value twice.  If you want to
+ * modify the list, modify the referent of the return value.
+ * While it's at it, deletes nodes whose time has expired (except the node
+ * you're looking for, of course).
+ * Returns NULL if the node isn't found.
+ * Assumes the caller owns cache's lock.
+ */
+static NBNameCacheNode **NBNameCacheWalk(struct NBNameCache *cache,
+ const char name[NCBNAMSZ])
+{
+    NBNameCacheNode **ret = NULL;
+
+    if (cache && cache->head)
+    {
+        NBNameCacheNode **ptr;
+
+        ptr = &cache->head;
+        while (ptr && *ptr && (*ptr)->entry)
+        {
+            if (!memcmp((*ptr)->entry->name, name, NCBNAMSZ - 1))
+                ret = ptr;
+            else
+            {
+                if (GetTickCount() > (*ptr)->expireTime)
+                    NBNameCacheUnlinkNode(cache, ptr);
+            }
+            if (*ptr)
+                ptr = &(*ptr)->next;
+        }
+    }
+    return ret;
+}
+
+struct NBNameCache *NBNameCacheCreate(HANDLE heap, DWORD entryExpireTimeMS)
+{
+    struct NBNameCache *cache;
+    
+    
+    if (!heap)
+        heap = GetProcessHeap();
+    cache = HeapAlloc(heap, 0, sizeof(struct NBNameCache));
+    if (cache)
+    {
+        cache->heap = heap;
+        InitializeCriticalSection(&cache->cs);
+        cache->entryExpireTimeMS = entryExpireTimeMS;
+        cache->head = NULL;
+    }
+    return cache;
+}
+
+BOOL NBNameCacheAddEntry(struct NBNameCache *cache, NBNameCacheEntry *entry)
+{
+    BOOL ret;
+
+    if (cache && entry)
+    {
+        NBNameCacheNode **node;
+
+        EnterCriticalSection(&cache->cs);
+        node = NBNameCacheWalk(cache, (char*)entry->name);
+        if (node)
+        {
+            (*node)->expireTime = GetTickCount() +
+             cache->entryExpireTimeMS;
+            HeapFree(cache->heap, 0, (*node)->entry);
+            (*node)->entry = entry;
+            ret = TRUE;
+        }
+        else
+        {
+            NBNameCacheNode *newNode = HeapAlloc(cache->heap, 0, sizeof(NBNameCacheNode));
+            if (newNode)
+            {
+                newNode->expireTime = GetTickCount() +
+                 cache->entryExpireTimeMS;
+                newNode->entry = entry;
+                newNode->next = cache->head;
+                cache->head = newNode;
+                ret = TRUE;
+            }
+            else
+                ret = FALSE;
+        }
+        LeaveCriticalSection(&cache->cs);
+    }
+    else
+        ret = FALSE;
+    return ret;
+}
+
+const NBNameCacheEntry *NBNameCacheFindEntry(struct NBNameCache *cache,
+ const UCHAR name[NCBNAMSZ])
+{
+    const NBNameCacheEntry *ret;
+    UCHAR printName[NCBNAMSZ];
+
+    memcpy(printName, name, NCBNAMSZ - 1);
+    printName[NCBNAMSZ - 1] = '\0';
+    if (cache)
+    {
+        NBNameCacheNode **node;
+
+        EnterCriticalSection(&cache->cs);
+        node = NBNameCacheWalk(cache, (char*)name);
+        if (node)
+            ret = (*node)->entry;
+        else
+            ret = NULL;
+        LeaveCriticalSection(&cache->cs);
+    }
+    else
+        ret = NULL;
+    return ret;
+}
+
+BOOL NBNameCacheUpdateNBName(struct NBNameCache *cache,
+ const UCHAR name[NCBNAMSZ], const UCHAR nbname[NCBNAMSZ])
+{
+    BOOL ret;
+
+    if (cache)
+    {
+        NBNameCacheNode **node;
+
+        EnterCriticalSection(&cache->cs);
+        node = NBNameCacheWalk(cache, (char*)name);
+        if (node && *node && (*node)->entry)
+        {
+            memcpy((*node)->entry->nbname, nbname, NCBNAMSZ);
+            ret = TRUE;
+        }
+        else
+            ret = FALSE;
+        LeaveCriticalSection(&cache->cs);
+    }
+    else
+        ret = FALSE;
+    return ret;
+}
+
+void NBNameCacheDestroy(struct NBNameCache *cache)
+{
+    if (cache)
+    {
+        DeleteCriticalSection(&cache->cs);
+        while (cache->head)
+            NBNameCacheUnlinkNode(cache, &cache->head);
+        HeapFree(cache->heap, 0, cache);
+    }
+}
diff --git a/reactos/lib/netapi32/nbnamecache.h b/reactos/lib/netapi32/nbnamecache.h
new file mode 100644 (file)
index 0000000..04e3663
--- /dev/null
@@ -0,0 +1,82 @@
+/* Copyright (c) 2003 Juan Lang
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __WINE_NBNAMECACHE_H
+#define __WINE_NBNAMECACHE_H
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "nb30.h"
+
+struct NBNameCache;
+
+/* Represents an entry in the name cache.  If the NetBIOS name is known, it's
+ * in nbname.  Otherwise, nbname begins with '*'.  numAddresses defines the
+ * number of addresses in addresses.
+ * Notice that it allows multiple addresses per name, but doesn't explicitly
+ * allow group names.  That's because all names so far are unique; if a use for
+ * group names comes up, adding a flag here is simple enough.
+ * Also, only the first NCBNAMSZ - 1 bytes are considered significant.  This is
+ * because a name may have been resolved using DNS, and the suffix byte is
+ * always truncated for DNS lookups.
+ */
+typedef struct _NBNameCacheEntry
+{
+    UCHAR name[NCBNAMSZ];
+    UCHAR nbname[NCBNAMSZ];
+    DWORD numAddresses;
+    DWORD addresses[1];
+} NBNameCacheEntry;
+
+/* Functions that create, manipulate, and destroy a name cache.  Thread-safe,
+ * with the exception of NBNameCacheDestroy--ensure that no other threads are
+ * manipulating the cache before destoying it.
+ */
+
+/* Allocates a new name cache from heap, and sets the expire time on new
+ * entries to entryExpireTimeMS after a cache entry is added.
+ */
+struct NBNameCache *NBNameCacheCreate(HANDLE heap, DWORD entryExpireTimeMS);
+
+/* Adds an entry to the cache.  The entry is assumed to have been allocated
+ * from the same heap as the name cache; the name cache will own the entry
+ * from now on.  The entry's expire time is initialized at this time to
+ * entryExpireTimeMS + the current time in MS.  If an existing entry with the
+ * same name was in the cache, the entry is replaced.  Returns TRUE on success
+ * or FALSE on failure.
+ */
+BOOL NBNameCacheAddEntry(struct NBNameCache *cache, NBNameCacheEntry *entry);
+
+/* Finds the entry with name name in the cache and returns a pointer to it, or
+ * NULL if it isn't found.
+ */
+const NBNameCacheEntry *NBNameCacheFindEntry(struct NBNameCache *cache,
+ const UCHAR name[NCBNAMSZ]);
+
+/* If the entry with name name is in the cache, updates its nbname member to
+ * nbname.  The entry's expire time is implicitly updated to entryExpireTimeMS
+ * + the current time in MS, since getting the NetBIOS name meant validating
+ * the name and address anyway.
+ * Returns TRUE on success or FALSE on failure.
+ */
+BOOL NBNameCacheUpdateNBName(struct NBNameCache *cache,
+ const UCHAR name[NCBNAMSZ], const UCHAR nbname[NCBNAMSZ]);
+
+void NBNameCacheDestroy(struct NBNameCache *cache);
+
+#endif /* ndef __WINE_NBNAMECACHE_H */
diff --git a/reactos/lib/netapi32/nbt.c b/reactos/lib/netapi32/nbt.c
new file mode 100644 (file)
index 0000000..3125df0
--- /dev/null
@@ -0,0 +1,1553 @@
+/* Copyright (c) 2003 Juan Lang
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * I am heavily indebted to Chris Hertel's excellent Implementing CIFS,
+ * http://ubiqx.org/cifs/ , for whatever understanding I have of NBT.
+ * I also stole from Mike McCormack's smb.c and netapi32.c, although little of
+ * that code remains.
+ * Lack of understanding and bugs are my fault.
+ *
+ * FIXME:
+ * - Of the NetBIOS session functions, only client functions are supported, and
+ *   it's likely they'll be the only functions supported.  NBT requires session
+ *   servers to listen on TCP/139.  This requires root privilege, and Samba is
+ *   likely to be listening here already.  This further restricts NetBIOS
+ *   applications, both explicit users and implicit ones:  CreateNamedPipe
+ *   won't actually create a listening pipe, for example, so applications can't
+ *   act as RPC servers using a named pipe protocol binding, DCOM won't be able
+ *   to support callbacks or servers over the named pipe protocol, etc.
+ *
+ * - Datagram support is omitted for the same reason.  To send a NetBIOS
+ *   datagram, you must include the NetBIOS name by which your application is
+ *   known.  This requires you to have registered the name previously, and be
+ *   able to act as a NetBIOS datagram server (listening on UDP/138).
+ *
+ * - Name registration functions are omitted for the same reason--registering a
+ *   name requires you to be able to defend it, and this means listening on
+ *   UDP/137.
+ *   Win98 requires you either use your computer's NetBIOS name (with the NULL
+ *   suffix byte) as the calling name when creating a session, or to register
+ *   a new name before creating one:  it disallows '*' as the calling name.
+ *   Win2K initially starts with an empty name table, and doesn't allow you to
+ *   use the machine's NetBIOS name (with the NULL suffix byte) as the calling
+ *   name.  Although it allows sessions to be created with '*' as the calling
+ *   name, doing so results in timeouts for all receives, because the
+ *   application never gets them.
+ *   So, a well-behaved Netbios application will typically want to register a
+ *   name.  I should probably support a do-nothing name list that allows
+ *   NCBADDNAME to add to it, but doesn't actually register the name, or does
+ *   attempt to register it without being able to defend it.
+ *
+ * - Name lookups may not behave quite as you'd expect/like if you have
+ *   multiple LANAs.  If a name is resolvable through DNS, or if you're using
+ *   WINS, it'll resolve on _any_ LANA.  So, a Call will succeed on any LANA as
+ *   well.
+ *   I'm not sure how Windows behaves in this case.  I could try to force
+ *   lookups to the correct adapter by using one of the GetPreferred*
+ *   functions, but with the possibility of multiple adapters in the same
+ *   same subnet, there's no guarantee that what IpHlpApi thinks is the
+ *   preferred adapter will actually be a LANA.  (It's highly probable because
+ *   this is an unusual configuration, but not guaranteed.)
+ *
+ * See also other FIXMEs in the code.
+ */
+
+#include "config.h"
+#include <stdarg.h>
+
+#include "winsock2.h"
+#include "windef.h"
+#include "winbase.h"
+#include "wine/debug.h"
+#include "winreg.h"
+#include "iphlpapi.h"
+
+#include "netbios.h"
+#include "nbnamecache.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(netbios);
+
+#define PORT_NBNS 137
+#define PORT_NBDG 138
+#define PORT_NBSS 139
+
+#ifndef INADDR_NONE
+#define INADDR_NONE ~0UL
+#endif
+
+#define NBR_ADDWORD(p,word) (*(WORD *)(p)) = htons(word)
+#define NBR_GETWORD(p) ntohs(*(WORD *)(p))
+
+#define MIN_QUERIES         1
+#define MAX_QUERIES         0xffff
+#define MIN_QUERY_TIMEOUT   100
+#define MAX_QUERY_TIMEOUT   0xffffffff
+#define BCAST_QUERIES       3
+#define BCAST_QUERY_TIMEOUT 750
+#define WINS_QUERIES        3
+#define WINS_QUERY_TIMEOUT  750
+#define MAX_WINS_SERVERS    2
+#define MIN_CACHE_TIMEOUT   60000
+#define CACHE_TIMEOUT       360000
+
+#define MAX_NBT_NAME_SZ (NCBNAMSZ * 2 + MAX_DOMAIN_NAME_LEN + 2)
+#define SIMPLE_NAME_QUERY_PKT_SIZE 26 + MAX_NBT_NAME_SZ
+
+#define DEFAULT_NBT_SESSIONS 16
+
+#define NBNS_TYPE_NB             0x0020
+#define NBNS_TYPE_NBSTAT         0x0021
+#define NBNS_CLASS_INTERNET      0x00001
+#define NBNS_HEADER_SIZE         (sizeof(WORD) * 6)
+#define NBNS_RESPONSE_AND_OPCODE 0xf800
+#define NBNS_RESPONSE_AND_QUERY  0x8000
+#define NBNS_REPLYCODE           0x0f
+
+#define NBSS_HDRSIZE 4
+
+#define NBSS_MSG       0x00
+#define NBSS_REQ       0x81
+#define NBSS_ACK       0x82
+#define NBSS_NACK      0x83
+#define NBSS_RETARGET  0x84
+#define NBSS_KEEPALIVE 0x85
+
+#define NBSS_ERR_NOT_LISTENING_ON_NAME    0x80
+#define NBSS_ERR_NOT_LISTENING_FOR_CALLER 0x81
+#define NBSS_ERR_BAD_NAME                 0x82
+#define NBSS_ERR_INSUFFICIENT_RESOURCES   0x83
+
+#define NBSS_EXTENSION 0x01
+
+typedef struct _NetBTSession
+{
+    CRITICAL_SECTION cs;
+    SOCKET           fd;
+    DWORD            bytesPending;
+} NetBTSession;
+
+typedef struct _NetBTAdapter
+{
+    MIB_IPADDRROW       ipr;
+    WORD                nameQueryXID;
+    struct NBNameCache *nameCache;
+    DWORD               xmit_success;
+    DWORD               recv_success;
+} NetBTAdapter;
+
+static ULONG gTransportID;
+static BOOL  gEnableDNS;
+static DWORD gBCastQueries;
+static DWORD gBCastQueryTimeout;
+static DWORD gWINSQueries;
+static DWORD gWINSQueryTimeout;
+static DWORD gWINSServers[MAX_WINS_SERVERS];
+static int   gNumWINSServers;
+static char  gScopeID[MAX_DOMAIN_NAME_LEN];
+static DWORD gCacheTimeout;
+static struct NBNameCache *gNameCache;
+
+/* Converts from a NetBIOS name into a Second Level Encoding-formatted name.
+ * Assumes p is not NULL and is either NULL terminated or has at most NCBNAMSZ
+ * bytes, and buffer has at least MAX_NBT_NAME_SZ bytes.  Pads with space bytes
+ * if p is NULL-terminated.  Returns the number of bytes stored in buffer.
+ */
+static int NetBTNameEncode(const UCHAR *p, UCHAR *buffer)
+{
+    int i,len=0;
+
+    if (!p) return 0;
+    if (!buffer) return 0;
+
+    buffer[len++] = NCBNAMSZ * 2;
+    for (i = 0; p[i] && i < NCBNAMSZ; i++)
+    {
+        buffer[len++] = ((p[i] & 0xf0) >> 4) + 'A';
+        buffer[len++] =  (p[i] & 0x0f) + 'A';
+    }
+    while (len < NCBNAMSZ * 2)
+    {
+        buffer[len++] = 'C';
+        buffer[len++] = 'A';
+    }
+    if (*gScopeID)
+    {
+        int scopeIDLen = strlen(gScopeID);
+
+        memcpy(buffer + len, gScopeID, scopeIDLen);
+        len += scopeIDLen;
+    }
+    buffer[len++] = 0;     /* add second terminator */
+    return len;
+}
+
+/* Creates a NBT name request packet for name in buffer.  If broadcast is true,
+ * creates a broadcast request, otherwise creates a unicast request.
+ * Returns the number of bytes stored in buffer.
+ */
+static DWORD NetBTNameReq(const UCHAR name[NCBNAMSZ], WORD xid, WORD qtype,
+ BOOL broadcast, UCHAR *buffer, int len)
+{
+    int i = 0;
+
+    if (len < SIMPLE_NAME_QUERY_PKT_SIZE) return 0;
+
+    NBR_ADDWORD(&buffer[i],xid);    i+=2; /* transaction */
+    if (broadcast)
+    {
+        NBR_ADDWORD(&buffer[i],0x0110); /* flags: r=req,op=query,rd=1,b=1 */
+        i+=2;
+    }
+    else
+    {
+        NBR_ADDWORD(&buffer[i],0x0100); /* flags: r=req,op=query,rd=1,b=0 */
+        i+=2;
+    }
+    NBR_ADDWORD(&buffer[i],0x0001); i+=2; /* one name query */
+    NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero answers */
+    NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero authorities */
+    NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero additional */
+
+    i += NetBTNameEncode(name, &buffer[i]);
+
+    NBR_ADDWORD(&buffer[i],qtype); i+=2;
+    NBR_ADDWORD(&buffer[i],NBNS_CLASS_INTERNET); i+=2;
+
+    return i;
+}
+
+/* Sends a name query request for name on fd to destAddr.  Sets SO_BROADCAST on
+ * fd if broadcast is TRUE.  Assumes fd is not INVALID_SOCKET, and name is not
+ * NULL.
+ * Returns 0 on success, -1 on failure.
+ */
+static int NetBTSendNameQuery(SOCKET fd, const UCHAR name[NCBNAMSZ], WORD xid,
+ WORD qtype, DWORD destAddr, BOOL broadcast)
+{
+    int ret = 0, on = 1;
+    struct in_addr addr;
+
+    addr.s_addr = destAddr;
+    TRACE("name %s, dest addr %s\n", name, inet_ntoa(addr));
+
+    if (broadcast)
+        ret = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (const char*)&on, sizeof(on));
+    if(ret == 0)
+    {
+        WSABUF wsaBuf;
+        UCHAR buf[SIMPLE_NAME_QUERY_PKT_SIZE];
+        struct sockaddr_in sin;
+
+        memset(&sin, 0, sizeof(sin));
+        sin.sin_addr.s_addr = destAddr;
+        sin.sin_family      = AF_INET;
+        sin.sin_port        = htons(PORT_NBNS);
+
+        wsaBuf.buf = (CHAR*)buf;
+        wsaBuf.len = NetBTNameReq(name, xid, qtype, broadcast, buf,
+         sizeof(buf));
+        if (wsaBuf.len > 0)
+        {
+            DWORD bytesSent;
+
+            ret = WSASendTo(fd, &wsaBuf, 1, &bytesSent, 0,
+             (struct sockaddr*)&sin, sizeof(sin), NULL, NULL);
+            if (ret < 0 || bytesSent < wsaBuf.len)
+                ret = -1;
+            else
+                ret = 0;
+        }
+        else
+            ret = -1;
+    }
+    return ret;
+}
+
+typedef BOOL (*NetBTAnswerCallback)(void *data, WORD answerCount,
+ WORD answerIndex, PUCHAR rData, WORD rdLength);
+
+/* Waits on fd until GetTickCount() returns a value greater than or equal to
+ * waitUntil for a name service response.  If a name response matching xid
+ * is received, calls answerCallback once for each answer resource record in
+ * the response.  (The callback's answerCount will be the total number of
+ * answers to expect, and answerIndex will be the 0-based index that's being
+ * sent this time.)  Quits parsing if answerCallback returns FALSE.
+ * Returns NRC_GOODRET on timeout or a valid response received, something else
+ * on error.
+ */
+static UCHAR NetBTWaitForNameResponse(NetBTAdapter *adapter, SOCKET fd,
+ DWORD waitUntil, NetBTAnswerCallback answerCallback, void *data)
+{
+    BOOL found = FALSE;
+    DWORD now;
+    UCHAR ret = NRC_GOODRET;
+
+    if (!adapter) return NRC_BADDR;
+    if (fd == INVALID_SOCKET) return NRC_BADDR;
+    if (!answerCallback) return NRC_BADDR;
+
+    while (!found && ret == NRC_GOODRET && (now = GetTickCount()) < waitUntil)
+    {
+        DWORD msToWait = waitUntil - now;
+        struct fd_set fds;
+        struct timeval timeout = { msToWait / 1000, msToWait % 1000 };
+        int r;
+
+        FD_ZERO(&fds);
+        FD_SET(fd, &fds);
+        r = select(fd + 1, &fds, NULL, NULL, &timeout);
+        if (r < 0)
+            ret = NRC_SYSTEM;
+        else if (r == 1)
+        {
+            /* FIXME: magic #, is this always enough? */
+            UCHAR buffer[256];
+            int fromsize;
+            struct sockaddr_in fromaddr;
+            WORD respXID, flags, queryCount, answerCount;
+            WSABUF wsaBuf = { sizeof(buffer), (CHAR*)buffer };
+            DWORD bytesReceived, recvFlags = 0;
+
+            fromsize = sizeof(fromaddr);
+            r = WSARecvFrom(fd, &wsaBuf, 1, &bytesReceived, &recvFlags,
+             (struct sockaddr*)&fromaddr, &fromsize, NULL, NULL);
+            if(r < 0)
+            {
+                ret = NRC_SYSTEM;
+                break;
+            }
+
+            if (bytesReceived < NBNS_HEADER_SIZE)
+                continue;
+
+            respXID = NBR_GETWORD(buffer);
+            if (adapter->nameQueryXID != respXID)
+                continue;
+
+            flags = NBR_GETWORD(buffer + 2);
+            queryCount = NBR_GETWORD(buffer + 4);
+            answerCount = NBR_GETWORD(buffer + 6);
+
+            /* a reply shouldn't contain a query, ignore bad packet */
+            if (queryCount > 0)
+                continue;
+
+            if ((flags & NBNS_RESPONSE_AND_OPCODE) == NBNS_RESPONSE_AND_QUERY)
+            {
+                if ((flags & NBNS_REPLYCODE) != 0)
+                    ret = NRC_NAMERR;
+                else if ((flags & NBNS_REPLYCODE) == 0 && answerCount > 0)
+                {
+                    PUCHAR ptr = buffer + NBNS_HEADER_SIZE;
+                    BOOL shouldContinue = TRUE;
+                    WORD answerIndex = 0;
+
+                    found = TRUE;
+                    /* decode one answer at a time */
+                    while (ret == NRC_GOODRET && answerIndex < answerCount &&
+                     ptr - buffer < bytesReceived && shouldContinue)
+                    {
+                        WORD rLen;
+
+                        /* scan past name */
+                        for (; ptr[0] && ptr - buffer < bytesReceived; )
+                            ptr += ptr[0] + 1;
+                        ptr++;
+                        ptr += 2; /* scan past type */
+                        if (ptr - buffer < bytesReceived && ret == NRC_GOODRET
+                         && NBR_GETWORD(ptr) == NBNS_CLASS_INTERNET)
+                            ptr += sizeof(WORD);
+                        else
+                            ret = NRC_SYSTEM; /* parse error */
+                        ptr += sizeof(DWORD); /* TTL */
+                        rLen = NBR_GETWORD(ptr);
+                        rLen = min(rLen, bytesReceived - (ptr - buffer));
+                        ptr += sizeof(WORD);
+                        shouldContinue = answerCallback(data, answerCount,
+                         answerIndex, ptr, rLen);
+                        ptr += rLen;
+                        answerIndex++;
+                    }
+                }
+            }
+        }
+    }
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+typedef struct _NetBTNameQueryData {
+    NBNameCacheEntry *cacheEntry;
+    UCHAR ret;
+} NetBTNameQueryData;
+
+/* Name query callback function for NetBTWaitForNameResponse, creates a cache
+ * entry on the first answer, adds each address as it's called again (as long
+ * as there's space).  If there's an error that should be propagated as the
+ * NetBIOS error, modifies queryData's ret member to the proper return code.
+ */
+static BOOL NetBTFindNameAnswerCallback(void *pVoid, WORD answerCount,
+ WORD answerIndex, PUCHAR rData, WORD rLen)
+{
+    NetBTNameQueryData *queryData = (NetBTNameQueryData *)pVoid;
+    BOOL ret;
+
+    if (queryData)
+    {
+        if (queryData->cacheEntry == NULL)
+        {
+            queryData->cacheEntry = HeapAlloc(
+             GetProcessHeap(), 0, sizeof(NBNameCacheEntry) +
+             (answerCount - 1) * sizeof(DWORD));
+            if (queryData->cacheEntry)
+                queryData->cacheEntry->numAddresses = 0;
+            else
+            {
+                ret = FALSE;
+                queryData->ret = NRC_OSRESNOTAV;
+            }
+        }
+        if (rLen == 6 && queryData->cacheEntry &&
+         queryData->cacheEntry->numAddresses < answerCount)
+        {
+            queryData->cacheEntry->addresses[queryData->cacheEntry->
+             numAddresses++] = *(PDWORD)(rData + 2);
+            ret = queryData->cacheEntry->numAddresses < answerCount;
+        }
+        else
+            ret = FALSE;
+    }
+    else
+        ret = FALSE;
+    return ret;
+}
+
+/* Workhorse NetBT name lookup function.  Sends a name lookup query for
+ * ncb->ncb_callname to sendTo, as a broadcast if broadcast is TRUE, using
+ * adapter->nameQueryXID as the transaction ID.  Waits up to timeout
+ * milliseconds, and retries up to maxQueries times, waiting for a reply.
+ * If a valid response is received, stores the looked up addresses as a
+ * NBNameCacheEntry in *cacheEntry.
+ * Returns NRC_GOODRET on success, though this may not mean the name was
+ * resolved--check whether *cacheEntry is NULL.
+ */
+static UCHAR NetBTNameWaitLoop(NetBTAdapter *adapter, SOCKET fd, PNCB ncb,
+ DWORD sendTo, BOOL broadcast, DWORD timeout, DWORD maxQueries,
+ NBNameCacheEntry **cacheEntry)
+{
+    unsigned int queries;
+    NetBTNameQueryData queryData;
+
+    if (!adapter) return NRC_BADDR;
+    if (fd == INVALID_SOCKET) return NRC_BADDR;
+    if (!ncb) return NRC_BADDR;
+    if (!cacheEntry) return NRC_BADDR;
+
+    queryData.cacheEntry = NULL;
+    queryData.ret = NRC_GOODRET;
+    for (queries = 0; queryData.cacheEntry == NULL && queries < maxQueries;
+     queries++)
+    {
+        if (!NCB_CANCELLED(ncb))
+        {
+            int r = NetBTSendNameQuery(fd, ncb->ncb_callname,
+             adapter->nameQueryXID, NBNS_TYPE_NB, sendTo, broadcast);
+
+            if (r == 0)
+                queryData.ret = NetBTWaitForNameResponse(adapter, fd,
+                 GetTickCount() + timeout, NetBTFindNameAnswerCallback,
+                 &queryData);
+            else
+                queryData.ret = NRC_SYSTEM;
+        }
+        else
+            queryData.ret = NRC_CMDCAN;
+    }
+    if (queryData.cacheEntry)
+    {
+        memcpy(queryData.cacheEntry->name, ncb->ncb_callname, NCBNAMSZ);
+        memcpy(queryData.cacheEntry->nbname, ncb->ncb_callname, NCBNAMSZ);
+    }
+    *cacheEntry = queryData.cacheEntry;
+    return queryData.ret;
+}
+
+/* Attempts to add cacheEntry to the name cache in *nameCache; if *nameCache
+ * has not yet been created, creates it, using gCacheTimeout as the cache
+ * entry timeout.  If memory allocation fails, or if NBNameCacheAddEntry fails,
+ * frees cacheEntry.
+ * Returns NRC_GOODRET on success, and something else on failure.
+ */
+static UCHAR NetBTStoreCacheEntry(struct NBNameCache **nameCache,
+ NBNameCacheEntry *cacheEntry)
+{
+    UCHAR ret;
+
+    if (!nameCache) return NRC_BADDR;
+    if (!cacheEntry) return NRC_BADDR;
+
+    if (!*nameCache)
+        *nameCache = NBNameCacheCreate(GetProcessHeap(), gCacheTimeout);
+    if (*nameCache)
+        ret = NBNameCacheAddEntry(*nameCache, cacheEntry)
+         ?  NRC_GOODRET : NRC_OSRESNOTAV;
+    else
+    {
+        HeapFree(GetProcessHeap(), 0, cacheEntry);
+        ret = NRC_OSRESNOTAV;
+    }
+    return ret;
+}
+
+/* Attempts to resolve name using inet_addr(), then gethostbyname() if
+ * gEnableDNS is TRUE, if the suffix byte is either <00> or <20>.  If the name
+ * can be looked up, returns 0 and stores the looked up addresses as a
+ * NBNameCacheEntry in *cacheEntry.
+ * Returns NRC_GOODRET on success, though this may not mean the name was
+ * resolved--check whether *cacheEntry is NULL.  Returns something else on
+ * error.
+ */
+static UCHAR NetBTinetResolve(const UCHAR name[NCBNAMSZ],
+ NBNameCacheEntry **cacheEntry)
+{
+    UCHAR ret = NRC_GOODRET;
+
+    TRACE("name %s, cacheEntry %p\n", name, cacheEntry);
+
+    if (!name) return NRC_BADDR;
+    if (!cacheEntry) return NRC_BADDR;
+
+    if (isalnum(name[0]) && (name[NCBNAMSZ - 1] == 0 ||
+     name[NCBNAMSZ - 1] == 0x20))
+    {
+        CHAR toLookup[NCBNAMSZ];
+        unsigned int i;
+
+        for (i = 0; i < NCBNAMSZ - 1 && name[i] && name[i] != ' '; i++)
+            toLookup[i] = name[i];
+        toLookup[i] = '\0';
+
+        if (isdigit(toLookup[0]))
+        {
+            unsigned long addr = inet_addr(toLookup);
+
+            if (addr != INADDR_NONE)
+            {
+                *cacheEntry = HeapAlloc(GetProcessHeap(),
+                 0, sizeof(NBNameCacheEntry));
+                if (*cacheEntry)
+                {
+                    memcpy((*cacheEntry)->name, name, NCBNAMSZ);
+                    memset((*cacheEntry)->nbname, 0, NCBNAMSZ);
+                    (*cacheEntry)->nbname[0] = '*';
+                    (*cacheEntry)->numAddresses = 1;
+                    (*cacheEntry)->addresses[0] = addr;
+                }
+                else
+                    ret = NRC_OSRESNOTAV;
+            }
+        }
+        if (gEnableDNS && ret == NRC_GOODRET && !*cacheEntry)
+        {
+            struct hostent *host;
+
+            if ((host = gethostbyname(toLookup)) != NULL)
+            {
+                for (i = 0; ret == NRC_GOODRET && host->h_addr_list &&
+                 host->h_addr_list[i]; i++)
+                    ;
+                if (host->h_addr_list && host->h_addr_list[0])
+                {
+                    *cacheEntry = HeapAlloc(
+                     GetProcessHeap(), 0, sizeof(NBNameCacheEntry) +
+                     (i - 1) * sizeof(DWORD));
+                    if (*cacheEntry)
+                    {
+                        memcpy((*cacheEntry)->name, name, NCBNAMSZ);
+                        memset((*cacheEntry)->nbname, 0, NCBNAMSZ);
+                        (*cacheEntry)->nbname[0] = '*';
+                        (*cacheEntry)->numAddresses = i;
+                        for (i = 0; i < (*cacheEntry)->numAddresses; i++)
+                            (*cacheEntry)->addresses[i] =
+                             (DWORD)host->h_addr_list[i];
+                    }
+                    else
+                        ret = NRC_OSRESNOTAV;
+                }
+            }
+        }
+    }
+
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+/* Looks up the name in ncb->ncb_callname, first in the name caches (global
+ * and this adapter's), then using gethostbyname(), next by WINS if configured,
+ * and finally using broadcast NetBT name resolution.  In NBT parlance, this
+ * makes this an "H-node".  Stores an entry in the appropriate name cache for a
+ * found node, and returns it as *cacheEntry.
+ * Assumes data, ncb, and cacheEntry are not NULL.
+ * Returns NRC_GOODRET on success--which doesn't mean the name was resolved,
+ * just that all name lookup operations completed successfully--and something
+ * else on failure.  *cacheEntry will be NULL if the name was not found.
+ */
+static UCHAR NetBTInternalFindName(NetBTAdapter *adapter, PNCB ncb,
+ const NBNameCacheEntry **cacheEntry)
+{
+    UCHAR ret = NRC_GOODRET;
+
+    TRACE("adapter %p, ncb %p, cacheEntry %p\n", adapter, ncb, cacheEntry);
+
+    if (!cacheEntry) return NRC_BADDR;
+    *cacheEntry = NULL;
+
+    if (!adapter) return NRC_BADDR;
+    if (!ncb) return NRC_BADDR;
+
+    if (ncb->ncb_callname[0] == '*')
+        ret = NRC_NOWILD;
+    else
+    {
+        *cacheEntry = NBNameCacheFindEntry(gNameCache, ncb->ncb_callname);
+        if (!*cacheEntry)
+            *cacheEntry = NBNameCacheFindEntry(adapter->nameCache,
+             ncb->ncb_callname);
+        if (!*cacheEntry)
+        {
+            NBNameCacheEntry *newEntry = NULL;
+
+            ret = NetBTinetResolve(ncb->ncb_callname, &newEntry);
+            if (ret == NRC_GOODRET && newEntry)
+            {
+                ret = NetBTStoreCacheEntry(&gNameCache, newEntry);
+                if (ret != NRC_GOODRET)
+                    newEntry = NULL;
+            }
+            else
+            {
+                SOCKET fd = WSASocketA(PF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL,
+                 0, WSA_FLAG_OVERLAPPED);
+
+                if(fd == INVALID_SOCKET)
+                    ret = NRC_OSRESNOTAV;
+                else
+                {
+                    int winsNdx;
+
+                    adapter->nameQueryXID++;
+                    for (winsNdx = 0; ret == NRC_GOODRET && *cacheEntry == NULL
+                     && winsNdx < gNumWINSServers; winsNdx++)
+                        ret = NetBTNameWaitLoop(adapter, fd, ncb,
+                         gWINSServers[winsNdx], FALSE, gWINSQueryTimeout,
+                         gWINSQueries, &newEntry);
+                    if (ret == NRC_GOODRET && newEntry)
+                    {
+                        ret = NetBTStoreCacheEntry(&gNameCache, newEntry);
+                        if (ret != NRC_GOODRET)
+                            newEntry = NULL;
+                    }
+                    if (ret == NRC_GOODRET && *cacheEntry == NULL)
+                    {
+                        DWORD bcastAddr =
+                         adapter->ipr.dwAddr & adapter->ipr.dwMask;
+
+                        if (adapter->ipr.dwBCastAddr)
+                            bcastAddr |= ~adapter->ipr.dwMask;
+                        ret = NetBTNameWaitLoop(adapter, fd, ncb, bcastAddr,
+                         TRUE, gBCastQueryTimeout, gBCastQueries, &newEntry);
+                        if (ret == NRC_GOODRET && newEntry)
+                        {
+                            ret = NetBTStoreCacheEntry(&adapter->nameCache,
+                             newEntry);
+                            if (ret != NRC_GOODRET)
+                                newEntry = NULL;
+                        }
+                    }
+                    closesocket(fd);
+                }
+            }
+            *cacheEntry = newEntry;
+        }
+    }
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+typedef struct _NetBTNodeQueryData
+{
+    BOOL gotResponse;
+    PADAPTER_STATUS astat;
+    WORD astatLen;
+} NetBTNodeQueryData;
+
+/* Callback function for NetBTAstatRemote, parses the rData for the node
+ * status and name list of the remote node.  Always returns FALSE, since
+ * there's never more than one answer we care about in a node status response.
+ */
+static BOOL NetBTNodeStatusAnswerCallback(void *pVoid, WORD answerCount,
+ WORD answerIndex, PUCHAR rData, WORD rLen)
+{
+    NetBTNodeQueryData *data = (NetBTNodeQueryData *)pVoid;
+
+    if (data && !data->gotResponse && rData && rLen >= 1)
+    {
+        /* num names is first byte; each name is NCBNAMSZ + 2 bytes */
+        if (rLen >= rData[0] * (NCBNAMSZ + 2))
+        {
+            WORD i;
+            PUCHAR src;
+            PNAME_BUFFER dst;
+
+            data->gotResponse = TRUE;
+            data->astat->name_count = rData[0];
+            for (i = 0, src = rData + 1,
+             dst = (PNAME_BUFFER)((PUCHAR)data->astat +
+              sizeof(ADAPTER_STATUS));
+             i < data->astat->name_count && src - rData < rLen &&
+             (PUCHAR)dst - (PUCHAR)data->astat < data->astatLen;
+             i++, dst++, src += NCBNAMSZ + 2)
+            {
+                UCHAR flags = *(src + NCBNAMSZ);
+
+                memcpy(dst->name, src, NCBNAMSZ);
+                /* we won't actually see a registering name in the returned
+                 * response.  It's useful to see if no other flags are set; if
+                 * none are, then the name is registered. */
+                dst->name_flags = REGISTERING;
+                if (flags & 0x80)
+                    dst->name_flags |= GROUP_NAME;
+                if (flags & 0x10)
+                    dst->name_flags |= DEREGISTERED;
+                if (flags & 0x08)
+                    dst->name_flags |= DUPLICATE;
+                if (dst->name_flags == REGISTERING)
+                    dst->name_flags = REGISTERED;
+            }
+            /* arbitrarily set HW type to Ethernet */
+            data->astat->adapter_type = 0xfe;
+            if (src - rData < rLen)
+                memcpy(data->astat->adapter_address, src,
+                 min(rLen - (src - rData), 6));
+        }
+    }
+    return FALSE;
+}
+
+/* This uses the WINS timeout and query values, as they're the
+ * UCAST_REQ_RETRY_TIMEOUT and UCAST_REQ_RETRY_COUNT according to the RFCs.
+ */
+static UCHAR NetBTAstatRemote(NetBTAdapter *adapter, PNCB ncb)
+{
+    UCHAR ret = NRC_GOODRET;
+    const NBNameCacheEntry *cacheEntry = NULL;
+
+    TRACE("adapter %p, NCB %p\n", adapter, ncb);
+
+    if (!adapter) return NRC_BADDR;
+    if (!ncb) return NRC_INVADDRESS;
+
+    ret = NetBTInternalFindName(adapter, ncb, &cacheEntry);
+    if (ret == NRC_GOODRET && cacheEntry)
+    {
+        if (cacheEntry->numAddresses > 0)
+        {
+            SOCKET fd = WSASocketA(PF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0,
+             WSA_FLAG_OVERLAPPED);
+
+            if(fd == INVALID_SOCKET)
+                ret = NRC_OSRESNOTAV;
+            else
+            {
+                NetBTNodeQueryData queryData;
+                DWORD queries;
+                PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer;
+
+                adapter->nameQueryXID++;
+                astat->name_count = 0;
+                queryData.gotResponse = FALSE;
+                queryData.astat = astat;
+                queryData.astatLen = ncb->ncb_length;
+                for (queries = 0; !queryData.gotResponse &&
+                 queries < gWINSQueries; queries++)
+                {
+                    if (!NCB_CANCELLED(ncb))
+                    {
+                        int r = NetBTSendNameQuery(fd, ncb->ncb_callname,
+                         adapter->nameQueryXID, NBNS_TYPE_NBSTAT,
+                         cacheEntry->addresses[0], FALSE);
+
+                        if (r == 0)
+                            ret = NetBTWaitForNameResponse(adapter, fd,
+                             GetTickCount() + gWINSQueryTimeout,
+                             NetBTNodeStatusAnswerCallback, &queryData);
+                        else
+                            ret = NRC_SYSTEM;
+                    }
+                    else
+                        ret = NRC_CMDCAN;
+                }
+                closesocket(fd);
+            }
+        }
+        else
+            ret = NRC_CMDTMO;
+    }
+    else if (ret == NRC_CMDCAN)
+        ; /* do nothing, we were cancelled */
+    else
+        ret = NRC_CMDTMO;
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+static UCHAR NetBTAstat(void *adapt, PNCB ncb)
+{
+    NetBTAdapter *adapter = (NetBTAdapter *)adapt;
+    UCHAR ret;
+
+    TRACE("adapt %p, NCB %p\n", adapt, ncb);
+
+    if (!adapter) return NRC_ENVNOTDEF;
+    if (!ncb) return NRC_INVADDRESS;
+    if (!ncb->ncb_buffer) return NRC_BADDR;
+    if (ncb->ncb_length < sizeof(ADAPTER_STATUS)) return NRC_BUFLEN;
+
+    if (ncb->ncb_callname[0] == '*')
+    {
+        DWORD physAddrLen;
+        MIB_IFROW ifRow;
+        PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer;
+  
+        memset(astat, 0, sizeof(ADAPTER_STATUS));
+        astat->rev_major = 3;
+        ifRow.dwIndex = adapter->ipr.dwIndex;
+        if (GetIfEntry(&ifRow) != NO_ERROR)
+            ret = NRC_BRIDGE;
+        else
+        {
+            physAddrLen = min(ifRow.dwPhysAddrLen, 6);
+            if (physAddrLen > 0)
+                memcpy(astat->adapter_address, ifRow.bPhysAddr, physAddrLen);
+            /* doubt anyone cares, but why not.. */
+            if (ifRow.dwType == MIB_IF_TYPE_TOKENRING)
+                astat->adapter_type = 0xff;
+            else
+                astat->adapter_type = 0xfe; /* for Ethernet */
+            astat->max_sess_pkt_size = 0xffff;
+            astat->xmit_success = adapter->xmit_success;
+            astat->recv_success = adapter->recv_success;
+        }
+        ret = NRC_GOODRET;
+    }
+    else
+        ret = NetBTAstatRemote(adapter, ncb);
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+static UCHAR NetBTFindName(void *adapt, PNCB ncb)
+{
+    NetBTAdapter *adapter = (NetBTAdapter *)adapt;
+    UCHAR ret;
+    const NBNameCacheEntry *cacheEntry = NULL;
+    PFIND_NAME_HEADER foundName;
+
+    TRACE("adapt %p, NCB %p\n", adapt, ncb);
+
+    if (!adapter) return NRC_ENVNOTDEF;
+    if (!ncb) return NRC_INVADDRESS;
+    if (!ncb->ncb_buffer) return NRC_BADDR;
+    if (ncb->ncb_length < sizeof(FIND_NAME_HEADER)) return NRC_BUFLEN;
+
+    foundName = (PFIND_NAME_HEADER)ncb->ncb_buffer;
+    memset(foundName, 0, sizeof(FIND_NAME_HEADER));
+
+    ret = NetBTInternalFindName(adapter, ncb, &cacheEntry);
+    if (ret == NRC_GOODRET)
+    {
+        if (cacheEntry)
+        {
+            DWORD spaceFor = min((ncb->ncb_length - sizeof(FIND_NAME_HEADER)) /
+             sizeof(FIND_NAME_BUFFER), cacheEntry->numAddresses);
+            DWORD ndx;
+
+            for (ndx = 0; ndx < spaceFor; ndx++)
+            {
+                PFIND_NAME_BUFFER findNameBuffer;
+
+                findNameBuffer =
+                 (PFIND_NAME_BUFFER)((PUCHAR)foundName +
+                 sizeof(FIND_NAME_HEADER) + foundName->node_count *
+                 sizeof(FIND_NAME_BUFFER));
+                memset(findNameBuffer->destination_addr, 0, 2);
+                memcpy(findNameBuffer->destination_addr + 2,
+                 &adapter->ipr.dwAddr, sizeof(DWORD));
+                memset(findNameBuffer->source_addr, 0, 2);
+                memcpy(findNameBuffer->source_addr + 2,
+                 &cacheEntry->addresses[ndx], sizeof(DWORD));
+                foundName->node_count++;
+            }
+            if (spaceFor < cacheEntry->numAddresses)
+                ret = NRC_BUFLEN;
+        }
+        else
+            ret = NRC_CMDTMO;
+    }
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+static UCHAR NetBTSessionReq(SOCKET fd, const UCHAR *calledName,
+ const UCHAR *callingName)
+{
+    UCHAR buffer[NBSS_HDRSIZE + MAX_DOMAIN_NAME_LEN * 2], ret;
+    int r;
+    unsigned int len = 0;
+    DWORD bytesSent, bytesReceived, recvFlags = 0;
+    WSABUF wsaBuf;
+
+    buffer[0] = NBSS_REQ;
+    buffer[1] = 0;
+
+    len += NetBTNameEncode(calledName, &buffer[NBSS_HDRSIZE]);
+    len += NetBTNameEncode(callingName, &buffer[NBSS_HDRSIZE + len]);
+
+    NBR_ADDWORD(&buffer[2], len);
+
+    wsaBuf.len = len + NBSS_HDRSIZE;
+    wsaBuf.buf = (char*)buffer;
+
+    r = WSASend(fd, &wsaBuf, 1, &bytesSent, 0, NULL, NULL);
+    if(r < 0 || bytesSent < len + NBSS_HDRSIZE)
+    {
+        ERR("send failed\n");
+        return NRC_SABORT;
+    }
+
+    /* I've already set the recv timeout on this socket (if it supports it), so
+     * just block.  Hopefully we'll always receive the session acknowledgement
+     * within one timeout.
+     */
+    wsaBuf.len = NBSS_HDRSIZE + 1;
+    r = WSARecv(fd, &wsaBuf, 1, &bytesReceived, &recvFlags, NULL, NULL);
+    if (r < 0 || bytesReceived < NBSS_HDRSIZE)
+        ret = NRC_SABORT;
+    else if (buffer[0] == NBSS_NACK)
+    {
+        if (r == NBSS_HDRSIZE + 1)
+        {
+            switch (buffer[NBSS_HDRSIZE])
+            {
+                case NBSS_ERR_INSUFFICIENT_RESOURCES:
+                    ret = NRC_REMTFUL;
+                    break;
+                default:
+                    ret = NRC_NOCALL;
+            }
+        }
+        else
+            ret = NRC_NOCALL;
+    }
+    else if (buffer[0] == NBSS_RETARGET)
+    {
+        FIXME("Got a session retarget, can't deal\n");
+        ret = NRC_NOCALL;
+    }
+    else if (buffer[0] == NBSS_ACK)
+        ret = NRC_GOODRET;
+    else
+        ret = NRC_SYSTEM;
+
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+static UCHAR NetBTCall(void *adapt, PNCB ncb, void **sess)
+{
+    NetBTAdapter *adapter = (NetBTAdapter *)adapt;
+    UCHAR ret;
+    const NBNameCacheEntry *cacheEntry = NULL;
+
+    TRACE("adapt %p, ncb %p\n", adapt, ncb);
+
+    if (!adapter) return NRC_ENVNOTDEF;
+    if (!ncb) return NRC_INVADDRESS;
+    if (!sess) return NRC_BADDR;
+
+    ret = NetBTInternalFindName(adapter, ncb, &cacheEntry);
+    if (ret == NRC_GOODRET)
+    {
+        if (cacheEntry && cacheEntry->numAddresses > 0)
+        {
+            SOCKET fd;
+
+            fd = WSASocketA(PF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
+             WSA_FLAG_OVERLAPPED);
+            if (fd != INVALID_SOCKET)
+            {
+                DWORD timeout;
+                struct sockaddr_in sin;
+
+                if (ncb->ncb_rto > 0)
+                {
+                    timeout = ncb->ncb_rto * 500;
+                    setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout,
+                     sizeof(timeout));
+                }
+                if (ncb->ncb_rto > 0)
+                {
+                    timeout = ncb->ncb_sto * 500;
+                    setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout,
+                     sizeof(timeout));
+                }
+
+                memset(&sin, 0, sizeof(sin));
+                memcpy(&sin.sin_addr, &cacheEntry->addresses[0],
+                 sizeof(sin.sin_addr));
+                sin.sin_family = AF_INET;
+                sin.sin_port   = htons(PORT_NBSS);
+                /* FIXME: use nonblocking mode for the socket, check the
+                 * cancel flag periodically
+                 */
+                if (connect(fd, (struct sockaddr *)&sin, sizeof(sin))
+                 == SOCKET_ERROR)
+                    ret = NRC_CMDTMO;
+                else
+                {
+                    static UCHAR fakedCalledName[] = "*SMBSERVER";
+                    const UCHAR *calledParty = cacheEntry->nbname[0] == '*'
+                     ? fakedCalledName : cacheEntry->nbname;
+
+                    ret = NetBTSessionReq(fd, calledParty, ncb->ncb_name);
+                    if (ret != NRC_GOODRET && calledParty[0] == '*')
+                    {
+                        FIXME("NBT session to \"*SMBSERVER\" refused,\n");
+                        FIXME("should try finding name using ASTAT\n");
+                    }
+                }
+                if (ret != NRC_GOODRET)
+                    closesocket(fd);
+                else
+                {
+                    NetBTSession *session = HeapAlloc(
+                     GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(NetBTSession));
+
+                    if (session)
+                    {
+                        session->fd = fd;
+                        InitializeCriticalSection(&session->cs);
+                        *sess = session;
+                    }
+                    else
+                    {
+                        ret = NRC_OSRESNOTAV;
+                        closesocket(fd);
+                    }
+                }
+            }
+            else
+                ret = NRC_OSRESNOTAV;
+        }
+        else
+            ret = NRC_NAMERR;
+    }
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+/* Notice that I don't protect against multiple thread access to NetBTSend.
+ * This is because I don't update any data in the adapter, and I only make a
+ * single call to WSASend, which I assume to act atomically (not interleaving
+ * data from other threads).
+ * I don't lock, because I only depend on the fd being valid, and this won't be
+ * true until a session setup is completed.
+ */
+static UCHAR NetBTSend(void *adapt, void *sess, PNCB ncb)
+{
+    NetBTAdapter *adapter = (NetBTAdapter *)adapt;
+    NetBTSession *session = (NetBTSession *)sess;
+    UCHAR buffer[NBSS_HDRSIZE], ret;
+    int r;
+    WSABUF wsaBufs[2];
+    DWORD bytesSent;
+
+    TRACE("adapt %p, session %p, NCB %p\n", adapt, session, ncb);
+
+    if (!adapter) return NRC_ENVNOTDEF;
+    if (!ncb) return NRC_INVADDRESS;
+    if (!ncb->ncb_buffer) return NRC_BADDR;
+    if (!session) return NRC_SNUMOUT;
+    if (session->fd == INVALID_SOCKET) return NRC_SNUMOUT;
+
+    buffer[0] = NBSS_MSG;
+    buffer[1] = 0;
+    NBR_ADDWORD(&buffer[2], ncb->ncb_length);
+
+    wsaBufs[0].len = NBSS_HDRSIZE;
+    wsaBufs[0].buf = (char*)buffer;
+    wsaBufs[1].len = ncb->ncb_length;
+    wsaBufs[1].buf = (char*)ncb->ncb_buffer;
+
+    r = WSASend(session->fd, wsaBufs, sizeof(wsaBufs) / sizeof(wsaBufs[0]),
+     &bytesSent, 0, NULL, NULL);
+    if (r == SOCKET_ERROR)
+    {
+        NetBIOSHangupSession(ncb);
+        ret = NRC_SABORT;
+    }
+    else if (bytesSent < NBSS_HDRSIZE + ncb->ncb_length)
+    {
+        FIXME("Only sent %ld bytes (of %d), hanging up session\n", bytesSent,
+         NBSS_HDRSIZE + ncb->ncb_length);
+        NetBIOSHangupSession(ncb);
+        ret = NRC_SABORT;
+    }
+    else
+    {
+        ret = NRC_GOODRET;
+        adapter->xmit_success++;
+    }
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+static UCHAR NetBTRecv(void *adapt, void *sess, PNCB ncb)
+{
+    NetBTAdapter *adapter = (NetBTAdapter *)adapt;
+    NetBTSession *session = (NetBTSession *)sess;
+    UCHAR buffer[NBSS_HDRSIZE], ret;
+    int r;
+    WSABUF wsaBufs[2];
+    DWORD bufferCount, bytesReceived, flags;
+
+    TRACE("adapt %p, session %p, NCB %p\n", adapt, session, ncb);
+
+    if (!adapter) return NRC_ENVNOTDEF;
+    if (!ncb) return NRC_BADDR;
+    if (!ncb->ncb_buffer) return NRC_BADDR;
+    if (!session) return NRC_SNUMOUT;
+    if (session->fd == INVALID_SOCKET) return NRC_SNUMOUT;
+
+    EnterCriticalSection(&session->cs);
+    bufferCount = 0;
+    if (session->bytesPending == 0)
+    {
+        bufferCount++;
+        wsaBufs[0].len = NBSS_HDRSIZE;
+        wsaBufs[0].buf = (char*)buffer;
+    }
+    wsaBufs[bufferCount].len = ncb->ncb_length;
+    wsaBufs[bufferCount].buf = (char*)ncb->ncb_buffer;
+    bufferCount++;
+
+    flags = 0;
+    /* FIXME: should poll a bit so I can check the cancel flag */
+    r = WSARecv(session->fd, wsaBufs, bufferCount, &bytesReceived, &flags,
+     NULL, NULL);
+    if (r == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK)
+    {
+        LeaveCriticalSection(&session->cs);
+        ERR("Receive error, WSAGetLastError() returns %d\n", WSAGetLastError());
+        NetBIOSHangupSession(ncb);
+        ret = NRC_SABORT;
+    }
+    else if (NCB_CANCELLED(ncb))
+    {
+        LeaveCriticalSection(&session->cs);
+        ret = NRC_CMDCAN;
+    }
+    else
+    {
+        if (bufferCount == 2)
+        {
+            if (buffer[0] == NBSS_KEEPALIVE)
+            {
+                LeaveCriticalSection(&session->cs);
+                FIXME("Oops, received a session keepalive and lost my place\n");
+                /* need to read another session header until we get a session
+                 * message header. */
+                NetBIOSHangupSession(ncb);
+                ret = NRC_SABORT;
+            }
+            else if (buffer[0] != NBSS_MSG)
+            {
+                LeaveCriticalSection(&session->cs);
+                FIXME("Received unexpected session msg type %d\n", buffer[0]);
+                NetBIOSHangupSession(ncb);
+                ret = NRC_SABORT;
+            }
+            else
+            {
+                if (buffer[1] & NBSS_EXTENSION)
+                {
+                    LeaveCriticalSection(&session->cs);
+                    FIXME("Received a message that's too long for my taste\n");
+                    NetBIOSHangupSession(ncb);
+                    ret = NRC_SABORT;
+                }
+                else
+                {
+                    session->bytesPending = NBSS_HDRSIZE
+                     + NBR_GETWORD(&buffer[2]) - bytesReceived;
+                    ncb->ncb_length = bytesReceived - NBSS_HDRSIZE;
+                    LeaveCriticalSection(&session->cs);
+                }
+            }
+        }
+        else
+        {
+            if (bytesReceived < session->bytesPending)
+                session->bytesPending -= bytesReceived;
+            else
+                session->bytesPending = 0;
+            LeaveCriticalSection(&session->cs);
+            ncb->ncb_length = bytesReceived;
+        }
+        if (session->bytesPending > 0)
+            ret = NRC_INCOMP;
+        else
+        {
+            ret = NRC_GOODRET;
+            adapter->recv_success++;
+        }
+    }
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+static UCHAR NetBTHangup(void *adapt, void *sess)
+{
+    NetBTSession *session = (NetBTSession *)sess;
+
+    TRACE("adapt %p, session %p\n", adapt, session);
+
+    if (!session) return NRC_SNUMOUT;
+
+    /* I don't lock the session, because NetBTRecv knows not to decrement
+     * past 0, so if a receive completes after this it should still deal.
+     */
+    closesocket(session->fd);
+    session->fd = INVALID_SOCKET;
+    session->bytesPending = 0;
+    DeleteCriticalSection(&session->cs);
+    HeapFree(GetProcessHeap(), 0, session);
+
+    return NRC_GOODRET;
+}
+
+static void NetBTCleanupAdapter(void *adapt)
+{
+    TRACE("adapt %p\n", adapt);
+    if (adapt)
+    {
+        NetBTAdapter *adapter = (NetBTAdapter *)adapt;
+
+        if (adapter->nameCache)
+            NBNameCacheDestroy(adapter->nameCache);
+        HeapFree(GetProcessHeap(), 0, adapt);
+    }
+}
+
+static void NetBTCleanup(void)
+{
+    TRACE("\n");
+    if (gNameCache)
+    {
+        NBNameCacheDestroy(gNameCache);
+        gNameCache = NULL;
+    }
+}
+
+static UCHAR NetBTRegisterAdapter(PMIB_IPADDRROW ipRow)
+{
+    UCHAR ret;
+    NetBTAdapter *adapter;
+    
+    if (!ipRow) return NRC_BADDR;
+
+    adapter = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(NetBTAdapter));
+    if (adapter)
+    {
+        memcpy(&adapter->ipr, ipRow, sizeof(MIB_IPADDRROW));
+        if (!NetBIOSRegisterAdapter(gTransportID, ipRow->dwIndex, adapter))
+        {
+            NetBTCleanupAdapter(adapter);
+            ret = NRC_SYSTEM;
+        }
+        else
+            ret = NRC_GOODRET;
+    }
+    else
+        ret = NRC_OSRESNOTAV;
+    return ret;
+}
+
+/* Callback for NetBIOS adapter enumeration.  Assumes closure is a pointer to
+ * a MIB_IPADDRTABLE containing all the IP adapters needed to be added to the
+ * NetBIOS adapter table.  For each callback, checks if the passed-in adapt
+ * has an entry in the table; if so, this adapter was enumerated previously,
+ * and it's enabled.  As a flag, the table's dwAddr entry is changed to
+ * INADDR_LOOPBACK, since this is an invalid address for a NetBT adapter.
+ * The NetBTEnum function will add any remaining adapters from the
+ * MIB_IPADDRTABLE to the NetBIOS adapter table.
+ */
+static BOOL NetBTEnumCallback(UCHAR totalLANAs, UCHAR lanaIndex,
+ ULONG transport, const NetBIOSAdapterImpl *data, void *closure)
+{
+    BOOL ret;
+    PMIB_IPADDRTABLE table = (PMIB_IPADDRTABLE)closure;
+
+    if (table && data)
+    {
+        DWORD ndx;
+
+        ret = FALSE;
+        for (ndx = 0; !ret && ndx < table->dwNumEntries; ndx++)
+        {
+            const NetBTAdapter *adapter = (const NetBTAdapter *)data->data;
+
+            if (table->table[ndx].dwIndex == adapter->ipr.dwIndex)
+            {
+                NetBIOSEnableAdapter(data->lana);
+                table->table[ndx].dwAddr = INADDR_LOOPBACK;
+                ret = TRUE;
+            }
+        }
+    }
+    else
+        ret = FALSE;
+    return ret;
+}
+
+/* Enumerates adapters by:
+ * - retrieving the IP address table for the local machine
+ * - eliminating loopback addresses from the table
+ * - eliminating redundant addresses, that is, multiple addresses on the same
+ *   subnet
+ * Calls NetBIOSEnumAdapters, passing the resulting table as the callback
+ * data.  The callback reenables each adapter that's already in the NetBIOS
+ * table.  After NetBIOSEnumAdapters returns, this function adds any remaining
+ * adapters to the NetBIOS table.
+ */
+static UCHAR NetBTEnum(void)
+{
+    UCHAR ret;
+    DWORD size = 0;
+
+    TRACE("\n");
+
+    if (GetIpAddrTable(NULL, &size, FALSE) == ERROR_INSUFFICIENT_BUFFER)
+    {
+        PMIB_IPADDRTABLE ipAddrs, coalesceTable = NULL;
+        DWORD numIPAddrs = (size - sizeof(MIB_IPADDRTABLE)) /
+         sizeof(MIB_IPADDRROW) + 1;
+
+        ipAddrs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
+        if (ipAddrs)
+            coalesceTable = HeapAlloc(GetProcessHeap(),
+             HEAP_ZERO_MEMORY, sizeof(MIB_IPADDRTABLE) +
+             (min(numIPAddrs, MAX_LANA + 1) - 1) * sizeof(MIB_IPADDRROW));
+        if (ipAddrs && coalesceTable)
+        {
+            if (GetIpAddrTable(ipAddrs, &size, FALSE) == ERROR_SUCCESS)
+            {
+                DWORD ndx;
+
+                for (ndx = 0; ndx < ipAddrs->dwNumEntries; ndx++)
+                {
+                    if ((ipAddrs->table[ndx].dwAddr &
+                     ipAddrs->table[ndx].dwMask) !=
+                     htonl((INADDR_LOOPBACK & IN_CLASSA_NET)))
+                    {
+                        BOOL newNetwork = TRUE;
+                        DWORD innerIndex;
+
+                        /* make sure we don't have more than one entry
+                         * for a subnet */
+                        for (innerIndex = 0; newNetwork &&
+                         innerIndex < coalesceTable->dwNumEntries; innerIndex++)
+                            if ((ipAddrs->table[ndx].dwAddr &
+                             ipAddrs->table[ndx].dwMask) ==
+                             (coalesceTable->table[innerIndex].dwAddr
+                             & coalesceTable->table[innerIndex].dwMask))
+                                newNetwork = FALSE;
+
+                        if (newNetwork)
+                            memcpy(&coalesceTable->table[
+                             coalesceTable->dwNumEntries++],
+                             &ipAddrs->table[ndx], sizeof(MIB_IPADDRROW));
+                    }
+                }
+
+                NetBIOSEnumAdapters(gTransportID, NetBTEnumCallback,
+                 coalesceTable);
+                ret = NRC_GOODRET;
+                for (ndx = 0; ret == NRC_GOODRET &&
+                 ndx < coalesceTable->dwNumEntries; ndx++)
+                    if (coalesceTable->table[ndx].dwAddr != INADDR_LOOPBACK)
+                        ret = NetBTRegisterAdapter(&coalesceTable->table[ndx]);
+            }
+            else
+                ret = NRC_SYSTEM;
+            HeapFree(GetProcessHeap(), 0, ipAddrs);
+            HeapFree(GetProcessHeap(), 0, coalesceTable);
+        }
+        else
+            ret = NRC_OSRESNOTAV;
+    }
+    else
+        ret = NRC_SYSTEM;
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+static const WCHAR VxD_MSTCPW[] = { 'S','Y','S','T','E','M','\\','C','u','r',
+ 'r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\','S','e','r','v',
+ 'i','c','e','s','\\','V','x','D','\\','M','S','T','C','P','\0' };
+static const WCHAR NetBT_ParametersW[] = { 'S','Y','S','T','E','M','\\','C','u',
+ 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\','S','e','r',
+ 'v','i','c','e','s','\\','N','e','t','B','T','\\','P','a','r','a','m','e','t',
+ 'e','r','s','\0' };
+static const WCHAR EnableDNSW[] = { 'E','n','a','b','l','e','D','N','S','\0' };
+static const WCHAR BcastNameQueryCountW[] = { 'B','c','a','s','t','N','a','m',
+ 'e','Q','u','e','r','y','C','o','u','n','t','\0' };
+static const WCHAR BcastNameQueryTimeoutW[] = { 'B','c','a','s','t','N','a','m',
+ 'e','Q','u','e','r','y','T','i','m','e','o','u','t','\0' };
+static const WCHAR NameSrvQueryCountW[] = { 'N','a','m','e','S','r','v',
+ 'Q','u','e','r','y','C','o','u','n','t','\0' };
+static const WCHAR NameSrvQueryTimeoutW[] = { 'N','a','m','e','S','r','v',
+ 'Q','u','e','r','y','T','i','m','e','o','u','t','\0' };
+static const WCHAR ScopeIDW[] = { 'S','c','o','p','e','I','D','\0' };
+static const WCHAR CacheTimeoutW[] = { 'C','a','c','h','e','T','i','m','e','o',
+ 'u','t','\0' };
+static const WCHAR Config_NetworkW[] = { 'S','o','f','t','w','a','r','e','\\',
+                                         'W','i','n','e','\\','N','e','t','w','o','r','k','\0' };
+
+/* Initializes global variables and registers the NetBT transport */
+void NetBTInit(void)
+{
+    HKEY hKey;
+    NetBIOSTransport transport;
+    LONG ret;
+
+    TRACE("\n");
+
+    gEnableDNS = TRUE;
+    gBCastQueries = BCAST_QUERIES;
+    gBCastQueryTimeout = BCAST_QUERY_TIMEOUT;
+    gWINSQueries = WINS_QUERIES;
+    gWINSQueryTimeout = WINS_QUERY_TIMEOUT;
+    gNumWINSServers = 0;
+    memset(gWINSServers, 0, sizeof(gWINSServers));
+    gScopeID[0] = '\0';
+    gCacheTimeout = CACHE_TIMEOUT;
+
+    /* Try to open the Win9x NetBT configuration key */
+    ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, VxD_MSTCPW, 0, KEY_READ, &hKey);
+    /* If that fails, try the WinNT NetBT configuration key */
+    if (ret != ERROR_SUCCESS)
+        ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, NetBT_ParametersW, 0, KEY_READ,
+         &hKey);
+    if (ret == ERROR_SUCCESS)
+    {
+        DWORD dword, size;
+
+        size = sizeof(dword);
+        if (RegQueryValueExW(hKey, EnableDNSW, NULL, NULL,
+         (LPBYTE)&dword, &size) == ERROR_SUCCESS)
+            gEnableDNS = dword;
+        size = sizeof(dword);
+        if (RegQueryValueExW(hKey, BcastNameQueryCountW, NULL, NULL,
+         (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERIES
+         && dword <= MAX_QUERIES)
+            gBCastQueries = dword;
+        size = sizeof(dword);
+        if (RegQueryValueExW(hKey, BcastNameQueryTimeoutW, NULL, NULL,
+         (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERY_TIMEOUT
+         && dword <= MAX_QUERY_TIMEOUT)
+            gBCastQueryTimeout = dword;
+        size = sizeof(dword);
+        if (RegQueryValueExW(hKey, NameSrvQueryCountW, NULL, NULL,
+         (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERIES
+         && dword <= MAX_QUERIES)
+            gWINSQueries = dword;
+        size = sizeof(dword);
+        if (RegQueryValueExW(hKey, NameSrvQueryTimeoutW, NULL, NULL,
+         (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERY_TIMEOUT
+         && dword <= MAX_QUERY_TIMEOUT)
+            gWINSQueryTimeout = dword;
+        size = MAX_DOMAIN_NAME_LEN - 1;
+        if (RegQueryValueExW(hKey, ScopeIDW, NULL, NULL, (LPBYTE)gScopeID + 1, &size)
+         == ERROR_SUCCESS)
+        {
+            /* convert into L2-encoded version, suitable for use by
+               NetBTNameEncode */
+            char *ptr, *lenPtr;
+
+            for (ptr = gScopeID + 1; *ptr &&
+             ptr - gScopeID < MAX_DOMAIN_NAME_LEN; )
+            {
+                for (lenPtr = ptr - 1, *lenPtr = 0; *ptr && *ptr != '.' &&
+                 ptr - gScopeID < MAX_DOMAIN_NAME_LEN; ptr++)
+                    *lenPtr += 1;
+                ptr++;
+            }
+        }
+        if (RegQueryValueExW(hKey, CacheTimeoutW, NULL, NULL,
+         (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_CACHE_TIMEOUT)
+            gCacheTimeout = dword;
+        RegCloseKey(hKey);
+    }
+    /* WINE-specific NetBT registry settings.  Because our adapter naming is
+     * different than MS', we can't do per-adapter WINS configuration in the
+     * same place.  Just do a global WINS configuration instead.
+     */
+    /* @@ Wine registry key: HKCU\Software\Wine\Network */
+    if (RegOpenKeyW(HKEY_CURRENT_USER, Config_NetworkW, &hKey) == ERROR_SUCCESS)
+    {
+        static const char *nsValueNames[] = { "WinsServer", "BackupWinsServer" };
+        char nsString[16];
+        DWORD size, ndx;
+
+        for (ndx = 0; ndx < sizeof(nsValueNames) / sizeof(nsValueNames[0]);
+         ndx++)
+        {
+            size = sizeof(nsString) / sizeof(char);
+            if (RegQueryValueExA(hKey, nsValueNames[ndx], NULL, NULL,
+             (LPBYTE)nsString, &size) == ERROR_SUCCESS)
+            {
+                unsigned long addr = inet_addr(nsString);
+
+                if (addr != INADDR_NONE && gNumWINSServers < MAX_WINS_SERVERS)
+                    gWINSServers[gNumWINSServers++] = addr;
+            }
+        }
+        RegCloseKey(hKey);
+    }
+
+    transport.enumerate      = NetBTEnum;
+    transport.astat          = NetBTAstat;
+    transport.findName       = NetBTFindName;
+    transport.call           = NetBTCall;
+    transport.send           = NetBTSend;
+    transport.recv           = NetBTRecv;
+    transport.hangup         = NetBTHangup;
+    transport.cleanupAdapter = NetBTCleanupAdapter;
+    transport.cleanup        = NetBTCleanup;
+    memcpy(&gTransportID, TRANSPORT_NBT, sizeof(ULONG));
+    NetBIOSRegisterTransport(gTransportID, &transport);
+}
diff --git a/reactos/lib/netapi32/netapi32.c b/reactos/lib/netapi32/netapi32.c
new file mode 100644 (file)
index 0000000..320109b
--- /dev/null
@@ -0,0 +1,135 @@
+/* Copyright 2001 Mike McCormack
+ * Copyright 2003 Juan Lang
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "config.h"
+
+#include "wine/debug.h"
+#include "netbios.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(netbios);
+
+HMODULE NETAPI32_hModule = 0;
+
+BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+    TRACE("%p,%lx,%p\n", hinstDLL, fdwReason, lpvReserved);
+
+    switch (fdwReason) {
+        case DLL_PROCESS_ATTACH:
+        {
+            DisableThreadLibraryCalls(hinstDLL);
+            NETAPI32_hModule = hinstDLL;
+            NetBIOSInit();
+            NetBTInit();
+            break;
+        }
+        case DLL_PROCESS_DETACH:
+        {
+            NetBIOSShutdown();
+            break;
+        }
+    }
+
+    return TRUE;
+}
+
+NET_API_STATUS  WINAPI NetServerEnum(
+  LPCWSTR servername,
+  DWORD level,
+  LPBYTE* bufptr,
+  DWORD prefmaxlen,
+  LPDWORD entriesread,
+  LPDWORD totalentries,
+  DWORD servertype,
+  LPCWSTR domain,
+  LPDWORD resume_handle
+)
+{
+    FIXME("Stub (%s %ld %p %ld %p %p %ld %s %p)\n", debugstr_w(servername),
+     level, bufptr, prefmaxlen, entriesread, totalentries, servertype,
+     debugstr_w(domain), resume_handle);
+
+    return ERROR_NO_BROWSER_SERVERS_FOUND;
+}
+
+
+/************************************************************
+ *                NetServerGetInfo  (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetServerGetInfo(LMSTR servername, DWORD level, LPBYTE* bufptr)
+{
+    FIXME("stub (%p, %ld, %p)\n", servername, level, bufptr);
+    return ERROR_ACCESS_DENIED;
+}
+
+
+/************************************************************
+ *                NetStatisticsGet  (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetStatisticsGet(LPWSTR server, LPWSTR service,
+                                       DWORD level, DWORD options,
+                                       LPBYTE *bufptr)
+{
+    TRACE("(%p, %p, %ld, %ld, %p)\n", server, service, level, options, bufptr);
+    return NERR_InternalError;
+}
+
+DWORD WINAPI NetpNetBiosStatusToApiStatus(DWORD nrc)
+{
+    DWORD ret;
+
+    switch (nrc)
+    {
+        case NRC_GOODRET:
+            ret = NO_ERROR;
+            break;
+        case NRC_NORES:
+            ret = NERR_NoNetworkResource;
+            break;
+        case NRC_DUPNAME:
+            ret = NERR_AlreadyExists;
+            break;
+        case NRC_NAMTFUL:
+            ret = NERR_TooManyNames;
+            break;
+        case NRC_ACTSES:
+            ret = NERR_DeleteLater;
+            break;
+        case NRC_REMTFUL:
+            ret = ERROR_REM_NOT_LIST;
+            break;
+        case NRC_NOCALL:
+            ret = NERR_NameNotFound;
+            break;
+        case NRC_NOWILD:
+            ret = ERROR_INVALID_PARAMETER;
+            break;
+        case NRC_INUSE:
+            ret = NERR_DuplicateName;
+            break;
+        case NRC_NAMERR:
+            ret = ERROR_INVALID_PARAMETER;
+            break;
+        case NRC_NAMCONF:
+            ret = NERR_DuplicateName;
+            break;
+        default:
+            ret = NERR_NetworkError;
+    }
+    return ret;
+}
diff --git a/reactos/lib/netapi32/netapi32.spec b/reactos/lib/netapi32/netapi32.spec
new file mode 100644 (file)
index 0000000..412ac72
--- /dev/null
@@ -0,0 +1,275 @@
+@ stub I_BrowserDebugCall
+@ stub I_BrowserDebugTrace
+@ stdcall I_BrowserQueryEmulatedDomains(wstr ptr ptr)
+@ stub I_BrowserQueryOtherDomains
+@ stub I_BrowserQueryStatistics
+@ stub I_BrowserResetNetlogonState
+@ stub I_BrowserResetStatistics
+@ stub I_BrowserServerEnum
+@ stdcall I_BrowserSetNetlogonState(wstr wstr wstr long)
+@ stub I_NetAccountDeltas
+@ stub I_NetAccountSync
+@ stub I_NetDatabaseDeltas
+@ stub I_NetDatabaseRedo
+@ stub I_NetDatabaseSync2
+@ stub I_NetDatabaseSync
+@ stub I_NetDfsCreateExitPoint
+@ stub I_NetDfsCreateLocalPartition
+@ stub I_NetDfsDeleteExitPoint
+@ stub I_NetDfsDeleteLocalPartition
+@ stub I_NetDfsFixLocalVolume
+@ stub I_NetDfsGetVersion
+@ stub I_NetDfsIsThisADomainName
+@ stub I_NetDfsModifyPrefix
+@ stub I_NetDfsSetLocalVolumeState
+@ stub I_NetDfsSetServerInfo
+@ stub I_NetGetDCList
+@ stub I_NetListCanonicalize
+@ stub I_NetListTraverse
+@ stub I_NetLogonControl2
+@ stub I_NetLogonControl
+@ stub I_NetLogonSamLogoff
+@ stub I_NetLogonSamLogon
+@ stub I_NetLogonUasLogoff
+@ stub I_NetLogonUasLogon
+@ stub I_NetNameCanonicalize
+@ stdcall I_NetNameCompare(ptr wstr wstr ptr ptr)
+@ stdcall I_NetNameValidate(ptr wstr ptr ptr)
+@ stub I_NetPathCanonicalize
+@ stub I_NetPathCompare
+@ stub I_NetPathType
+@ stub I_NetServerAuthenticate2
+@ stub I_NetServerAuthenticate
+@ stub I_NetServerPasswordSet
+@ stub I_NetServerReqChallenge
+@ stub I_NetServerSetServiceBits
+@ stub I_NetServerSetServiceBitsEx
+@ stub NetAlertRaise
+@ stub NetAlertRaiseEx
+@ stdcall NetApiBufferAllocate(long ptr)
+@ stdcall NetApiBufferFree(ptr)
+@ stdcall NetApiBufferReallocate(ptr long ptr)
+@ stdcall NetApiBufferSize(ptr ptr)
+@ stub NetAuditClear
+@ stub NetAuditRead
+@ stub NetAuditWrite
+@ stub NetBrowserStatisticsGet
+@ stub NetConfigGet
+@ stub NetConfigGetAll
+@ stub NetConfigSet
+@ stub NetConnectionEnum
+@ stub NetDfsAdd
+@ stub NetDfsEnum
+@ stub NetDfsGetInfo
+@ stub NetDfsManagerGetConfigInfo
+@ stub NetDfsMove
+@ stub NetDfsRemove
+@ stub NetDfsRename
+@ stub NetDfsSetInfo
+@ stub NetEnumerateTrustedDomains
+@ stub NetErrorLogClear
+@ stub NetErrorLogRead
+@ stub NetErrorLogWrite
+@ stub NetFileClose
+@ stub NetFileEnum
+@ stub NetFileGetInfo
+@ stub NetGetAnyDCName
+@ stdcall NetGetDCName(wstr wstr ptr)
+@ stub NetGetDisplayInformationIndex
+@ stdcall NetGetJoinInformation(wstr ptr ptr)
+@ stub NetGroupAdd
+@ stub NetGroupAddUser
+@ stub NetGroupDel
+@ stub NetGroupDelUser
+@ stub NetGroupEnum
+@ stub NetGroupGetInfo
+@ stub NetGroupGetUsers
+@ stub NetGroupSetInfo
+@ stub NetGroupSetUsers
+@ stdcall NetLocalGroupAdd(wstr long ptr ptr)
+@ stub NetLocalGroupAddMember
+@ stub NetLocalGroupAddMembers
+@ stub NetLocalGroupDel
+@ stub NetLocalGroupDelMember
+@ stub NetLocalGroupDelMembers
+@ stub NetLocalGroupEnum
+@ stub NetLocalGroupGetInfo
+@ stub NetLocalGroupGetMembers
+@ stub NetLocalGroupSetInfo
+@ stdcall NetLocalGroupSetMembers(wstr wstr long ptr ptr)
+@ stub NetMessageBufferSend
+@ stub NetMessageNameAdd
+@ stub NetMessageNameDel
+@ stub NetMessageNameEnum
+@ stub NetMessageNameGetInfo
+@ stdcall NetQueryDisplayInformation(wstr long long long long ptr ptr)
+@ stub NetRemoteComputerSupports
+@ stub NetRemoteTOD
+@ stub NetReplExportDirAdd
+@ stub NetReplExportDirDel
+@ stub NetReplExportDirEnum
+@ stub NetReplExportDirGetInfo
+@ stub NetReplExportDirLock
+@ stub NetReplExportDirSetInfo
+@ stub NetReplExportDirUnlock
+@ stub NetReplGetInfo
+@ stub NetReplImportDirAdd
+@ stub NetReplImportDirDel
+@ stub NetReplImportDirEnum
+@ stub NetReplImportDirGetInfo
+@ stub NetReplImportDirLock
+@ stub NetReplImportDirUnlock
+@ stub NetReplSetInfo
+@ stub NetRplAdapterAdd
+@ stub NetRplAdapterDel
+@ stub NetRplAdapterEnum
+@ stub NetRplBootAdd
+@ stub NetRplBootDel
+@ stub NetRplBootEnum
+@ stub NetRplClose
+@ stub NetRplConfigAdd
+@ stub NetRplConfigDel
+@ stub NetRplConfigEnum
+@ stub NetRplGetInfo
+@ stub NetRplOpen
+@ stub NetRplProfileAdd
+@ stub NetRplProfileClone
+@ stub NetRplProfileDel
+@ stub NetRplProfileEnum
+@ stub NetRplProfileGetInfo
+@ stub NetRplProfileSetInfo
+@ stub NetRplSetInfo
+@ stub NetRplSetSecurity
+@ stub NetRplVendorAdd
+@ stub NetRplVendorDel
+@ stub NetRplVendorEnum
+@ stub NetRplWkstaAdd
+@ stub NetRplWkstaClone
+@ stub NetRplWkstaDel
+@ stub NetRplWkstaEnum
+@ stub NetRplWkstaGetInfo
+@ stub NetRplWkstaSetInfo
+@ stub NetScheduleJobAdd
+@ stub NetScheduleJobDel
+@ stub NetScheduleJobEnum
+@ stub NetScheduleJobGetInfo
+@ stub NetServerComputerNameAdd
+@ stub NetServerComputerNameDel
+@ stub NetServerDiskEnum
+@ stdcall NetServerEnum(wstr long ptr long ptr ptr long wstr ptr)
+@ stub NetServerEnumEx
+@ stdcall NetServerGetInfo(wstr long ptr)
+@ stub NetServerSetInfo
+@ stub NetServerTransportAdd
+@ stub NetServerTransportAddEx
+@ stub NetServerTransportDel
+@ stub NetServerTransportEnum
+@ stub NetServiceControl
+@ stub NetServiceEnum
+@ stub NetServiceGetInfo
+@ stub NetServiceInstall
+@ stub NetSessionDel
+@ stub NetSessionEnum
+@ stub NetSessionGetInfo
+@ stub NetShareAdd
+@ stub NetShareCheck
+@ stub NetShareDel
+@ stub NetShareDelSticky
+@ stdcall NetShareEnum(wstr long ptr long ptr ptr ptr)
+@ stub NetShareEnumSticky
+@ stub NetShareGetInfo
+@ stub NetShareSetInfo
+@ stdcall NetStatisticsGet(wstr wstr long long ptr)
+@ stub NetUseAdd
+@ stub NetUseDel
+@ stub NetUseEnum
+@ stub NetUseGetInfo
+@ stdcall NetUserAdd(wstr long ptr ptr)
+@ stub NetUserChangePassword
+@ stdcall NetUserDel(wstr wstr)
+@ stdcall NetUserEnum(wstr long long ptr long ptr ptr ptr)
+@ stub NetUserGetGroups
+@ stdcall NetUserGetInfo(wstr wstr long ptr)
+@ stub NetUserGetLocalGroups
+@ stdcall NetUserModalsGet(wstr long ptr)
+@ stub NetUserModalsSet
+@ stub NetUserSetGroups
+@ stub NetUserSetInfo
+@ stdcall NetWkstaGetInfo(wstr long ptr)
+@ stub NetWkstaSetInfo
+@ stub NetWkstaTransportAdd
+@ stub NetWkstaTransportDel
+@ stdcall NetWkstaTransportEnum (wstr long ptr long ptr ptr ptr)
+@ stub NetWkstaUserEnum
+@ stdcall NetWkstaUserGetInfo(wstr long ptr)
+@ stub NetWkstaUserSetInfo
+@ stdcall NetapipBufferAllocate(long ptr) NetApiBufferAllocate
+@ stdcall Netbios(ptr)
+@ stub NetpAccessCheck
+@ stub NetpAccessCheckAndAudit
+@ stub NetpAllocConfigName
+@ stub NetpAllocStrFromStr
+@ stub NetpAllocStrFromWStr
+@ stub NetpAllocTStrFromString
+@ stub NetpAllocWStrFromStr
+@ stub NetpAllocWStrFromWStr
+@ stub NetpApiStatusToNtStatus
+@ stub NetpAssertFailed
+@ stub NetpCloseConfigData
+@ stub NetpCopyStringToBuffer
+@ stub NetpCreateSecurityObject
+@ stub NetpDbgDisplayServerInfo
+@ stub NetpDbgPrint
+@ stdcall NetpDeleteSecurityObject(long) ntdll.RtlDeleteSecurityObject
+@ stdcall NetpGetComputerName(ptr)
+@ stub NetpGetConfigBool
+@ stub NetpGetConfigDword
+@ stub NetpGetConfigTStrArray
+@ stub NetpGetConfigValue
+@ stub NetpGetDomainName
+@ stub NetpGetFileSecurity
+@ stub NetpGetPrivilege
+@ stub NetpHexDump
+@ stdcall NetpInitOemString(ptr str) ntdll.RtlInitAnsiString
+@ stub NetpIsRemote
+@ stub NetpIsUncComputerNameValid
+@ stub NetpLocalTimeZoneOffset
+@ stub NetpLogonPutUnicodeString
+@ stub NetpNetBiosAddName
+@ stub NetpNetBiosCall
+@ stub NetpNetBiosDelName
+@ stub NetpNetBiosGetAdapterNumbers
+@ stub NetpNetBiosHangup
+@ stub NetpNetBiosReceive
+@ stub NetpNetBiosReset
+@ stub NetpNetBiosSend
+@ stdcall NetpNetBiosStatusToApiStatus(long)
+@ stub NetpNtStatusToApiStatus
+@ stub NetpOpenConfigData
+@ stub NetpPackString
+@ stub NetpReleasePrivilege
+@ stub NetpSetConfigBool
+@ stub NetpSetConfigDword
+@ stub NetpSetConfigTStrArray
+@ stub NetpSetFileSecurity
+@ stub NetpSmbCheck
+@ stub NetpStringToNetBiosName
+@ stub NetpTStrArrayEntryCount
+@ stub NetpwNameCanonicalize
+@ stub NetpwNameCompare
+@ stub NetpwNameValidate
+@ stub NetpwPathCanonicalize
+@ stub NetpwPathCompare
+@ stub NetpwPathType
+@ stub NlBindingAddServerToCache
+@ stub NlBindingRemoveServerFromCache
+@ stub NlBindingSetAuthInfo
+@ stub RxNetAccessAdd
+@ stub RxNetAccessDel
+@ stub RxNetAccessEnum
+@ stub RxNetAccessGetInfo
+@ stub RxNetAccessGetUserPerms
+@ stub RxNetAccessSetInfo
+@ stub RxNetServerEnum
+@ stub RxNetUserPasswordSet
+@ stub RxRemoteApi
diff --git a/reactos/lib/netapi32/netapi32.xml b/reactos/lib/netapi32/netapi32.xml
new file mode 100644 (file)
index 0000000..b5db33d
--- /dev/null
@@ -0,0 +1,27 @@
+<module name="netapi32" type="win32dll" baseaddress="${BASEADDRESS_NETAPI32}" installbase="system32" installname="netapi32.dll" allowwarnings="true">
+       <importlibrary definition="netapi32.spec.def" />
+       <include base="netapi32">.</include>
+       <include base="ReactOS">include/wine</include>
+       <define name="__REACTOS__" />
+       <define name="__USE_W32API" />
+       <define name="_WIN32_IE">0x600</define>
+       <define name="_WIN32_WINNT">0x501</define>
+       <define name="WINVER">0x501</define>
+       <define name="_SVRAPI_" />
+       <library>wine</library>
+       <library>ntdll</library>
+       <library>kernel32</library>
+       <library>advapi32</library>
+       <library>ws2_32</library>
+       <library>iphlpapi</library>
+       <file>access.c</file>
+       <file>apibuf.c</file>
+       <file>browsr.c</file>
+       <file>nbcmdqueue.c</file>
+       <file>nbnamecache.c</file>
+       <file>nbt.c</file>
+       <file>netapi32.c</file>
+       <file>netbios.c</file>
+       <file>wksta.c</file>
+       <file>netapi32.spec</file>
+</module>
diff --git a/reactos/lib/netapi32/netapi32_misc.h b/reactos/lib/netapi32/netapi32_misc.h
new file mode 100644 (file)
index 0000000..92cbc26
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2002 Andriy Palamarchuk
+ *
+ * netapi32 internal functions.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, writ
+e to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __WINE_NETAPI32_MISC_H
+#define __WINE_NETAPI32_MISC_H
+
+extern BOOL NETAPI_IsLocalComputer(LPCWSTR ServerName);
+
+#define NETAPI_ForceLocalComputer(ServerName, FailureCode) \
+    if (!NETAPI_IsLocalComputer(ServerName)) \
+    { \
+        FIXME("Action Implemented for local computer only. " \
+              "Requested for server %s\n", debugstr_w(ServerName)); \
+        return FailureCode; \
+    }
+
+#endif
diff --git a/reactos/lib/netapi32/netbios.c b/reactos/lib/netapi32/netbios.c
new file mode 100644 (file)
index 0000000..71f503a
--- /dev/null
@@ -0,0 +1,857 @@
+/* Copyright (c) 2003 Juan Lang
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include "config.h"
+#include "wine/debug.h"
+#include "nbcmdqueue.h"
+#include "netbios.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(netbios);
+
+/* This file provides a NetBIOS emulator that implements the NetBIOS interface,
+ * including thread safety and asynchronous call support.  The protocol
+ * implementation is separate, with blocking (synchronous) functions.
+ */
+
+#define ADAPTERS_INCR 8
+#define DEFAULT_NUM_SESSIONS 16
+
+typedef struct _NetBIOSTransportTableEntry
+{
+    ULONG            id;
+    NetBIOSTransport transport;
+} NetBIOSTransportTableEntry;
+
+typedef struct _NetBIOSSession
+{
+    BOOL  inUse;
+    UCHAR state;
+    UCHAR local_name[NCBNAMSZ];
+    UCHAR remote_name[NCBNAMSZ];
+    void *data;
+} NetBIOSSession;
+
+/* This struct needs a little explanation, unfortunately.  enabled is only
+ * used by nbInternalEnum (see).  If transport_id is not 0 and transport
+ * is not NULL, the adapter is considered valid.  (transport is a pointer to
+ * an entry in a NetBIOSTransportTableEntry.)  data has data for the callers of
+ * NetBIOSEnumAdapters to be able to see.  The lana is repeated there, even
+ * though I don't use it internally--it's for transports to use reenabling
+ * adapters using NetBIOSEnableAdapter.
+ */
+typedef struct _NetBIOSAdapter
+{
+    BOOL               enabled;
+    BOOL               shuttingDown;
+    LONG               resetting;
+    ULONG              transport_id;
+    NetBIOSTransport  *transport;
+    NetBIOSAdapterImpl impl;
+    struct NBCmdQueue *cmdQueue;
+    CRITICAL_SECTION   cs;
+    DWORD              sessionsLen;
+    NetBIOSSession    *sessions;
+} NetBIOSAdapter;
+
+typedef struct _NetBIOSAdapterTable {
+    CRITICAL_SECTION cs;
+    BOOL             enumerated;
+    BOOL             enumerating;
+    UCHAR            tableSize;
+    NetBIOSAdapter  *table;
+} NetBIOSAdapterTable;
+
+/* Just enough space for NBT right now */
+static NetBIOSTransportTableEntry gTransports[1];
+static UCHAR gNumTransports = 0;
+static NetBIOSAdapterTable gNBTable;
+
+static UCHAR nbResizeAdapterTable(UCHAR newSize)
+{
+    UCHAR ret;
+
+    if (gNBTable.table)
+        gNBTable.table = HeapReAlloc(GetProcessHeap(),
+         HEAP_ZERO_MEMORY, gNBTable.table,
+         newSize * sizeof(NetBIOSAdapter));
+    else
+        gNBTable.table = HeapAlloc(GetProcessHeap(),
+         HEAP_ZERO_MEMORY, newSize * sizeof(NetBIOSAdapter));
+    if (gNBTable.table)
+    {
+        gNBTable.tableSize = newSize;
+        ret = NRC_GOODRET;
+    }
+    else
+        ret = NRC_OSRESNOTAV;
+    return ret;
+}
+
+void NetBIOSInit(void)
+{
+    memset(&gNBTable, 0, sizeof(gNBTable));
+    InitializeCriticalSection(&gNBTable.cs);
+}
+
+void NetBIOSShutdown(void)
+{
+    UCHAR i;
+
+    EnterCriticalSection(&gNBTable.cs);
+    for (i = 0; i < gNBTable.tableSize; i++)
+    {
+        if (gNBTable.table[i].transport &&
+         gNBTable.table[i].transport->cleanupAdapter)
+            gNBTable.table[i].transport->cleanupAdapter(
+             gNBTable.table[i].impl.data);
+    }
+    for (i = 0; i < gNumTransports; i++)
+        if (gTransports[i].transport.cleanup)
+            gTransports[i].transport.cleanup();
+    LeaveCriticalSection(&gNBTable.cs);
+    DeleteCriticalSection(&gNBTable.cs);
+    HeapFree(GetProcessHeap(), 0, gNBTable.table);
+}
+
+BOOL NetBIOSRegisterTransport(ULONG id, NetBIOSTransport *transport)
+{
+    BOOL ret;
+
+    TRACE(": transport 0x%08lx, p %p\n", id, transport);
+    if (!transport)
+        ret = FALSE;
+    else if (gNumTransports >= sizeof(gTransports) / sizeof(gTransports[0]))
+    {
+        FIXME("You tried to add %d transports, but I only have space for %d\n",
+         gNumTransports + 1, sizeof(gTransports) / sizeof(gTransports[0]));
+        ret = FALSE;
+    }
+    else
+    {
+        UCHAR i;
+
+        ret = FALSE;
+        for (i = 0; !ret && i < gNumTransports; i++)
+        {
+            if (gTransports[i].id == id)
+            {
+                WARN("Replacing NetBIOS transport ID %ld\n", id);
+                memcpy(&gTransports[i].transport, transport,
+                 sizeof(NetBIOSTransport));
+                ret = TRUE;
+            }
+        }
+        if (!ret)
+        {
+            gTransports[gNumTransports].id = id;
+            memcpy(&gTransports[gNumTransports].transport, transport,
+             sizeof(NetBIOSTransport));
+            gNumTransports++;
+            ret = TRUE;
+        }
+    }
+    TRACE("returning %d\n", ret);
+    return ret;
+}
+
+/* In this, I acquire the table lock to make sure no one else is modifying it.
+ * This is _probably_ overkill since it should only be called during the
+ * context of a NetBIOSEnum call, but just to be safe..
+ */
+BOOL NetBIOSRegisterAdapter(ULONG transport, DWORD ifIndex, void *data)
+{
+    BOOL ret;
+    UCHAR i;
+
+    TRACE(": transport 0x%08lx, ifIndex 0x%08lx, data %p\n", transport, ifIndex,
+     data);
+    for (i = 0; i < gNumTransports && gTransports[i].id != transport; i++)
+        ;
+    if (gTransports[i].id == transport)
+    {
+        NetBIOSTransport *transportPtr = &gTransports[i].transport;
+
+        TRACE(": found transport %p for id 0x%08lx\n", transportPtr, transport);
+
+        EnterCriticalSection(&gNBTable.cs);
+        ret = FALSE;
+        for (i = 0; i < gNBTable.tableSize &&
+         gNBTable.table[i].transport != 0; i++)
+            ;
+        if (i == gNBTable.tableSize && gNBTable.tableSize < MAX_LANA + 1)
+        {
+            UCHAR newSize;
+
+            if (gNBTable.tableSize < (MAX_LANA + 1) - ADAPTERS_INCR)
+                newSize = gNBTable.tableSize + ADAPTERS_INCR;
+            else
+                newSize = MAX_LANA + 1;
+            nbResizeAdapterTable(newSize);
+        }
+        if (i < gNBTable.tableSize && gNBTable.table[i].transport == 0)
+        {
+            TRACE(": registering as LANA %d\n", i);
+            gNBTable.table[i].transport_id = transport;
+            gNBTable.table[i].transport = transportPtr;
+            gNBTable.table[i].impl.lana = i;
+            gNBTable.table[i].impl.ifIndex = ifIndex;
+            gNBTable.table[i].impl.data = data;
+            gNBTable.table[i].cmdQueue = NBCmdQueueCreate(GetProcessHeap());
+            InitializeCriticalSection(&gNBTable.table[i].cs);
+            gNBTable.table[i].enabled = TRUE;
+            ret = TRUE;
+        }
+        LeaveCriticalSection(&gNBTable.cs);
+    }
+    else
+        ret = FALSE;
+    TRACE("returning %d\n", ret);
+    return ret;
+}
+
+/* In this, I acquire the table lock to make sure no one else is modifying it.
+ * This is _probably_ overkill since it should only be called during the
+ * context of a NetBIOSEnum call, but just to be safe..
+ */
+void NetBIOSEnableAdapter(UCHAR lana)
+{
+    TRACE(": %d\n", lana);
+    if (lana < gNBTable.tableSize)
+    {
+        EnterCriticalSection(&gNBTable.cs);
+        if (gNBTable.table[lana].transport != 0)
+            gNBTable.table[lana].enabled = TRUE;
+        LeaveCriticalSection(&gNBTable.cs);
+    }
+}
+
+static void nbShutdownAdapter(NetBIOSAdapter *adapter)
+{
+    if (adapter)
+    {
+        adapter->shuttingDown = TRUE;
+        NBCmdQueueCancelAll(adapter->cmdQueue);
+        if (adapter->transport->cleanupAdapter)
+            adapter->transport->cleanupAdapter(adapter->impl.data);
+        NBCmdQueueDestroy(adapter->cmdQueue);
+        DeleteCriticalSection(&adapter->cs);
+        memset(adapter, 0, sizeof(NetBIOSAdapter));
+    }
+}
+
+static void nbInternalEnum(void)
+{
+    UCHAR i;
+
+    EnterCriticalSection(&gNBTable.cs);
+    TRACE("before mark\n");
+    /* mark: */
+    for (i = 0; i < gNBTable.tableSize; i++)
+        if (gNBTable.table[i].enabled && gNBTable.table[i].transport != 0)
+            gNBTable.table[i].enabled = FALSE;
+
+    TRACE("marked, before store, %d transports\n", gNumTransports);
+    /* store adapters: */
+    for (i = 0; i < gNumTransports; i++)
+        if (gTransports[i].transport.enumerate)
+            gTransports[i].transport.enumerate();
+
+    TRACE("before sweep\n");
+    /* sweep: */
+    for (i = 0; i < gNBTable.tableSize; i++)
+        if (!gNBTable.table[i].enabled && gNBTable.table[i].transport != 0)
+            nbShutdownAdapter(&gNBTable.table[i]);
+    gNBTable.enumerated = TRUE;
+    LeaveCriticalSection(&gNBTable.cs);
+}
+
+UCHAR NetBIOSNumAdapters(void)
+{
+    UCHAR ret, i;
+
+    if (!gNBTable.enumerated)
+        nbInternalEnum();
+    for (i = 0, ret = 0; i < gNBTable.tableSize; i++)
+        if (gNBTable.table[i].transport != 0)
+            ret++;
+    return ret;
+}
+
+void NetBIOSEnumAdapters(ULONG transport, NetBIOSEnumAdaptersCallback cb,
+ void *closure)
+{
+    TRACE("transport 0x%08lx, callback %p, closure %p\n", transport, cb,
+     closure);
+    if (cb)
+    {
+        BOOL enumAll = memcmp(&transport, ALL_TRANSPORTS, sizeof(ULONG)) == 0;
+        UCHAR i, numLANAs = 0;
+
+        EnterCriticalSection(&gNBTable.cs);
+        if (!gNBTable.enumerating)
+        {
+            gNBTable.enumerating = TRUE;
+            nbInternalEnum();
+            gNBTable.enumerating = FALSE;
+        }
+        for (i = 0; i < gNBTable.tableSize; i++)
+            if (enumAll || gNBTable.table[i].transport_id == transport)
+                numLANAs++;
+        if (numLANAs > 0)
+        {
+            UCHAR lanaIndex = 0;
+
+            for (i = 0; i < gNBTable.tableSize; i++)
+                if (gNBTable.table[i].transport_id != 0 &&
+                 (enumAll || gNBTable.table[i].transport_id == transport))
+                    cb(numLANAs, lanaIndex++, gNBTable.table[i].transport_id,
+                     &gNBTable.table[i].impl, closure);
+        }
+        LeaveCriticalSection(&gNBTable.cs);
+    }
+}
+
+static NetBIOSAdapter *nbGetAdapter(UCHAR lana)
+{
+    NetBIOSAdapter *ret = NULL;
+
+    TRACE(": lana %d, num allocated adapters %d\n", lana, gNBTable.tableSize);
+    if (lana < gNBTable.tableSize && gNBTable.table[lana].transport_id != 0
+     && gNBTable.table[lana].transport)
+        ret = &gNBTable.table[lana];
+    TRACE("returning %p\n", ret);
+    return ret;
+}
+
+static UCHAR nbEnum(PNCB ncb)
+{
+    PLANA_ENUM lanas = (PLANA_ENUM)ncb->ncb_buffer;
+    UCHAR i, ret;
+
+    TRACE(": ncb %p\n", ncb);
+
+    if (!lanas)
+        ret = NRC_BUFLEN;
+    else if (ncb->ncb_length < sizeof(LANA_ENUM))
+        ret = NRC_BUFLEN;
+    else
+    {
+        nbInternalEnum();
+        lanas->length = 0;
+        for (i = 0; i < gNBTable.tableSize; i++)
+            if (gNBTable.table[i].transport)
+            {
+                lanas->length++;
+                lanas->lana[i] = i;
+            }
+        ret = NRC_GOODRET;
+    }
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+static UCHAR nbInternalHangup(NetBIOSAdapter *adapter, NetBIOSSession *session);
+
+static UCHAR nbCancel(NetBIOSAdapter *adapter, PNCB ncb)
+{
+    UCHAR ret;
+
+    TRACE(": adapter %p, ncb %p\n", adapter, ncb);
+
+    if (!adapter) return NRC_BRIDGE;
+    if (!ncb) return NRC_INVADDRESS;
+
+    switch (ncb->ncb_command & 0x7f)
+    {
+        case NCBCANCEL:
+        case NCBADDNAME:
+        case NCBADDGRNAME:
+        case NCBDELNAME:
+        case NCBRESET:
+        case NCBSSTAT:
+            ret = NRC_CANCEL;
+            break;
+
+        /* NCBCALL, NCBCHAINSEND/NCBSEND, NCBHANGUP all close the associated
+         * session if cancelled */
+        case NCBCALL:
+        case NCBSEND:
+        case NCBCHAINSEND:
+        case NCBSENDNA:
+        case NCBCHAINSENDNA:
+        case NCBHANGUP:
+        {
+            if (ncb->ncb_lsn >= adapter->sessionsLen)
+                ret = NRC_SNUMOUT;
+            else if (!adapter->sessions[ncb->ncb_lsn].inUse)
+                ret = NRC_SNUMOUT;
+            else
+            {
+                ret = NBCmdQueueCancel(adapter->cmdQueue, ncb);
+                if (ret == NRC_CMDCAN || ret == NRC_CANOCCR)
+                    nbInternalHangup(adapter, &adapter->sessions[ncb->ncb_lsn]);
+            }
+            break;
+        }
+
+        default:
+            ret = NBCmdQueueCancel(adapter->cmdQueue, ncb);
+    }
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+/* Resizes adapter to contain space for at least sessionsLen sessions.
+ * If allocating more space for sessions, sets the adapter's sessionsLen to
+ * sessionsLen.  If the adapter's sessionsLen was already at least sessionsLen,
+ * does nothing.  Does not modify existing sessions.  Assumes the adapter is
+ * locked.
+ * Returns NRC_GOODRET on success, and something else on failure.
+ */
+static UCHAR nbResizeAdapter(NetBIOSAdapter *adapter, UCHAR sessionsLen)
+{
+    UCHAR ret = NRC_GOODRET;
+
+    if (adapter && adapter->sessionsLen < sessionsLen)
+    {
+        NetBIOSSession *newSessions;
+
+        if (adapter->sessions)
+            newSessions = HeapReAlloc(GetProcessHeap(),
+             HEAP_ZERO_MEMORY, adapter->sessions, sessionsLen *
+             sizeof(NetBIOSSession));
+        else
+            newSessions = HeapAlloc(GetProcessHeap(),
+             HEAP_ZERO_MEMORY, sessionsLen * sizeof(NetBIOSSession));
+        if (newSessions)
+        {
+            adapter->sessions = newSessions;
+            adapter->sessionsLen = sessionsLen;
+        }
+        else
+            ret = NRC_OSRESNOTAV;
+    }
+    return ret;
+}
+
+static UCHAR nbReset(NetBIOSAdapter *adapter, PNCB ncb)
+{
+    UCHAR ret;
+
+    TRACE(": adapter %p, ncb %p\n", adapter, ncb);
+
+    if (!adapter) return NRC_BRIDGE;
+    if (!ncb) return NRC_INVADDRESS;
+
+    if (InterlockedIncrement(&adapter->resetting) == 1)
+    {
+        UCHAR i, resizeTo;
+
+        NBCmdQueueCancelAll(adapter->cmdQueue);
+
+        EnterCriticalSection(&adapter->cs);
+        for (i = 0; i < adapter->sessionsLen; i++)
+            if (adapter->sessions[i].inUse)
+                nbInternalHangup(adapter, &adapter->sessions[i]);
+        if (!ncb->ncb_lsn)
+            resizeTo = ncb->ncb_callname[0] == 0 ? DEFAULT_NUM_SESSIONS :
+             ncb->ncb_callname[0];
+        else if (adapter->sessionsLen == 0)
+            resizeTo = DEFAULT_NUM_SESSIONS;
+        else
+            resizeTo = 0;
+        if (resizeTo > 0)
+            ret = nbResizeAdapter(adapter, resizeTo);
+        else
+            ret = NRC_GOODRET;
+        LeaveCriticalSection(&adapter->cs);
+    }
+    else
+        ret = NRC_TOOMANY;
+    InterlockedDecrement(&adapter->resetting);
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+static UCHAR nbSStat(NetBIOSAdapter *adapter, PNCB ncb)
+{
+    UCHAR ret, i, spaceFor;
+    PSESSION_HEADER sstat;
+
+    TRACE(": adapter %p, NCB %p\n", adapter, ncb);
+
+    if (!adapter) return NRC_BADDR;
+    if (adapter->sessionsLen == 0) return NRC_ENVNOTDEF;
+    if (!ncb) return NRC_INVADDRESS;
+    if (!ncb->ncb_buffer) return NRC_BADDR;
+    if (ncb->ncb_length < sizeof(SESSION_HEADER)) return NRC_BUFLEN;
+
+    sstat = (PSESSION_HEADER)ncb->ncb_buffer;
+    ret = NRC_GOODRET;
+    memset(sstat, 0, sizeof(SESSION_HEADER));
+    spaceFor = (ncb->ncb_length - sizeof(SESSION_HEADER)) /
+     sizeof(SESSION_BUFFER);
+    EnterCriticalSection(&adapter->cs);
+    for (i = 0; ret == NRC_GOODRET && i < adapter->sessionsLen; i++)
+    {
+        if (adapter->sessions[i].inUse && (ncb->ncb_name[0] == '*' ||
+         !memcmp(ncb->ncb_name, adapter->sessions[i].local_name, NCBNAMSZ)))
+        {
+            if (sstat->num_sess < spaceFor)
+            {
+                PSESSION_BUFFER buf;
+               
+                buf = (PSESSION_BUFFER)((PUCHAR)sstat + sizeof(SESSION_HEADER)
+                 + sstat->num_sess * sizeof(SESSION_BUFFER));
+                buf->lsn = i;
+                buf->state = adapter->sessions[i].state;
+                memcpy(buf->local_name, adapter->sessions[i].local_name,
+                 NCBNAMSZ);
+                memcpy(buf->remote_name, adapter->sessions[i].remote_name,
+                 NCBNAMSZ);
+                buf->rcvs_outstanding = buf->sends_outstanding = 0;
+                sstat->num_sess++;
+            }
+            else
+                ret = NRC_BUFLEN;
+        }
+    }
+    LeaveCriticalSection(&adapter->cs);
+
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+static UCHAR nbCall(NetBIOSAdapter *adapter, PNCB ncb)
+{
+    UCHAR ret, i;
+
+    TRACE(": adapter %p, NCB %p\n", adapter, ncb);
+
+    if (!adapter) return NRC_BRIDGE;
+    if (adapter->sessionsLen == 0) return NRC_ENVNOTDEF;
+    if (!adapter->transport->call) return NRC_ILLCMD;
+    if (!ncb) return NRC_INVADDRESS;
+
+    EnterCriticalSection(&adapter->cs);
+    for (i = 0; i < adapter->sessionsLen && adapter->sessions[i].inUse; i++)
+        ;
+    if (i < adapter->sessionsLen)
+    {
+        adapter->sessions[i].inUse = TRUE;
+        adapter->sessions[i].state = CALL_PENDING;
+        memcpy(adapter->sessions[i].local_name, ncb->ncb_name, NCBNAMSZ);
+        memcpy(adapter->sessions[i].remote_name, ncb->ncb_callname, NCBNAMSZ);
+        ret = NRC_GOODRET;
+    }
+    else
+        ret = NRC_LOCTFUL;
+    LeaveCriticalSection(&adapter->cs);
+
+    if (ret == NRC_GOODRET)
+    {
+        ret = adapter->transport->call(adapter->impl.data, ncb,
+         &adapter->sessions[i].data);
+        if (ret == NRC_GOODRET)
+        {
+            ncb->ncb_lsn = i;
+            adapter->sessions[i].state = SESSION_ESTABLISHED;
+        }
+        else
+        {
+            adapter->sessions[i].inUse = FALSE;
+            adapter->sessions[i].state = 0;
+        }
+    }
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+static UCHAR nbSend(NetBIOSAdapter *adapter, PNCB ncb)
+{
+    UCHAR ret;
+    NetBIOSSession *session;
+
+    if (!adapter) return NRC_BRIDGE;
+    if (!adapter->transport->send) return NRC_ILLCMD;
+    if (!ncb) return NRC_INVADDRESS;
+    if (ncb->ncb_lsn >= adapter->sessionsLen) return NRC_SNUMOUT;
+    if (!adapter->sessions[ncb->ncb_lsn].inUse) return NRC_SNUMOUT;
+    if (!ncb->ncb_buffer) return NRC_BADDR;
+
+    session = &adapter->sessions[ncb->ncb_lsn];
+    if (session->state != SESSION_ESTABLISHED)
+        ret = NRC_SNUMOUT;
+    else
+        ret = adapter->transport->send(adapter->impl.data, session->data, ncb);
+    return ret;
+}
+
+static UCHAR nbRecv(NetBIOSAdapter *adapter, PNCB ncb)
+{
+    UCHAR ret;
+    NetBIOSSession *session;
+
+    if (!adapter) return NRC_BRIDGE;
+    if (!adapter->transport->recv) return NRC_ILLCMD;
+    if (!ncb) return NRC_INVADDRESS;
+    if (ncb->ncb_lsn >= adapter->sessionsLen) return NRC_SNUMOUT;
+    if (!adapter->sessions[ncb->ncb_lsn].inUse) return NRC_SNUMOUT;
+    if (!ncb->ncb_buffer) return NRC_BADDR;
+
+    session = &adapter->sessions[ncb->ncb_lsn];
+    if (session->state != SESSION_ESTABLISHED)
+        ret = NRC_SNUMOUT;
+    else
+        ret = adapter->transport->recv(adapter->impl.data, session->data, ncb);
+    return ret;
+}
+
+static UCHAR nbInternalHangup(NetBIOSAdapter *adapter, NetBIOSSession *session)
+{
+    UCHAR ret;
+
+    if (!adapter) return NRC_BRIDGE;
+    if (!session) return NRC_SNUMOUT;
+
+    if (adapter->transport->hangup)
+        ret = adapter->transport->hangup(adapter->impl.data, session->data);
+    else
+        ret = NRC_ILLCMD;
+    EnterCriticalSection(&adapter->cs);
+    memset(session, 0, sizeof(NetBIOSSession));
+    LeaveCriticalSection(&adapter->cs);
+    return NRC_GOODRET;
+}
+
+static UCHAR nbHangup(NetBIOSAdapter *adapter, PNCB ncb)
+{
+    UCHAR ret;
+    NetBIOSSession *session;
+
+    if (!adapter) return NRC_BRIDGE;
+    if (!ncb) return NRC_INVADDRESS;
+    if (ncb->ncb_lsn >= adapter->sessionsLen) return NRC_SNUMOUT;
+    if (!adapter->sessions[ncb->ncb_lsn].inUse) return NRC_SNUMOUT;
+
+    session = &adapter->sessions[ncb->ncb_lsn];
+    if (session->state != SESSION_ESTABLISHED)
+        ret = NRC_SNUMOUT;
+    else
+    {
+        session->state = HANGUP_PENDING;
+        ret = nbInternalHangup(adapter, session);
+    }
+    return ret;
+}
+
+void NetBIOSHangupSession(PNCB ncb)
+{
+    NetBIOSAdapter *adapter;
+
+    if (!ncb) return;
+
+    adapter = nbGetAdapter(ncb->ncb_lana_num);
+    if (adapter)
+    {
+        if (ncb->ncb_lsn < adapter->sessionsLen &&
+         adapter->sessions[ncb->ncb_lsn].inUse)
+            nbHangup(adapter, ncb);
+    }
+}
+
+static UCHAR nbAStat(NetBIOSAdapter *adapter, PNCB ncb)
+{
+    UCHAR ret;
+
+    if (!adapter) return NRC_BRIDGE;
+    if (!adapter->transport->astat) return NRC_ILLCMD;
+    if (!ncb) return NRC_INVADDRESS;
+    if (!ncb->ncb_buffer) return NRC_BADDR;
+    if (ncb->ncb_length < sizeof(ADAPTER_STATUS)) return NRC_BUFLEN;
+
+    ret = adapter->transport->astat(adapter->impl.data, ncb);
+    if (ncb->ncb_callname[0] == '*')
+    {
+        PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer;
+
+        astat->max_sess = astat->max_cfg_sess = adapter->sessionsLen;
+    }
+    return ret;
+}
+
+static UCHAR nbDispatch(NetBIOSAdapter *adapter, PNCB ncb)
+{
+    UCHAR ret, cmd;
+
+    TRACE(": adapter %p, ncb %p\n", adapter, ncb);
+
+    if (!adapter) return NRC_BRIDGE;
+    if (!ncb) return NRC_INVADDRESS;
+
+    cmd = ncb->ncb_command & 0x7f;
+    if (cmd == NCBRESET)
+        ret = nbReset(adapter, ncb);
+    else
+    {
+        ret = NBCmdQueueAdd(adapter->cmdQueue, ncb);
+        if (ret == NRC_GOODRET)
+        {
+            switch (cmd)
+            {
+                case NCBCALL:
+                    ret = nbCall(adapter, ncb);
+                    break;
+
+                /* WinNT doesn't chain sends, it always sends immediately.
+                 * Doubt there's any real significance to the NA variants.
+                 */
+                case NCBSEND:
+                case NCBSENDNA:
+                case NCBCHAINSEND:
+                case NCBCHAINSENDNA:
+                    ret = nbSend(adapter, ncb);
+                    break;
+
+                case NCBRECV:
+                    ret = nbRecv(adapter, ncb);
+                    break;
+
+                case NCBHANGUP:
+                    ret = nbHangup(adapter, ncb);
+                    break;
+
+                case NCBASTAT:
+                    ret = nbAStat(adapter, ncb);
+                    break;
+
+                case NCBFINDNAME:
+                    if (adapter->transport->findName)
+                        ret = adapter->transport->findName(adapter->impl.data,
+                         ncb);
+                    else
+                        ret = NRC_ILLCMD;
+                    break;
+
+                default:
+                    FIXME("(%p): command code 0x%02x\n", ncb, ncb->ncb_command);
+                    ret = NRC_ILLCMD;
+            }
+            NBCmdQueueComplete(adapter->cmdQueue, ncb, ret);
+        }
+    }
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+static DWORD WINAPI nbCmdThread(LPVOID lpVoid)
+{
+    PNCB ncb = (PNCB)lpVoid;
+
+    if (ncb)
+    {
+        UCHAR ret;
+        NetBIOSAdapter *adapter = nbGetAdapter(ncb->ncb_lana_num);
+
+        if (adapter)
+            ret = nbDispatch(adapter, ncb);
+        else
+            ret = NRC_BRIDGE;
+        ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret;
+        if (ncb->ncb_post)
+            ncb->ncb_post(ncb);
+        else if (ncb->ncb_event)
+            SetEvent(ncb->ncb_event);
+    }
+    return 0;
+}
+
+UCHAR WINAPI Netbios(PNCB ncb)
+{
+    UCHAR ret, cmd;
+
+    TRACE("ncb = %p\n", ncb);
+
+    if (!ncb) return NRC_INVADDRESS;
+
+    TRACE("ncb_command 0x%02x, ncb_lana_num %d, ncb_buffer %p, ncb_length %d\n",
+     ncb->ncb_command, ncb->ncb_lana_num, ncb->ncb_buffer, ncb->ncb_length);
+    cmd = ncb->ncb_command & 0x7f;
+
+    if (cmd == NCBENUM)
+        ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret = nbEnum(ncb);
+    else if (cmd == NCBADDNAME)
+    {
+        FIXME("NCBADDNAME: stub, returning success");
+        ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret = NRC_GOODRET;
+    }
+    else
+    {
+        NetBIOSAdapter *adapter;
+
+        /* Apps not specifically written for WinNT won't do an NCBENUM first,
+         * so make sure the table has been enumerated at least once
+         */
+        if (!gNBTable.enumerated)
+            nbInternalEnum();
+        adapter = nbGetAdapter(ncb->ncb_lana_num);
+        if (!adapter)
+            ret = NRC_BRIDGE;
+        else
+        {
+            if (adapter->shuttingDown)
+                ret = NRC_IFBUSY;
+            else if (adapter->resetting)
+                ret = NRC_TOOMANY;
+            else
+            {
+                /* non-asynch commands first */
+                if (cmd == NCBCANCEL)
+                    ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret =
+                     nbCancel(adapter, ncb);
+                else if (cmd == NCBSSTAT)
+                    ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret =
+                     nbSStat(adapter, ncb);
+                else
+                {
+                    if (ncb->ncb_command & ASYNCH)
+                    {
+                        HANDLE thread = CreateThread(NULL, 0, nbCmdThread, ncb,
+                         CREATE_SUSPENDED, NULL);
+
+                        if (thread != NULL)
+                        {
+                            ncb->ncb_retcode = ncb->ncb_cmd_cplt = NRC_PENDING;
+                            if (ncb->ncb_event)
+                                ResetEvent(ncb->ncb_event);
+                            ResumeThread(thread);
+                            CloseHandle(thread);
+                            ret = NRC_GOODRET;
+                        }
+                        else
+                        ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret =
+                         NRC_OSRESNOTAV;
+                    }
+                    else
+                        ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret =
+                         nbDispatch(adapter, ncb);
+                }
+            }
+        }
+    }
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
diff --git a/reactos/lib/netapi32/netbios.h b/reactos/lib/netapi32/netbios.h
new file mode 100644 (file)
index 0000000..0130aaf
--- /dev/null
@@ -0,0 +1,183 @@
+/* Copyright (c) 2003 Juan Lang
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __WINE_NETBIOS_H__
+#define __WINE_NETBIOS_H__
+
+#include <stdarg.h>
+#include "windef.h"
+#include "winbase.h"
+#include "lm.h"
+#include "nb30.h"
+
+/* This file describes the interface WINE's NetBIOS implementation uses to
+ * interact with a transport implementation (where a transport might be
+ * NetBIOS-over-TCP/IP (aka NetBT, NBT), NetBIOS-over-IPX, etc.)
+ */
+
+/**
+ * Public functions
+ */
+
+void NetBIOSInit(void);
+void NetBIOSShutdown(void);
+
+struct _NetBIOSTransport;
+
+/* A transport should register itself during its init function (see below) with
+ * a unique id (the transport_id of ACTION_HEADER, for example) and an
+ * implementation.  Returns TRUE on success, and FALSE on failure.
+ */
+BOOL NetBIOSRegisterTransport(ULONG id, struct _NetBIOSTransport *transport);
+
+/* Registers an adapter with the given transport and ifIndex with NetBIOS.
+ * ifIndex is an interface index usable by the IpHlpApi.  ifIndex is not
+ * required to be unique, but is required so that NetWkstaTransportEnum can use
+ * GetIfEntry to get the name and hardware address of the adapter.
+ * Returns TRUE on success, FALSE on failure.
+ * FIXME: need functions for retrieving the name and hardware index, rather
+ * than assuming a correlation with IpHlpApi.
+ */
+BOOL NetBIOSRegisterAdapter(ULONG transport, DWORD ifIndex, void *adapter);
+
+/* During enumeration, all adapters from your transport are disabled
+ * internally.  If an adapter is still valid, reenable it with this function.
+ * Adapters you don't enable will have their transport's NetBIOSCleanupAdapter
+ * function (see below) called on them, and will be removed from the table.
+ * (This is to deal with lack of plug-and-play--sorry.)
+ */
+void NetBIOSEnableAdapter(UCHAR lana);
+
+/* Gets a quick count of the number of NetBIOS adapters.  Not guaranteed not
+ * to change from one call to the next, depending on what's been enumerated
+ * lately.  See also NetBIOSEnumAdapters.
+ */
+UCHAR NetBIOSNumAdapters(void);
+
+typedef struct _NetBIOSAdapterImpl {
+    UCHAR lana;
+    DWORD ifIndex;
+    void *data;
+} NetBIOSAdapterImpl;
+
+typedef BOOL (*NetBIOSEnumAdaptersCallback)(UCHAR totalLANAs, UCHAR lanaIndex,
+ ULONG transport, const NetBIOSAdapterImpl *data, void *closure);
+
+/* Enumerates all NetBIOS adapters for the transport transport, or for all
+ * transports if transport is ALL_TRANSPORTS.  Your callback will be called
+ * once for every enumerated adapter, with a count of how many adapters have
+ * been enumerated, a 0-based index relative to that count, the adapter's
+ * transport, and its ifIndex.
+ * Your callback should return FALSE if it no longer wishes to be called.
+ */
+void NetBIOSEnumAdapters(ULONG transport, NetBIOSEnumAdaptersCallback cb,
+ void *closure);
+
+/* Hangs up the session identified in the NCB; the NCB need not be a NCBHANGUP.
+ * Will result in the transport's hangup function being called, so release any
+ * locks you own before calling to avoid deadlock.
+ * This function is intended for use by a transport, if the session is closed
+ * by some error in the transport layer.
+ */
+void NetBIOSHangupSession(PNCB ncb);
+
+/**
+ * Functions a transport implementation must implement
+ */
+
+/* This function is called to ask a transport implementation to enumerate any
+ * LANAs into the NetBIOS adapter table by:
+ * - calling NetBIOSRegisterAdapter for any new adapters
+ * - calling NetBIOSEnableAdapter for any existing adapters
+ * NetBIOSEnumAdapters (see) may be of use to determine which adapters already
+ * exist.
+ * A transport can assume no other thread is modifying the NetBIOS adapter
+ * table during the lifetime of its NetBIOSEnum function (and, therefore, that
+ * this function won't be called reentrantly).
+ */
+typedef UCHAR (*NetBIOSEnum)(void);
+
+/* A cleanup function for a transport.  This is the last function called on a
+ * transport.
+ */
+typedef void (*NetBIOSCleanup)(void);
+
+/* Adapter functions */
+
+/* Functions with direct mappings to the Netbios interface.  These functions
+ * are expected to be synchronous, although the first four bytes of the
+ * reserved member of the ncb are a cancel flag.  A long-running function
+ * should check whether this is not FALSE from time to time (see the
+ * NCB_CANCELLED macro), and return NRC_CMDCAN if it's been cancelled.  (The
+ * remainder of the NCB's reserved field is, well, reserved.)
+ */
+
+/* Used to see whether the pointer to an NCB has been cancelled.  The NetBIOS
+ * interface designates certain functions as non-cancellable functions, but I
+ * use this flag for all NCBs.  Support it if you can.
+ * FIXME: this isn't enough, need to support an EVENT or some such, because
+ * some calls (recv) will block indefinitely, so a reset, shutdown, etc. will
+ * never occur.
+ */
+#define NCB_CANCELLED(pncb) *(PBOOL)((pncb)->ncb_reserve)
+
+typedef UCHAR (*NetBIOSAstat)(void *adapter, PNCB ncb);
+typedef UCHAR (*NetBIOSFindName)(void *adapter, PNCB ncb);
+
+/* Functions to support the session service */
+
+/* Implement to support the NCBCALL command.  If you need data stored for the
+ * session, return it in *session.  You can clean it up in your NetBIOSHangup
+ * function (see).
+ */
+typedef UCHAR (*NetBIOSCall)(void *adapter, PNCB ncb, void **session);
+typedef UCHAR (*NetBIOSSend)(void *adapter, void *session, PNCB ncb);
+typedef UCHAR (*NetBIOSRecv)(void *adapter, void *session, PNCB ncb);
+typedef UCHAR (*NetBIOSHangup)(void *adapter, void *session);
+
+/* The last function called on an adapter; it is not called reentrantly, and
+ * no new calls will be made on the adapter once this has been entered.  Clean
+ * up any resources allocated for the adapter here.
+ */
+typedef void (*NetBIOSCleanupAdapter)(void *adapter);
+
+typedef struct _NetBIOSTransport
+{
+    NetBIOSEnum           enumerate;
+    NetBIOSAstat          astat;
+    NetBIOSFindName       findName;
+    NetBIOSCall           call;
+    NetBIOSSend           send;
+    NetBIOSRecv           recv;
+    NetBIOSHangup         hangup;
+    NetBIOSCleanupAdapter cleanupAdapter;
+    NetBIOSCleanup        cleanup;
+} NetBIOSTransport;
+
+/* Transport-specific functions.  When adding a transport, add a call to its
+ * init function in netapi32's DllMain.  The transport can do any global
+ * initialization it needs here.  It should call NetBIOSRegisterTransport to
+ * register itself with NetBIOS.
+ */
+
+/* NetBIOS-over-TCP/IP (NetBT) functions */
+
+/* Not defined by MS, so make my own private define: */
+#define TRANSPORT_NBT "MNBT"
+
+void NetBTInit(void);
+
+#endif /* ndef __WINE_NETBIOS_H__ */
diff --git a/reactos/lib/netapi32/wksta.c b/reactos/lib/netapi32/wksta.c
new file mode 100644 (file)
index 0000000..6ba6665
--- /dev/null
@@ -0,0 +1,567 @@
+/* Copyright 2002 Andriy Palamarchuk
+ * Copyright (c) 2003 Juan Lang
+ *
+ * netapi32 user functions
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "config.h"
+#include "wine/port.h"
+
+#include <stdarg.h>
+#include <stdlib.h>
+#define WIN32_NO_STATUS
+#include "windef.h"
+#include "winbase.h"
+#include "winsock2.h"
+#include "nb30.h"
+#include "lmcons.h"
+#include "lmapibuf.h"
+#include "lmerr.h"
+#include "lmwksta.h"
+#include "iphlpapi.h"
+#include "winerror.h"
+#undef WIN32_NO_STATUS
+#include "ntstatus.h"
+#include "winreg.h"
+#include "winternl.h"
+#include "ntsecapi.h"
+#include "netbios.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(netapi32);
+
+/************************************************************
+ *                NETAPI_IsLocalComputer
+ *
+ * Checks whether the server name indicates local machine.
+ */
+BOOL NETAPI_IsLocalComputer(LPCWSTR ServerName)
+{
+    if (!ServerName)
+    {
+        return TRUE;
+    }
+    else if (ServerName[0] == '\0')
+        return TRUE;
+    else
+    {
+        DWORD dwSize = MAX_COMPUTERNAME_LENGTH + 1;
+        BOOL Result;
+        LPWSTR buf;
+
+        NetApiBufferAllocate(dwSize * sizeof(WCHAR), (LPVOID *) &buf);
+        Result = GetComputerNameW(buf,  &dwSize);
+        if (Result && (ServerName[0] == '\\') && (ServerName[1] == '\\'))
+            ServerName += 2;
+        Result = Result && !lstrcmpW(ServerName, buf);
+        NetApiBufferFree(buf);
+
+        return Result;
+    }
+}
+
+static void wprint_mac(WCHAR* buffer, int len, PMIB_IFROW ifRow)
+{
+    int i;
+    unsigned char val;
+
+    if (!buffer)
+        return;
+    if (len < 1)
+        return;
+    if (!ifRow)
+    {
+        *buffer = '\0';
+        return;
+    }
+
+    for (i = 0; i < ifRow->dwPhysAddrLen && 2 * i < len; i++)
+    {
+        val = ifRow->bPhysAddr[i];
+        if ((val >>4) >9)
+            buffer[2*i] = (WCHAR)((val >>4) + 'A' - 10);
+        else
+            buffer[2*i] = (WCHAR)((val >>4) + '0');
+        if ((val & 0xf ) >9)
+            buffer[2*i+1] = (WCHAR)((val & 0xf) + 'A' - 10);
+        else
+            buffer[2*i+1] = (WCHAR)((val & 0xf) + '0');
+    }
+    buffer[2*i]=(WCHAR)0;
+}
+
+/* Theoretically this could be too short, except that MS defines
+ * MAX_ADAPTER_NAME as 128, and MAX_INTERFACE_NAME_LEN as 256, and both
+ * represent a count of WCHARs, so even with an extroardinarily long header
+ * this will be plenty
+ */
+#define MAX_TRANSPORT_NAME MAX_INTERFACE_NAME_LEN
+#define MAX_TRANSPORT_ADDR 13
+
+#define NBT_TRANSPORT_NAME_HEADER "\\Device\\NetBT_Tcpip_"
+#define UNKNOWN_TRANSPORT_NAME_HEADER "\\Device\\UnknownTransport_"
+
+static void wprint_name(WCHAR *buffer, int len, ULONG transport,
+ PMIB_IFROW ifRow)
+{
+    WCHAR *ptr1, *ptr2;
+    const char *name;
+
+    if (!buffer)
+        return;
+    if (!ifRow)
+    {
+        *buffer = '\0';
+        return;
+    }
+
+    if (!memcmp(&transport, TRANSPORT_NBT, sizeof(ULONG)))
+        name = NBT_TRANSPORT_NAME_HEADER;
+    else
+        name = UNKNOWN_TRANSPORT_NAME_HEADER;
+
+    for (ptr1 = buffer; *name && ptr1 < buffer + len; ptr1++, name++)
+        *ptr1 = *name;
+    for (ptr2 = ifRow->wszName; *ptr2 && ptr1 < buffer + len; ptr1++, ptr2++)
+        *ptr1 = *ptr2;
+    *ptr1 = '\0';
+}
+
+/***********************************************************************
+ *                NetWkstaTransportEnum  (NETAPI32.@)
+ */
+struct WkstaTransportEnumData
+{
+    UCHAR          n_adapt;
+    UCHAR          n_read;
+    DWORD          prefmaxlen;
+    LPBYTE        *pbuf;
+    NET_API_STATUS ret;
+};
+
+/**********************************************************************/
+
+static BOOL WkstaEnumAdaptersCallback(UCHAR totalLANAs, UCHAR lanaIndex,
+ ULONG transport, const NetBIOSAdapterImpl *data, void *closure)
+{
+    BOOL ret;
+    struct WkstaTransportEnumData *enumData = (struct WkstaTransportEnumData *)
+     closure;
+
+    if (enumData && enumData->pbuf)
+    {
+        if (lanaIndex == 0)
+        {
+            DWORD toAllocate;
+
+            enumData->n_adapt = totalLANAs;
+            enumData->n_read = 0;
+
+            toAllocate = totalLANAs * (sizeof(WKSTA_TRANSPORT_INFO_0)
+             + MAX_TRANSPORT_NAME * sizeof(WCHAR) +
+             MAX_TRANSPORT_ADDR * sizeof(WCHAR));
+            if (enumData->prefmaxlen != MAX_PREFERRED_LENGTH)
+                toAllocate = enumData->prefmaxlen;
+            NetApiBufferAllocate(toAllocate, (LPVOID *)enumData->pbuf);
+        }
+        if (*(enumData->pbuf))
+        {
+            UCHAR spaceFor;
+
+            if (enumData->prefmaxlen == MAX_PREFERRED_LENGTH)
+                spaceFor = totalLANAs;
+            else
+                spaceFor = enumData->prefmaxlen /
+                 (sizeof(WKSTA_TRANSPORT_INFO_0) + (MAX_TRANSPORT_NAME +
+                 MAX_TRANSPORT_ADDR) * sizeof(WCHAR));
+            if (enumData->n_read < spaceFor)
+            {
+                PWKSTA_TRANSPORT_INFO_0 ti;
+                LPWSTR transport_name, transport_addr;
+                MIB_IFROW ifRow;
+
+                ti = (PWKSTA_TRANSPORT_INFO_0)(*(enumData->pbuf) +
+                 enumData->n_read * sizeof(WKSTA_TRANSPORT_INFO_0));
+                transport_name = (LPWSTR)(*(enumData->pbuf) +
+                 totalLANAs * sizeof(WKSTA_TRANSPORT_INFO_0) +
+                 enumData->n_read * MAX_TRANSPORT_NAME * sizeof(WCHAR));
+                transport_addr = (LPWSTR)(*(enumData->pbuf) +
+                 totalLANAs * (sizeof(WKSTA_TRANSPORT_INFO_0) +
+                 MAX_TRANSPORT_NAME * sizeof(WCHAR)) +
+                 enumData->n_read * MAX_TRANSPORT_ADDR * sizeof(WCHAR));
+
+                ifRow.dwIndex = data->ifIndex;
+                GetIfEntry(&ifRow);
+                ti->wkti0_quality_of_service = 0;
+                ti->wkti0_number_of_vcs = 0;
+                ti->wkti0_transport_name = transport_name;
+                wprint_name(ti->wkti0_transport_name, MAX_TRANSPORT_NAME,
+                 transport, &ifRow);
+                ti->wkti0_transport_address = transport_addr;
+                wprint_mac(ti->wkti0_transport_address, MAX_TRANSPORT_ADDR,
+                 &ifRow);
+                if (!memcmp(&transport, TRANSPORT_NBT, sizeof(ULONG)))
+                    ti->wkti0_wan_ish = TRUE;
+                else
+                    ti->wkti0_wan_ish = FALSE;
+                TRACE("%d of %d:ti at %p\n", lanaIndex, totalLANAs, ti);
+                TRACE("transport_name at %p %s\n",
+                 ti->wkti0_transport_name,
+                 debugstr_w(ti->wkti0_transport_name));
+                TRACE("transport_address at %p %s\n",
+                 ti->wkti0_transport_address,
+                 debugstr_w(ti->wkti0_transport_address));
+                enumData->n_read++;
+                enumData->ret = NERR_Success;
+                ret = TRUE;
+            }
+            else
+            {
+                enumData->ret = ERROR_MORE_DATA;
+                ret = FALSE;
+            }
+        }
+        else
+        {
+            enumData->ret = ERROR_OUTOFMEMORY;
+            ret = FALSE;
+        }
+    }
+    else
+        ret = FALSE;
+    return ret;
+}
+
+/**********************************************************************/
+
+NET_API_STATUS WINAPI 
+NetWkstaTransportEnum(LPWSTR ServerName, DWORD level, PBYTE* pbuf,
+      DWORD prefmaxlen, LPDWORD read_entries,
+      PDWORD total_entries, PDWORD hresume)
+{
+    NET_API_STATUS ret;
+
+    TRACE(":%s, 0x%08lx, %p, 0x%08lx, %p, %p, %p\n", debugstr_w(ServerName), 
+     level, pbuf, prefmaxlen, read_entries, total_entries,hresume);
+    if (!NETAPI_IsLocalComputer(ServerName))
+    {
+        FIXME(":not implemented for non-local computers\n");
+        ret = ERROR_INVALID_LEVEL;
+    }
+    else
+    {
+        if (hresume && *hresume)
+        {
+          FIXME(":resume handle not implemented\n");
+          return ERROR_INVALID_LEVEL;
+        }
+
+        switch (level)
+        {
+            case 0: /* transport info */
+            {
+                ULONG allTransports;
+                struct WkstaTransportEnumData enumData;
+
+                if (NetBIOSNumAdapters() == 0)
+                  return ERROR_NETWORK_UNREACHABLE;
+                if (!read_entries)
+                  return STATUS_ACCESS_VIOLATION;
+                if (!total_entries || !pbuf)
+                  return RPC_X_NULL_REF_POINTER;
+
+                enumData.prefmaxlen = prefmaxlen;
+                enumData.pbuf = pbuf;
+                memcpy(&allTransports, ALL_TRANSPORTS, sizeof(ULONG));
+                NetBIOSEnumAdapters(allTransports, WkstaEnumAdaptersCallback,
+                 &enumData);
+                *read_entries = enumData.n_read;
+                *total_entries = enumData.n_adapt;
+                if (hresume) *hresume= 0;
+                ret = enumData.ret;
+                break;
+            }
+            default:
+                ERR("Invalid level %ld is specified\n", level);
+                ret = ERROR_INVALID_LEVEL;
+        }
+    }
+    return ret;
+}
+
+
+/************************************************************
+ *                NetWkstaUserGetInfo  (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetWkstaUserGetInfo(LPWSTR reserved, DWORD level,
+                                          PBYTE* bufptr)
+{
+    TRACE("(%s, %ld, %p)\n", debugstr_w(reserved), level, bufptr);
+    switch (level)
+    {
+    case 0:
+    {
+        PWKSTA_USER_INFO_0 ui;
+        DWORD dwSize = UNLEN + 1;
+
+        /* set up buffer */
+        NetApiBufferAllocate(sizeof(WKSTA_USER_INFO_0) + dwSize * sizeof(WCHAR),
+                             (LPVOID *) bufptr);
+
+        ui = (PWKSTA_USER_INFO_0) *bufptr;
+        ui->wkui0_username = (LPWSTR) (*bufptr + sizeof(WKSTA_USER_INFO_0));
+
+        /* get data */
+        if (!GetUserNameW(ui->wkui0_username, &dwSize))
+        {
+            NetApiBufferFree(ui);
+            return ERROR_NOT_ENOUGH_MEMORY;
+        }
+        else
+            NetApiBufferReallocate(
+                *bufptr, sizeof(WKSTA_USER_INFO_0) +
+                (lstrlenW(ui->wkui0_username) + 1) * sizeof(WCHAR),
+                (LPVOID *) bufptr);
+        break;
+    }
+
+    case 1:
+    {
+        PWKSTA_USER_INFO_1 ui;
+        PWKSTA_USER_INFO_0 ui0;
+        DWORD dwSize;
+        LSA_OBJECT_ATTRIBUTES ObjectAttributes;
+        LSA_HANDLE PolicyHandle;
+        PPOLICY_ACCOUNT_DOMAIN_INFO DomainInfo;
+        NTSTATUS NtStatus;
+
+        /* sizes of the field buffers in WCHARS */
+        int username_sz, logon_domain_sz, oth_domains_sz, logon_server_sz;
+
+        FIXME("Level 1 processing is partially implemented\n");
+        oth_domains_sz = 1;
+        logon_server_sz = 1;
+
+        /* get some information first to estimate size of the buffer */
+        ui0 = NULL;
+        NetWkstaUserGetInfo(NULL, 0, (PBYTE *) &ui0);
+        username_sz = lstrlenW(ui0->wkui0_username) + 1;
+
+        ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));
+        NtStatus = LsaOpenPolicy(NULL, &ObjectAttributes,
+                                 POLICY_VIEW_LOCAL_INFORMATION,
+                                 &PolicyHandle);
+        if (NtStatus != STATUS_SUCCESS)
+        {
+            ERR("LsaOpenPolicyFailed with NT status %lx\n",
+                LsaNtStatusToWinError(NtStatus));
+            NetApiBufferFree(ui0);
+            return ERROR_NOT_ENOUGH_MEMORY;
+        }
+        LsaQueryInformationPolicy(PolicyHandle, PolicyAccountDomainInformation,
+                                  (PVOID*) &DomainInfo);
+        logon_domain_sz = lstrlenW(DomainInfo->DomainName.Buffer) + 1;
+        LsaClose(PolicyHandle);
+
+        /* set up buffer */
+        NetApiBufferAllocate(sizeof(WKSTA_USER_INFO_1) +
+                             (username_sz + logon_domain_sz +
+                              oth_domains_sz + logon_server_sz) * sizeof(WCHAR),
+                             (LPVOID *) bufptr);
+        ui = (WKSTA_USER_INFO_1 *) *bufptr;
+        ui->wkui1_username = (LPWSTR) (*bufptr + sizeof(WKSTA_USER_INFO_1));
+        ui->wkui1_logon_domain = (LPWSTR) (
+            ((PBYTE) ui->wkui1_username) + username_sz * sizeof(WCHAR));
+        ui->wkui1_oth_domains = (LPWSTR) (
+            ((PBYTE) ui->wkui1_logon_domain) +
+            logon_domain_sz * sizeof(WCHAR));
+        ui->wkui1_logon_server = (LPWSTR) (
+            ((PBYTE) ui->wkui1_oth_domains) +
+            oth_domains_sz * sizeof(WCHAR));
+
+        /* get data */
+        dwSize = username_sz;
+        lstrcpyW(ui->wkui1_username, ui0->wkui0_username);
+        NetApiBufferFree(ui0);
+
+        lstrcpynW(ui->wkui1_logon_domain, DomainInfo->DomainName.Buffer,
+                logon_domain_sz);
+        LsaFreeMemory(DomainInfo);
+
+        /* FIXME. Not implemented. Populated with empty strings */
+        ui->wkui1_oth_domains[0] = 0;
+        ui->wkui1_logon_server[0] = 0;
+        break;
+    }
+    case 1101:
+    {
+        PWKSTA_USER_INFO_1101 ui;
+        DWORD dwSize = 1;
+
+        FIXME("Stub. Level 1101 processing is not implemented\n");
+        /* FIXME see also wkui1_oth_domains for level 1 */
+
+        /* set up buffer */
+        NetApiBufferAllocate(sizeof(WKSTA_USER_INFO_1101) + dwSize * sizeof(WCHAR),
+                             (LPVOID *) bufptr);
+
+        ui = (PWKSTA_USER_INFO_1101) *bufptr;
+        ui->wkui1101_oth_domains = (LPWSTR)(ui + 1);
+
+        /* get data */
+        ui->wkui1101_oth_domains[0] = 0;
+        break;
+    }
+    default:
+        ERR("Invalid level %ld is specified\n", level);
+        return ERROR_INVALID_LEVEL;
+    }
+    return NERR_Success;
+}
+
+/************************************************************
+ *                NetpGetComputerName  (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetpGetComputerName(LPWSTR *Buffer)
+{
+    DWORD dwSize = MAX_COMPUTERNAME_LENGTH + 1;
+
+    TRACE("(%p)\n", Buffer);
+    NetApiBufferAllocate(dwSize * sizeof(WCHAR), (LPVOID *) Buffer);
+    if (GetComputerNameW(*Buffer,  &dwSize))
+    {
+        NetApiBufferReallocate(
+            *Buffer, dwSize * sizeof(WCHAR),
+            (LPVOID *) Buffer);
+        return NERR_Success;
+    }
+    else
+    {
+        NetApiBufferFree(*Buffer);
+        return ERROR_NOT_ENOUGH_MEMORY;
+    }
+}
+
+NET_API_STATUS WINAPI I_NetNameCompare(LPVOID p1, LPWSTR wkgrp, LPWSTR comp,
+ LPVOID p4, LPVOID p5)
+{
+    FIXME("(%p %s %s %p %p): stub\n", p1, debugstr_w(wkgrp), debugstr_w(comp),
+     p4, p5);
+    return ERROR_INVALID_PARAMETER;
+}
+
+NET_API_STATUS WINAPI I_NetNameValidate(LPVOID p1, LPWSTR wkgrp, LPVOID p3,
+ LPVOID p4)
+{
+    FIXME("(%p %s %p %p): stub\n", p1, debugstr_w(wkgrp), p3, p4);
+    return ERROR_INVALID_PARAMETER;
+}
+
+NET_API_STATUS WINAPI NetWkstaGetInfo( LPWSTR servername, DWORD level,
+                                       LPBYTE* bufptr)
+{
+    NET_API_STATUS ret;
+
+    TRACE("%s %ld %p\n", debugstr_w( servername ), level, bufptr );
+    if (servername)
+    {
+        if (!NETAPI_IsLocalComputer(servername))
+        {
+            FIXME("remote computers not supported\n");
+            return ERROR_INVALID_LEVEL;
+        }
+    }
+    if (!bufptr) return ERROR_INVALID_PARAMETER;
+
+    switch (level)
+    {
+        case 100:
+        {
+            DWORD computerNameLen, domainNameLen, size;
+            WCHAR computerName[MAX_COMPUTERNAME_LENGTH + 1];
+            LSA_OBJECT_ATTRIBUTES ObjectAttributes;
+            LSA_HANDLE PolicyHandle;
+            NTSTATUS NtStatus;
+           
+            computerNameLen = MAX_COMPUTERNAME_LENGTH + 1;
+            GetComputerNameW(computerName, &computerNameLen);
+            computerNameLen++; /* include NULL terminator */
+
+            ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));
+            NtStatus = LsaOpenPolicy(NULL, &ObjectAttributes,
+             POLICY_VIEW_LOCAL_INFORMATION, &PolicyHandle);
+            if (NtStatus != STATUS_SUCCESS)
+                ret = LsaNtStatusToWinError(NtStatus);
+            else
+            {
+                PPOLICY_ACCOUNT_DOMAIN_INFO DomainInfo;
+
+                LsaQueryInformationPolicy(PolicyHandle,
+                 PolicyAccountDomainInformation, (PVOID*)&DomainInfo);
+                domainNameLen = lstrlenW(DomainInfo->DomainName.Buffer) + 1;
+                size = sizeof(WKSTA_INFO_100) + computerNameLen * sizeof(WCHAR)
+                 + domainNameLen * sizeof(WCHAR);
+                ret = NetApiBufferAllocate(size, (LPVOID *)bufptr);
+                if (ret == NERR_Success)
+                {
+                    PWKSTA_INFO_100 info = (PWKSTA_INFO_100)*bufptr;
+                    OSVERSIONINFOW verInfo;
+
+                    info->wki100_platform_id = PLATFORM_ID_NT;
+                    info->wki100_computername = (LPWSTR)(*bufptr +
+                     sizeof(WKSTA_INFO_100));
+                    memcpy(info->wki100_computername, computerName,
+                     computerNameLen * sizeof(WCHAR));
+                    info->wki100_langroup = (LPWSTR)(*bufptr +
+                     sizeof(WKSTA_INFO_100) + computerNameLen * sizeof(WCHAR));
+                    memcpy(info->wki100_langroup, DomainInfo->DomainName.Buffer,
+                     domainNameLen * sizeof(WCHAR));
+                    memset(&verInfo, 0, sizeof(verInfo));
+                    verInfo.dwOSVersionInfoSize = sizeof(verInfo);
+                    GetVersionExW(&verInfo);
+                    info->wki100_ver_major = verInfo.dwMajorVersion;
+                    info->wki100_ver_minor = verInfo.dwMinorVersion;
+                }
+                LsaFreeMemory(DomainInfo);
+                LsaClose(PolicyHandle);
+            }
+            break;
+        }
+
+        default:
+            FIXME("level %ld unimplemented\n", level);
+            ret = ERROR_INVALID_LEVEL;
+    }
+    return ret;
+}
+
+/************************************************************
+ *                NetGetJoinInformation (NETAPI32.@)
+ */
+NET_API_STATUS NET_API_FUNCTION NetGetJoinInformation(
+    LPCWSTR Server,
+    LPWSTR *Name,
+    PNETSETUP_JOIN_STATUS type)
+{
+    FIXME("Stub %s %p %p\n", wine_dbgstr_w(Server), Name, type);
+
+    *Name = NULL;
+    *type = NetSetupUnknownStatus;
+
+    return NERR_Success;
+}
index 295e45c..25e2e00 100644 (file)
@@ -54,7 +54,7 @@ reactos/lib/msacm             # Out of sync
 reactos/lib/msimg32            # Synced to Wine-0_9_1
 reactos/lib/msi                        # Synced to Wine-0_9_1
 reactos/lib/msvideo            # Out of sync
-reactos/lib/netapi32           # Out of sync
+reactos/lib/netapi32           # Synced to Wine-0_9_1
 reactos/lib/objsel             # Synced to Wine-0_9_1
 reactos/lib/odbc32             # Out of sync. Depends on port of Linux ODBC.
 reactos/lib/ole32               # Synced to Wine-0_9_1
index db1779d..e0baa6a 100644 (file)
@@ -23,5 +23,6 @@
 #include <lmwksta.h>
 #include <lmserver.h>
 #include <lmstats.h>
+#include <lmjoin.h>
 
 #endif
diff --git a/reactos/w32api/include/lmbrowsr.h b/reactos/w32api/include/lmbrowsr.h
deleted file mode 100644 (file)
index e6f4796..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-#ifndef _LMBROWSR_H
-#define _LMBROWSR_H
-#if __GNUC__ >=3
-#pragma GCC system_header
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-#define BROWSER_ROLE_PDC 1
-#define BROWSER_ROLE_BDC 2
-typedef struct _BROWSER_STATISTICS {
-       LARGE_INTEGER StatisticsStartTime;
-       LARGE_INTEGER NumberOfServerAnnouncements;
-       LARGE_INTEGER NumberOfDomainAnnouncements;
-       ULONG NumberOfElectionPackets;
-       ULONG NumberOfMailslotWrites;
-       ULONG NumberOfGetBrowserServerListRequests;
-       ULONG NumberOfServerEnumerations;
-       ULONG NumberOfDomainEnumerations;
-       ULONG NumberOfOtherEnumerations;
-       ULONG NumberOfMissedServerAnnouncements;
-       ULONG NumberOfMissedMailslotDatagrams;
-       ULONG NumberOfMissedGetBrowserServerListRequests;
-       ULONG NumberOfFailedServerAnnounceAllocations;
-       ULONG NumberOfFailedMailslotAllocations;
-       ULONG NumberOfFailedMailslotReceives;
-       ULONG NumberOfFailedMailslotWrites;
-       ULONG NumberOfFailedMailslotOpens;
-       ULONG NumberOfDuplicateMasterAnnouncements;
-LARGE_INTEGER NumberOfIllegalDatagrams;
-} BROWSER_STATISTICS,*PBROWSER_STATISTICS,*LPBROWSER_STATISTICS;
-typedef struct _BROWSER_STATISTICS_100 {
-       LARGE_INTEGER StartTime;
-       LARGE_INTEGER NumberOfServerAnnouncements;
-       LARGE_INTEGER NumberOfDomainAnnouncements;
-       ULONG NumberOfElectionPackets;
-       ULONG NumberOfMailslotWrites;
-       ULONG NumberOfGetBrowserServerListRequests;
-       LARGE_INTEGER NumberOfIllegalDatagrams;
-} BROWSER_STATISTICS_100,*PBROWSER_STATISTICS_100;
-typedef struct _BROWSER_STATISTICS_101 {
-       LARGE_INTEGER StartTime;
-       LARGE_INTEGER NumberOfServerAnnouncements;
-       LARGE_INTEGER NumberOfDomainAnnouncements;
-       ULONG NumberOfElectionPackets;
-       ULONG NumberOfMailslotWrites;
-       ULONG NumberOfGetBrowserServerListRequests;
-       LARGE_INTEGER NumberOfIllegalDatagrams;
-       ULONG NumberOfMissedServerAnnouncements;
-       ULONG NumberOfMissedMailslotDatagrams;
-       ULONG NumberOfMissedGetBrowserServerListRequests;
-       ULONG NumberOfFailedServerAnnounceAllocations;
-       ULONG NumberOfFailedMailslotAllocations;
-       ULONG NumberOfFailedMailslotReceives;
-       ULONG NumberOfFailedMailslotWrites;
-       ULONG NumberOfFailedMailslotOpens;
-       ULONG NumberOfDuplicateMasterAnnouncements;
-} BROWSER_STATISTICS_101,*PBROWSER_STATISTICS_101;
-
-NET_API_STATUS WINAPI I_BrowserServerEnum(LPCWSTR,LPCWSTR,LPCWSTR,DWORD,PBYTE*,DWORD,PDWORD,PDWORD,DWORD,LPCWSTR,PDWORD);
-NET_API_STATUS WINAPI I_BrowserServerEnumEx(LPCWSTR,LPCWSTR,LPCWSTR,DWORD,PBYTE*,DWORD,PDWORD,PDWORD,DWORD,LPCWSTR,LPCWSTR);
-NET_API_STATUS WINAPI I_BrowserQueryEmulatedDomains(LPWSTR,PBYTE*,PDWORD);
-NET_API_STATUS I_BrowserQueryOtherDomains(LPCWSTR,PBYTE*,PDWORD,PDWORD);
-NET_API_STATUS I_BrowserResetNetlogonState(LPCWSTR);
-NET_API_STATUS WINAPI I_BrowserSetNetlogonState(LPWSTR,LPWSTR,LPWSTR,DWORD);
-NET_API_STATUS I_BrowserQueryStatistics(LPCWSTR,LPBROWSER_STATISTICS*);
-NET_API_STATUS I_BrowserResetStatistics(LPCWSTR);
-WORD I_BrowserServerEnumForXactsrv(LPCWSTR,LPCWSTR,ULONG,USHORT,PVOID,WORD,DWORD,PDWORD,PDWORD,DWORD,LPCWSTR,LPCWSTR,PWORD);
-NET_API_STATUS I_BrowserDebugTrace(PWCHAR,PCHAR);
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/reactos/w32api/include/lmjoin.h b/reactos/w32api/include/lmjoin.h
new file mode 100644 (file)
index 0000000..7c18ca0
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2005 Ulrich Czekalla (For CodeWeavers)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __WINE_LMJOIN_H
+#define __WINE_LMJOIN_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum tagNETSETUP_JOIN_STATUS
+{
+    NetSetupUnknownStatus = 0,
+    NetSetupUnjoined,
+    NetSetupWorkgroupName,
+    NetSetupDomainName
+} NETSETUP_JOIN_STATUS, *PNETSETUP_JOIN_STATUS;
+
+NET_API_STATUS NET_API_FUNCTION NetGetJoinInformation(
+    LPCWSTR Server,
+    LPWSTR *Name,
+    PNETSETUP_JOIN_STATUS type);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
index 16b0bcf..0c8ae3e 100644 (file)
@@ -125,16 +125,6 @@ extern "C" {
 
 #if !defined(_NTDEF_H) && !defined(_SUBAUTH_H)
 typedef LONG NTSTATUS, *PNTSTATUS;
-typedef struct _UNICODE_STRING {
-  USHORT Length;
-  USHORT MaximumLength;
-  PWSTR Buffer;
-} UNICODE_STRING, *PUNICODE_STRING;
-typedef struct _STRING {
-  USHORT Length;
-  USHORT MaximumLength;
-  PCHAR Buffer;
-} STRING, *PSTRING;
 #endif
 
 #if defined (_NTDEF_H)